ambiguous use of subscript swift 2.2 - swift

I have a lot of issues in my code with this error. Hopefully if someone can help me here than I can figure out the rest of the problems. I have updated to xcode 7.3 and running swift 2.2.
I have read that the compiler has been "more restrictive", and I have to tell it what the "intermediary" objects are. This is causing me some confusion and would love further explanation.
func getMessage(dictionary:NSDictionary)->String{
var message = String()
if let dict = dictionary["aps"] {
if let message:String = dict["alert"] as? String {
return message
}
else{
message = ""
}
}
return message
}
Another example:
for object in objects {
let getDriver = object.objectForKey("driver")
if let picture = getDriver!["thumbnailImage"] as? PFFile {
self.profilePictures.append(picture)
}
self.requestsArray.append(object.objectId as String!)
}

The type of a dictionary value is always AnyObject. Cast the type to something more specific for example
if let dict = dictionary["aps"] as? [String:AnyObject] {
then the compiler knows that key subscripting is valid and possible
The second example is similar: object is a dictionary and the compiler needs to know that the value for key driver is also a dictionary
if let getDriver = object.objectForKey("driver") as? [String:AnyObject] {
if let picture = getDriver["thumbnailImage"] as? PFFile {
...

Related

Swift cast if possible

I have this code
let jsonData = try JSONSerialization.jsonObject(with: data, options: []) as! [Any?]
if var first = jsonData[0] as! String?{
if(first=="Error"){
DispatchQueue.main.async(execute: {
self.postNotFoundLabel.isHidden = false
});
}else if(first=="Empty"){
print("Empty")
}
}
What i want to do is to cast jsonData[0] to String if it's possible and if it's not then move on.But instead when it's not possible application stops and gives me an error
Could not cast value of type '__NSDictionaryI' (0x1092054d8) to 'NSString' (0x108644508).
How can i cast only when it's possible?
You are trying to force-cast to an optional String. That's not what you want.
Change:
if var first = jsonData[0] as! String? {
to:
if var first = jsonData[0] as? String {
This tries to cast to String. If jsonData[0] isn't actually a String, you get nil and the if var fails.
And you probably want if let, not if var since you don't seem to be making any change to first.
First of all JSON objects will never return optional values so [Any?] is nonsense.
Second of all the error message says the type cast to string is inappropriate because the type of the result is actually a dictionary.
Solution: Check the type for both String and Dictionary
if let jsonData = try JSONSerialization.jsonObject(with: data) as? [Any],
let first = jsonData.first {
if let firstIsDictionary = first as? [String:Any] {
// handle case dictionary
} else if let firstIsString = first as? String {
// handle case string
}
}
PS: A type cast forced unwrap optional to optional (as! String?) is nonsense, too.
Here's the Swifty way to do what you're doing :)
guard let jsonData = try JSONSerialization.jsonObject(with: data, options: []) as? [Any?], let first = jsonData[0] as? String else {
DispatchQueue.main.async(execute: {
self.postNotFoundLabel.isHidden = false
});
return
}
if(first == "Empty") {
print(first)
}
Don't use as! if you are not sure that casting will succeed. The exclamation mark after the as keyword forces the casting, which throws an error if the casting does not succeed.
Use as? instead, which returns an optional variable of the type you were trying to casting to. If the casting fails, instead of throwing an error, it just returns nil.
let jsonData = try JSONSerialization.jsonObject(with: data) as? [Any]
if var first = jsonData.first as? String{
if(first=="Error"){
DispatchQueue.main.async(execute: {
self.postNotFoundLabel.isHidden = false
});
}else if(first=="Empty"){
print("Empty")
}
}

Swift Cannot assign through subscript: subscript is get only

I am fairly new to the Swift syntax and am receiving this error with my code "Cannot assign through subscript: subscript is get only"
This is from the line: friendDictionary[(friendUID as? String)!] = ["name": friendsData!["name"]]
Any advice on the correct way of doing it would be very helpful.
func getFriendsUIDs() {
if FBSDKAccessToken.currentAccessToken() == nil {
print("failed to start graph request")
return
}else{
}
if FBSDKAccessToken.currentAccessToken() != nil {
}
let parameters = ["fields": "name, id, picture"]
FBSDKGraphRequest(graphPath: "/me/friends", parameters: parameters).startWithCompletionHandler {
(NSURLConnection, result, requestError) in
let friendIds = result["id"] as? NSDictionary
let friendsData = friendIds!["data"] as? [NSDictionary]
var ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
ref.child("users").child((FIRAuth.auth()?.currentUser?.uid)!).child("friendUIDs").observeEventType(.Value, withBlock: { (snapshot) in
self.FriendUIDs = NSArray()
self.FriendUIDs = (snapshot.value as? NSArray)!
print(self.FriendUIDs)
var friendDictionary = NSDictionary()
for friendUID in self.FriendUIDs {
friendDictionary[(friendUID as? String)!] = ["name": friendsData!["name"]]
}
self.fetchFriendFeed(friendDictionary)
}) { (error) in
print(error.localizedDescription)
}
}
}
func fetchFriendFeed(friendDictionary: NSDictionary) {
var ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
for friendUID in FriendUIDs {
ref.child("users").child(friendUID as! String).child("Agenda").observeEventType(.ChildAdded, withBlock: { (snapshot) in
print(snapshot)
if let dictionary = snapshot.value as? [String: AnyObject] {
let friendPost = FriendPost()
friendPost.picture = friendDictionary[friendUID as! String]? ["picture"] as? String
friendPost.activity = dictionary["activity"] as? String
friendPost.date = dictionary["date"] as? String
friendPost.time = dictionary["time"] as? String
friendPost.friendname = friendDictionary[friendUID as! String]? ["name"] as? String
self.friendPosts.append(friendPost)
dispatch_async(dispatch_get_main_queue(), {
self.collectionView?.reloadData()
Nothing to do with Swift. You've elected to use Objective-C, in effect, by making friendDictionary an NSDictionary. NSDictionary is immutable; you can't assign into it or alter it in any way. That is simply a fact about Objective-C. The Swift var declaration makes no difference to this fact.
A better choice, since you are writing in Swift, would be to use a Swift dictionary, which is [AnyHashable:Any]() (in Swift 3). This will interchange with NSDictionary when you are talking to Objective-C, but it will give you a mutable dictionary because you (rightly) declared it with var.
Have you tried using NSMutableDictionary? That solved the issue for me.
For those who get stuck here, another reason for this happens when you try to assign something that does not conform the actual dictionary, in my example i was doing something like this:
var dict = [Date : UUID]()
let randomUUID = UUID()
dict[randomUUID] = Date.now
whereas I meant to write UUID : Date but I was sleepy so i made a mistake, and Swift gave me a misleading error saying subscript is get-only. So this error also appears with type mismatch for Swift 5.7.

Init has been renamed to init(describing) error in Swift 3

This code works fine in Swift 2:
guard let userData = responseData["UserProfile"] as? [String : AnyObject] else { return }
var userProfileFieldsDict = [String: String]()
if let profileUsername = userData["Username"] as? NSString {
userProfileFieldsDict["username"] = String(profileUsername)
}
if let profileReputationpoints = userData["ReputationPoints"] as? NSNumber {
userProfileFieldsDict["reputation"] = String(profileReputationpoints)
}
But, in Swift 3 it throws an error on userProfileFieldsDict["reputation"] saying
init has been renamed to init(describing:)
My question is why does it trigger on that line and not on the userProfileFieldsDict["username"] assignment line, and how to go about fixing it? I'm assuming it's because I'm casting a NSNumber to a String, but I can't really understand why that matters.
NSNumber is a very generic class. It can be anything from a bool to a long to even a char. So the compiler is really not sure of the exact data type hence it's not able to call the right String constructor.
Instead use the String(describing: ) constructor as shown below
userProfileFieldsDict["reputation"] = String(describing: profileReputationpoints)
Here's more info about it.
You need to drop your use of Objective-C types. This was always a bad habit, and now the chickens have come home to roost. Don't cast to NSString and NSNumber. Cast to String and to the actual numeric type. Example:
if let profileUsername = userData["Username"] as? String {
userProfileFieldsDict["username"] = profileUsername
}
if let profileReputationpoints = userData["ReputationPoints"] as? Int { // or whatever
userProfileFieldsDict["reputation"] = String(profileReputationpoints)
}

Using NSURLConnection With Swift

I am using the following code to get data from an API:
typealias JSONdic = [String: AnyObject]
if let json = json as? JSONdic, history = json["history"] as? JSONdic, hour = history["hour"] as? String {
println(hour)
}
However, Xcode tells me that "json" is not a recognized identifier. I believe this can be solved with NSURLConnection, but I have no idea how to use that. Can anyone provide any examples of this protocol in use?
You're declaring a variable by setting it to itself, which doesn't make any sense. In order to use a variable on the right hand side of an assignment, it needs to have already been declared. So let's give json a value outside of the casting statements and it works fine.
typealias JSONdic = [String: AnyObject]
let json: AnyObject = ["greeting": "Hello"]
if let json = json as? JSONdic, history = json["history"] as? JSONdic, hour = history["hour"] as? String {
println(hour)
}

Swift: Stuck with a compiler error

While exploring a structure read from a json file, I've got this message on the “if let” line which I'm stuck with:
'String' is not a subtype of '(String, AnyObject)'
The code is as follows:
if let descriptions: Array<Dictionary<String,AnyObject>> = fields["description"] as? Array {
let description = descriptions[0]
if let text:String = description["text"] as? String { // where the error occurs
poi.description = text
}
}
You have to unwrap what's read from the description dictionary:
if let text:String = description["text"]! as? String { // where the error occurs
...
}
But that's not safe, because if the key is not found in the dict, it throws a runtime exception. A safer way is:
if let text:String = (description["text"] as AnyObject?) as? String { // where the error occurs
...
}
However, I presume that you're using NSJSONSerialization to deserialize your json data, so a better way to do that is to stick with obj-c types rather than pure swift data types with generics:
if let descriptions = fields["description"] as? NSArray {
let description = descriptions[0] as NSDictionary
if let text = description["text"] as? String {
let x = text
}
}
More compact and much easier to read.
Use the new syntax and less of it.
Test declarations: Dictionary of Array of Dictionary
// let testFields: [String:[[String:Any]]]
or
// let testFields: [String:[[String:String]]]
let testFields = ["description":[["text":"value"]]]
if let descriptions = testFields["description"] {
let description = descriptions[0]
if let text = description["text"] as String? {
println("text: \(text)")
}
}
Output:
text: value