I'd like to understand how could the function below sometimes generates a Swift "_assertionFailure" in line:
if let s = dict![key] as? String
I suppose that if dict![key] is not found, a nil would return, so the if let would get a nil value and the condition would fail, with no errors, no assertions. Where I am getting wrong?
func getDictKey(_ dict: NSDictionary?, key: String) -> String?
{
var value: String?;
if (dict != nil && !key.isEmpty)
{
if let s = dict![key] as? String {
value = s;
}
}
return value;
}
Your syntax is very, very objective-c-ish.
In Swift you can simply write
func getDictKey(_ dict: NSDictionary?, key: String) -> String?
{
return dict?[key] as? String
}
It contains all checks except the empty string check. The question mark after dictaborts the chain if the dictionary is nil.
You should not use NSDictionary in Swift but a more meaningful naming
func getValue(from dict: [String:Any]?, forKey key: String) -> String?
{
return dict?[key] as? String
}
Related
I have Dictionary of type Dictionary (Key, Value). When I am trying to access the value it's giving the error:
cannot subscript a value of type 'Dictionary' with an index of type 'String' self[key]
My Class.
extension Dictionary where Key == String {
public func optionalValue<T>(_ key: String) throws -> T?
{
guard let value = self[key],
!(value is NSNull) else { return nil }
guard let typedValue = value as? T else { throw JSONParserError.invalidValue(key: key, value: value) }
return typedValue
}
}
The following built from your question above compiles and works fine for me in a playground using Xcode 10.2.
import Foundation
extension Dictionary where Key == String {
public func optionalValue<T>(_ key: String) throws -> T? {
guard let value = self[key] else { return nil }
guard !(value is NSNull) else { return nil }
guard let typedValue = value as? T else { throw NSError() }
return typedValue
}
}
var dictionary = [String:Any]()
dictionary["nullKey"] = NSNull()
dictionary["intKey"] = 12345
var value: Int?
value = try dictionary.optionalValue("nullKey")
value = try dictionary.optionalValue("intKey")
The line value = try dictionary.optionalValue("nullKey") outputs nil as expected and value = try dictionary.optionalValue("intKey") outputs 12345.
So to answer your question it should work fine.
I got the issue. Actually in my case the code was something like that
var dictionary = [String:AnyObject]()
So When It was getting Int value it was throwing Error.
This solved the issue.
var dictionary = [String:Any]()
In a following class
class Foo {
let _defaultValue = "N/A"
let value: String
init (dict: NSDictionary) {
self.value = dict["bar"] as? String! ?? _defaultValue
}
}
compiler fails with the message
constant 'self.value' captured by a closure before being initialized
As far as I can see, no operators read `self.value. The message seems somewhat confusing.
I accidentally came up with a workaround. I should say it confuses me even more:
class Foo {
let value: String
init (dict: NSDictionary) {
let _defaultValue = "N/A"
self.value = dict["bar"] as? String! ?? _defaultValue
}
}
Declaring _defaultValue and initializing it within the constructor makes the code compile.
It'd be an immense help if someone could explain why an error occurs in the first case and what is the compiler happier in the second case?
The reason for the error message is that the nil-coalescing operator
is defined as
public func ??<T>(optional: T?, defaultValue: #autoclosure () throws -> T) rethrows -> T
and does a "auto closure" on the second argument (in order to get
a short-circuiting behaviour). So
self.value = dict["bar"] as? String ?? _defaultValue
is transformed by the compiler to
self.value = dict["bar"] as? String ?? { self._defaultValue }()
and here the compiler complains because self is captured before
being fully initialised. (The error messages are slightly different
between Swift 2 and Swift 3).
Possible workarounds. You can assign the property to a local variable first:
init(dict: NSDictionary){
let defValue = _defaultValue
self.value = dict["bar"] as? String! ?? defValue
}
Or you can make it a static property of the class:
class Foo {
static let _defaultValue = "N/A"
let value: String
init(dict: NSDictionary) {
self.value = dict["bar"] as? String ?? Foo._defaultValue
}
}
Or replace ?? by an if-statement:
class Foo {
let _defaultValue = "N/A"
let value: String
init (dict: NSDictionary) {
if let value = dict["bar"] as? String {
self.value = value
} else {
self.value = _defaultValue
}
}
}
Addendum: Related resources:
Autoclosure of self in initializers in the Swift forum.
SR-944 Cannot use 'self' in RHS of && before all properties are initialized bug report.
Quote from the bug report:
Jordan Rose: This is true since && is implemented using #autoclosure, but it's certainly suboptimal.
In Swift 2.3 I had the following code:
func getMyName() -> String {
return NSUserDefaults.standardUserDefaults().objectForKey("account")!["name"] as! String
}
Now, I am trying to convert this code to Swift 3, but I am struggling with this error:
Type 'Any' has no subscript members
Here's my migrated code:
func getMyName() -> String {
return UserDefaults.standard.object(forKey: "account")!["name"] as! String
}
UserDefaults has a method called dictionaryForKey exactly for that:
func getMyName() -> String {
return UserDefaults.standard.dictionary(forKey: "account")?["name"] as? String ?? ""
}
In a following class
class Foo {
let _defaultValue = "N/A"
let value: String
init (dict: NSDictionary) {
self.value = dict["bar"] as? String! ?? _defaultValue
}
}
compiler fails with the message
constant 'self.value' captured by a closure before being initialized
As far as I can see, no operators read `self.value. The message seems somewhat confusing.
I accidentally came up with a workaround. I should say it confuses me even more:
class Foo {
let value: String
init (dict: NSDictionary) {
let _defaultValue = "N/A"
self.value = dict["bar"] as? String! ?? _defaultValue
}
}
Declaring _defaultValue and initializing it within the constructor makes the code compile.
It'd be an immense help if someone could explain why an error occurs in the first case and what is the compiler happier in the second case?
The reason for the error message is that the nil-coalescing operator
is defined as
public func ??<T>(optional: T?, defaultValue: #autoclosure () throws -> T) rethrows -> T
and does a "auto closure" on the second argument (in order to get
a short-circuiting behaviour). So
self.value = dict["bar"] as? String ?? _defaultValue
is transformed by the compiler to
self.value = dict["bar"] as? String ?? { self._defaultValue }()
and here the compiler complains because self is captured before
being fully initialised. (The error messages are slightly different
between Swift 2 and Swift 3).
Possible workarounds. You can assign the property to a local variable first:
init(dict: NSDictionary){
let defValue = _defaultValue
self.value = dict["bar"] as? String! ?? defValue
}
Or you can make it a static property of the class:
class Foo {
static let _defaultValue = "N/A"
let value: String
init(dict: NSDictionary) {
self.value = dict["bar"] as? String ?? Foo._defaultValue
}
}
Or replace ?? by an if-statement:
class Foo {
let _defaultValue = "N/A"
let value: String
init (dict: NSDictionary) {
if let value = dict["bar"] as? String {
self.value = value
} else {
self.value = _defaultValue
}
}
}
Addendum: Related resources:
Autoclosure of self in initializers in the Swift forum.
SR-944 Cannot use 'self' in RHS of && before all properties are initialized bug report.
Quote from the bug report:
Jordan Rose: This is true since && is implemented using #autoclosure, but it's certainly suboptimal.
I am having trouble casting an option AnyObject into a string. Whenever I try to call the fuction my program crashes with (lldb). This is the function.
func name() -> String {
print(attributes["name"])
print(attributes["name"]! as! String)
let name = attributes["name"]! as! String
return name
}
The output from the prints is:
Optional(Optional(Josh))
Josh
(lldb)
Thanks in advance for your help!
Lets say attributes is defined as follow
var attributes: NSMutableDictionary? = NSMutableDictionary()
and can be populated like follow
attributes?.setValue("Walter White", forKey: "name")
Optionals
You should design the name() function to return a String or nil (aka String? which is an Optional type)
func name() -> String? {
guard let
attributes = attributes,
name = attributes["name"] as? String else { return nil }
return name
}
The same logic can also be written this way
func name() -> String? {
return attributes?["name"] as? String
}
Now if a valid String value is found inside attributes with key name then it is returned. Otherwise the function does return nil.
Invoking the function
When using the function you should unwrap the result like this
if let name = name() {
print(name) // prints "Walter White"
}
In all these examples, attributes is defined as:
var attributes: AnyObject? = ["name": "Josh"]
Looks like the crash occurs due to type-safety issues. Try:
func name() -> String? {
if let name = attributes!["name"] as? String {
return name
}
return nil
}
Another option, which is slightly swiftier:
func name() -> String? {
guard let name = attributes!["name"] as? String else { return nil }
return name
}
Yet another option that would be using a block for the function, so that it doesn't return anything if attributes doesn't contain a key "name":
func name(block: ((text: String?) -> Void)) {
guard let name = attributes!["name"] as? String else { return }
return block(text: name)
}
// Usage:
name { text in
print(text!)
}
Prints:
Josh
if let _string = attributes["name"] as? String {
return _string
}
// fallback to something else, or make the method signature String?
return ""
When working with optionals, you don't want to just wrap things with exclamation points. If the value ever ended up not being a string, or not being there at all in the map, you're code would fail hard and potentially crash your application.
If you need a non-optional String, consider returning an empty string as a fallback method and using the if let pattern to return the optional string if it is available.
-- EDIT --
Not sure about the downvote... Here it is in a playground.
var attributes = [String:AnyObject]()
attributes["name"] = "test"
func name() -> String {
print(attributes["name"])
print(attributes["name"]! as! String)
let name = attributes["name"]! as! String
return name
}
// does not compile
//print(name())
func name2() -> String {
if let _string = attributes["name"] as? String {
return _string
}
// fallback to something else, or make the method signature String?
return ""
}
// prints test
print(name2())