CompletionHandler Behavior on Main vs Background thread - ios

Does the thread from which a method is being used matter in terms of completionHandlers? When running on the Main thread, I have no problems; the (data) in completionHandler is called and thus the function's completionHandler within that.
The following is my code:
static func getInfo(user: User,completion: (([String:String]) -> Void)? = nil){
print(user.apiUid)
var enrolledCourses: [String:String] = [:]
NetworkingManager.staticGeneralRequest(withEndpoint: someURL, oauthToken: user.oauth_token, oauthTokenSecret: user.oauth_token_secret) { (data) in
let decodedData: [String:String] = data.getData()
completion?(decodedData)
}
}
//NOTE: According to the compiler,
//the use of an optional completionHandler means that it's `#escaping` by default.
Is this an issue pretaining to threading, and what are some best practices to ensure that code works well across threads?

Your closure was deallocated before you get the response. Adding #escaping like this: static func getInfo(user: User, completion: #escaping (([String:String]) -> Void)? = nil) to keep it in scope.

Related

Failed "Dictionary.subscript.getter" all within same dispatchQueue

I have a handful of production crashes on iOS that look like this:
Crashed: com.superdupertango.Server.CallbackRunQueue
0 SDTServer 0x102b78ed0 specialized Dictionary.subscript.getter + 4378562256 (<compiler-generated>:4378562256)
1 SDTServer 0x102a22fec closure #1 in Server.processError(_:) + 4377161708 (<compiler-generated>:4377161708)
2 SDTServer 0x10257ce90 thunk for #escaping #callee_guaranteed () -> () + 4372287120 (<compiler-generated>:4372287120)
This is the offending code (all code in this area is Swift5):
private let callbackRunQueue = DispatchQueue(label: "com.superdupertango.Server.CallbackRunQueue", qos: DispatchQoS.userInitiated, target: nil)
var bodyCompletionBlocks: [String: ((Data?, Error?, String) -> Void)] = [:]
...
private init() {
NotificationCenter.default.addObserver(self, selector: #selector(self.processError(_:)), name: Notification.Name.SDTServerWriteFailed, object: nil)
}
...
#objc private func processError(_ notification: Notification) {
guard let userInfo = notification.userInfo, let requestId = userInfo["requestId"] as? String else {
return
}
self.callbackRunQueue.async {
DLog("Server.processError (\(requestId)): Got Error for request. Shutting down request.")
guard let bodyCompletionBlock = self.bodyCompletionBlocks[requestId] else {
DLog("Server.processError (\(requestId)): Failed getting body completion block. Returning")
return
}
bodyCompletionBlock(Data(), nil, requestId)
self.bodyCompletionBlocks.removeValue(forKey: requestId)
self.initialCompletionBlocks.removeValue(forKey: requestId)
}
}
Note that the callbackRunQueue is a serial queue.
The only dictionary value that's being retrieved inside the callbackRunQueue is the self.bodyCompletionBlocks:
guard let bodyCompletionBlock = self.bodyCompletionBlocks[requestId] else {
I've found a few references to this error, but they all say that this may be a multi-thread access issue.
However, in my code, there are only 3 places where there is access to self.bodyCompletionBlocks, and they are all within callbackRunQueue.async or callbackRunQueue.sync blocks. Also note that some of this code is running inside threads within GCDWebServer, but as I mentioned, I'm always ensuring the code runs in my callbackRunQueue queue, so I don't think this is GCDWebServer related.
I thought that enclosing thread sensitive code inside a serial queue's async or sync blocks would protect against multi-thread access problems like this.
Any ideas?
Thanks!

How to avoid Memory Leaks due to AFNetworking in swift

Im using AFNetworking in my project. When I try to find the memory leaks using instruments, its showing AFNetworking as reasons for memory leak. I'm creating a model class and within that class I'm calling the AFNetworking. Call to API is done from Viewcontroller to model class.Please help me how to fix this issue.
My coding pattern is adding below.
Class myViewController:UIViewcontroller{
override func viewDidLoad() {
super.viewDidLoad()
//apiCall
myModel.apiCallFuncT("value"){ (success) in
}
}
}
Class myModel{
var somevariable:String
class apiCallFuncT(parameters:string,completionHandler:#escaping(_ success:Bool)->Void){
//here Im calling the AFNetworking class. Im adding the call below.
ServiceManager.getDataFromNewServiceSolOne(serviceName: URL + "\(apiName)", parameters: param , success: { (result) in
completion(true)
})
}
//This is the serviceManger I'm calling from all models I have for api Calls.
class ServiceManager: NSObject {
static var sharedManager = AFHTTPSessionManager()
class func getDataFromNewServiceSolOne(serviceName: String, parameters : [String : String]?, success:#escaping (_ result: AnyObject) -> Void, failure :#escaping (_ error : NSError) -> Void) {
let manager = AFHTTPSessionManager(baseURL: NSURL(string: URL)! as URL)
manager.responseSerializer.acceptableContentTypes = ["text/html", "application/json"]
manager.post(serviceName, parameters: parameters, progress: { (progress) in
}, success: { (task, result) in
success(result as AnyObject)
}) { (task, error) in
failure(error as NSError)
}
}
}
To diagnose these issues, the “Debug Memory Graph” feature with “Malloc Stack” option is the first tool for which we reach. See How to debug memory leaks when Leaks instrument does not show them?
Anyway, when we run that, we can see that the problem is not in your use of the API. You don’t have any strong reference cycles in your code. In this example, we don’t see any of our app’s objects (other than app and scene delegates) in the panel on the left (because I dismissed the custom view controller that initiated the request). So that’s great.
So, what’s going on? The issue is the cycle between AFHTTPSessionManager and its NSURLSession. Unlike most delegates, NSURLSession keeps a strong reference to its delegate. As the documentation says:
The session object keeps a strong reference to this delegate until your app exits or explicitly invalidates the session. If you do not invalidate the session, your app leaks memory until it exits.
So, you have two possible solutions:
Invalidate the session when the request is done. Call finishTasksAndInvalidate:
class func getDataFromNewServiceSolOne(serviceName: String, parameters: [String: String]?, success: #escaping (_ result: AnyObject) -> Void, failure: #escaping (_ error: NSError) -> Void) {
let manager = AFHTTPSessionManager(baseURL: URL(string: baseUrlString))
manager.post(serviceName, parameters: parameters, headers: nil, progress: { _ in
// this is intentionally blank
}, success: { task, result in
success(result as AnyObject)
}, failure: { task, error in
failure(error as NSError)
})
manager.session.finishTasksAndInvalidate() // add this line
}
The other approach is to not create a separate AFHTTPSessionManager for every request, but rather instantiate it once and then store it in a property. Then you don’t need to worry about invalidating it. Also, there is a certain amount of overhead in creating sessions, so we often instantiate a single session, and then perform many requests using that. And then you don’t have to worry about creating and invalidating a new session for every request.

Weak DispatchGroup in closures and other GCD questions

Swift closures strongly capture reference types.
DispatchGroup is a reference type.
My questions have to do with the following code:
func getUsername(onDone: #escaping (_ possUsername: String?) -> ())
{
//Post request for username that calls onDone(retrievedUsername)...
}
func getBirthdate(using username: String?, onDone: #escaping (_ possBday: String?) -> ())
{
//Post request for token that calls onDone(retrievedToken)...
}
func asyncTasksInOrder(onDone: #escaping (_ resultBDay: String?) -> ())
{
let thread = DispatchQueue(label: "my thread", qos: .userInteractive, attributes: [],
autoreleaseFrequency: .workItem, target: nil)
thread.async { [weak self, onDone] in
guard let self = self else {
onDone(nil)
return
}
let dg = DispatchGroup() //This is a reference type
var retrievedUsername: String?
var retrievedBday: String?
//Get username async first
dg.enter()
self.getUsername(onDone: {[weak dg](possUsername) in
retrievedUsername = possUsername
dg?.leave() //DG is weak here
})
dg.wait()
//Now that we've waited for the username, get bday async now
dg.enter()
self.getBirthdate(using: retrievedUsername, onDone: {[weak dg](possBday) in
retrievedBday = possBday
dg?.leave() //DG is also weak here
})
dg.wait()
//We've waited for everything, so now call the return callback
onDone(retrievedBday)
}
}
So the two closures inside of asyncTasksInOrder(onDone:) each capture dg, my DispatchGroup.
Is it even necessary to capture my dispatch group?
If I don't capture it, how would I even know I've got a retain cycle?
What if the dispatch group evaporates during one of the callback executions? Would it even evaporate since it's waiting?
Is it unnecessarily expensive to instantiate a DispatchQueue like this often (disregarding the .userInteractive)? I'm asking this particular question because spinning up threads in Android is extremely expensive (so expensive that JetBrains has dedicated lots of resources towards Kotlin coroutines).
How does dg.notify(...) play into all of this? Why even have a notify method when dg.wait() does the same thing?
I feel like my understanding of GCD is not bulletproof, so I'm asking to build some confidence. Please critique as well if there's anything to critique. The help is truly appreciated.
1) No, the dispatch group is captured implicitly. You don't even need to capture self in async because GCD closures don't cause retain cycles.
2) There is no retain cycle.
3) Actually you are misusing DispatchGroup to force an asynchronous task to become synchronous.
4) No, GCD is pretty lightweight.
5) The main purpose of DispatchGroup is to notify when all asynchronous tasks – for example in a repeat loop – are completed regardless of the order.
A better solution is to nest the asynchronous tasks. With only two tasks the pyramid of doom is manageable.
func asyncTasksInOrder(onDone: #escaping (String?) -> Void)
{
let thread = DispatchQueue(label: "my thread", qos: .userInteractive, autoreleaseFrequency: .workItem)
thread.async {
//Get username async first
self.getUsername { [weak self] possUsername in
guard let self = self else { onDone(nil); return }
//Now get bday async
self.getBirthdate(using: possUsername) { possBday in
//Now call the return callback
onDone(possBday)
}
}
}
}

Swift 3 :Closure use of non-escaping parameter may allow it to escape

I have the following function where I have completion handler but I'm getting this error:
Closure use of non-escaping parameter may allow it to escape
Here is my code:
func makeRequestcompletion(completion:(_ response:Data, _ error:NSError)->Void) {
let urlString = URL(string: "http://someUrl.com")
if let url = urlString {
let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, urlRequestResponse, error) in
completion(data, error) // <-- here is I'm getting the error
})
task.resume()
}
}
Any of you knows why I'm getting this error?
I'll really appreciate you help
Looks like you need to explicitly define that the closure is allowed to escape.
From the Apple Developer docs,
A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. When you declare a function that takes a closure as one of its parameters, you can write #escaping before the parameter’s type to indicate that the closure is allowed to escape.
TLDR; Add the #escaping keyword after the completion variable:
func makeRequestcompletion(completion: #escaping (_ response:Data, _ error:NSError)->Void) {
let urlString = URL(string: "http://someUrl.com")
if let url = urlString {
let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, urlRequestResponse, error) in
completion(data, error) // <-- here is I'm getting the error
})
task.resume()
}
}
An "escaping" closure is a closure that can outlive the scope that it was created in. Escaping closures require special care around reference counting and memory management and can be harder to optimize.
Prior to Swift 3, the default for closures was to assume that they were escaping. This meant that developers had to specifically identify closures that are known not to escape to allow the compiler to make optimizations. The community found that in fact, the compiler could easily find out by itself if a closure is escaping or not, and decided that an aggressive approach to escaping could result in faster code. The result is that closures are now assumed to be non-escaping, and you need to flag closures that are escaping with the #escaping attribute.
In your case, the closure that URLSession.shared.dataTask accepts is itself an escaping closure, so if you use a closure inside of it, it also needs to be marked #escaping.
#escaping is infectious to all calling methods, and the compiler determines when you must include it.
Consider this example (which compiles):
dispatchSometime( { print("Oh yeah") })
func dispatchSometime(_ block: ()->()) {
dispatchNow(block)
}
func dispatchNow(_ block: ()->()) {
block()
}
This modified example, however, produces two errors of type non-escaping parameter may allow it to escape:
dispatchSometime( { print("Oh yeah") })
func dispatchSometime(_ block: ()->()) {
dispatchLater(block)
}
func dispatchLater(_ block: ()->()) {
DispatchQueue.main.async(execute: block)
}
The dispatch on main means the dispatchLater method needs #escaping, and once you've added that, the dispatchSometime method also requires #escaping for the example to compile.
dispatchSometime( { print("Oh yeah") })
func dispatchSometime(_ block: #escaping ()->()) {
dispatchLater(block)
}
func dispatchLater(_ block: #escaping ()->()) {
DispatchQueue.main.async(execute: block)
}
However, the take away is just:
Keep adding #escaping up the call chain until the compiler stops complaining.
The keyword doesn't change anything: it's a warning which says, essentially, "be careful to use weak with captured variables as they may be retained along with the block itself."
Implications
The really fun case with this is where you have to adjust several methods to include the #escaping keyword, which gets the compiler to stop complaining. However, if those methods are actually conforming to a protocol, that protocol's methods must also get the #escaping keyword, which also infects all other protocol conformants. Fun!

Shorter ways to specify completion handler

I've had this problem a couple of times now, so thought I'd reach out.
I have a number of network interfaces responsible for making Async network calls, there's about 5/6 functions in each interface which all use a completion handler with the same definition:
(success: Bool, resources: [String: AnyObject] -> Void)
I'm looking for an alternative to adding this to the end of every function as it forces every function declaration onto 2/3 lines. e.g.
func performSomeNetworkCall(withThisParam parm1: AnyObject, param2: AnyObject, completion: (success: Bool, resources: [String: AnyObject] -> Void)) {
}
I've had a couple of ideas about how to solve the problem of long declarations and rewriting the completion definition for every function.
Thought One
Using a typealias to define the closure like so:
typealias CompletionHandler = (success: Bool, resources: [String: AnyObject] -> Void)?
This, in theory, solves both issues as it's quick to write and solves the code length problems. However when calling function from an external source, the typealias doesn't autocomplete like regular closures, meaning you have to write it out every-time leading to mistakes.
Thought Two
Using a code snippet (when Xcode actually remembers you set them). This in theory works as well, as long as every other developer working on the code also has the same code snippet and it aware of it instead of writing the entire declaration.
Thought Three
I thought about defining the completion handler as a function and passing the function as the parameter, something like this:
func completionHandler() -> (success: Bool, resources: [String: AnyObject] -> Void) {
return completionHandler()
}
But haven't been able to achieve what I wanted with that.
Edit
What I was hoping to achieve with Thought Three
func completionHandler() -> ((success: Bool, resources: [String: AnyObject]) -> Void) {
return completionHandler()
}
func networkCall(completion: (success: Bool, resources: [String: AnyObject]) -> Void) {
let request = NSURLRequest(URL: NSURL(string: "http://www.google.co.uk")!)
let session = NSURLSession.sharedSession()
session.dataTaskWithRequest(request) { (data, response, error) -> Void in
let handler = completionHandler()
handler(success: true, resources: [String: AnyObject]())
}
}
func foo() {
let handler = completionHandler()
networkCall(handler)
print(handler)
// hope to use handler.success || handler.resources here
}
Although currently just gets stuck on the completionHandler method call - (To point out the obvious...)
A typealias is the typical solution for this. It should autocomplete even outside of the original definition's file. I think the issue is in your definition of your typealias. If you define it like this (pay attention to the parentheses and no optional):
typealias CompletionHandler = (success: Bool, resources: [String: AnyObject]) -> Void
func performSomeNetworkCall(withThisParam parm1: AnyObject, param2: AnyObject, completion: CompletionHandler?) {
// Implementation
}
Then autocompleting it works like so:
After hitting the return key:
I'm looking for an alternative
IMHO, the better alternative to write much more concise code would utilise "Promises" or "Futures". These are not yet part of the Swift Standard Library, but other languages do have one or the other implementation - and there are already third party libraries for Swift.
For example, with "Scala-like" Futures your code may look as follows:
func performSomeNetworkCall(
withThisParam parm1: AnyObject,
param2: AnyObject)
-> Future<[String: AnyObject]>
So, instead of a completion handler, the asynchronous function returns a Future. The future is a generic class, whose type parameter equals the computed value. A future can also represent an error, if the underlying asynchronous function fails.
You obtain the eventual result though registering a continuation via a combinator function:
let future = performSomeNetworkCall(param1, param2: param2)
future.map { computedValue in
print("Result: \(computedValue)")
}
.onFailure { error in
print("Error: \(error)")
}
Here, map is a combinator function with which we can register a continuation. A combinator returns a new future based on the return value of its continuation, and thus we can combine futures which perform more complex tasks.
The continuation function will be called when the future has been successfully completed. It get's passed the value of the underlying asynchronous function as its parameter. The continuation returns a value or Void.
Another combinator is flatMap. As opposed to map, it's continuation just returns another future:
login().flatMap { token in
fetchUser(id: userId).map { user in
print("User: \(user)")
}
}.onFailure { error in
print("Error: \(error)")
}
The function login is an asynchronous function. When it completed successfully, it calls the asynchronous function fetchUser in its continuation. When fetchUser completes successfully, it prints the obtained user object.
If anything fails, the error will be propagated to the "failure handler" which has been registered with onFailure.

Resources