How to create Dictionary extension [duplicate] - swift

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.

Related

Assertion Failure on getting a key from NSDictionary in Swift

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
}

cannot subscript a value of type 'Dictionary<Key, Value>' with an index of type 'String' self[key]

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]()

Cannot downcast object of type Any to Int when accessing from dictionary

I have a Gfycat struct that represents the data I want to store after making a network call to the Gfycat API.
typealias JSONDictionary = [String: Any]
struct Gfycat {
let id: String
let number: Int
}
In an extension to the Gfycat struct, I wrote a failable initializer that takes a dictionary of type [String: Any] as its argument. This dictionary is then used to assign values to the struct's properties. This is the original init method I wrote:
extension Gfycat {
init?(dictionary: JSONDictionary) {
guard let id = dictionary["gfyId"] as? String,
let number = dictionary["gfyNumber"] as? Int { return nil }
self.id = id
self.number = number
}
}
The problem is that when accessing a value from the dictionary, I cannot downcast the value from Any to Int. I must first downcast Any to String, then convert that string to Int. Is this a bug or rather a feature of Swift that I don't understand?
This was my solution:
extension Gfycat {
init?(dictionary: JSONDictionary) {
guard let id = dictionary["gfyId"] as? String,
let uncastedNumber = dictionary["gfyNumber"] as? String,
let number = Int(uncastedNumber) else { return nil }
self.id = id
self.number = number
}
}
I must first downcast Any to String, then convert that string to Int. Is this a bug or rather a feature of Swift that I don't understand?
It's neither a bug nor a feature of Swift. It's a fact about the dictionary you're working with. This thing is a String, not an Int. So you cannot cast it to an Int.

Get string from object in Swift 3

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 ?? ""
}

cannot return String in function

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())