I have a Swift class that has a constant ivar (are they called instance constants now?). To set the value to this constant, I need to call an initializer of the desired object and pass itself. However, I am not allowed to as I need to initialize all values first, then call super.init()
and after that I am allowed to access self
. So what to do in this case?
class Broadcaster: NSObject, CBPeripheralManagerDelegate { let broadcastID: NSUUID let bluetoothManager: CBPeripheralManager init(broadcastID: NSUUID) { self.broadcastID = broadcastID let options: Dictionary<NSString, AnyObject> = [ CBPeripheralManagerOptionShowPowerAlertKey: true ] self.bluetoothManager = CBPeripheralManager(delegate: self, queue: nil, options: options) // error: 'self' used before super.init call super.init() } }
An initializer is a special type of function that is used to create an object of a class or struct. In Swift, we use the init() method to create an initializer. For example, class Wall { ... // create an initializer init() { // perform initialization ... } }
Swift init() Initialization is the process of preparing an instance of a class, structure, or enumeration for use. This process involves setting an initial value for each stored property on that instance and performing any other setup or initialization that is required before the new instance is ready for use.
The init() initializer for Bicycle starts by calling super. init(), which calls the default initializer for the Bicycle class's superclass, Vehicle. This ensures that the numberOfWheels inherited property is initialized by Vehicle before Bicycle has the opportunity to modify the property.
Unfortunately, it doesn’t seem to be possible any more to have bluetoothManager
as a constant. Starting from Swift 1.2, in an initializer, constant properties can only assign a value once. This doesn’t allow us to start with a nil
value by declaring it as an optional and change it later in the initialization process. Here’s the updated version with bluetoothManager
as a variable.
class Broadcaster: NSObject, CBPeripheralManagerDelegate { let broadcastID: NSUUID var bluetoothManager: CBPeripheralManager! init(broadcastID: NSUUID) { self.broadcastID = broadcastID super.init() let options: Dictionary<String, AnyObject> = [ CBPeripheralManagerOptionShowPowerAlertKey: true ] self.bluetoothManager = CBPeripheralManager(delegate: self, queue: nil, options: options) } }
You could use implicitly unwrapped optional here (for bluetoothManager
) and assign the value to it after super.init()
:
class Broadcaster: NSObject, CBPeripheralManagerDelegate { let broadcastID: NSUUID let bluetoothManager: CBPeripheralManager! init(broadcastID: NSUUID) { self.broadcastID = broadcastID super.init() let options: Dictionary<NSString, AnyObject> = [ CBPeripheralManagerOptionShowPowerAlertKey: true ] self.bluetoothManager = CBPeripheralManager(delegate: self, queue: nil, options: options) } }
Because bluetoothManager
is an optional, by the time super.init()
is called, all properties are initialized (bluetoothManager
is implicitly initialized with nil
). But because we know that bluetoothManager
will definitely have the value after the class is initialized, we declare it as explicitly unwrapped to avoid checks when using it.
UPDATE
A property can be declared as constant and still be changed in the initializer. One just has to make sure it has a definite value by the time initialization finishes. This is documented in chapter “Modifying Constant Properties During Initialization” of Swift book.
The situation when a property needs to be initialized with a call where self must be passed from not yet fully initialized object is described in chapter “Unowned References and Implicitly Unwrapped Optional Properties.”
How about setting up your bluetoothManager as a @lazy
property and accessing it later on e.g. to startAdvertising
?
@lazy var bluetoothManager: CBPeripheralManager = CBPeripheralManager(delegate: self, queue: nil) init() { ... } func start() { self.bluetoothManager.startAdvertising([ "foo" : "bar" ]) }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With