Parsing JSON & work with ObjectMapper,SwiftRealm,ObjectMapperAdditions,ObjectMapperAdditionsRealm & Alamofire - json

regards, I am trying to serialize a json from the placeHolder page, the users one (https://jsonplaceholder.typicode.com/ users), I am trying to use the following libraries together:
-> Alamofire
-> RealmSwift
-> ObjectMapper
-> ObjectMapperAdditions
-> ObjectMapper_Realm
Here is the model that I am defining, but the problem is that SwiftRealm does not support the ArrayDictionary data type, as you try changing the type in the COMPANY & ADDRESS attributes, which are of the array type, try to put it as List , [Address1]?, Address1, List , etc. but it does not work, it always marks the same error "The operation could not be completed, ObjectMapper could not serialize the response.", so it can be received as an object but it can not be saved in RealmSwift or failing that What to do, but you will also receive as JSON or ARRAY and it is also not possible to serialize it, have you implemented something like that?
import Foundation
import RealmSwift
import ObjectMapper
import ObjectMapperAdditions
import ObjectMapper_Realm
class PlaceHolderClass: Object, Mappable {
#objc dynamic var id = 0
#objc dynamic var name = ""
#objc dynamic var username = ""
#objc dynamic var email = ""
var address: Address1?
#objc dynamic var phone = ""
#objc dynamic var website = ""
var company: Company?
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
id <- map["id"]
name <- map["name"]
username <- map["username"]
email <- map["email"]
address <- map["address"]
phone <- map["phone"]
website <- map["website"]
company <- map["company"]
}
}
class Address1: Object, Mappable {
var street = List<String>()
var suite = List<String>()
var city = List<String>()
var zipcode = List<String>()
var geo: Geo?
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
street <- map["street"]
suite <- map["suite"]
city <- map["city"]
zipcode <- map["zipcode"]
geo <- map["geo"]
}
}
class Geo: Object, Mappable {
var lat = List<String>()
var lng = List<String>()
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
lat <- map["lat"]
lng <- map["lng"]
}
}
class Company: Object, Mappable {
var name = List<String>()
var catchPhrase = List<String>()
var bs = List<String>()
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
name <- map["name"]
catchPhrase <- map["catchPhrase"]
bs <- map["bs"]
}
}
My request Code:
func requestPlaceHolder(){
let URL = "https://jsonplaceholder.typicode.com/users"
Alamofire.request(URL).responseObject{ (response: DataResponse<PlaceHolderClass>) in
switch response.result {
case .success(let objects):
let realm = try! Realm()
let myPlaceHolderResponse = PlaceHolderClass(value: response.result.value)
try! realm.write {
realm.add(myPlaceHolderResponse, update: true)
print("se agrego correctamente")
}
print(Realm.Configuration.defaultConfiguration.fileURL)
case .failure(let error):
print("Ocurrio el siguiente error \(error.localizedDescription)")
}
print(Realm.Configuration.defaultConfiguration.fileURL)
}
}

Related

Mapping local JSON file to Realm object using Object Mapper

I have local JSON file and i am trying to map that json to realm object using ObjectMapper_Realm library.
var totalLessonArray = [Lesson]()
if let path = Bundle.main.path(forResource: "data", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
let jsonResult = try JSONSerialization.jsonObject(with: data, options: [])
totalLessonArray = Mapper<Lesson>().mapArray(JSONArray: jsonResult as! [[String : Any]])
} catch {
// handle error
}
}
When i print totalLessonArray i get following error in my 4 level deep list
//...
quizScore = 0;
quizQuestions = List<Question> <0x6000022e4d80> (
[0] Question {
question = What are the synonym of sympathy? Drag and drop in the box below.;
answer = <Maximum depth exceeded>;
}
);
//...
This is the model file
class Lesson : Object, Mappable{
#objc dynamic var lessonNumber : Int = 0
#objc dynamic var totalChapter : Int = 0
#objc dynamic var completedChapter : Int = 0
#objc dynamic var quizTaken : Bool = false
#objc dynamic var isFinished : Bool = false
#objc dynamic var isCurrentlyWatchingLesson : Bool = false
#objc dynamic var currentlyWatchingChapter : Int = 0
#objc dynamic var lessonTitle : String = ""
#objc dynamic var quizScore : Int = 0
var chapter = List<Chapter>()
var quizQuestions = List<Question>()
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
lessonNumber <- map["lessonNumber"]
totalChapter <- map["totalChapter"]
completedChapter <- map["completedChapter"]
quizTaken <- map["quizTaken"]
isFinished <- map["isFinished"]
isCurrentlyWatchingLesson <- map["isCurrentlyWatchingLesson"]
currentlyWatchingChapter <- map["currentlyWatchingChapter"]
lessonTitle <- map["lessonTitle"]
quizScore <- map["quizScore"]
chapter <- (map["chapter"], ListTransform<Chapter>())
quizQuestions <- (map["quizQuestions"], ListTransform<Question>())
}
}
I have json which is 4-5 level down deep. Am i doing something wrong?

how I can call variable in mappable class from main view

I have three class one is the main view and here is the code of it
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
getJSONS().getLowerJSON()
setupUI()
}
func setupUI(){
print(lowerData.companyName)
}
}
getsJSONS call an function that call almofire to handle json as in this code
class getJSONS {
func getLowerJSON(){
let URL = "http://tickerchart.com/interview/company-details.json"
Alamofire.request(URL).responseObject { (response: DataResponse<lowereViewResponse>) in
let lowereViewResponse = response.result.value
}
}
}
lowereViewResponse call the mappable class as in this code
class lowereViewResponse: NSObject, Mappable {
var companyName : String?
var symbol : String?
var tradesCount : Int?
var high : Int?
var low : Int?
var volume : Int?
var amount : Int?
override init() {
super.init()
}
convenience required init?(_ map: Map) {
self.init()
}
func mapping(map: Map) {
companyName <- map["company-name"]
symbol <- map["symbol"]
tradesCount <- map["trades-count"]
high <- map["high"]
low <- map["low"]
volume <- map["volume"]
}
required init?(map: Map){
companyName = ""
symbol = ""
tradesCount = 0
high = 0
low = 0
volume = 0
amount = 0
price = 0
}
}
all imports are done
I am trying to call class lowereViewResponse to access the variable but I get null when this is exited "print(lowerData.companyName)"
You can use like following:
let lowereViewObject: lowereViewResponse = Mapper< lowereViewResponse >().map(JSON: result)

Convert json response to Realm object

hi I am trying to convert json response from a web service to realm object and insert it into realm database using object mapper but returns objects with null values
Alamofire.request(url).responseJSON { response in
let products = Mapper<ProRealm>().map(JSONObject:response.result.value)
print("products",products) // object with null values
}
My ProRealm class is
class ProRealm: Object, Mappable {
dynamic var additives_count: String?
var rating: String?
var updated: Bool = false;
var name: String?
var barcode: String?
var product_key: String?
var hazard_count: String?
var state: String?
var no_of_users_rated: String?
var thumbnail: String?
var overall_rating : String?
var is_food_or_beverage : Bool = false
//Impl. of Mappable protocol
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
additives_count <- map["additives_count"]
rating <- map["rating"]
updated <- map["updated"]
name <- map["name"]
barcode <- map["barcode"]
product_key <- map["product_key"]
hazard_count <- map["hazard_count"]
state <- map["state"]
no_of_users_rated <- map["no_of_users_rated"]
thumbnail <- map["thumbnail"]
overall_rating <- map["overall_rating"]
is_food_or_beverage <- map["is_food_or_beverage"]
}
}
In Json response, the products are inside the products key. please advice how to convert it into realm object
["kind": aWareInternalAPI#productsItem, "products":(
{
"additives_count" = 3;
barcode = 12345;
"hazard_count" = 0;
"is_food_or_beverage" = 1;
name = "Water Bottle";
"no_of_users_rated" = 1;
"overall_rating" = "0.0";
"product_key" = "ahdzfmF3YXJlLWJhY2tlbmQtc3RhZ2luZ3IlCxIEVXNlchiAgICgvoOTCQwLEgdQcm9kdWN0GICAgIDE_JAKDA";
rating = 0;
state = "OCR_PROCESSING";
thumbnail = "http://lh3.googleusercontent.com/dMxwgSQB02osZJJex4S57iupaMT9tjDYZaD7mweJUjYmI1KNEcZZe1syBwrRs1GbYdZNrRUtQwRYUwXiAEscGNYH-J9f3gJOXYO1rQ=s150";
updated = 1;
},
{
"additives_count" = 0;
barcode = 53647825898248485;
"hazard_count" = 0;
"is_food_or_beverage" = 1;
"no_of_users_rated" = 0;
"overall_rating" = "0.0";
"product_key" = ahdzfmF3YXJlLWJhY2tlbmQtc3RhZ2luZ3IlCxIEVXNlchiAgICgvoOTCQwLEgdQcm9kdWN0GICAgICumYAJDA;
rating = "";
state = "OCR_PROCESSING";
thumbnail = "http://lh3.googleusercontent.com/0D55ZXkG8Ua5ULDK69Po-IHeDPIfXZHOi7LlLURoc1qZzmNst57xUMQSPzWTW5miSDglc5wKDA4QlvLvnD6aMOqIHcwlj_HY-Hs=s150";
updated = 1;
})]
Please advice
All your properties except updated and is_food_or_beverage are nilable, so there is a possibility that they might be nil. If you know that they should have a value and you´re still getting nil make sure to check the keys in your mapping function.
Update:
I´m not 100% sure of how this works with the syntax with Mappable, but you need to do it something like this:
class Products: Object, Mappable {
let products: [Product]?
//Impl. of Mappable protocol
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
products <- map["products"]
}
}
class Product: Object, Mappable {
dynamic var additives_count: String?
var rating: String?
var updated: Bool = false;
var name: String?
var barcode: String?
var product_key: String?
var hazard_count: String?
var state: String?
var no_of_users_rated: String?
var thumbnail: String?
var overall_rating : String?
var is_food_or_beverage : Bool = false
//Impl. of Mappable protocol
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
additives_count <- map["additives_count"]
rating <- map["rating"]
updated <- map["updated"]
name <- map["name"]
barcode <- map["barcode"]
product_key <- map["product_key"]
hazard_count <- map["hazard_count"]
state <- map["state"]
no_of_users_rated <- map["no_of_users_rated"]
thumbnail <- map["thumbnail"]
overall_rating <- map["overall_rating"]
is_food_or_beverage <- map["is_food_or_beverage"]
}
}
So create two different classes one that holds an array of products and the other is a Product.

Swift - Mapping of Nested Objects (Objectmapper)

I am developing an application with Swift 4. Where I make a call to the APIRest with Alamofire and I want to map the JSON response with Objectmapper. Well, the JSON that calls me back is the following:
The code to the APIRest is:
func retrievePostListData() {
Alamofire
.request("http://www.speedrun.com/api/v1/games", method: .get)
.validate()
.responseArray(completionHandler: { (response:
DataResponse<[PostModelSpeedRunModel]>) in
switch response.result {
case .success(let posts):
self.remoteRequestHandler?.onPostsRetrievedData(posts)
case .failure( _):
self.remoteRequestHandler?.onError()
}
})
}
The problem is that I do not know how to access each of the values (func mapping). Because there are some nested values. In addition to that some of the annunciations are objects and others are array. My erroneous code is the following:
import Foundation
import ObjectMapper
struct PostModelSpeedRunModel {
var id = ""
var international = ""
var abbreviation = ""
var links = [Links]??? // I need to get "rel" and "uri" of "runs"
var uri = ""
}
extension PostModelSpeedRunModel: Mappable {
init?(map: Map) {
}
mutating func mapping(map: Map) {
id <- map["data.id"]
international <- map["data.international"]
abbreviation <- map["data.abbreviation"]
link <- map["data.Links"]
uri <- map["data.logo"]
}
}
Can you help me do / understand doing the function mapping? Thanks
I'm assuming you are getting no values at all, because I tried your code and got nothing. If you only need the contents of the data array from the json and, as you have it now, ObjectMapper is expecting a json with just an array of PostModelSpeedRunModels. Therefore, you need to add a keyPath to tell AlamofireObjectMapper it's starting point:
Alamofire.request("http://www.speedrun.com/api/v1/games", method: .get)
.responseArray(keyPath: "data") { (response: DataResponse<[PostModelSpeedRunModel]>) in
...
}
If you also need the info from the pagination node, then you'll need to create a new object that has a data and pagination properties, and change responseArray(keyPath: ...) to simply responseObject using the new root object in DataResponse.
Then I believe you only want runs's uri, so I recommend just having a String in your model for storing it, instead of an array of Links. Assuming that the array of links is unsorted and may change order in the future (if not you can access directly like map["links.1.uri"] and you are done), all links need to be parsed and then filtered. It can be done as follows:
struct PostModelSpeedRunModel {
var id = ""
var international = ""
var abbreviation = ""
var runsLink = ""
var uri = ""
}
extension PostModelSpeedRunModel: Mappable {
init?(map: Map) {
}
mutating func mapping(map: Map) {
id <- map["id"]
international <- map["international"]
abbreviation <- map["abbreviation"]
uri <- map["logo"]
var links: [Link]?
links <- map["links"]
if let uri = links?.first(where: {$0.rel == "runs"})?.uri {
runsLink = uri
}
}
}
struct Link {
var rel = ""
var uri = ""
}
extension Link: Mappable {
init?(map: Map) {
}
mutating func mapping(map: Map) {
rel <- map["rel"]
uri <- map["uri"]
}
}

Is Swift incapable of specializing generic types with generic types?

I am working with a JSON API (Reddit) using Alamofire + AlamofireObjectMapper, as well as ObjectMapper. I am working on creating the model and have a proposed solution which is not working as expected. I'll do my best to explain...
The JSON response I am receiving has a base class Thing as follows
class Thing<T where T:Mappable>: Mappable {
var id: String?
var name: String?
var kind: String?
var data: T?
required init?(_ map: Map) {}
func mapping(map: Map) {
id <- map["id"]
name <- map["name"]
kind <- map["kind"]
data <- map["data"]
}
}
The data property varies depending on the type of request I am making. In this particular case, it should be of type Listing as follows.
class Listing<T where T: Mappable>: Mappable {
var before: String?
var after: String?
var modhash: String?
var children: [Thing<T>]?
required init?(_ map: Map) {}
func mapping(map: Map) {
before <- map["before"]
after <- map["after"]
modhash <- map["modhash"]
children <- map["children"]
}
}
The children property is an array of Thing objects, however with data as a different type, in this case Post
class Post: Mappable {
var author: String?
var creationDate: Int?
var name: String?
var numberOfComments: Int?
var score: Int?
var text: String?
var stickied: Int?
var subreddit: String?
var title: String?
var url: String?
var preview: Images?
required init?(_ map: Map) {}
func mapping(map: Map) {
author <- map["author"]
creationDate <- map["created_utc"]
name <- map["name"]
numberOfComments <- map["num_comments"]
score <- map["score"]
text <- map["selftext"]
stickied <- map["stickied"]
subreddit <- map["subreddit"]
title <- map["title"]
url <- map["url"]
preview <- map["preview"]
}
}
Using the AlamofireObjectMapper extension, to parse the JSON response to my custom model, we use
public func responseObject<T : Mappable>(keyPath: String? = default, completionHandler: Alamofire.Response<T, NSError> -> Void) -> Self
where the it is expecting a generic type. I will use a type defined as Response<Thing<Listing<Post>>, NSError> as follows...
RedditAPIService.requestSubReddit(subReddit: nil, withSortType: .Hot, afterPost: nil, postsAlreadyShown: nil).responseObject { (response: Response<Thing<Listing<Post>>, NSError>) -> Void in
if let value = response.result.value {
print(value.kind)
}
}
The problem I am seeing is corrupt data on the Listing object.
I am expecting only 1 value in the children object, not > 900 Zillion!
I know I can subclass Thing and Listing to use specific types, but I also am curious why this does not work as expected. Any ideas? Thanks!