diff --git a/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Example/Example/Info.plist b/Example/Example/Info.plist index 38e98af..9a21b4f 100644 --- a/Example/Example/Info.plist +++ b/Example/Example/Info.plist @@ -34,5 +34,10 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + diff --git a/Example/Example/ViewController.swift b/Example/Example/ViewController.swift index d716449..4491574 100644 --- a/Example/Example/ViewController.swift +++ b/Example/Example/ViewController.swift @@ -14,25 +14,21 @@ class ViewController: UIViewController { lazy var webView: WKCookieWebView = { let webView: WKCookieWebView = WKCookieWebView(frame: self.view.bounds) + webView.translatesAutoresizingMaskIntoConstraints = false // webView.wkNavigationDelegate = self return webView }() - override func loadView() { - super.loadView() - setupWebView() - } - override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. + setupWebView() webView.onDecidePolicyForNavigationAction = { (webView, navigationAction, decisionHandler) in decisionHandler(.allow) } webView.onDecidePolicyForNavigationResponse = { (webView, navigationResponse, decisionHandler) in - print("allHeaderFields : \((navigationResponse.response as? HTTPURLResponse)?.allHeaderFields ?? [:])") decisionHandler(.allow) } diff --git a/WKCookieWebView.podspec b/WKCookieWebView.podspec index a85ec51..49fc3e4 100644 --- a/WKCookieWebView.podspec +++ b/WKCookieWebView.podspec @@ -16,7 +16,7 @@ Pod::Spec.new do |s| # s.name = "WKCookieWebView" - s.version = "1.0.0" + s.version = "1.0.1" s.summary = "WKCookieWebView" # This description is used to generate tags and improve search results. diff --git a/WKCookieWebView/WKCookieWebView.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/WKCookieWebView/WKCookieWebView.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/WKCookieWebView/WKCookieWebView.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/WKCookieWebView/WKCookieWebView/Info.plist b/WKCookieWebView/WKCookieWebView/Info.plist index 20b05ec..f9a8194 100644 --- a/WKCookieWebView/WKCookieWebView/Info.plist +++ b/WKCookieWebView/WKCookieWebView/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.0.0 + 1.0.1 CFBundleVersion $(CURRENT_PROJECT_VERSION) NSPrincipalClass diff --git a/WKCookieWebView/WKCookieWebView/WKCookieWebView.swift b/WKCookieWebView/WKCookieWebView/WKCookieWebView.swift index e4faa6c..7d4ce8a 100644 --- a/WKCookieWebView/WKCookieWebView/WKCookieWebView.swift +++ b/WKCookieWebView/WKCookieWebView/WKCookieWebView.swift @@ -27,7 +27,8 @@ open class WKCookieWebView: WKWebView { // The closure where cookie information is called at update time public var onUpdateCookieStorage: ((WKCookieWebView) -> Void)? - fileprivate var updatedCookies = [String]() + private var updatedCookies = [String]() + public init(frame: CGRect, configurationBlock: ((WKWebViewConfiguration) -> Void)? = nil) { HTTPCookieStorage.shared.cookieAcceptPolicy = .always @@ -69,73 +70,119 @@ open class WKCookieWebView: WKWebView { } - fileprivate func update(webView: WKWebView) { + private func update(webView: WKWebView) { + // WKWebView -> HTTPCookieStorage webView.evaluateJavaScript("document.cookie;") { [weak self] (result, error) in - guard let `self` = self, - let documentCookie = result as? String else { - return + guard let host = self?.url?.host, + let documentCookie = result as? String else { + return } - let cookieValues = documentCookie.components(separatedBy: "; ") + self?.fetchCookies(fileter: host, completion: { [weak self] (cookies) in + self?.update(with: cookies, documentCookie: documentCookie, host: host) + }) + } + } + + private func update(with cachedCookies: [HTTPCookie]?, documentCookie: String, host: String) { + let cookieValues = documentCookie.components(separatedBy: "; ") + var cachedCookies: [HTTPCookie]? = cachedCookies + + for value in cookieValues { + var comps = value.components(separatedBy: "=") + if comps.count < 2 { continue } - for value in cookieValues { - let comps = value.components(separatedBy: "=") - if comps.count < 2 { continue } - - let localCookie = HTTPCookieStorage.shared.cookies?.filter { $0.name == comps[0] }.first - - if let localCookie = localCookie { - if !comps[1].isEmpty && localCookie.value != comps[1] { - // cookie value is different - if self.updatedCookies.contains(localCookie.name) { - webView.evaluateJavaScript("document.cookie='\(localCookie.name)=\(localCookie.value);domain=\(localCookie.domain);path=\(localCookie.path);'", completionHandler: nil) - } else { - // set cookie - var properties: [HTTPCookiePropertyKey: Any] = [ - .name: localCookie.name, - .value: comps[1], - .domain: localCookie.domain, - .path: "/" - ] - - if let expireDate = localCookie.expiresDate { - properties[.expires] = expireDate - } - - if let cookie = HTTPCookie(properties: properties) { - HTTPCookieStorage.shared.setCookie(cookie) - self.onUpdateCookieStorage?(self) - } - } + let cookieName = comps.removeFirst() + let cookieValue = comps.joined(separator: "=") + let localCookie = cachedCookies?.filter { $0.name == cookieName }.first + + if let localCookie = localCookie { + if !cookieValue.isEmpty && localCookie.value != cookieValue { + // set/update cookie + var properties: [HTTPCookiePropertyKey: Any] = localCookie.properties ?? [ + .name: localCookie.name, + .domain: localCookie.domain, + .path: localCookie.path, + ] + + properties[.value] = cookieValue + + if let cookie = HTTPCookie(properties: properties) { + self.set(cookie: cookie) + self.onUpdateCookieStorage?(self) } - } else { - if let rootDomain = webView.url?.host, !comps[0].isEmpty && !comps[1].isEmpty { - let properties: [HTTPCookiePropertyKey: Any] = [ - .name: comps[0], - .value: comps[1], - .domain: rootDomain, - .path: "/", - ] - - if let cookie = HTTPCookie(properties: properties) { - // set cookie - HTTPCookieStorage.shared.setCookie(cookie) - self.onUpdateCookieStorage?(self) - } + } else if let index = cachedCookies?.index(of: localCookie) { + cachedCookies?.remove(at: index) + } + } else { + if !cookieName.isEmpty && !cookieValue.isEmpty { + let properties: [HTTPCookiePropertyKey: Any] = [ + .name: cookieName, + .value: cookieValue, + .domain: host, + .path: "/", + ] + + if let cookie = HTTPCookie(properties: properties) { + // set cookie + self.set(cookie: cookie) + self.onUpdateCookieStorage?(self) } } } } + + // Delete cookies with the same name + cachedCookies?.forEach { + self.delete(cookie: $0) + self.onUpdateCookieStorage?(self) + } } - fileprivate func update(cookies: [HTTPCookie]?) { + private func update(cookies: [HTTPCookie]?) { cookies?.forEach { - HTTPCookieStorage.shared.setCookie($0) + set(cookie: $0) updatedCookies.append($0.name) } - onUpdateCookieStorage?(self) } + +} + +public extension WKCookieWebView { + + typealias HTTPCookieHandler = ([HTTPCookie]?) -> Void + + public func fetchCookies(completion: @escaping HTTPCookieHandler) { + if #available(iOS 11.0, *) { + WKWebsiteDataStore.default().httpCookieStore.getAllCookies(completion) + } else { + completion(HTTPCookieStorage.shared.cookies) + } + } + + public func fetchCookies(fileter host: String, completion: @escaping HTTPCookieHandler) { + fetchCookies { (cookies) in + completion(cookies?.filter { $0.domain.range(of: host) != nil }) + } + } + + func set(cookie: HTTPCookie) { + HTTPCookieStorage.shared.setCookie(cookie) + + if #available(iOS 11.0, *) { + WKWebsiteDataStore.default().httpCookieStore.setCookie(cookie, completionHandler: nil) + } + } + + func delete(cookie: HTTPCookie) { + HTTPCookieStorage.shared.deleteCookie(cookie) + + if #available(iOS 11.0, *) { + WKWebsiteDataStore.default().httpCookieStore.delete(cookie, completionHandler: nil) + } + } + }