Reputation: 470
Using the excellent Alamofire library v1.2, in a IOS 8, Xcode 6.3 and swift project, I'm trying to serialize objects from JSON-API response and I would like to know which is the best way to accomplish it. I think the main issues in the code below are:
The JSON-API response is:
hits = [{
"_id" : "5470def9e0c0be27780121d7",
"imageUrl" : "https:\/\/s3-eu-west-1.amazonaws.com\/api-static\/clubs\/5470def9e0c0be27780121d7_180.png",
"name" : "Mondo",
"hasVip" : false,
"location" : {
"city" : "Madrid"
}
}, {
"_id" : "540b2ff281b30f3504a1c72f",
"imageUrl" : "https:\/\/s3-eu-west-1.amazonaws.com\/api-static\/clubs\/540b2ff281b30f3504a1c72f_180.png",
"name" : "Teatro Kapital",
"hasVip" : false,
"location" : {
"address" : "Atocha, 125",
"city" : "Madrid"
}
}, {
"_id" : "540cd44581b30f3504a1c73b",
"imageUrl" : "https:\/\/s3-eu-west-1.amazonaws.com\/api-static\/clubs\/540cd44581b30f3504a1c73b_180.png",
"name" : "Charada",
"hasVip" : false,
"location" : {
"address" : "La Bola, 13",
"city" : "Madrid"
}
}]
Generic Response Collection Serialization:
@objc public protocol ResponseCollectionSerializable {
static func collection(#response: NSHTTPURLResponse, representation: AnyObject) -> [Self]
}
extension Alamofire.Request {
public func responseCollection<T: ResponseCollectionSerializable>(completionHandler: (NSURLRequest, NSHTTPURLResponse?, [T]?, NSError?) -> Void) -> Self {
let serializer: Serializer = { (request, response, data) in
let JSONSerializer = Request.JSONResponseSerializer(options: .AllowFragments)
let (JSON: AnyObject?, serializationError) = JSONSerializer(request, response, data)
if response != nil && JSON != nil {
return (T.collection(response: response!, representation: JSON!), nil)
} else {
return (nil, serializationError)
}
}
return response(serializer: serializer, completionHandler: { (request, response, object, error) in
completionHandler(request, response, object as? [T], error)
})
}
}
the Club Object Class
final class Club: ResponseCollectionSerializable {
@objc static func collection(#response: NSHTTPURLResponse, representation: AnyObject) -> [Club] {
var clubs = [Club]()
if let representation = representation as? [[String: AnyObject]] {
for representationValue in representation {
let club = Club(JSON: representationValue)
clubs.append(club)
}
}
return clubs
}
let id: String
let name: String
let imageUrl: String
let hasVip: Bool
let location: String
init(JSON: AnyObject) {
id = JSON.valueForKeyPath("id") as! String
name = JSON.valueForKeyPath("name") as! String
imageUrl = JSON.valueForKeyPath("imageUrl") as! String
hasVip = JSON.valueForKeyPath("hasVip") as! Bool
//is OK this implementation?
location = JSON.valueForKeyPath("location") as! String
}
}
the View Controller Class
class ClubsViewController: UIViewController, UITableViewDataSource{
var results: [JSON]? = []
var clubs: [Club]?
@IBOutlet var tableview:UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.loadClubsObjects()
}
func loadClubsObjects(){
var URL = NSURL(string: "https://api.com/v1/clubs")
var mutableURLRequest = NSMutableURLRequest(URL: URL!)
mutableURLRequest.setValue("Content-Type", forHTTPHeaderField: "application/x-www-form-urlencoded")
mutableURLRequest.HTTPMethod = "GET"
mutableURLRequest.setValue("Bearer R01.iNsG3xjv/r1LDkhkGOANPv53xqUFDkPM0en5LIDxx875fBjdUZLn1jtUlKVJqVjsNwDe1Oqu2WuzjpaYbiWWhw==", forHTTPHeaderField: "Authorization")
let manager = Alamofire.Manager.sharedInstance
let request = manager.request(mutableURLRequest)
request.responseCollection { (request, response, clubs: [Club]?, error) in
println("request = \(request)")
println("response = \(response)")
println("clubs = \(clubs)")
println("error = \(error)")
if (json != nil){
var jsonObj = JSON(json!)
if let data = jsonObj["hits"].arrayValue as [JSON]? {
self.results = data
self.tableview.reloadData()
}
}
}
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.results?.count ?? 0
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("clubsObjectCell") as! ClubsTableViewCell
cell.clubsObject = self.results?[indexPath.row]
return cell }
}
the println(clubs) output is:
request = <NSMutableURLRequest: 0x7fd553725870> { URL: https://api.com/v1/clubs }
response = Optional(<NSHTTPURLResponse: 0x7fd553439e20> { URL: https://api.com/v1/clubs } { status code: 200, headers {
"Access-Control-Allow-Headers" = "X-Requested-With, Accept, Origin, Referer, User-Agent, Content-Type, Authorization";
"Access-Control-Allow-Methods" = "GET,PUT,POST,DELETE,OPTIONS";
"Access-Control-Allow-Origin" = "*";
Connection = "keep-alive";
"Content-Encoding" = gzip;
"Content-Type" = "application/json; charset=utf-8";
Date = "Tue, 21 Apr 2015 20:18:07 GMT";
Etag = "W/\"sEDn5KBhpfpInjAtNsF4gQ==\"";
Server = "nginx/1.6.2";
"Transfer-Encoding" = Identity;
Vary = "Accept-Encoding";
"X-Powered-By" = Express;
} })
clubs = Optional([])
error = nil
Upvotes: 0
Views: 1139
Reputation: 16643
Just to make sure, could you change your last lines in the ViewController
class to be the following?
request.responseJSON { request, response, json, error in
println(request)
println(response)
println(json)
println(error)
}
I want to make sure that you have set up your request properly and are getting the response back that you expect. That is certainly half the battle. Once you can verify that, then we can work on the responseCollection
parsing logic.
Also, what version of Xcode are you using and what version of Alamofire?
The issue you are having is two-fold.
First, you are not calling your responseCollection
method correctly. You should instead call it as follows:
request.responseCollection { request, response, clubs: [Club], error in
println("request = \(request)")
println("response = \(response)")
println("clubs = \(clubs)")
println("error = \(error)")
}
This will then call into your Club
class properly.
The second issue is that you are not implementing the collection
method inside your Club
object. You're never going to get any clubs without actually iterating through the collection. Something roughly like the following should get you going in the right direction.
final class Club: ResponseCollectionSerializable {
@objc static func collection(#response: NSHTTPURLResponse, representation: AnyObject) -> [Club] {
var clubs = [Club]()
if let representation = representation as? [[String: AnyObject]] {
for representationValue in representation {
let club = Club(JSON: representationValue)
clubs.append(club)
}
}
return clubs
}
let id: String
let name: String
let imageUrl: String
let hasVip: Bool
init(JSON: AnyObject) {
id = JSON.valueForKeyPath("id") as! String
name = JSON.valueForKeyPath("name") as! String
imageUrl = JSON.valueForKeyPath("imageUrl") as! String
hasVip = JSON.valueForKeyPath("hasVip") as! Bool
}
}
Once your collection
function is actually iterating through all the representation values in the JSON array, you should have more luck.
For bonus points, here are a few other tips to improve your code.
Club
class to guarantee that objects are only created if the JSON is parsed successfullyresponseCollection
completion closure to actually store the new clubs value and display those clubs in your table view.The object returned to the
responseCollection
closure is no longer a JSON AnyObject that you can use with SwiftyJSON, but instead an array of Clubs.
Upvotes: 1