I'm using swift perfect 2.0 and i need to call a function after 10 seconds. I can make it work on a normal iOS application with this code:
let when = DispatchTime.now() + 10
DispatchQueue.main.asyncAfter(deadline: when){
//call function
}
But i can't do this in swift perfect, and i don't know how to work arround. This is the structure of my request:
public func InsertPost(_ request: HTTPRequest, response: HTTPResponse)
//logic
response.status = .custom(code: 200, message: "Success!")
response.completed()
//here i want to send a notification to some users, but only after 10 seconds.
//So i try to call function sendNotifications() like this:
let when = DispatchTime.now() + 10
DispatchQueue.main.asyncAfter(deadline: when){
sendNotifications()
}
{
It never calls sendNotifications(), even if i place it before response.completed(), and i'm probably thinking wrong. So my question is, is there any other way to use Dispatchqueues in perfect 2.0? They don't seem to work.
Ok, now i understand. I can't block main queue in swift perfect.
solution:
let backgroundQueue = DispatchQueue(label: "com.app.queue", qos: .background, target: nil)
let when = DispatchTime.now() + 10
backgroundQueue.asyncAfter(deadline: when){
//call function
}
It looks like perfect 2.0 has it's own thread management setup. Checkout this link:
http://perfect.org/docs/thread.html
Related
Once my background operation completed i need to call handleError function. Since isToast, errorMessage are published variables i need to put in main thread. I wrote a func for test test__Failure() but before simulateRequestFailure complete, this line is executed in function XCTAssertTrue(self.viewModel.isToast). How to put wait, delay for few seconds
#Published var isToast: Bool = false
#Published var eMessage: String = ""
func handleError() {
DispatchQueue.main.async {
self.isToast = true
self.eMessage = “Test message”
}
}
func test__Failure() {
// Some simulate response which call handleError
self.simulateRequestFailure()
XCTAssertTrue(self.vm.isToast)
}
You can delay verification and check it on main thread as well, something like this:
let expectation = XCTestExpectation()
self.simulateRequestFailure()
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
XCTAssertTrue(self.vm.isToast)
expectation.fulfill()
}
wait(for: [expectation], timeout: 10.0)
What this does:
Expectation allows to synchronize test thread with main thread. I.e. test will not complete until either expectation.fulfill() happens, or 10 seconds expire (you can of course change 10 sec to whatever)
simulateRequestFailure() runs on main thread asynchronously, so we let it run and schedule verification on the same thread, but a bit delayed (by 1 sec, but you can change it to whatever makes sense)
I am hitting first api which gives response in 60 seconds wherein second api needs to hit after stopping/cancelling first api request/response and get second api response immediately. But in my case, first api is cancelled and seconds api response not able to get.
I have even tried with Alamofire and default URLSession as well for the same.
You can use DispatchWorkItem and perform debouncing, this will help cancel your first api if second needs to be hit.
private var pendingRequestWorkItem : DispatchWorkItem? // your work item
//function which calls your API being hit
func getData() {
// cancels the first api or the the api currently being hit
self.pendingRequestWorkItem?.cancel()
let requestWorkItem = DispatchWorkItem { [weak self] in
self?.getDataFromAPI()
}
self.pendingRequestWorkItem = requestWorkItem
//this debounces for 0.5 secs, you can configure it as per your requirement(debouncing time you want)
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500), execute: requestWorkItem)
}
func getDataFromAPI() {
//your function which hits the API
}
Hope this helps.
I want to show an activity indicator for my network request if its taking a long time to complete.
Is there a way I can start the indicator after a certain number of seconds have passed to allow a request to complete? I dont want it to flash up instantly as that makes for a choppy UI/UX on fast requests, but cant see the best way to present it on longer requests?
I was considering using:
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0, execute: {}
however this would cause the timer to display even if the request was completed within 3 seconds...
Use DispatchWorkItem:
fileprivate var indicatorTask: DispatchWorkItem?
to run it after those 3 seconds:
func prepareActivityIndicator() {
indicatorTask?.cancel()
indicatorTask = DispatchWorkItem {
// show indicator
}
DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: indicatorTask!)
}
Since you have a reference to indicatorTask you can cancel it anytime.
The quickest way is to use a Timer object. When the request completes, just call myTimer.invalidate
let timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(showIndicator), userInfo:nil, repeats:false)
I do push notifications. If there is no data in the database, they downloaded before display. I did a time interval using Grand Central Dispatch, but it is not right because for example if the user is a weak Internet application falls.
How delay with barrier of GCD the display of the data before download data?
This code with time interval:
if equal == false {
let url = "https://****.**/json/post/\(data)"
self.download.getLastestNews(url)
}
let when = DispatchTime.now() + 2
DispatchQueue.main.asyncAfter(deadline: when) {
let newsCategory = self.realm.objects(News.self).filter("newsID == \(self.id)")
vc.titleText = (newsCategory.first?.newsTitle)!
vc.fullText = (newsCategory.first?.newsFullText)!
vc.imageLink = (newsCategory.first?.newsImage)!
if let tabBarController = self.window?.rootViewController {
tabBarController.present(vc, animated: true, completion: nil)
}
}
Platform iOS 9.*, iOS 3
Delaying the queue won't always work because as you said, sometimes Internet connection is too slow and there might be no data received, so the program may fail with error. While downloading anything from Internet, check for completion handling methods. Inside them use the code you gave in DispatchQueue closure.
I am toying around with Swift 3, and dispatch_async was changed.
Previously I used :
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
// Do stuff
dispatch_async(dispatch_get_main_queue()) {
// Do stuff
}
}
And in Swift 3 I use:
let qos = DispatchQoS.init(qosClass: .userInteractive, relativePriority: 0)
DispatchQueue.global(qos: qos.qosClass).async {
// Do stuff
DispatchQueue.main.async {
// Do stuff
}
}
I have the feeling it is allot slower than how it worked in Swift 2.
Some tasks take up to 7 seconds, previously it took only 1.
For example counting all the photos of the users device
In the docs, qosClass .userInteractive is the fasted way to excecute a task.
(table 4.1 on https://developer.apple.com/library/content/documentation/Performance/Conceptual/EnergyGuide-iOS/PrioritizeWorkWithQoS.html#//apple_ref/doc/uid/TP40015243-CH39-SW9)
Am I doing something wrong? Or is it just... slower?
TLDR;
Swift 3 async task slower?