Update UI in closure - multithreading

I'm trying to update my UI while executing a function with a status closure and a completion closure. The UI is only updated when the closure is complete. I understand that this happens because the operation is not happening on the main thread and the UI need to be updated on the main thread. I have tried moving the UI updates to the main thread, but without any luck. I have included a simplified version of my code.
How can I solve this problem? Will it solve the problem if I specify a custom thread for my code to be excecuted? If so, how is that done?
Thank you very much for taking your time to read.
Code is included below. Let me know if you need any more information regarding this issue.
func parse(array: [String], status:(status: String!, progress: Float!) -> (), completion:(result: [String]!) ->()) -> () {
status(status: "Process is starting.", progress: 0)
var newArray = [String]()
for (index, txt) in enumerate(array) {
//Update status
let progress = Float(index + 1) / Float(array.count)
status(status: "Checking string: \(txt)", progress: progress)
//Do something with txt
let newTxt = txt + "OK"
newArray.append(newTxt)
}
status(status: "Complete!", progress: 1.0)
//Send completion
completion(result: newArray)
}
var startArray = [String]()
for index in 0...10000 {
startArray.append("\(index)")
}
parse(startArray, { (status: String!, progress: Float!) -> () in
dispatch_async(dispatch_get_main_queue(), { () -> Void in
println(status)
self.statusLabel.text = status
println(progress)
self.progressView.progress = progress
})
}, { (result: [String]!) -> () in
println("Process complete. Here is the result:\n\(result)")
})

My problem was that parse was called on the main thread, so when updating the UI, that thread was already blocked. I solved the problem (with the help of David's comments) by putting parse in another thread, then updating the UI on the main thread with GCD.

Related

ZIO scala sleep method not sleeping the thread vs. using directly Thread.sleep

In my existing Scala code I replaced Thread.sleep(10000) with ZIO.sleep(Duration.fromScala(10.seconds)) with the understanding that it won't block thread from the thread pool (performance issue). When program runs it does not wait at this line (whereas of course in first case it does). Do I need to add any extra code for ZIO method to work ?
Adding code section from Play+Scala code:
def sendMultipartEmail = Action.async(parse.multipartFormData) { request =>
.....
//inside this controller below method is called
def retryEmailOnFail(pList: ListBuffer[JsObject], content: String) = {
if (!sendAndGetStatus(pList, content)) {
println("<--- email sending failed - retry once after a delay")
ZIO.sleep(Duration.fromScala(10.seconds))
println("<--- retrying email sending after a delay")
finalStatus = finalStatus && sendAndGetStatus(pList, content)
} else {
finalStatus = finalStatus && true
}
}
.....
}
As you said, ZIO.sleep will only suspend the fiber that is running, not the operating system thread.
If you want to start something after sleeping, you should just chain it after the sleep:
// value 42 will only be computed after waiting for 10s
val io = ZIO.sleep(Duration.fromScala(10.seconds)).map(_ => 42)

Threading + multiple windows causing strange errors

I have 2 windows in swift, one is like a login dialog which sends an NSURLConnection.sendAsynchronousRequest to a server to get authenticated. Once it gets the response, the window is supposed to close.
When I close the Window (either from the login window class or main window clasS) I get these errors:
This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes. This will cause an exception in a future release.
I have tried all manner of background threads etc. But I think the issue is that I am closing the window why the asynch NSURLConnection request is still hanging.
My code to send the async request from the login window:
dispatch_async(dispatch_get_main_queue(), {
let queue:NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue(), completionHandler:{ (response:NSURLResponse!, data: NSData!, error: NSError!) -> Void in
var error: AutoreleasingUnsafeMutablePointer<NSError?> = nil
let jsonResult: NSDictionary! = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers, error: error) as? NSDictionary
let result: NSString = NSString(data: data, encoding: NSUTF8StringEncoding)!
let expectedString = "special auth string"!
if(result == expectedString) {
self.callback.loginOK()
} else {
self.output.stringValue = result
}
return
})
})
The callback member of the class is the parent view contoller that spawns it, I then close the login window using loginVC.view.window?.close() from the main application window. That causes the error.
The problem is that NSURLConnection.sendAsynchronousRequest will always run in a secondary thread and thus its callback will be called from that secondary thread despite you calling it explicitly from main thread.
You don't need to wrap NSURLConnection.sendAsynchronousRequest in the main thread, instead wrap your ' self.callback.loginOK()' to run in main thread using the dispatch_async to ensure no UI related operations take place in secondary thread. Something like this-
let queue:NSOperationQueue = NSOperationQueue()
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue(), completionHandler:{ (response:NSURLResponse!, data: NSData!, error: NSError!) -> Void in
var error: AutoreleasingUnsafeMutablePointer<NSError?> = nil
let jsonResult: NSDictionary! = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers, error: error) as? NSDictionary
let result: NSString = NSString(data: data, encoding: NSUTF8StringEncoding)!
dispatch_async(dispatch_get_main_queue() {
let expectedString = "special auth string"!
if(result == expectedString) {
self.callback.loginOK()
} else {
self.output.stringValue = result
}
})
return
})

Trouble understanding NSOperation & NSOperationQueue (swift)

I'm having trouble understanding how to create a synchronous NSOperationQueue.
I've created a prototype that basically says:
Create 4 operations that very long or very short to complete
Regardless of time to complete, they should finish in the order they are created in the queue.
My NSOperation class is very simple:
class LGOperation : NSOperation
{
private var operation: () -> ()
init(operation: () -> ())
{
self.operation = operation
}
override func main()
{
if self.cancelled {
return
}
operation()
}
}
And my test class is also quite simple:
class LGOperationTest
{
class func downloadImage(url: String)
{
// This is a simple AFHTTPRequestOperation for the image
LGImageHelper.downloadImageWithUrl(url, complete: { (image: AnyObject?) in
println("downloaded \(url)")
})
}
class func test()
{
var queue = NSOperationQueue.mainQueue()
queue.maxConcurrentOperationCount = 1
var op1 = LGOperation(operation: { self.downloadImage("http://www.toysrus.com/graphics/tru_prod_images/Animal-Planet-T-Rex---Grey--pTRU1-2909995dt.jpg") })
var op2 = LGOperation(operation: { println("OPERATION 2") })
var op3 = LGOperation(operation: { self.downloadImage("http://www.badassoftheweek.com/trex.jpg") })
var op4 = LGOperation(operation: { println("OPERATION 3") })
var ops: [NSOperation] = [op1, op2, op3, op4]
op2.addDependency(op1)
op3.addDependency(op2)
op4.addDependency(op3)
op4.completionBlock = {
println("finished op 4")
}
queue.addOperation(op1)
queue.addOperation(op2)
queue.addOperation(op3)
queue.addOperation(op4)
println("DONE")
}
}
So I would expect here is for the operations to finish in order, instead the output is:
DONE
OPERATION 2
OPERATION 4
finished op 4
downloaded
http://www.toysrus.com/graphics/tru_prod_images/Animal-Planet-T-Rex---Grey--pTRU1-2909995dt.jpg
downloaded http://www.badassoftheweek.com/trex.jpg
WHY can't I make web requests fire synchronously with other code? (I know I can use completion blocks and chain them but I'd like to figure out how to do it with NSOperation)
Operation queues are used to schedule asynchronous operations, primarily these operations may be long running and you don't want to block the current (typically UI) thread. Blocking the UI thread leads to unresponsive UI.
When you create 4 operations, when they finish is a factor of what is being performed. In your case, you have operations that are doing println (which is very fast) and you have operations that are downloading from the internet (which is very slow).
The whole point of the operation queue is to allow you to fire these operations asynchronously, and whenever the operations complete, fire the completion handler.
In other words, you do cannot control the sequence.
If you want to control the sequence, my suggestion is to do the following:
Start operation 1
In operation 1's completion handler, start operation 2
In operation 2's completion handler, start operation 3
In operation 3's completion handler, start operation 4
In this way, you still achieve the benefits of Operation queues (you do not block the UI thread), and you can chain the operations in order.

run a block on main thread immediately after NSBlockOperation on background thread

In my project I run an operation on a background thread using NSBlockOperation:
var operationQueue = NSOperationQueue()
var iop = NSBlockOperation(block: { self.reloadSize() /*calculation...*/ })
operationQueue.addOperation(iop)
Immediately after the calculations in the background thread are completed, I need to call: table.reloadData() on an NSTableView. I would do that in the very same thread, however, due to auto layout issues, the table has to be reloaded on the main thread. How can I accomplish this asynchronous relationship across both threads?
Two possible approaches:
Dispatch the reloading of the table from inside the block:
let operationQueue = NSOperationQueue()
let operation = NSBlockOperation() {
self.reloadSize()
...
dispatch_async(dispatch_get_main_queue()) { // or you can use NSOperationQueue.mainQueue().addOperationWithBlock()
self.table.reloadData()
}
}
operationQueue.addOperation(operation)
or just use addOperationWithBlock:
let operationQueue = NSOperationQueue()
operationQueue.addOperationWithBlock() {
self.reloadSize()
...
dispatch_async(dispatch_get_main_queue()) { // or you can use NSOperationQueue.mainQueue().addOperationWithBlock()
self.table.reloadData()
}
}
Create a new operation dependent upon this one:
let operationQueue = NSOperationQueue()
let operation = NSBlockOperation() {
self.reloadSize()
...
}
let completionOperation = NSBlockOperation() {
self.table.reloadData()
}
completionOperation.addDependency(operation)
operationQueue.addOperation(operation)
NSOperationQueue.mainQueue().addOperation(completionOperation)
Personally, I'd generally lean towards the first approach, though the latter approach can be useful in more complicated scenarios (e.g. the completion operation is dependent upon a number of other operations).
Try calling CFRunLoopRun().
It should run in the current queue.
If your operation ran on main queue, the current queue would be main queue and the operation would run on it succesfully

Function/Code Design with Concurrency in Swift

I'm trying to create my first app in Swift which involves making multiple requests to a website. These requests are each done using the block
var task = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in ... }
task.resume()
From what I understand this block uses a thread different to the main thread.
My question is, what is the best way to design code that relies on the values in that block? For instance, the ideal design (however not possible due to the fact that the thread executing these blocks is not the main thread) is
func prepareEmails() {
var names = getNames()
var emails = getEmails()
...
sendEmails()
}
func getNames() -> NSArray {
var names = nil
....
var task = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
names = ...
})
task.resume()
return names
}
func getEmails() -> NSArray {
var emails = nil
....
var task = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
emails = ...
})
task.resume()
return emails
}
However in the above design, most likely getNames() and getEmails() will return nil, as the the task will not have updated emails/name by the time it returns.
The alternative design (which I currently implement) is by effectively removing the 'prepareEmails' function and doing everything sequentially in the task functions
func prepareEmails() {
getNames()
}
func getNames() {
...
var task = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
getEmails(names)
})
task.resume()
}
func getEmails(names: NSArray) {
...
var task = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
sendEmails(emails, names)
})
task.resume()
}
Is there a more effective design than the latter? This is my first experience with concurrency, so any advice would be greatly appreciated.
The typical pattern when calling an asynchronous method that has a completionHandler parameter is to use the completionHandler closure pattern, yourself. So the methods don't return anything, but rather call a closure with the returned information as a parameter:
func getNames(completionHandler:(NSArray!)->()) {
....
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {data, response, error -> Void in
let names = ...
completionHandler(names)
}
task.resume()
}
func getEmails(completionHandler:(NSArray!)->()) {
....
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {data, response, error -> Void in
let emails = ...
completionHandler(emails)
}
task.resume()
}
Then, if you need to perform these sequentially, as suggested by your code sample (i.e. if the retrieval of emails was dependent upon the names returned by getNames), you could do something like:
func prepareEmails() {
getNames() { names in
getEmails() {emails in
sendEmails(names, emails) // I'm assuming the names and emails are in the input to this method
}
}
}
Or, if they can run concurrently, then you should do so, as it will be faster. The trick is how to make a third task dependent upon two other asynchronous tasks. The two traditional alternatives include
Wrapping each of these asynchronous tasks in its own asynchronous NSOperation, and then create a third task dependent upon those other two operations. This is probably beyond the scope of the question, but you can refer to the Operation Queue section of the Concurrency Programming Guide or see the Asynchronous vs Synchronous Operations and Subclassing Notes sections of the NSOperation Class Reference.
Use dispatch groups, entering the group before each request, leaving the group within the completion handler of each request, and then adding a dispatch group notification block (called when all of the group "enter" calls are matched by their corresponding "leave" calls):
func prepareEmails() {
let group = dispatch_group_create()
var emails: NSArray!
var names: NSArray!
dispatch_group_enter(group)
getNames() { results in
names = results
dispatch_group_leave(group)
}
dispatch_group_enter(group)
getEmails() {results in
emails = results
dispatch_group_leave(group)
}
dispatch_group_notify(group, dispatch_get_main_queue()) {
if names != nil && emails != nil {
self.sendEmails(names, emails)
} else {
// one or both of those requests failed; tell the user
}
}
}
Frankly, if there's any way to retrieve both the emails and names in a single network request, that's going to be far more efficient. But if you're stuck with two separate requests, you could do something like the above.
Note, I wouldn't generally use NSArray in my Swift code, but rather use an array of String objects (e.g. [String]). Furthermore, I'd put in error handling where I return the nature of the error if either of these fail. But hopefully this illustrates the concepts involved in (a) writing your own methods with completionHandler blocks; and (b) invoking a third bit of code dependent upon the completion of two other asynchronous tasks.
The answers above (particularly Rob's DispatchQueue based answer) describe the concurrency concepts necessary to run two tasks in parallel and then respond to the result. The answers lack error handling for clarity because traditionally, correct solutions to concurrency problems are quite verbose.
Not so with HoneyBee.
HoneyBee.start()
.setErrorHandler(handleErrorFunc)
.branch {
$0.chain(getNames)
+
$0.chain(getEmails)
}
.chain(sendEmails)
This code snippet manages all of the concurrency, routes all errors to handleErrorFunc and looks like the concurrent pattern that is desired.

Resources