Can Swift convert a class / struct data into dictionary?
You can just add a computed property to your struct
to return a Dictionary
with your values. Note that Swift native dictionary type doesn't have any method called value(forKey:)
. You would need to cast your Dictionary
to NSDictionary
:
struct Test {
let name: String
let age: Int
let height: Double
var dictionary: [String: Any] {
return ["name": name,
"age": age,
"height": height]
}
var nsDictionary: NSDictionary {
return dictionary as NSDictionary
}
}
You can also extend Encodable
protocol as suggested at the linked answer posted by @ColGraff to make it universal to all Encodable
structs:
struct JSON {
static let encoder = JSONEncoder()
}
extension Encodable {
subscript(key: String) -> Any? {
return dictionary[key]
}
var dictionary: [String: Any] {
return (try? JSONSerialization.jsonObject(with: JSON.encoder.encode(self))) as? [String: Any] ?? [:]
}
}
struct Test: Codable {
let name: String
let age: Int
let height: Double
}
let test = Test(name: "Alex", age: 30, height: 170)
test["name"] // Alex
test["age"] // 30
test["height"] // 170
Swift converting Decodable struct to Dictionary
You don't need it to be Decodable. What you need is to be able to encode it (Encodable). So start by declaring your structure as Codable. After encoding it you can convert your data into a dictionary using JSONSerialization jsonObject method:
extension Encodable {
func data(using encoder: JSONEncoder = .init()) throws -> Data { try encoder.encode(self) }
func string(using encoder: JSONEncoder = .init()) throws -> String { try data(using: encoder).string! }
func dictionary(using encoder: JSONEncoder = .init(), options: JSONSerialization.ReadingOptions = []) throws -> [String: Any] {
try JSONSerialization.jsonObject(with: try data(using: encoder), options: options) as? [String: Any] ?? [:]
}
}
extension Data {
func decodedObject<D: Decodable>(using decoder: JSONDecoder = .init()) throws -> D {
try decoder.decode(D.self, from: self)
}
}
extension Sequence where Element == UInt8 {
var string: String? { String(bytes: self, encoding: .utf8) }
}
I would also declare the srtuct properties as constants. If you need to change any value just create a new object:
struct DiscussionMessage: Codable {
let message, userCountryCode, userCountryEmoji, userName, userEmailAddress: String
let messageTimestamp: Double
let fcmToken, question, recordingUrl: String?
}
let message: DiscussionMessage = .init(message: "message", userCountryCode: "BRA", userCountryEmoji: "quot;, userName: "userName", userEmailAddress: "email@address.com", messageTimestamp: 1610557474.227274, fcmToken: "fcmToken", question: "question", recordingUrl: nil)
do {
let string = try message.string()
print(string) // {"fcmToken":"fcmToken","userName":"userName","message":"message","userCountryEmoji":"quot;,"userEmailAddress":"email@address.com","question":"question","messageTimestamp":1610557474.2272739,"userCountryCode":"BRA"}
let dictionary = try message.dictionary()
print(dictionary) // ["userName": userName, "userEmailAddress": email@address.com, "userCountryEmoji": , "messageTimestamp": 1610557474.227274, "question": question, "message": message, "fcmToken": fcmToken, "userCountryCode": BRA]
let data = try message.data() // 218 bytes
let decodedMessages: DiscussionMessage = try data.decodedObject()
print("decodedMessages", decodedMessages) // ecodedMessages DiscussionMessage(message: "message", userCountryCode: "BRA", userCountryEmoji: "quot;, userName: "userName", userEmailAddress: "email@address.com", messageTimestamp: 1610557474.227274, fcmToken: Optional("fcmToken"), question: Optional("question"), recordingUrl: nil)
} catch {
print(error)
}
How to convert a class / struct into dictionary with key different as that of property name
You can use a regular expression to convert to snake case and use reflection (Mirror) to convert to a dictionary.
The regex is rather simple and will not work so well if you for instance have several uppercase letters after each other so this part could be improved if needed.
func snakeKeyDictionary(_ mirror: Mirror) -> [String: Any] {
var dictionary = [String: Any]()
for (key, value) in mirror.children {
if let key = key {
let snakeKey = key.replacingOccurrences(of: #"[A-Z]"#, with: "_$0", options: .regularExpression).lowercased()
dictionary[snakeKey] = value
}
}
return dictionary
}
Example if usage
let item = AddItemToEvent(key: "1", shoppingListId: "12", shoppingListName: "List",
commonName: "some", storeCode: "ABC", sourceType: "A", sourceId: "fgd",
isDefaultList: "yes", storeLocation: "home")
let mirror = Mirror(reflecting: item)
print(snakeKeyDictionary(mirror))
prints
["common_name": "some", "is_default_list": "yes", "store_code": "ABC", "store_location": "home", "key": "1", "shopping_list_name": "List", "source_id": "fgd", "shopping_list_id": "12", "source_type": "A"]
But of course if the goal is to create json data it is quite simple
Make the struct conform to Codable
and then set the keyEncodingStrategy
property when encoding
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
Should a dictionary be converted to a class or struct in Swift?
To access it like this you need to convert your dictionary to Struct as follow:
edit/update: Swift 3.x
struct Job: CustomStringConvertible {
let number: Int
let name, client: String
init(dictionary: [String: Any]) {
self.number = dictionary["jobNumber"] as? Int ?? 0
self.name = dictionary["jobName"] as? String ?? ""
self.client = dictionary["client"] as? String ?? ""
}
var description: String {
return "Job#: " + String(number) + " - name: " + name + " - client: " + client
}
}
let dict: [String: Any] = ["jobNumber": 1234,
"jobName" : "Awards Ceremony",
"client" : "ACME Productions"]
let job = Job(dictionary: dict)
print(job.number) // 1234
print(job.name) // "Awards Ceremony"
print(job.client) // "ACME Productions"
print(job) // "Job#: 1234 - name: Awards Ceremony - client: ACME Productions"""
edit/update:
Swift 4 or later you can use JSON Codable protocol:
struct Job {
let number: Int
let name, client: String
}
extension Job: Codable {
init(dictionary: [String: Any]) throws {
self = try JSONDecoder().decode(Job.self, from: JSONSerialization.data(withJSONObject: dictionary))
}
private enum CodingKeys: String, CodingKey {
case number = "jobNumber", name = "jobName", client
}
}
extension Job: CustomStringConvertible {
var description: String {
return "Job#: " + String(number) + " - name: " + name + " - client: " + client
}
}
let dict: [String: Any] = ["jobNumber": 1234,
"jobName" : "Awards Ceremony",
"client" : "ACME Productions"]
do {
let job = try Job(dictionary: dict)
print(job.number) // 1234
print(job.name) // "Awards Ceremony"
print(job.client) // "ACME Productions"
print(job) // "Job#: 1234 - name: Awards Ceremony - client: ACME Productions\n"
} catch {
print(error)
}
How to convert a Swift object to a dictionary
Swift currently does not support advanced reflection like Java or C# so the answer is: no, there is not an equally easy and automated way with pure Swift.
[Update] Swift 4 has meanwhile the Codable
protocol which allows serializing to/from JSON and PLIST.
typealias Codable = Decodable & Encodable
How can I use Swift’s Codable to encode into a dictionary?
If you don't mind a bit of shifting of data around you could use something like this:
extension Encodable {
func asDictionary() throws -> [String: Any] {
let data = try JSONEncoder().encode(self)
guard let dictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] else {
throw NSError()
}
return dictionary
}
}
Or an optional variant
extension Encodable {
var dictionary: [String: Any]? {
guard let data = try? JSONEncoder().encode(self) else { return nil }
return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { $0 as? [String: Any] }
}
}
Assuming Foo
conforms to Codable
or really Encodable
then you can do this.
let struct = Foo(a: 1, b: 2)
let dict = try struct.asDictionary()
let optionalDict = struct.dictionary
If you want to go the other way(init(any)
), take a look at this Init an object conforming to Codable with a dictionary/array
Convert array data model to dictionary (swift)
var allDictionaries : [[String : AnyObject]]//array of all dictionaries.
func convertArrayToDictionaries([DataCart]) {
for data in DataCart {
let dictionary = [
"icon" : data.icon,
"cartId" : data.cartId,
"price" : data.price,
"productName" : data.productName,
"quantity" : data.quantity
]
allDictionaries.append(dictionary)
}
}
Related Topics
Using a Dispatch_Once Singleton Model in Swift
How to Display an Activity Indicator With Text on iOS 8 With Swift
Saving Picked Image to Coredata
Why Can't I Pass a Protocol.Type to a Generic T.Type Parameter
Multiple Functions With the Same Name
Access Firebase Variable Outside Closure
What's the Difference Between "As", "As!", and "As"
Firestore Search Array Contains For Multiple Values
Cfrunloop in Swift Command Line Program
How to Convert Unix Epoch Time to Date and Time in iOS Swift
What Is the "Some" Keyword in Swift(Ui)
Input from the Keyboard in Command Line Application
How to Hash Nsstring With Sha1 in Swift
How to Unwrap an Optional Value from Any Type