iOS Swift Storing Alamofire Response To Realm - ios

I need to store alamofire JSON response to realm storage directly. Here is the response tat I get from alamofire JSON.
{
"all": [
{
"date": "2017-03-30T00:00:00.000Z",
"subject": "loe",
"desc": "",
"id": 13,
"number": "19/312/2012",
"title": "loe",
"name": "Supreme",
"type": "Event",
"practice": 20,
"contact": "",
"object": "{\"id\":20,\"id\":13,\"name\":\"loe\",\"time\":\"2017-03-30T00:00:00.000Z\",\"end\":\"2017-03-31T00:00:00.000Z\",\"creator\":\"user\",\"created_by\":132,\"des\":\"\",\"created_at\":\"2017-03-30T08:22:31.150Z\",\"updated_at\":\"2017-03-30T08:23:04.944Z\",\"judge\":null,\"purpose\":null,\"google_event_id\":null,\"is_completed\":false,\"business\":null,\"last_notified\":\"2017-03-30T08:23:04.926Z\",\"next\":null,\"business_date\":null,\"business\":false}"
},
{
"date": "2017-03-30T00:30:00.000Z",
"subject": "user",
"desc": "Loe",
"id": 138,
"number": "19/312/2012",
"title": "loe user",
"name": "Supreme India",
"type": "Appointment",
"practice": 6,
"contact": 91,
"object": "{\"id\":20,\"id\":13,\"name\":\"loe\",\"time\":\"2017-03-30T00:00:00.000Z\",\"end\":\"2017-03-31T00:00:00.000Z\",\"creator\":\"user\",\"created_by\":132,\"des\":\"\",\"created_at\":\"2017-03-30T08:22:31.150Z\",\"updated_at\":\"2017-03-30T08:23:04.944Z\",\"judge\":null,\"purpose\":null,\"google_event_id\":null,\"is_completed\":false,\"business\":null,\"last_notified\":\"2017-03-30T08:23:04.926Z\",\"next\":null,\"business_date\":null,\"business\":false}"
}
And Here is the code that I have used for realm storage
class PracticeArea:Object,Mappable
{
dynamic var contact = 0
dynamic var id = ""
dynamic var number = ""
dynamic var title = ""
dynamic var name = ""
dynamic var date = ""
dynamic var description2 = ""
dynamic var object = ""
dynamic var practice = 0
dynamic var subject = ""
dynamic var type = ""
override static func primaryKey() -> String?
{
return "contact"
}
//Impl. of Mappable protocol
required convenience init?(map: Map)
{
self.init()
}
func mapping(map: Map)
{
contact <- map["contact"]
id <- map["id"]
number <- map["number"]
title <- map["title"]
name <- map["name"]
date <- map["date"]
description2 <- map["description"]
object <- map["object"]
practice <- map["practice"]
subject <- map["subject"]
type <- map["type"]
}
}
Code Used for Storing Values:
let PracticeTestValues = response.result.value!
let realm:Realm = try! Realm()
try! realm.write
{
for all in PracticeTestValues as! [Any]
{
realm.add(all as! Object, update: true)
}
}

I would sugget to use this:
for all in PracticeTestValues as? NSArray
{
realm.add(all as! Object, update: true)
}

Related

Ordering info of Dictionary to populate a tableView

I'm dealing with this. This is mi JSON response
{
"code": 200,
"message": "ok",
"data": {
"section1": [
{
"clave": "xxxx",
"orden": 0,
"nombre": "xxxxx",
"video": "xxxxx",
"imagen": "xxxxx",
"series": 0,
"repeticiones": 0,
"descanso":0,
"completado": false
},
{
"clave": "xxxx",
"orden": 0,
"nombre": "xxxxx",
"video": "xxxxx",
"imagen": "xxxxx",
"series": 0,
"repeticiones": 0,
"descanso":0,
"completado": false
}
}
],
"section2": [
{
"clave": "xxx",
"equipo": "xx",
"imagen": "x",
"tiempo": 0,
"intensidad": 0,
"completado": false
}
],
"section3": [
{
"clave": "xxx",
"nombre": "xxxx",
"imagen": "",
"completado": false
},
{
"clave": "xxx",
"nombre": "xxxx",
"imagen": "",
"completado": false
}
],
"section4": [
{
"clave": "xx",
"nombre": "xxxx",
"imagen": "x",
"completado": false
},
{
"clave": "xx",
"nombre": "xxxx",
"imagen": "x",
"completado": false
}
]
}
}
What I want to do is display the info in sections, the sections should be "section1", "section2", "section3", "section4" ,obviously and display all the info that "section1" contains, and if the section is "section2" display all the info in cardios an so on... But I want to display it in the same tableView just divided in sections Could you help me?. thanks in Advance
Since NSDictionary is not an ordered data container, you would have to use a different data structure or you would have to update the API and return an ordered array inside the "data".
First of all in the JSON above Section2 is a extraneous closing brace.
This is a starting point.
Decode the value for data as [String:[Item]] and map each dictionary to a helper struct Section containing the name (dictionary key) and the array of Item (dictionary value). The sections array is sorted by name
struct Root : Decodable {
let code : Int
let message : String
let sections : [Section]
enum CodingKeys: String, CodingKey { case code, message, sections = "data"}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
code = try container.decode(Int.self, forKey: .code)
message = try container.decode(String.self, forKey: .message)
let data = try container.decode([String : [Item]].self, forKey: .sections)
sections = data.map({Section(name: $0.0, items: $0.1)}).sorted(by: {$0.name < $1.name})
}
}
struct Section {
let name : String
let items : [Item]
}
struct Item : Decodable {
let clave : String
let completado : Bool
let repeticiones : Int?
// ... other properties
}
Decode the Root struct (data is the JSON data)
let result = try JSONDecoder().decode(Root.self, from: data)
let sections = result.sections
In the table view sections are the sections and items are the rows
Another solution should create a custom parser which will convert your data into an array of a model which would represent any section.
Model
class SomethingObj {
var clave: String?
var orden: Int?
var nombre: String?
var video: String?
var imagen: String?
var series: Int?
var repeticiones: Int?
var descanso: Int?
var completado: Bool?
init() {
}
}
Parser
private func parseData(for structure: NSDictionary) -> [[SomethingObj]] {
var sectionsArray = [[SomethingObj]]()
guard let sectionsLoop = structure["data"] as? NSDictionary else { return sectionsArray }
var sectionIndex = 1
while let sectionObjsData = sectionsLoop["section\(sectionIndex)"] as? [NSDictionary] {
var sectionArray = [SomethingObj]()
for sectionObjData in sectionObjsData {
let obj = SomethingObj()
obj.clave = sectionObjData["clave"] as? String
obj.orden = sectionObjData["orden"] as? Int
obj.nombre = sectionObjData["nombre"] as? String
obj.video = sectionObjData["video"] as? String
obj.imagen = sectionObjData["imagen"] as? String
obj.series = sectionObjData["series"] as? Int
obj.repeticiones = sectionObjData["repeticiones"] as? Int
obj.descanso = sectionObjData["descanso"] as? Int
obj.completado = sectionObjData["completado"] as? Bool
sectionArray.append(obj)
}
sectionsArray.append(sectionArray)
sectionIndex = sectionIndex + 1
}
return sectionsArray
}
Display parsed data
Use whatever you want in order to display something with your array of parsed data.

Nested repeatative loop in swift

I am trying to parse a nested iterative loop in swift
I am getting the response from web service in the following format
{
"categories": [{
"name": "Default Category",
"id": "default_category",
"children": [{
"uuid": "783f491fef5041438fb7a2c3bf6a3650",
"name": "Accessories",
"children": [{
"uuid": "d21b4491ff784a9bae88de279b99fac3",
"name": "All Accessories",
"children": [{
"uuid": "2b1a23c4107844ad8a7afc1b324d0ffd",
"name": "Belts",
"children": [{
"uuid": "2b1a23c4107844ad8a7afc1b324d0ffd",
"name": "Belts",
"children": []
},
{
"uuid": "2b1a23c4107844ad8a7afc1b324d0ffd",
"name": "Belts",
"children": []
}
]
},
{
"uuid": "a1c2a64c36c2461cad3d5f850e4fd0f5",
"name": "Hats",
"children": []
},
{
"uuid": "8f26bc764b8342feaa0cb7f3b96adcae",
"name": "Scarves",
"children": []
},
{
"uuid": "aa1116d1a0254ecea836cc6b32eeb9e0",
"name": "Sunglasses",
"children": []
},
{
"uuid": "9d7033233e8f47eaa69eb1aaf2e98cdd",
"name": "Watches",
"children": []
}
]
}]
}],
"uuid": "6a23415771064e7aaad59f84f8113561"
}]
}
Inside, the categories, there is 'children' key which in turn can contain another children and so on.
I want to continuously loop inside the children key until the children key is empty and insert the last child into database.
Following is the code which i have done
for currentCategory in mainCategories {
// guard against if there are child categories
guard var children = currentCategory.children, children.count > 0 else {
// Save the context
self.coreData.saveStore()
continue
}
for thisChildCategory in children {
if thisChildCategory.children?.count > 0 {
for innerChildCategory in thisChildCategory.children! {
print("innerChildCategory name \(String(describing: innerChildCategory.name))")
}
}
if let child = thisChildCategory.children {
children = child
}
// Create new object
if let currentChildCategory = self.coreData.insertNewObject(CoreDataEntities.BijouCategories.rawValue,
keyValues: ["id" : thisChildCategory.id! as Optional<AnyObject>,
"uuid" : thisChildCategory.uuid as Optional<AnyObject>,
"name" : thisChildCategory.name! as Optional<AnyObject>,
"gender" : thisChildCategory.gender as Optional<AnyObject>!,
"active" : NSNumber(value: false)]) as? BijouCategories {
// Set as parent category
currentChildCategory.parentCategory = parentCategory
// Save the context
self.coreData.saveStore()
}
}
}
But this is not saving all the last child category in database.
Swift 4
You should let Swift 4's codable do the work for you.
You can use the following class as a struct but I find using a class is better if you plan on editing the data.
class Categories: Codable {
var categories: [CategoryItems]
}
class CategoryItems: Codable {
var name: String?
var id: String?
var uuid: String?
var children: [CategoryItems]?
required init(from decoder: Decoder) throws {
var container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decodeIfPresent(String.self, forKey: CodingKeys.name)
id = try container.decodeIfPresent(String.self, forKey: CodingKeys.id)
uuid = try container.decodeIfPresent(String.self, forKey: CodingKeys.uuid)
children = try container.decodeIfPresent([CategoryItems].self, forKey: CodingKeys.children)
if children != nil, children!.count == 0 {
children = nil
}
}
You can see here we add create the root level class "Categories" that has an array of CategoryItems. CategoryItems has all the possible values within it, but each item in the array may or may not have all of the possible values, hence they are optional. The important one is the children which is optional. Then in the required init we only se the optional values if the key value pair is available when decoding. I also set the children to nil if there are zero items, this is optional but helps when doing if statements later.
Then to decode your json using these codable classes you use the following code.
func decode(jsonData data: Data) {
let decoder = JSONDecoder()
do {
let decoded = try decoder.decode(Categories.self, from: data)
}
catch let error as NSError {
print("JSON Decode error = ", error)
}
}
If you want to do a quick test to see if you got the deeping children level which I did you can simply run the following on the decoded variable.
for i in decoded.categories.first!.children!.first!.children!.first!.children!.first!.children! {
print(i.name)
print(i.uuid)
}
With more than 2 nested levels a recursive function is recommended. recursive means the function calls itself.
Here is an simple example assuming jsonString is the given JSON in the question.
The function parseCategory passes the children array and the UUID string as parent identifier. The print line is the place to save the object in Core Data and of course you can pass the created Core Data object as parent as well to set the relationship.
func parseCategory(children: [[String:Any]], parent: String) {
for child in children {
print("Save in Core Data", child["name"] as! String, parent)
let descendants = child["children"] as! [[String:Any]]
parseCategory(children:descendants, parent: child["uuid"] as! String)
}
}
let data = Data(jsonString.utf8)
do {
let json = try JSONSerialization.jsonObject(with: data) as! [String:Any]
parseCategory(children: json["categories"] as! [[String:Any]], parent: "")
} catch { print(error)}
The output is
"Save in Core Data Default Category
Save in Core Data Accessories 6a23415771064e7aaad59f84f8113561
Save in Core Data All Accessories 783f491fef5041438fb7a2c3bf6a3650
Save in Core Data Belts d21b4491ff784a9bae88de279b99fac3
Save in Core Data Belts 2b1a23c4107844ad8a7afc1b324d0ffd
Save in Core Data Belts 2b1a23c4107844ad8a7afc1b324d0ffd
Save in Core Data Hats d21b4491ff784a9bae88de279b99fac3
Save in Core Data Scarves d21b4491ff784a9bae88de279b99fac3
Save in Core Data Sunglasses d21b4491ff784a9bae88de279b99fac3
Save in Core Data Watches d21b4491ff784a9bae88de279b99fac3"
Created an model class to hold your nested children in the form of a tree.
class Children {
var uuid: String?
var name: String?
var children: [Children] = [Children(array: [])]
init(array: NSArray) {
let childrenDic = array[0] as! NSDictionary
uuid = childrenDic["uuid"] as? String
name = childrenDic["name"] as? String
children[0] = Children.init(array: childrenDic["children"] as! NSArray)
}
}
Use like
var childrenModel = Children.init(array: yourArray)
I would suggest you to use ObjectMapper instead of unwrapping the json manually.
https://github.com/Hearst-DD/ObjectMapper
then everything should be much cleaner
class Child: Mappable {
var uuid: String?
var name: String?
var childern: [Child]?
required init?(map: Map) {
}
// Mappable
func mapping(map: Map) {
uuid <- map["uuid"]
name <- map["name"]
childern <- map["childern"]
}
}
class Category: Mappable {
var _id: String? //id is the reserved word
var name: String?
var childern: [Child]?
required init?(map: Map) {
}
// Mappable
func mapping(map: Map) {
_id <- map["id"]
name <- map["name"]
childern <- map["childern"]
}
}

ObjectMapper - nested dynamic keys

I'm writing in Swift 3.1, using ObjectMapper to map my JSON response to my models.
I'm trying to map this rather complex JSON response with dynamic keys and am hoping to get some feedback on what I'm doing wrong.
A group has statistics about it's progress. It has stats broken down to years and then months. Each month within a year has results, ROI and win. The ROI and win are just percentages but the results key is fixed with the keys below, 1-5, and then some integer as a value.
My JSON
"stats": {
"2017": {
"1": {
"results": {
"1": 13,
"2": 3,
"3": 1,
"4": 1,
"5": 0
},
"roi": 0.40337966202464975,
"win": 0.8181818181818182
},
"2": {
"results": {
"1": 13,
"2": 5,
"3": 1,
"4": 2,
"5": 1
},
"roi": 0.26852551067922953,
"win": 0.717948717948718
}
}
}
My models
class GroupResponse: Mappable {
var stats: [String: [String: StatsMonthResponse]]?
func mapping(map: Map) {
stats <- map["stats"]
}
}
class StatsMonthResponse: Mappable {
var tips: [String: Int]?
var roi: Double?
var win: Double?
func mapping(map: Map) {
tips <- map["results"]
roi <- map["roi"]
win <- map["win"]
}
}
What I get
The response I get has the stats property in my GroupResponse class, as nil.
What other approach could I do to accomplish this, or change in my implementation to get this done?
Solution
I solved my problem by mapping the JSON manually.
class GroupResponse: Mappable {
var stats: [String: StatsYear]?
func mapping(map: Map) {
stats <- map["stats"]
}
}
class StatsYear: Mappable {
var months: [String: StatsMonth] = [:]
override func mapping(map: Map) {
for (monthKey, monthValue) in map.JSON as! [String: [String: Any]] {
let month = StatsMonth()
for (monthKeyType, valueKeyType) in monthValue {
if monthKeyType == "results" {
let tipResultDict = valueKeyType as! [String: Int]
for (result, tipsForResult) in tipResultDict {
month.tips[result] = tipsForResult
}
}
else if monthKeyType == "roi" {
month.roi = valueKeyType as? Double
}
else if monthKeyType == "win" {
month.win = valueKeyType as? Double
}
}
months[monthKey] = month
}
}
}
class StatsMonth {
var tips: [String: Int] = [:]
var roi: Double?
var win: Double?
}
There's probably a better solution to this problem but this is what I'm sticking with for now.
Hopefully this helps!

Model from JSON containing Array and Dictionary

class AppUsers: Object{
dynamic var email: String = ""
dynamic var type: String = ""
required convenience init?(map: Map) {
self.init()
mapping(map: map)
}
override class func primaryKey() -> String {
return "email"
}
}
extension AppUsers : Mappable {
func mapping(map: Map) {
email <- map["email"]
// active_platforms <- map["active_platforms"]
type <- map["type"]
// linked_to <- map["linked_to"]
}
}
JSON RESPONSE:
{
"email": "asd#gmail.com",
"type": "primary_email",
"linked_to": {
"_id": "DAS44564dasdDASd",
"image": null,
"company": null,
"designation": null,
"name": null
},
"active_platforms": [
"asd",
"qwe"
]
}
From above response how to get linked_to which is a Dictionary and active_platforms which is an Array. I tried creating separate class for linked_to create var of it in AppUsers but it didn't helped.
A one to one relationship is
dynmamic var linkedTo: LinkObject?
A one to many relationship is
let activePlatforms = List<ActivePlatform>()
If you want to use object mapper to fill them, LinkObject must be mappable and ActivePlatform must be mappable AND you must supply a custom transform to convert the JSON array into a Realm List.

How to handle root JSON arrays with AlamofireObjectMapper?

I'm currently using AlamofireObjectMapper to create JSON objects. I'm having a tough time accessing JSON arrays:
This is the JSON Array:
[
{
"city": "string",
"country": "string",
"county": "string",
}
]
This is the function:
func getMemberDetails ( id: String) {
Alamofire.request(.GET, "\(baseURL)/api/Members/\(id)/memberDetails").responseArray { (response: Response<[MemberDetailInfo], NSError>) in
let memberDetailArray = response.result.value
if let memberDetailArray = memberDetailArray {
for memberDetail in memberDetailArray {
print(memberDetail.createDate)
print(memberDetail.id)
}
}
}
}
This Is the class:
class MemberDetailInfo: Mappable{
var city: String?
var country: String?
var county: String?
required init?(_ map: Map) {
mapping(map)
}
unc mapping(map: Map) {
city <- map["city"]
country <- map["country"]
county <- map["county"]
}
}
Whenever I step through it it just jumps right to the end, I’m not sure why it isn’t working. If anyone knows how to extract the JSON data from the array it would be greatly appreciated.

Resources