How to avoid Memory Leaks due to AFNetworking in swift - ios

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.

Related

How to set up completion handlers in API Call - Swift 5

I've run into a problem.
I've made a service for API calls that returns a User object, and the API call works fine and it returns the User object.
However, I need to use completion and #escaping, and I'm not sure how to do it properly, since I'm new to Swift and iOS development.
The Code is below.
func login(with credentials: LoginCredentials) {
let params = credentials
AF.request(Service.baseURL.appending("/user/login"), method: .post, parameters: params, encoder: JSONParameterEncoder.default).responseJSON { response in
switch response.result {
case .success:
print("Success")
case.failure:
print("Fail")
}
}
Also, how do I call a function when there is completion?
It’s not clear what kind of argument (if it takes one) the completion for your method shall take, thus I’m gonna give you a couple of hints.
Perhaps you want to also have the completion for your method execute on a queue specified as parameter in the call.
Therefore you might structure your method’s signature in this way:
func login(with credentials: LoginCredentials, queue: DispatchQueue = .main, _ completion: #esacping (Bool) -> Void)
Here I’ve used the type Bool as argument for the completion to be executed, and defaulted the queue argument to be .main, assuming that you’re just interested in deliver as result argument in the completion a value of true in case you’ve gotten a .success response, false otherwise; I’m also assuming that this method will mainly be called with the completion being executed on the main thread for updating a state, hence the default value for the queue argument.
Therefore your method body could become like this:
func login(with credentials: LoginCredentials, queue: DispatchQueue = .main, _ completion: #escaping (Bool) -> Void) {
let params = credentials
AF.request(
Service.baseURL.appending("/user/login"),
method: .post,
parameters: params,
encoder: JSONParameterEncoder.default
).responseJSON { response in
switch response.result {
case .success:
queue.async { completion(true) }
case.failure:
queue.async { completion(false) }
}
}
Now of course if you need to deliver something different (as in the token for the login session and an error in case of failure) then you’d better adopt a Result<T,Error> Swift enum as argument to pass to the completion.
Without you giving more details in these regards, I can only give a generic suggestion for this matter, cause I cannot go into the specifics.
EDIT
As a bonus I'm also adding how you could structure this method with the new Swift concurrency model:
// Assuming you are returning either a value of type User or
// throwing an error in case the login failed (as per your comments):
func login(with credentials: LoginCredentials) async throws -> User {
withCheckedThrowingContinuation { continuation in
AF.request(
Service.baseURL.appending("/user/login"),
method: .post,
parameters: credentials,
encoder: JSONParameterEncoder.default
).responseJSON { response in
switch response.result {
case .success:
let loggedUser = User(/* your logic to create the user to return*/)
continuation.resume(returning: loggedUser)
case.failure:
continuation.resume(throwing: User.Error.loginError)
}
}
}
// Here's possible way to implement the error on your User type
extension User {
enum Error: Swift.Error {
case loginError
// add more cases for other errors related to this type
}
}

CompletionHandler Behavior on Main vs Background thread

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.

Swift 3.0 + iOS 10 + Singleton sharedInstance - Update the UI after data array changes (the Angular Way)

Soooo.... I'm a JS dev and I'm building an app in Swift 3.0 for iOS 10+ and I want to interact with data much in the same way I do in Angular 1.6+...
Here is my situation:
I have a Singleton sharedInstance that house session data for a webRTC session. One of the properties of the sharedInstance holds an array of UInt and I need to update a UI element (IBOutlet) to show the user who the available opponents are they can call at any given moment.
In angular I would just update the model and the view/UI would change automatically... boom bang... done...
I am looking to create the same behavior in Swift 3.0 so here goes some code:
class Singleton {
static let sharedInstance = Singleton()
var session = (
peers: [UInt]()
)
private init() { }
}
Here is the controller:
class ViewController: UIViewController {
#IBOutlet weak var UIPeerList: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
UIPeerList.text = String(describing: Singleton.sharedInstance.session.peers)
self.updatePeerList(room: roomName, completion: {(oUser: AnyObject) -> Void in
QBRequest.dialogs(
for: QBResponsePage(limit: 100, skip: 0),
extendedRequest: [roomName : "name"],
successBlock: {(
response: QBResponse,
dialogs: [QBChatDialog]?,
dialogsUsersIDs: Set<NSNumber>?,
page: QBResponsePage?
) -> Void in
print("<------------------------------<<< Successfully found chat dialog - " + roomName)
}, errorBlock: { (response: QBResponse) -> Void in
print("<------------------------------<<< Handle Error finding chat dialog - " + roomName)
let chatDialog: QBChatDialog = QBChatDialog(dialogID: nil, type: QBChatDialogType.group)
//chatDialog.occupantIDs = []
//chatDialog.occupantIDs?.append(NSNumber(value: DataModel.sharedInstance.qbLoginParams.id))
//chatDialog.occupantIDs?.append(12186)
chatDialog.setValue(roomName, forKey: "Name")
QBRequest.createDialog(chatDialog, successBlock: {(response: QBResponse?, createdDialog: QBChatDialog?) in
print("<------------------------------<<< Success creating chat dialog")
print(response ?? "No Response")
print(createdDialog ?? "No Created Dialog")
}, errorBlock: {(response: QBResponse!) in
print("<------------------------------<<< Error creating chat dialog")
print(response ?? "No Response")
})
}
)
})
So above in the controller I set the UI element to the datasource right after the viewDidLoad is triggered (I know this is not exactly what's going on currently.) Now whenever I change the Singleton.sharedInstance.session.peers data model like for instance here in this ASYNC API request I want the UI to update:
func updatePeerList( room: String, completion: #escaping (_ response: AnyObject) -> ()) {
QBRequest.users(
withTags: [room],
page: QBGeneralResponsePage(currentPage: 1, perPage: 10),
successBlock: {( response: QBResponse, page: QBGeneralResponsePage?, users: [QBUUser]? ) -> Void in
guard users != nil else { return }
print("<------------------------------<<< Success getting users with room tag - "+room)
DataModel.sharedInstance.sessionInfo.peers.removeAll()
for object in users! {
DataModel.sharedInstance.sessionInfo.peers.append(object.id)
}
DispatchQueue.main.async { completion(response) }
}, errorBlock: {(response: QBResponse!) in
print("<------------------------------<<< Error getting users with room tag - "+room)
print(response)
DispatchQueue.main.async { completion(response) }
}
)
}
How do I achieve such a feat?
NOTE: I believe this is possible as I looked into the UISwitch method however that didn't exactly work as I wanted to..
One solution that came to my mind is using delegation. There may be more elegant solutions though.
To use delegation, you could go through the following steps:
1- Create a protocol that have one method update(), and let's name that protocol UpdateUIDelegate.
2- Make your ViewController implement that protocol, and implement update() method in the way you want.
3- Set your ViewController object as a property in the Singleton class.
4- Make all changes that affect Singleton.sharedInstance.session.peers happen through a method inside Singleton, and make that method calls your delegate's update method in its end.

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