WKHTTPCookieStore HTTPCookieStorage 同步class
WKHTTPCookieStore HTTPCookieStorage synchronization class
用例:我有两个 WKWebView
,一个在主应用中,一个在应用扩展中。我希望他们共享同一个 cookie 存储。
Apple API 有 2 种不同的 cookie 存储方式 classes:WKHTTPCookieStore
和 HTTPCookieStorage
。
Apple 也有 sharedCookieStorage(forGroupContainerIdentifier identifier: String) -> HTTPCookieStorage
URLSession
s(不是 WKWebView
s)跨 Apps/Build Targets/Extensions.
使用
我的计划是将一个 WKWebKit
cookie 传输到 sharedCookieStorage,然后再传输到另一个 WKWebKit
cookie 存储。
在我写一个之前,有没有人有一个简单的包装器 class 来接收这些 classes,观察它们,并使它们保持同步?
或者是否有更简单的方法来完成这个看似非常常见的用例?
这是我整理的。需要牢记的重要事项:
WKWebView
cookie 是异步设置和获取的,因此回调的数量
- 有一个观察者 class 附加到
WKHTTPCookieStore
。它并不完美,但有 cookiesDidChange
方法用于手动更新,比如在页面加载后
- 您应该在
addCookieStore
回调中实例化并加载 WKWebView
,以确保在加载页面之前同步商店
- 添加的商店最初与添加的第一个商店同步。您应该嵌套
addCookieStore
回调以确保正确的顺序
(抱歉,出于兼容性原因我无法使用 Combine)
代码:
import WebKit
import Foundation
class CookieSync : NSObject, WKHTTPCookieStoreObserver {
var wkStores = [WKHTTPCookieStore]();
var sessionStores = [HTTPCookieStorage]();
static let debug = false
//The first store added is the canon
func addCookieStore(_ store: WKHTTPCookieStore, callback:(()->())?) {
wkStores.append(store)
store.getAllCookies { (cookies) in
if CookieSync.debug { print("Adding WK:\(cookies.count)") }
store.add(self)
if self.sessionStores.count > 0 {
self.synchronizeAll(self.sessionStores[0]) {
store.getAllCookies { (cookies) in
if CookieSync.debug { print("Added WK:\(cookies.count)") }
callback?()
}
}
} else if self.wkStores.count > 1 {
self.synchronizeAll(self.wkStores[0]) {
store.getAllCookies { (cookies) in
if CookieSync.debug { print("Added WK:\(cookies.count)") }
callback?()
}
}
} else {
callback?()
}
}
}
//The first store added is the canon
func addCookieStore(_ store: HTTPCookieStorage, callback:(()->())?) {
sessionStores.append(store)
if CookieSync.debug { print("Adding S:\(store.cookies?.count ?? 0)") }
if wkStores.count > 0 {
synchronizeAll(wkStores[0]) {
if CookieSync.debug { print("Added S:\(store.cookies?.count ?? 0)") }
callback?()
}
} else if sessionStores.count > 1 {
synchronizeAll(sessionStores[0]) {
if CookieSync.debug { print("Added S:\(store.cookies?.count ?? 0)") }
callback?()
}
} else {
callback?()
}
}
//There is no Observer callback for HTTPCookieStorage
func cookiesDidChange(in cookieStore: HTTPCookieStorage) {
synchronizeAll(cookieStore) {
if CookieSync.debug { print("Synced S:\(cookieStore.cookies?.count ?? 0)") }
}
}
func cookiesDidChange(in cookieStore: WKHTTPCookieStore) {
synchronizeAll(cookieStore) {
cookieStore.getAllCookies { (cookies) in
if CookieSync.debug { print("Synced WK:\(cookies.count)") }
for cookie in cookies {
if CookieSync.debug { print("\(cookie.name) = \(cookie.value)") }
}
}
}
}
//Private
fileprivate func synchronizeAll(_ to: WKHTTPCookieStore, callback:(()->())?) {
let dispatch = DispatchGroup()
let queue = Thread.isMainThread ? DispatchQueue.main : DispatchQueue(label: "cookie_sync1")
for store in self.wkStores {
if store == to { continue }
dispatch.enter()
self.removeAllCookies(store) {
dispatch.enter()
to.getAllCookies { (cookies) in
for cookie in cookies {
dispatch.enter()
store.setCookie(cookie) {
dispatch.leave()
}
}
dispatch.leave()
}
dispatch.leave()
}
}
for store in self.sessionStores {
self.removeAllCookies(store)
dispatch.enter()
to.getAllCookies { (cookies) in
for cookie in cookies {
store.setCookie(cookie)
}
dispatch.leave()
}
}
dispatch.notify(queue: queue) {
callback?()
}
}
fileprivate func synchronizeAll(_ to: HTTPCookieStorage, callback:(()->())?) {
guard let cookies = to.cookies else { callback?(); return; }
let queue = Thread.isMainThread ? DispatchQueue.main : DispatchQueue(label: "cookie_sync2")
let dispatch = DispatchGroup()
for store in self.sessionStores {
if store == to { continue }
self.removeAllCookies(store)
for cookie in cookies {
store.setCookie(cookie)
}
}
for store in self.wkStores {
dispatch.enter()
self.removeAllCookies(store) {
for cookie in cookies {
dispatch.enter()
store.setCookie(cookie) {
dispatch.leave()
}
}
dispatch.leave()
}
}
dispatch.notify(queue: queue) {
callback?()
}
}
fileprivate func removeAllCookies(_ store: WKHTTPCookieStore, callback:(()->())?) {
let queue = Thread.isMainThread ? DispatchQueue.main : DispatchQueue(label: "cookie_delete")
let dispatch = DispatchGroup()
dispatch.enter()
store.getAllCookies { (cookies) in
for cookie in cookies {
dispatch.enter()
store.delete(cookie) {
dispatch.leave()
}
}
dispatch.leave()
}
dispatch.notify(queue: queue) {
callback?()
}
}
fileprivate func removeAllCookies(_ store: HTTPCookieStorage) {
guard let cookies = store.cookies else { return }
for cookie in cookies {
store.deleteCookie(cookie)
}
}
}
用例:我有两个 WKWebView
,一个在主应用中,一个在应用扩展中。我希望他们共享同一个 cookie 存储。
Apple API 有 2 种不同的 cookie 存储方式 classes:WKHTTPCookieStore
和 HTTPCookieStorage
。
Apple 也有 sharedCookieStorage(forGroupContainerIdentifier identifier: String) -> HTTPCookieStorage
URLSession
s(不是 WKWebView
s)跨 Apps/Build Targets/Extensions.
我的计划是将一个 WKWebKit
cookie 传输到 sharedCookieStorage,然后再传输到另一个 WKWebKit
cookie 存储。
在我写一个之前,有没有人有一个简单的包装器 class 来接收这些 classes,观察它们,并使它们保持同步?
或者是否有更简单的方法来完成这个看似非常常见的用例?
这是我整理的。需要牢记的重要事项:
WKWebView
cookie 是异步设置和获取的,因此回调的数量- 有一个观察者 class 附加到
WKHTTPCookieStore
。它并不完美,但有cookiesDidChange
方法用于手动更新,比如在页面加载后 - 您应该在
addCookieStore
回调中实例化并加载WKWebView
,以确保在加载页面之前同步商店 - 添加的商店最初与添加的第一个商店同步。您应该嵌套
addCookieStore
回调以确保正确的顺序
(抱歉,出于兼容性原因我无法使用 Combine)
代码:
import WebKit
import Foundation
class CookieSync : NSObject, WKHTTPCookieStoreObserver {
var wkStores = [WKHTTPCookieStore]();
var sessionStores = [HTTPCookieStorage]();
static let debug = false
//The first store added is the canon
func addCookieStore(_ store: WKHTTPCookieStore, callback:(()->())?) {
wkStores.append(store)
store.getAllCookies { (cookies) in
if CookieSync.debug { print("Adding WK:\(cookies.count)") }
store.add(self)
if self.sessionStores.count > 0 {
self.synchronizeAll(self.sessionStores[0]) {
store.getAllCookies { (cookies) in
if CookieSync.debug { print("Added WK:\(cookies.count)") }
callback?()
}
}
} else if self.wkStores.count > 1 {
self.synchronizeAll(self.wkStores[0]) {
store.getAllCookies { (cookies) in
if CookieSync.debug { print("Added WK:\(cookies.count)") }
callback?()
}
}
} else {
callback?()
}
}
}
//The first store added is the canon
func addCookieStore(_ store: HTTPCookieStorage, callback:(()->())?) {
sessionStores.append(store)
if CookieSync.debug { print("Adding S:\(store.cookies?.count ?? 0)") }
if wkStores.count > 0 {
synchronizeAll(wkStores[0]) {
if CookieSync.debug { print("Added S:\(store.cookies?.count ?? 0)") }
callback?()
}
} else if sessionStores.count > 1 {
synchronizeAll(sessionStores[0]) {
if CookieSync.debug { print("Added S:\(store.cookies?.count ?? 0)") }
callback?()
}
} else {
callback?()
}
}
//There is no Observer callback for HTTPCookieStorage
func cookiesDidChange(in cookieStore: HTTPCookieStorage) {
synchronizeAll(cookieStore) {
if CookieSync.debug { print("Synced S:\(cookieStore.cookies?.count ?? 0)") }
}
}
func cookiesDidChange(in cookieStore: WKHTTPCookieStore) {
synchronizeAll(cookieStore) {
cookieStore.getAllCookies { (cookies) in
if CookieSync.debug { print("Synced WK:\(cookies.count)") }
for cookie in cookies {
if CookieSync.debug { print("\(cookie.name) = \(cookie.value)") }
}
}
}
}
//Private
fileprivate func synchronizeAll(_ to: WKHTTPCookieStore, callback:(()->())?) {
let dispatch = DispatchGroup()
let queue = Thread.isMainThread ? DispatchQueue.main : DispatchQueue(label: "cookie_sync1")
for store in self.wkStores {
if store == to { continue }
dispatch.enter()
self.removeAllCookies(store) {
dispatch.enter()
to.getAllCookies { (cookies) in
for cookie in cookies {
dispatch.enter()
store.setCookie(cookie) {
dispatch.leave()
}
}
dispatch.leave()
}
dispatch.leave()
}
}
for store in self.sessionStores {
self.removeAllCookies(store)
dispatch.enter()
to.getAllCookies { (cookies) in
for cookie in cookies {
store.setCookie(cookie)
}
dispatch.leave()
}
}
dispatch.notify(queue: queue) {
callback?()
}
}
fileprivate func synchronizeAll(_ to: HTTPCookieStorage, callback:(()->())?) {
guard let cookies = to.cookies else { callback?(); return; }
let queue = Thread.isMainThread ? DispatchQueue.main : DispatchQueue(label: "cookie_sync2")
let dispatch = DispatchGroup()
for store in self.sessionStores {
if store == to { continue }
self.removeAllCookies(store)
for cookie in cookies {
store.setCookie(cookie)
}
}
for store in self.wkStores {
dispatch.enter()
self.removeAllCookies(store) {
for cookie in cookies {
dispatch.enter()
store.setCookie(cookie) {
dispatch.leave()
}
}
dispatch.leave()
}
}
dispatch.notify(queue: queue) {
callback?()
}
}
fileprivate func removeAllCookies(_ store: WKHTTPCookieStore, callback:(()->())?) {
let queue = Thread.isMainThread ? DispatchQueue.main : DispatchQueue(label: "cookie_delete")
let dispatch = DispatchGroup()
dispatch.enter()
store.getAllCookies { (cookies) in
for cookie in cookies {
dispatch.enter()
store.delete(cookie) {
dispatch.leave()
}
}
dispatch.leave()
}
dispatch.notify(queue: queue) {
callback?()
}
}
fileprivate func removeAllCookies(_ store: HTTPCookieStorage) {
guard let cookies = store.cookies else { return }
for cookie in cookies {
store.deleteCookie(cookie)
}
}
}