Swift error extending Dictionary Any? is not convertible to Profiles - swift

I am getting an error: 'Any?' is not convertible to 'Profiles' I am not sure how to correct. I am trying to extend the dictionary. I am needing the structure show in the Profile Struct
struct Profiles {
var id: Int
var name: String
}
extension NSDictionary {
var profileDictionary: [String : Profiles] {
var dictionary: [String : Profiles] = [:]
let keys = self.allKeys.compactMap { $0 as? String }
for key in keys {
let keyValue = self.value(forKey: key) as Profiles
dictionary[key] = keyValue
}
return dictionary
}
}

Not sure why you need to use NSDictionary when coding with Swift. I will post the Dictionary approach but NSDictionary would be exactly the same. You can use reduce method and convert all key value pairs to (String, Profiles) and add it to the resulting dictionary:
extension Dictionary {
var profileDictionary: [String : Profiles] {
reduce(into: [:], { result, keyValue in
if let kv = keyValue as? (String, Profiles) {
result[kv.0] = kv.1
}
})
}
}

Related

Swift update value inside Dictionary<String, Any> in an Array

I have an array of Dictionaries, like so
var myArray: [Dictionary<String, Any>] = [Dictionary<String, Any>]()
Somewhere in my class i add values there, like so:
var myDict = Dictionary<String, Any>()
myDict["Id"] = "someUniqueId"
myDict["v1"] = -42
self.myArray.append(myDict)
Then later i try to update the v1 inside this dict like so:
self.myArray.first(where: { $0["Id"] == "someUniqueId" })?["v1"] = -56
However, the compiler tells me:
Cannot assign through subscript: function call returns immutable value
Any way i can modify the value in array outside of copying the whole dictionary, removing it from array, and re-inserting?
UPDATE This dictionary is not strongly typed because what is inside the dictionary, both types and fieldnames, are controlled by the user in an external system. E.g. we download some metadata and then render controls for this dynamic data.
This is because Swift Dictionary is a value type.
Use a custom class or you can use NSMutableDictionary for this purpose (because NSDictionary is immutable).
var myArray = [NSMutableDictionary]()
var myDict = NSMutableDictionary()
myDict["Id"] = "someUniqueId"
myDict["v1"] = -42
myArray.append(myDict)
myArray.first(where: { $0["Id"] as? String == "someUniqueId" })?["v1"] = -56
print(myArray)
// [{
// Id = someUniqueId;
// v1 = "-56";
// }]
You can make it easier for yourself by wrapping your dictionary in a simple class
class Config {
private var dict: [String: Any]
init(_ dict: [String: Any]) {
self.dict = dict
}
func valueFor(_ key: String) -> Any? {
return dict[key]
}
func setValue(_ value: Any, forKey key: String) {
dict[key] = value
}
}
and use that in your array
var myArray = [Config]()
And use it like this
array.filter { $0.valueFor("Id") as? String == "someUniqueId" }
.first?.setValue(-56, forKey: "v1")
That way you can avoid using classes like NSMutableDictionary and keeping it more pure Swift. Another advantage with a custom class is that you can add functionality to it to simplify your code using it, so for instance if you will be looking up dictionaries by "Id" a lot we can add a computed property for it
var id: String? {
return valueFor("Id") as? String
}
and then the example above would become
array.filter { $0.id == "someUniqueId" }.first?.setValue(-56, forKey: "v1")

Is it possible to write an extension for a specific swift Dictionary ... [String : AnyObject]? [duplicate]

I am trying to create a dictionary extension where Dictionary is of the type <String, AnyObject>.
Was looking in many places and trying different approaches, but none of them seemed to work. This was one of them:
extension Dictionary where <String, AnyObject>{
var jsonString:String {
return ""
}
}
Another method that didn't actually work for some reason:
extension Dictionary where Key:Hashable, Value:AnyObject {
var jsonString:String {
do {
let stringData = try NSJSONSerialization.dataWithJSONObject(self, options: NSJSONWritingOptions.PrettyPrinted)
if let string = String(data: stringData, encoding: NSUTF8StringEncoding){
return string
}
}catch _ {
}
return ""
}
}
Got: Argument type 'Dictionary' does not conform to expected type of 'AnyObject'
>=3.1
From 3.1, we can do concrete extensions, ie:
extension Dictionary where Key == String {}
<3.1
We can not conform concrete types w/ concrete generics, ie:
extension Dictionary where Key == String
However, because Dictionary conforms to sequence and we can conform protocol types w/ concrete generics, we could do:
extension Sequence where Iterator.Element == (key: String, value: AnyObject) {
func doStuff() {...
Otherwise, we can constrain our key to a protocol that string conforms to like this:
extension Dictionary where Key: StringLiteralConvertible, Value: AnyObject {
var jsonString: String {
return ""
}
}
As per your updated answer. Json serialization needs an object, Swift Dictionaries are structs. You need to convert to an NSDictionary You must specify Key to conform to NSObject to properly convert to an NSDictionary.
Small note: Dictionaries already type constrain Key to be Hashable, so your original constraint wasn't adding anything.
extension Dictionary where Key: NSObject, Value:AnyObject {
var jsonString:String {
do {
let stringData = try NSJSONSerialization.dataWithJSONObject(self as NSDictionary, options: NSJSONWritingOptions.PrettyPrinted)
if let string = String(data: stringData, encoding: NSUTF8StringEncoding){
return string
}
}catch _ {
}
return ""
}
}
Note, that the dictionaries must conform to this type to access the extension.
let dict = ["k" : "v"]
Will become type [String : String], so you must be explicit in declaring:
let dict: [NSObject : AnyObject] = ["k" : "v"]
Swift 3 Approach
extension Dictionary where Key: ExpressibleByStringLiteral, Value: AnyObject
As StringLiteralConvertible is now deprecated and replaced by ExpressibleByStringLiteral
Update for Swift 3
Here is my example using ExpressibleByStringLiteral for Key and Any for Value.
extension Dictionary where Key: StringLiteralConvertible, Value: Any {
var jsonString: String? {
if let dict = (self as AnyObject) as? Dictionary<String, AnyObject> {
do {
let data = try JSONSerialization.data(withJSONObject: dict, options: JSONSerialization.WritingOptions(rawValue: UInt.allZeros))
if let string = String(data: data, encoding: String.Encoding.utf8) {
return string
}
} catch {
print(error)
}
}
return nil
}
}
and then I use it like this:
let dict: Dictionary<String, AnyObject> = [...]
let jsonString = dict.jsonString
You can convert self to AnyObject or NSObject, both works, then you do unwrap as Dictionary or any other specific type.
Adding to the answer provided by #Logan, for those looking to add custom properties to the string-subscripted Dictionary, that is possible as well (was looking to do this when I came across this SO question):
extension Dictionary where Key: StringLiteralConvertible {
var somePropertyThatIsAColor:UIColor? {
get {
return self["someKey"] as? UIColor
}
set {
// Have to cast as optional Value
self["someKey"] = (newValue as? Value)
}
}
Anyone using [String:Any] instead of Dictionary can use below extension
extension Dictionary where Key == String, Value == Any {
mutating func append(anotherDict:[String:Any]) {
for (key, value) in anotherDict {
self.updateValue(value, forKey: key)
}
}
}
So, Dictionary is:
public struct Dictionary<Key : Hashable, Value> : CollectionType, DictionaryLiteralConvertible {..}
How about a protocol extension? :D
extension CollectionType where Self: DictionaryLiteralConvertible, Self.Key == String, Self.Value == AnyObject, Generator.Element == (Self.Key, Self.Value) {
...
}
I was not able to make any of the offered solutions work in Swift 3, but by taking advantage of bridging between Dictionary and NSDictionary I could make this work:
extension NSDictionary {
var jsonString:String {
do {
let stringData = try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
if let string = String(data: stringData, encoding: .utf8) {
return string
}
}catch _ {
}
return ""
}
}

Trouble accessing dictionary item in Swift 2.0

I'm experienced in Obj-C, but fairly new to Swift. I have a simple function that takes a Set and a Dictionary as parameters:
func buildSource(dataToParse:Set<String>, lookupData:Dictionary<String,AnyObject>) {
for item in dataToParse {
for dict in lookupData {
let nameID = dict["name"] // compile error
}
}
}
The passed in parameter lookupData is a dictionary containing nested dictionaries. I know that each of these dictionaries contains a key called name but when I try to access that key using the following syntax:
let nameID = dict["name"]
I get the following comile error:
Type '(String, AnyObject)' has no subscript members
If I know that a key exists, how do I access it? Thanks!
import Foundation
func buildSource(dataToParse:Set<String>, lookupData:Dictionary<String,AnyObject>)->[AnyObject] {
var ret: Array<AnyObject> = []
for item in dataToParse {
for (key, value) in lookupData {
if key == item {
ret.append(value)
}
}
}
return ret
}
let set = Set(["alfa","beta","gama"])
var data: Dictionary<String,AnyObject> = [:]
data["alfa"] = NSNumber(integer: 1)
data["beta"] = NSDate()
data["theta"] = NSString(string: "some string")
let find = buildSource(set, lookupData: data)
dump(find)
/* prints
▿ 2 elements
- [0]: 28 Nov 2015 18:02
▿ [1]: 1 #0
▿ NSNumber: 1
▿ NSValue: 1
- NSObject: 1
*/
in your code
for dict in lookupData {
let nameID = dict["name"] // compile error
}
dict is not a dictionary, but (key, value) tuple!
Update:
get value from lookupData with "name" as the key
downcast to a dictionary with as? [String:AnyObject]
iterate over dataToParse
use the String from dataToParse to access the nested dictionary.
func buildSource(dataToParse:Set<String>, lookupData:[String:AnyObject]) {
guard let dict = lookupData["name"] as? [String:AnyObject] else {
return
}
for item in dataToParse {
let value = dict[item]
}
}
More possible solutions:
If lookupData is an array of dictionaries:
func buildSource(dataToParse:Set<String>, lookupData:[[String:AnyObject]]) {
for item in dataToParse { // String
for dict in lookupData { // dict
let nameID = dict["name"] // value
}
}
}
If lookupData is a nested dictionary :
func buildSource(dataToParse:Set<String>, lookupData:[String:[String:AnyObject]]) {
for item in dataToParse {
guard let dict = lookupData[item] else {
continue
}
guard let nameID = dict["name"] else {
continue
}
}
}
If lookupData is definitely [String:AnyObject] and it's value might be another [String:AnyObject], but it might also not be.
func buildSource(dataToParse:Set<String>, lookupData:[String:AnyObject]) {
for item in dataToParse {
guard let dict = lookupData[item] as? [String:AnyObject] else {
continue
}
guard let nameID = dict["name"] else {
continue
}
}
}

Lowercase Dictionary Keys in Swift

I need to convert a Dictionary with mixed case keys in the same exact Dictionary but with only lowercase keys.
Here is my attempt (It works but I found this implementation extremely rough)
extension Dictionary {
func lowercaseKeys()->Dictionary<String, AnyObject>{
var newDictionary = Dictionary<String,AnyObject>()
for k in keys{
if let k_string = k as? String{
newDictionary[k_string.lowercaseString] = self[k] as? AnyObject
}
}
return newDictionary
}
}
Can you suggest a more elegant way to solve this problem?
Changes its own keys without the need of a temporary dictionary ;)
var dict = ["HEJ":"DÅ", "NeJ":"tack"]
for key in dict.keys {
dict[key.lowercaseString] = dict.removeValueForKey(key)
}
print(dict)
[hej: DÅ, nej: tack]
EDIT
I made this extension, its a little dirty for now but I will update it again.
extension Dictionary {
mutating func lowercaseKeys() {
for key in self.keys {
let str = (key as! String).lowercaseString
self[str as! Key] = self.removeValueForKey(key)
}
}
}
var dict = ["HeJ":"Då", "nEJ":"taCK!"]
dict.lowercaseKeys()
print(dict)
["hej": "Då", "nej": "taCK!"]
EDIT 2
extension Dictionary where Key: StringLiteralConvertible {
mutating func lowercaseKeys() {
for key in self.keys {
self[String(key).lowercaseString as! Key] = self.removeValueForKey(key)
}
}
}
var dict = ["NamE":"David", "LAST_NAME":"Göransson"]
dict.lowercaseKeys() // Will compile
var dict2 = [0:"David", 0:"Göransson"]
dict2.lowercaseKeys() // Won't compile because Key isn't StringLiteralConvertible
Smth like that?
var dict = ["KEY": "value"]
var newDict = [String: AnyObject]()
for (key, value) in dict {
newDict[key.lowercaseString] = value
}
Arbitur's answer, updated for Swift 3:
extension Dictionary where Key: ExpressibleByStringLiteral {
public mutating func lowercaseKeys() {
for key in self.keys {
self[String(describing: key).lowercased() as! Key] = self.removeValue(forKey: key)
}
}
}
Here is my solution, no force casting, totally safe.
protocol LowercaseConvertible {
var lowercaseString: Self { get }
}
extension String: LowercaseConvertible {}
extension Dictionary where Key: LowercaseConvertible {
func lowercaseKeys() -> Dictionary {
var newDictionary = Dictionary()
for k in keys {
newDictionary[k.lowercaseString] = self[k]
}
return newDictionary
}
}
Just as an alternative to looping, you could use the map function:
func lowercaseKeys () -> Dictionary<String, AnyObject> {
var newDictionary = Dictionary<String,AnyObject>()
Array(keys).map { key in
newDictionary[(key as! String).lowercaseString] = self[key] as? AnyObject
}
return newDictionary
}

extension of Dictionary where <String, AnyObject>

I am trying to create a dictionary extension where Dictionary is of the type <String, AnyObject>.
Was looking in many places and trying different approaches, but none of them seemed to work. This was one of them:
extension Dictionary where <String, AnyObject>{
var jsonString:String {
return ""
}
}
Another method that didn't actually work for some reason:
extension Dictionary where Key:Hashable, Value:AnyObject {
var jsonString:String {
do {
let stringData = try NSJSONSerialization.dataWithJSONObject(self, options: NSJSONWritingOptions.PrettyPrinted)
if let string = String(data: stringData, encoding: NSUTF8StringEncoding){
return string
}
}catch _ {
}
return ""
}
}
Got: Argument type 'Dictionary' does not conform to expected type of 'AnyObject'
>=3.1
From 3.1, we can do concrete extensions, ie:
extension Dictionary where Key == String {}
<3.1
We can not conform concrete types w/ concrete generics, ie:
extension Dictionary where Key == String
However, because Dictionary conforms to sequence and we can conform protocol types w/ concrete generics, we could do:
extension Sequence where Iterator.Element == (key: String, value: AnyObject) {
func doStuff() {...
Otherwise, we can constrain our key to a protocol that string conforms to like this:
extension Dictionary where Key: StringLiteralConvertible, Value: AnyObject {
var jsonString: String {
return ""
}
}
As per your updated answer. Json serialization needs an object, Swift Dictionaries are structs. You need to convert to an NSDictionary You must specify Key to conform to NSObject to properly convert to an NSDictionary.
Small note: Dictionaries already type constrain Key to be Hashable, so your original constraint wasn't adding anything.
extension Dictionary where Key: NSObject, Value:AnyObject {
var jsonString:String {
do {
let stringData = try NSJSONSerialization.dataWithJSONObject(self as NSDictionary, options: NSJSONWritingOptions.PrettyPrinted)
if let string = String(data: stringData, encoding: NSUTF8StringEncoding){
return string
}
}catch _ {
}
return ""
}
}
Note, that the dictionaries must conform to this type to access the extension.
let dict = ["k" : "v"]
Will become type [String : String], so you must be explicit in declaring:
let dict: [NSObject : AnyObject] = ["k" : "v"]
Swift 3 Approach
extension Dictionary where Key: ExpressibleByStringLiteral, Value: AnyObject
As StringLiteralConvertible is now deprecated and replaced by ExpressibleByStringLiteral
Update for Swift 3
Here is my example using ExpressibleByStringLiteral for Key and Any for Value.
extension Dictionary where Key: StringLiteralConvertible, Value: Any {
var jsonString: String? {
if let dict = (self as AnyObject) as? Dictionary<String, AnyObject> {
do {
let data = try JSONSerialization.data(withJSONObject: dict, options: JSONSerialization.WritingOptions(rawValue: UInt.allZeros))
if let string = String(data: data, encoding: String.Encoding.utf8) {
return string
}
} catch {
print(error)
}
}
return nil
}
}
and then I use it like this:
let dict: Dictionary<String, AnyObject> = [...]
let jsonString = dict.jsonString
You can convert self to AnyObject or NSObject, both works, then you do unwrap as Dictionary or any other specific type.
Adding to the answer provided by #Logan, for those looking to add custom properties to the string-subscripted Dictionary, that is possible as well (was looking to do this when I came across this SO question):
extension Dictionary where Key: StringLiteralConvertible {
var somePropertyThatIsAColor:UIColor? {
get {
return self["someKey"] as? UIColor
}
set {
// Have to cast as optional Value
self["someKey"] = (newValue as? Value)
}
}
Anyone using [String:Any] instead of Dictionary can use below extension
extension Dictionary where Key == String, Value == Any {
mutating func append(anotherDict:[String:Any]) {
for (key, value) in anotherDict {
self.updateValue(value, forKey: key)
}
}
}
So, Dictionary is:
public struct Dictionary<Key : Hashable, Value> : CollectionType, DictionaryLiteralConvertible {..}
How about a protocol extension? :D
extension CollectionType where Self: DictionaryLiteralConvertible, Self.Key == String, Self.Value == AnyObject, Generator.Element == (Self.Key, Self.Value) {
...
}
I was not able to make any of the offered solutions work in Swift 3, but by taking advantage of bridging between Dictionary and NSDictionary I could make this work:
extension NSDictionary {
var jsonString:String {
do {
let stringData = try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
if let string = String(data: stringData, encoding: .utf8) {
return string
}
}catch _ {
}
return ""
}
}