5

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?

Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
jerney
  • 2,187
  • 1
  • 19
  • 31
  • 1
    Unrelated but why is `init(authState` marked as `throws`? Actually It doesn't throw any errors at all. – vadian Apr 10 '19 at 15:43
  • Good question. This was a port from another code base, so I missed that detail. Took it out and everything is fine. – jerney Apr 10 '19 at 16:15
  • 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 Apr 10 '19 at 17:57
  • What version of swift are you running? – jerney Apr 10 '19 at 17:58
  • Swift 5 in Xcode version 10.2 (10E125). I don’t see any use of `self` before the call to `self.init(...)`. – Rob Apr 10 '19 at 17:58
  • Running the same thing here. That's odd. I just copied the code from here back into my IDE and I'm still getting those errors. Not sure what would cause the discrepancy. – jerney Apr 10 '19 at 18:01

2 Answers2

3

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
}
vadian
  • 274,689
  • 30
  • 353
  • 361
0

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)
}
jerney
  • 2,187
  • 1
  • 19
  • 31