objective-cswiftdesignated-initializercode-migration

Calling an instance method in designated initializer of a Swift class


In Swift programming language, it is mandatory that

“A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.” source of quote from another s/o

Otherwise, an error similar to the following would be displayed

Property 'self.baz' not initialized at super.init call

I would like to implement the following Objective-C code in Swift:

@interface FooBar : NSObject

@property (nonatomic, readonly) NSString *baz;

@end

@implementataion FooBar

@synthesize baz = _baz;

- (instancetype)initWithString:(NSString *)string {
    self = [super init];
    if (self) {
        _baz = [self parseString:string];
    }
    return self;
}

- (NSString *)parseString:(NSString *)string {
    // Perform something on the original string
    return ...;
}

@end

The following implementation would have thrown a compiler error as described above:

class FooBar: NSObject {
    let baz: NSString?

    init(string: NSString?) {
        super.init()
        baz = parseString(string)
    }

    private func parseString(string: NSString?) -> NSString? {
        // Perform something on the original string
        return ...;
    }
}

And if I did the following, I would get an error that says

Use of self in method call 'parseString' before super.init initialises self

class FooBar: NSObject {
    let baz: NSString?

    init(string: NSString?) {
        baz = parseString(string)
        super.init()
    }

    private func parseString(string: NSString?) -> NSString? {
        // Perform something on the original string
        return string;
    }
}

So my solution, presently, is to use a private class method:

class FooBar: NSObject {
    let baz: NSString?

    init(string: NSString?) {
        baz = FooBar.parseString(string)
        super.init()
    }

    private class func parseString(string: NSString?) -> NSString? {
        // Perform something on the original string
        return ...;
    }
}

My question is whether there is a better way to achieve the same or is private class method the best approach?


Solution

  • As you correctly mentioned the compiler complains about the "Use of self in method call 'parseString' before super.init initialises self". The reason for that is that the compiler has to ensure that you do not access anything on self before it did finish initializing. Because the compiler does not want to (or even can not) check every outgoing method call before the initialization finished it simply disallows any usage of self before it finished.

    Luckily your method parseString does not want to change or access any property. Therefore you can and should make it a non-instance function. If it has nothing do with the instance of FooBar, why should it have be only available on an instance of FooBar?

    You can simply make it a static or class function as you already did, which is perfectly fine.

    But I would go even further and move it completely out of FooBar. There are a few options for that: