Skip to content

Instantly share code, notes, and snippets.

@spookyd
Forked from harshaspatil/CookieManager.swift
Created September 9, 2021 00:18
Show Gist options
  • Select an option

  • Save spookyd/ce5464451afddfbab7dc5aa553dd3496 to your computer and use it in GitHub Desktop.

Select an option

Save spookyd/ce5464451afddfbab7dc5aa553dd3496 to your computer and use it in GitHub Desktop.
WKWebview Cookie Sync Example
import Foundation
import WebKit
/// Class which syncronizes cookies for the webview.
internal final class CookieManager {
/// Cookies from http cookie store.
var httpCookies: [HTTPCookie] {
return HTTPCookieStorage.shared.cookies ?? [HTTPCookie]()
}
/// Pass cookie to wkwebview.
/// - Parameter completion: returns WKConfiguration.
func cookieSyncHTTPToWK(completion: @escaping (WKWebViewConfiguration) -> Void) {
DispatchQueue.main.async {
let websiteDataStore = WKWebsiteDataStore.nonPersistent()
let configuration = DefaultWebViewConfiguration() //So it uses the same global process pool.
configuration.websiteDataStore = websiteDataStore
let dispatchGroup = DispatchGroup()
self.httpCookies.forEach {
dispatchGroup.enter()
websiteDataStore.httpCookieStore.setCookie($0) {
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: DispatchQueue.main, execute: {
completion(configuration)
})
}
}
/// Clears all cookie.
/// - Parameter completion: completion block.
func removeCookies(completion: @escaping() -> Void) {
DispatchQueue.main.async {
self.deleteWKcookie {
for cookie in self.httpCookies {
if cookie.domain.contains("hsn.com") {
HTTPCookieStorage.shared.deleteCookie(cookie)
}
}
completion()
}
}
}
}
private extension CookieManager {
func deleteWKcookie(completion: @escaping() -> Void) {
let websiteDataStore = WKWebsiteDataStore.nonPersistent()
let configuration = DefaultWebViewConfiguration()
configuration.websiteDataStore = websiteDataStore
let dispatchGroup = DispatchGroup()
httpCookies.forEach {
dispatchGroup.enter()
websiteDataStore.httpCookieStore.delete($0) {
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: DispatchQueue.main, execute: {
completion()
})
}
}
import UIKit
import WebKit
/// CustomWKWebView which injects the header to the request.
internal final class CustomWKWebView: WKWebView {
fileprivate var cookieManager = CookieManager()
override init(frame: CGRect = CGRect(), configuration: WKWebViewConfiguration = DefaultWebViewConfiguration()) {
super.init(frame: frame, configuration: configuration)
navigationDelegate = self
}
// Storyboard init is not recommended as it will use the default configuration without setting the cookie.
required init?(coder: NSCoder) {
super.init(coder: coder)
navigationDelegate = self
}
@discardableResult override func load(_ request: URLRequest) -> WKNavigation? {
if shouldAddHeader(request: request) {
let customRequests = requestBy(apply: APIHeader().httpHeaders(), for: request)
return super.load(customRequests)
} else {
return super.load(request)
}
}
}
extension CustomWKWebView: WKNavigationDelegate {
func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if shouldAddHeader(request: navigationAction.request) {
load(navigationAction.request)
decisionHandler(.cancel)
return
}
decisionHandler(shouldAllowRedirecting ? .allow : .cancel)
}
}
private extension CustomWKWebView {
func requestBy(apply headers: [String: String], for request: URLRequest) -> URLRequest {
var mutableRequest = request
if mutableRequest.allHTTPHeaderFields == nil {
mutableRequest.allHTTPHeaderFields = [String: String]()
}
headers.forEach {
mutableRequest.allHTTPHeaderFields?[$0.key] = $0.value
}
return mutableRequest
}
}
import WebKit
/// Default WKWebViewConfiguration for the app that injects cookie and set default policies.
internal final class DefaultWebViewConfiguration: WKWebViewConfiguration {
override init() {
super.init()
setupConfiguration()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupConfiguration()
}
}
private extension DefaultWebViewConfiguration {
func setupConfiguration() {
preferences = defaultPreferences
processPool = defaultProcessPool
userContentController = defaultUserContentController
}
var defaultConfiguration: WKWebViewConfiguration {
let configuration = WKWebViewConfiguration()
configuration.preferences = defaultPreferences
configuration.processPool = defaultProcessPool
configuration.userContentController = defaultUserContentController
return configuration
}
var defaultPreferences: WKPreferences {
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
preferences.javaScriptCanOpenWindowsAutomatically = false
return preferences
}
var defaultProcessPool: WKProcessPool {
return AppDependencies.shared.processPool
}
var defaultUserContentController: WKUserContentController {
return WKUserContentController()
}
}
//A code snippet to demonstrate calling
/// Class to manage cookies.
fileprivate lazy var cookieManager = CookieManager()
override func viewDidLoad() {
super.viewDidLoad()
loadURL(<some url>)
}
func loadURL(url: URL) {
cookieManager.cookieSyncHTTPToWK { (configuration) in
self.initWithConfiguration(configuration)
self.webView?.load(URLRequest(url: url))
}
}
private func initWithConfiguration(_ configuration: WKWebViewConfiguration) {
let webView = CustomWKWebView(frame: CGRect(), configuration: configuration)
self.webView = webView
webView.allowsLinkPreview = false
view.addSubview(webView)
webView.autoPinEdgesToSuperviewEdges()
webView.redirectDelegate = self
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment