본문 바로가기

iOS/기타

WKWebView 쿠키 공유하기

혼란스러운 WKWebView간 쿠키 공유처리에 대해 그동안 겪은 시행착오를 정리해보았습니다.

 

기존 UIWebView의 경우

HTTPCookieStorage.shared에 쿠키를 저장하고 읽도록 처리되었습니다. 따라서 개발자가 별도의 쿠키관리를 해주지 않아도 webview간의 쿠키공유가 가능했었습니다.

서버 API를 통해 서버에서 전달받은 쿠키도 HTTPCookieStorage.shared에 저장되기 때문에, webview에서 해당 쿠키값들도 조회가 가능했습니다.

반대로 webview에서 저장한 쿠키값들도 별도의 처리 없이 서버 API를 통해 서버로 전달될수 있었습니다.

 

하지만, WKWebView의 경우 HTTPCookieStorage.shared를 쿠키저장소로 이용하지 않습니다. 따라서 기본적으로 webview간 쿠키공유가 불가능합니다.(OS버전에 따라 차이가 있어 보입니다만...)

 

WKWebView 생성시 보통 다음과 같이 처리합니다.

let configuration = WKWebViewConfiguration()
let webView = WKWebView(frame: view.frame, configuration: configuration)

이때, webview생성시 넘겨주는 configuration의 websiteDataStore에 다음과 같이 WKWebView의 쿠키저장소가 존재하게 됩니다.

- "configuration.websiteDataStore.httpCookieStore"(httpCookieStore는 WKHTTPCookieStore 클래스입니다.)

 

이 websiteDataStore에는 기본적으로 default 저장소가 설정되어 있습니다. 따라서 위 코드는 다음코드와 동일한 동작을 하게 됩니다.

let configuration = WKWebViewConfiguration()
configuration.websiteDataStore = WKWebsiteDataStore.default() // 설정하지 않아도 기본적으로 default저장소가 할당되어 있음.
let webView = WKWebView(frame: view.frame, configuration: configuration)

이렇게 WKWebView의 저장소를 설정하게 되면 webview간의 쿠키공유가 되지 않습니다.(동일한 default 저장소 객체를 갖는데 왜 공유가 안될까요...)

쿠키공유를 위해서는 다음과 같이 동일한 processPool을 configuration에 설정해 주면 됩니다.

let commonProcessPool = WKProcessPool()

let configuration1 = WKWebViewConfiguration()
configuration1.processPool = commonProcessPool
let webView1 = WKWebView(frame: view.frame, configuration: configuration1)

let configuration2 = WKWebViewConfiguration()
configuration2.processPool = commonProcessPool
let webView2 = WKWebView(frame: view.frame, configuration: configuration2)

 

만일, 특정webView는 쿠키공유를 하고싶지 않을때는 어떻게 하면 될까요?

네. 다음과 같이 commonProcessPool을 설정해주지 않으면 됩니다. 

let commonProcessPool = WKProcessPool()

let configuration1 = WKWebViewConfiguration()
configuration1.processPool = commonProcessPool
let webView1 = WKWebView(frame: view.frame, configuration: configuration1)

let configuration2 = WKWebViewConfiguration()
configuration2.processPool = commonProcessPool
let webView2 = WKWebView(frame: view.frame, configuration: configuration2)

let configuration3 = WKWebViewConfiguration()
let webView3 = WKWebView(frame: view.frame, configuration: configuration3) // 쿠키공유를 하고 싶지 않은 webView

어? 그런데 일부 단말에서 webView3에도 쿠키가 공유되는 현상이 발생했습니다. ㅜㅜ

해당 단말의 iOS버전이 15.4입니다.(13버전에서는 문제 없었는데...)

15.4에서는 commonProcessPool을 설정해주지 않아도 webView간 쿠키 공유가 되네요.

그래서, 다음과 같이 configuration3에는 WKWebsiteDataStore.nonPersistent()를 설정해 주었습니다.

let commonProcessPool = WKProcessPool()

let configuration1 = WKWebViewConfiguration()
configuration1.processPool = commonProcessPool
let webView1 = WKWebView(frame: view.frame, configuration: configuration1)

let configuration2 = WKWebViewConfiguration()
configuration2.processPool = commonProcessPool
let webView2 = WKWebView(frame: view.frame, configuration: configuration2)

let configuration3 = WKWebViewConfiguration()
configuration3.websiteDataStore = WKWebsiteDataStore.nonPersistent()
let webView3 = WKWebView(frame: view.frame, configuration: configuration3) // 쿠키공유를 하고 싶지 않은 webView

default와 nonPersistent의 차이는 다음 규격이 설명해 주는 것 같습니다.

1. WKWebsiteDataStore.default() : Returns the default data store, which stores data persistantly to disk.

- 이 저장소는 Expires Date가 지나지 않은 쿠키들은 계속해서 파일에 저장해 둡니다. 따라서 앱을 재 실행하여도 Expires Date가 남아있는 쿠키들은 접근이 가능합니다.

2. WKWebsiteDataStore.nonPersistent() : Creates a new data store object that stores website data in memory, and doesn't write that data to disk.

- 아예 새로운 저장소 객체를 생성하여 설정하므로 쿠키 공유를 할수가 없습니다. commonProcessPool을 설정해줘도 공유가 안됩니다.

 

 

마지막으로 위에 잠깐 언급하긴 했는데요, Http 서버 API호출에 이용되는 쿠키와 WKWebView간 쿠키를 공유하려면 어떻게 해야 할까요?

Http 서버통신시 쿠키는 모두 HTTPCookieStorage.shared에 저장이 되고, WKWebView는 별도의 쿠키 저장소에 쿠키를 저장하게 되므로 두 저장소를 필요시 동기화처리를 해주면 됩니다.

 

1. WKWebView의 쿠키들을 HTTPCookieStorage.shared에 동기화

static func syncHTTPCookieStorage(_ cookieStore: WKHTTPCookieStore, completionHandler: (() -> Void)?) {
    HTTPCookieStorage.shared.cookieAcceptPolicy = .always
    cookieStore.getAllCookies({(_ cookies: [HTTPCookie]) -> Void in
        for cookie: HTTPCookie in cookies {
            HTTPCookieStorage.shared.setCookie(cookie)
        }

        completionHandler?()
    })
}

syncHTTPCookieStorage(webView.configuration.websiteDataStore.httpCookieStore)

2. HTTPCookieStorage.shared의 쿠키들을 WKWebView에 동기화

func loadUrlWithCookies(_ path: String, cookies: [HTTPCookie]? = nil) {
    if let url = URL(string: path) {
        if let cookies = cookies {
            let group = DispatchGroup()
            cookies.forEach({ cookie in
                group.enter()
                self.webView.configuration.websiteDataStore.httpCookieStore.setCookie(cookie) {
                    group.leave()
                }
            })
            group.notify(queue: .main) { [weak self] in
                self?.webView.load(URLRequest(url: url))
            }
        }
        else {
            self.webView.load(URLRequest(url: url))
        }
    }
}

loadUrlWithCookies(path, cookies: HTTPCookieStorage.shared.cookies)
반응형