How do I dispatch_sync, dispatch_async, dispatch_after, etc in Swift 3, Swift 4, and beyond? - swift

I have lots of code in Swift 2.x (or even 1.x) projects that looks like this:
// Move to a background thread to do some long running work
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
let image = self.loadOrGenerateAnImage()
// Bounce back to the main thread to update the UI
dispatch_async(dispatch_get_main_queue()) {
self.imageView.image = image
}
}
Or stuff like this to delay execution:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
print("test")
}
Or any of all kinds of other uses of the Grand Central Dispatch API...
Now that I've opened my project in Xcode 8 (beta) for Swift 3, I get all kinds of errors. Some of them offer to fix my code, but not all of the fixes produce working code. What do I do about this?

Since the beginning, Swift has provided some facilities for making ObjC and C more Swifty, adding more with each version. Now, in Swift 3, the new "import as member" feature lets frameworks with certain styles of C API -- where you have a data type that works sort of like a class, and a bunch of global functions to work with it -- act more like Swift-native APIs. The data types import as Swift classes, their related global functions import as methods and properties on those classes, and some related things like sets of constants can become subtypes where appropriate.
In Xcode 8 / Swift 3 beta, Apple has applied this feature (along with a few others) to make the Dispatch framework much more Swifty. (And Core Graphics, too.) If you've been following the Swift open-source efforts, this isn't news, but now is the first time it's part of Xcode.
Your first step on moving any project to Swift 3 should be to open it in Xcode 8 and choose Edit > Convert > To Current Swift Syntax... in the menu. This will apply (with your review and approval) all of the changes at once needed for all the renamed APIs and other changes. (Often, a line of code is affected by more than one of these changes at once, so responding to error fix-its individually might not handle everything right.)
The result is that the common pattern for bouncing work to the background and back now looks like this:
// Move to a background thread to do some long running work
DispatchQueue.global(qos: .userInitiated).async {
let image = self.loadOrGenerateAnImage()
// Bounce back to the main thread to update the UI
DispatchQueue.main.async {
self.imageView.image = image
}
}
Note we're using .userInitiated instead of one of the old DISPATCH_QUEUE_PRIORITY constants. Quality of Service (QoS) specifiers were introduced in OS X 10.10 / iOS 8.0, providing a clearer way for the system to prioritize work and deprecating the old priority specifiers. See Apple's docs on background work and energy efficiency for details.
By the way, if you're keeping your own queues to organize work, the way to get one now looks like this (notice that DispatchQueueAttributes is an OptionSet, so you use collection-style literals to combine options):
class Foo {
let queue = DispatchQueue(label: "com.example.my-serial-queue",
attributes: [.serial, .qosUtility])
func doStuff() {
queue.async {
print("Hello World")
}
}
}
Using dispatch_after to do work later? That's a method on queues, too, and it takes a DispatchTime, which has operators for various numeric types so you can just add whole or fractional seconds:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { // in half a second...
print("Are we there yet?")
}
You can find your way around the new Dispatch API by opening its interface in Xcode 8 -- use Open Quickly to find the Dispatch module, or put a symbol (like DispatchQueue) in your Swift project/playground and command-click it, then brouse around the module from there. (You can find the Swift Dispatch API in Apple's spiffy new API Reference website and in-Xcode doc viewer, but it looks like the doc content from the C version hasn't moved into it just yet.)
See the Migration Guide for more tips.

In Xcode 8 beta 4 does not work...
Use:
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
print("Are we there yet?")
}
for async two ways:
DispatchQueue.main.async {
print("Async1")
}
DispatchQueue.main.async( execute: {
print("Async2")
})

This one is good example for Swift 4 about async:
DispatchQueue.global(qos: .background).async {
// Background Thread
DispatchQueue.main.async {
// Run UI Updates or call completion block
}
}

in Xcode 8 use:
DispatchQueue.global(qos: .userInitiated).async { }

Swift 5.2, 4 and later
Main and Background Queues
let main = DispatchQueue.main
let background = DispatchQueue.global()
let helper = DispatchQueue(label: "another_thread")
Working with async and sync threads!
background.async { //async tasks here }
background.sync { //sync tasks here }
Async threads will work along with the main thread.
Sync threads will block the main thread while executing.

Swift 4.1 and 5. We use queues in many places in our code. So, I created Threads class with all queues. If you don't want to use Threads class you can copy the desired queue code from class methods.
class Threads {
static let concurrentQueue = DispatchQueue(label: "AppNameConcurrentQueue", attributes: .concurrent)
static let serialQueue = DispatchQueue(label: "AppNameSerialQueue")
// Main Queue
class func performTaskInMainQueue(task: #escaping ()->()) {
DispatchQueue.main.async {
task()
}
}
// Background Queue
class func performTaskInBackground(task:#escaping () throws -> ()) {
DispatchQueue.global(qos: .background).async {
do {
try task()
} catch let error as NSError {
print("error in background thread:\(error.localizedDescription)")
}
}
}
// Concurrent Queue
class func perfromTaskInConcurrentQueue(task:#escaping () throws -> ()) {
concurrentQueue.async {
do {
try task()
} catch let error as NSError {
print("error in Concurrent Queue:\(error.localizedDescription)")
}
}
}
// Serial Queue
class func perfromTaskInSerialQueue(task:#escaping () throws -> ()) {
serialQueue.async {
do {
try task()
} catch let error as NSError {
print("error in Serial Queue:\(error.localizedDescription)")
}
}
}
// Perform task afterDelay
class func performTaskAfterDealy(_ timeInteval: TimeInterval, _ task:#escaping () -> ()) {
DispatchQueue.main.asyncAfter(deadline: (.now() + timeInteval)) {
task()
}
}
}
Example showing the use of main queue.
override func viewDidLoad() {
super.viewDidLoad()
Threads.performTaskInMainQueue {
//Update UI
}
}

Related

Realm Transaction without notifying tokens

I have Realm notifications on a background thread created with the following code (taken from Realm's website)
class BackgroundWorker: NSObject {
private let name: String
private var thread: Thread!
private var block: (()->Void)!
init(name: String) {
self.name = name
}
#objc internal func runBlock() {
block()
}
internal func start(_ block: #escaping () -> Void) {
self.block = block
if thread == nil {
createThread()
}
perform(
#selector(runBlock),
on: thread,
with: nil,
waitUntilDone: false,
modes: [RunLoop.Mode.default.rawValue]
)
}
private func createThread() {
thread = Thread { [weak self] in
while (self != nil && !self!.thread.isCancelled) {
RunLoop.current.run(
mode: RunLoop.Mode.default,
before: Date.distantFuture)
}
Thread.exit()
}
thread.name = name
thread.start()
}
func stop() {
thread.cancel()
}
}
And using the background worker like this
struct RealmBackGroundWorker {
static var tokens: [NotificationToken] = []
static let backgroundWorker = BackGroundWorker(name: "RealmWorker")
static func start() {
backgroundWorker.start {
self.tokens = ...
}
}
}
The background notifications work great. But I often need to save data to realm without notifying these transactions. From what I have found, it does not look like there is a way write data without notifying all tokens. You always have to specify the tokens you want to ignore.
How can I write data to the Realm without notifying these background tokens?
Let me preface this answer with a couple of things. The Realm website the OP got their code from was here Realm Notifications on Background Threads with Swift and part of the point of that code was to not only spin up a runloop on a background thread to handle Realm functions but to also handle notifications on that same thread.
That code is pretty old - 4+ years and is somewhat outdated. In essence, there are possibly better options. From Apple:
... newer technologies such as Grand Central Dispatch (GCD) provide a
more modern and efficient infrastructure for implementing concurrency
But to address the question, if an observer is added to a Realm results on thread A, then all of the notifications will also occur on thread A. e.g. the token returned from the observe function is tied to that thread.
It appears the OP wants to write data without receiving notifications
I do not want to sync local changes to the server, so I would like to
call .write(withouNotifying: RealmWorkBlock.tokens)
and
I want a way to write data to the realm database without notifying
these notifications.
Noting that those notifications will occur on the same thread as the runloop. Here's the code that we need to look at
static func start() {
backgroundWorker.start {
self.tokens = ...
}
and in particular this line
self.tokens = ...
because the ... is the important part. That ... leads to this line (from the docs)
self?.token = files.observe { changes in
which is where the observer is added that generates the notifications. If no notifications are needed then that code, starting with self?.token can be completely removed as that's is sole purpose - to generate notifications.
One thought is to add a different init to the background worker class to have a background worker with no notifications:
static func start() {
backgroundWorker.startWithoutNotifications()
}
Another thought is to take a more modern approach and leverage DispatchQueue with an autorelease pool which eliminates the need for these classes completely, will run in the background freeing up the UI ad does not involve tokens or notifications.
DispatchQueue(label: "background").async {
autoreleasepool {
let realm = try! Realm()
let files = realm.objects(File.self).filter("localUrl = ''")
}
}

Properly pass Swift closure into another thread

How do I properly (from multithreading point of view) pass a closure to another thread?
Consider a situation:
class NetManager {
...
var processingClosure : (Data, DispatchQueue, #escaping (Data?) -> ()) -> () = {
respData, complQueue, complClosure in
let resultData = // process respData according to some logic and get resultData
complQueue.async {
complClosure(resultData)
}
// PLEASE NOTE that there is no captured variables in this closure
}
...
func requestData1(..., complClosure) {
// this is main thread context
// make request to endpoint 1 somehow and process result in separate processing queue
...
let procClosure = self.processingClosure
// processingQueue is NOT main queue and not completion queue
request.processingQueue.async {
// Question HERE:
procClosure(data, DispatchQueue.main, complClosure)
// is such passing of the closure safe? Can I have issues with concurrency?
}
}
func requestData2(..., complClosure) {
// the same as requestData1 but gets data from endpoint 2
...
let procClosure = self.processingClosure
request.processingQueue.async {
procClosure(data, DispatchQueue.main, complClosure)
}
}
}
This seems a safe way to pass closure since it doesn't capture any variables. Will I have any concurrency issues with procClosure call?
Is there a better way to encapsulate a common functionality of data transformation to reuse in similar requests to different endpoints (I can encapsulate only data processing but not requesting)?

JavacriptCore WebKit EXC_BAD_ACCESS crash after executing callback with data from background thread

I'm currently trying to debug crashes in a JavascriptCore implementation of an interface for native code to perform some work on behalf of the javascript code in the WebView.
The crash sometimes occurs very soon after launching the application, other times it may take a few minutes of executing hundreds of calls to the native code for it to occur.
These are the top two lines for the backtrace of every crash:
(lldb) thread backtrace
* thread #1: tid = 0x37960c, 0x00007fff8de6ecca JavaScriptCore`sanitizeStackForVMImpl + 15, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x700001a2c000)
frame #0: 0x00007fff8de6ecca JavaScriptCore`sanitizeStackForVMImpl + 15
Here is a simplified version of my view controller:
class MyViewController: NSViewController, WebFrameLoadDelegate {
let worker = Worker()
// other setup code...
func webView(webView: WebView!, didCreateJavaScriptContext context: JSContext!, forFrame frame: WebFrame!) {
context.setObject(worker, forKeyedSubscript: "ClientWorker")
}
}
The JSExport protocol itself, and implementation of the code performing work. For testing I removed the actual work and just return a dictionary with dummy data, and the crash still occurs.
#objc protocol WorkerJSExports: JSExport {
func doWork(params: [String:AnyObject], callback: JSValue)
}
#objc class Worker: NSObject, WorkerJSExports {
func doWork(params: [String:AnyObject], callback: JSValue) {
executeBackground(callback) {
return [
"test": "data"
]
}
}
private func executeBackground(callback: JSValue!, f: ()->([String:AnyObject])) {
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)) {
let result = f()
dispatch_sync(dispatch_get_main_queue()) {
self.executeCallback(callback, result: result)
}
}
}
private func executeCallback(callback: JSValue!, result: AnyObject) {
callback.context.evaluateScript("setTimeout").callWithArguments([callback, 0, result])
}
}
The executeBackground and executeCallback methods are helper functions, and executeCallback is making use of setTimeout in response to what I read in this SO post/answer: JavaScriptCore -- Passing a function as a parameter to ObjC and it seems to have resolved other crashes related to locking.
When I swap out executeBackground for the following function that runs just on the main thread, I have not been able to replicate the crash:
private func executeMain(callback: JSValue!, f: ()->([String:AnyObject])) {
dispatch_async(dispatch_get_main_queue()) {
self.executeCallback(callback, result: f())
}
}
It seems that there is some sort of issue that occurs when passing data created in a background thread into the WebView, but after pouring through the documentation I'm uncertain that what could be. The only taboo I found mentioned was passing data between multiple JSVirtualMachine instances, which doesn't seem applicable since I'm only interacting with a single WebView instance. Any assistance in figuring this out is greatly appreciated!
Update
I seem to have solved the issue by switching out the use of Grand Central Dispatch directly for NSOperationQueues. After changing executeBackground to the following, the crashes have not recurred.
private let workerQueue = NSOperationQueue()
private func executeAsync(callback: JSValue!, f: ()->([String:AnyObject])) {
self.workerQueue.addOperationWithBlock({
let result = f()
NSOperationQueue.mainQueue().addOperationWithBlock({
self.executeCallback(callback, result: result)
})
})
}
Though I can't really prove that this has fixed the crash, we've done fairly extensive testing of this functionality and haven't seen it again. The reason I didn't post this an an answer to my own question is that I'm at a loss as to why exactly this is different and/or better. If anyone has insight into what the change to NSOperationQueues may have solved, it would be very much appreciated!

Modifying struct instance variables within a Dispatch closure in Swift

I'm using the DEVELOPMENT-SNAPSHOT-2016-06-06-a version of Swift. I cannot seem to get around this issue, I've tried using #noescape in various places, but I still have the following error:
Closure cannot implicitly capture a mutating self parameter
To better explain, here is a simple example:
public struct ExampleStruct {
let connectQueue = dispatch_queue_create("connectQueue", nil)
var test = 10
mutating func example() {
if let connectQueue = self.connectQueue {
dispatch_sync(connectQueue) {
self.test = 20 // error happens here
}
}
}
}
Something must have changed in these Swift binaries that is now causing my previously working code to break. A workaround I want to avoid is making my struct a class, which does help in fixing the issue. Let me know if there is another way.
I cannot test it, because I'm not using a build with that error, but I'm pretty sure by capturing self explicitly you can fix it:
dispatch_sync(connectQueue) { [self] in
self.test = 20
}
EDIT: Apparently it doesn't work, maybe you can try this (not very nice tbh):
var copy = self
dispatch_sync(connectQueue) {
copy.test = 20
}
self = copy
If you want to read more on why, here is the responsible Swift proposal.
The new dispatch API makes the sync method #noreturn so you wouldn't need the explicit capture:
connectQueue.sync {
test = 20
}
You are using Swift3 since you mentioned a recent dev snapshot of Swift. Try below and let me know if it works:
public struct ExampleStruct {
let connectQueue = DispatchQueue(label: "connectQueue", attributes: .concurrent)//This creates a concurrent Queue
var test = 10
mutating func example() {
connectQueue.sync {
self.test = 20
}
}
}
If you are interested in other types of queues, check these:
let serialQueue = DispatchQueue(label: "YOUR_QUEUE", attributes: .serial)
serialQueue.sync {
//
}
Get the mainQueue asynchronously and synchronously:
DispatchQueue.main.async {
//async operations
}
DispatchQueue.main.sync {
//sync operations
}
And if you are interested in Background:
DispatchQueue.global(attributes: .qosDefault).async {
//async operations
}
You could refer this for new features in Swift3 and for changes to existing version: Migrating to Swift 2.3 or Swift 3 from Swift 2.2

How to use multithread in Swift

I have two tasks : task1 and task2. I want to execute task2 after task1 finishes.
let globalQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)
dispatch_sync(globalQueueDefault){
self.activityIndicatorView.hidden = false
self.activityIndicatorView.startAnimating()
task1()
sleep(6)
dispatch_sync(globalQueueDefault) { () -> Void in
task2()
}
}
I searched in internet, I find NSLock,NSConditionLock and objc_sync_enter...I have try them, but it doesn't work...
let lock = NSLock()
let globalQueueDefault = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)
dispatch_sync(globalQueueDefault){
self.activityIndicatorView.hidden = false
self.activityIndicatorView.startAnimating()
self.lock.lock()
task1()
self.lock.unlock()
sleep(6)
dispatch_sync(globalQueueDefault) { () -> Void in
self.lock.lock()
task2()
self.lock.unlock()
}
}
I also tried NSConditionLock and objc_sync_enter...It doesn't work. How I can use lock in swift ? Could you give me a example base on my code? Thank you.
PS: I don't want to use callback here...because I have tried it, I think multithread is more closer to my answer, Thank you.
I'm going out on a limp and making some guesses about your program structures. The first problem with your code is that it's trying to access a view on a background thread. GUI elements should always be accessed on the main thread. The second problem is sleep: don't use it to write concurrent code. It makes assumptions about how the asynchronous task is going take. You should treat that time as unknown and use a sync pattern or a call back.
Since you mentioned that task1() download JSON, it's likely asynchronous. Here's how I'd do it:
func task1(finish: () -> Void) {
// Set up your connection to the website
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
// Handle the response, parse the json, etc
...
// Now call the completion handler
finish()
}
}
func task2() {
// Do whatever here
}
// In the function that triggers the JSON download
func downloadJSON() {
self.activityIndicatorView.hidden = false
self.activityIndicatorView.startAnimating()
task1(task2)
}