Swift: About assign nil to a var - swift

I was trying to learn about swift basics and I encountered this problem:
Assume we have a dic:
var presidentialPetsDict = ["Barack Obama":"Bo", "Bill Clinton": "Socks", "George Bush": "Miss Beazley", "Ronald Reagan": "Lucky"]
And to Remove the entry for "George Bush" and replace it with an entry for "George W. Bush":
What I did:
var oldvalue = presidentialPetsDict.removeValueForKey("George Bush")
if let value = oldvalue
{
presidentialPetsDict["George W. Bush"] = value
}else
{
print("no matching found")
}
Because I believe removeValueForKey method will return an optional value in case key "George Bush" will not return a value but nil so we need to safely unwrap it by using if let.
However, the solution code looks like this:
var oldValue = presidentialPetsDict.removeValueForKey("Georgee Bush")
presidentialPetsDict["George W. Bush"] = oldValue
The part I don't understand is that if we want to assign nil to a var we usually do this:
var value:String?
value = nil
But the solution code above works even though the method returns nil, could somebody explain why it worked because I think in solution we didn't declare oldValue as optional at all.

Very good question!
Since your array is defined as [String:String] you wonder why the compiler let you assign an optional String (so String?) to a value.
How can the compiler risk you to put a nil inside something that should be a non optional String ?
How can this code compile?
var oldValue: String? = presidentialPetsDict.removeValueForKey("Georgee Bush")
presidentialPetsDict["George W. Bush"] = oldValue
Here's the answer
Subscript has the following logic, even if the value of the Dictionary is String, you can use subscript to assign nil.
In that case the key is removed from the array.
Look here
var numbers: [String:Int] = ["one": 1, "two": 2, "three": 3]
numbers["two"] = nil // it looks like I'm putting nil into a Int right?
numbers // ["one": 1, "three": 3]

var oldValue = presidentialPetsDict.removeValueForKey("George Bush")
oldValue gets Optional String because of type inference of the return type.
See the function definition of removeValueForKey
public mutating func removeValueForKey(key: Key) -> Value?

Related

What's the correct way to create a dictionary typealias?

If I want to define a typealias for a Dictionary, which accept String as the key, and String for the value, but I also want it to can accept nil, which one of these definition is correct?
typealias dictString = Dictionary<String, String?>
or
typealias dictString = Dictionary<String, String>
or
typealias dictString = [String:String?]
or
typealias dictString = [String:String]
Because I use the first one, and now each time I want to unwrap a value, I need to unwrap it twice (e.g. dict["name"]!!) so I began to wondering whether I really need to add the question mark or not (but I need the dictionary to still able to take nil value). Thanks.
The second and fourth are okay.
You don't need to make the value optional in most cases. You can set a key's value to nil even if you did not declare the value as an optional type:
var dict = [String: String]()
dict["Hello"] = nil
And when you access "Hello", it returns nil. Fantastic!
In fact, setting a key's value to nil means to "delete the key". This can cause unexpected results if you don't know this.
For example,
var dict = [String: String]()
dict["Hello"] = nil
print(dict.keys.contains("Hello"))
prints false.
If you need to check if a key exists even if its value is nil, then you have to make the value an optional type.
A dictionary will return nil for any key that's not present. Now, if you want to be able to actually have the key present but have the value nil then version 1 and 3 are the way to go and you keep the double unwrapping. Otherwise use 2 or 4.
Update
Example:
Version 1 & 3:
typealias DictString = Dictionary<String, String?> // or [String: String?]
var myDict: DictString = ["MyKey1": "MyVal1", "MyKey2": nil]
var value1: String? = myDict["MyKey1"]! // This is an Optional String so you'd need to unwrap it
var value2: String? = myDict["MyKey2"]! // This will return nil
Version 2 & 4:
typealias DictString = Dictionary<String, String> // or [String: String]
var myDict: DictString = ["MyKey1": "MyVal1"]
var value1: String? = myDict["MyKey1"] // This is an Optional String so you'd need to unwrap it
var value2: String? = myDict["MyKey2"] // This will return nil
Now the difference between the two is that the first case actually stores the keys. So if you ever do this:
var allKeys: [String] = Array(myDict.keys)
You'll get two different results:
1 & 3: allKeys will be ["MyKey1", "MyKey2"]
2 & 4: allKeys will be ["MyKey1"]
P.S: A good practice is to use upper cases for types' names. So I would suggest to changes your typealias to DictString.

Using reduce() to build a dictionary in Swift

I'd like to build a dictionary using a functional programming style. My reduce() doesn't seem to work - I get a "fatal error: unexpectedly found nil while unwrapping an Optional value"
func loadMoveToCalendarsRules(calendarIndex: Int) -> [String]? {
// return something like ["phone call", "buzz", "ring"]
return NSUserDefaults.standardUserDefaults().objectForKey(generateMoveToCalendarsRules_NSUserDefaultsKey(calendarIndex)) as? [String]
}
// Add indeces to an array of any type
func addIndices<T>(toArray: [T]) -> [(index: Int, value: T)] {
return Array(zip(toArray.indices, toArray))
}
typealias CalendarRules = [EKCalendar : [String]]?
func buildCalendarRules(cals: [EKCalendar]) -> CalendarRules {
let sortedCals = cals.sort { $0.title.lowercaseString < $1.title.lowercaseString }
// build move to cal rules.
let indexedCalList = addIndices(sortedCals)
// go through the sorted calendars and build a dictionary that associates each calendar with a string array. (These are keywords that apply to the given calendar.)
let calendarRules = indexedCalList.reduce(nil as CalendarRules) {
accumulator, nextValue in
var retVal: [EKCalendar : [String]]? = accumulator
// if there are values found in NSUserDefaults for this calendar index then retrieve them.
if let rulesForCurrentCal = loadMoveToCalendarsRules(nextValue.index) {
retVal![nextValue.value] = rulesForCurrentCal // fatal error: unexpectedly found nil while unwrapping an Optional value
}
return retVal
}
print("------------ built calendar rules -------------")
print(Array(arrayLiteral: calendarRules?.keys))
print(Array(arrayLiteral: calendarRules?.values))
return calendarRules
}
Your retVal is optional, and starts as nil (the initial value you pass in), yet you are using retVal! to force-unwrap it. You could just use [:] (an empty dictionary) as the initial value, and then retVal wouldn't need to be optional at all.
You are starting with nil, and never instantiate a CalendarRules dictionary, so the attempt to performed a forced unwrapping of it with ! is going to fail. Instead, test to see if it's nil and if so, instantiate one.
Before I get to that, I'd first suggest defining calendar rules as a non-optional type. It makes things less confusing this way:
typealias CalendarRules = [EKCalendar : [String]]
Then, you could use nil-coalescing operator, ??, to instantiate the CalendarRules object when needed:
let calendarRules = indexedCalList.reduce(nil as CalendarRules?) { accumulator, nextValue in
if let rulesForCurrentCal = loadMoveToCalendarsRules(nextValue.index) {
var retVal = accumulator ?? CalendarRules()
retVal[nextValue.value] = rulesForCurrentCal
return retVal
}
return accumulator
}
It strikes me that there might be more efficient approaches, but this should address your "unexpectedly found nil" error.

Converting from Int to String Swift 2.2

Dears
I have this case where chatId is a property of type Int
let StringMessage = String(self.listingChat?.messages.last?.chatId)
When I debug I find that StringMessage is returning Optional(15) Which means it is unwrapped. But at the same time XCode does not allow me to put any bangs (!) to unwrap it. So I am stuck with Unwrapped Variable. I know its noob question but it I really cant get it. Your help is appreciated.
Thank you
It depends on what you want the default value to be.
Assuming you want the default value to be an empty string (""), You could create a function or a method to handle it.
func stringFromChatId(chatId: Int?) -> String {
if let chatId = chatId {
return String(chatId)
} else {
return ""
}
}
let stringMessage = stringFromChatId(self.listingChat?.messages.last?.chatId)
Or you could handle it with a closure.
let stringMessage = { $0 != nil ? String($0!) : "" }(self.listingChat?.messages.last?.chatId)
If you don't mind crashing if self.listingChat?.messages.last?.chatId is nil, then you should be able to directly unwrap it.
let StringMessage = String((self.listingChat?.messages.last?.chatId)!)
or with a closure
let stringMessage = { String($0!) }(self.listingChat?.messages.last?.chatId)
Update
Assuming chatId is an Int and not an Optional<Int> (AKA Int?) I missed the most obvious unwrap answer. Sorry, I was tired last night.
let StringMessage = String(self.listingChat!.messages.last!.chatId)
Force unwrap all the optionals along the way.
Optionals have a very nice method called map (unrelated to map for Arrays) which returns nil if the variable is nil, otherwise it calls a function on the (non-nil) value. Combined with a guard-let, you get very concise code. (I've changed the case of stringMessage because variables should begin with a lower-case letter.)
guard let stringMessage = self.listingChat?.messages.last?.chatId.map { String($0) } else {
// Do failure
}
// Success. stringMessage is of type String, not String?
I think:
let StringMessage = String(self.listingChat?.messages.last?.chatId)!

How to check if a variable is nil

I have a variable
var a: [AnyObject? -> Void]
and I am adding data in to it by append method. Now I want to check if the variable is nil or not. I tried using [] but not working and also tried "", this also not working, can anyone tell what is the meaning of this variable and how to check if it is nil.
As far as I understand, var a is an Array of functions that take an optional Object of any type, and return void. So these functions's parameter IS optional, but the Array itself isn't : it cannot be nil, or it would be declared [AnyObject? -> Void]? , no?
EDIT : if, nevertheless, you declared this a as an optional (but WHY would you do that ?) - adding a ? - you check an optional existence with if let :
if let b = a {
// a not nil, do some stuff
} else {
// a is null
}
If you just want to check if the array is empty, use isEmpty method from Swift Array
Update: Xcode 7.3 Swift 2.2
If you want to check if a variable is nil you should use if let to unwrap if for you. There is no need to create a second var.
let str = "123"
var a = Int(str)
if let a = a {
print(a)
}
Or
if let a = Int(str) {
print(a)
}
In Swift, nil is not a pointer—it is the absence of a value of a certain type. Optionals of any type can be set to nil, not just object types.
So, You can check it with below code:
let possibleNumber = "123"
let convertedNumber = possibleNumber.toInt()
if convertedNumber != nil {
println("convertedNumber contains some integer value.")
}
// prints "convertedNumber contains some integer value."
Please refer this about nil for more information.
In Swift 3.0
if let imageURL = dictObj["list_image"] as? String {
print(imageURL)
}
You can use if let. if let is a special structure in Swift that allows you to check if an Optional holds a value, and in case it does – do something with the unwrapped value.
var a:Int=0
if let b=a{
println(a)
} else {
println("Value - nil")
}
But for Strings you can also use .isEmpty() If you have initialized it to "".
var str:String=""
if !str.isEmpty(){
println(str)
}
For me none of the above solutions worked when I was using an AVFoundation object.
I would get Type 'AVCaptureDeviceInput does not conform to protocol 'BooleanType' when I tried if (audioDeviceInput) and I would get Binary operator '!=' cannot be applied to operands of type 'AVCaptureDeviceInput' and 'nil'.
Solution in my situation
if (audioDeviceInput.isEqual(nil))
nil is a pointer like any other and can be referenced as such, which is why this works.

Printing optional variable

I am trying with these lines of code
class Student {
var name: String
var age: Int?
init(name: String) {
self.name = name
}
func description() -> String {
return age != nil ? "\(name) is \(age) years old." : "\(name) hides his age."
}
}
var me = Student(name: "Daniel")
println(me.description())
me.age = 18
println(me.description())
Above code produces as follow
Daniel hides his age.
Daniel is Optional(18) years old.
My question is why there is Optional (18) there, how can I remove the optional and just printing
Daniel is 18 years old.
You have to understand what an Optional really is. Many Swift beginners think var age: Int? means that age is an Int which may or may not have a value. But it means that age is an Optional which may or may not hold an Int.
Inside your description() function you don't print the Int, but instead you print the Optional. If you want to print the Int you have to unwrap the Optional. You can use "optional binding" to unwrap an Optional:
if let a = age {
// a is an Int
}
If you are sure that the Optional holds an object, you can use "forced unwrapping":
let a = age!
Or in your example, since you already have a test for nil in the description function, you can just change it to:
func description() -> String {
return age != nil ? "\(name) is \(age!) years old." : "\(name) hides his age."
}
To remove it, there are three methods you could employ.
If you are absolutely sure of the type, you can use an exclamation mark to force unwrap it, like this:
// Here is an optional variable:
var age: Int?
// Here is how you would force unwrap it:
var unwrappedAge = age!
If you do force unwrap an optional and it is equal to nil, you may encounter this crash error:
This is not necessarily safe, so here's a method that might prevent crashing in case you are not certain of the type and value:
Methods 2 and three safeguard against this problem.
The Implicitly Unwrapped Optional
if let unwrappedAge = age {
// continue in here
}
Note that the unwrapped type is now Int, rather than Int?.
The guard statement
guard let unwrappedAge = age else {
// continue in here
}
From here, you can go ahead and use the unwrapped variable. Make sure only to force unwrap (with an !), if you are sure of the type of the variable.
Good luck with your project!
For testing/debugging purposes I often want to output optionals as strings without always having to test for nil values, so I created a custom operator.
I improved things even further after reading this answer in another question.
fileprivate protocol _Optional {
func unwrappedString() -> String
}
extension Optional: _Optional {
fileprivate func unwrappedString() -> String {
switch self {
case .some(let wrapped as _Optional): return wrapped.unwrappedString()
case .some(let wrapped): return String(describing: wrapped)
case .none: return String(describing: self)
}
}
}
postfix operator ~? { }
public postfix func ~? <X> (x: X?) -> String {
return x.unwrappedString
}
Obviously the operator (and its attributes) can be tweaked to your liking, or you could make it a function instead. Anyway, this enables you to write simple code like this:
var d: Double? = 12.34
print(d) // Optional(12.34)
print(d~?) // 12.34
d = nil
print(d~?) // nil
Integrating the other guy's protocol idea made it so this even works with nested optionals, which often occur when using optional chaining. For example:
let i: Int??? = 5
print(i) // Optional(Optional(Optional(5)))
print("i: \(i~?)") // i: 5
Update
Simply use me.age ?? "Unknown age!". It works in 3.0.2.
Old Answer
Without force unwrapping (no mach signal/crash if nil) another nice way of doing this would be:
(result["ip"] ?? "unavailable").description.
result["ip"] ?? "unavailable" should have work too, but it doesn't, not in 2.2 at least
Of course, replace "unavailable" with whatever suits you: "nil", "not found" etc
To unwrap optional use age! instead of age. Currently your are printing optional value that could be nil. Thats why it wrapped with Optional.
In swift Optional is something which can be nil in some cases. If you are 100% sure that a variable will have some value always and will not return nil the add ! with the variable to force unwrap it.
In other case if you are not much sure of value then add an if let block or guard to make sure that value exists otherwise it can result in a crash.
For if let block :
if let abc = any_variable {
// do anything you want with 'abc' variable no need to force unwrap now.
}
For guard statement :
guard is a conditional structure to return control if condition is not met.
I prefer to use guard over if let block in many situations as it allows us to return the function if a particular value does not exist.
Like when there is a function where a variable is integral to exist, we can check for it in guard statement and return of it does not exist.
i-e;
guard let abc = any_variable else { return }
We if variable exists the we can use 'abc' in the function outside guard scope.
age is optional type: Optional<Int> so if you compare it to nil it returns false every time if it has a value or if it hasn't. You need to unwrap the optional to get the value.
In your example you don't know is it contains any value so you can use this instead:
if let myAge = age {
// there is a value and it's currently undraped and is stored in a constant
}
else {
// no value
}
I did this to print the value of string (property) from another view controller.
ViewController.swift
var testString:NSString = "I am iOS Developer"
SecondViewController.swift
var obj:ViewController? = ViewController(nibName: "ViewController", bundle: nil)
print("The Value of String is \(obj!.testString)")
Result :
The Value of String is I am iOS Developer
Check out the guard statement:
for student in class {
guard let age = student.age else {
continue
}
// do something with age
}
When having a default value:
print("\(name) is \(age ?? 0) years old")
or when the name is optional:
print("\(name ?? "unknown") is \(age) years old")
I was getting the Optional("String") in my tableview cells.
The first answer is great. And helped me figure it out. Here is what I did, to help the rookies out there like me.
Since I am creating an array in my custom object, I know that it will always have items in the first position, so I can force unwrap it into another variable. Then use that variable to print, or in my case, set to the tableview cell text.
let description = workout.listOfStrings.first!
cell.textLabel?.text = description
Seems so simple now, but took me a while to figure out.
This is not the exact answer to this question, but one reason for this kind of issue.
In my case,
I was not able to remove Optional from a String with "if let" and "guard let".
So use AnyObject instead of Any to remove optional from a string in swift.
Please refer link for the answer.
https://stackoverflow.com/a/51356716/8334818
If you just want to get rid of strings like Optional(xxx) and instead get xxx or nil when you print some values somewhere (like logs), you can add the following extension to your code:
extension Optional {
var orNil: String {
if self == nil {
return "nil"
}
return "\(self!)"
}
}
Then the following code:
var x: Int?
print("x is \(x.orNil)")
x = 10
print("x is \(x.orNil)")
will give you:
x is nil
x is 10
PS. Property naming (orNil) is obviously not the best, but I can't come up with something more clear.
With the following code you can print it or print some default value. That's what XCode generally recommend I think
var someString: String?
print("Some string is \(someString ?? String("Some default"))")
If you are printing some optional which is not directly printable but has a 'to-printable' type method, such as UUID, you can do something like this:
print("value is: \(myOptionalUUID?.uuidString ?? "nil")")
eg
let uuid1 : UUID? = nil
let uuid2 : UUID? = UUID.init()
print("uuid1: \(uuid1?.uuidString ?? "nil")")
print("uuid2: \(uuid2?.uuidString ?? "nil")")
-->
uuid1: nil
uuid2: 0576137D-C6E6-4804-848E-7B4011B40C11