Home > Enterprise >  Subsequent ordered HTTP calls
Subsequent ordered HTTP calls

Time:10-04

I'm building a simple iOS client for HackerNews. I'm using their APIs, according to which I'll be able to get the ordered post IDs (sorted by new, best and top) and a single post item passing the ID to the request. The problem I'm facing is the following: how can I, once I get the IDs array, make an HTTP call for every post in an ordered fashion? With the way I currently implemented it, I'm not having any luck. E.g. say the IDs array is [3001, 3002, 3003, 3004]. I tried calling the method to get those posts inside a for loop issuing dispatch groups and dispatch semaphores, but I still get them unordered, like the call for item 3003 completes before 3002, and so on.

The methods I'm using:

@Published var posts: [Post] = []
    
    func getPosts(feedType: FeedType){
        posts = []
        
        self.getFeedIDs(feedType: feedType).subscribe{ ids in
            let firstFifteen = ids[0...15]
            
            let dGroup = DispatchGroup()
            let dQueue = DispatchQueue(label: "network-queue")
            let dSemaphore = DispatchSemaphore(value: 0)
            
            dQueue.async {
                for id in firstFifteen{
                    dGroup.enter()
                    self.getPost(id: id).subscribe{ post in
                        self.posts.append(post)
                        dSemaphore.signal()
                        dGroup.leave()
                    }
                    dSemaphore.wait()
                }
            }
        }
    }
    
    func getFeedIDs(feedType: FeedType) -> Observable<[Int]> {
        return self.execute(url: URL(string: "https://hacker-news.firebaseio.com/v0/\(feedType)stories.json")!)
    }
    
    func getPost(id: Int) -> Observable<Post>{
        return self.execute(url: URL(string: "https://hacker-news.firebaseio.com/v0/item/\(id).json")!)
    }

func execute <T: Decodable>(url: URL) -> Observable<T> {
        return Observable.create { observer -> Disposable in
            let task = URLSession.shared.dataTask(with: url) { res, _, _ in
                guard let data = res, let decoded = try? JSONDecoder().decode(T.self, from: data) else {
                    return
                }
                
                observer.onNext(decoded)
                observer.onCompleted()
            }
            task.resume()
            
            return Disposables.create {
                task.cancel()
            }
        }
    }

Any help would be greatly appreciated.

CodePudding user response:

The semaphore makes no sense and is inefficient anyway.

Use the same pattern which Apple suggests in conjunction with TaskGroups: Collect the data in a dictionary and after being notified sort the data by the dictionary keys

func getPosts(feedType: FeedType){
    var postData = [Int:Post]()
    posts = []
     
     self.getFeedIDs(feedType: feedType).subscribe{ ids in
         let firstFifteen = ids[0...15]
         
         let dGroup = DispatchGroup()
         
         for (index, element) in firstFifteen.enumerated() {
             dGroup.enter()
             self.getPost(id: element).subscribe{ post in
                 postData[index] = post
                 dGroup.leave()
             }   
         }
         
         dGroup.notify(queue: .main) {
             for key in postData.keys.sorted() {
                 posts.append(postData[key]!)
             }
         }
     }
 }
  • Related