12

I have a response model that looks like this:

class ResponseModel: Mappable {

    var data: T?
    var code: Int = 0

    required init?(map: Map) {}

    func mapping(map: Map) {
        data <- map["data"]
        code <- map["code"]
    }
}

If the json-data is not an array it works:

{"code":0,"data":{"id":"2","name":"XXX"}}

but if it is an array, it does not work

{"code":0,"data":[{"id":"2","name":"XXX"},{"id":"3","name":"YYY"}]}

My mapping code;

let apiResponse = Mapper<ResponseModel>().map(JSONObject: response.result.value)

For details; I tried this code using this article : http://oramind.com/rest-client-in-swift-with-promises/

Ruben Steins
  • 2,782
  • 4
  • 27
  • 48
frontier
  • 123
  • 1
  • 1
  • 6

6 Answers6

15

you need to use mapArray method instead of map :

let apiResponse = Mapper<ResponseModel>().mapArray(JSONObject: response.result.value)
MohyG
  • 1,335
  • 13
  • 25
4

What I do is something like this:

func mapping(map: Map) {
    if let _ = try? map.value("data") as [Data] {
       dataArray <- map["data"]
    } else {
       data <- map["data"]
    }

    code <- map["code"]
}

where:

var data: T?
var dataArray: [T]?
var code: Int = 0

The problem with this is that you need to check both data and dataArray for nil values.

Abrahanfer
  • 346
  • 4
  • 5
3

You need to change your declaration of data to an array, since that's how it is in the JSON:

var data: [T]? 
Alex S
  • 572
  • 7
  • 15
  • but data sometimes object sometimes array. is there a way for both? – frontier Dec 11 '16 at 20:28
  • Create two separate mappable classes, one for an array and one without an array. Try mapping the first one and if it fails try the second one. – Alex S Dec 11 '16 at 20:46
  • 2
    Also, the API should really wrap the object in an array object even when there is only one element. – Alex S Dec 11 '16 at 20:47
1
let apiResponse = Mapper<ResponseModel>().mapArray(JSONObject: response.result.value)

works for me

Giorgos Myrianthous
  • 36,235
  • 20
  • 134
  • 156
ioSana
  • 117
  • 2
0

Anyone using SwiftyJSON and if you want an object from JSON directly without having a parent class, for example, you want the "data" from it. You can do something like this,

if let data = response.result.value {
   let json = JSON(data)
   let dataResponse = json["data"].object
   let responseObject = Mapper<DataClassName>().mapArray(JSONObject: dataResponse)
}

This will return you [DataClassName]? as response.

Sharukh Mastan
  • 1,491
  • 18
  • 17
0

Based on Abrahanfer's answer. I share my solution. I wrote a BaseResult for Alamofire.

class BaseResult<T: Mappable> : Mappable {

var Result : Bool = false
var Error : ErrorResult?
var Index : Int = 0
var Size : Int = 0
var Count : Int = 0
var Data : T?
var DataArray: [T]?

required init?(map: Map){

}

func mapping(map: Map) {
    Result  <- map["Result"]
    Error <- map["Error"]
    Index <- map["Index"]
    Size <- map["Size"]
    Count <- map["Count"]

    if let _ = try? map.value("Data") as [T] {
       DataArray <- map["Data"]
    } else {
       Data <- map["Data"]
    }
}}

The usage for Alamofire :

WebService.shared.request(url, params, encoding: URLEncoding.default, success: { (response : BaseResult<TypeData>) in

            if let arr = response.DataArray
            {
                for year in arr
                {
                   self.years.append(year)
                }
            }
        }, failure: {

        })

The request method is :

 func request<T: Mappable>(_ url: String,_ parameters: [String : Any] = [:], _ method: HTTPMethod = .post,_ httpHeaders: HTTPHeaders? = nil, encoding: ParameterEncoding = JSONEncoding.default, success: @escaping (T) -> Void, failure: @escaping () -> () ) {

    AF.request(newUrl, method:method, parameters:parameters, encoding:encoding, headers: httpHeaders)

    .responseJSON { response in

        if let res = response.value {
            let json = res as! [String: Any]
            if let object = Mapper<T>().map(JSON: json) {
                success(object)
                return
            }
        }else if let _ = response.error {
            failure()
        }
    }
}

And TypeData class is :

class TypeData : Mappable
{
var Id : String = ""
var Title: String =  ""

required init(map: Map){

}

func mapping(map: Map) {
    Id  <- map["ID"]
    Title  <- map["YEAR"]
}}
xevser
  • 369
  • 4
  • 5