Blog

Swift & AFNetworking 2.2.4 Part 2

I recently published a post about why your app and Xcode crashes when using AFNetworking 2.2.4 with Swift.

I received feedback from readers suggesting different solutions to this problem. After some discussions we have found out, in more detail, why it crashes and a better way of solving the problem.

The difference between Objective-C and Swift initializersIn my previous post we found that the designated initializer in AFHTTPSessionManager is the init(baseURL:sessionConfiguration:) method.

We found this by checking the error message in the logs when Xcode kept crashing when launching the app. Another way to find the designated initializer is to look for the method with the most coverage. In many cases it is the method that takes the most arguments.

The designated initializer is also the init method that invokes the superclass in a message to super. If you have access to the implementation of the superclass you can look for the message to super to determine if it is a designated initializer or a convenience initializer.

The difference between Objective-C and Swift initializers is that in Objective-C all subclasses inherits their superclass initializers by default. Meaning it is safe to call a superclass convenience initializer from your subclass.

In Swift this is no longer possible because subclasses do not inherit their superclass initializers by default.

In the example in my previous post we had a subclass of AFHTTPSessionManager where we invoked a superclass convenience initializer and not the designated initializer:

class SessionManager: AFHTTPSessionManager { init(baseURL url: NSURL!) { super.init(baseURL: url) requestSerializer = AFJSONRequestSerializer(); responseSerializer = AFJSONResponseSerializer(); }}

As we have learnt this is not possible in Swift because the superclass initializers is not inherited to my subclass by default.

Meaning my application will crash and, unfortunately, Xcode will also crash at the same time because the initializer I call does not exist.

A better solution

Instead of using the workaround from my previous post, where we override the designated initializer of the superclass, we can just call it from our subclass with a message to super:

class SessionManager: AFHTTPSessionManager { init(baseURL url: NSURL!) { super.init(baseURL: url, sessionConfiguration: nil) requestSerializer = AFJSONRequestSerializer(); responseSerializer = AFJSONResponseSerializer(); }}

As described in the iBook The Swift Programming Language, the rules of initializer chaining is:

  • Designated initializers must call a designated initializer from their immediate superclass.
  • Convenience initializers must call another initializer available in the same class.
  • Convenience initializers must ultimately end up calling a designated initializer.

How to clarify the distinction of a designated initializer and a convenience initializer?

In Swift you add the keyword convenience to your init method to destinct it from your designated initializer.

Let’s look at NSURL as an example:

convenience init(fileURLWithPath path: String!)convenience init(string URLString: String!)init(string URLString: String!, relativeToURL baseURL: NSURL!)

This would not be as easy to in Objective-C a couple of months ago, but now we have a new macro for it: NS_DESIGNATED_INITIALIZER

By using this macro NSURL in Objective-C would look like this:

- (id)initFileURLWithPath:(NSString *)path;- (id)initWithString:(NSString *)URLString;- (id)initWithString:(NSString *)URLString relativeToURL:(NSURL *)baseURL NS_DESIGNATED_INITIALIZER;

With this macro we will know in the subclass which initializer to message with super.

It is especially helpful when the subclass is a Swift class and the superclass is an Objective-C class. It is also helpful when you integrate a framework where you only have access to the header files. Because then you can quickly look for the macro and know which init method to call from your subclass.

Please make good use of this new macro, especially if you write frameworks in Objective-C. It will make the world a better place!