Search code examples
iosswiftobjective-cmacoslocation

Dispatch Group Multiple Joined Swift


Hi everyone i have a question about a problem that i would like to do in swift and to see the best possible way to do that. I currently have an approach that i don't like and would be good if i can get some ideas about it.

Here it is the problem:

  • DispatchQ serial which is started
  • Then another Thread starts and waits for a completion block that returns 1 element per run
  • That thread will run multiple times and it will give 1 element each time until it returns all elements
  • The code shouldn't proceed forward and has to wait for all the elements to be collected in a array
  • After the array is full then the code can proceed forward.

Example with LocationManager:

  • Location manager starts fetching location
  • It output the location each time it finds a new location
  • At the end i have to collect all the locations i have gotten in 10 sec for example
  • Then proceed the work of the current thread

Example code:

var queue = DispatchQueue("q")

queue.async {
    
    var locationCollected = Array<Location>()

    // this will output multiple times
    getLocation { location in

        // add location to the collection
        locationCollected.append(location)
    }

    // the current Q need to wait until all results are collected
    return locationCollected
}

func getLocation() -> Location {
    // this is the function that will return location
    self.manager?.location(completion: { location in
        // will provide location and update 

        completion(location)
    }
}

Thank you much appreciated the help


Solution

  • If you want to adopt a linear flow of code approach then you can adopt the new async/await construct but that will limit your iOS versions.

    In general blocking threads is best avoided as it can cause deadlocks. You should use completion handlers to get your asynchronous result rather than blocking.

    For the purposes of this answer I will stick with the approach in your question.

    Based on the requirements in your question, let's create a function that:

    • Fetches locations for a specified duration
    • Only fetches one location at a time
    • Provides the fetched location in an array
    • Is, itself, an asynchronous function

    We need to ensure only a single location fetch is active at a time, which we can do with a DispatchSemaphore

    
    func getLocations(for duration: TimeInterval, completion: (([Location])->Void)) {
    
        var queue = DispatchQueue("q")
    
        queue.async {
            let semaphore = DispatchSemaphore(0)
            var locationCollected = [Location]()
            let startTime = Date()
    
            while (startTime.timeIntervalSinceNow > -duration) {
                getLocation { location in
                    locationCollected.append(location)
                    semaphore.signal()
                }
                semaphore.wait()
            }
            completion(result)
        }
    }