Object Mapper making my values to optionals - swift

struct User: Mappable {
init?(map: Map) {
}
mutating func mapping(map: Map) {
token <- map["token"]
email <- map["email"]
}
var token : String!
var email : String!
}
I'm declaring my strings as conditionally wrapped so that I can use directly with out wrapping, but after mapping all my strings to access I need to wrap again?
Why do need to wrap again?

You don't need the wrapping if you check the values in the initializer, something like this will work.
struct User: Mappable {
var token: String
var email: String
init?(map: Map) {
guard let token: String = map["token"].value(),
let email: String = map["email"].value() else {
print("User should have token and email")
return nil
}
self.token = token
self.email = email
}
mutating func mapping(map: Map) {
token <- map["token"]
email <- map["email"]
}
}
Now you can use token and email in your code without wrapping

Related

Creating new instance of object implementing Mappable interface

I am using ObjectMapper library to convert my model objects (classes and structs) to and from JSON.
But sometimes I would like to create objects without JSON.
Supposse, I have class like this:
class User: Mappable {
var username: String?
var age: Int?
required init?(map: Map) {
}
func mapping(map: Map) {
username <- map["username"]
age <- map["age"]
}
}
I would like to create object without JSON, like this:
let newUser = User(username: "john", age: 18)
Is creating objects in this way possible for class implementing Mappable?
Add another init method with username and age as parameters.
class User: Mappable {
var username: String?
var age: Int?
init(username:String, age:Int) {
self.username = username
self.age = age
}
required init?(map: Map) {
}
func mapping(map: Map) {
username <- map["username"]
age <- map["age"]
}
}
And use it like this.
let user = User(username: "hello", age: 34)

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)
}
}

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) } }))

Generic function to map JSON objects with ObjectMapper

I have a generic function:
func toObjectMapper<T: Mappable>(mapper: T, success: (result: Mappable) -> Void, failure: (error: NSError) -> Void){
let alomofireApiRequest = AlamofireApiRequest(apiRequest: self)
Alamofire.request(alomofireApiRequest)
.responseObject { (response: Response<T, NSError>) in
guard let value = response.result.value else {
failure(error: response.result.error!)
return
}
success(result: value)
}
}
And I want to call it like this:
public func login(login: String, password: String) -> UserResponse {
let params = ["email":login, "password":password]
let request = ApiRequest(method: .POST, path: "login", parameters: params)
request.toObjectMapper(UserResponse.self, success: { result in
print(result)
}, failure: { error in
print(error.description)
})
}
But I always get this error:
Cannot invoke 'toObjectMapper' with an argument list of type '(UserResponse.Type, success: (result: Mappable) -> Void, failure: (error: NSError) -> Void)'
This is my userResponse:
import Foundation
import ObjectMapper
import RealmSwift
public class UserResponse: Object, Mappable {
dynamic var id = 0
dynamic var name = ""
dynamic var address = ""
dynamic var zipcode = ""
dynamic var city = ""
dynamic var country = ""
dynamic var vat = ""
dynamic var email = ""
dynamic var created_at = NSDate()
dynamic var updated_at = NSDate()
override public static func primaryKey() -> String? {
return "id"
}
//Impl. of Mappable protocol
required convenience public init?(_ map: Map) {
self.init()
}
public func mapping(map: Map) {
id <- map["id"]
name <- map["name"]
address <- map["address"]
zipcode <- map["zipcode"]
city <- map["city"]
country <- map["country"]
vat <- map["vat"]
email <- map["email"]
created_at <- map["created_at"]
updated_at <- map["updated_at"]
}
}
Any help ?
I think the problem is that you are trying to use UserResponse as an instantiated object but using UserResponse.self is only the class type.
A solution is to make UserResonse a singleton (or just instantiate an instance before passing it to 'toObjectMapper' as an argument)
I don't know if this code specifically will work but it's along these lines:-
public class UserResponse: Object, Mappable {
dynamic var id = 0
dynamic var name = ""
dynamic var address = ""
dynamic var zipcode = ""
dynamic var city = ""
dynamic var country = ""
dynamic var vat = ""
dynamic var email = ""
dynamic var created_at = NSDate()
dynamic var updated_at = NSDate()
static let shared = UserResponse() //singleton instantiation
override public static func primaryKey() -> String? {
return "id"
}
//Impl. of Mappable protocol
required convenience public init?(_ map: Map) {
self.init()
}
public func mapping(map: Map) {
id <- map["id"]
name <- map["name"]
address <- map["address"]
zipcode <- map["zipcode"]
city <- map["city"]
country <- map["country"]
vat <- map["vat"]
email <- map["email"]
created_at <- map["created_at"]
updated_at <- map["updated_at"]
}
}
and then in your function call
public func login(login: String, password: String) -> UserResponse {
let params = ["email":login, "password":password]
let request = ApiRequest(method: .POST, path: "login", parameters: params)
request.toObjectMapper(UserResponse.shared, success: { result in
print(result)
}, failure: { error in
print(error.description)
})
}

Using Alamofire and Objectmapper the integer value always zero

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.