Measuring total response time for NSURLSession's dataTaskWithRequest - swift

When using NSURLSession's dataTaskWithRequest how can total response time be measured in the event that many NSURLSessionDataTask are created and will not necessarily be executed immediately? Storing a starting time and calculating the difference within the block doesn't account for time that a task may have been waiting for an available thread. IE:
let startTime = NSDate();
let task = session.dataTaskWithRequest(request) { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
let responseTime = NSDate().timeIntervalSinceDate(startTime);
}

I think you can probably do this with a fairly trivial custom NSURLProtocol.
In your canInitWithRequest: method, call the setProperty:forKey:inRequest: method on NSURLProtocol to set a custom start time key for the request. Then reject the request (by returning NO) and allow the normal HTTP protocol to handle the request normally.
When you get a response, call property:forKey:inRequest: to get the start time.
With that said, there's no guarantee that the request really will start immediately after the call to canInitWithRequest:, and because there's no good way to subclass the original HTTP protocol handler class (to wrap the startLoading method), this may or may not be precise enough. I'm not sure.
So if that doesn't work, then to improve the accuracy, you would have to create a full-blown protocol that:
Returns YES in canInitWithRequest: (except if the start time has already been set for that request)
Creates a new URL session in its startLoading method (to ensure that the request will start instantly)
Adds the start time to the request
Begins the request in that new session
But even that will not necessarily give you precise timing if the request is happening while the app is in the background or if the discretionary flag is set on the session configuration. That approach also won't work very easily if you're using more than one session with different configurations. It is, however, probably the best you can do in terms of accuracy, because I doubt that there's any way to swizzle the built-in HTTP protocol class (if such a class even exists).

Related

AKMetronome countdown, how to publish to main thread from background thread to start recorder?

I've been looking into how to implement a Metronome countdown with the AudioKit lib but ran into an issue related to threads and my implementation.
My implementation uses the AudioKit AKMetronome, a method to handle the metronome.start and the metronome.callback handler to start recording after a given number of bars are completed.
init () {
metronome.callback = handler
}
func record () {
self.metronome.start()
}
The handler computes the metronome position in time and if the desired number of bars completes (countdown), the recorder starts.
Unfortunately, running the .record of AKNodeRecorder in the .callback handler of AKMetronome causes the warning message:
Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.
For that reason, the call to start recording in the metronome.callback handler is passed to the main thread through the GCD API:
DispatchQueue.main.sync {
do {
try self.recorder.record()
} catch {
print("[ERROR] Oops! Failed to record...")
}
}
I've used the .sync blocking method to resolve the computation immediately as possible since
timing is critical in audio applications (the call should be executed in a real-time thread, ideally); Is my understanding that the GCP API main thread provides the highest priority, but I'm not certain if that's the best option for a time-sensitive application?
Would like to get some feedback or direction if possible from other users, thank you!
OK, so the actual question has nothing to do with metronomes or countdowns? What you really want to know is: will using sync from a background thread get me onto the main thread faster?
If so: Basically no. This is not what sync is for or what it means. The async/sync difference has absolutely no effect on speed. If you get onto the main thread using async, you will get on as soon it is free, so you gain nothing whatever by saying sync.
Moreover, if you already arrived into the callback in an async background thread, any damage that can be done is already done; your timing is now inexact and there is absolutely nothing you can do about it unless you happen to have a time machine in your pocket.
Following up on #matt advice regarding calls inside callbacks, the approach I took change based in a .scheduledTimer of Timer.
func record () {
metronome.restart()
Timer.scheduledTimer(withTimeInterval: self.barLength, repeats: false) { _ in
self.startRecording()
print("Timer fired!")
}
}
The record is a handler that initiates the AKMetronome, opted to use .restart so that every time's requested starts from the very start.
A property .barLength holds the value for the computed length of the desired countdown. The .startRecording method that is called, is a handler that takes care of AKRecorder .record call.
Future reader, have in mind that Timer is not meant to be accurate according to (Accuracy of NSTimer) but
unfortunately, this is the best approach I've tested and I found so far.

Setting timeoutInterval on Alamofire DataRequest

I'm making a custom class that takes an Alamofire DataRequest in it's initializer. Now I want to add a timeoutInterval to it, but I'm getting the compiling error
Value of type 'DataRequest' has no member 'timeoutInterval'
Here is the code:
init(request: DataRequest, timeoutInterval: Double = 10) {
request.timeoutInterval = timeoutInterval // <- compile error here
self.request = request
}
Obviously Alamofire DataRequest doesn't have that property. But is there any other way to specify the timeout to a request in this way (without using a SessionManager preferable)? URLRequest has it, so it should be possible somehow, but I cannot figure out how.
I know that this question has been asked before here on Stack Overflow, but I cannot find any answer that fits this situation.
In Alamofire 5.1 we've added a requestModifier parameter to the top level request methods that gives you access to the URLRequest that will be performed.
In Alamofire 4 you have a few, less elegant options. One straightforward way to set it is to use a RequestAdapter that will set it as part of the request pipeline. Another, more involved option, is to move from the top level request method that takes individual parameters, such as headers, to the API that takes a URLRequestConvertible value. That way you have full control over the URLRequests that are performed by Alamofire on your behalf.

CoreData - Fetch NSManagedObject with perform and background thread

I'm developing an SDK that uses only 1 NSManagedObjectContext with type of privateQueueConcurrencyType.
In order to fetch objects, i'm using perform() and then i pass the results to a closure.
I'm calling this method from a background thread and also use the result on a background thread (which might be different than the one that called it).
I know that passing objects between threads is a no-go, but i'm not satisfied with the way i handle it today.
The way I handle it, is that every NSManagedObject is mapped to "normal" Swift object and then i use the swift object.
For example:
Foreach NSManagedObject from the results, i create new Object (which is not NSManagedObject) and then i use these objects.
I would like to use the NSManagedObjects instead of creating new ones that holds similar data.
What's the best approach to do it?
Can I still use the NSManagedObject?
func getRecordsByPredicate<T: NSManagedObject>(type: T.Type, predicate: NSPredicate, success: #escaping (_ record: [T]?) -> Void, failure: #escaping () -> Void) {
self.context.perform {
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: String(describing: type.self))
fetchRequest.includesPropertyValues = false
fetchRequest.predicate = predicate
do {
let results = try context.fetch(fetchRequest)
success(results as? [T])
} catch {
print(error.localizedDescription)
failure()
}
}
}
Providing an API with CoreData involved is difficult, at best.
You can not expose only the managed object in the API, since these are tied to a specific thread or dispatch queue which is private to your library. You would require the client to pass a Managed Object Context as well which defines the execution context where the client will use the managed object.
If your internal MOC and the client's MOC is not the same, the API inevitably becomes asynchronous - or it will block a thread.
You may require that this API can be used on the main thread only and your library takes care to use the same MOC as well. This of course has a couple of drawbacks, possibly making the API asynchronous would be only one of it.
Since you also cannot force to make the developer read your documentation, the first developer using your API will likely not call it from the main thread. ;)
Another alternative would be to let the client pass a closure to the API instead, which is then called from your library on the right execution context. This also makes the API asynchronous, and also requires the developer a deep understanding of CoreData as well, since she gets CoreData managed objects.
Your first approach using "Swift" values is probably the best approach to handle this. Make CoreData an "implementation detail" of your library and save the developers the hassles involved when using CoreData.

Swift - Application crash when using two different OperationQueues with KVO

I'm getting two type of information with JSON and I'm adding "operations" to 2 different Operation Queues Classes with addObserver(forKeyPath:"operations"...).
In the function observeValue I'm checking if operationQueue1.operations.isEmpty and then I refresh my information in UI. I'm doing the same thing with if else with operationQueue2, but when the 2 operations are started in sometime the application crash with error message: *** Terminating app due to uncaught exception 'NSRangeException', reason: 'Cannot remove an observer <AppName.ViewController 0x102977800> for the key path "operations" from <AppName.OperationQueue1 0x1c4a233c0> because it is not registered as an observer.'
I don't have problem when only 1 operation is started. Any suggestions?
func getInfo1(){//runned in viewDidLoad
operationQueue1.addObserver(forKeyPath:"operations"...)
operationQueue1.dataTask(URL:"..."....){
DispatchQueue.main.async{
NotificationCenter.default.postNotification(NSNotification.Name(rawValue: "NewDataReceived1", userInfo:infoFromTheWebsite)
}
}
}
func NewDataReceived1(){
here I add the information to arrays to be loaded in tableView1
}
HERE IS THE CODE FOR 2ND INFO WHICH IS THE SAME
override func observeValue(forKeyPath keyPath: String?, ....){
if(object as? operationQueue1 == operationQueue1Class && keyPath == "operations" && context == context1){
if(operationQueue1.operations.isEmpty){
DispatchQueue.main.async{
operationQueue1..removeObserver(self, forKeyPath:"operations")
Timer.scheduled("refreshingTableInformation1")
}
}
}else if(operationQueue2....){
SAME AS OPERATION 1, BUT USING DIFFERENT FUNC TO REFRESH TABLE INFORMATION AND THE TABLES ARE DIFFERENT
}else{
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
func refreshingTableInformation1(){
tableView1.reloadData()
Timer.scheduled("getInfo1", repeat:false)
}
func refreshingTableInformation2(){
tableView2.reloadData()
Timer.scheduled("getInfo2", repeat:false)
}
Sometimes it works 10 secs and crash and sometimes works for more than 60 seconds and then crash...
Your code here is vulnerable to race conditions. Consider the following scenario:
getInfo1() is called, which adds an operation to operationQueue1.
The operation completes, which means your KVO observation is called. The queue is now empty, so your observation schedules removal of your observer on the main dispatch queue.
Now, before the operation you've submitted to the main queue is able to run, something else calls getInfo1(), which adds a new operation to operationQueue1, which completes before the operation you queued in step 2 has had the chance to run (hey, maybe the main queue was busy with something; it's easy for this to happen since it's a serial queue).
Your observation for the first call of getInfo1() is called again while the queue is empty, causing another deregister block to be submitted to the main queue.
The two deregister blocks finally get to execute on the main queue. The second one crashes the program, since you've already deregistered your observer.
You could probably fix this problem (assuming the code doesn't have more problems of this nature) by using Swift 4's block-based observers instead, and setting the observer to nil instead of explicitly deregistering it. However, I propose that KVO is the wrong tool for what you're trying to do. As the instructions for the old "Crystal Quest" game used to say, it's a bit like using an anti-aircraft gun to kill a mosquito.
From what I can see from the code above, it looks like you're using KVO just to schedule a notification for when an operation or a group of operations you submit to the queue finishes. Depending on what your dataTask method actually does, here's what I'd do instead:
If you submit only one operation: set the operation's completionBlock property to a closure that refreshes your table information.
If you submit more than one operation: Make a new BlockOperation which refreshes your table information, and call addDependency on that operation with every other operation you submit to the queue. Then, submit that operation.
This will provide you with a much cleaner and more trouble-free way of monitoring completion for your tasks. And since you don't need the queue to completely empty anymore, you might not even have to use two separate queues anymore, depending on what else you're doing with them.

A solution to track a batch of HTTP requests in swift 3.0

I am using swift 3.0 running under iOS 10.0 and I want to craft some code that fires when a batch condition is met.
for i in 0 ..< rex {
async code, disappears and does it stuff
}
Imagine the async code is a collection of URL requests, that basically background as soon as I loop thru them. Now how can I fire off more code when "rex" requests have completed?
I thought of setting up a timer to watch and check every second, but its surely not a good solution.
I thought kicking off another thread to simply watch the data being collected, and fire when its quota is full, but well that's worse then the timer really.
I am thinking to include a test at the end of each URL request to see if it was the last that completed and than uses the NotificationCenter, but is this the optimal solution?
While OperationQueue (aka NSOperationQueue) is a good choice in many cases, it's not suitable for your use case. The problem is that URL requests are called asynchronously. Your NSOperation will finish before you get a response from the webservice.
Use DispatchGroup instead
let group = DispatchGroup()
// We need to dispatch to a background queue because we have
// to wait for the response from the webservice
DispatchQueue.global(qos: .utility).async {
for i in 0 ..< rex {
group.enter() // signal that you are starting a new task
URLSession.shared.dataTask(with: urls[i]) { data, response, error in
// handle your response
// ....
group.leave() // signal that you are done with the task
}.resume()
}
group.wait() // don't ever call wait() on the main queue
// Now all requests are complete
}
So I'm pretty sure what you want can be found here. Basically you want to use GCD and have a completion closure. It's one line of code, which always makes me giggle. A longer post on the topic is here.
What you're looking for is NSOperationQueue (or OperationQueue in Swift 3). Here's a Swift tutorial (might be a bit out of date). Here's Apple's documentation on it -- in Swift 3 they drop all the NS prefixes, so it's OperationQueue / Operation.
Basically you should add each of your URL tasks as an Operation to an OperationQueue, and have a "done" Operation with each of your URL tasks as a dependency, and add it to the queue. Then as soon as all your URL tasks are done, it will call your done operation, which you can set up to do whatever you want.
You will probably need to subclass Operation so you can update the isExecuting and isFinished properties properly. This question may be of some help here.