Working around URLSession delegate bridging issue

We've been tracking an Alamofire issue for a while that involves a very rare crash in the URLSessionDataDelegate method urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data), where the underlying NSData is nil when it's _unconditionallyBridgeFromObjectiveC'd. Multiple Radars have been filed and I even spoke to an Apple engineer at WWDC '17 that said it was likely a rare bug where nil is returned unintentionally. The fix seems easy enough to me (?: [NSData data]) but we've gotten reports for iOS 10 and iOS 11 at least, so it wasn't fixed then. So the question becomes, how to work around it?

Well, surprisingly, marking the Data parameter as Data? actually works: urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data?). However, this leads to a warnings and perhaps unintentional compiler behavior: Parameter of 'urlSession(_:dataTask:didReceive:)' has different optionality than expected by protocol 'URLSessionDataDelegate'. However, I don't think a permanent warning for all Alamofire users in acceptable here, especially since the vast majority have never seen this crash. So conditionalizing the fallback seems like the best option. However, Swift doesn't support the obvious version we'd want:

#if ENABLE_NIL_DATA_WORKAROUND
open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data?) {
    let data = data ?? Data()
#else
open func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
#endif

So before we do the work to create a version that works while minimizing duplication, does anyone have a better idea for a workaround? Additionally, is this fuzzy delegate matching behavior intentional, and will it remain?

Is it possible to check with something like this (untested):

let checkedData = data as Data? ?? data
// or
let checkedData: Data? = data
if checkedData == nil { … }

Data._unconditionallyBridgeFromObjectiveC should return an empty Data() in Swift 4.0 and later (see f88f624 in apple/swift#7429).

Terms of Service

Privacy Policy

Cookie Policy