Сериализация объектов с помощью Alamofire и токена носителя

Используя отличную библиотеку Alamofire v1.2, в проекте IOS 8, Xcode 6.3 и swift, я пытаюсь сериализовать объекты из ответа JSON-API, и я хотел бы знать, как лучше всего это сделать. Я думаю, что основные проблемы в приведенном ниже коде:

  • в контроллере класса println(data) показывает ноль.
  • в классе клубных объектов свойство местоположения отображается неправильно.

Ответ JSON-API:

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"
  }
}]

Общая сериализация коллекции ответов:

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

класс клубных объектов

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

класс контроллера представления

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    }

}

вывод println(clubs):

   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

person Edu    schedule 19.04.2015    source источник


Ответы (1)


Просто чтобы убедиться, не могли бы вы изменить свои последние строки в классе ViewController, чтобы они были следующими?

request.responseJSON { request, response, json, error in
    println(request)
    println(response)
    println(json)
    println(error)
}

Я хочу убедиться, что вы правильно настроили свой запрос и получаете ожидаемый ответ. Это, безусловно, полдела. Как только вы сможете это проверить, мы сможем работать над логикой разбора responseCollection.

Кроме того, какую версию Xcode вы используете и какую версию Alamofire?


ОБНОВИТЬ

Проблема, с которой вы столкнулись, двояка.

Выпуск 1

Во-первых, вы неправильно вызываете свой метод responseCollection. Вместо этого вы должны называть его следующим образом:

request.responseCollection { request, response, clubs: [Club], error in
    println("request = \(request)")
    println("response = \(response)")
    println("clubs = \(clubs)")
    println("error = \(error)")
}

Затем это правильно вызовет ваш класс Club.

Выпуск 2

Вторая проблема заключается в том, что вы не реализуете метод collection внутри вашего объекта Club. Вы никогда не получите какие-либо клубы без фактического повторения коллекции. Что-то примерно похожее на следующее должно заставить вас двигаться в правильном направлении.

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

Как только ваша функция collection фактически перебирает все значения представления в массиве JSON, вам должно повезти.

Бонусные очки

Чтобы получить бонусные баллы, вот еще несколько советов по улучшению вашего кода.

  • Переключитесь на использование неудачного инициализатора в вашем классе Club, чтобы гарантировать, что объекты будут созданы только в том случае, если JSON успешно проанализирован.
  • Измените свою реализацию внутри закрытия завершения responseCollection, чтобы фактически сохранить новое значение клубов и отобразить эти клубы в представлении таблицы.

Объект, возвращенный замыканию responseCollection, больше не является AnyObject JSON, который можно использовать с SwiftyJSON, а представляет собой массив Clubs.

person cnoon    schedule 20.04.2015
comment
Я только что отредактировал класс контроллера представления, добавив выходные данные println, и я включил вывод. Версия Xcode — 6.3, а Alamofire — 1.2. - person Edu; 20.04.2015
comment
Я обновил свой ответ, чтобы отразить проблемы, которые, как мне кажется, у вас возникают. Код может быть неточным, так как у меня недостаточно вашего примера кода, чтобы его скомпилировать, но это должно быть довольно близко. - person cnoon; 21.04.2015
comment
Что касается второй проблемы, я реализовал функцию в классе объектов, как вы мне сказали, и добавил вложенное свойство местоположения (я не знаю, реализовано ли это нормально). Что касается 1-й проблемы, я обновил request.responseCollection(..,..,[Club]?,..) в классе контроллера представления и вывод println(clubs), но, к сожалению, ответ равен нулю - person Edu; 22.04.2015
comment
но если я изменю запрос на request.responseJSON {запрос, ответ, json, ошибка, ответ не равен нулю, но я не могу назначить json (хиты) классу клуба - person Edu; 22.04.2015
comment
Извините, @EdZ, но помочь вам здесь слишком сложно. Вы используете очень продвинутые концепции Alamofire, но не понимаете основ Alamofire. Я бы сначала попытался использовать SwiftyJSON для анализа ответа с использованием метода сериализации responseJSON. Как только вы заработаете правильно и приобретете некоторый опыт работы с Alamofire, вернитесь к этому подходу, и, надеюсь, все будет иметь больше смысла. Я не могу не подчеркнуть, что README в Alamofire потрясающий и действительно очень лаконично разбивает все на части. - person cnoon; 23.04.2015
comment
Большое спасибо за ваше время @cnoon, я собираюсь попробовать другой способ добиться этого, и я дополню эту тему правильным решением для вас и для всех. - person Edu; 23.04.2015