4
votes

I'm trying to use a required convenience failable initializer. Here's the code I'm working with:

public init(authState: OIDAuthState, config: [String: String], accessibility: CFString = kSecAttrAccessibleWhenUnlockedThisDeviceOnly) throws {
        self.authState = authState
        self.config = config
        self.accessibility = accessibility

        super.init()

        KeycloakAuth.configuration = config
    }

public required convenience init?(coder aDecoder: NSCoder) {
        try? self.init(authState:     aDecoder.decodeObject(forKey: "authState")     as! OIDAuthState,
                       config:        aDecoder.decodeObject(forKey: "config")        as! [String: String],
                       accessibility: aDecoder.decodeObject(forKey: "accessibility") as! CFString)
    }

I get the error 'self' used before 'self.init' call on the public required... line, and the same error again on the try? self.init(... line.

I've looked at a few other related questions on Stack Overflow. Namely these:

  1. iOS Swift convenience initializer self used before self.init called
  2. 'self' used before self.init call error while using NSCoding on a custom class

So I arranged my convenience init accordingly to try and return nil if there were any problems:

public required convenience init?(coder aDecoder: NSCoder) {
        guard
            let authState     = aDecoder.decodeObject(forKey: "authState")     as? OIDAuthState,
            let config        = aDecoder.decodeObject(forKey: "config")        as? [String: String]
        else {
            print("KeycloakTokenManager: There was an error intializing authState or config")
            return nil
        }

        let accessibility = aDecoder.decodeObject(forKey: "accessibility") as! CFString

        try? self.init(authState: authState, config: config, accessibility: accessibility)
    }

But I'm getting same the errors on the same code (initializer, and call to self.init). Interestingly, my project built fine on Swift 4, but I haven't heard anything about this being a bug is Swift 5. How can I get rid of this error?

2
Unrelated but why is init(authState marked as throws? Actually It doesn't throw any errors at all. - vadian
Good question. This was a port from another code base, so I missed that detail. Took it out and everything is fine. - jerney
FWIW, the above code does not produce the error you say it does for me in Xcode 10.2 (10E125). It must be something that you removed in the process of cleaning up the question for posting here. - Rob
What version of swift are you running? - jerney
Swift 5 in Xcode version 10.2 (10E125). I don’t see any use of self before the call to self.init(...). - Rob

2 Answers

3
votes

A solution is not to call the designated initializer

public required init?(coder aDecoder: NSCoder) {
    guard
        let authState     = aDecoder.decodeObject(forKey: "authState")     as? OIDAuthState,
        let config        = aDecoder.decodeObject(forKey: "config")        as? [String: String]
    else {
        print("KeycloakTokenManager: There was an error intializing authState or config")
        return nil
    }

    self.authState = authState
    self.config = config 
    self.accessibility =  aDecoder.decodeObject(forKey: "accessibility") as! CFString

    super.init()

    KeycloakAuth.configuration = config
}
0
votes

Here's another solution I just found that also works. Because the initializer doesn't throw, the final code can be like this:

public init(authState: OIDAuthState, config: [String: String], accessibility: CFString = kSecAttrAccessibleWhenUnlockedThisDeviceOnly) {
    self.authState = authState
    self.config = config
    self.accessibility = accessibility

    super.init()

    KeycloakAuth.configuration = config
}

public required convenience init?(coder aDecoder: NSCoder) {
    self.init(authState:     aDecoder.decodeObject(forKey: "authState")     as! OIDAuthState,
              config:        aDecoder.decodeObject(forKey: "config")        as! [String: String],
              accessibility: aDecoder.decodeObject(forKey: "accessibility") as! CFString)
}