Swift 3: Cant unwrap optional from Dictionary? - ios

Ok I don't know what is going on here. I have a dictionary of Strings below:
var animals = ["max": "z", "Royal": nil] //store key pairs
and I am unable to print the value of the value in the key value pair without it printing "Optional" along with it.
I have tried using ! !! and casting as a String as well as the following:
var animalsToReturn = [String]()
if animals[selected]! != nil
{
if let pairName = animals[selected]
{
print("\(pairName)")
print("has pair",selected, animals[selected]!)
//trying to append to another array here
animalsToReturn.append("\(animals[selected]!)")
animalsToReturn.append(selected)
}
}
else {
print("no pair")
}
I check to make sure the value isn't nil, so it won't crash if I unwrap. But this is what is printed and the word Optional is appended to my other array:

You have included nil as a value, so the type of your dictionary's value is not String but Optional<String>. But fetching a value by key from a dictionary is itself an Optional. Therefore:
If your entry is present and is ultimately a String, it is an Optional<Optional<String>> and you have to unwrap it twice.
If your entry is present and is ultimately nil, it is an Optional wrapping nil.
If your entry is not present, it is nil.
You can readily test this as follows:
func test(_ selected:String) {
var animals = ["max": "z", "Royal": nil]
if let entry = animals[selected] { // attempt to find
if let entry = entry { // attempt to double-unwrap
print("found", entry)
} else {
print("found nil")
}
} else {
print("not found")
}
}
test("max") // found z
test("Royal") // found nil
test("glop") // not found
Contemplation of that example will answer your original question, namely "I don't know what is going on here".

animals[selected] is a Optional<Optional<String>> because you're storing nil. You can:
Double unwrap your value either by using if let or ! twice.
Change the type of your dictionary to [String: String] (instead of [String: String?]), and thus avoiding nil values.
Flatten the dictionary, removing nil values, and then accessing it as a [String: String]
You can flatten the dictionary using the code in this question.

Please enclose that in bracket and use double unwrapping. try this : -
animalsToReturn.append("\((animals[selected])!!)")

func addAnimal(_ animal: String) {
guard let animal = animals[animal] else {
print("No pair")
return
}
animalsToReturn.append(animal ?? "")
}

Related

Swift KeyPath on optional value

If I have
class Info {
var name: String?
}
class User {
var info: Info?
}
class WrappedClass {
var user: User = User()
}
let nameKeyPath = \WrappedClass.user.info?.name //Gets me KeyPath
let referencedNameKeyPath = \WrappedClass.user.info!.name //Gets me ReferenceWritableKeyPath
nameKeyPath gets me a KeyPath which I can't later on use to modify the name value, but if I force unwrap it I get a ReferenceWritableKeyPath which is what I'm after.
Unfortunately using referencedNameKeyPath with a nil value along the line expectedly crashes, due to unexpectedly finding nil.
My question is is there a way to convert the KeyPath to a ReferenceWritableKeyPath or somehow unwrap it along the way?
You can also use optional chaining on keypaths. Create two keypaths one for user and the other one for name in info.
let infoKeyPath = \WrappedClass.user.info
let nameKeyPath = \Info.name
Now, use optional chaining with keypath and it will yield String? as the result.
let name = wrappedInstance[keyPath: infoKeyPath]?[keyPath: nameKeyPath]
Try having the key path as a computed optional variable. Like that:
class WrappedClass {
var user: User = User()
var nameKeyPath: ReferenceWritableKeyPath<WrappedClass, String?>? {
guard let _ = user.info else { return nil }
return \WrappedClass.user.info!.name
}
}
You still end up with the force unwrap notation but it should not cause any issues since you specifically guard for it.
That way you can use the computed key path in a safe and convenient way:
let instance = WrappedClass()
if let nameKeyPath = instance.nameKeyPath {
instance[keyPath: nameKeyPath] = "Nikola"
}

Checking if `if let` is nil

I have an app where I'm currently using the SwiftKeychainWrapper. Below is the code I have which checks if retrievedString is nil. However I'm still getting retrievedString: nil in the console.
Shouldn't the code in the if-let statement not run, or am I using/understanding if-let incorrectly?
With the given example, what's the correct way to use if-let to unwrap my optional value?
if let retrievedString: String? = KeychainWrapper.stringForKey("username") {
print("retrievedString: \(retrievedString)")
//value not nil
} else {
//Value is nil
}
This is because you are setting the value of a optional String, String? KeychainWrapper.stringForKey("username") to another optional String retrievedString.
By trying to set a String? to another String?, the if let check always succeeds, even when the value is nil, because both the types are the same, and both can accept a nil value.
Instead, you should attempt to set the optional string, String? to a non-optional string, String. When Swift tries to set a non-optional String to nil, it will fail, because a non-optional cannot have a nil value. The code will then continue in the else statement
You should use
//notice the removal of the question mark
// |
// v
if let retrievedString: String = KeychainWrapper.stringForKey("username") {
print("retrievedString: \(retrievedString)")
//value not nil
} else {
//value is nil
}
You are setting the type of retrievedString to be optional. The whole point of the check is to remove the optional and just have a String.
if let retrievedString: String = KeychainWrapper.stringForKey("username") {
print("retrievedString: \(retrievedString)")
//value not nil
} else {
//Value is nil
}

unexpectedly found nil while unwrapping an Optional value?

I'm facing with an error: "unexpectedly found nil while unwrapping an Optional value"
when I insert new data in coreData and reload my tableview, I recall this function
var unique = [String]()
var loadMovie = [String:[Movie]]()
func insertMovie(movie : Movie) {
let genre = movie.genre!
if unique.contains(genre) {
loadMovie[genre]!.append(movie)
} else {
unique.append(genre)
loadMovie[genre] = [movie]
}
}
and fetch data:
func fetchAndSetResults() {
let app = UIApplication.sharedApplication().delegate as! AppDelegate
let context = app.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Movie")
do {
let movies = try context.executeFetchRequest(fetchRequest) as! [Movie]
loadMovie.removeAll()
for movie in movies {
insertMovie(movie)
}
} catch let err as NSError {
print(err.debugDescription)
}
}
and the app crushes for the error mentioned above on line: " loadMovie[genre]!.append(movie)" but if I reload app, my data are stored and visible in tableview. What's the problem?
you unwrapped optional variable means you just resolving the compile time error only. In swift you unwrapping the variable means it is represents that variable won't get the nil.You are just telling to the compiler .But now you are getting the nil (Run time Error) you need to handle this by using Optional Binding.
if let movies = try context.executeFetchRequest(fetchRequest)
{
loadMovie.removeAll()
}
Your variable loadMovie is a Dictionary with Strings as the keys and Arrays of Movies as what is stored for each key. If you are getting the error "unexpectedly found nil while unwrapping an Optional value" for line " loadMovie[genre]!.append(movie)" it means without a doubt the String called genre is sometimes not a stored as a key in your loadMovie Dictionary.
Use the code below to first make sure you can get the Array stored for that key (stored in the genre string), and if you can't then print out the String so you can debug, to find out what key is missing.
var unique = [String]()
var loadMovie = [String:[Movie]]()
func insertMovie(movie : Movie) {
let genre = movie.genre!
if unique.contains(genre) {
if let genreArray = loadMovie[genre]{
genreArray.append(movie)
} else {
NSLog("The missing genre: \(genre)")
}
} else {
unique.append(genre)
loadMovie[genre] = [movie]
}
}
Anytime you want a value that could be nil (not there) you can use the if/let pattern above. So for your second question in the comments you could replace return loadMovie[genre].count with:
if let genreArray = loadMovie[genre]{
return genreArray.count
} else {
return 0 // zero because there are no items
}
There are other ways too. You should checkout a good basic swift tutorial like: http://www.tutorialspoint.com/swift/
If you look at the section on optionals this should all be more clear. Here at stack overflow you are generally expected to first have tried to find out answers for yourself, and understand the basic theory. Unfortunately, that is why you are getting so many down votes. I hope this has helped.
If this has helped you please accept this answer by clicking on the checkmark next to it.

How to check if NSDictionary is not nil in Swift 2

I'm getting NSDictionary as parameter in my function but having problem because don't know how to check if that parameter is not nil.
My function looks like this:
func doSmth(val : NSDictionary)
Inside my function I'm trying to get some values:
let action = val["action"] as! String
But getting error "fatal error: unexpectedly found nil while unwrapping an Optional value" when receive parameter val as nil.
The error is due to assuming (force casting) a value that can sometimes be nil. Swift is awesome, because it allows conditional unwraps and conditional casts in very concise statements. I recommend the following (for Swift 1-3):
Use "if let" to conditionally check for "action" in the dictionary.
Use as? to conditionally cast the value to a String
if let actionString = val["action"] as? String {
// action is not nil, is a String type, and is now stored in actionString
} else {
// action was either nil, or not a String type
}
You can also access the allKeys or alternatively allValues property and check if the array contains any elements like so:
let dic = NSDictionary()
let total = dic.allKeys.count
if total > 0 {
// Something's in there
}
else {
// Nothing in there
}
EDIT
Here is how you can detect if the NSDictionary is nil, if they key you are looking for exists, and if it does attempt to access it's value:
let yourKey = "yourKey"
if let dic = response.someDictionary as? NSDictionary {
// We've got a live one. NSDictionary is valid.
// Check the existence of key - OR check dic.allKeys.containsObject(yourKey).
let keyExists: Bool = false;
for var key as String in dic.allKeys {
if key == yourKey {
keyExists = true;
}
}
// If yourKey exists, access it's possible value.
if keyExists == true {
// Access your value
if let value = dic[yourKey] as? AnyObject {
// We're in business. We have the value!
}
else {
// yourKey does not contain a value.
}
}
else {
// yourKey does not exist in NSDictionary.
}
}
else {
// Call an ambulance. NSDictionary is nil.
}
That's not particularly related to Swift 2.
If the dictionary could be nil declare it as optional
func doSmth(val : NSDictionary?)
Then use optional bindings to check
if let valIsNonOptional = val {
let action = valIsNonOptional["action"] as! String
}
The code assumes that there is a key action containing a String value anyway if the dictionary is not nil
Your dictionary parameter is probably not nil. The problem is probably that your dictionary doesn't contain a value for the key "action".
When you say val["action"], the dictionary (being an NSDictionary) returns an Optional<AnyObject>. If val contains the key "action", it returns Some(value). If val doesn't contain the key "action", it returns None, which is the same as nil.
You can unwrap the Optional in your cast, and choose a course of action based on whether it was nil, using an if-let statement:
if let action = val["action"] as? String {
// action is a String, not an Optional<String>
} else {
// The dictionary doesn't contain the key "action", and
// action isn't declared in this scope.
}
If you really think val itself might be nil, you need to declare your function that way, and you can unwrap val without renaming it using a somewhat confusing guard statement:
func doSmth(val: NSDictionary?) {
guard let val = val else {
// If val vas passed in as nil, I get here.
return
}
// val is now an NSDictionary, not an Optional<NSDictionary>.
...
}

How to extend the Swift Dictionary type to return a non-empty String or nil

I'm writing an extension to Dictionary so that when I give it a String key, it'll return me a String only if the value associated with the key is non-nil and not empty.
extension Dictionary {
subscript(key: String) -> String? {
if let string = super.subscript(key) {
if string.isEmpty == false {
return string
}
}
return nil
}
}
However, at the if let string = super.subscript(key) { line, I get the following compile error and I don't know what it means--neither is there a Google result that explains it:
Expected -> for subscript element type
I am doing this because I'm working with an API that returns a JSON where a key's value may be an empty string--which is an invalid value to the app by our requirements, and hence, as good as nil.
Of course the longer way works, but I'm looking for a way to make this shorter.
if let value = dict["key"] as? String {
if value.isEmpty == false {
// The value is non-nil and non-empty.
}
}
You're going to think this is very silly, but my suggestion would be: do more or less exactly what you're doing, but encapsulate it as a separate function rather than trying to deal with the implications of defining a new subscript:
extension Dictionary {
func nes(key:Key) -> String? {
var result : String? = nil
if let s = self[key] as? String {
if !s.isEmpty {
result = s
}
}
return result
}
}
(nes stands for "non-empty string".)
Now call it like d.nes("foo").

Resources