Swift 2.2 breaks optionals/unwrapping optionals - ios

Swift 2.2 has broken almost all my code. Even this simple string assigning to label doesn't work anymore:
cell.categoryName.text = peopleArray![indexPath.row]["Name"] as? String
The error says "Downcast from 'String?!' to 'String' only unwraps optionals, did you mean to use '!!'?"
What changes do I have to do now.
EDIT:
More Problems:
if (dict["data"]!["DataDetails"] as! NSArray).count == 0 {
}
Due to this I am getting a segmentation fault and the error shows this: warning: cast from 'String?!' to unrelated type 'NSArray' always fails
UPDATE:
I was using NSDictionaries, NSArrays in my classes that seems to cause the problem. Changing all the literals from Obj-C to swift made the code work properly.
So, I will also recommend other developers to prefer swift literals.

it seems there are some slight differences when using the swift types and the objective-c NS... types
eg
let dic:NSDictionary? = ["a":"a"]
let str:NSString? = dic!["a"] as? NSString
let dic2:Dictionary? = ["b":"b"]
let str2:String? = dic2!["b"] //dont need to do any casting, already optional
print(str)
print(str2)
prints
Optional(a)
Optional("b")
so depending on how your array / dictionary is defined, you might need different casting/unwrapping

Related

Intricate access to dictionary key

From a server I receive a JSON string, then I try to convert it to an NSDictionary this way:
let JSON = try NSJSONSerialization.JSONObjectWithData(rToData!, options:[])
guard let JSONDictionary:NSDictionary = (JSON as! NSDictionary) else {
print("My grandma is way more NSDictionary than this")
return
}
Once converted, I try to get some data contained in the dictionary: in particular I need an array I can access this way:
let myArray = JSONDictionary["data1"][0]["data2"];
XCode really doesn't like this idea, it puts an arrow under the first bracket and says Value of optional type "AnyObject?" not unwrapped, did you mean to use "!" or "?" ?. I follow its suggestion and I insert a "!", converting my preceding code to this:
let myArray = JSONDictionary["data1"]![0]["data2"];
At this point, the following line (where I count the number of elements in data2) shows an error, stating AnyObject has no member count.
The only thing that seems to work fine is this solution but, apart from being ugly and unreadable, I really don't understand it:
let myArray = (JSONDictionary["data1"]?[0]["data2"])!;
Can you help me understand why this basic access to a key in a dictionary must be so intricate?
I must say I like Swift but I spend a lot of time dealing with optionals and bizarre XCode alerts.
There is no guarantee that your JSON dictionary will contain a value for the key data1 (OK, you know it will, but Swift doesn't) so JSONDictionary["data1"] returns an optional. You need to unwrap the optional with ? or !
Also, since you have an NSDictionary, not a Swift dictionary, Swift doesn't know the type of the values, so they are AnyObject. Now again, you know it is an array, but Swift doesn't so you get an error stating that AnyObject doesn't have a count method.
While it is more verbose, it is cleaer for both the compiler and anyone else looking at your code if you split the line into multiple lines. It also lets you downcast the various objects so that Swift knows what is going on and handle any malformed JSON;
if let array1 = JSONDictionary["data1"] as? NSArray {
if let dictionary1 = array1[0] as? NSDictionary {
if let data2Array = dictionary1["data2"] as? NSArray {
let count=data2Array.count
}
}
}
You could implement appropriate else statements to handle errors
Optionals are one of Swift's most powerful features. They help avoid a whole family of bugs associated with uninitialised variables and special sentinnel values for boundary conditions. It is important that you learn how they can help you and not just throw ? or ! at your code until it compiles.

Int64 does't work while Int works

I'm receiving a JSON and I want to parse it to get a value. I'm doing this
let ddd = oneJson["restaurantId"] as! Int
print("ddd = \(ddd)")
let restaurantId = oneJson["restaurantId"] as! Int64
as you see, I'm parsing the same field. Here's my JSON
"location":"location here location","restaurantId":0
The print statement works just fine, but I get an exception on oneJson["restaurantId"] as! Int64
I love this quirk in swift (NOT).
It's one of the least intuitive gotchas of the language I know of. So it turns out that when you get a Dictionary with type AnyObject, Ints, Doubles, Floats, ARE NOT stored as the Swift native types. They're stored as... surprise! NSNumber.
Which leads to a whole host of unintuitive behavior, for instance type checking AnyObjects to see whether you have a Double or an Int (it can't be done).
For the same reason, your code is failing. Change it to:
let ddd = oneJson["restaurantId"] as! Int
print("ddd = \(ddd)")
let restaurantId = (oneJson["restaurantId"] as? NSNumber)?.longLongValue
And remind yourself again and again that when it's an AnyObject you're casting from, Swift is hiding from you the fact that it does a cast from NSNumber to Swift base types, and that in truth, they're still just NSNumbers.
I would recommend, not to use Int64 (or Int32). Int will be working in most cases.
See this post about the different integers in Swift: https://stackoverflow.com/a/27440158/4687348
Yes, it's a known bug in Swift 3, which has been solved in Swift 4.
Now, you just write like this,
let n = NSNumber.init(value: 9223372036854775807) // 2^63 - 1
print(n, n as! Int64) // will print the right answer.

Cannot subscript a value of type [String: AnyObject]? with an index of type String

I know have many the same question but still cannot find the way to fix my error. Please see the image for more detail. I used Xcode 7 and swift 2.0
Edit: fcking the warning of Swift. finnaly (change?[NSKeyValueChangeNewKey]?.boolValue)! fixed the error
change is an optional. Either unwrap the optional
let isCaptureStillImage = change![NSKeyValueChangeNewKey]!.boolValue
or use optional bindings
if let changeNewKey = change?[NSKeyValueChangeNewKey] {
let isCaptureStillImage = changeNewKey.boolValue
...

How to cast an typed array to one of the types protocols?

I have this in my playground:
func foo(printables: [Printable]) {
// do something
}
enum MenuOptions: String, Printable {
case ChangePickup = "Change Pickup Time"
case RequestSupport = "Request Support"
var description: String {
get {
return self.rawValue
}
}
}
var menuOptions: [MenuOptions] = [.ChangePickup]
foo(menuOptions)
let printables = menuOptions as [Printable]
Both last lines give a compiler error. I would expect menuOptions to be implicitly casted to [Printable] but the compiler complains with a [MenuOptions] is not convertible to [Printable] error. Am I missing something?
If I do menuOptions as! AnyObject as! [Printable] the compiler doesn't complain and the code works properly, but this seems dirty. Funnily enough, doing foo(menuOptions.map { $0 }) also works! Is this just a compiler bug?
You are trying to create a generic function constrained to objects that conform to the Printable protocol. Just define it differently:
func foo<T: Printable>(printables: [T]) {
// do something
}
The function will now work in your context. It will take an array of any object type that conforms to Printable.
Here are the relevant docs on Generics, specifically Type Constraints.
As to why this works, but your definition doesn't - this is just the Swift syntax. Printable is a protocol, but T is a type (generic, but still a type). You want your function to accept any type (T) that conforms to a protocol (<T: Protocol>). This is just how the language has been designed.
Using menuOptions as! AnyObject as! [Printable] may work in this example, but it is a bad practice. Using as! is like telling the compiler "trust me, this will work." This is fine in limited cases, but sets you up for trouble in the long run. Using as! turns off the compiler's type checking, which means if there is a problem it will happen at runtime, and your app will crash.
Again, I highly suggest reading the docs on Type Constraints, which I linked above. It explains it in detail.
I found that this is working. It seems that Swift compiler is not that smart.
var menuOptions: [MenuOptions] = [MenuOptions.ChangePickup]
let printables:[Printable] = menuOptions as! [Printable]
foo(printables)
I don't know why these are not working
foo(menuOptions)
foo(menuOptions as! [Printable])

Error: Deployment Update target 8.3 NSMutableArray and addObjectsFromArray - swift

After updating the xcode and my device some functions are not running anymore.
see It:
var jsonUnico: NSMutableArray! = jsonResult["lista"] as? NSMutableArray
self.tableList.addObjectsFromArray(jsonUnico)
Error: Cannot invoke 'addObjectsFromArray' with an argument list of type '(NSMutableArray!)'
It was working yesterday before upgrading
note: the tablelist is an NSMutableArray
Swift 1.2 no longer implicitly converts between NSArray and Swift’s native array type – you need to explicitly cast from one to the other. Since addObjectsFromArray takes a Swift array, that means you need to convert it to [AnyObject].
Normally you’d get a more helpful error message: error: 'NSMutableArray' is not implicitly convertible to '[AnyObject]'; did you mean to use 'as' to explicitly convert?, with a offer to “fix-it”. But it looks like this isn’t happening because of your use of implicitly-unwrapped optional NSMutableArray!.
But… this isn’t such a bad thing, since using implicitly-unwrapped optionals like that when fetching values out of dictionaries is dangerous (if the entry is ever not there, your app will crash). An alternative is:
if let jsonUnico = jsonResult["lista"] as? NSMutableArray {
let tableList = NSMutableArray()
// Xcode will recommend adding the "as [AnyObject]"
tableList.addObjectsFromArray(jsonUnico as [AnyObject])
}
But since you’re already doing an as above it, you may as well combine them:
if let jsonUnico = jsonResult["lista"] as? [AnyObject] {
tableList.addObjectsFromArray(jsonUnico)
}

Resources