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
Alamofire Https Change in 10.3
Are Boolean Reads and Writes Guaranteed Atomic in Swift
Swift Cannot Invoke '*' with an Argument List of Type '(Int, Int)'
How to Make Nsbutton Title at Bottom and Centered
Convenience Failable Initializers Assign Self
iOS 8 Sdk, Swift, Mapkit Drawing a Route
Converting Cmtime to String Is Wrong Value Return
Strange Behavior with Swift Compiler
How to Write to a Variable from Within The Firebase Getdocument Function (Swift)
Detect What Is The Location of The Tap/Press Inside UIbutton Inside Sender Action Method
Create an Array of Protocols with Constrained Associated Types
Member Operator '==' Must Have at Least One Argument of Type
How to Change The Data Type in Realm Database
How to Get Access Token from Instagram API
How to Unwrap Optional<Optional<T>> in Swift 1.2
Pause Spritekit Scene When Iad Is Clicked in Swift