If I have property like:
var animatedImagesView:JSAnimatedImagesView?
And eventually it gets initialized at the proper time, do I need to just keep using ! to unwrap it ad nauseum when I want to do something to it? For instance:
self.animatedImagesView!.reloadData()
Usually I unwrap optionals like:
if let dailySleepTime:AnyObject = uw_JSON["daily_sleep_time"] {
self.settings.dailySleepTime = dailySleepTime as String
} else {
log.warning("\n Error finding attr in \(request)\n")
}
but I can't just go around casting my properties to constants in the same way right? I'm not complaining, I'm just wondering if I'm using the exclamation point correctly.
One option is to define animatedImageView as an implicitly unwrapped optional to begin with:
var animatedImagesView: JSAnimatedImagesView!
This is common when dealing with Cocoa .nib objects in Interface Builder, because the View can't be initialized until it is unarchived from the .nib, but you know that it will always be initialized before you use it.
I hate using the ! in general, because it is a runtime error just waiting to happen, but IB objects are one of the few places where its use seems both legitimate and necessary. Otherwise, your two other options are the ones that you have already found - unwrapping it every time using if let... (which is safest, but a pain in the a**), or using the ! every time that you use it, which isn't any safer than just declaring it using ! to begin with.
If you initialize it at the correct time (for example in any of the lifecycle methods, or in the constructor for that matter) you could just use ! instead of ? for the property declaration. I myself usually only use ? for stuff which I dont necessarily know will have a value at all times during my lifecycle.
If the latter is the case for you as well, you need to continue using ? since you can't guarantee you have a value stored. And you should also use the if let myVar = myVar { .. } syntax if you do multiple calls to the variable.
Instead, declare it as follows:
var animatedImagesView:JSAnimatedImagesView!
That implicitly unwrapped optional will obviate the need to constantly "bang" dereference it, but be sure it is never nil at any time accessed.
It's a great way to specify IBOutlets you know will always be initialized from the nib or storyboard before you use them.
Related
I have a swift UIViewController in which there is an array of data type ( e.g. countries/person/ bank details) fetched from network service asynchronously in viewDidLoad. This array remains constant once fetched. Hence I want to enforce it using let keyword.
I don't have any value when UIViewController is initialized. Hence it gives me compile time error for not initializing it.
If I declare it as optional with '?' , I have to use if-let/ guard let or optional chaining to use it. I don't want to clutter the code with unwrapping.
What are my options to declare a variable as constant but initialized later in execution without making it an optional variable?
"Constant once fetched" is not constant. There is some period of time when it's not set, and some point later when it is. The fact that it's fetched from the network means that it may fail, so your code has to deal with that (i.e. it may never be set). The right tool to use here is an Optional var.
Since there must be some view state that handles "no data yet" and a different view state that handles "data received," you can improve your design by breaking those into two view controllers, and having your container view controller switch between them when the data becomes available. In that case, you can pass the available data to the "data received" view controller in init, and so it can be let.
What are my options to declare a variable as constant but initialized later
There are no such options. If you do not have the real value of a property at instantiation time, it must be declared with var so that you can set it when you do have the real value.
And use of an Optional is a common pattern to help your code distinguish between before you have the real value (nil) and after (not nil).
I don't want to clutter the code with unwrapping.
Then declare the property as an implicitly unwrapped Optional! This use case is exactly what it is good for.
(It would be nice to be able to “lock” the property somehow after assigning its final value, but that is not a Swift language feature. I have often wished it were. lazy has the same issue.)
When you want to create an instance property that has an initializer that you want to initialize after self is available, you typically would make it a variable and declare it as an option, like so:
var mapView: MGLMapView?
And then when self is available, you would initialize it, like so:
mapView = MGLMapView(frame: view.bounds)
However, when you later call mapView, should you call it with ? or !
mapView?.userTrackingMode = .follow
mapView!.userTrackingMode = .follow
Or should it be unwrapped right when the property is first created, like so:
var mapView: MGLMapView!
And then forego any use of ? or ! thereafter. All three possibilities will compile and run so is there a best practice or rules of thumb to follow when having to choose?
A variable should be declared as implicitly unwrapped (using !) only when you will be assigning a value outside of an init but before all of your other code will access the variable, and the variable will definitely be given a value.
Typical examples of this are outlets in a view controller. The values are assigned after init but before all other code will make use of them. In this case awakeFromNib or viewDidLoad is a typical place for the initialization. Since all other code can safely assume the variable has a non-nil value, the use of the implicitly unwrapped variable makes sense.
For "normal" optionals (using ? in the declaration), you should never force-unwrap those value since the whole reason the variable is an optional is because it might be nil.
In these cases, always use conditional binding (if let) or optional chaining.
In your example, you will most likely setup the map view in the viewDidLoad method so declare it as var mapView: MGLMapView!. Then you can reference mapView like it's not an optional everywhere else in your code. Just make sure you don't attempt to access mapView before viewDidLoad initializes it.
Another option is to set up the property as a lazily loaded property. Then you don't need it to be declared with either ? or !.
If we assume that MapView will be initialized when controller loads, we can then also assume that all subsequent references will not be nil and that we can implicitly unwrap MapView.
Therefore a valid approach is to declare mapView as follows
var mapView: MGLMapView!
This is referred to as an implicitly unwrapped option and then you may reference it directly as below (provided there is no possibility of mapView being set to nil elsewhere)
mapView.userTrackingMode = .follow
There is a section in the Swift Programming Language by Apple entitled "Implicity Unwrapped Optionals" which will go into further detail.
You can also write code other ways as you have indicated - the important thing to consider is whether or not there is a possibility of the mapView variable being nil when it is unwrapped. If not, use above.
You will also notice that the interface builder within XCode also makes use of implicitly unwrapped variables when referencing UI components (labels, textboxes, etc) which gives me confidence that this is an appropriate approach.
I was trying to assign a value to a var but I get this error: fatal error: unexpectedly found nil while unwrapping an Optional value.
the code:
vehicle.chassis = Chasis.text
but the variable is not an optional, I declare the variable this way:
var vehicle: Vehicle!
how can I fix this problem?
check the image
You are trying to set a property to an instance that doesn't exist, because the implicitly unwrapped Optional vehicle is nil.
You can't set vehicle.chassis if vehicle is nil.
Before accessing .chassis you have to populate vehicle somewhere with an instance of Vehicle, for example in an init, or in viewDidLoad, etc:
vehicle = Vehicle()
and then you can access the .chassis property:
vehicle.chassis = Chasis.text
To clarify what some of the above commenters have already mentioned, you're declaring your property as an Implicitly Unwrapped Optional (IUO herein). Only optionals in Swift can be nil, but optionals must be unwrapped, IUO's don't need to be unwrapped, but might crash. IUO's exist for 2 reasons.
To provide an unsafe/more raw access to your property to achieve ingrained Objective-C design patterns more quickly. That is, directly act on "pointers" that may or may not be nil and crash when it is.
To provide a non-optional interface to properties that you cannot guarantee will be initialized by the time your class/struct init finishes, but you can guarantee will be initialized by the time you use it. Useful for something the compiler can't verify but you as a programmer can.
That being said, I can only think of 2 sane proper ways to use IUO's in a Swifty manner, 1 is for IBOutlets, these aren't compiled by the swift compiler, don't exist after init(), but are guaranteed to be fulfilled by the time you use it (if your nib isn't corrupt), perfect use case for IUO's.
Another, while less imperative since it can be designed around, is Database models where you want lazy reads (which in itself is a bit of a pain to implement anyway)
I'm trying to wrap my head around why what feels intuitive is illegal when it comes to a dictionary containing an array in Swift.
Suppose I have:
var arr = [1,2,3,4,5]
var dict = ["a":1, "b":2, "test":arr]
I can easily access the dictionary members like so:
dict["a"]
dict["b"]
dict["test"]
As expected, each of these will return the stored value, including the array for key "test":
[1,2,3,4,5]
My intuitive reaction to this based on other languages is that this should be legal:
dict["test"][2]
Which I would expect to return 3. Of course, this doesn't work in Swift. After lots of tinkering I realize that this is the proper way to do this:
dict["test"]!.objectAtIndex(2)
Realizing this, I return to my intuitive approach and say, "Well then, this should work too:"
dict["test"]![2]
Which it does... What I don't really get is why the unwrap isn't implied by the array dereference. What am I missing in the way that Swift "thinks?"
All dictionary lookups in Swift return Optional variables.
Why? Because the key you are looking up might not exist. If the key doesn't exist, the dictionary returns nil. Since nil can be returned, the lookup type has to be an Optional.
Because the dictionary lookup returns an Optional value, you must unwrap it. This can be done in various ways. A safe way to deal with this is to use Optional Chaining combined with Optional Binding:
if let value = dict["test"]?[2] {
print(value)
}
In this case, if "test" is not a valid key, the entire chain dict["test"]?[2] will return nil and the Optional Binding if let will fail so the print will never happen.
If you force unwrap the dictionary access, it will crash if the key does not exist:
dict["test"]![2] // this will crash if "test" is not a valid key
The problem is that Dictionary’s key-based subscript returns an optional – because the key might not be present.
The easiest way to achieve your goal is via optional chaining. This compiles:
dict["test"]?[2]
Note, though, that the result will still be optional (i.e. if there were no key test, then you could get nil back and the second subscript will not be run). So you may still have to unwrap it later. But it differs from ! in that if the value isn’t present your program won’t crash, but rather just evaluate nil for the optional result.
See here for a long list of ways to handle optionals, or here for a bit more background on optionals.
In the Objective-C world, it has potential crash if you are trying to access 3 by dict[#"test"][2]. What if dict[#"test"] is nil or the array you get from dict[#"test"] has two elements only? Of course, you already knew the data is just like that. Then, it has no problem at all.
What if the data is fetched from the backend and it has some problems with it? This will still go through the compiler but the application crashes at runtime. Users are not programmers and they only know: The app crashes. Probably, they don't want to use it anymore. So, the bottom line is: No Crashes at all.
Swift introduces a type called Optional Type which means value might be missing so that the codes are safe at runtime. Inside the Swift, it's actually trying to implement an if-else to examine whether data is missing.
For your case, I will separate two parts:
Part 1: dict["test"]! is telling compiler to ignore if-else statement and return the value no matter what. Instead, dict["test"]? will return nil if the value if missing. The terminology is Explicit Unwrapping
Part 2: dict["test"]?[2] has potential crash. What if dict["test"]? returns a valid array and it only has two element? This way to store the data is the same using dict[#"test"][2] in Objective-C. That's why it has something called Optional Type Unwrapping. It will only go the if branch when valid data is there. The safest way:
if let element = dict["test"]?[2] {
// do your stuff
}
Hello everyone I am trying to figure out why the swift code below allows me to assign a new value to the wee string in my class. I thought let was immutable but it works here. Can someone please explain this. Thanks.
import Foundation
class MyClass {
let wee:String
init?(inInt:Int) {
let j:String = "what"
//j = "I shouldn't be able to do this wiht let" // error rightly so
//println(j)
self.wee = "wow"
if inInt != 2 {
return nil
}
self.wee = "hey"
self.wee = "blas" // I shouldn't be able to do this
}
}
if let myClass:MyClass = MyClass(inInt: 2) {
myClass.wee // prints blas
}
The "Modifying Constant Properties During Initialization" heading under the Initialization section of The Swift Programming Language says:
You can modify the value of a constant property at any point during
initialization, as long as it is set to a definite value by the time
initialization finishes.
Reading between the lines, and considering your example, it sounds very much like restrictions on setting the value of a constant don't apply to initialization. Further evidence supporting that idea appears earlier in the same section:
When you assign a default value to a stored property, or set its
initial value within an initializer, the value of that property is set
directly, without calling any property observers.
It's not unlikely that the constancy of a stored property is enforced by the accessors for that property. If those accessors aren't used during initialization, then it makes sense that you can modify even a constant property as many times as you like during initialization.
The fact that you can't modify j in your example after first setting it is due to the fact that j is a local constant, not a property. There probably aren't any accessors for j at all -- instead the compiler probably enforces access rules for local constants/variables.
Because you are assigning it in initialiser, at a point when object of that class is being created. I just assume, it take any last given value and then creates a constant.
If you would try the same in different function, it would not work. You would get an error.
It is not possible in swift 4.
Apple docs says:
You can assign a value to a constant property at any point during initialization, as long as it is set to a definite value by the time initialization finishes. Once a constant property is assigned a value, it can't be further modified.