Search code examples
jsonalamofireflickrobjectmapper

Swift - How to use ObjectMapper to read Flickr JSON


I'm wanting to consume the JSON from the publicly available feed from Flickr using the URL

https://api.flickr.com/services/feeds/photos_public.gne?nojsoncallback=1&format=json

I am attempting to use Alamofire to consume the JSON and then use ObjectMapper to map the JSON to a basic photo data model.

The problem I am having is that I am unable to get it to parse the JSON and then map it.

objects = Mapper<Photo>().mapArray(JSONArray: json)!

Returns:

Cannot convert value of type 'JSON' to expected argument type '[[String : Any]]'

My code is as follows;

 FlickrAPI.executeRequestURL(url, parameters: parameters) { (success, error, response) in
            if (success) {
                //print ("Response: \(response)")

                if let jsonObject = response?.result.value {
                    let json = JSON(jsonObject)

                    objects = Mapper<Photo>().mapArray(JSONArray: json)!

                }

            }
            else {
                print ("** Error -- \(error?.localizedDescription) **")
                taskCallback(false, error)
            }
        }
// Execute URL request code:
 static func executeRequestURL(_ requestURL: URL, parameters:[String: String], taskCallback: @escaping (Bool, Error?, DataResponse<Any>?) -> ())
    {
        print ("Attempting URL -- \(requestURL)")

        Alamofire.request(requestURL, parameters:["nojsoncallback": "1", "format": "json"])
            .validate(statusCode: 200..<300)
            .validate(contentType: ["application/json"])
            .responseJSON { response in

                switch(response.result) {
                case .success(_):
                    taskCallback(true, nil, response)
                    break

                case .failure(_):
                    print("Error while fetching url: \(response.result.error)")
                    taskCallback(false, response.result.error, nil)
                    break
                }
        }
    }

// Photo model

final class Photo: Object, Mappable {
    private(set) dynamic var uuid: String = UUID().uuidString

    dynamic var name: String = ""
    dynamic var author_id: String = ""
    dynamic var title: String = ""

override public static func primaryKey() -> String? {
        return "uuid"
    }

    required convenience public init?(map: Map) {
        self.init()
    }

    // MARK: - ObjectMapper protocol methods

    public func mapping(map: Map) {
        self.name <- map["name"]
        self.author_id <- map["author_id"]
        self.title <- map["title"]
    }

When reviewing the photos public URL I notice that the response has the photo objects collated together in an array called "items":

I read that I can use AlamofireObjectMapper to help; but I am confused as to how to actually use it.

Anyway, my query is specifically how do I consume the items array within the Flickr Public photos feed using ObjectMapper?

Many thanks


Solution

  • I was able to solve this using AlamofireObjectMapper

    Firstly, create a class to hold the response coming back. Secondly make sure this class conforms to the mapping;

    // Required to help integrate Alamofire with Objectmapper (via AlamofireObjectMapper)
    final class PhotoResponse: Mappable {
    
        var items: [Photo]?
    
        required init?(map: Map){
    
        }
    
        func mapping(map: Map) {
            items <- map["items"]
        }
    }
    

    Then when I call the execute URL I change it slightly so it conforms to this class;

    static func executeRequestURL(_ requestURL: URL, parameters:[String: String], taskCallback: @escaping (Bool, Error?, DataResponse<PhotoResponse>?) -> ())
        {
            print ("Attempting URL -- \(requestURL)\n")
    
            Alamofire.request(requestURL, parameters:["nojsoncallback": "1", "format": "json"])
                .validate(statusCode: 200..<300)
                .validate(contentType: ["application/json"])
                .responseObject { (response: DataResponse<PhotoResponse>) in
    
                    switch(response.result) {
                    case .success(_):
                        taskCallback(true, nil, response)
                        break
    
                    case .failure(_):
                        print("** Error while fetching url: \(response.result.error) **")
                        taskCallback(false, response.result.error, nil)
                        break
                    }
            }
        }
    

    The key part here is the response: DataResponse<PhotoResponse>) in line.

    Finally, when we call it via my fetchPublicPhotos function I can query the result coming back

    let photoResponse = response?.result.value
    
    if let items = photoResponse?.items {
        print ("Items found: #\(items.count)")
       // do something with items here
    }