Why I get nil in value, that I got by optional chaining(if let f == ..) [duplicate] - swift

This question already has answers here:
Unexpected nil check results for optional value in dictionary
(3 answers)
Closed yesterday.
In my opinion, var in optional chaining always would be not nil, but I get another results in my simple programm
Recently I started to learn swift, and write following code to print possiont of chessman or "killed" if position is null
typealias Chessman = [String: (alpha: Character, num: Int)?]
var Chessmans = Chessman()
Chessmans["White king"] = ("a", 8)
Chessmans["White horse"] = ("b", 7)
Chessmans.updateValue(nil, forKey: "Black horse")
func figure_info(key: String) -> Void{
print(key)
if let pos = Chessmans[key] {
print(pos)
} else {
print("killed")
}
}
for name in Chessmans{
figure_info(key: name.key)
}
In my opinion, var pos in function would be always not nil
but it's not correct!
Furthermore - pos has optional type! and killed never printing
Got this in output:
Black horse
nil
White horse
Optional((alpha: "b", num: 7))
White king
Optional((alpha: "a", num: 8))

Solved my problem!
What I needed it's unwarp value with nil before do let if
var pos = Chessmans[key] ?? nil
if let info = pos ...

Related

Check for nil in dictionary

I have a class in my app, where the user inputs values and i set them to an instance of the class, then i upload this data to Database, but i have to convert the class to something the database accepts and i'm converting to dictionary using Mirror Reflection. Some properties in my class can be nil, because by design not all properties are required. But i can't pass nil values to the database.
I have recreated my example is very simplified playground. i didn't set a value for the name property of the class
I tried to check for nil before adding the key, value pair to the dictionary
Below is my code
import UIKit
class Color: NSObject {
var name: String?
var code: Int?
var shade: String?
}
let cl = Color()
cl.code = 3456
cl.shade = "DARK"
var colorDict = [String: Any]()
for x in Mirror(reflecting: cl).children.makeIterator() {
if let val = x.value as Any? {
print(type(of: val))
colorDict[x.label!] = val
}
}
print (colorDict)
the output in console is as below
Optional<String>
Optional<Int>
Optional<String>
["name": nil, "code": Optional(3456), "shade": Optional("DARK")]
how can i check for nil values and skip adding that property to the Dictionary
i have tried to loop through the dictionary after i add all values including nils and check for values too but i get the below warning
Comparing non-optional value of type 'Any' to nil always returns false
declaring the dictionary as below
var colorDict = [String: Any?]()
for x in colorDict {
if x.value == nil {
colorDict.removeValue(forKey: x.key)
}
}
removes the warning but it doesn't remove anything.
I would really appreciate your help.
The way of unwrapping objects of type Any that contain optionals is kind of weird but you can check that the values aren't nil in your mirror like this:
for x in Mirror(reflecting: cl).children {
if case Optional<Any>.some(let val) = x.value {
print(type(of: val))
colorDict[x.label!] = val
}
}
You can do this really easily in a one-liner, using filter:
let dict: [String : Any?] = ["Foo" : 3, "Bar" : nil, "Baz" : "Qux"]
let noNils = dict.filter { $0.value != nil }
print(noNils) // prints ["Foo": Optional(3), "Baz": Optional("Qux")]
As i have suggested, initialise all values.
If you decide not to store the nil values you will end up with children that some of them will have 1, some 2 and some 3 nodes, nothing wrong with that, BUT what happens when you go to read them?
You havent shared any info as to how these values will be used by the app, but assuming you have one function to read the properties/nodes of stored colors, it will go to read all 3 :
ref.child("colors").child("someSpecificColor").observeSingleEvent(of: .value, with: { (snapshot) in
// Get color values
let value = snapshot.value as? NSDictionary
let name = value?["name"] as? String ?? ""
let code = value?["code"] as? String ?? ""
let shade = value?["shade"] as? String ?? ""
// ...
}) { (error) in
print(error.localizedDescription)
}
See the issue?
Here is a simple solution :
var colorDict = [String: Any?]()
for x in Mirror(reflecting: cl).children.makeIterator() {
if let val = x.value, val != nil {
print(type(of: val))
colorDict[x.label!] = val
}
}
Here before to print and add you val, you check if the val is different than nil. As your output suggests in your console log you print :
Optional<String>
Optional<Int>
Optional<String>
val is an optional. So, if it's nil it won't be added. If not, you enter into the if statement and that's it.

Getting the value of a property using it's string name in pure Swift using reflection

I want to use Swift (not Objective-C runtime) Reflection to create a method like this:
func valueFor(property:String, of object:Any) -> Any? {
...
}
To some extent, I can do this using:
func valueFor(property:String, of object:Any) -> Any? {
let mirror = Mirror(reflecting: object)
return mirror.descendant(property)
}
With
class TestMe {
var x:Int!
}
let t = TestMe()
t.x = 100
let result = valueFor(property: "x", of: t)
print("\(result); \(result!)")
This prints out what I'd expect:
Optional(100); 100
When I do:
let t2 = TestMe()
let result2 = valueFor(property: "x", of: t2)
print("\(result2)")
The output is:
Optional(nil)
This might seem reasonable, except that if I do:
var x:Int!
print("\(x)")
This prints out:
nil
and not Optional(nil). The bottom line is that I'm having difficulty programmatically determining that the value of t2.x is nil using my valueFor method.
If I continue the above code with:
if result2 == Optional(nil)! {
print("Was nil1")
}
if result2 == nil {
print("Was nil2")
}
Neither of these print statements output anything.
When I put a breakpoint into Xcode and look at the value of result2 with the debugger, it shows:
▿ Optional<Any>
- some : nil
So, my question is: How can I determine if the original member variable was nil using the result from valueFor?
Additional1:
If I do:
switch result2 {
case .some(let x):
// HERE
break
default:
break
}
and put a breakpoint at HERE, the value of x turns out to be nil. But, even if I assign it to an Any?, comparing it to nil is not true.
Additional2:
If I do:
switch result2 {
case .some(let x):
let z:Any? = x
print("\(z)")
if z == nil {
print("Was nil3")
}
break
default:
break
}
This prints out (only):
Optional(nil)
I find this especially odd. result2 prints out exactly the same thing!
This is a bit of a hack, but I think it's going to solve the problem for me. I'm still looking for better solutions:
func isNilDescendant(_ any: Any?) -> Bool {
return String(describing: any) == "Optional(nil)"
}
func valueFor(property:String, of object:Any) -> Any? {
let mirror = Mirror(reflecting: object)
if let child = mirror.descendant(property), !isNilDescendant(child) {
return child
}
else {
return nil
}
}
well, i know it has been 4 years, but I am on Xcode 12 and still facing the same issue. since this question seems to be unanswered, I will add what worked for me.
func valueFor(property: String, of object: Any) -> Any? {
let optionalPropertyName = "some"
let mirror = Mirror(reflecting: object)
if let child = mirror.descendant(property) {
if let optionalMirror = Mirror(reflecting: child), optionalMirror.displayStyle == DisplayStyle.optional {
return optionalMirror.descendant(optionalPropertyName)
} else {
return child
}
} else {
return nil
}
}
by using Mirror to check for optional and then extract the optional using "some" you get back either a true object or nil. when this is returned to the caller via the Any? return, you are now able to nil check the value and have that work appropriately.

Float conversion crash

I have this :
let value = data[1] // its a string
print("val:",value) // prints 28.3
let a:Float = Float(value)!
print("a:",a)
Which prints a ok till sometimes crashes where value has a value on it
fatal error: unexpectedly found nil while unwrapping an Optional value
I can put the question mark to mark it as optional, but I am trying to understand how it crashes when there is a value .
Try to use optional wrapping with if let.
if let a = value as? Float {
print("a:%f",a)
}
To remove space try like this
let newValue = value.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
Swift 3
let newValue = value.trimmingCharacters(in: .whitespaces)

Swift 1.2 cast [AnyObject?] to [AnyObject]

If you look at this function:
typealias JSONDictionary = [String: AnyObject]
private func allIdentifiersInJson(json: [JSONDictionary], key: String?) -> [AnyObject] {
if let identifier = key {
let identifiers = json.map{ $0[identifier] } //this returns [AnyObject?]
return identifiers as? [AnyObject] ?? [] // this crashes the compiler in Release with Optimizations
}
return []
}
How would you cast an [AnyObject?] to an [AnyObject]
In Release config with optimizations enabled, I get this error during compilation:
Bitcast requires both operands to be pointer or neither
%548 = bitcast i64 %547 to %objc_object*, !dbg !10033
LLVM ERROR: Broken function found, compilation aborted!
One way would be to use flatMap:
let l: [AnyObject?] = [1, 2, 3, 4, 5, 6, nil, 7]
let m = l.flatMap { $0 } // Returns [1, 2, 3, 4, 5, 6, 7] as [AnyObject]
(tested only in Swift 2)
Expanding on what is in my comment above, if you use the following, your app will crash if identifier is not a key in the dictionary:
let identifiers = json.map { $0[ identifier ]! }
So, what you might want to do is filter that first, then perform the mapping knowing that you can force unwrap because you've already checked for that key:
let identifiers = json.filter({ $0[ identifier ] != nil }).map { $0[ identifier ]! }
But if it were me, I'd want to get rid of the force unwrap altogether for total safety, even when someone else or even yourself comes back to this code a week later and monkeys with it by taking out the filter:
let identifiers = json.map({ $0[ identifier ] ?? NSNull() }).filter { !($0 is NSNull) }
Now you are safely unwrapping with the nil coalescing operator (??) and providing a default object (NSNull), then filtering out those default objects.
You probably want to be using something like this instead:
private func allIdentifiersInJson(json: [JSONDictionary], key: String?) -> [AnyObject] {
if let key = key {
return json.map { $0[key] }.filter { $0 != nil } as! [AnyObject]
}
return []
}
Because we are using filter to remove all instances of nil, we can use the forced cast to [AnyObject] without fear of a crash.
Or, as pointed out by #MirekE in their answer, flatMap is even more succinct:
return json.flatMap { $0[key] }
Which only returns non-nil results, and removes the need for any casting.
This function will not crash the compiler:
private func allIdentifiersInJson(json: [JSONDictionary], key: String?) -> [AnyObject] {
if let key = key {
let identifiers = json.map{ $0[key]! } //this returns [AnyObject]
return identifiers ?? []
}
return []
}

How to use swift flatMap to filter out optionals from an array

I'm a little confused around flatMap (added to Swift 1.2)
Say I have an array of some optional type e.g.
let possibles:[Int?] = [nil, 1, 2, 3, nil, nil, 4, 5]
In Swift 1.1 I'd do a filter followed by a map like this:
let filtermap = possibles.filter({ return $0 != nil }).map({ return $0! })
// filtermap = [1, 2, 3, 4, 5]
I've been trying to do this using flatMap a couple ways:
var flatmap1 = possibles.flatMap({
return $0 == nil ? [] : [$0!]
})
and
var flatmap2:[Int] = possibles.flatMap({
if let exercise = $0 { return [exercise] }
return []
})
I prefer the last approach (because I don't have to do a forced unwrap $0!... I'm terrified for these and avoid them at all costs) except that I need to specify the Array type.
Is there an alternative away that figures out the type by context, but doesn't have the forced unwrap?
Since Swift 4.1 you can use compactMap:
let possibles:[Int?] = [nil, 1, 2, 3, nil, nil, 4, 5]
let actuals = possibles.compactMap { $0 }
(Swift 4.1 replaced some overloads of flatMap with compactmap.
If you are interested in more detail on this then see for example:
https://useyourloaf.com/blog/replacing-flatmap-with-compactmap/
)
With Swift 2 b1, you can simply do
let possibles:[Int?] = [nil, 1, 2, 3, nil, nil, 4, 5]
let actuals = possibles.flatMap { $0 }
For earlier versions, you can shim this with the following extension:
extension Array {
func flatMap<U>(transform: Element -> U?) -> [U] {
var result = [U]()
result.reserveCapacity(self.count)
for item in map(transform) {
if let item = item {
result.append(item)
}
}
return result
}
}
One caveat (which is also true for Swift 2) is that you might need to explicitly type the return value of the transform:
let actuals = ["a", "1"].flatMap { str -> Int? in
if let int = str.toInt() {
return int
} else {
return nil
}
}
assert(actuals == [1])
For more info, see http://airspeedvelocity.net/2015/07/23/changes-to-the-swift-standard-library-in-2-0-betas-2-5/
I still like the first solution, which creates only one intermediate
array. It can slightly more compact be written as
let filtermap = possibles.filter({ $0 != nil }).map({ $0! })
But flatMap() without type annotation and without forced
unwrapping is possible:
var flatmap3 = possibles.flatMap {
flatMap($0, { [$0] }) ?? []
}
The outer flatMap is the array method
func flatMap<U>(transform: #noescape (T) -> [U]) -> [U]
and the inner flatMap is the function
func flatMap<T, U>(x: T?, f: #noescape (T) -> U?) -> U?
Here is a simple performance comparison (compiled in Release mode).
It shows that the first method is faster, approximately by a factor
of 10:
let count = 1000000
let possibles : [Int?] = map(0 ..< count) { $0 % 2 == 0 ? $0 : nil }
let s1 = NSDate()
let result1 = possibles.filter({ $0 != nil }).map({ $0! })
let e1 = NSDate()
println(e1.timeIntervalSinceDate(s1))
// 0.0169369578361511
let s2 = NSDate()
var result2 = possibles.flatMap {
flatMap($0, { [$0] }) ?? []
}
let e2 = NSDate()
println(e2.timeIntervalSinceDate(s2))
// 0.117663979530334
Related to the question. If you are applying flatMap to an optional array, do not forget to optionally or force unwrap your array otherwise it will call flatMap on Optional and not objects conforming to Sequence protocol. I made that mistake once, E.g. when you want to remove empty strings:
var texts: [String]? = ["one", "two", "", "three"] // has unwanted empty string
let notFlatMapped = texts.flatMap({ $0.count > 0 ? $0 : nil })
// ["one", "two", "", "three"], not what we want - calls flatMap on Optional
let flatMapped = texts?.flatMap({ $0.count > 0 ? $0 : nil })
// ["one", "two", "three"], that's what we want, calls flatMap on Array
You could use reduce:
let flattened = possibles.reduce([Int]()) {
if let x = $1 { return $0 + [x] } else { return $0 }
}
You are still kind of declaring the type, but it's slightly less obtrusive.
Since this is something I seem to end up doing quite a lot I'm exploring a generic function to do this.
I tried to add an extension to Array so I could do something like possibles.unwraped but couldn't figure out how to make an extension on an Array. Instead used a custom operator -- hardest part here was trying to figure out which operator to choose. In the end I chose >! to show that the array is being filtered > and then unwrapped !.
let possibles:[Int?] = [nil, 1, 2, 3, nil, nil, 4, 5]
postfix operator >! {}
postfix func >! <T>(array: Array<T?>) -> Array<T> {
return array.filter({ $0 != nil }).map({ $0! })
}
possibles>!
// [1, 2, 3, 4, 5]