Swift - can someone explain ? and !

Discussion in 'iOS Programming' started by solinari6, Jan 15, 2015.

  1. solinari6 macrumors member

    Joined:
    Aug 13, 2008
    #1
    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:
     
  2. PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #2
    Have you gone through Apple's book on Swift? That's where it's all explained!!??!!

    Download it for free!!??!!
     
  3. solinari6 thread starter macrumors member

    Joined:
    Aug 13, 2008
    #3
    I've read that over a few times, but it never really stuck.

    I need the seseme street level explanation :p
     
  4. PhoneyDeveloper, Jan 15, 2015
    Last edited: Jan 15, 2015

    PhoneyDeveloper macrumors 68030

    PhoneyDeveloper

    Joined:
    Sep 2, 2008
    #4
    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?
     
  5. solinari6 thread starter macrumors member

    Joined:
    Aug 13, 2008
    #5
    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?
     
  6. ArtOfWarfare macrumors 604

    ArtOfWarfare

    Joined:
    Nov 26, 2007
    #6
    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.
     
  7. theSeb, Feb 8, 2015
    Last edited: Feb 8, 2015

    theSeb macrumors 604

    theSeb

    Joined:
    Aug 10, 2010
    Location:
    Poole, England
    #7
    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.
     
  8. bbeagle macrumors 68040

    bbeagle

    Joined:
    Oct 19, 2010
    Location:
    Buffalo, NY
    #8
    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.
     
  9. bbeagle macrumors 68040

    bbeagle

    Joined:
    Oct 19, 2010
    Location:
    Buffalo, NY
    #9
    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.
     
  10. cipo macrumors member

    Joined:
    Nov 23, 2010
    Location:
    Stuttgart, Germany
    #10
    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.
     
  11. kage207 macrumors 6502a

    Joined:
    Jul 23, 2008
    #11
    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.
     
  12. grandM, Apr 28, 2015
    Last edited: Apr 28, 2015

    grandM macrumors 6502a

    grandM

    Joined:
    Oct 14, 2013
    #12
    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
        }
     
  13. bbeagle macrumors 68040

    bbeagle

    Joined:
    Oct 19, 2010
    Location:
    Buffalo, NY
    #13
    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.
     
  14. grandM macrumors 6502a

    grandM

    Joined:
    Oct 14, 2013
    #14
    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
        }
     
  15. bbeagle macrumors 68040

    bbeagle

    Joined:
    Oct 19, 2010
    Location:
    Buffalo, NY
    #15
    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!)
    
     
  16. grandM macrumors 6502a

    grandM

    Joined:
    Oct 14, 2013
    #16
    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
        }
     
  17. grandM macrumors 6502a

    grandM

    Joined:
    Oct 14, 2013
    #17
    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
        }
     
     

Share This Page