When I am about to fire my segue for the 2nd view I also send some values like this:
if let aTime = ads[indexPath.row]["unix_t"].int {
toView.time = aTime
}
if let aTitle = ads[indexPath.row]["title"].string {
toView.title = aTitle
}
In the second VC I have declared the varibles like:
var time: Int?
var title: String?
and this is how I unwrap the values:
if time != nil {
timeLabel.text = String(time!)
}
if title != nil {
titleLabel.text = title!
}
This all works I never get any error caused by unwrapped varibles or nil values. But is there any easier way to do it?
Right now it feels like I am checking too much
I can think of three alternatives.
if/let. Very similar to your current option, but you don't have to implicitly unwrap.
if let time = time {
timeLabel.text = "\(time)"
}
if let title = title {
titleLabel.text = title
}
You can even unwrap them on the same line. The downside to this is that if one of them is nil, then neither label will be set.
if let time = time, let title = title {
timeLabel.text = "\(time)"
titleLabel.text = title
}
guard/let. If these are in a function like setupViews(), then you can one-line your unwrapping like so:
func setupViews() {
guard let time = time, let title = title else { return }
timeLabel.text = "\(time)"
titleLabel.text = title
}
You can use default values and the ?? operator to unwrap quickly.
timeLabel.text = "\(time ?? 0)"
titleLabel.text = title ?? ""
You can use the Nil Coalescing Operator too as given in the Docs:
The nil coalescing operator (a ?? b) unwraps an optional a if it contains a value, or returns a default value b if a is nil. The expression a is always of an optional type. The expression b must match the type that is stored inside a.
The nil coalescing operator is shorthand for the code below:
a != nil ? a! : b
if let time = time {
timeLabel.text = "\(time)"
}
if let title = title {
titleLabel.text = title
}
It's the same as with your JSON
Yes, you are checking too much (twice).
Since only non-optional values are passed you can declare the variables non-optional
var time = 0
var title = ""
and set the labels
timeLabel.text = "\(time)"
titleLabel.text = title
The strong type system of Swift highly recommends first to consider
Can I accomplish this without optionals?
and – if there is no other option – then use optionals.
You don't need to check nil condition in the first view.
Please follow below code:
toView.time = ads[indexPath.row]["unix_t"] as? Int
toView.title = ads[indexPath.row]["title"] as? String
time and title variable in second view are optionals. So, when you assign a value to toView.time and toView.title, it will either assign the value of type time and title respectively or nil.
Related
I am new to guard let patterns. How come my app crashes when selectedRooms.text is nil instead of doing the return part? And why is rooms of optional type when I know that numberOfRooms is not nil?
guard let numberOfRooms = selectedRooms.text else {
return selectedRooms.placeholder = "type something"
}
let rooms = Int(numberOfRooms)
x = Int(ceil(sqrt(Double(rooms!)))) //found nil value
selectedRooms.text cannot return nil.
A UITextField and UITextView always returns a String value. An empty String ("") is returned if there is no text in the UITextField and UITextView. That's the reason else part is not executing and rooms value is nil.
Now, in the below statement you're force-unwrapping(!) the rooms.
x = Int(ceil(sqrt(Double(rooms!))))
But, since the rooms is nil, so forcefully unwrapping it is throwing runtime exception.
Solution:
You need to add an empty check as well for the else part to take effect, i.e.
guard let numberOfRooms = selectedRooms.text, !numberOfRooms.isEmpty else { //here...
return selectedRooms.placeholder = "type something"
}
guard let numberOfRooms = selectedRooms.text?.trimmingCharacters(in: .whitespacesAndNewlines), !numberOfRooms.isEmpty else {
return selectedRooms.placeholder = "type something"
}
if let rooms = Int(numberOfRooms) {
x = Int(ceil(sqrt(rooms)))
}
you don't want to force unwrap value of numberOfRooms, you can use by checking if let
I'm using some base swift functionality, the fact that you can unwrapped Optional values with a if. I am working on swift 4 and Xcode 10.3
let xyz: String? = nil
if let unwrappedValue = xyz {
print("This is unwrapped")
} else {
print("This is not unwrapped") // Program goes there
}
I have made a dictionary with an Optional value as an element, and when I try to unwrap it, it doesn't unwrap the variable, but keeps the Optional value.
var referenceDatesIdentifiers: [String: Date?] =
["ReferenceDateIdentifier": nil]
if let referenceDate = referenceDatesIdentifiers["ReferenceDateIdentifier"] {
referenceDateLabel.text = otherDateFormater.string(from: referenceDate!) // ReferenceDate is Optional and not unwrapped
}
Why isn't referenceDate unwrapped ?
I tried to export the elem of the Dictionary in a constant, but same problem, it's not unwrapped
let refDateOpt = referenceDatesIdentifiers["ReferenceDateIdentifier"]
if let referenceDate = refDateOpt {
referenceDateLabel.text = otherDateFormater.string(from: referenceDate!) // Reference date is also Optional and not unwrapped
}
What is happening ?
You should required again optionally unwrap date with type casting will fix your issue. Update code as follow:
if let referenceDate = referenceDatesIdentifiers["ReferenceDateIdentifier"] as? Date {
print(referenceDate)
}
The problem here is that it doesn't makes any sense to create a dictionary with a nil value. So instead of checking if the key exists before adding a value, I just add the value and check the value with a if let
In the end, this was the answer
var referenceDatesIdentifiers: [String : Date] = [:]
if let referenceDate = referenceDatesIdentifiers["ReferenceDateIdentifier"] {
referenceDateLabel.text = otherDateFormater.string(from: referenceDate)
}
You could use below way. It will work perfectly.Update code as follow:
var referenceDatesIdentifiers: [String: Date?] =
["ReferenceDateIdentifier": nil]
if let refDateOpt = referenceDatesIdentifiers["ReferenceDateIdentifier"] as? Date {
print(refDateOpt)
}
or
if let refDateOpt = referenceDatesIdentifiers["ReferenceDateIdentifier"]{
if let referenceDate = refDateOpt {
print(referenceDate)
}
}
I think that a force cast I have in my app is causing it to crash, (userDefaults.value(forKey: "timeDiffSecondsDefault") as! Int?)...but I really don't know how to avoid it. Any guidance is greatly appreciated!
func getProductionTime(store: Bool = false) {
let userDefaults = UserDefaults.standard
let productionTimeFormatter = DateFormatter()
productionTimeFormatter.timeZone = TimeZone(abbreviation: defaultTimeZone)
productionTimeFormatter.dateFormat = defaultTimeFormat
if let defaultTimeDiffSeconds: Int = userDefaults.value(forKey: "timeDiffSecondsDefault") as! Int? {
timeDiffSeconds = defaultTimeDiffSeconds
}
let productionTime = Calendar.current.date(byAdding: .second, value: timeDiffSeconds, to: Date())!
if store {
storeDateComponents(nowProdTime: productionTime)
}
productionTimeString = productionTimeFormatter.string(from: productionTime)
liveCounterButton.setTitle(productionTimeString, for: .normal)
}
Use the dedicated API which returns a non-optional
timeDiffSeconds = userDefaults.integer(forKey: "timeDiffSecondsDefault")
If a default value != 0 is required register it.
Note: Never use value(forKey with UserDefaults unless you really need KVC
When the key is absent, you are trying to force-cast an empty Any? to Int?, and thus, the if condition is not executed:
if let defaultTimeDiffSeconds: Int = userDefaults.value(forKey: "timeDiffSecondsDefault") as! Int? {
timeDiffSeconds = defaultTimeDiffSeconds
}
And if timeDiffSeconds was not initialized elsewhere, it will cause the crash when you try to use it.
The appropriate way would be conditional casting with as?:
if let defaultTimeDiffSeconds = userDefaults.object(forKey: "timeDiffSecondsDefault") as? Int { ... }
object(forKey:) was kindly suggested by Mr Leonardo.
Using userDefaults.integer(forKey: "timeDiffSecondsDefault") might be confusing when using timeDiffSeconds later, since integer(forKey:) would return 0 if the key is absent in user defaults, and returns an integer even if the value is a string or a boolean.
Reading about Optional values I was sure that all the bases were covered in my code, but I still get the dreaded unexpectedly found nil while unwrapping an Optional value.
That makes sense, since I've read: What does “fatal error: unexpectedly found nil while unwrapping an Optional value” mean?. It suggests making the Int optional, which is what I want:
func myCountUpdate(mainDict: [String : NSObject]) {
let myDict = mainDict["start"] as! [String : CFString]
let myCount = subDict["count"] as? String
let myTotal = Int(myCount)? // nope, it forces me to use non-optional !
// as the other thread suggest it's easy to check for nil with an optional int.
// how the hell can you do that if it won't allow you to make it optional?
if myTotal != nil {
print(myCount!)
let label: String = "\(myCount)"
text = label
} else {
text = nil
}
}
I've tried quite a bunch of things, including using other values to check for nil, etc. The issue is that the compiler will not allow me to declare the Int as non-optional, so what are my options? Xcode shows no warnings or suggestions on this issue, so maybe someone here has one - ty.
The best approach here is to use swift guards in order to check if a value is nil.
First, in the second line, where you use the subDict, its not referenced anywhere else, should it be myDict ?
The thing here is that the cast in let myCount = subDict["count"] as? String may be returning nil or there is not "count" in subDict. Therefore, when you do Int(myCount!), the force unwrapp of myCount is throwing the exception, since its nil.
You should avoid force unwrappings as much as you can, unless you are 100% sure that the value is not nil. In other cases, you should use the setting of a variable to check if it is not nil.
With your code, an updated version using guard would be the following:
func myCountUpdate(mainDict: [String : NSObject]) {
guard let myDict = mainDict["start"] as? [String : CFString],
let myCount = myDict["count"] as? String,
let myTotal = Int(myCount) else {
text = nil
return
}
print(myTotal)
let label: String = "\(count)"
text = label
}
This is safer, because if any of the conditions in the guard fails, then it's setting the text to nil an ending the method.
First unwrap the variable optional myCount(String?) to a variable called count (String).
let myCount = mainDict["count"] as? String
if let count = myCount {
//..
}
Then try to create a Int based on the variable count (String).
Which could return a nil since you could pass Int("Hi") or Int("1").
myTotal = Int(count)
Then after that you will have a variable called myTotal (Int?) with the result that you want.
Code
func myCountUpdate(mainDict: [String : Any]) {
let myDict = mainDict["start"] as? [String : Any]
if let myCount = myDict?["count"] as? String {
if let myTotal = Int(myCount) {
print(myTotal)
}
}
if let myCount = myDict?["count"] as? Int {
print(myCount)
}
}
Example 1
let data = [
"start": [
"count": "1"
]
]
myCountUpdate(mainDict: data) // outputs 1
Example 2
let data1 = [
"start": [
"count": 1
]
]
myCountUpdate(mainDict: data1) // outputs 1
I need to extract data from a dictionary (attributes from NSXMLParser, but I don't think that matters). The below works but is this really the "easiest" why to do it? The attribute may or may not exist in the dictionary. The value of the attribute may or may not convert to an integer (i.e. toInt() returns an optional). 'mandatory' is a Bool and 'minimumLength' is an Int and are class properties.
func decodeDataRestrictions(#attributeDictionary: [NSObject: AnyObject]!) {
var stringValue: String?
var intValue: Int?
// Extract data restrictions from the element attributes
self.mandatory = false
stringValue = attributeDictionary["mandatory"] as String?
if stringValue != nil {
if stringValue! == "true" {
self.mandatory = true
}
}
self.minimumLength = 1
stringValue = attributeDictionary["minimumLength"] as String?
if stringValue != nil {
intValue = stringValue!.toInt()
if intValue != nil {
self.minimumLength = intValue!
}
}
in Objective-C it was much easier:
self.mandatory = NO;
if ([[attributeDict objectForKey:#"mandatory"] isEqualToString:#"true"]) {
self.mandatory = YES;
}
self.minimumLength = 1;
if ([attributeDict objectForKey:#"minimumLength"] != nil) {
self.minimumLength = [NSNumber numberWithInteger:[[attributeDict objectForKey:#"minimumLength"] integerValue]];
}
You ought to be able to write that whole function as follows:
func decodeDataRestrictions(#attributeDictionary: [NSObject: AnyObject]!) {
if (attributeDictionary["mandatory"] as? String) == "true" {
self.mandatory == true
}
if let minimumLength = (attributeDictionary["minimumLength"] as? String)?.toInt() {
self.minimumLength = minimumLength
}
}
If you need to check an optional for nil-ness, and then use the value if it isn’t nil, then if let combines these two things, setting a local variable to the unwrapped value if non-nil. This is what’s happening with minimumLength, along with some optional chaining (i.e. if the value is non-nil, move on to do toInt() else nil).
In the case of mandatory, you can compare an optional value to a non-optional value with ==, so there’s no need to check for nil at all.
edit: having read your Objective-C version, you can simplify it even further if you’re happy to default the self values even in cases of missing dictionary data, as you’re doing there:
func decodeDataRestrictions(#attributeDictionary: [NSObject: AnyObject]!) {
self.mandatory = (attributeDictionary["mandatory"] as? String) == "true"
self.minimumLength = (attributeDictionary["minimumLength"] as? String)?.toInt() ?? 1
}
The minimumLength version uses the nil-coalescing operator, which substitutes a default value from the right-hand side in case of nil on the left-hand side.
You should use optional binding - the first extraction can be simply written as:
if let mandatory = attributeDictionary["mandatory"] as? String {
self.mandatory = mandatory == "true"
}
whereas the 2nd requires an additional check:
if let minLen = attributeDictionary["minimumLength"] as String? {
if let minLen = minLen.toInt() {
self.minimumLength = minLen
}
}
the first if verifies that a value for the mandatory key exists, the second verifies that the value is convertible to an integer.