I have a http request, and if I receive the response correctly, then I want to start a timer that fires a function each second. That function is also a http request.
this is my code to fire the timer
if let data = data {
do{
let resultJSON = try NSJSONSerialization.JSONObjectWithData(data, options: [])
requestClient.id = resultJSON["id"] as! Double
self.timerResponse = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: "checkIfThereAreResponeses", userInfo: nil, repeats: true)
self.timerResponse!.fire()
}catch{
}
}
as you see, I'm calling a function called checkIfThereAreResponses
In that function, i have a print statement, and that print statement is being printed just once, though my timer supposed to work each 1 second
what missing i have?
And this is the function
func checkIfThereAreResponeses(){
if (requestClient!.id != nil) {
let url = NSURL(string: "http://localhost:blablabla")
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "POST"
let session = NSURLSession.sharedSession()
task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if let error = error {
print("error = \(error)")
}
if let data = data {
do {
let resultJSONArray = try NSJSONSerialization.JSONObjectWithData(data, options: []) as! NSArray
bla bla bla
}catch {
print("no responses yet = \(error)")
}
}
})
task!.resume()
}else {
print("not yet ")
}
}
the print that i receive JUST ONCE is the no response yet
If you have this code in completion handler, it is recommended NSTimer is running in the main thread so this could be the reason. You might need something like:
dispatch_async(dispatch_get_main_queue(), {
// Your timer logic here
})
Apple docs say:
Timers work in conjunction with run loops. To use a timer effectively,
you should be aware of how run loops operate—see NSRunLoop.
(NSTimer)
The NSRunLoop class is generally not considered to be thread-safe and
its methods should only be called within the context of the current
thread. You should never try to call the methods of an NSRunLoop
object running in a different thread, as doing so might cause
unexpected results.
(NSRunLoop)
An NSTimer works by being scheduled on a runloop. If you call this method in the completionHandler of the session, it will be scheduled to the runloop of the thread that that completionHandler is currently running on. As this thread is not owned by you, but by the system, it will be disposed of by the system. This might be immediately after the completionHandler is done executing, or it might be much later. With the thread, the runloop is gone too, thus, your timer might fire never, a couple of times or just once; there is no telling really as it depends on when the system will remove the thread.
Therefore, you should create the timer on a thread you own, or, easier, dispatch the creation of the timer to the main thread (dispatch_get_main_queue() is your friend). Another option is to create an NSTimer with one of the +timerWithTimeInterval... methods and then add it to the main runloop using [NSRunloop mainRunloop] addTimer: yourTimer forMode NSRunLoopCommonModes]. Remember that the timer will call your selector on the same thread as the the runloop it runs on.
As a side note, polling a server might not be the best way to go on a mobile device. If you control the server, it might be better to send a push notification to the device when there is new data for it. This will save battery on the device, and reduce the load on your server. However, if you do not control the server, it would be more complicated to achieve this, and then polling might be a good compromise.
Related
I need to sync web database in my coredata, for which I perform service api calls. I am using Alamofire with Swift 3. There are 23 api calls, giving nearly 24k rows in different coredata entities.
My problem: These api calls blocks UI for a minute, which is a long time for a user to wait.
I tried using DispatchQueue and performing the task in background thread, though nothing worked. This is how I tried :
let dataQueue = DispatchQueue.init(label: "com.app.dataSyncQueue")
dataQueue.async {
DataSyncController().performStateSyncAPICall()
DataSyncController().performRegionSyncAPICall()
DataSyncController().performStateRegionSyncAPICall()
DataSyncController().performBuildingRegionSyncAPICall()
PriceSyncController().performBasicPriceSyncAPICall()
PriceSyncController().performHeightCostSyncAPICall()
// Apis which will be used in later screens are called in background
self.performSelector(inBackground: #selector(self.performBackgroundTask), with: nil)
}
An API call from DataSyncController:
func performStateSyncAPICall() -> Void {
DataSyncRequestManager.fetchStatesDataWithCompletionBlock {
success, response, error in
self.apiManager.didStatesApiComplete = true
}
}
DataSyncRequestManager Code:
static func fetchStatesDataWithCompletionBlock(block:#escaping requestCompletionBlock) {
if appDelegate.isNetworkAvailable {
Util.setAPIStatus(key: kStateApiStatus, with: kInProgress)
DataSyncingInterface().performStateSyncingWith(request:DataSyncRequest().createStateSyncingRequest() , withCompletionBlock: block)
} else {
//TODO: show network failure error
}
}
DataSyncingInterface Code:
func performStateSyncingWith(request:Request, withCompletionBlock block:#escaping requestCompletionBlock)
{
self.interfaceBlock = block
let apiurl = NetworkHttpClient.getBaseUrl() + request.urlPath!
Alamofire.request(apiurl, parameters: request.getParams(), encoding: URLEncoding.default).responseJSON { response in
guard response.result.isSuccess else {
block(false, "error", nil )
return
}
guard let responseValue = response.result.value else {
block (false, "error", nil)
return
}
block(true, responseValue, nil)
}
}
I know many similar questions have been already posted on Stackoverflow and mostly it is suggested to use GCD or Operation Queue, though trying DispatchQueues didn't work for me.
Am I doing something wrong?
How can I not block UI and perform the api calls simultaneously?
You can do this to run on a background thread:
DispatchQueue.global(qos: .background).async {
// Do any processing you want.
DispatchQueue.main.async {
// Go back to the main thread to update the UI.
}
}
DispatchQueue manages the execution of work items. Each work item submitted to a queue is processed on a pool of threads managed by the system.
I usually use NSOperationQueue with Alamofire, but the concepts are similar. When you set up an async queue, you allow work to be performed independently of the main (UI) thread, so that your app doesn't freeze (refuse user input). The work will still take however long it takes, but your program doesn't block while waiting to finish.
You really have only put one item into the queue.
You are adding to the queue only once, so all those "perform" calls wait for the previous one to finish. If it is safe to run them concurrently, you need to add each of them to the queue separately. There's more than one way to do this, but the bottom line is each time you call .async {} you are adding one item to the queue.
dataQueue.async {
DataSyncController().performStateSyncAPICall()
}
dataQueue.async {
DataSyncController(). performRegionSyncAPICall l()
}
I'm having troubles with Alamofire using Operation and OperationQueue.
I have an OperationQueue named NetworkingQueue and I push some operation (wrapping AlamofireRequest) into it, everything works fine, but during application living, at one moment all Alamofire request are not sent. My queue is getting bigger and bigger and no request go to the end.
I do not have a scheme to reproduce it anytime.
Does anybody have a clue for helping me?
Here is a sample of code
The BackgroundAlamoSession
let configuration = URLSessionConfiguration.background(withIdentifier: "[...].background")
self.networkingSessionManager = Alamofire.SessionManager(configuration: configuration)
AbstractOperation.swift
import UIKit
import XCGLogger
class AbstractOperation:Operation {
private let _LOGGER:XCGLogger = XCGLogger.default
enum State:String {
case Ready = "ready"
case Executing = "executing"
case Finished = "finished"
var keyPath: String {
get{
return "is" + self.rawValue.capitalized
}
}
}
override var isAsynchronous:Bool {
get{
return true
}
}
var state = State.Ready {
willSet {
willChangeValue(forKey: self.state.rawValue)
willChangeValue(forKey: self.state.keyPath)
willChangeValue(forKey: newValue.rawValue)
willChangeValue(forKey: newValue.keyPath)
}
didSet {
didChangeValue(forKey: oldValue.rawValue)
didChangeValue(forKey: oldValue.keyPath)
didChangeValue(forKey: self.state.rawValue)
didChangeValue(forKey: self.state.keyPath)
}
}
override var isExecuting: Bool {
return state == .Executing
}
override var isFinished:Bool {
return state == .Finished
}
}
A concrete Operation implementation
import UIKit
import XCGLogger
import SwiftyJSON
class FetchObject: AbstractOperation {
public let _LOGGER:XCGLogger = XCGLogger.default
private let _objectId:Int
private let _force:Bool
public var object:ObjectModel?
init(_ objectId:Int, force:Bool) {
self._objectId = objectId
self._force = force
}
convenience init(_ objectId:Int) {
self.init(objectId, force:false)
}
override var desc:String {
get{
return "FetchObject(\(self._objectId))"
}
}
public override func start(){
self.state = .Executing
_LOGGER.verbose("Fetch object operation start")
if !self._force {
let objectInCache:objectModel? = Application.main.collections.availableObjectModels[self._objectId]
if let objectInCache = objectInCache {
_LOGGER.verbose("object with id \(self._objectId) founded on cache")
self.object = objectInCache
self._LOGGER.verbose("Fetch object operation end : success")
self.state = .Finished
return
}
}
if !self.isCancelled {
let url = "[...]\(self._objectId)"
_LOGGER.verbose("Requesting object with id \(self._objectId) on server")
Application.main.networkingSessionManager.request(url, method : .get)
.validate()
.responseJSON(
completionHandler: { response in
switch response.result {
case .success:
guard let raw:Any = response.result.value else {
self._LOGGER.error("Error while fetching json programm : Empty response")
self._LOGGER.verbose("Fetch object operation end : error")
self.state = .Finished
return
}
let data:JSON = JSON(raw)
self._LOGGER.verbose("Received object from server \(data["bId"])")
self.object = ObjectModel(objectId:data["oId"].intValue,data:data)
Application.main.collections.availableobjectModels[self.object!.objectId] = self.object
self._LOGGER.verbose("Fetch object operation end : success")
self.state = .Finished
break
case .failure(let error):
self._LOGGER.error("Error while fetching json program \(error)")
self._LOGGER.verbose("Fetch object operation end : error")
self.state = .Finished
break
}
})
} else {
self._LOGGER.verbose("Fetch object operation end : cancel")
self.state = .Finished
}
}
}
The NetworkQueue
class MyQueue {
public static let networkQueue:SaootiQueue = SaootiQueue(name:"NetworkQueue", concurent:true)
}
How I use it in another operation and wait for for result
let getObjectOperation:FetchObject = FetchObject(30)
SaootiQueue.networkQueue.addOperations([getObjectOperation], waitUntilFinished: true)
How I use it the main operation using KVO
let getObjectOperation:FetchObject = FetchObject(30)
operation.addObserver(self, forKeyPath: #keyPath(Operation.isFinished), options: [.new], context: nil)
operation.addObserver(self, forKeyPath: #keyPath(Operation.isCancelled), options: [.new], context: nil)
queue.addOperation(operation)
//[...]
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let operation = object as? FetchObject {
operation.removeObserver(self, forKeyPath: #keyPath(Operation.isFinished))
operation.removeObserver(self, forKeyPath: #keyPath(Operation.isCancelled))
if keyPath == #keyPath(Operation.isFinished) {
//Do something
}
}
A few clarifications:
My application is a radio player and I need, while playing music and the background, to fetch the currently playing program. This is why I need background Session.
In fact I also use the background session for all the networking I do when the app is foreground. Should I avoid that ?
The wait I'm using is from another queue and is never used in the main queue (I know it is a threading antipattern and I take care of it).
In fact it is used when I do two networking operation and the second one depends of the result of the second. I put a wait after the first operation to avoid KVO observing. Should I avoid that ?
Additional edit:
When I say "My queue is getting bigger and bigger and no request go to the end", it means that at one moment during application livecycle, random for the moment (I can not find a way to reproduce it at every time), Alamofire request don't reach the response method.
Because of that the Operation wrapper don't end and the queue is growing.
By the way I'm working on converting Alamofire request into URLRequest for having clues and I founded some problem on using the main queue. I have to sort what is due to the fact that Alamofire use the main queue for reponse method and I'll see if I find a potential deadlock
I'll keep you informed. Thanks
There are minor issues, but this operation implementation looks largely correct. Sure, you should make your state management thread-safe, and there are other stylistic improvements you could make, but I don't think this is critical to your question.
What looks worrisome is addOperations(_:waitUntilFinished:). From which queue are you waiting? If you do that from the main queue, you will deadlock (i.e. it will look like the Alamofire requests never finish). Alamofire uses the main queue for its completion handlers (unless you override the queue parameter of responseJSON), but if you're waiting on the main thread, this can never take place. (As an aside, if you can refactor so you never explicitly "wait" for operations, that not only avoids the deadlock risk, but is a better pattern in general.)
I also notice that you're using Alamofire requests wrapped in operations in conjunction with a background session. Background sessions are antithetical to operations and completion handler closure patterns. Background sessions continue after your app has been jettisoned and you have to rely solely upon the SessionDelegate closures that you set when you first configure your SessionManager when the app starts. When the app restarts, your operations and completion handler closures are long gone.
Bottom line, do you really need background session (i.e. uploads and downloads that continue after your app terminates)? If so, you may want to lose this completion handler and operation based approach. If you don't need this to continue after the app terminates, don't use background sessions. Configuring Alamofire to properly handle background sessions is a non-trivial exercise, so only do so if you absolutely need to. Remember to not conflate background sessions and the simple asynchronous processing that Alamofire (and URLSession) do automatically for you.
You asked:
My application is a radio player and I need, while playing music and the background, to fetch the currently playing program. This is why I need background Session.
You need background sessions if you want downloads to proceed while the app is not running. If your app is running in the background, though, playing music, you probably don't need background sessions. But, if the user chooses to download a particular media asset, you may well want background session so that the download proceeds when the user leaves the app, whether the app is playing music or not.
In fact I also use the background session for all the networking I do when the app is foreground. Should I avoid that ?
It's fine. It's a little slower, IIRC, but it's fine.
The problem isn't that you're using background session, but that you're doing it wrong. The operation-based wrapping of Alamofire doesn't make sense with a background session. For sessions to proceed in the background, you are constrained as to how you use URLSession, namely:
You cannot use data tasks while the app is not running; only upload and download tasks.
You cannot rely upon completion handler closures (because the entire purpose of background sessions is to keep them running when your app terminates and then fire up your app again when they're done; but if the app was terminated, your closures are all gone).
You have to use delegate based API only for background sessions, not completion handlers.
You have to implement the app delegate method to capture the system provided completion handler that you call when you're done processing background session delegate calls. You have to call that when your URLSession tells you that it's done processing all the background delegate methods.
All of this is a significant burden, IMHO. Given that the system is keeping you app alive for background music, you might contemplate using a standard URLSessionConfiguration. If you're going to use background session, you might need to refactor all of this completion handler-based code.
The wait I'm using is from another queue and is never used in the main queue (I know it is a threading antipattern and I take care of it).
Good. There's still serious code smell from ever using "wait", but if you are 100% confident that it's not deadlocking here, you can get away with it. But it's something you really should check (e.g. put some logging statement after the "wait" and make sure you're getting past that line, if you haven't already confirmed this).
In fact it is used when I do two networking operation and the second one depends of the result of the second. I put a wait after the first operation to avoid KVO observing. Should I avoid that ?
Personally, I'd lose that KVO observing and just establish addDependency between the operations. Also, if you get rid of that KVO observing, you can get rid of your double KVO notification process. But I don't think this KVO stuff is the root of the problem, so maybe you defer that.
In my app I need to send some instructions to server when the user terminated an app. In applicationWillTerminate func I tried to send it, but it never came to server. I tried to use Alamofire and native URLSession but it doesn't work. Does anybody know how can I send it?
I use this code
let request = "\(requestPrefix)setDriverOrderStatus"
if let url = URL(string:request) {
var parameters : [String : String] = [:]
parameters["access_token"] = UserSession.accessToken
parameters["driver_id"] = UserSession.userID
parameters["status"] = status
var req = URLRequest(url: url)
req.httpMethod = HTTPMethod.put.rawValue
do {
req.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
}
_ = URLSession.shared.dataTask(with: req, completionHandler: { data, response, error in
guard error == nil else {
print(error ?? "error")
return
}
guard let data = data else {
print("Data is empty")
return
}
let json = try! JSONSerialization.jsonObject(with: data, options: [])
print(json)
}).resume
}
One solution that worked for me is to add sleep at the end of the applicationWillTerminate function like this :
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
// HERE YOU will make you HTTP request asynchronously
self.postLogoutHistory()
// 3 is the number of seconds in which you estimate your request
// will be finished before system terminate the app process
sleep(3)
print("applicationWillTerminate")
// self.saveContext()
}
put breakpoint in applicationWillTerminate and check that, function is getting called or not because applicationWillTerminate is not called everytime when application is getting terminated, especially when user quit application manually from multitasking window, applicationWillTerminate will not get called! When system terminates the application at that time applicationWillTerminate will get called and you will got approximately five seconds to complete your task!! So, it is not good idea to perform network related task on applicationWillTerminate!!
Refer Apple Documentation for applicationWillTerminate, It states,
This method lets your app know that it is about to be terminated and
purged from memory entirely. You should use this method to perform any
final clean-up tasks for your app, such as freeing shared resources,
saving user data, and invalidating timers. Your implementation of this
method has approximately five seconds to perform any tasks and return.
If the method does not return before time expires, the system may kill
the process altogether.
For apps that do not support background execution or are linked
against iOS 3.x or earlier, this method is always called when the user
quits the app. For apps that support background execution, this method
is generally not called when the user quits the app because the app
simply moves to the background in that case. However, this method may
be called in situations where the app is running in the background
(not suspended) and the system needs to terminate it for some reason.
After calling this method, the app also posts a
UIApplicationWillTerminate notification to give interested objects a
chance to respond to the transition.
How to cancel Alamofire request if the downloaded file is already exists in documents folder?
Here is the code for request:
Alamofire.download(.GET, fileUrls[button.tag], destination: { (temporaryURL, response) in
if let directoryURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] as? NSURL {
let fileURL = directoryURL.URLByAppendingPathComponent(response.suggestedFilename!)
self.localFilePaths[button.tag] = fileURL
if NSFileManager.defaultManager().fileExistsAtPath(fileURL.path!) {
NSFileManager.defaultManager().removeItemAtPath(fileURL.path!, error: nil)
}
return fileURL
}
println("temporaryURL - \(temporaryURL)")
self.localFilePaths[button.tag] = temporaryURL
return temporaryURL
}).progress { _, totalBytesRead, totalBytesExpectedToRead in
println("\(totalBytesRead) - \(totalBytesExpectedToRead)")
dispatch_async(dispatch_get_main_queue()) {
self.progressBar.setProgress(Float(totalBytesRead) / Float(totalBytesExpectedToRead), animated: true)
if totalBytesRead == totalBytesExpectedToRead {
self.progressBar.hidden = true
self.progressBar.setProgress(0, animated: false)
}
}
}.response { (_, _, data, error) in
let previewQL = QLReaderViewController()
previewQL.dataSource = self
previewQL.currentPreviewItemIndex = button.tag
self.navigationController?.pushViewController(previewQL, animated: true)
}
I've also tried to create a request variable var request: Alamofire.Request? and then cancel request?.cancel() it if that file exists but it doesn't work.
Can someone help me to solve this issue?
Rather than cancelling the request, IMO you shouldn't make it in the first place. You should do the file check BEFORE you start the Alamofire request.
If you absolutely feel you need to start the request, you can always cancel immediately after starting the request.
var shouldCancel = false
let request = Alamofire.request(.GET, "some_url") { _, _ in
shouldCancel = true
}
.progress { _, _, _ in
// todo...
}
.response { _, _, _ in
// todo...
}
if shouldCancel {
request.cancel()
}
TL; DR: Canceling a request is a bit cumbersome in many cases. Even Alamofire, as far as I know, does not guarentee that request will be cancelled upon your request, immediately. However, you may use dispatch_suspend or NSOperation in order to overcome this.
Grand Central Dispatch (GCD)
This way utilizes functional programming.
Here we enlight our way with low-level programming. Apple introduced a good library, aka GCD, to do some thread-level programming.
You cannot cancel a block, unless... you suspend a queue (if it is not main or global queue).
There is a C function called dispatch_suspend, (from Apple's GCD Reference)
void dispatch_suspend(dispatch_object_t object);
Suspends the invocation of block objects on a dispatch object.
And you can also create queues (who are dispatch_object_ts) with dispatch_queue_create.
So you can do your task in user created queue, and you may suspend this queue in order to prevent CPU from doing something unnecessary.
NSOperation (also NSThread)
This way utilizes functional programming over object-oriented interface.
Apple also introduced NSOperation, where object-oriented programming may be object, whereas it is easier to cope with.
NSOperation is an abstract class, which associates code and data, according to the Apple's documentation.
In order to use this class, you should either use one of its defined subclasses, or create your own subclass: In your case particularly, I suppose NSBlockOperation is the one.
You may refer to this code block:
let block = NSBlockOperation { () -> Void in
// do something here...
}
// Cancel operation
block.cancel()
Nevertheless, it also does not guarantee stopping from whatever it is doing. Apple also states that:
This method does not force your operation code to stop. Instead, it updates the object’s internal flags to reflect the change in state. If the operation has already finished executing, this method has no effect. Canceling an operation that is currently in an operation queue, but not yet executing, makes it possible to remove the operation from the queue sooner than usual.
If you want to take advantage of flags, you should read more: Responding to the Cancel Command
I am self taught Swift user and trying to do something simple but it's got me pretty stumped. I have a simple registration form. After submitting the items for registration, I want to move the page to a "how it works" page via a segue, but ONLY when my restful API returns success. Here's what I have so far; feel free to send me a better way to do this as well. All criticisms are welcome.
let myUrl = NSURL(string:"http://www.example.com/scripts/Register.php")
let request = NSMutableURLRequest(URL: myUrl!)
request.HTTPMethod = "POST"
let postString = "email=\(email)&password=\(pass)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request){
data, response, error in
if (error != nil) {
println("Error: \(error)")
return
}
var err: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: &err) as? NSDictionary
var showTutorial : Bool = false
if let parseJSON = json {
var returnValue = parseJSON["status"] as? String
println("Status: \(returnValue)")
var isUserRegistered: Bool = false
if (returnValue == "Success") {
showTutorial = true
} else if (returnValue == "Error") {
// handle error
}
}
// if successful registration, show how it works page
if (showTutorial) {
self.performSegueWithIdentifier("howItWorksSegue", sender: self)
}
}
task.resume()
I have a segue named howItWorksSegue attached to this view controller going to the HowItWorksViewController. I'm receiving this error from Xcode:
2015-10-12 21:22:43.261 ZiftDine[11396:2307755] Assertion failure in -[UIKeyboardTaskQueue waitUntilAllTasksAreFinished], /SourceCache/UIKit_Sim/UIKit-3347.44.2/Keyboard/UIKeyboardTaskQueue.m:374
2015-10-12 21:22:43.391 ZiftDine[11396:2307755] Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UIKeyboardTaskQueue waitUntilAllTasksAreFinished] may only be called from the main thread.'
Anything done with UI should be done on the main thread, try wrapping you performSegue call like this:
dispatch_async(dispatch_get_main_queue(),{
self.performSegueWithIdentifier("howItWorksSegue", sender: self)
})
#Swinny89 gave the solution to your problem but some explanation is in order.
If you read the description of dataTaskWithRequest:completionHandler:, which is the method you are using (although your Swift code uses trailing closure syntax to drop the completionHandler label and put the closure outside the parentheses) it says:
completionHandler: The completion handler to call when the load
request is complete. This handler is executed on the delegate queue.
Then if you read the description of the init method sessionWithConfiguration:delegate:delegateQueue: it says:
queue: A queue for scheduling the delegate calls and completion
handlers. If nil, the session creates a serial operation queue for
performing all delegate method calls and completion handler calls.
Serial operation queues run on a different thread.
So, taking all of those pieces of information together, it means that your completion closure is going to be executed on a thread other than the main thread.
A cardinal rule of iOS/Mac development is that you must do all UI calls from the main thread. If a call changes anything on the screen, it's a UI call.
Your code is invoking performSegueWithIdentifier: from a background thread. It changes what's displayed on the screen, so it must be a UI call. Therefore it needs to be run on the main thread.
The GCD function dispatch_async(), with a queue of dispatch_get_main_queue(), submits a closure to be run on the main dispatch queue, a queue that runs on the main thread.
So Swinny's solution fixes your problem.
The take-away here:
Any time you are running code in a closure, stop and think: "Am I positive that this closure will always be run on the main thread?" If the answer is no, enclose the code in a call to dispatch_async(dispatch_get_main_queue(), like Swinny's answer.
The answers by #Duncan C and #Swinny89 are good. For anyone coming in from Google, the syntax in Swift 3 has changed a little:
DispatchQueue.main.async(execute: {
self.performSegueWithIdentifier("howItWorksSegue", sender: self)
})