Promises + Alamofire make sure network calls always on background - swift

Does promises always run on background thread.
#IBAction func doNetworkCall(_ sender: Any) { // Does this run on
background thread
Network.fetchPhotos().done { (photos) in
}
}
static func fetchPhotos () -> Promise<[Photo]> {
return Promise { seal in
AF.request("https:photosURL", method: .post, parameters: ["auth":"1231","user_id":"u12312"]).responseJSON { (response) in
guard let data = response.data else { return }
let coder = JSONDecoder()
let photos = try! coder.decode([Photo].self, from: data)
seal.fulfill(photos)
}
}
}
I have used promises with purpose of all network calls runs on background thread irrespective calling from main thread. I have some chain network requests which will be easier to implement.
is this assumption correct?

Alamofire always runs its requests on a background queue. In your example the only part that isn't in the background is the responseJSON closure. By default that closure runs on the .main queue. I recommend you adopt responseDecodable to decode your responses so that the parsing is also in the background only call fulfill the promise in the closure. (I'm not sure whether fulfilling on the main queue is otherwise necessary.)

Related

Do I have place upload or download tasks onto a background thread or does Swift do it for me?

I am unsure about the thread/queue control in Swift for HTTP methods.
Working on the part of my app that makes GET requests, as well as POST and others, from a 3rd party server.
From my main UIViewController, I initialize the class with:
let uploadService = UploadService()
lazy var uploadSession: URLSession = {
let configuration = URLSessionConfiguration.default
return URLSession(configuration: configuration, delegate: self, delegateQueue: .main)
}()
Then later on I call it from the same UIViewController, let uploadService = UploadService()
Before I start to add more functionality to the class below I wanted to ask about:
return URLSession(configuration: configuration, delegate: self, delegateQueue: .main)
When the UploadService class calls methods to my UIViewController, Xcode warned me to do it from the main thread/queue, which I do. So my questions is, does Swift automatically move anything HTTP related to the non-main thread/queue so I don't have to worry about it? Because if that's the case, great. But then why ask me to place it in the main thread/queue, as I did up above? (which I did from a tutorial).
Just want to be clear, should I be declaring it the background thread/queue, or does Swift handle that for me regardless of how I declared it up above?
class UploadService {
var uploadSession = URLSession.shared
func start(upFile: XFile, script: String, upLoadInvoiceClass: UploadInvoice) {
var request = upFile.makeUrlReq(upFile: upFile, script: script)
uploadSession.uploadTask(with: request, from: request.httpBody )
{ (data, response, error) in
if let response = response {
upLoadoiceClass.upResp(resp: response)
}
if let error = error {
upLoadoiceClass.upErr(error: error)
}
if let data = data {
upLoadoiceClass.upData(data: data)
}
}.resume()
}
}
URLSession ALWAYS does network interactions on a background thread. You don't have to worry about that.
The completion handlers/delegate methods are also run on a background thread by default. That lets you do time-consuming processing on the resulting data without tying up the main thread.
Unless you point give your URL session a different delegate queue when you create it, you should wrap any UI code you put in your completion handlers/delegate methods in a call to the main thread.
Alternately, you can create a URLSession and give it a foreground queue in the delegateQueue parameter you pass in the initializer. If you do that then your completion handlers/delegate methods will be run on that foreground queue (and thus run on the main thread.) If you do that you don't need to explicitly wrap UIKit calls in a call to the main thread, but you will stall the UI if you do time-consuming work in your URLSession completion handlers/delegate methods

Swift async Task captures value too long

I want to do some async processing in my MTKView's draw(_:) method. For that I'm first getting the currentDrawable and then using it inside of a Task:
override func draw(_ rect: CGRect) {
guard let currentDrawable = self.currentDrawable else { return }
Task {
// <image filtering>
let destination = CIRenderDestination(width: Int(drawableSize.width),
height: Int(drawableSize.height),
pixelFormat: self.colorPixelFormat,
commandBuffer: nil,
mtlTextureProvider: { () -> MTLTexture in
return currentDrawable.texture
})
try await asynContext.startTask(toClear: destination)
try await asynContext.startTask(toRender: image, to: destination)
currentDrawable.present()
}
}
While this works, the performance is much worse than using a DispatchQueue or staying on the main thread.
It seems the Task is holding on to the drawable for much longer than its actual execution, which causes the next call to currentDrawable to stall when no more drawables are in the pool at the moment.
I assume the Task itself is still referenced/used by the concurrency runtime after its execution for a little while longer.
Is there a way to prevent that? Or, alternatively, to have the Task's operation block release the drawable early?
As by matt's request, a little more elaboration:
I want to move some computation Core Image is doing on the filter graph into a background queue to not block the main thread. For that, I created a little helper actor:
actor AsyncContext {
private let context: CIContext
init(context: CIContext) {
self.context = context
}
#discardableResult
func startTask(toClear destination: CIRenderDestination) throws -> CIRenderTask {
return try self.context.startTask(toClear: destination)
}
#discardableResult
func startTask(toRender image: CIImage, to destination: CIRenderDestination) throws -> CIRenderTask {
return try self.context.startTask(toRender: image, to: destination)
}
}
The actor should ensure that calls to startTask(...) are happening in a background queue (which they do).
I'm pretty confident that the actor itself is not the problem. It's the Task not getting released in time, I think.

How can I convert to Swift async/await from GCD (DispatchQueue)?

I am following Stanfords' CS193p Developing Apps for iOS online course.
It is using the Grand Central Dispatch (GCD) API for a demo of multithreading.
But they noted, that
"GCD has been mostly replaced by Swift's new built-in async API as of WWDC 2021".
So I wanted to learn how the code from the Lecture would look like after updating it to use this new API.
After watching Apple's WWDC videos, it seems to me like
DispatchQueue.global(qos: .userInitiated).async { } is replaced in this new async API with Task { } or Task(priority: .userInitiated) {}, but I'm not sure, what has DispatchQueue.main.async { } been replaced with?
So, my questions are:
Am I correctly assuming, that DispatchQueue.global(qos: .userInitiated).async { } has been replaced with Task(priority: .userInitiated) {}
What has DispatchQueue.main.async { } been replaced with?
Please help, I want to learn this new async-await API.
Here's the code from the Lecture, using old GCD API:
DispatchQueue.global(qos: .userInitiated).async {
let imageData = try? Data(contentsOf: url)
DispatchQueue.main.async { [weak self] in
if self?.emojiArt.background == EmojiArtModel.Background.url(url) {
self?.backgroundImageFetchStatus = .idle
if imageData != nil {
self?.backgroundImage = UIImage(data: imageData!)
}
// L12 note failure if we couldn't load background image
if self?.backgroundImage == nil {
self?.backgroundImageFetchStatus = .failed(url)
}
}
}
}
The whole function (in case you need to see more code):
private func fetchBackgroundImageDataIfNecessary() {
backgroundImage = nil
switch emojiArt.background {
case .url(let url):
// fetch the url
backgroundImageFetchStatus = .fetching
DispatchQueue.global(qos: .userInitiated).async {
let imageData = try? Data(contentsOf: url)
DispatchQueue.main.async { [weak self] in
if self?.emojiArt.background == EmojiArtModel.Background.url(url) {
self?.backgroundImageFetchStatus = .idle
if imageData != nil {
self?.backgroundImage = UIImage(data: imageData!)
}
// L12 note failure if we couldn't load background image
if self?.backgroundImage == nil {
self?.backgroundImageFetchStatus = .failed(url)
}
}
}
}
case .imageData(let data):
backgroundImage = UIImage(data: data)
case .blank:
break
}
}
If you really are going to do something slow and synchronous, Task.detached is a closer analog to GCD’s dispatching to a global queue. If you just use Task(priority: ...) { ... } you are leaving it to the discretion of the concurrency system to decide which thread to run it on. (And just because you specify a lower priority does not guarantee that it might not run on the main thread.)
For example:
func fetchAndUpdateUI(from url: URL) {
Task.detached { // or specify a priority with `Task.detached(priority: .background)`
let data = try Data(contentsOf: url)
let image = UIImage(data: data)
await self.updateUI(with: image)
}
}
And if you want to do the UI update on the main thread, rather than dispatching it back to the main queue, you would simply add the #MainActor modifier to the method that updates the UI:
#MainActor
func updateUI(with image: UIImage?) async {
imageView.image = image
}
That having been said, this is a pretty unusual pattern (doing the network request synchronously and creating a detached task to make sure you don't block the main thread). We would probably use URLSession’s new asynchronous data(from:delegate:) method to perform the request asynchronously. It offers better error handling, greater configurability, participates in structured concurrency, and is cancelable.
In short, rather than looking for one-to-one analogs for the old GCD patterns, use the concurrent API that Apple has provided where possible.
FWIW, in addition to the #MainActor pattern shown above (as a replacement for dispatching to the main queue), you can also do:
await MainActor.run {
…
}
That is roughly analogous to the dispatching to the main queue. In WWDC 2021 video Swift concurrency: Update a sample app, they say:
In Swift’s concurrency model, there is a global actor called the main actor that coordinates all operations on the main thread. We can replace our DispatchQueue.main.async with a call to MainActor’s run function. This takes a block of code to run on the MainActor. …
But he goes on to say:
I can annotate functions with #MainActor. And that will require that the caller switch to the main actor before this function is run. … Now that we've put this function on the main actor, we don’t, strictly speaking, need this MainActor.run anymore.

Swift - Network Requests and Background Queue

just wanted some clarification on the best practices to make network api calls in Swift 2.
Here is how my typical network requests looks like to download JSON data:
let session = NSURLSession(configuration: .defaultSessionConfiguration())
let url = NSURL(string: my_url_string)
let request = NSURLRequest(URL: url)
let dataTask = session.dataTaskWithRequest(request) { data, response, error in
do {
self.tableData = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! [NSDictionary]
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
} catch let error {
print(error)
}
}
dataTask.resume()
My question is: should I wrap all of this code block into a background queue? Should I do as follows:
let download_queue = dispatch_queue_create("download", nil)
dispatch_async(download_queue) { () -> Void in
previous code here
}
Or should I use one of the given high priority queues such as:
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0)
Also, when making additional network requests on subsequent view controllers, should I use the same queue I use here or should I create a new one?
By default NSURLSession API is highly asynchronous. Usefull information from the Apple docs.
There is no visible issues that indicate to wrap you're code block with GCD and also completion block runs on background thread so there is right usage of the GCD to update UITableview

NSLock.lock() executed while lock already held?

I'm reviewing some Alamofire sample Retrier code:
func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: #escaping RequestRetryCompletion) {
lock.lock() ; defer { lock.unlock() }
if let response = request.task.response as? HTTPURLResponse, response.statusCode == 401 {
requestsToRetry.append(completion)
if !isRefreshing {
refreshTokens { [weak self] succeeded, accessToken, refreshToken in
guard let strongSelf = self else { return }
strongSelf.lock.lock() ; defer { strongSelf.lock.unlock() }
...
}
}
} else {
completion(false, 0.0)
}
}
I don't follow how you can have lock.lock() on the first line of the function and then also have that same line strongSelf.lock.lock() within the closure passed to refreshTokens.
If the first lock is not released until the end of the should method when the defer unlock is executed then how does the the second strongSelf.lock.lock() successfully execute while the first lock is held?
The trailing closure of refreshTokens, where this second call to lock()/unlock() is called, runs asynchronously. This is because the closure is #escaping and is called from within a responseJSON inside the refreshTokens routine. So the should method will have performed its deferred unlock by the time the closure of refreshTokens is actually called.
Having said that, this isn't the most elegant code that I've seen, where the utility of the lock is unclear and the risk of deadlocking is so dependent upon the implementation details of other routines. It looks like it's OK here, but I don't blame you for raising an eyebrow at it.