【问题标题】:WKHTTPCookieStore HTTPCookieStorage synchronization classWKHTTPCookieStore HTTPCookieStorage 同步类
【发布时间】:2020-12-03 16:04:57
【问题描述】:

用例:我有两个WKWebViews,一个在主应用程序中,一个在应用程序扩展程序中。我希望他们共享同一个 cookie 存储。

Apple API 有 2 个不同的 cookie 存储类:WKHTTPCookieStoreHTTPCookieStorage

Apple 还为 URLSessions(不是 WKWebViews)提供了 sharedCookieStorage(forGroupContainerIdentifier identifier: String) -> HTTPCookieStorage,以便在应用程序/构建目标/扩展中使用。

我的计划是将一个 WKWebKit cookie 传输到 sharedCookieStorage,然后再传输到另一个 WKWebKit cookie 存储。

在我写之前,有没有人有一个简单的包装类来接收这些类,观察它们并保持它们同步?

或者有没有更简单的方法来完成这个看似非常常见的用例?

【问题讨论】:

    标签: cookies wkwebview wkwebviewconfiguration wkhttpcookiestore


    【解决方案1】:

    这是我整理的。要记住的重要事项:

    • WKWebView cookie 是异步设置和获取的,因此回调的数量
    • 有一个观察者类附加到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)
            }
        }
    }
    

    【讨论】:

    • 感谢您的回答!您能否提供将 CookieSync 用于 WKWebView、URLRequests 的示例?
    猜你喜欢
    • 1970-01-01
    • 2021-11-22
    • 2018-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-06
    • 1970-01-01
    相关资源
    最近更新 更多