Is it possible to initialize properties at the beginning of a class? - swift

I am writing my project and wondered.
When I read literature or watch videos, I see that this is bad practice. Why? Is this bad for the system?
What is the difference between this
class SomeClass {
var someView = SomeView()
var someViewModel = SomeViewModel()
// ...
}
and this
class SomeClass {
var someView: SomeView!
var someViewModel: SomeViewModel?
// ...
}
How to get used to it better?

You have to initialize all instance properties somehow. And you have to do it right up front, either in the declaration line or in your init method.
But what if you don't actually have the initial value until later, like in viewDidLoad? Then it is silly to supply a real heavyweight value only to replace it later:
var v = MyView()
override func viewDidLoad() {
self.v = // get _real_ MyView and assign it in place of that
}
Instead, we use an Optional to mark the fact that we have no value yet; until we obtain and assign one, it will be nil:
var v : MyView? // means it is initially `nil`
override func viewDidLoad() {
self.v = // get _real_ MyView and assign it to our property
}

There's nothing wrong with the first way (which is called a "default property value", by the way), and in fact, often times it's preferable. But of course, the devil is in the details:
How would the initialization of a SomeViewModel work? Without acess the initializer parameters of SomeClass, you're stuck with only being able to construct an instance from a parameter-less init, like SomeViewModel(). What exactly could that do? Suppose it was a person view model, and you had PersonViewModel(). What person? Whats their name? What will this default value do at all?
It's not a great pattern if it requires overwriting the default value with some other value in the initializer
It initializes the value up-front, where sometimes a lazy or computed value might be more appropriate.

Related

Swift class ivars - instantiation time

Lets say you have a class.
class SomeClass: UIViewController {
private var _dataSource = SomeOtherClass()
init() {
// --> breakpoint
super.init(nibName: nil, bundle: nil)
}
}
At the breakpoint in init - I can see that _dataSource is already allocated. At what point is the class actually getting allocated, and when do the class iVars also get init?
Are there some docs on what happens under the hood?
And what's the difference compared to this?
class SomeClass: UIViewController {
private var _dataSource: SomeOtherClass!
init() {
_dataSource = SomeOtherClass()
super.init(nibName: nil, bundle: nil)
}
}
Thanks,
p.s. Coming from Obj-C to Swift world.
In swift, iVar's are assimilated with properties, so I'll be using the term "property" in the rest of the answer.
Property initialization code runs right before running the code of any initializer, as part of a compiler-genarated "initializer". One method to find out about this is by initializing the property from a function, and set a breakpoint inside that function:
class SomeClass: UIViewController {
private var _dataSource = {
// --> breakpoint
return SomeOtherClass()
}()
init() {
// --> breakpoint
super.init(nibName: nil, bundle: nil)
}
}
You should see a stacktrace like this
BTW, in your second code snippet you don't need to declare the property as implicitly unwrapped, as long as you give it a value either inline or within the initializer the compiler will be happy.
The answer to your second question is that, at runtime, there are no differences between assigning the property the value inline (at the declaration site), or within the initializer. There are other advantages/disadvantages with both approaches, at compile time, like with multiple initializers giving a value inline avoids code duplication.
Swift does not expose allocation or bare iVars. Only init methods and properties are exposed.
Do not make assumptions about how allocation is done or how iVars are initialized. The compiler is free to optimize those details in any way it chooses.
Declaration of properties are straight forward.
class SomeClass: UIViewController {
// This is an instance property. It must be set in init(…) before the call to
// super.init(…).
var _dataSource1: SomeOtherClass
// This is an instance property that is set in its declaration. A way to think
// of this is to imagine it being set after allocation and before init(…) is
// called.
var _dataSource2 = SomeOtherClass()
// This is a lazy property. Instead of being set in init(…), lazy properties
// are set when they are first used.
lazy var _dataSource3 = SomeOtherClass()
// This is a class property accessed with SomeClass._dataSource4. All static
// properties are lazy. So, they are set on first use.
static var _dataSource4 = SomeOtherClass()
}
Initialization in Swift is a two-phase process. In the first phase, each property is assigned an initial value. Whereas in Objective-C this meant assigning zeroes and null values, in Swift it also assigns default property values, like here
class SomeClass: UIViewController {
private var _dataSource = SomeOtherClass()
}
Second phase is when initializers are being called. That is why by the time your breakpoint triggers, the default property value for _dataSource has already been assigned.
The difference between
private var _dataSource = SomeOtherClass()
and
private var _dataSource: SomeOtherClass!
is that in the second example you are only declaring a property, which needs to be initialized somewhere further along the road, whereas in the first one you declare it and provide it with a default value.
Note that in the first example, you don't need to explicitly specify the type of the property, because the compiler can infer it from the default value.

How do I pass arguments to an objects function without a crash

I'm trying to move the logic of a viewController to a view model but for some reason it always crashes, saying unexpectedly found nil while unwrapping an Optional value. I constantly get this error no matter what code I try to move, so I must be doing something fundamentally wrong. Here's a sample of the code I have in the viewController:
var recipesViewModel: RecipesViewModel! //VIEW MODEL CLASS REFERENCE
var recipeCategory = recipesViewModel.transformToUpperCase(dataRecieverStringRecipeView: "testString")
Then in the view modelclass:
func transformToUpperCase(dataRecieverStringRecipeView: String) -> String {
var recipeCategory = dataRecieverStringRecipeView
var prefixRecipeCategory = recipeCategory.prefix(1).uppercased()
var dropFirstRecipeCategory = recipeCategory.dropFirst()
var upperCasedRecipeCategory = prefixRecipeCategory + dropFirstRecipeCategory
return upperCasedRecipeCategory
}
...it translates the string to have an uppercase letter as its first letter.
The code works perfectly fine when everything is in the view model, but as soon I move it to another class and call the function through an object it crashes. What am I missing?
You need to fix your view model class reference
Line to change:
var recipesViewModel: RecipesViewModel!
Replacement Line:
var recipesViewModel = RecipesViewModel()
This line of code will properly declare/create recipesViewModel as an object of class RecipesViewModel.
Hope that resolves your inquiry!
var recipesViewModel: RecipesViewModel! does not instantiate a new object. Put the category function back in your RecipesViewModel class, then change the variable declaration to:
Declaration
var recipesViewModel: RecipesViewModel!
Later on
recipesViewModel = RecipesViewModel() //or whatever initializer you need.
Edit
As rmaddy pointed out: you probably can ditch the ! and use var recipesViewModel: RecipesViewModel = RecipesViewModel() instead if you don't have any failable initializers or this isn't a runtime injected property and just put it as a 1-liner:
var recipesViewModel: RecipesViewModel = RecipesViewModel()

Swift Setting Non-Primitive Members with get/set Notation

I have a superclass where a vector is being created as a member variable.
class GameScene: SKScene, SKPhysicsContactDelegate {
var floatingBlockPositions: [CGPoint] = [CGPointMake(130.0, 70.0)]
}
I am overriding it in a subclass like so:
class StreetFight: GameScene {
override var floatingBlockPositions: [CGPoint] {
get {
return [CGPointMake(400.0, 70.0), CGPointMake(250.0, 95.0)]
}
set (newValue) {
newValue
}
}
}
I have a class method that mutates it to set another parameter.
temp = 0
for _ in floatingBlockPositions {
floatingBlockPositions[temp].y = floatingBlockPositions[temp].y + positionsForFirstBlock.y + (groundBlockSize.height / 2)
let aerialBlock = SKSpriteNode(imageNamed: "aerialBlock")
aerialBlock.position = floatingBlockPositions[temp]
temp++
}
After debugging, I see that the setter in the subclass is being called, but it is not being set properly because the equals operator in the function is trying to set only one part of one of the CGPoints in the vector, which the setter is not set up to handle, no pun intended. How can I write the setter in the subclass so it recognizes what part of floatingBlockPositions needs to be set and sets it. If possible, should I overload the method, or is there a better way to do it?
You've changed floatingBlockPositions from a stored property to a computed variable (because it has a get). Therefore, you cannot store a new value for it. However, you can create a separate private var and set that, and then in get, if that is set, return that new value.

Can't permanently change instance property from method?

Ok, I'm probably missing something super basic. I have an instance property called currentValue, initialized to be a String.
class ViewController: NSViewController {
var currentValue = ""
// ...
func getNewValue() {
currentValue = computeNewValue()
aLabel.stringValue = currentValue
}
func calledLater() {
println("\(currentValue)")
}
}
When I call getNewValue(), the label updates correctly.
But, when I call calledLater(), the currentValue is "reset" to an empty string.
At first I thought it was a weak storage thing but Swift apparently defaults to strong storage?
I tested this by initializing currentValue to "a" and, again, the label updates correctly, but when I get the variable later it returns "a".
I feel like I'm missing something but can't word it in a way that will let me do correct research.
You might be calling the methods on different instances of your 'ViewController' class.
(A typical scenario is when loading controllers from a storyboard; it "looks" like instances in the storyboard, but they are really just blueprints.)

What is the purpose of willSet and didSet in Swift?

Swift has a property declaration syntax very similar to C#'s:
var foo: Int {
get { return getFoo() }
set { setFoo(newValue) }
}
However, it also has willSet and didSet actions. These are called before and after the setter is called, respectively. What is their purpose, considering that you could just have the same code inside the setter?
The point seems to be that sometimes, you need a property that has automatic storage and some behavior, for instance to notify other objects that the property just changed. When all you have is get/set, you need another field to hold the value. With willSet and didSet, you can take action when the value is modified without needing another field. For instance, in that example:
class Foo {
var myProperty: Int = 0 {
didSet {
print("The value of myProperty changed from \(oldValue) to \(myProperty)")
}
}
}
myProperty prints its old and new value every time it is modified. With just getters and setters, I would need this instead:
class Foo {
var myPropertyValue: Int = 0
var myProperty: Int {
get { return myPropertyValue }
set {
print("The value of myProperty changed from \(myPropertyValue) to \(newValue)")
myPropertyValue = newValue
}
}
}
So willSet and didSet represent an economy of a couple of lines, and less noise in the field list.
My understanding is that set and get are for computed properties (no backing from stored properties)
if you are coming from an Objective-C bare in mind that the naming conventions have changed. In Swift an iVar or instance variable is named stored property
Example 1 (read only property) - with warning:
var test : Int {
get {
return test
}
}
This will result in a warning because this results in a recursive function call (the getter calls itself).The warning in this case is "Attempting to modify 'test' within its own getter".
Example 2. Conditional read/write - with warning
var test : Int {
get {
return test
}
set (aNewValue) {
//I've contrived some condition on which this property can be set
//(prevents same value being set)
if (aNewValue != test) {
test = aNewValue
}
}
}
Similar problem - you cannot do this as it's recursively calling the setter.
Also, note this code will not complain about no initialisers as there is no stored property to initialise.
Example 3. read/write computed property - with backing store
Here is a pattern that allows conditional setting of an actual stored property
//True model data
var _test : Int = 0
var test : Int {
get {
return _test
}
set (aNewValue) {
//I've contrived some condition on which this property can be set
if (aNewValue != test) {
_test = aNewValue
}
}
}
Note The actual data is called _test (although it could be any data or combination of data)
Note also the need to provide an initial value (alternatively you need to use an init method) because _test is actually an instance variable
Example 4. Using will and did set
//True model data
var _test : Int = 0 {
//First this
willSet {
println("Old value is \(_test), new value is \(newValue)")
}
//value is set
//Finaly this
didSet {
println("Old value is \(oldValue), new value is \(_test)")
}
}
var test : Int {
get {
return _test
}
set (aNewValue) {
//I've contrived some condition on which this property can be set
if (aNewValue != test) {
_test = aNewValue
}
}
}
Here we see willSet and didSet intercepting a change in an actual stored property.
This is useful for sending notifications, synchronisation etc... (see example below)
Example 5. Concrete Example - ViewController Container
//Underlying instance variable (would ideally be private)
var _childVC : UIViewController? {
willSet {
//REMOVE OLD VC
println("Property will set")
if (_childVC != nil) {
_childVC!.willMoveToParentViewController(nil)
self.setOverrideTraitCollection(nil, forChildViewController: _childVC)
_childVC!.view.removeFromSuperview()
_childVC!.removeFromParentViewController()
}
if (newValue) {
self.addChildViewController(newValue)
}
}
//I can't see a way to 'stop' the value being set to the same controller - hence the computed property
didSet {
//ADD NEW VC
println("Property did set")
if (_childVC) {
// var views = NSDictionaryOfVariableBindings(self.view) .. NOT YET SUPPORTED (NSDictionary bridging not yet available)
//Add subviews + constraints
_childVC!.view.setTranslatesAutoresizingMaskIntoConstraints(false) //For now - until I add my own constraints
self.view.addSubview(_childVC!.view)
let views = ["view" : _childVC!.view] as NSMutableDictionary
let layoutOpts = NSLayoutFormatOptions(0)
let lc1 : AnyObject[] = NSLayoutConstraint.constraintsWithVisualFormat("|[view]|", options: layoutOpts, metrics: NSDictionary(), views: views)
let lc2 : AnyObject[] = NSLayoutConstraint.constraintsWithVisualFormat("V:|[view]|", options: layoutOpts, metrics: NSDictionary(), views: views)
self.view.addConstraints(lc1)
self.view.addConstraints(lc2)
//Forward messages to child
_childVC!.didMoveToParentViewController(self)
}
}
}
//Computed property - this is the property that must be used to prevent setting the same value twice
//unless there is another way of doing this?
var childVC : UIViewController? {
get {
return _childVC
}
set(suggestedVC) {
if (suggestedVC != _childVC) {
_childVC = suggestedVC
}
}
}
Note the use of BOTH computed and stored properties. I've used a computed property to prevent setting the same value twice (to avoid bad things happening!); I've used willSet and didSet to forward notifications to viewControllers (see UIViewController documentation and info on viewController containers)
If I've made a mistake anywhere, please edit to fix it!
You can also use the didSet to set the variable to a different value. This does not cause the observer to be called again as stated in Properties guide. For example, it is useful when you want to limit the value as below:
let minValue = 1
var value = 1 {
didSet {
if value < minValue {
value = minValue
}
}
}
value = -10 // value is minValue now.
These are called Property Observers:
Property observers observe and respond to changes in a property’s
value. Property observers are called every time a property’s value is
set, even if the new value is the same as the property’s current
value.
Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/ca/jEUH0.l
I suspect it's to allow for things we would traditionally do with KVO such as data binding with UI elements, or triggering side effects of changing a property, triggering a sync process, background processing, etc, etc.
NOTE
willSet and didSet observers are not called when a property is set in an initializer before delegation takes place
The many well-written existing answers cover the question well, but I'll mention, in some detail, an addition that I believe is worth covering.
The willSet and didSet property observers can be used to call delegates, e.g., for class properties that are only ever updated by user interaction, but where you want to avoid calling the delegate at object initialization.
I'll cite Klaas up-voted comment to the accepted answer:
willSet and didSet observers are not called when a property is first
initialized. They are only called when the property’s value is set
outside of an initialization context.
This is a quite neat as it means e.g. the didSet property is a good choice of launch point for delegate callbacks & functions, for your own custom classes.
As an example, consider some custom user control object, with some key property value (e.g. position in rating control), implemented as a subclass of UIView:
// CustomUserControl.swift
protocol CustomUserControlDelegate {
func didChangeValue(value: Int)
// func didChangeValue(newValue: Int, oldValue: Int)
// func didChangeValue(customUserControl: CustomUserControl)
// ... other more sophisticated delegate functions
}
class CustomUserControl: UIView {
// Properties
// ...
private var value = 0 {
didSet {
// Possibly do something ...
// Call delegate.
delegate?.didChangeValue(value)
// delegate?.didChangeValue(value, oldValue: oldValue)
// delegate?.didChangeValue(self)
}
}
var delegate: CustomUserControlDelegate?
// Initialization
required init?(...) {
// Initialise something ...
// E.g. 'value = 1' would not call didSet at this point
}
// ... some methods/actions associated with your user control.
}
After which your delegate functions can be used in, say, some view controller to observe key changes in the model for CustomViewController, much like you'd use the inherent delegate functions of the UITextFieldDelegate for UITextField objects (e.g. textFieldDidEndEditing(...)).
For this simple example, use a delegate callback from the didSet of the class property value to tell a view controller that one of it's outlets have had associated model update:
// ViewController.swift
Import UIKit
// ...
class ViewController: UIViewController, CustomUserControlDelegate {
// Properties
// ...
#IBOutlet weak var customUserControl: CustomUserControl!
override func viewDidLoad() {
super.viewDidLoad()
// ...
// Custom user control, handle through delegate callbacks.
customUserControl = self
}
// ...
// CustomUserControlDelegate
func didChangeValue(value: Int) {
// do some stuff with 'value' ...
}
// func didChangeValue(newValue: Int, oldValue: Int) {
// do some stuff with new as well as old 'value' ...
// custom transitions? :)
//}
//func didChangeValue(customUserControl: CustomUserControl) {
// // Do more advanced stuff ...
//}
}
Here, the value property has been encapsulated, but generally: in situations like these, be careful not to update the value property of the customUserControl object in the scope of the associated delegate function (here: didChangeValue()) in the view controller, or you'll end up with infinite recursion.
The willSet and didSet observers for the properties whenever the property is assigned a new value. This is true even if the new value is the same as the current value.
And note that willSet needs a parameter name to work around, on the other hand, didSet does not.
The didSet observer is called after the value of property is updated. It compares against the old value. If the total number of steps has increased, a message is printed to indicate how many new steps have been taken. The didSet observer does not provide a custom parameter name for the old value, and the default name of oldValue is used instead.
Getter and setter are sometimes too heavy to implement just to observe proper value changes. Usually this needs extra temporary variable handling and extra checks, and you will want to avoid even those tiny labour if you write hundreds of getters and setters. These stuffs are for the situation.
In your own (base) class, willSet and didSet are quite reduntant , as you could instead define a calculated property (i.e get- and set- methods) that access a _propertyVariable and does the desired pre- and post- prosessing.
If, however, you override a class where the property is already defined, then the willSet and didSet are useful and not redundant!
One thing where didSet is really handy is when you use outlets to add additional configuration.
#IBOutlet weak var loginOrSignupButton: UIButton! {
didSet {
let title = NSLocalizedString("signup_required_button")
loginOrSignupButton.setTitle(title, for: .normal)
loginOrSignupButton.setTitle(title, for: .highlighted)
}
I do not know C#, but with a little guesswork I think I understand what
foo : int {
get { return getFoo(); }
set { setFoo(newValue); }
}
does. It looks very similar to what you have in Swift, but it's not the same: in Swift you do not have the getFoo and setFoo. That is not a little difference: it means you do not have any underlying storage for your value.
Swift has stored and computed properties.
A computed property has get and may have set (if it's writable). But the code in the getter and setter, if they need to actually store some data, must do it in other properties. There is no backing storage.
A stored property, on the other hand, does have backing storage. But it does not have get and set. Instead it has willSet and didSet which you can use to observe variable changes and, eventually, trigger side effects and/or modify the stored value. You do not have willSet and didSet for computed properties, and you do not need them because for computed properties you can use the code in set to control changes.