iosiphonensurlconnectionswizzling

NSURLConnection swizzling in iOS


I have created NSURLConnection method swizzling for sendSynchronousRequest, however its not been working below is my code. Whenever I am trying to call this from main function, its crashing.

    let originalRequestSyncSelector = #selector(self.sendSynchronousRequest(_:returning:))
    let swizzledRequestSyncSelector = #selector(self.swizzSendSynchronousRequest(_:returning:error:))

    let originalSyncRequestMethod = class_getClassMethod(self, originalRequestSyncSelector)
    let swizzledSyncRequestMethod = class_getInstanceMethod(self, swizzledRequestSyncSelector)

    if originalSyncRequestMethod == nil || swizzledSyncRequestMethod == nil {
      return
    }
    method_exchangeImplementations(originalSyncRequestMethod!, swizzledSyncRequestMethod!)


  @objc func swizzSendSynchronousRequest(_ request: NSURLRequest?, returning response: URLResponse?, error: Error?) -> Data? {

    var tempData: Data?
    print("Inside Sync Swizzled Method")
    print("------------\(String(describing: request))")
    return tempData
  }

Solution

  • There's a couple of things that might cause problems:

    1. The method signatures are different. The original implementation does not have an error parameter, but is a throwing function. Also, unlike your swizzled method, sendSynchronousRequest takes a URLRequest instead of an NSURLRequest.
    2. You're swizzling an instance and a class method. I'm not sure how (or if) this can be made to work.
    3. sendSynchronousRequest is exposed to Swift as a throwing function. It works if your swizzled function is not marked with throws, but throwing functions can't be exposed to ObjC and might cause issues when being swizzled.

    Here is some working code for a playground:

    import Foundation
    import ObjectiveC
    
    extension NSURLConnection {
        @objc static func swizzSendSynchronousRequest(_ request: URLRequest?, returning response: URLResponse?) -> Data? {
    
            print("Inside Sync Swizzled Method")
            print("------------\(String(describing: request))")
            return Data()
        }
    }
    
    let originalRequestSyncSelector = #selector(NSURLConnection.sendSynchronousRequest(_:returning:))
    let swizzledRequestSyncSelector = #selector(NSURLConnection.swizzSendSynchronousRequest(_:returning:))
    
    let originalSyncRequestMethod = class_getClassMethod(NSURLConnection.self, originalRequestSyncSelector)
    let swizzledSyncRequestMethod = class_getClassMethod(NSURLConnection.self, swizzledRequestSyncSelector)
    
    method_exchangeImplementations(originalSyncRequestMethod!, swizzledSyncRequestMethod!)
    
    
    let request = URLRequest(url: URL(string: "https://example.com")!)
    do {
        let data = try NSURLConnection.sendSynchronousRequest(request, returning: nil)
        print(data)
    } catch {
        print(error)
    }
    

    Regardless, I think it would be a better idea to perform swizzling in Objective-C. There's much better documentation on how to do that, and you can avoid pitfalls in Swift <-> Objective-C bridging magic.