Nested array of double in realm (swift) - ios

I have this JSON:
{
"location": {
"position": {
"type": "Point",
"coordinates": [
45.579553,
11.751805
]
}
}
}
Which belongs to another JSON object.
Trying to map it with Realm and ObjectMapper, I am findind difficulties mapping the coordinates property which is an array of double.
That's what reading the documentation and S.O. seems to have sense:
import Foundation
import RealmSwift
import ObjectMapper
class Coordinate:Object, Mappable{
dynamic var latitude:Double = 0.0
dynamic var longitude:Double = 0.0
required convenience init?(_ map: Map) {
self.init()
}
func mapping(map: Map) {
latitude <- map[""]
longitude <- map[""]
}
}
class Position: Object, Mappable{
var type:String = ""
var coordinates:Coordinate?
required convenience init?(_ map: Map) {
self.init()
}
func mapping(map: Map) {
type <- map["type"]
coordinates <- map["coordinates"]
}
}
class Location: Object, Mappable{
dynamic var id = ""
dynamic var position:Position?
dynamic var desc = ""
override static func indexedProperties()->[String]{
return["id"]
}
override class func primaryKey() -> String? {
return "id"
}
required convenience init?(_ map: Map) {
self.init()
}
func mapping(map: Map) {
id <- map["id"]
position <- map["position"]
}
}
However I'm stuck in understanding how to map the "coordinates" object. Please note that this problem has nothing to do with ObjectMapper itself, it's more of a question on how to assign an array of Double to a property in a Realm model.

I was able to solve this following the indications in this issue:
https://github.com/realm/realm-cocoa/issues/1120 (credits #jazz-mobility)
class DoubleObject:Object{
dynamic var value:Double = 0.0
}
class Position: Object, Mappable{
var type:String = ""
var coordinates = List<DoubleObject>()
required convenience init?(_ map: Map) {
self.init()
}
func mapping(map: Map) {
type <- map["type"]
var coordinates:[Double]? = nil
coordinates <- map["coordinates"]
coordinates?.forEach { coordinate in
let c = DoubleObject()
c.value = coordinate
self.coordinates.append(c)
}
}
}

You can now simply use List<Double>() without it storing an object.
More information can be found here: https://academy.realm.io/posts/realm-list-new-superpowers-array-primitives/

#objcMembers class RealmObject: Object, Mappable {
dynamic var listValues = List<MyRealmObject>()
required convenience init?(map: Map) {
self.init()
}
// Mappable
func mapping(map: Map) {
listValues <- (map["listValues"], RealmlistObjectTransform())
}
}
#objcMembers class MyRealmObject: Object, Mappable {
required convenience init?(map: Map) {
self.init()
}
// Mappable
func mapping(map: Map) {
}
}
class RealmlistObjectTransform: TransformType {
typealias Object = List<MyRealmObject> // My Realm Object here
typealias JSON = [[String: Any]] // Dictionary here
func transformFromJSON(_ value: Any?) -> List<MyRealmObject>? {
let list = List<MyRealmObject>()
if let actors = value as? [[String: Any]] {
let objects = Array<MyRealmObject>(JSONArray: actors)
list.append(objectsIn: objects)
}
return list
}
func transformToJSON(_ value: List<MyRealmObject>?) -> [[String: Any]]? {
if let actors = value?.sorted(byKeyPath: "").toArray(ofType: MyRealmObject.self).toJSON() {
return actors
}
return nil
}
}

Related

ObjectMapper how map Dictionary of [String:CustomObject] With Index as CustomObject Property

I started to use ObjectMapper this week and I'm trying to map a JSON into 2 CustomClasses but I don't known if ObjectMapper has some function to do what I want. First CustomClass has a property of type: [String:CustomClass2] where the index of this Dictionary should be property ID of second CustomObject.
JSON Used:
{
"types": [
{
"id": "mk8QPMSo2xvtSoP0cBUD",
"name": "type 1",
"img": "type_1",
"showCategories": false,
"modalityHint": [
"K7VqeFkRQNXoh2OBxgIf"
],
"categories": [
"mP3MqbJrO5Da1dVAPRvk",
"SlNezp2m3PECnTyqQMUV"
]
}
]
}
Classes Used:
class MyClass: Mappable {
var types:[String:MyClass2] = [String:MyClass2]() //Index should be ID property of MyClass2 Object
required init?(map:Map) {
guard map.JSON["types"] != nil else {
return nil
}
}
func mapping(map: Map) {
types <- map["types"]
}
}
class MyClass2: Mappable {
private var id: String!
private var name: String!
private var img: String!
private var showCategories: Bool!
private var modalityHint: [String]?
private var categories: [String]?
required init?(map: Map) { }
func mapping(map: Map) {
id <- map["id"]
name <- map["name"]
img <- map["img"]
showCategories <- map["showCategories"]
modalityHint <- map["modalityHint"]
categories <- map["categories"]
}
In your JSON the types key is an array not a Dictionary.
Change:
var types:[String:MyClass2] = [String:MyClass2]()
To:
var types:[Class2] = []
Like this:
class MyClass: Mappable {
private var arrayTypes = [MyClass2] {
didSet{
var mapTypes = [String:MyClass2]?
for obj in arrayTypes {
mapTypes[obj.id] = obj
}
types = mapTypes
}
}
var types:[String:MyClass2] = [String:MyClass2]()
required init?(map:Map) {
guard map.JSON["types"] != nil else {
return nil
}
}
func mapping(map: Map) {
arrayTypes <- map["types"]
}
}

Use ObjectMapper to parse Dictionary within a Dictionary Swift

My old JSON structure was like this:
{
"parameters": {
"customerId": 9,
"from": "2014-06-05T14:00:00",
"until": "2014-06-05T15:00:00",
"km": 20,
"insurance": false
},
"estimatesPerCategory": {
"2": {
"timeCost": 5,
"kmCost": 6,
...
}
To parse JSON I use ObjectMapper.
This code works very well:
if let myObject = Mapper<CostEstimateResult>().map(JSONObject: JSON) {
}
The class CostEstimateResult looks like this.
class CostEstimateResult : NSObject, Mappable {
var parameters:CostEstimateParameters?
var estimatesPerCategory:[String: CostEstimate]? // CostEstimate.id -> CostEstimate
override init() {}
required convenience init?(map: Map) {
self.init()
self.mapping(map: map)
}
func mapping(map: Map) {
estimatesPerCategory <- map["estimatesPerCategory"]
parameters <- map["parameters"]
}
}
And last but not least my CostEstimate class for estimatesPerCategory
class CostEstimate : NSObject, Mappable {
var timeCost:NSNumber?
...
override init() {}
required convenience init?(map: Map) {
self.init()
self.mapping(map: map)
}
func mapping(map: Map) {
timeCost <- map["timeCost"]
...
}
}
This is working. The mapper in CostEstimateResult will call the mapper in CostEstimates and I get my parsed data.
Now I changed the JSON Structure:
{
"parameters": {
//same like before
},
"estimatesPerCategory": {
"2": {
"total": {
"preTax": {
"value": 1,
"text": "1,00 €"
},
"configTax": false
},
"3": {
//same...
}
}
}
In my opinion in the basic structure nothing changes. estimatesPerCategory is still the same dictionary. The data behind estimatesPerCategory are changed, okay.
The problem is, when I call the mapper ...Mapper<CostEstimateResult>().map... the parameters will be parse, but not the costEstimates. The func mapping in CostEstimate isn't call anymore.
Long story short: It seems that the type of estimatesPerCategory in CostEstimateResult changes with the json structure. I can't figure out which type I need.
Now with new JSON Structure, your Class CostEstimate structure will be changed and a new Class PreTax will be required to be created:
class CostEstimateResult : NSObject, Mappable {
var parameters:CostEstimateParameters?
var estimatesPerCategory:[String: CostEstimate]? // CostEstimate.id -> CostEstimate
override init() {}
required convenience init?(map: Map) {
self.init()
self.mapping(map: map)
}
func mapping(map: Map) {
estimatesPerCategory <- map["estimatesPerCategory"]
}
}
class CostEstimate : NSObject, Mappable {
var total:totalTax?
override init() {}
required convenience init?(map: Map) {
self.init()
self.mapping(map: map)
}
func mapping(map: Map) {
total <- map["total"]
}
}
class totalTax : NSObject, Mappable {
var preTax:PreTax?
var configTax:Bool?
override init() {}
required convenience init?(map: Map) {
self.init()
self.mapping(map: map)
}
func mapping(map: Map) {
preTax <- map["preTax"]
configTax <- map["configTax"]
}
}
class PreTax : NSObject, Mappable {
var value:NSNumber?
var text:String?
override init() {}
required convenience init?(map: Map) {
self.init()
self.mapping(map: map)
}
func mapping(map: Map) {
value <- map["value"]
text <- map["text"]
}
}

How to map custom Enum/RawRepresentable to dictionary with ObjectMapper?

using the following simplified structure:
class Property: Mappable {
var path: String?
override func mapping(map: Map) {
path <- map["path"]
}
}
class Specification {
enum Name: String {
case Small = "SMALL"
case Medium = "MEDIUM"
}
}
class ItemWithImages: Mappable {
var properties: [Specification.Name : Property]?
override func mapping(map: Map) {
properties <- (map["properties"], EnumTransform<Specification.Name>())
}
}
... with that JSON:
[{"properties: ["SMALL": {"path": "http://..."}, "MEDIUM": {"path": "http://..."}]}]
... produces when using EnumTransform() as Transform the following (reasonable) compile error:
Binary operator '<-' cannot be applied to operands of type '[Specification.Name : Property]?' and '(Map, EnumTransform<Specification.Name>)'
So how does a custom TransformType have to look like, to map that dictionary the right way?
You can find the source of EnumTransform here: https://github.com/Hearst-DD/ObjectMapper/blob/master/ObjectMapper/Transforms/EnumTransform.swift
Thanks!
TransformTypes are IMHO primary designed to transform values and not keys. And your example is a little bit complicated because even value is not just basic type.
What do you think about this little hack?
struct ItemWithImages: Mappable {
var properties: [Specification.Name : Property]?
init?(_ map: Map) {
}
mutating func mapping(map: Map) {
let stringProperties: [String: Property]?
// map local variable
stringProperties <- map["properties"]
// post process local variable
if let stringProperties = stringProperties {
properties = [:]
for (key, value) in stringProperties {
if let name = Specification.Name(rawValue: key) {
properties?[name] = value
}
}
}
}
}
We should use DictionaryTransform instead of EnumTransform. We are transforming type of Dictionary [String:Any] to [Key:Value]. In our case type is [Specification.Name: Property].
Key need to conforms protocols like Hashable, RawRepresentable, and Key.RawValue should be String.
And Value Should be conforms Mappable Protocol.
class Property: Mappable {
var path: String?
override func mapping(map: Map) {
path <- map["path"]
}
}
class Specification {
enum Name: String {
case Small = "SMALL"
case Medium = "MEDIUM"
}
}
class ItemWithImages: Mappable {
var properties: [Specification.Name : Property]?
override func mapping(map: Map) {
properties <- (map["properties"], DictionaryTransform<Specification.Name,Property>())
}
}

How to map this JSON using ObjectMapper?

I am getting this JSON in response from a webservice. I am able to understand the structure of the file.
{"response":200,"message":"fetch successfully","data":[
{"id":1,"question":"Are you currently exercising regularly?","choices":{"too_lazy":"Too lazy","times_1_3_week":"1-3 times a week","i_love_it":"I just love it!"},"answer_select":"single"},
{"id":2,"question":"Are you active member of Gym?","choices":{"yes":"Yes","no":"No"},"answer_select":"single"}]
}
and this is what I have so far
import Foundation
import UIKit
import ObjectMapper
class YASUserQuestion: Mappable {
var question: String
var id: Int
var choices: [YASExercisingQuestionChoices]
required init?(_ map: Map) {
question = ""
id = 0
choices = []
}
func mapping(map: Map) {
question <- map["question"]
id <- map["id"]
choices <- map["choices"]
}
}
class YASExercisingQuestionChoices: Mappable {
var tooLazy: String
var times_1_3_Week: String
var ILoveIt: String
required init?(_ map: Map) {
tooLazy = ""
times_1_3_Week = ""
ILoveIt = ""
}
func mapping(map: Map) {
tooLazy <- map["too_lazy"]
times_1_3_Week <- map["times_1_3_week"]
ILoveIt <- map["i_love_it"]
}
}
class YASGymMemberQuestionChoices: Mappable {
var yes: String
var no: String
required init?(_ map: Map) {
yes = ""
no = ""
}
func mapping(map: Map) {
yes <- map["yes"]
no <- map["no"]
}
}
I am not sure how to map this JSON response so can you please guide me How can I map this JSON ???
This will be your classes structure. After making these classes, next you just have map your JSON using ObjectMapper.
import UIKit
import ObjectMapper
class UserResponse: NSObject,Mappable {
var message: String?
var response: String?
var data: [DataClass]?
override init() {
super.init()
}
convenience required init?(map: Map) {
self.init()
}
func mapping(map: Map) {
message <- map["message"]
response <- map["response"]
data <- map["data"]
}
}
class DataClass: NSObject,Mappable {
var answer_select: String?
var ID: String?
var question: String?
var choices: [ChoicesClass]?
override init() {
super.init()
}
convenience required init?(map: Map) {
self.init()
}
func mapping(map: Map) {
answer_select <- map["answer_select"]
ID <- map["id"]
question <- map["question"]
choices <- map["choices"]
}
}
class ChoicesClass: NSObject,Mappable {
var i_love_it: String?
var times_1_3_week: String?
var too_lazy: String?
override init() {
super.init()
}
convenience required init?(map: Map) {
self.init()
}
func mapping(map: Map) {
i_love_it <- map["i_love_it"]
times_1_3_week <- map["times_1_3_week"]
too_lazy <- map["too_lazy"]
}
}

Swift: How to Convert JSON String with Alamofilre or SwiftyJSON to ObjectMapper?

I'm currently using ObjectMapper for Swift for mapping JSON Object from API to model Object
but my restful api return API look like this:
{
success: true,
data:
[{
"stats":{
"numberOfYes":0,
"numberOfNo":2,
"progress":{
"done":false,
"absolute":"2/100",
"percent":2
}
},
"quickStats":null,
"uid":5,
"name":"Flora",
"imageArray":[
"http://s3.com/impr_5329beac79400000",
"http://s3.com/impr_5329beac79400001"
],
"metaData":{
"description":"Floral Midi Dress",
"price":"40$"
}
}]
}
In data node is array i can't look up to mapping with this code
let json = JSON(responseObject!)
for tests in json["impressions"][0] {
let test = Mapper<myTests>().map(tests)
println(test?.impressionID)
}
How should I fix?
Thanks
** Edited **
I found solution similar #tristan_him
ObjectModel mapping structure
class Response: Mappable {
var success: Bool?
var data: [Data]?
required init?(_ map: Map) {
mapping(map)
}
func mapping(map: Map) {
success <- map["success"]
data <- map["data"]
}
}
class Data: Mappable {
var uid: Int?
var name: String?
// add other field which you want to map
required init?(_ map: Map) {
mapping(map)
}
func mapping(map: Map) {
uid <- map["uid"]
name <- map["name"]
}
}
Mapping with Alamofire response
let response: Response = Mapper<Response>().map(responseObject)!
for item in response.data! {
let dataModel: Data = item
println(dataModel.name)
}
You can map the JSON above by using the following class structure:
class Response: Mappable {
var success: Bool?
var data: [Data]?
required init?(_ map: Map) {
mapping(map)
}
func mapping(map: Map) {
success <- map["success"]
data <- map["data"]
}
}
class Data: Mappable {
var uid: Int?
var name: String?
// add other field which you want to map
required init?(_ map: Map) {
mapping(map)
}
func mapping(map: Map) {
uid <- map["uid"]
name <- map["name"]
}
}
Then you can map it as follows:
let response = Mapper<Response>().map(responseObject)
if let id = response?.data?[0].uid {
println(id)
}

Resources