Question on Dictionary error

Could someone Please help.


I am reading "Learn IOS 8 App Development" and I have hit a road block on a chapter that explains userDefaults. The author gives an example extending a MapKit MKPointAnnotation but I'm getting the following error with the get{ } portion of the new property propertyState where it returns the dictionary. Would anyone know why it's throwing this error. "Type of expression is ambiguous without more context"


Also on the set{ } block I had to change the "title" from title = newValue[LocationKey.Title.rawValue] as NSString. To

title = newValue[LocationKey.Title.rawValue] as! NSString as String. Note: I came to this by following the Fixit popups. Is this correct? I don't

understand why (if this is correct) I would cast the newValue to NSString and then to String.


Thanks


Dave.




import MapKit

enum LocationKey: String {

case Latitude = "lat"

case Longitude = "long"

case Title = "title'"

}

extension MKPointAnnotation {


var propertyState: [NSObject: AnyObject] {

get {

return [ LocationKey.Latitude.rawValue: NSNumber(double: coordinate.latitude),

LocationKey.Longitude.rawValue: NSNumber(double: coordinate.longitude),

LocationKey.Title.rawValue: title ]

}

set {

let lat = (newValue[LocationKey.Latitude.rawValue] as! NSNumber).doubleValue

let long = (newValue[LocationKey.Longitude.rawValue] as! NSNumber).doubleValue

coordinate = CLLocationCoordinate2D(latitude: lat, longitude: long)

title = newValue[LocationKey.Title.rawValue] as! NSString as String

}

}

}

Answered by OOPer in 68238022

First Answer.

Swift warns for this sort of assignment:

title = newValue[LocationKey.Title] as! String

Treating a forced downcast to 'String' as optional will never produce 'nil'

I just wanted to avoid warning generating code. (I tried to explain why Swift warns us in this case, but had given up, that might seem more of a guess than an explanation.)


Second Answer.

Was this a good decision or should I have started with Objective C ( which I know nothing about right now)?

Yes, it was a good decision.

Swift has some features common with C#. I have no doubt about C# affected the design of Swift language (as well as some other languages), so you feel more familliar with Swift than Objctive-C.

`Similar but not same` makes some part of your learning tough, or you may need to learn Objective-C when you start to write practical apps, but I believe such things are in an acceptable range.


Is Core Data a good way to persist data

Yes. I do both client side and server side. I've been working with SQL Server, MySQL, PostgreSQL, DB/2 or Oracle... when writing server side code. But I choose Core Data to store data, even if it seems to fit for a simple db table, in iOS apps.


Third Answer.

Do IOS app's only connect to database's using API's.

Yes. It is not because iPhone can't connect directly to a network, but because it is an established and preferred way of providing database functionality to clients. As I wrote in another thread, connecting client devices directly to DBMS is thought to be outdated and should be avoided (mainly for security reasons).


As you know, I simplified many aspects and the answers are just my answers, my opinion.

First, the error at `get{}`.


Among the 6 elements found in the Dictionary literal, these values have clear specific types:

LocationKey.Latitude.rawValue, LocationKey.Longitude.rawValue and LocationKey.Title.rawValue are all Strings.

NSNumber(double: coordinate.latitude) and NSNumber(double: coordinate.longitude) are all NSNumbers.


So, let's find how the last thing `title` is declared.

title in MKShape (base class of MKPointAnnotation)

var title: String?

Its type is `String?` aka Optional<String>, so it cannot be used as a value of [NSObject : AnyObject], which value is AnyObject -- non-Optional.


So, if you are sure that `title` may never be nil, you can write the get block as:

        get {
            return [ LocationKey.Latitude.rawValue: NSNumber(double: coordinate.latitude),
                LocationKey.Longitude.rawValue: NSNumber(double: coordinate.longitude),
                LocationKey.Title.rawValue: title!]
        }


Second:

Note: I came to this by following the Fixit popups. Is this correct?

As noted above, the Dictionary's Value type is AnyObject, and the type of `title` is `String?`. AnyObject values cannot be assigned to `String?` property.

So you just need to cast AnyObject to `String?`, which would be like this:

            title = newValue[LocationKey.Title.rawValue] as! String?

This code should work as well as your code. (If newValue[LocationKey.Title.rawValue] contains a value which cannot be convertible to String, both codes crash.)

"OOPer:


Thank you so much for replying and helping. Your answer pointed out a number of things that I now realize I don't totally understand. First from my understanding of Swift dictionaries, the keys and value's must all be the same type, ie: All key's are strings, all values are Int's. etc.


First question: Since the property is of type "var propertyState: [NSObject: AnyObject]" Does that mean that the key can be anything, and the value can be anyobject? I know that all the key's are strings, but for values we have two NSNumber(double: value). I'm not positive but I'm guessing this converts coordinate.latitude and coordinate.longitude to doubles. Thus we get a dictionary [string: double, string: double, string: string]. Now does this work because

all three values conform to being AnyObject? Also, when you said Title is type 'String?' which is an Optional (can be null) and not a AnyObject, by Unwrapping it as "LocationKey.Title.rawValue: title!" does that now make it an AnyObject to fit the dictionary type.


Second Question: I don't totally understand the set{ } portion of this property, does the following statement create a new dictionary. What variables does this setter actually set.


set {

let lat = (newValue[LocationKey.Latitude.rawValue] as! NSNumber).doubleValue

let long = (newValue[LocationKey.Longitude.rawValue] as! NSNumber).doubleValue

coordinate = CLLocationCoordinate2D(latitude: lat, longitude: long)

title = newValue[LocationKey.Title.rawValue] as! NSString as String

}


Sorry for the questions. I'm just obsessed with understanding everything I can.


Dave.

First answer(a little bit long for a single Q & A):

Does that mean that the key can be anything, and the value can be anyobject?

Very near, but cannot say correct. In Swift, the type NSObject should not be called as `anything`, as, in pure Swift, you can define classes that have no base classes, instances of such classes cannot be stored in varible of type NSObject.

AnyObject is a special thing in Swift and it can contain any ARC compatible refereces, Objective-C instances, CF type objects and also pure Swift class instances. So all things conforming to NSObject can be stored in AnyObject variable, but AnyObject can contain wider variety of thins. (Precisely how wide depends on the current implementation of Swift and chaging...)


And when you import Foundation (as you know it's indirectly imported with most Apple's framework), some Swift types can be automatically (or with always-success-as operator, some cases) converted to corresponding Objective-C types:

String -> NSString

Array -> NSArray

Int, UInt, Double, Float, Bool -> NSNumber


Also Optional is another special thing in Swift, you need explicit unwrapping or failable-as!/as? casting to retrieve non-Optional values.


Second answer:

What variables does this setter actually set.

`newValue` is given as a `value to be set` in the property settting statement:

var annotation = MKPointAnnotation()
annotation.propertyState = ["lat": 45, "long": 135, "title": "Japan"]

In the above example, the Dictionary value `["lat": 45, "long": 135, "title": "Japan"]` is passed to `newValue`, and then set to `coordinate` and `title`, which are the properties of MKMapAnnotation.


If I write such extesion, I would change the type of propertyState to `[LocationKey: AnyObject]` and write the computed property as:

    var propertyState: [LocationKey: AnyObject] {
        get {
            return [LocationKey.Latitude: coordinate.latitude,
                LocationKey.Longitude: coordinate.longitude,
                LocationKey.Title: title!]
        }
        set {
            let lat = newValue[LocationKey.Latitude] as! Double
            let long = newValue[LocationKey.Longitude] as! Double
            coordinate = CLLocationCoordinate2D(latitude: lat, longitude: long)
            title = newValue[LocationKey.Title] as! String?
        }
    }

With this property, you can write someting like this:

var annotation = MKPointAnnotation()
annotation.propertyState = [.Latitude: 45, .Longitude: 135, .Title: "Japan"]

Or this sort of change is already on your book?

(In fact, I prefere using tuples than structure-fixed Dictionary literals...)


This sort of extension is not needed to write apps, but sometimes makes your code pretty readable.


ADDITON: Tuple version and its usage:

    var propertyState: (lat: Double, long: Double, title: String?) {
        get {
            return (coordinate.latitude,
                coordinate.longitude,
                title)
        }
        set {
            let lat = newValue.lat
            let long = newValue.long
            coordinate = CLLocationCoordinate2D(latitude: lat, longitude: long)
            title = newValue.title
        }
    }


var annotation = MKPointAnnotation()
annotation.propertyState = (45, 135, "Japan")

(Too much for one time? Waiting for another Question.)

OOper:


OK I must admit it took my like a day to digest and finally understand what you all said. Thank you so much, I actually understand and get what the you said and what the Author was trying to explain. (Saving a dictionary of values in userDefaults). I hope you have time for more questions 🙂


First Question: From what you said AnyObject cannot contain an Optional thus you forcibly downcast the .title to a string in the getter of the computed property and then because the .title property in the MKPointAnnotation is an Optional you downcast that to an optional String? before setting it. What I don't get is why

do this: "title = newValue[LocationKey.Title] as! String?" Wouldn't title = newValue[LocationKey.Title] as! String downcast it to an Optional<String>.


The reason I ask is this. I did some needed reading to better understand downcasting using 'is', 'as!', 'as?' and from what I read if you do the following

let myNumber = '[Key1" : "1", "key2" : 2, "key3" : 1.055]

var TestVal = myNumber["key1"] as? String // this will convert the value in key1 to an optional String?

Print(TestVal) // you getOptional("1")\n"


they way you did it I would have stated "var TEstVal = myNumber[key1"] as? String?" // note the ? at the end of 'String?'


Second Question. This is more of a informational big picture question. Pardon my ignorance on this but I'm still in the IOS App development for Dummies mode of learning 🙂


I am a .Net Developer by trade. The company that I work for has been gracious enough to allow me to learn IOS app developement to create app's for our company. It's a Win Win for both of us. Anyway I have been teaching myself by watching Lynda training classes (Swift Essential Training, and IOS App developement with Swift) and now reading the book "Learn IOS 8 App Developement" I have been trying to lay out a logical direction of learning for myself to get up to speed. My question is this.


I chose Swift because I was under the impression that it was the newest language and the direction that IOS app developement will be using in the future. Was this a good decision or should I have started with Objective C ( which I know nothing about right now)?


Also, the apps that I will be writing at first will be data entry apps to coincide with .Net / MS Sql database applications that I wrote and are currently using. My goal is to allow a salesperson to enter a customer complain (via my app) and save the record to the device off line. When the use is connected to the internet connect to a .Net WCF API and upload the data. What is the best way to do this. I have heard about several technologies via Google and YouTube. Core Data, SQLite, and connecting to an API. Is Core Data a good way to persist data to a db on the device and then connect to the API to upload the data. Or do developers save data directly to SQLite etc.


Third Question: Do IOS app's only connect to database's using API's. When I write a .Net application I connect using ADO.Net or Entity framework to the data store. Usually a MS Sql Server. Do you have to connect using a web service API etc because the IPhone can't connect directly to a network and only has internet connectivity.


Thanks


Dave.

Accepted Answer

First Answer.

Swift warns for this sort of assignment:

title = newValue[LocationKey.Title] as! String

Treating a forced downcast to 'String' as optional will never produce 'nil'

I just wanted to avoid warning generating code. (I tried to explain why Swift warns us in this case, but had given up, that might seem more of a guess than an explanation.)


Second Answer.

Was this a good decision or should I have started with Objective C ( which I know nothing about right now)?

Yes, it was a good decision.

Swift has some features common with C#. I have no doubt about C# affected the design of Swift language (as well as some other languages), so you feel more familliar with Swift than Objctive-C.

`Similar but not same` makes some part of your learning tough, or you may need to learn Objective-C when you start to write practical apps, but I believe such things are in an acceptable range.


Is Core Data a good way to persist data

Yes. I do both client side and server side. I've been working with SQL Server, MySQL, PostgreSQL, DB/2 or Oracle... when writing server side code. But I choose Core Data to store data, even if it seems to fit for a simple db table, in iOS apps.


Third Answer.

Do IOS app's only connect to database's using API's.

Yes. It is not because iPhone can't connect directly to a network, but because it is an established and preferred way of providing database functionality to clients. As I wrote in another thread, connecting client devices directly to DBMS is thought to be outdated and should be avoided (mainly for security reasons).


As you know, I simplified many aspects and the answers are just my answers, my opinion.

Question on Dictionary error
 
 
Q