Convert JSON into Objects in Swift - ios

I have a json response which is fetched from server and it should be parsed as dictionary and convert it to NSObjects in Swift. How can i do this.
func convertJSONToObjects(object:Dictionary<String,AnyObject>?) -> CommonResponse{
var response:CommonResponse;
if(object != nil){
response = CommonResponse()
var responseObject = object //It is dictionary
var responseHeader = object["responseHeader"] //error
response.status = responseHeader["status"] as Int //error
//response.status is Int
}
return response
}
var parsedJson = NSJSONSerialization.JSONObjectWithData(object as NSData, options: NSJSONReadingOptions.MutableContainers, error: &parseError) as NSDictionary
calling function
convertJSONToObjects(parsedJson)

object is optional. You need some kind of unwrapping optional chaining, or optional bidding.
if let object = object {
var responseHeader = object["responseHeader"]
}
or
var responseHeader = object!["responseHeader"]
or
if let responseHeader = object?["responseHeader"] {
// ...
}
and so on.

You could set a break point before:
var responseHeader = object["responseHeader"] //error
to make sure that object does contains a key named "responseHeader".
Otherwise you should let variable responseHeader to be an Optional value.

I wrote a class that parses JSON text and maps it to Swift structs/classes.
let p = CutePossumParser(json: json)
let model = Possum(
name: p.parse("name", miss: ""),
home: Address(
planet: p["home"].parse("planet", miss: "")
)
)
if !p.success { /* mapping failed */ }
https://github.com/exchangegroup/cute-possum-parser

Related

Realm + Object Mapper + SwityJSON

I need help in mapping my object
Realm Model: https://gist.github.com/n1tesh/7d6c6e155285dd6b39c8edba76f6eba5
This is how I'm doing
// write request result to realm database
let entries = json["data"]
realm.beginWrite()
let entry: ChatGroups = Mapper<ChatGroups>().map(JSONObject: entries)!
realm.add(entry, update: true)
do {
try realm.commitWrite()
} catch {
}
JSON Response: https://gist.github.com/n1tesh/bf84cbd930f8c76b340f21723a217ebe
But i'm getting error
fatal error: unexpectedly found nil while unwrapping an Optional value
Please help me out with what I'm doing wrong.
Create a class to transform Array to List, because the Realm doesn't accept arrays.
import ObjectMapper
import RealmSwift
public class ListTransform<T:RealmSwift.Object> : TransformType where T:Mappable {
public typealias Object = List<T>
public typealias JSON = [AnyObject]
let mapper = Mapper<T>()
public init(){}
public func transformFromJSON(_ value: Any?) -> Object? {
let results = List<T>()
if let value = value as? [AnyObject] {
for json in value {
if let obj = mapper.map(JSONObject: json) {
results.append(obj)
}
}
}
return results
}
public func transformToJSON(_ value: Object?) -> JSON? {
var results = [AnyObject]()
if let value = value {
for obj in value {
let json = mapper.toJSON(obj)
results.append(json as AnyObject)
}
}
return results
}
}
Then in your ChatGroups class you have to call the Transform function to make the transformation, make this change:
updated_by <- map["updated_by"]
members <- map["member"]
to this:
updated_by <- (map["updated_by"], ListTransform<QuorgUser>())
members <- (map["member"], ListTransform<GroupMember>())
In your ChatGroup class you have declared
dynamic var image: String = ""
but in your response your are getting null in image key of JSON response.
You need to convert your declaration to:
dynamic var image: String? = nil

Turn swift object into a JSON string

I have classes like these:
class MyDate
{
var year : String = ""
var month : String = ""
var day : String = ""
init(year : String , month : String , day : String) {
self.year = year
self.month = month
self.day = day
}
}
class Lad
{
var firstName : String = ""
var lastName : String = ""
var dateOfBirth : MyDate?
init(firstname : String , lastname : String , dateofbirth : MyDate) {
self.firstName = firstname
self.lastName = lastname
self.dateOfBirth = dateofbirth
}
}
class MainCon {
func sendData() {
let myDate = MyDate(year: "1901", month: "4", day: "30")
let obj = Lad(firstname: "Markoff", lastname: "Chaney", dateofbirth: myDate)
let api = ApiService()
api.postDataToTheServer(led: obj)
}
}
class ApiService {
func postDataToTheServer(led : Lad) {
// here i need to json
}
}
And I would like to turn a Lad object into a JSON string like this:
{
"firstName":"Markoff",
"lastName":"Chaney",
"dateOfBirth":
{
"year":"1901",
"month":"4",
"day":"30"
}
}
EDIT - 10/31/2017: This answer mostly applies to Swift 3 and possibly earlier versions. As of late 2017, we now have Swift 4 and you should be using the Encodable and Decodable protocols to convert data between representations including JSON and file encodings. (You can add the Codable protocol to use both encoding and decoding)
The usual solution for working with JSON in Swift is to use dictionaries. So you could do:
extension Date {
var dataDictionary {
return [
"year": self.year,
"month": self.month,
"day": self.day
];
}
}
extension Lad {
var dataDictionary {
return [
"firstName": self.firstName,
"lastName": self.lastName,
"dateOfBirth": self.dateOfBirth.dataDictionary
];
}
}
and then serialize the dictionary-formatted data using JSONSerialization.
//someLad is a Lad object
do {
// encoding dictionary data to JSON
let jsonData = try JSONSerialization.data(withJSONObject: someLad.dataDictionary,
options: .prettyPrinted)
// decoding JSON to Swift object
let decoded = try JSONSerialization.jsonObject(with: jsonData, options: [])
// after decoding, "decoded" is of type `Any?`, so it can't be used
// we must check for nil and cast it to the right type
if let dataFromJSON = decoded as? [String: Any] {
// use dataFromJSON
}
} catch {
// handle conversion errors
}
If you just need to do this for few classes, providing methods to turn them into dictionaries is the most readable option and won't make your app noticeably larger.
However, if you need to turn a lot of different classes into JSON it would be tedious to write out how to turn each class into a dictionary. So it would be useful to use some sort of reflection API in order to be able to list out the properties of an object. The most stable option seems to be EVReflection. Using EVReflection, for each class we want to turn into json we can do:
extension SomeClass: EVReflectable { }
let someObject: SomeClass = SomeClass();
let someObjectDictionary = someObject.toDictionary();
and then, just like before, we can serialize the dictionary we just obtained to JSON using JSONSerialization. We'll just need to use object.toDictionary() instead of object.dataDictionary.
If you don't want to use EVReflection, you can implement reflection (the ability to see which fields an object has and iterate over them) yourself by using the Mirror class. There's an explanation of how to use Mirror for this purpose here.
So, having defined either a .dataDictionary computed variable or using EVReflection's .toDictionary() method, we can do
class ApiService {
func postDataToTheServer(lad: Lad) {
//if using a custom method
let dict = lad.dataDictionary
//if using EVReflection
let dict = lad.toDictionary()
//now, we turn it into JSON
do {
let jsonData = try JSONSerialization.data(withJSONObject: dict,
options: .prettyPrinted)
// send jsonData to server
} catch {
// handle errors
}
}
}
May this GitHub code will help you.
protocol SwiftJsonMappable {
func getDictionary() -> [String: Any]
func JSONString() -> String
}
extension SwiftJsonMappable {
//Convert the Swift dictionary to JSON String
func JSONString() -> String {
do {
let jsonData = try JSONSerialization.data(withJSONObject: self.getDictionary(), options: .prettyPrinted)
// here "jsonData" is the dictionary encoded in JSON data
let jsonString = String(data: jsonData, encoding: .utf8) ?? ""
// here "decoded" is of type `Any`, decoded from JSON data
return jsonString
// you can now cast it with the right type
} catch {
print(error.localizedDescription)
}
return ""
}
//Convert Swift object to Swift Dictionary
func getDictionary() -> [String: Any] {
var request : [String : Any] = [:]
let mirror = Mirror(reflecting: self)
for child in mirror.children {
if let lable = child.label {
//For Nil value found for any swift propery, that property should be skipped. if you wanna print nil on json, disable the below condition
if !checkAnyContainsNil(object: child.value) {
//Check whether is custom swift class
if isCustomType(object: child.value) {
//Checking whether its an array of custom objects
if isArrayType(object: child.value) {
if let objects = child.value as? [AMSwiftBase] {
var decodeObjects : [[String:Any]] = []
for object in objects {
//If its a custom object, nested conversion of swift object to Swift Dictionary
decodeObjects.append(object.getDictionary())
}
request[lable] = decodeObjects
}
}else {
//Not an arry, and custom swift object, convert it to swift Dictionary
request[lable] = (child.value as! AMSwiftBase).getDictionary()
}
}else {
request[lable] = child.value
}
}
}
}
return request
}
//Checking the swift object is swift base type or custom Swift object
private func isCustomType(object : Any) -> Bool {
let typeString = String(describing: type(of: object))
if typeString.contains("String") || typeString.contains("Double") || typeString.contains("Bool") {
return false
}
return true
}
//checking array
private func isArrayType(object : Any) -> Bool {
let typeString = String(describing: type(of: object))
if typeString.contains("Array"){
return true
}
return false
}
//Checking nil object
private func checkAnyContainsNil(object : Any) -> Bool {
let value = "\(object)"
if value == "nil" {
return true
}
return false
}
}
https://github.com/anumothuR/SwifttoJson

How can I store a Dictionary with RealmSwift?

Considering the following model:
class Person: Object {
dynamic var name = ""
let hobbies = Dictionary<String, String>()
}
I'm trying to stock in Realm an object of type [String:String] that I got from an Alamofire request but can't since hobbies has to to be defined through let according to RealmSwift Documentation since it is a List<T>/Dictionary<T,U> kind of type.
let hobbiesToStore: [String:String]
// populate hobbiestoStore
let person = Person()
person.hobbies = hobbiesToStore
I also tried to redefine init() but always ended up with a fatal error or else.
How can I simply copy or initialize a Dictionary in RealSwift?
Am I missing something trivial here?
Dictionary is not supported as property type in Realm.
You'd need to introduce a new class, whose objects describe each a key-value-pair and to-many relationship to that as seen below:
class Person: Object {
dynamic var name = ""
let hobbies = List<Hobby>()
}
class Hobby: Object {
dynamic var name = ""
dynamic var descriptionText = ""
}
For deserialization, you'd need to map your dictionary structure in your JSON to Hobby objects and assign the key and value to the appropriate property.
I am currently emulating this by exposing an ignored Dictionary property on my model, backed by a private, persisted NSData which encapsulates a JSON representation of the dictionary:
class Model: Object {
private dynamic var dictionaryData: NSData?
var dictionary: [String: String] {
get {
guard let dictionaryData = dictionaryData else {
return [String: String]()
}
do {
let dict = try NSJSONSerialization.JSONObjectWithData(dictionaryData, options: []) as? [String: String]
return dict!
} catch {
return [String: String]()
}
}
set {
do {
let data = try NSJSONSerialization.dataWithJSONObject(newValue, options: [])
dictionaryData = data
} catch {
dictionaryData = nil
}
}
}
override static func ignoredProperties() -> [String] {
return ["dictionary"]
}
}
It might not be the most efficient way but it allows me to keep using Unbox to quickly and easily map the incoming JSON data to my local Realm model.
I would save the dictionary as JSON string in Realm. Then retrive the JSON and convert to dictionary. Use below extensions.
extension String{
func dictionaryValue() -> [String: AnyObject]
{
if let data = self.data(using: String.Encoding.utf8) {
do {
let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: AnyObject]
return json!
} catch {
print("Error converting to JSON")
}
}
return NSDictionary() as! [String : AnyObject]
} }
and
extension NSDictionary{
func JsonString() -> String
{
do{
let jsonData: Data = try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
return String.init(data: jsonData, encoding: .utf8)!
}
catch
{
return "error converting"
}
}
}
UPDATE 2021
Since Realm 10.8.0, it is possible to store a dictionary in a Realm object using the Map type.
Example from the official documentation:
class Dog: Object {
#objc dynamic var name = ""
#objc dynamic var currentCity = ""
// Map of city name -> favorite park in that city
let favoriteParksByCity = Map<String, String>()
}
Perhaps a little inefficient, but works for me (example dictionary from Int->String, analogous for your example):
class DictObj: Object {
var dict : [Int:String] {
get {
if _keys.isEmpty {return [:]} // Empty dict = default; change to other if desired
else {
var ret : [Int:String] = [:];
Array(0..<(_keys.count)).map{ ret[_keys[$0].val] = _values[$0].val };
return ret;
}
}
set {
_keys.removeAll()
_values.removeAll()
_keys.appendContentsOf(newValue.keys.map({ IntObj(value: [$0]) }))
_values.appendContentsOf(newValue.values.map({ StringObj(value: [$0]) }))
}
}
var _keys = List<IntObj>();
var _values = List<StringObj>();
override static func ignoredProperties() -> [String] {
return ["dict"];
}
}
Realm can't store a List of Strings/Ints because these aren't objects, so make "fake objects":
class IntObj: Object {
dynamic var val : Int = 0;
}
class StringObj: Object {
dynamic var val : String = "";
}
Inspired by another answer here on stack overflow for storing arrays similarly (post is eluding me currently)...

Extract Value from Locksmith with SwiftyJson

I'm trying the value of an access token in Swift. I'm loading the token using Locksmith like so:
let (dictionary, error) = Locksmith.loadDataForUserAccount("userAccount")
This returns the following in the console:
Optional({
"access_token" = 123123123123123;
})
I'm using SwiftyJSON to extract the value of the access token
var access_token = dictionary["access_token"].stringValue
I'm getting the following error:
'NSDictionary?' does not have a member named 'subscript'
Any idea why?
LockSmith returns (NSDictionary?, NSError?):
public class func loadDataForUserAccount(userAccount: String, inService service: String = LocksmithDefaultService) -> (NSDictionary?, NSError?)
So, in your case dictionary itself is Optional:
Try:
var access_token = dictionary?["access_token"] as? String
Here, access_token is String?, it can be nil. If you want to store it to AnyObject variable, you must unwrap it. For example, using "Optional Binding":
if let token = access_token {
// Here, `token` is `String`, while `access_token` is `String?`
self.access_token = token
}
Or more directly:
if let access_token = dictionary?["access_token"] as? String {
// Here, access_token is `String`, not `String?`
self.access_token = token
}
else {
// error handling...
}
BTW, If you want to use SwiftyJSON, you should create JSON object from dictionary.
if let dict = dictionary {
var json = JSON(dict)
var access_token = json["access_token"].stringValue
}
As described at your 'NSDictionary?' Dictionary is an optional. You have to unwrap your optional before extracting stringValue from it. You can safely unwrap it using if let:
Edit
taking a hint from Rintaro as mentioned by him LockSmith returns an optional Dictionary so you need to first unwrap your dictionary and them you can unwrap access_token key value
if let dictionary = dictionary {
if let access_token = dictionary["access_token"] as? Int { // or as? String
println(access_token)
} else {
println(false)
}
} else {
println(false)
}
or if it returns a [String:String] Dictionary there is no need to cast the value to String
if let dictionary = dictionary {
if let access_token = dictionary["access_token"] {
println(access_token)
} else {
println(false)
}
} else {
println(false)
}

NSURL getResourceValue in swift

Having trouble figuring out how to make the following call in swift:
var anyError: NSError? = nil
var rsrc: NSNumber? = nil
var success = url.getResourceValue(&rsrc, forKey:NSURLIsUbiquitousItemKey, error:&anyError)
The above does not compile:
Cannot convert the expression's type 'Bool' to type 'inout Bool'
So I tried this:
var anyError: NSError? = nil
var rsrc: AutoreleasingUnsafePointer<AnyObject?> = nil
var success = url.getResourceValue(rsrc, forKey:NSURLIsUbiquitousItemKey, error:&anyError)
but this generates EXC_BAD_ACCESS runtime error.
How do I pass in the expected first arg as AutoreleasingUnsafePointer<AnyObject?> (which should point to a boolean NSNumber according to doc), and then be able to check its expected Bool value ?
You need to make rsrc an optional AnyObject and pass it by reference like so:
var anyError: NSError?
var rsrc: AnyObject?
var success = url.getResourceValue(&rsrc, forKey:NSURLIsUbiquitousItemKey, error:&anyError)
Note: You do not need to initialize Optionals to nil, they are set to nil by default.
If you then want to check if the value is an NSNumber you can then do a conversion:
if let number = rsrc as? NSNumber {
// use number
}
Here is drewag's code updated for Swift 2.0
do {
var rsrc: AnyObject?
try element.getResourceValue(&rsrc, forKey: NSURLIsDirectoryKey)
if let number = rsrc as? NSNumber {
if number == true {
// do something
}
}
} catch {
}
in swift 3 previous calls are not available/deprecated:
so use:
var keys = Set<URLResourceKey>()
keys.insert(URLResourceKey.isDirectoryKey)
do{
let URLResourceValues = try fileURL.resourceValues(forKeys: keys)
}catch _{
}
If the getResourceValue(:forKey:) result is ultimately a logical value, you can cast the returned pointer value directly to a Bool value:
let boolResult = rsrc as! Bool
or simply test it as a Bool value without assigning it:
if rsrc as! Bool {
// true code
} else {
// false code
}
or do both:
if let rsrc as! Bool {
// true
} else {
// false
}
Here's a concrete example as an extension of the URL type:
extension URL {
var creationDate : Date {
let url = self as NSURL
var value : AnyObject?
try? url.getResourceValue(&value, forKey: .creationDateKey)
if let date = value as? Date {
return date
}
return .distantPast
}
}
For convenience, if the call to getResourceValue() fails, it returns the value .distantPast by default. Using the try? form allows discarding the error which isn't always needed.

Resources