All Stored Properties of a Class Instance Must Be Initialized Before Returning Nil from an Initializer

All stored properties of a class instance must be initialized before throwing from an initializer

Is this problem caused by instantiating a variable that throws an error, inside a function that throws an error?

Yes. You are doing a do...catch inside a non-failable initializer. This means there are circumstances remaining where initialization might not correctly take place. You must finish initialization before you can throw. For example, in the code you've shown, if you add super.init() as the first line of the initializer, all is well, because you have finished initialization before throwing.

You might be more comfortable, if initialization can fail, writing a failable initializer (init?).

EDIT: Please note that in starting in Swift 2.2, this requirement will be lifted: it will be legal to throw before finishing initialization.

Use of 'self' in method call 'f' before all stored properties are initialized

It doesn't mean that if you haven't written self with function call f then it will not reference to self as of f is still instance method, so with init it must be called after all the instance properties are initialized means after the the instance of class is initialized. So simply call f() after you initialized the instance property a.

class A {
var a: String
init(a: String) {
self.a = a
f()
}

func f() {
print("HeHe")
}
}

Swift Failable Initializer Can’t Return Nil?

Apparently it’s a bug that’ll be fixed as of Swift 2.2 https://stackoverflow.com/a/26497229/5792198

In the meantime, you can initialize all the properties with dummy data and set a flag. At the end of all the init code check the flag and return nil.

Initialising member to class function causes 'self' used in method call error

It might be better to not use a Bool, but rather a nested Enum, which is also more extendible if you wanna add some other modes of haptic feedback later on.

I have a generalized solution for a generalized problem of your question. So either you do:


public class FunctionOwner {

private let mode: Mode

public init(`do` mode: Mode = .default) {
self.mode = mode
}
}

public extension FunctionOwner {

enum Mode {
case foo, bar
}

func fooOrBar() {
switch mode {
case .foo: foo()
case .bar: bar()
}
}
}

private extension FunctionOwner {
func foo() {
print("doing foo")
}

func bar() {
print("doing bar")
}
}

public extension FunctionOwner.Mode {
static var `default`: FunctionOwner.Mode {
return .foo
}
}

// USAGE
FunctionOwner(do: .bar).fooOrBar() // prints "doing foo"
FunctionOwner(do: .foo).fooOrBar() // prints "doing bar"

Or if you for some reason do want to keep the stored Mode, you can do this (might be relevant for your actual question on how you do a workaround of referencing self in the init.):

public class FunctionOwner {

private let _function: (FunctionOwner) -> Void

public init(`do` mode: Mode = .default) {
_function = { functionOwner in
switch mode {
case .foo: functionOwner.foo()
case .bar: functionOwner.bar()
}
}
}
}

public extension FunctionOwner {

enum Mode {
case foo, bar
}

func fooOrBar() {
_function(self)
}
}

// The rest of the code is the same as the example above

Swift initialization stored property outside init method of class issue

You also can do it by using this code:

var fanMode: FanMode = {
if let unwrappedFanMode = userDefaults.valueForKey(Consts.kStoredFanMode) as? FanMode {
return unwrappedFanMode
} else {
return FanMode()//some default implementation
}
}()

It is readable as You want.

Swift Failable Initializer with SwiftyJSON

Failable Initializers for Classes:

"For classes, however, a failable initializer can trigger an initialization failure only after all stored properties introduced by that class have been set to an initial value and any initializer delegation has taken place."

So

init?(json: JSON) {
self.id = json["id"].string
self.sortOrder = json["sortOrder"].int
...
if ... { return nil }
}

self' used before all stored properties are initialized

In this case I would suggest you to make the property a variable but hiding it (make it seem like a constant) through a computed property:

class Country {
let name: String

private var _capitalCity: City!
var capitalCity: City {
return _capitalCity
}

init(name: String, capitalName: String) {
self.name = name
self._capitalCity = City(name: capitalName, country: self)
}
}

Custom Decoder error due to self before all stored properties are initialized

You can't call methods on self before stored properties are all initialised because the methods could potentially access an uninitialised property (See also this answer of mine), and who knows what happens then? You can tell Swift that sort and computeLastVideos won't access self at all, by putting the word static:

static func sort(_ videos: [Video]) -> [Video] { ... }
static func computeLastVideos(from videos: [Video]) -> [Video] { ... }

You would also have to put the sorted videos into a temporary variable sortedVideos first, because you can't access self.videos:

let container = try decoder.container(keyedBy: CodingKeys.self)
let videos = try container.decode([Video].self, forKey: .videos)
let sortedVideos = VideoResponse.sort(videos)
self.lastVideos = VideoResponse.computeLastVideos(from: sortedVideos)
self.videos = sortedVideos

If sort and computerLastVideos does access self though, you are out of luck, and have to make videos a non-let, and initialise it first, then change it later. You can't guarantee to Swift that sort and computeLastVideos will only access the initialised part of self.

Best practice to implement a failable initializer in Swift

Update: From the Swift 2.2 Change Log (released March 21, 2016):

Designated class initializers declared as failable or throwing may now return nil or throw an error, respectively, before the object has been fully initialized.


For Swift 2.1 and earlier:

According to Apple's documentation (and your compiler error), a class must initialize all its stored properties before returning nil from a failable initializer:

For classes, however, a failable initializer can trigger an
initialization failure only after all stored properties introduced by
that class have been set to an initial value and any initializer
delegation has taken place.

Note: It actually works fine for structures and enumerations, just not classes.

The suggested way to handle stored properties that can't be initialized before the initializer fails is to declare them as implicitly unwrapped optionals.

Example from the docs:

class Product {
let name: String!
init?(name: String) {
if name.isEmpty { return nil }
self.name = name
}
}

In the example above, the name property of the Product class is
defined as having an implicitly unwrapped optional string type
(String!). Because it is of an optional type, this means that the name
property has a default value of nil before it is assigned a specific
value during initialization. This default value of nil in turn means
that all of the properties introduced by the Product class have a
valid initial value. As a result, the failable initializer for Product
can trigger an initialization failure at the start of the initializer
if it is passed an empty string, before assigning a specific value to
the name property within the initializer.

In your case, however, simply defining userName as a String! does not fix the compile error because you still need to worry about initializing the properties on your base class, NSObject. Luckily, with userName defined as a String!, you can actually call super.init() before you return nil which will init your NSObject base class and fix the compile error.

class User: NSObject {

let userName: String!
let isSuperUser: Bool = false
let someDetails: [String]?

init?(dictionary: NSDictionary) {
super.init()

if let value = dictionary["user_name"] as? String {
self.userName = value
}
else {
return nil
}

if let value: Bool = dictionary["super_user"] as? Bool {
self.isSuperUser = value
}

self.someDetails = dictionary["some_details"] as? Array
}
}


Related Topics



Leave a reply



Submit