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 ?? ""
}
Related
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
}
This question already has answers here:
extension of Dictionary where <String, AnyObject>
(7 answers)
Closed 3 years ago.
I have this code to get a Double amount from a [String: Any] and format a string like this
if let amount = details["amount"] as? Double
{
self.amountLbl.text = String(format: "%.2f", amount)
}
Im trying to create an extension for this
Expected
//self.amountLbl.text = details.getInAmountFormat(str: "amount")
My attempt
extension Dictionary where Key: StringProtocol {
func getInAmountFormat(str: String) -> String? {
if let value = self[str] as? Double {//Cannot subscript a value of type 'Dictionary<Key, Value>' with an index of type 'String'
return String(format: "%.2f", value)
}
return nil
}
}
You're almost done, just make right constraint on the Key type:
extension Dictionary where Key == String {
func getInAmountFormat(str: String) -> String? {
if let value = self[str] as? Double {
return String(format: "%.2f", value)
}
return nil
}
}
Also, here is an useful answer.
let dic: [String: Any] = [
"b": true
]
func cast<T>(value: Any, to type: T) -> T? {
return value as? T
}
let value = dic["b"]!
let casted = cast(value: value, to: Bool.self)
print(casted.debugDescription) // nil
print(value as! Bool) // true
I'm doing dynamic casting using generic function, the results are different from direct casting when the value is Bool, why?
You need to pass the type correctly as below,
func cast<T>(value: Any, to type: T.Type) -> T?
OR
You can remove passing the type as below,
func cast<T>(value: Any) -> T? {
return value as? T
}
let casted: Bool? = cast(value: value)
I am getting error while migrating to Swift 3. Below is the code in which error comes.
func getProfileFieldValue(_ formFields:NSMutableArray,keyValue:String) -> String {
for key in formFields{
if keyValue == key["name"] as! String{
return key["value"] as! String
}
}
return ""
}
Please help and thanks in advance.
NSMutableArray does not provide any type information, use native Swift array
func getProfileFieldValue(_ formFields:[[String:Any]], keyValue: String) -> String {
for key in formFields {
if let value = key["name"] as? String, value == keyValue {
return key["value"] as! String
}
}
return ""
}
or if the dictionaries contain only String values
func getProfileFieldValue(_ formFields:[[String:String]], keyValue: String) -> String {
for key in formFields {
if let value = key["name"], value == keyValue {
return key["value"]!
}
}
return ""
}
Or still swiftier
func getProfileFieldValue(_ formFields:[[String:Any]], keyValue: String) -> String {
if let profileField = formFields.first(where { $0["name"] as? String == keyValue }) {
return profileField["value"] as! String
}
return ""
}
Finally the waterproof-will-never-crash version:
func getProfileFieldValue(_ formFields:[[String:Any]], keyValue: String) -> String {
guard let profileField = formFields.first(where: { $0["name"] as? String == keyValue }),
let value = profileField["value"] as? String else { return "" }
return value
}
NSMutableArray does not provide type information, so you'll need to cast the array prior to the for loop
let array = NSMutableArray(array: [1, 2, 3, 4, 5])
let keyValue = 3
for item in array as! [Int]
{
if keyValue == item
{}
}
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())