Using Alamofire and Objectmapper the integer value always zero - swift

I am using Alamofire with ObjectMapper and my model class is like that
class Category: Object, Mappable {
dynamic var id: Int = 0
dynamic var name = ""
dynamic var thumbnail = ""
var children = List<Category>()
override static func primaryKey() -> String? {
return "id"
}
required convenience init?(_ map: Map) {
self.init()
}
func mapping(map: Map) {
id <- map["id"]
name <- map["name"]
thumbnail <- map["thumbnail"]
children <- map["children"]
}
}
and I am using Alamofire like that
Alamofire.request(.GET, url).responseArray { (response: Response<[Category], NSError>) in
let categories = response.result.value
if let categories = categories {
for category in categories {
print(category.id)
print(category.name)
}
}
}
the id is always zero, I don't know why?

I fixed it by adding transformation in the mapping function in model class like that
id <- (map["id"], TransformOf<Int, String>(fromJSON: { Int($0!) }, toJSON: { $0.map { String($0) } }))
thanks to #BobWakefield

Does the "id" field exist in the JSON file? If it does not, your initial value of zero will remain. Is the value in quotes in the JSON file? If it is, then it's a string. I don't know if ObjectMapper will convert it to Int.
Moved my comment to an answer.

Related

ObjectMapper how can I change datatype while Mapping in Swift

I am using this ObjectMapper library to map JSON to Core Data object and Vice versa.
But the problem here is I cant type cast objects.
here
import ObjectMapper
class network: NSManagedObject, Mappable {
#NSManaged var localId: NSNumber?
#NSManaged var version: String?
#NSManaged var port: String?
required public init?(map: Map) {
let ctx = DbHelper .getContext()
let entity = NSEntityDescription.entity(forEntityName: "network", in: ctx)
super.init(entity: entity!, insertInto: ctx)
mapping(map: map)
}
public func mapping(map: Map) {
localId <- map["localId"]
port <- map["port"] // Returns Int but I want to save it as String
version <- map["version"] // Returns Int but I want to save it as String
}
}
Here how to save version and port as String when I get value from JSON as Int.
Hope I am clear in explaining my question, if further clarification required please let me know.
Thank You
In a lot of situations you can use the built-in transform class TransformOf to quickly perform a desired transformation. TransformOf is initialized with two types and two closures. The types define what the transform is converting to and from and the closures perform the actual transformation.
For example, if you want to transform a JSON String value to an Int you could use TransformOf as follows:
let transform = TransformOf<Int, String>(fromJSON: { (value: String?) -> Int? in
// transform value from String? to Int?
return Int(value!)
}, toJSON: { (value: Int?) -> String? in
// transform value from Int? to String?
if let value = value {
return String(value)
}
return nil
})
id <- (map["port"], transform)
Here is a more condensed version of the above:
id <- (map["port"], TransformOf<Int, String>(fromJSON: { Int($0!) }, toJSON: { $0.map { String($0) } }))
I hope you can find solution from this.
Solution get from: https://github.com/Hearst-DD/ObjectMapper

Using Object Mapping with Kinvey

I have an array of objects I'm trying to get out of one of my collections. I've followed along using their docs and also some Googling and I believe I'm close to the solution, however not close enough. Here's what I have:
class Clothing: Entity {
var categories: [Category]!
var gender: String!
override class func collectionName() -> String {
//return the name of the backend collection corresponding to this entity
return "categories"
}
override func propertyMapping(_ map: Map) {
super.propertyMapping(map)
categories <- map["clothing"]
gender <- map["gender"]
}
}
class Category: NSObject, Mappable{
var title: String?
var image: String?
convenience required init?(map: Map) {
self.init()
}
func mapping(map: Map) {
title <- map["category"]
image <- map["image"]
}
}
I'm able to get the right gender, but the array of categories doesn't seem to get mapped to the Category object. Any thoughts?
your model actually have one issue, as you can see at https://devcenter.kinvey.com/ios/guides/datastore#Model you should use let categories = List<Category>() instead of var categories: [Category]!. Here's the model that and test and worked:
import Kinvey
class Clothing: Entity {
let categories = List<Category>()
var gender: String!
override class func collectionName() -> String {
//return the name of the backend collection corresponding to this entity
return "clothing"
}
override func propertyMapping(_ map: Map) {
super.propertyMapping(map)
categories <- ("categories", map["categories"])
gender <- ("gender", map["gender"])
}
}
class Category: Object, Mappable{
var title: String?
var image: String?
convenience required init?(map: Map) {
self.init()
}
func mapping(map: Map) {
title <- ("category", map["category"])
image <- ("image", map["image"])
}
}
and here's a sample code how to save a new Clothing object
let casualCategory = Category()
casualCategory.title = "Casual"
let shirtCategory = Category()
shirtCategory.title = "Shirt"
let clothing = Clothing()
clothing.gender = "male"
clothing.categories.append(shirtCategory)
clothing.categories.append(casualCategory)
dataStore.save(clothing) { (result: Result<Clothing, Swift.Error>) in
switch result {
case .success(let clothing):
print(clothing)
case .failure(let error):
print(error)
}
}

how to filter data from object mapper class

i want to implement search functionality in my app but i get data from services. i have an array like this in object mapper
class Country : Mappable {
var countryName:String = ""
var countryID:Int = 0
var countryImage:String = ""
var countryColor:String = ""
required init?(_ map: Map) {
}
func mapping(map: Map) {
countryID <- map["id"]
countryName <- map["name"]
countryColor <- map["color"]
countryImage <- map["image"]
}
}
from here i want to filter my data for search functionality how to do this.
here i am filtering only country names but i want to filter whole array how i can do that
func updateSearchResultsForSearchController(searchController: UISearchController) {
self.filteredData = self.countryNames.filter { (country:String) -> Bool in
if country.lowercaseString.containsString(self.searchController.searchBar.text!.lowercaseString) {
return true
} else {
return false
}
}
print(filteredData)
// update results table view
self.resultController.tableView.reloadData()
}
You can filter your array like this way.
let filter = countries.filter { $0.countryName.lowercaseString.containsString(self.searchCon‌​troller.searchBar.te‌​xt!.lowercaseString) }
self.resultController.tableView.reloadData()

RealmSwift + ObjectMapper managing String Array (tags)

What I need to represent in RealmSwift is the following JSON scheme:
{
"id": 1234,
"title": "some value",
"tags": [ "red", "blue", "green" ]
}
Its a basic string array that I'm stumbling on. I'm guessing in Realm I need to represent "tags" as
dynamic id: Int = 0
dynamic title: String = ""
let tags = List<MyTagObject>()
making tags its own table in Realm, but how to map it with ObjectMapper? This is how far I got...
func mapping(map: Map) {
id <- map["id"]
title <- map["title"]
tags <- map["tags"]
}
... but the tags line doesn't compile of course because of the List and Realm cannot use a [String] type.
This feels like a somewhat common problem and I'm hoping someone who has faced this can comment or point to a post with a suggestion.
UPDATE 1
The MyTagObject looks like the following:
class MyTagObject: Object {
dynamic var name: String = ""
}
UPDATE 2
I found this post which deals with the realm object but assumes the array has named elements rather than a simple string.
https://gist.github.com/Jerrot/fe233a94c5427a4ec29b
My solution is to use an ObjectMapper TransformType as a custom method to map the JSON to a Realm List<String> type. No need for 2 Realm models.
Going with your example JSON:
{
"id": 1234,
"title": "some value",
"tags": [ "red", "blue", "green" ]
}
First, create an ObjectMapper TransformType object:
import Foundation
import ObjectMapper
import RealmSwift
public struct StringArrayTransform: TransformType {
public init() { }
public typealias Object = List<String>
public typealias JSON = [String]
public func transformFromJSON(_ value: Any?) -> List<String>? {
guard let value = value else {
return nil
}
let objects = value as! [String]
let list = List<String>()
list.append(objectsIn: objects)
return list
}
public func transformToJSON(_ value: Object?) -> JSON? {
return value?.toArray()
}
}
Create your 1 Realm model used to store the JSON data:
import Foundation
import RealmSwift
import ObjectMapper
class MyObjectModel: Object, Mappable {
#objc dynamic id: Int = 0
#objc dynamic title: String = ""
let tags = List<MyTagObject>()
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
id <- map["id"]
title <- map["title"]
tags <- (map["tags"], StringArrayTransform())
}
}
Done!
This line is the magic: tags <- (map["tags"], StringArrayTransform()). This tells ObjectMapper to use our custom StringArrayTransform I showed above which takes the JSON String array and transforms it into a Realm List<String>.
First of all we should assume that our model extends both Object and Mappable.
Let's create a wrapper model to store the primitive (String) type:
class StringObject: Object {
dynamic var value = ""
}
Then we describe corresponding properties and mapping rules for the root model (not the wrapper one):
var tags = List<StringObject>()
var parsedTags: [String] {
var result = [String]()
for tag in tags {
result.append(tag.value)
}
return result
}
override static func ignoredProperties() -> [String] {
return ["parsedTags"]
}
func mapping(map: Map) {
if let unwrappedTags = map.JSON["tags"] as? [String] {
for tag in unwrappedTags {
let tagObject = StringObject()
tagObject.value = tag
tags.append(tagObject)
}
}
}
We need a tags property to store and obtain the data about tags from Realm.
Then a parsedTags property simplifies extraction of tags in the usual array format.
An ignoredProperties definition allows to avoid some failures with Realm while data savings (because of course we can't store non-Realm datatypes in the Realm).
And at last we are manually parsing our tags in the mapping function to store it in the Realm.
It will work if your tags array will contains a Dictionary objects with a key: "name"
{
"id": 1234,
"title": "some value",
"tags": [ ["name" : "red"], ... ]
}
If you cannot modify JSON object, I recommend you to map json to realm programmatically.
for tagName in tags {
let tagObject = MyTagObject()
tagObject.name = tagName
myObject.tags.append(tagObject)
}
Follow this code
import ObjectMapper
import RealmSwift
//Create a Model Class
class RSRestaurants:Object, Mappable {
#objc dynamic var status: String?
var data = List<RSData>()
required convenience init?(map: Map) {
self.init()
}
func mapping(map: Map) {
status <- map["status"]
data <- (map["data"], ListTransform<RSData>())
}
}
//Use this for creating Array
class ListTransform<T:RealmSwift.Object> : TransformType where T:Mappable {
typealias Object = List<T>
typealias JSON = [AnyObject]
let mapper = Mapper<T>()
func transformFromJSON(_ value: Any?) -> Object? {
let results = List<T>()
if let objects = mapper.mapArray(JSONObject: value) {
for object in objects {
results.append(object)
}
}
return results
}
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
}
}

ObjectMapper not serialising new fields

I have an class:
class ChatMessage: Object, Mappable {
dynamic var fromId = ""
dynamic var toId = ""
dynamic var message = ""
dynamic var fromName = ""
dynamic var created: Int64 = 0
required convenience init?(map: Map) {
self.init()
}
func configure(_ fromId:String,toId:String, message:String) {
self.fromId=fromId
self.toId=toId
self.message=message
self.created = Int64((NSDate().timeIntervalSince1970 * 1000.0))
}
func mapping(map: Map) {
created <- map["created"] //a: this was added later
fromId <- map["fromId"]
toId <- map["toId"]
message <- map["message"]
fromName <- map["fromName"]
}
}
I am using ObjectMapper to serialise the object to JSON and Realm to store it in the local database.
I had added the created field later to the mapping when the Realm db was already storing the ChatMessage object.
Now when I am instantiating the ChatMessage object and trying to convert it into JSON object using ObjectMapper. Following is the code:
func sendChatMessage(_ chatMessage:ChatMessage, callback: #escaping DataSentCallback) -> Void {
var chatMessageString:String!
let realm = DBManager.sharedInstance.myDB
try! realm?.write {
chatMessageString = Mapper().toJSONString(chatMessage, prettyPrint: false)!
}
...
}
Now when I print chatMessage, I get:
ChatMessage {
fromId = 14;
toId = 20;
message = 2;
fromName = ;
created = 1477047392597;
}
And when I print chatMessageString, I get:
"{\"toId\":\"20\",\"message\":\"2\",\"fromName\":\"\",\"fromId\":\"14\"}"
How come does the created field not appear in the string?
The problem was in the mapping of Int64 type as mentioned in this issue on github.
By changing the mapping of created to the following form, everything worked fine:
created <- (map["created"], TransformOf<Int64, NSNumber>(fromJSON: { $0?.int64Value }, toJSON: { $0.map { NSNumber(value: $0) } }))