Fetching API data asynchronously and making calls every 1 second - swift

I have a function:
func fetchHistoricalCurrencyData(completion: #escaping (() -> ())) {
let g = DispatchGroup()
DispatchQueue.main.async { [weak self] in
var historicalCurrencyDataTemp = [HistoricalCurrencyDataModel]()
let dayDurationInSeconds: TimeInterval = 60*60*24
for date in stride(from: self!.startingDate, to: self!.endingDate, by: dayDurationInSeconds) {
g.enter()
APIManager.shared.getHistoricalCurrencyData(date: self!.convertDateToStringDate(date: date), fromCurrency: String(self!.fromCurrency.prefix(3)), amount: self!.amount, toCurrency: String(self!.toCurrency.prefix(3))) {
historicalCurrencyDataModel in
historicalCurrencyDataTemp.append(historicalCurrencyDataModel)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
g.leave()
}
}
}
g.notify(queue:.main) {
self?.historicalCurrencyData = DataManager.shared.getHistoricalCurrencyData(historicalCurrencyDataModels: historicalCurrencyDataTemp)
print(self?.historicalCurrencyData)
completion()
}
}
}
What's more, I have a dateRange which I divide into particular days within this range by using stride() function. Every day I get from this function I use to make an API Call to get desired data. Every API data I get is being converted to particular HistoricalCurrencyDataModel and appended to temp array which I create before the loop (historicalCurrencyDataTemp). When I receive all the data I want I use DataManager class to transform this data into [String: Double] dictionary which I finally assign to my #Published var historicalCurrencyData: [String: Double] variable.
One clue thing is that API I use make it possible to make API calls one after another at least after 1 second so I have to create 1 second delay between each call. I made it possible with DispatchQueue.main.asyncAfter(deadline: .now() + 1) statement. I control the whole flow of asynchronous operations with DispatchGroup().
Unfortunately after all of this what I get is empty Optional([:]) within print(self?.historicalCurrencyData) statement. And for example the correct behaviour would be that if I use dateRange with 5 days I get a dictionary with 5 entries.

I ultimately got rid of all DispatchQueues and now it works as it should. I believe there was something wrong with the delay and not all values were fetched correctly. What's more, I discovered that there is no need of creating 1 second between API calls I thought otherwise after reading API documentation. I must have been wrong.
As suggested (which I agree with) I attach working code:
func fetchHistoricalCurrencyData(completion: #escaping (() -> ())) {
let g = DispatchGroup()
var historicalCurrencyDataTemp = [HistoricalCurrencyDataModel]()
for date in stride(from: self.startingDate, to: self.endingDate, by: dayDurationInSeconds) {
g.enter()
APIManager.shared.getHistoricalCurrencyData(date: self.convertDateToStringDate(date: date), fromCurrency: String(self.fromCurrency.prefix(3)), amount: self.amount, toCurrency: String(self.toCurrency.prefix(3))) {
historicalCurrencyDataModel in
historicalCurrencyDataTemp.append(historicalCurrencyDataModel)
g.leave()
}
}
g.notify(queue:.main) {
self.historicalCurrencyData = DataManager.shared.getHistoricalCurrencyData(historicalCurrencyDataModels: historicalCurrencyDataTemp)
print(self.historicalCurrencyData)
completion()
}
}

Related

Swift combine publishers where one hasn't sent a value yet

I have a publisher which would need re-evaluating on day change, but should continue to emit values at any other time.
As such, I thought I could use a NotificationCenter publisher for the UIApplication.significantTimeChangeNotification notification and combine it with my publisher such that the combine emission process would re-run on either on data change or day change and hence re-evaluate the map filter. See a rough outline of that code below.
The problem is that there is no published event by NotificationCenter at the point in time that this is setup and hence, none of the following map etc calls actually evaluate. merge(with:) won't work as the two publishers publish different types, but combineLatest(_:) and zip(_:) both won't emit events until both publishers have emitted a single event.
I can validate that my code operates as expected by adding NotificationCenter.default.post(name: UIApplication.significantTimeChangeNotification, object: nil) after this code, but that is undesirable due to it potentially signalling other areas of the app that an actual time change has occurred when it hasn't
private func todaysDate() -> String {
let formatter = DateFormatter()
formatter.dateFormat = "YYYY-MM-dd"
return formatter.string(from: Date())
}
#Published var entities: [MyEntity]
let dayChangePublisher = NotificationCenter.default
.publisher(for: UIApplication.significantTimeChangeNotification)
$entities.combineLatest(dayChangePublisher)
.map(\.0) // Only pass on the entity for further operations
.map { entities -> MyEntity? in
let today = todaysDate()
return entities?.first(where: { $0.id == today })
}
...remainder of combine code
Can this combination of publishers and evaluation of events occur with the current Swift combine framework? Like the behaviour I'd expect from merge(with:) but where the publishers emit two different types.
edit:
I found one solution where I map the notification publisher to a nil array
let dayChangePublisher = NotificationCenter.default
.publisher(for: UIApplication.significantTimeChangeNotification)
.map { _ ➝ [MyEntity]? in
return nil
}
And then use merge and compactMap to avoid passing any nil values on
let mergedPub = repo.$entities
.merge(with: dayChangePublisher)
.compactMap { entity -> MyEntity? in
let today = todaysDate()
return entities?.first { $0.id == today }
}
.share()
It works, but maybe a bit cumbersome if anyone has a better solution?
If I understood your question, you need a combineLatest that is not blocked by not having an initial value from one of the publishers.
You can achieve that with .prepend(value) operator. In this case, since you don't care about the actual value, map first to Void, then prepend a Void. It would work like so:
let dayChangePublisher = NotificationCenter.default
.publisher(for: UIApplication.significantTimeChangeNotification)
$entities.combineLatest(
dayChangePublisher
.map { _ in }
.prepend(()) // make sure to prepend a () value
)
.map(\.0) // Only pass on the entity for further operations
.map { entities -> MyEntity? in
let today = todaysDate()
return entities?.first(where: { $0.id == today })
}
//...

RxSwift Skip Events Until Own Sequence has finished

I have one observable (we will call it trigger) that can emit many times in a short period of time. When it emits I am doing a network Request and the I am storing the result with the scan Operator.
My problem is that I would like to wait until the request is finished to do it again. (But as it is now if trigger emits 2 observables it doesn't matter if fetchData has finished or not, it will do it again)
Bonus: I also would like to take only the first each X seconds (Debounce is not the solution because it can be emitting all the time and I want to get 1 each X seconds, it isn't throttle neither because if an observable emits 2 times really fast I will get the first and the second delayed X seconds)
The code:
trigger.flatMap { [unowned self] _ in
self.fetchData()
}.scan([], accumulator: { lastValue, newValue in
return lastValue + newValue
})
and fetchData:
func fetchData() -> Observable<[ReusableCellVMContainer]>
trigger:
let trigger = Observable.of(input.viewIsLoaded, handle(input.isNearBottomEdge)).merge()
I'm sorry, I misunderstood what you were trying to accomplish in my answer below.
The operator that will achieve what you want is flatMapFirst. This will ignore events from the trigger until the fetchData() is complete.
trigger
.flatMapFirst { [unowned self] _ in
self.fetchData()
}
.scan([], accumulator: { lastValue, newValue in
return lastValue + newValue
})
I'm leaving my previous answer below in case it helps (if anything, it has the "bonus" answer.)
The problem you are having is called "back pressure" which is when the observable is producing values faster than the observer can handle.
In this particular case, I recommend that you don't restrict the data fetch requests and instead map each request to a key and then emit the array in order:
trigger
.enumerated()
.flatMap { [unowned self] count, _ in
Observable.combineLatest(Observable.just(count), self.fetchData())
}
.scan(into: [Int: Value](), accumulator: { lastValue, newValue in
lastValue[newValue.0] = newValue.1
})
.map { $0.sorted(by: { $0.key < $1.key }).map { $0.value }}
To make the above work, you need this:
extension ObservableType {
func enumerated() -> Observable<(Int, E)> {
let shared = share()
let counter = shared.scan(0, accumulator: { prev, _ in return prev + 1 })
return Observable.zip(counter, shared)
}
}
This way, your network requests are starting ASAP but you aren't loosing the order that they are made in.
For your "bonus", the buffer operator will do exactly what you want. Something like:
trigger.buffer(timeSpan: seconds, count: Int.max, scheduler: MainScheduler.instance)
.map { $0.first }

How to ensure two asynchronous tasks that 'start' together are completed before running another?

I am setting up an app that utilizes promiseKit as a way to order asynchronous tasks. I currently have a set up which ensures two async functions (referred to as promises) are done in order (lets call them 1 and 2) and that another set of functions (3 and 4) are done in order. Roughly:
import PromiseKit
override func viewDidAppear(_ animated: Bool) {
firstly{
self.promiseOne() //promise #1 happening first (in relation to # 1 and #2)
}.then{_ -> Promise<[String]> in
self.promiseTwo()//promise #2 starting after 1 has completed
}
.catch{ error in
print(error)
}
firstly{
self.promiseThree()//Promise #3 happening first (in relation to #3 and #4)
}.then{_ -> Promise<[String]> in
self.promiseFour()//Promise #4 starting after #3 has completed
}.
.catch{ error in
print(error)
}
}
Each firstly ensures the order of the functions within them by making sure the first one is completed before the second one can initiate. Using two separate firstly's ensures that 1 is done before 2, 3 is done before 4, and (importantly) 1 and 3 start roughly around the same time (at the onset of the viewDidAppear()). This is done on purpose because 1 and 3 are not related to each other and can start at the same time without any issues (same goes for 2 and 4). The issue is that there is a fifth promise, lets call it promiseFive that must only be run after 2 and 4 have been completed. I could just link one firstly that ensure the order is 1,2,3,4,5, but since the order of 1/2 and 3/4 is not relevant, linking them in this fashion would waste time.
I am not sure how to set this up so that promiseFive is only run upon completion of both 2 and 4. I have thought to have boolean-checked functions calls at the end of both 2 and 4, making sure the other firstly has finished to then call promiseFive but, since they begin asynchronously (1/2 and 3/4), it is possible that promiseFive would be called by both at the exact same time with this approach, which would obviously create issues. What is the best way to go about this?
You can use when or join to start something after multiple other promises have completed. The difference is in how they handled failed promises. It sounds like you want join. Here is a concrete, though simple example.
This first block of code is an example of how to create 2 promise chains and then wait for both of them to complete before starting the next task. The actual work being done is abstracted away into some functions. Focus on this block of code as it contains all the conceptual information you need.
Snippet
let chain1 = firstly(execute: { () -> (Promise<String>, Promise<String>) in
let secondPieceOfInformation = "otherInfo" // This static data is for demonstration only
// Pass 2 promises, now the next `then` block will be called when both are fulfilled
// Promise initialized with values are already fulfilled, so the effect is identical
// to just returning the single promise, you can do a tuple of up to 5 promises/values
return (fetchUserData(), Promise(value: secondPieceOfInformation))
}).then { (result: String, secondResult: String) -> Promise<String> in
self.fetchUpdatedUserImage()
}
let chain2 = firstly {
fetchNewsFeed() //This promise returns an array
}.then { (result: [String : Any]) -> Promise<String> in
for (key, value) in result {
print("\(key) \(value)")
}
// now `result` is a collection
return self.fetchFeedItemHeroImages()
}
join(chain1, chain2).always {
// You can use `always` if you don't care about the earlier values
let methodFinish = Date()
let executionTime = methodFinish.timeIntervalSince(self.methodStart)
print(String(format: "All promises finished %.2f seconds later", executionTime))
}
PromiseKit uses closures to provide it's API. Closures have an scope just like an if statement. If you define a value within an if statement's scope, then you won't be able to access it outside of that scope.
You have several options to passing multiple pieces of data to the next then block.
Use a variable that shares a scope with all of the promises (you'll likely want to avoid this as it works against you in managing the flow of asynchronous data propagation)
Use a custom data type to hold both (or more) values. This can be a tuple, struct, class, or enum.
Use a collection (such as a dictionary), example in chain2
Return a tuple of promises, example included in chain1
You'll need to use your best judgement when choosing your method.
Complete Code
import UIKit
import PromiseKit
class ViewController: UIViewController {
let methodStart = Date()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
<<Insert The Other Code Snippet Here To Complete The Code>>
// I'll also mention that `join` is being deprecated in PromiseKit
// It provides `when(resolved:)`, which acts just like `join` and
// `when(fulfilled:)` which fails as soon as any of the promises fail
when(resolved: chain1, chain2).then { (results) -> Promise<String> in
for case .fulfilled(let value) in results {
// These promises succeeded, and the values will be what is return from
// the last promises in chain1 and chain2
print("Promise value is: \(value)")
}
for case .rejected(let error) in results {
// These promises failed
print("Promise value is: \(error)")
}
return Promise(value: "finished")
}.catch { error in
// With the caveat that `when` never rejects
}
}
func fetchUserData() -> Promise<String> {
let promise = Promise<String> { (fulfill, reject) in
// These dispatch queue delays are standins for your long-running asynchronous tasks
// They might be network calls, or batch file processing, etc
// So, they're just here to provide a concise, illustrative, working example
DispatchQueue.global().asyncAfter(deadline: .now() + 2.0) {
let methodFinish = Date()
let executionTime = methodFinish.timeIntervalSince(self.methodStart)
print(String(format: "promise1 %.2f seconds later", executionTime))
fulfill("promise1")
}
}
return promise
}
func fetchUpdatedUserImage() -> Promise<String> {
let promise = Promise<String> { (fulfill, reject) in
DispatchQueue.global().asyncAfter(deadline: .now() + 2.0) {
let methodFinish = Date()
let executionTime = methodFinish.timeIntervalSince(self.methodStart)
print(String(format: "promise2 %.2f seconds later", executionTime))
fulfill("promise2")
}
}
return promise
}
func fetchNewsFeed() -> Promise<[String : Any]> {
let promise = Promise<[String : Any]> { (fulfill, reject) in
DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
let methodFinish = Date()
let executionTime = methodFinish.timeIntervalSince(self.methodStart)
print(String(format: "promise3 %.2f seconds later", executionTime))
fulfill(["key1" : Date(),
"array" : ["my", "array"]])
}
}
return promise
}
func fetchFeedItemHeroImages() -> Promise<String> {
let promise = Promise<String> { (fulfill, reject) in
DispatchQueue.global().asyncAfter(deadline: .now() + 2.0) {
let methodFinish = Date()
let executionTime = methodFinish.timeIntervalSince(self.methodStart)
print(String(format: "promise4 %.2f seconds later", executionTime))
fulfill("promise4")
}
}
return promise
}
}
Output
promise3 1.05 seconds later
array ["my", "array"]
key1 2017-07-18 13:52:06 +0000
promise1 2.04 seconds later
promise4 3.22 seconds later
promise2 4.04 seconds later
All promises finished 4.04 seconds later
Promise value is: promise2
Promise value is: promise4
The details depend a little upon what types of these various promises are, but you can basically return the promise of 1 followed by 2 as one promise, and return the promise of 3 followed by 4 as another, and then use when to run those two sequences of promises concurrently with respect to each other, but still enjoy the consecutive behavior within those sequences. For example:
let firstTwo = promiseOne().then { something1 in
self.promiseTwo(something1)
}
let secondTwo = promiseThree().then { something2 in
self.promiseFour(something2)
}
when(fulfilled: [firstTwo, secondTwo]).then { results in
os_log("all done: %#", results)
}.catch { error in
os_log("some error: %#", error.localizedDescription)
}
This might be a situation in which your attempt to keep the question fairly generic might make it harder to see how to apply this answer in your case. So, if you are stumbling, you might want to be more specific about what these four promises are doing and what they're passing to each other (because this passing of results from one to another is one of the elegant features of promises).

Recursive/looping NSURLSession async completion handlers

The API I use requires multiple requests to get search results. It's designed this way because searches can take a long time (> 5min). The initial response comes back immediately with metadata about the search, and that metadata is used in follow up requests until the search is complete. I do not control the API.
1st request is a POST to https://api.com/sessions/search/
The response to this request contains a cookie and metadata about the search. The important fields in this response are the search_cookie (a String) and search_completed_pct (an Int)
2nd request is a POST to https://api.com/sessions/results/ with the search_cookie appended to the URL. eg https://api.com/sessions/results/c601eeb7872b7+0
The response to the 2nd request will contain either:
The search results if the query has completed (aka search_completed_pct == 100)
Metadata about the progress of search, search_completed_pct is the progress of the search and will be between 0 and 100.
If the search is not complete, I want to make a request every 5 seconds until it's complete (aka search_completed_pct == 100)
I've found numerous posts here that are similar, many use Dispatch Groups and for loops, but that approach did not work for me. I've tried a while loop and had issues with variable scoping. Dispatch groups also didn't work for me. This smelled like the wrong way to go, but I'm not sure.
I'm looking for the proper design to make these recursive calls. Should I use delegates or are closures + loop the way to go? I've hit a wall and need some help.
The code below is the general idea of what I've tried (edited for clarity. No dispatch_groups(), error handling, json parsing, etc.)
Viewcontroller.swift
apiObj.sessionSearch(domain) { result in
Log.info!.message("result: \(result)")
})
ApiObj.swift
func sessionSearch(domain: String, sessionCompletion: (result: SearchResult) -> ()) {
// Make request to /search/ url
let task = session.dataTaskWithRequest(request) { data, response, error in
let searchCookie = parseCookieFromResponse(data!)
********* pseudo code **************
var progress: Int = 0
var results = SearchResults()
while (progress != 100) {
// Make requests to /results/ until search is complete
self.getResults(searchCookie) { searchResults in
progress = searchResults.search_pct_complete
if (searchResults == 100) {
completion(searchResults)
} else {
sleep(5 seconds)
} //if
} //self.getResults()
} //while
********* pseudo code ************
} //session.dataTaskWithRequest(
task.resume()
}
func getResults(cookie: String, completion: (searchResults: NSDictionary) -> ())
let request = buildRequest((domain), url: NSURL(string: ResultsUrl)!)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { data, response, error in
let theResults = getJSONFromData(data!)
completion(theResults)
}
task.resume()
}
Well first off, it seems weird that there is no API with a GET request which simply returns the result - even if this may take minutes. But, as you mentioned, you cannot change the API.
So, according to your description, we need to issue a request which effectively "polls" the server. We do this until we retrieved a Search object which is completed.
So, a viable approach would purposely define the following functions and classes:
A protocol for the "Search" object returned from the server:
public protocol SearchType {
var searchID: String { get }
var isCompleted: Bool { get }
var progress: Double { get }
var result: AnyObject? { get }
}
A concrete struct or class is used on the client side.
An asynchronous function which issues a request to the server in order to create the search object (your #1 POST request):
func createSearch(completion: (SearchType?, ErrorType?) -> () )
Then another asynchronous function which fetches a "Search" object and potentially the result if it is complete:
func fetchSearch(searchID: String, completion: (SearchType?, ErrorType?) -> () )
Now, an asynchronous function which fetches the result for a certain "searchID" (your "search_cookie") - and internally implements the polling:
func fetchResult(searchID: String, completion: (AnyObject?, ErrorType?) -> () )
The implementation of fetchResult may now look as follows:
func fetchResult(searchID: String,
completion: (AnyObject?, ErrorType?) -> () ) {
func poll() {
fetchSearch(searchID) { (search, error) in
if let search = search {
if search.isCompleted {
completion(search.result!, nil)
} else {
delay(1.0, f: poll)
}
} else {
completion(nil, error)
}
}
}
poll()
}
This approach uses a local function poll for implementing the polling feature. poll calls fetchSearch and when it finishes it checks whether the search is complete. If not it delays for certain amount of duration and then calls poll again. This looks like a recursive call, but actually it isn't since poll already finished when it is called again. A local function seems appropriate for this kind of approach.
The function delay simply waits for the specified amount of seconds and then calls the provided closure. delay can be easily implemented in terms of dispatch_after or a with a cancelable dispatch timer (we need later implement cancellation).
I'm not showing how to implement createSearch and fetchSearch. These may be easily implemented using a third party network library or can be easily implemented based on NSURLSession.
Conclusion:
What might become a bit cumbersome, is to implement error handling and cancellation, and also dealing with all the completion handlers. In order to solve this problem in a concise and elegant manner I would suggest to utilise a helper library which implements "Promises" or "Futures" - or try to solve it with Rx.
For example a viable implementation utilising "Scala-like" futures:
func fetchResult(searchID: String) -> Future<AnyObject> {
let promise = Promise<AnyObject>()
func poll() {
fetchSearch(searchID).map { search in
if search.isCompleted {
promise.fulfill(search.result!)
} else {
delay(1.0, f: poll)
}
}
}
poll()
return promise.future!
}
You would start to obtain a result as shown below:
createSearch().flatMap { search in
fetchResult(search.searchID).map { result in
print(result)
}
}.onFailure { error in
print("Error: \(error)")
}
This above contains complete error handling. It does not yet contain cancellation. Your really need to implement a way to cancel the request, otherwise the polling may not be stopped.
A solution implementing cancellation utilising a "CancellationToken" may look as follows:
func fetchResult(searchID: String,
cancellationToken ct: CancellationToken) -> Future<AnyObject> {
let promise = Promise<AnyObject>()
func poll() {
fetchSearch(searchID, cancellationToken: ct).map { search in
if search.isCompleted {
promise.fulfill(search.result!)
} else {
delay(1.0, cancellationToken: ct) { ct in
if ct.isCancelled {
promise.reject(CancellationError.Cancelled)
} else {
poll()
}
}
}
}
}
poll()
return promise.future!
}
And it may be called:
let cr = CancellationRequest()
let ct = cr.token
createSearch(cancellationToken: ct).flatMap { search in
fetchResult(search.searchID, cancellationToken: ct).map { result in
// if we reach here, we got a result
print(result)
}
}.onFailure { error in
print("Error: \(error)")
}
Later you can cancel the request as shown below:
cr.cancel()

How to modify a struct with async callbacks?

I'm trying to update a struct with multi-level nested async callback, Since each level callback provides info for next batch of requests till everything is done. It's like a tree structure. And each time I can only get to one level below.
However, the first attempt with inout parameter failed. I now learned the reason, thanks to great answers here:
Inout parameter in async callback does not work as expected
My quest is still there to be solved. The only way I can think of is to store the value to a local file or persistent store and modify it directly each time. And after writing the sample code, I think a global var can help me out on this as well. But I guess the best way is to have a struct instance for this job. And for each round of requests, I store info for this round in one place to avoid the mess created by different rounds working on the same time.
With sample code below, only the global var update works. And I believe the reason the other two fail is the same as the question I mentioned above.
func testThis() {
var d = Data()
d.getData()
}
let uriBase = "https://hacker-news.firebaseio.com/v0/"
let u: [String] = ["bane", "LiweiZ", "rdtsc", "ssivark", "sparkzilla", "Wogef"]
var successfulRequestCounter = 0
struct A {}
struct Data {
var dataOkRequestCounter = 0
var dataArray = [A]()
mutating func getData() {
for s in u {
let p = uriBase + "user/" + s + ".json"
getAnApiData(p)
}
}
mutating func getAnApiData(path: String) {
var req = NSURLRequest(URL: NSURL(string: path)!)
var config = NSURLSessionConfiguration.ephemeralSessionConfiguration()
var session = NSURLSession(configuration: config)
println("p: \(path)")
var task = session.dataTaskWithRequest(req) {
(data: NSData!, res: NSURLResponse!, err: NSError!) in
if let e = err {
// Handle error
} else if let d = data {
// Successfully got data. Based on this data, I need to further get more data by sending requests accordingly.
self.handleSuccessfulResponse()
}
}
task.resume()
}
mutating func handleSuccessfulResponse() {
println("successfulRequestCounter before: \(successfulRequestCounter)")
successfulRequestCounter++
println("successfulRequestCounter after: \(successfulRequestCounter)")
println("dataOkRequestCounter before: \(dataOkRequestCounter)")
dataOkRequestCounter++
println("dataOkRequestCounter after: \(dataOkRequestCounter)")
println("dataArray count before: \(dataArray.count)")
dataArray.append(A())
println("dataArray count after: \(dataArray.count)")
if successfulRequestCounter == 6 {
println("Proceeded")
getData()
}
}
}
func getAllApiData() {
for s in u {
let p = uriBase + "user/" + s + ".json"
getOneApiData(p)
}
}
Well, in my actual project, I successfully append a var in the struct in the first batch of callbacks and it failed in the second one. But I failed to make it work in the sample code. I tried many times so that it took me so long to update my question with sample code. Anyway, I think the main issue is to learn appropriate approach for this task. So I just put it aside for now.
I guess there is no way to do it with closure, given how closure works. But still want to ask and learn the best way.
Thanks.
What I did was use an inout NSMutableDictionary.
func myAsyncFunc(inout result: NSMutableDictionary){
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0)) {
let intValue = result.valueForKey("intValue")
if intValue as! Int > 0 {
//Do Work
}
}
dispatch_async(dispatch_get_main_queue()) {
result.setValue(0, forKey: "intValue")
}
}
I know you already tried using inout, but NSMutableDictionary worked for me when no other object did.