Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

solinari6

macrumors regular
Original poster
Aug 13, 2008
101
13
For the life of me, I just don't get it.

Usually what happens is I will write a line, and the compiler will error out saying "hey, did you mean to put a ? there" so I'll be like, ok, put a ? there.

But then it will complain again, and say "maybe you should put a ! there" and I'll be like ok, I'll try that. But it doesn't really like that. Then it will suggest !! which I don't think has ever been right.

Basically I end up trying every combination of ? and ! until the compiler finally agrees that I did it right. UGH

But really, if the compiler KNOWS that ? is the wrong thing, why does it suggest it? And what does it all MEAN?!?!?
:confused:
 

PhoneyDeveloper

macrumors 68040
Sep 2, 2008
3,114
93
Have you gone through Apple's book on Swift? That's where it's all explained!!??!!

Download it for free!!??!!
 

solinari6

macrumors regular
Original poster
Aug 13, 2008
101
13
I've read that over a few times, but it never really stuck.

I need the seseme street level explanation :p
 

PhoneyDeveloper

macrumors 68040
Sep 2, 2008
3,114
93
I borried this text from APPLE.

You can use if and let together to work with values that might be missing. These values are represented as optionals. An optional value either contains a value or contains nil to indicate that the value is missing. Write a question mark (?) after the type of a value to mark the value as optional.

var optionalString: String? = "Hello"

OK, that's the ?.

Once you’re sure that the optional does contain a value, you can access its underlying value by adding an exclamation mark (!) to the end of the optional’s name. The exclamation mark effectively says, “I know that this optional definitely has a value; please use it.” This is known as forced unwrapping of the optional’s value:

In other languages a value may be nil or some valid value. The ? means it may be invalid or valid. The ! means I know that it's valid. Trust me.

Swift is very type save. Some might be offended by how type-safe it is. The ! allows you to sneak around the type-safety.

If this doesn't make sense to you then explain what you think these symbols mean and someone will tell you what you need to know. Also, tell what other computer languages you know so we can tailor the response to your level of knowledge.

BTW, Kermit doesn't understand this either.

OK, couple more bits of info.

Image you have two methods. One returns a random number as an Integer. The other returns a bank balance for a bank in Thailand. The random number method always always returns a valid random number. ALWAYS. The bank balance method usually returns a valid Integer but sometimes returns nil. It returns nil if you don't have a valid bank account or if the internet is broken or if it feels like it. The random number method always returns a non-optional value. The bank balance method returns an optional value.

Dig?
 
Last edited:

solinari6

macrumors regular
Original poster
Aug 13, 2008
101
13
Ok, I guess that much makes sense. What confuses me, is why you would actually declare something that way. What is the purpose?

In my code I have an Int defined like this:

var swipeFromColumn: Int?

and one defined like this:

var wordSize: Int = 0

The one defined as Int? was something I copied from a tutorial. The other is one I defined myself.

Why would I choose one way over the other? Seems like defining the Int as ? just made the code more confusing, since i ended up having to stick ? or ! everywhere when I reference it

Is the main difference, that the first case you can tell if it wasn't set, since it will be nil, and the other case you can't tell if it wasn't set, or someone set it to 0?
 

ArtOfWarfare

macrumors G3
Nov 26, 2007
9,559
6,059
Is the main difference, that the first case you can tell if it wasn't set, since it will be nil, and the other case you can't tell if it wasn't set, or someone set it to 0?

Yes. A "Int?" is an Int that may or may not have been set. A "Int" has definitely been set. Swift will want you to make sure "Int?" was set before using it (and you assure it that it really is by saying "!", with terrible results if you lie) while both you and Swift know that a "Int" was set.
 

theSeb

macrumors 604
Aug 10, 2010
7,466
1,893
none
Ok, I guess that much makes sense. What confuses me, is why you would actually declare something that way. What is the purpose?

In simplest terms it's to allow you to have variables that may, or may not, have values in them during run-time. It's basically the equivalent of Objective C nil with the benefit of compile-time checking.

Code:
var someVariable: Int?

In English: this is a variable that may, or may not, hold an integer value. To be more correct, it is an optional that can be an integer. This is really the key to understanding Swift.
 
Last edited:

bbeagle

macrumors 68040
Oct 19, 2010
3,541
2,981
Buffalo, NY
Use ! if you KNOW that the variable has a value, and ? if it 'could' not have a value.

var snoopy : Dog?

(Snoopy is declared as a Dog, but it's reference is still nil)

snoopy?.bark() - use if you're unsure if snoopy = Dog() has been called yet
snoopy!.bark() - use if you KNOW that snoopy = Dog() has been called earlier in the code.

Note that if you call snoopy!.bark() when snoopy is nil, your program will crash.
If you call snoopy?.bark() when snoopy is nil, your program does NOT crash.
 

bbeagle

macrumors 68040
Oct 19, 2010
3,541
2,981
Buffalo, NY
Why would I choose one way over the other? Seems like defining the Int as ? just made the code more confusing, since i ended up having to stick ? or ! everywhere when I reference it

Less code checks. Say you had a family object:
Family {
Mom, Dad, Children, Dog, Cat, Fish, Bird
}

You had a 'speak' method to family where everyone would shout out when you said Family.speak()

Without '?' syntax:
Code:
func speak {
  if (family.has('Mom')) { Mom.speak() }
  if (family.has('Dad')) { Dad.speak() }
  if (family.has('Dog')) { Dog.bark() }
  if (family.has('Cat')) { Cat.meow() }
...
...  

}

With '?' syntax:

Code:
func speak {
   Mom?.speak()
   Dad?.speak()
   Dog?.bark()
   Cat?.meow()
}

Cleaner, easier to read code without all the if/thens. This can get MUCH MORE complicated than this simple example. Makes the code much easier to read, less likely to have bugs where someone forgets an if/then, puts the braces in the wrong spots, etc.

If the family had no Cat, then the Cat?.meow() would do nothing, and the code wouldn't bomb. If the family DID have a Cat, then the same code would cause the Cat to meow. All without if/thens.
 

cipo

macrumors member
Nov 23, 2010
58
11
Stuttgart, Germany
It's also a great way to document (and enforce!) constraints on arguments and return values. Consider this:

Code:
func convert(input: Input) -> Output {
...
}

If you want to call this function, you know that you won't have to check if the output is nil, because it can't be. Not because the developer said so in the documentation, but because the compiler enforces it. It also tells you that you cannot pass in a nil input. Likewise, whoever implements the "convert" function does not need to account for a nil input.

So in a sense, even though optionals are the newly introduced concept, the non-optionals are just as exciting: references that are guaranteed to be set.
 

kage207

macrumors 6502a
Jul 23, 2008
971
56
Ok, I guess that much makes sense. What confuses me, is why you would actually declare something that way. What is the purpose?

In my code I have an Int defined like this:

var swipeFromColumn: Int?

and one defined like this:

var wordSize: Int = 0

The one defined as Int? was something I copied from a tutorial. The other is one I defined myself.

Why would I choose one way over the other? Seems like defining the Int as ? just made the code more confusing, since i ended up having to stick ? or ! everywhere when I reference it

Is the main difference, that the first case you can tell if it wasn't set, since it will be nil, and the other case you can't tell if it wasn't set, or someone set it to 0?

Optionals - ?
-------------------
Ok the reason there are optionals is because you may want an object or object's property to be nil (null). I try to use an optional ONLY with user input. This is because the input can be empty and I would want to make sure that it is not empty and has a value.

So when you declare a property

Code:
var aProperty: Int?

There is no default value for that property. The above lets you to declare a property that does not need to be initialized in the init function. So the initial value will be set to null, so you must declare an init function that gives
Code:
aProperty
a value.

Xcode ask you to use
Code:
aNewProperty = aProperty?
when assign the value to a new variable or even using the property because it may be nil. Though you should always check when unwrapping. The above is called Optional Unwrapping.

There are other places where using optionals may make sense but you have to know for sure when you unwrap an optional value it has something in there or you will crash your app. So look below on how to properly use optionals.

Force Unwrapping - !
----------------------
Says, hey I know this property is an optional but I knows he variable has a value that is not null. Xcode will sometimes push you use this if you set this variable if you try to the variable without checking to see if it actually has a value. So instead of using a force optional, you should do something like:
Code:
if let newProperty = aProperty? { ... }
and then your stuff with
Code:
newProperty
.

BAD: If you do something like
Code:
anObject!.aFunction()
on a null object it will crash your app. I highly suggest avoiding ! when possible as Apple recommends this as well. Just because they give you power does not mean you should abuse that power.
 

grandM

macrumors 68000
Oct 14, 2013
1,508
298
Less code checks. Say you had a family object:
Family {
Mom, Dad, Children, Dog, Cat, Fish, Bird
}

You had a 'speak' method to family where everyone would shout out when you said Family.speak()

Without '?' syntax:
Code:
func speak {
  if (family.has('Mom')) { Mom.speak() }
  if (family.has('Dad')) { Dad.speak() }
  if (family.has('Dog')) { Dog.bark() }
  if (family.has('Cat')) { Cat.meow() }
...
...  

}

With '?' syntax:

Code:
func speak {
   Mom?.speak()
   Dad?.speak()
   Dog?.bark()
   Cat?.meow()
}

Cleaner, easier to read code without all the if/thens. This can get MUCH MORE complicated than this simple example. Makes the code much easier to read, less likely to have bugs where someone forgets an if/then, puts the braces in the wrong spots, etc.

If the family had no Cat, then the Cat?.meow() would do nothing, and the code wouldn't bomb. If the family DID have a Cat, then the same code would cause the Cat to meow. All without if/thens.
I was wondering, we are writing code as this:
Code:
 class func jsonAsUSDAIdAndNameSearchResults (json: NSDictionary) -> [(name: String, idValue: String)] {
        
        var usdaItemsSearchResults: [(name: String, idValue: String)] = []
        var searchResult: (name: String, idValue: String)
        
        if json["hits"] != nil {
            let results: [AnyObject] = json["hits"] as [AnyObject]
            for itemDictonary in results {
                if itemDictonary["_id"] != nil {
                    let idValue:String = itemDictonary["_id"] as String
                    if itemDictonary["fields"] != nil {
                        let fieldsDictionary: NSDictionary = itemDictonary["fields"] as NSDictionary
                        if fieldsDictionary["item_name"] != nil {
                            let name: String = fieldsDictionary["item_name"] as String
                            searchResult = (name: name, idValue: idValue)
                            usdaItemsSearchResults += [searchResult]
                        }
                    }
                }
            }
        }
        return usdaItemsSearchResults
    }

Why isn't is possible to do this:
Code:
        class func jsonAsUSDAIdAndNameSearchResults (json: NSDictionary) -> [(name: String, idValue: String)] {
        
        var usdaItemsSearchResults: [(name: String, idValue: String)] = []
        var searchResult: (name: String, idValue: String)
        
        if json["hits"] != nil {
            let results: [AnyObject] = json["hits"] as [AnyObject]
            for itemDictionary in results {
                let name: String = itemDictionary["_id"]?["_fields"]?["item_name"]? as String
                let idValue:String = itemDictionary["_id"]? as String
                searchResult = (name: name, idValue: idValue)
                usdaItemsSearchResults += [searchResult]
            }
        }
        return usdaItemsSearchResults
    }
 
Last edited:

bbeagle

macrumors 68040
Oct 19, 2010
3,541
2,981
Buffalo, NY
let name: String = itemDictionary["_id"]?["_fields"]?["item_name"]? as String
let idValue:String = itemDictionary["_id"]? as String
searchResult = (name: name, idValue: idValue)
[/code]

You're setting 'name' and 'idValue' to be Strings, but then saying that the value is OPTIONAL? Can't do that.

You need to declare the 'type' to be optional as well. Like:

Code:
let name: String? = itemDictionary["_id"]?["_fields"]?["item_name"]?
let idValue: String? = itemDictionary["_id"]?

because name and idValue could be nil, which is not a String.
 

grandM

macrumors 68000
Oct 14, 2013
1,508
298
You're setting 'name' and 'idValue' to be Strings, but then saying that the value is OPTIONAL? Can't do that.

You need to declare the 'type' to be optional as well. Like:

Code:
let name: String? = itemDictionary["_id"]?["_fields"]?["item_name"]?
let idValue: String? = itemDictionary["_id"]?

because name and idValue could be nil, which is not a String.

At the moment I'm getting two mistakes. First with the name statement. There it states "type int does not conform to protocol 'StringLiteralConvertible'". Second mistake with the searchResult. It asks met to unwrap them using ! but I want to place ? as I am not sure they won't be nil. I could first check in an if-clause of course and then unwrap them. Would serve logic too. First problem puzzles me still though.

Code:
 class func jsonAsUSDAIdAndNameSearchResults (json: NSDictionary) -> [(name: String, idValue: String)] {
        
        var usdaItemsSearchResults: [(name: String, idValue: String)] = []
        var searchResult: (name: String, idValue: String)
        
        if json["hits"] != nil {
            let results: [AnyObject] = json["hits"] as [AnyObject]
            for itemDictionary in results {
                let name: String? = itemDictionary["_id"]?["_fields"]?["item_name"]? as? String
                let idValue:String? = itemDictionary["_id"]? as? String
                searchResult = (name: name?, idValue: idValue?)
                usdaItemsSearchResults += [searchResult]
            }
        }
        return usdaItemsSearchResults
    }


----------

At the moment I'm getting two mistakes. First with the name statement. There it states "type int does not conform to protocol 'StringLiteralConvertible'". Second mistake with the searchResult. It asks met to unwrap them using ! but I want to place ? as I am not sure they won't be nil. I could first check in an if-clause of course and then unwrap them. Would serve logic too. First problem puzzles me still though.

Code:
 class func jsonAsUSDAIdAndNameSearchResults (json: NSDictionary) -> [(name: String, idValue: String)] {
        
        var usdaItemsSearchResults: [(name: String, idValue: String)] = []
        var searchResult: (name: String, idValue: String)
        
        if json["hits"] != nil {
            let results: [AnyObject] = json["hits"] as [AnyObject]
            for itemDictionary in results {
                let name: String? = itemDictionary["_id"]?["_fields"]?["item_name"]? as? String
                let idValue:String? = itemDictionary["_id"]? as? String
                searchResult = (name: name?, idValue: idValue?)
                usdaItemsSearchResults += [searchResult]
            }
        }
        return usdaItemsSearchResults
    }

Code beneath implements the if-clause. Still a problem for the name though.
Code:
     class func jsonAsUSDAIdAndNameSearchResults (json: NSDictionary) -> [(name: String, idValue: String)] {
        
        var usdaItemsSearchResults: [(name: String, idValue: String)] = []
        var searchResult: (name: String, idValue: String)
        
        if json["hits"] != nil {
            let results: [AnyObject] = json["hits"] as [AnyObject]
            for itemDictionary in results {
                let name: String? = itemDictionary["_id"]?["_fields"]?["item_name"]? as? String
                let idValue:String? = itemDictionary["_id"]? as? String
                if (name? != nil && idValue? != nil) {
                    searchResult = (name: name!, idValue: idValue!)
                    usdaItemsSearchResults += [searchResult]
                }
            }
        }
        return usdaItemsSearchResults
    }
 

bbeagle

macrumors 68040
Oct 19, 2010
3,541
2,981
Buffalo, NY
At the moment I'm getting two mistakes. First with the name statement. There it states "type int does not conform to protocol 'StringLiteralConvertible'". Second mistake with the searchResult. It asks met to unwrap them using ! but I want to place ? as I am not sure they won't be nil. I could first check in an if-clause of course and then unwrap them. Would serve logic too. First problem puzzles me still though.

Okay, look at what you're returning in the top...

Code:
 class func jsonAsUSDAIdAndNameSearchResults (json: NSDictionary) -> [(name: String, idValue: String)] {

You're returning what you're GUARANTEEING to be 2 Strings, a name and an idValue....

But down here...
Code:
                searchResult = (name: name?, idValue: idValue?)


You're setting searchResult name and idValue to things that COULD be Strings.... those aren't the same.

Either what you're returning must have an optional (?) after it, or you need to set searchResult to:

Code:
                searchResult = (name: name!, idValue: idValue!)
 

grandM

macrumors 68000
Oct 14, 2013
1,508
298
Okay, look at what you're returning in the top...

Code:
 class func jsonAsUSDAIdAndNameSearchResults (json: NSDictionary) -> [(name: String, idValue: String)] {

You're returning what you're GUARANTEEING to be 2 Strings, a name and an idValue....

But down here...
Code:
                searchResult = (name: name?, idValue: idValue?)


You're setting searchResult name and idValue to things that COULD be Strings.... those aren't the same.

Either what you're returning must have an optional (?) after it, or you need to set searchResult to:

Code:
                searchResult = (name: name!, idValue: idValue!)

In this code I solved the problem of the searchResult. The name issue remains unresolved though.

Code:
 class func jsonAsUSDAIdAndNameSearchResults (json: NSDictionary) -> [(name: String, idValue: String)] {
        
        var usdaItemsSearchResults: [(name: String, idValue: String)] = []
        var searchResult: (name: String, idValue: String)
        
        if json["hits"] != nil {
            let results: [AnyObject] = json["hits"] as [AnyObject]
            for itemDictionary in results {
                let name: String? = itemDictionary["_id"]?["_fields"]?["item_name"]? as? String
                let idValue:String? = itemDictionary["_id"]? as? String
                if (name? != nil && idValue? != nil) {
                    searchResult = (name: name!, idValue: idValue!)
                    usdaItemsSearchResults += [searchResult]
                }
            }
        }
        return usdaItemsSearchResults
    }
 

grandM

macrumors 68000
Oct 14, 2013
1,508
298
In this code I solved the problem of the searchResult. The name issue remains unresolved though.

Code:
 class func jsonAsUSDAIdAndNameSearchResults (json: NSDictionary) -> [(name: String, idValue: String)] {
        
        var usdaItemsSearchResults: [(name: String, idValue: String)] = []
        var searchResult: (name: String, idValue: String)
        
        if json["hits"] != nil {
            let results: [AnyObject] = json["hits"] as [AnyObject]
            for itemDictionary in results {
                let name: String? = itemDictionary["_id"]?["_fields"]?["item_name"]? as? String
                let idValue:String? = itemDictionary["_id"]? as? String
                if (name? != nil && idValue? != nil) {
                    searchResult = (name: name!, idValue: idValue!)
                    usdaItemsSearchResults += [searchResult]
                }
            }
        }
        return usdaItemsSearchResults
    }

solved!
Code:
class func jsonAsUSDAIdAndNameSearchResults (json: NSDictionary) -> [(name: String, idValue: String)] {
        
        var usdaItemsSearchResults: [(name: String, idValue: String)] = []
        var searchResult: (name: String, idValue: String)
        
        if json["hits"] != nil {
            let results:[AnyObject] = json["hits"]! as [AnyObject]
            for itemDictionary in results {
                let name:String? = (itemDictionary["fields"]? as NSDictionary)["item_name"] as? String
                let idValue:String? = itemDictionary["_id"]? as? String
                if (name? != nil && idValue? != nil) {
                    searchResult = (name: name!, idValue: idValue!)
                    usdaItemsSearchResults += [searchResult]
                }
            }
        }
        return usdaItemsSearchResults
    }
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.