I have a function:
func setId(id: Int64?) {
self._id = id
}
This function is called here:
let defaults = UserDefaults.standard
let id = defaults.object(forKey: "userId") as? String
if (id != nil) {
setId(id: Int64(id!))
}
In the function setId() the value of id always seems to be nil. When debugging and stepping one call up in the stack, to the caller (2nd piece of code) the value of id is
po id:
Optional<String>
- some : "Optional(350350002)"
When I check it as Int64:
po Int64(id!):
nil
When I check it with a hardcoded value:
po Int64(Optional("350350002")!):
Optional<Int64>
- some : 350350002
Also po Int64((id as NSString)): nil
What is going on here? How does the hardcoded value differ from the actual variable?
Thanks.
The problem was that the following was saved to UserDefaults: String(describing: self._userId), of course saving the actual word 'Optional(...' with it, as String.
I thought the debugger was showing me an Optional with value 350350002 instead of an actual string: "Optional(350350002)" + the saving to UserDefaults code was not written by me, why I didn't notice this.
Related
I have the following code in a playground file:
extension Dictionary {
func test() {
for key in self.keys {
self[key]
}
}
}
var dict: [String: AnyObject?] = [
"test": nil
]
dict.test()
I will henceforth refer to the line within the for-each loop as the output since it is what's relevant. In this particular instance the output is nil.
When I change the for-each loop to look like this:
for key in self.keys {
print(self[key])
}
The output is "Optional(nil)\n".
What I really want to do is check the value for nil, but the code:
for key in self.keys {
self[key] == nil
}
outputs false.
One other thing I tried was the following:
for key in self.keys {
self[key] as! AnyObject? == nil
}
which produces the error:
Could not cast value of type 'Swift.Optional<Swift.AnyObject>' to 'Swift.AnyObject'
Any help with this is much appreciated!
You've gotten yourself into kind a mess, because a dictionary whose values can be nil presents you with the prospect of a double-wrapped Optional, and therefore two kinds of nil. There is the nil that you get if the key is missing, and then the nil that you get if the key is not missing and you unwrap the fetched result. And unfortunately, you're testing in a playground, which is a poor venue for exploring the distinction.
To see what I mean, consider just the following:
var d : [String:Int?] = ["Matt":1]
let val = d["Matt"]
What is the type of val? It's Int?? - an Int wrapped in an Optional wrapped in another Optional. That's because the value inside the dictionary was, by definition, an Int wrapped in an Optional, and then fetching the value by its key wraps that in another Optional.
So now let's go a bit further and do it this way:
var d : [String:Int?] = ["Matt":nil]
let val = d["Matt"]
What is val? Well, the playground may say it is nil, but the truth is more complicated; it's nil wrapped in another Optional. That is easiest to see if you print val, in which case you get, not nil, but "Optional(nil)".
But if we try for something where the key isn't there at all, we get a different answer:
let val2 = d["Alex"]
That really is nil, signifying that the key is missing. And if we print val2, we get "nil". The playground fails to make the distinction (it says nil for both val and val2), but converting to a String (which is what print does) shows the difference.
So part of the problem is this whole double-wrapped Optional thing, and the other part is that the Playground represents a double-wrapped Optional in a very misleading way.
MORAL 1: A dictionary whose value type is an Optional can get really tricky.
MORAL 2: Playgrounds are the work of the devil. Avoid them. Use a real app, and use logging to the console, or the variables pane of the debugger, to see what's really happening.
Checking if a dictionary value is nil makes sense only if the
dictionary values are an Optional type, which means that you have to
restrict your extension method to that case.
This is possible by defining a protocol that all optional types conform
to (compare How can I write a function that will unwrap a generic property in swift assuming it is an optional type?, which is just a slight modification of Creating an extension to filter nils from an Array in Swift):
protocol OptionalType {
typealias Wrapped
var asOptional : Wrapped? { get }
}
extension Optional : OptionalType {
var asOptional : Wrapped? {
return self
}
}
Now you can define an extension method with is restricted to
dictionaries of optional value types:
extension Dictionary where Value : OptionalType {
func test() {
for key in self.keys { ... }
}
}
As Matt already explained, self[key] can only be nil if that key
is missing in the dictionary, and that can not happen here. So you
can always retrieve the value for that key
let value = self[key]!
inside that loop. Better, enumerate over keys and values:
for (key, value) in self { ... }
Now value is the dictionary value (for key), and its type
conforms to OptionalType. Using the asOptional protocol property
you get an optional which can be tested against nil:
if value.asOptional == nil { ... }
or used with optional binding.
Putting all that together:
extension Dictionary where Value : OptionalType {
func test() {
for (key, value) in self {
if let unwrappedValue = value.asOptional {
print("Unwrapped value for '\(key)' is '\(unwrappedValue)'")
} else {
print("Value for '\(key)' is nil")
}
}
}
}
Example:
var dict: [String: AnyObject?] = [
"foo" : "bar",
"test": nil
]
dict.test()
Output:
Value for 'test' is nil
Unwrapped value for 'foo' is 'bar'
I'm getting the error message Argument labels '(of:)' do not match any available overloads. Below is the code I'm using.
let prefs = UserDefaults.standard
var id: String!
if var array = prefs.string(forKey: "myArray"){
if let index = array.index(of: id) {
array.remove(at: index)
prefs.setValue(array, forKey: "myArray")
}
}
I've seen a lot of answers on Stack Overflow with very similar code to that. So I'm not quite sure why this wouldn't be working.
Basically I'm just trying to remove the element in the array that = id then set that new array to the user defaults.
Update
Just updated the code above to show how array is getting defined. id is a string that is defined in a separate section.
By accessing prefs.string(forKey: "myArray"), you are getting a String, not an array of strings. You should use this:
if var prefs.array(forKey: "myArray") as? [String] { }
or
if var prefs.value(forKey: "myArray") as? [String] { }
Make sure to not forget putting as! [String], because the first method returns [Any], an which can contain objects of any type, not specifically String. Then your error should be solved, because index(of: ) can only be used on Arrays of specified types.
Hope it helps!
Just make an alt + Click on an "array" variable to make sure it is of type Array ([String]), not a String. To apply .index(of:) method it must be an array.
Like this:
String does not have a method .index(of:). That's what the error is pointing at. And sure make a cast to [String]? if it fits.
I set up breakpoints all along my function, everything is running fine, but when it comes to the print part, it always skips over it.
func testForGettingAllValues(){
let uid = user2?.id
let ref = FIRDatabase.database().reference().child("user-reviews").child(uid!)
ref.observe(.childAdded, with: {(firstSnapshot) in
let reviewId = firstSnapshot.key
let messageReference = FIRDatabase.database().reference().child("reviews").child(reviewId)
messageReference.queryOrdered(byChild: "ratingNumber").observeSingleEvent(of: .value, with: {(reviewSnapshot) in
if reviewSnapshot.exists(){
if let values = reviewSnapshot.value as? [String:AnyObject]{
for reviews in values {
if let ratingNumber = reviews.value["ratingNumber"] as? String{
print("This is the ratingNumber list", ratingNumber)
}
}
}
}
})
})
}
}
In you nestedness, the following holds:
if binding to values is successful, it is of type [String:AnyObject]
each reviews instances is a named tuple of type (key: String, value: AnyObject)
when you try to bind to ratingNumber, you access the value property of reviews, and act as if this value property is a dictionary itself. However, it is not, it is of type AnyObject.
Most likely, in the third bullet above, lies the logical error, which in turn cause the innermost optional binding to fail. The compiler cannot know whether or not the AnyObject instance accessible by reviews.value is in fact wrapping a dictionary (an NSDictionary, perhaps?). You could attempt to first attempt a type conversion of it to the dictionary type you believe it to be, and thereafter attempting to access the "ratingNumber" key of the converted value.
Possibly (without the chance to test this out myself, due to your lack of a reproducable example ...):
if let ratingNumber = (reviews.value as? [String: String])?["ratingNumber"]
I am using Vapor for Swift backend. Following is the code i am working with.
drop.post("postTodo") { request in
var jsonContent: JSON?
if let contentType = request.headers["Content-Type"], contentType.contains("application/json"), let jsonData = request.json {
jsonContent = jsonData
print("Got JSON: \(jsonContent)")
}
guard let id = jsonContent?.node.object?["id"]?.string
else {
return JSON(["message": "Please include mandatory parameters"])
}
let tempId = Int(id)!
I am getting "id" as optional string for eg: Optional("123") for jsonContent?.node.object?["id"]?.string
When I try to convert it to int using Int(id)! i get back nil
If i try to do let tempId = Int(id!) it gives error.
But when i do the same thing in playground i get proper int value.
let id: String?
id = "1234"
let myInt = Int(id!)
Why Optional string to Int is not working properly in my Vapor app ?
Any idea.
If "id" is an optional string, then you probably don't want to be force unwrapping it with the "!".
The safest approach would be something like:
if let id = id
{
let myIdAsInt = Int(id)
}
The reason it "works" in the playground, is you are definitely assigning a non-nil value to the string (therefore you get away with the force unwrap).
String!might contain a string, or it might contain nil. It’s like a regular optional, but Swift lets you access the value directly without the unwrapping safety. If you try to do it, it means you know there’s a value there – but if you’re wrong your app will crash.
var optionalString: String? = "123"
// first check if it doesn't contain nil
if let str = optionalString {
// string to -> Int
if let id = Int(str) {
print(id) // work with id
}
} else {
// optionalString contains nil
}
what i found is in my iOS code i had a struct with optional properties coz of which when mapped to Dict gave object with optional values to keys.
If I make properties non optional and send it to vapor backend after it works fine.
So basically it was the case of using Optionals properly.
I am doing what I believe to be a very simple task. I'm trying to get a value out of a dictionary if the key exists. I am doing this for a couple keys in the dictionary and then creating an object if they all exist (basically decoding a JSON object). I am new to the language but this seems to me like it should work, yet doesn't:
class func fromDict(d: [String : AnyObject]!) -> Todo? {
let title = d["title"]? as? String
// etc...
}
It gives me the error: Operand of postfix ? should have optional type; type is (String, AnyObject)
HOWEVER, if I do this, it works:
class func fromDict(d: [String : AnyObject]!) -> Todo? {
let maybeTitle = d["title"]?
let title = maybeTitle as? String
// etc...
}
It appears to be basic substitution but I may be missing some nuance of the language. Could anyone shed some light on this?
The recommended pattern is
if let maybeTitle = d["title"] as? String {
// do something with maybeTitle
}
else {
// abort object creation
}
It is possibly really a question of nuance. The form array[subscript]? is ambiguous because it could mean that the whole dictionary (<String:AnyObject>) is optional while you probably mean the result (String). In the above pattern, you leverage the fact that Dictionary is designed to assume that accessing some key results in an optional type.
After experimenting, and noticing that the ? after as is just as ambiguous, more, here is my solution:
var dictionary = ["one":"1", "two":"2"]
// or var dictionary = ["one":1, "two":2]
var message = ""
if let three = dictionary["three"] as Any? {
message = "\(three)"
}
else {
message = "No three available."
}
message // "No three available."
This would work with all non-object Swift objects, including Swift Strings, numbers etc. Thanks to Viktor for reminding me that String is not an object in Swift. +
If you know the type of the values you can substitute Any? with the appropriate optional type, like String?
There are a few of things going on here.
1) The ? in d["title"]? is not correct usage. If you're trying to unwrap d["title"] then use a ! but be careful because this will crash if title is not a valid key in your dictionary. (The ? is used for optional chaining like if you were trying to call a method on an optional variable or access a property. In that case, the access would just do nothing if the optional were nil). It doesn't appear that you're trying to unwrap d["title"] so leave off the ?. A dictionary access always returns an optional value because the key might not exist.
2) If you were to fix that:
let maybeTitle = d["title"] as? String
The error message changes to: error: '(String, AnyObject)' is not convertible to 'String'
The problem here is that a String is not an object. You need to cast to NSString.
let maybeTitle = d["title"] as? NSString
This will result in maybeTitle being an NSString?. If d["title"] doesn't exist or if the type is really NSNumber instead of NSString, then the optional will have a value of nil but the app won't crash.
3) Your statement:
let title = maybeTitle as? String
does not unwrap the optional variable as you would like. The correct form is:
if let title = maybeTitle as? String {
// title is unwrapped and now has type String
}
So putting that all together:
if let title = d["title"] as? NSString {
// If we get here we know "title" is a valid key in the dictionary, and
// we got the type right. title has now been unwrapped and is ready to use
}
title will have the type NSString which is what is stored in the dictionary since it holds objects. You can do most everything with NSString that you can do with String, but if you need title to be a String you can do this:
if var title:String = d["title"] as? NSString {
title += " by Poe"
}
and if your dictionary has NSNumbers as well:
if var age:Int = d["age"] as? NSNumber {
age += 1
}