4

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?

Community
  • 1
  • 1
Mark Kang
  • 161
  • 11

1 Answers1

2

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:

  • create a class Helper where you define it as a class function as well
  • create a class Helper where you define it as instance function and create a Helper instance in your class FooBar or create a global one and pass that one in the init as well.
luk2302
  • 55,258
  • 23
  • 97
  • 137
  • To keep more in the spirit of the original code, you could just initialize `baz` at the declaration to be `nil`. This is what's happening on the Objective-C side, after all. This would avoid the initial error you received. – Avi Dec 27 '15 at 13:29
  • @Avi but that would revoke it being a constant. – luk2302 Dec 27 '15 at 13:31
  • How does changing `parseString` into a private class method leave `baz` as a constant? The fact is that it's being assigned to after declaration in any event. That's not a constant. Also, in the original Objective-C, it's not a constant either. – Avi Dec 27 '15 at 13:33
  • 2
    @Avi: You can initialize constants after declaring them in Swift. – Crowman Dec 27 '15 at 13:34
  • @Avi, please look at the code OP posted in his question, baz is still a constant. The only time you are allowed to assign something to it is in the constructor. – luk2302 Dec 27 '15 at 13:35
  • @luk2302: makes good sense. And thanks for the suggestion about moving it out completely. I would generally prefer the first option over the second, since I would rather inject the helper class into FooBar instead of having it create an instance from within. The first option, however, decouples the FooBar from the Helper class. – Mark Kang Dec 30 '15 at 19:58