We have recently migrated to WKWebview. We have added a listener for cookie change, to get the updated cookies and update our own store.
- (void)cookiesDidChangeInCookieStore:(WKHTTPCookieStore *)cookieStore {
    [cookieStore getAllCookies:^(NSArray* cookies) {
    }];
}
Once the controller is loaded, it calls cookiesDidChangeInCookieStore and crashes at "getAllCookies".But this crash happens only in TestFlight/Fabric build. Doesn't happen when i run the app directly on device from xcode (both in debug and release mode). Below is the crash report,
Thread 9 name:  WebThread
Thread 9 Crashed:
0   WebKit                          0x0000000192fbfc10 WebKit::CallbackMap::put+ 1186832 (WTF::Ref<WebKit::CallbackBase, WTF::DumbPtrTraits<WebKit::CallbackBase> >&&) + 128
1   WebKit                          0x0000000192fbfbb4 WebKit::CallbackMap::put+ 1186740 (WTF::Ref<WebKit::CallbackBase, WTF::DumbPtrTraits<WebKit::CallbackBase> >&&) + 36
2   WebKit                          0x00000001930490cc WebKit::CallbackID WebKit::CallbackMap::put<WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WebKit::CallbackBase::Error>(WTF::Function<void + 1749196 (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WebKit::CallbackBase::Error)>&&) + 136
3   WebKit                          0x0000000193049008 WebKit::WebCookieManagerProxy::getAllCookies(PAL::SessionID, WTF::Function<void + 1749000 (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&, WebKit::CallbackBase::Error)>&&) + 44
4   WebKit                          0x0000000192eb5b90 API::HTTPCookieStore::cookies(WTF::Function<void + 97168 (WTF::Vector<WebCore::Cookie, 0ul, WTF::CrashOnOverflow, 16ul, WTF::FastMalloc> const&)>&&) + 124
5   WebKit                          0x00000001931fbdf8 -[WKHTTPCookieStore getAllCookies:] + 92
6   WebKit                          0x00000001931fc96c WKHTTPCookieStoreObserver::cookiesDidChange+ 3533164 (API::HTTPCookieStore&) + 44
7   WebKit                          0x0000000192eb61b0 API::HTTPCookieStore::cookiesDidChange+ 98736 () + 72
8   JavaScriptCore                  0x000000018a0e17d4 WTF::dispatchFunctionsFromMainThread+ 6100 () + 344
9   JavaScriptCore                  0x000000018a208650 WTF::timerFired+ 1214032 (__CFRunLoopTimer*, void*) + 40
10  CoreFoundation                  0x0000000183527aa8 __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 28
11  CoreFoundation                  0x000000018352776c __CFRunLoopDoTimer + 864
12  CoreFoundation                  0x0000000183527010 __CFRunLoopDoTimers + 248
13  CoreFoundation                  0x0000000183524b60 __CFRunLoopRun + 2168
14  CoreFoundation                  0x0000000183444da8 CFRunLoopRunSpecific + 552
15  WebCore                         0x000000018b6d1dcc RunWebThread+ 265676 (void*) + 592
16  libsystem_pthread.dylib         0x00000001831a5220 _pthread_body + 272
17  libsystem_pthread.dylib         0x00000001831a5110 _pthread_body + 0
18  libsystem_pthread.dylib         0x00000001831a3b10 thread_start + 4
Looks like there is an overflow when getAllCookies is called.This happens only in iOS 11.3.
After some investigation we have come to the following working solution:
We have a crash when the user updates to a newer version of our App.
We were using UIWebView and injecting cookies into it. The problem comes when:
getAllCookies(_ completionHandler: @escaping ([HTTPCookie]) -> Void) on the component wkhttpcookiestore so we can loop through them and remove them one by one. UIWebView uses nshttpcookiestorage: 
https://developer.apple.com/documentation/foundation/nshttpcookiestorage
WKWebView uses wkhttpcookiestore: 
https://developer.apple.com/documentation/webkit/wkhttpcookiestore
Somewhere in the middle of the synchronisation from nshttpcookiestorage to the wkhttpcookiestore when we try to retrieve the cookies, it is passing one of the values as a NSURL and then someone is calling length() function on that object which crashes because NSURL does not have that function.
Therefore, we should remove the cookies that are set on the nshttpcookiestorage with the right method which is by using:
HTTPCookieStorage.shared.removeCookies(since: Date.distantPast) and then remove the cookies from the wkhttpcookiestore with the right method that is removeData(ofTypes:for:completionHandler:) and set the type as WKWebsiteDataTypeCookies rather than looping through all the cookies and removing them one by one. 
All tests MUST be done on real devices (iPhone/iPad) since this crash is NOT reproducible on iOS simulators.
public func clearCookies(completion: @escaping (() -> Swift.Void)) {
    // First remove any previous cookies set in the NSHTTP cookie storage.
    HTTPCookieStorage.shared.removeCookies(since: Date.distantPast)
    // Second remove any previous cookies set in the WKHTTP cookie storage.
    let typeCookiesToBeRemoved: Set<String> = [WKWebsiteDataTypeCookies]
    // Only fetch the records in the storage with a cookie type.
    WKWebsiteDataStore.default().fetchDataRecords(ofTypes: typeCookiesToBeRemoved) { records in
        let dispatchGroup = DispatchGroup()
        records.forEach { record in
            dispatchGroup.enter()
            WKWebsiteDataStore.default().removeData(ofTypes: record.dataTypes, for: [record], completionHandler: {
                dispatchGroup.leave()
            })
        }
        dispatchGroup.notify(queue: DispatchQueue.main) {
            print("All cookies removed.")
            completion()
        }
    }
}
I was able to fix this crash by calling getAllCookies asynchronously in the main thread.
func cookiesDidChange(in cookieStore: WKHTTPCookieStore) {
     DispatchQueue.main.async {
         cookieStore.getAllCookies { (cookies) in
             //Code here...
         })
     }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With