1
votes

I am currently trying to learn Rx programming. I found Moya intriguing and have been trying to implement a simple network request which then gets mapped to objects which I can then use to populate a tableView.

I have been following this tutorial: http://www.thedroidsonroids.com/blog/ios/rxswift-examples-3-networking/

I believe I am getting a successful response as I am using .debug and getting the following output:

2016-04-09 13:29:30.398: MyApi.swift:37 (findRepository) -> subscribed
2016-04-09 13:29:30.400: MyApi.swift:35 (findRepository) -> subscribed
2016-04-09 13:29:32.633: MyApi.swift:35 (findRepository) -> Event Next(Status Code: 20..., Data Length: 5747)
2016-04-09 13:29:32.633: MyApi.swift:35 (findRepository) -> Event Completed

Here is the code that I am using:

let provider: RxMoyaProvider<MyApi>
let repositoryName: Observable<String>

func trackIssues() -> Observable<[Train]> {
    return repositoryName
        .observeOn(MainScheduler.instance)
        .flatMapLatest { name -> Observable<[Train]?> in
            print("Name: \(name)")

            return self.findRepository(name)
        }.replaceNilWith([])
}

internal func findRepository(name: String) -> Observable<[Train]?> {
    print("help")
    return self.provider
        .request(MyApi.Trains(name, "c76a46ce2b3d8685982b/raw/10e86080c3b1beedd46db47f5bb188cc74ce5c78/sample.json"))
        .debug()
        .mapArrayOptional(Train.self)
        .debug()
}

And here is the object I am trying to map to:

import Mapper

struct Train: Mappable {

    let distance: String
    let eta: String

    init(map: Mapper) throws {
        try distance = map.from("distance")
        try eta = map.from("eta")
    }
}

I have looked at the network response and am wondering if I first need to abstract the "trains" data. I have tried this by mapping to the following object with out luck:

import Mapper

struct TrainsResponse: Mappable {

    let trains: String

    init(map: Mapper) throws {
        try trains = map.from("trains")
    }
}

Please find example json response here: http://pastebin.com/Wvx8d5Lg

So I was wondering if anyone can help me see why I am unable to turn the response into objects. Thanks.

=======

I have tried doing a pod update and it's still not working. Here is where I am binding it to the tableView:

func setupRx() {
    // First part of the puzzle, create our Provider
    provider = RxMoyaProvider<MyApi>()

    // Now we will setup our model
    myApi = MyApi(provider: provider, repositoryName: userName)

    // And bind issues to table view
    // Here is where the magic happens, with only one binding
    // we have filled up about 3 table view data source methods
    myApi
        .trackIssues()
        .bindTo(tableView.rx_itemsWithCellFactory) { (tableView, row, item) in
            let cell = tableView.dequeueReusableCellWithIdentifier("issueCell", forIndexPath: NSIndexPath(forRow: row, inSection: 0))
            cell.textLabel?.text = "Hello"

            print("Hello")

            return cell
        }
        .addDisposableTo(disposeBag)
}

The code inside bind to (where I set the cell) never gets called. Also if I put a break point inside my Train mapper class that also never gets called.

1
Can you try updating pods? If it doesn't help, the requests seems OK because you get the 200 response. How do you bind the trackIssues() to UI? - sunshinejr
Thanks for the response. I did a pod update but that didn't work. I have edited code to include where I am binding trackIssues() to UI. - pls
If the array is in "trains" key, try using .mapArrayOptional(Train.self, keyPath: "trains"), instead of .mapArrayOptional(Train.self). - sunshinejr
That works. Thanks a lot :-). - pls
No problem! I've made an answer to help other people that encounters the same problem as well. - sunshinejr

1 Answers

1
votes

The reason it doesn't work is that the JSON retrieved from the server returns dictionary, not an array. The array you wanted to parse is under the "trains" key in that dictionary. The Moya-ModelMapper used in the tutorial has methods for this usage as well. You just need to pass second argument, keyPath: in a mapArrayOptional() method.

So the answer for your question is replace:
.mapArrayOptional(Train.self)
with
.mapArrayOptional(Train.self, keyPath: "trains")