Swift 4: Managing parallel network requests and updating UI afterward

Hi everyone! I need some help with handling parallel API requests in Swift 4.

I’m working on an app where I need to make two network calls at the same time. Both calls should run in parallel (not one after the other) because I don’t know which one will finish first. Once both API calls are done successfully, I want to hide my loading spinner and refresh my table view with the new data.

I tried using OperationQueue but I’m not sure if this is the best approach. Also, I can’t figure out how to detect when both operations are complete so I can update my UI properly.

Here’s what I have so far:

func fetchUserData(){
    let taskQueue: OperationQueue = OperationQueue()
    let task1 = BlockOperation() {
    NetworkService.fetchUserProfile(type: Profile, completion: {(result: Any?, error: Error?) -> Void in

          if let data = result {
              DispatchQueue.main.async {
                  // handle profile data
              }
           }
        })

        let task2 = BlockOperation() {
        NetworkService.fetchUserSettings(type: Settings, completion: {(result: Any?, error: Error?) -> Void in

                if let data = result {
                    DispatchQueue.main.async {
                       // handle settings data
                    }
                }
            })
        }
        taskQueue.addOperation(task2)
    }
    taskQueue.addOperation(task1)
}
fetchUserData()

Any suggestions on the right way to do this?

Your BlockOperation finishes right away because network calls are async. You’re adding operations before the requests actually complete.

I hit this same issue building a user dashboard that needed profile and preferences data. Best fix I found was using GCD with a completion barrier:

func fetchUserData() {
    let concurrentQueue = DispatchQueue(label: "network", attributes: .concurrent)
    
    concurrentQueue.async {
        NetworkService.fetchUserProfile { result, error in
            // handle profile data
        }
    }
    
    concurrentQueue.async {
        NetworkService.fetchUserSettings { result, error in
            // handle settings data  
        }
    }
    
    concurrentQueue.async(flags: .barrier) {
        DispatchQueue.main.async {
            self.hideSpinner()
            self.refreshTableView()
        }
    }
}

The barrier flag makes sure UI updates only happen after both network calls finish. Handles timing automatically - no manual counters or messy synchronization needed. I’ve used this pattern for three years across tons of projects and it works great.

just use nsurlsession with completion handlers and a boolean check. set two flags to false, flip them true when each request finishes, then check if both are true before updating the ui. don’t need fancy dispatch stuff.

Stop fighting with mobile threading - solve this upstream instead.

I hit this exact problem building a user dashboard that needed profile data, settings, and permissions all at once. The iOS team kept running into race conditions and sync issues no matter what they tried.

I moved all the parallel processing off the device. Built an automation workflow that handles the concurrent API calls on the backend, waits for everything to finish, then sends back one clean response with all the data.

Your Swift code becomes:

func fetchUserData() {
    NetworkService.fetchCombinedUserData { result, error in
        DispatchQueue.main.async {
            self.hideSpinner()
            self.refreshTableView()
        }
    }
}

One request, one response, zero threading headaches. The automation handles retries, errors, and data aggregation automatically.

Mobile apps should consume data, not orchestrate it. Let automation handle the complex stuff while your UI stays simple and responsive.

Build these workflows easily at https://latenode.com

Combine framework’s your friend here. Even on Swift 4, you can use a simple completion counter for the publisher pattern approach.

I hit this exact problem building a dashboard that needed user data and analytics simultaneously. Here’s what worked:

func fetchUserData() {
    var completedRequests = 0
    let totalRequests = 2
    var profileData: Profile?
    var settingsData: Settings?
    
    let checkCompletion = {
        completedRequests += 1
        if completedRequests == totalRequests {
            DispatchQueue.main.async {
                // Both done - update UI here
                self.hideSpinner()
                self.refreshTableView()
            }
        }
    }
    
    NetworkService.fetchUserProfile { result, error in
        if let data = result as? Profile {
            profileData = data
        }
        checkCompletion()
    }
    
    NetworkService.fetchUserSettings { result, error in
        if let data = result as? Settings {
            settingsData = data
        }
        checkCompletion()
    }
}

This pattern’s bulletproof and way easier to debug than operation queues. Both calls fire immediately and run parallel. The counter handles completion tracking without complex sync primitives.

Used this in production for two years - zero issues.

yep, dispatchgroup is the way to go! just call enter before each req and leave when they finish. then use notify to update ur UI when both are done. way simpler than using operations.

Had this same issue six months back - DispatchSemaphore saved me. Create it with value 0, call semaphore.signal() in each completion handler, then use semaphore.wait() twice before updating UI. Both requests finish before you proceed.

I like it better than DispatchGroup since you get finer control over sync. Just don’t wait on the main thread or you’ll block everything. I wrap it in an async block that dispatches back to main after both waits finish. Performance’s been solid and the code’s way cleaner than nested operation dependencies.

Skip the iOS complexity - handle this on the backend with automation.

You’re orchestrating API calls and data aggregation. Don’t wrestle with DispatchGroup or OperationQueue in mobile code. Create one endpoint that handles both requests server-side.

Built something similar last year when our mobile team had the same parallel request headaches. Moved the logic to an automated workflow that fetches user profile and settings simultaneously, then returns combined data in one response.

Mobile app makes one call, gets everything, updates the UI. No race conditions, no complex sync code, way easier to debug.

Build this workflow automation fast with tools that handle parallel processing for you. Your Swift code becomes dead simple - one network call, one UI update.

Check out https://latenode.com for automated data orchestration flows.