How to "loosely" trigger a function? - ios

I have the following async recursive code:
func syncData() {
dal.getList(...) { [unowned self] list, error in
if let objects = list {
if oneTime {
oneTime = false
syncOtherStuffNow()
}
syncData() // recurse until all data synced
} else if let error = error {... }
func syncOtherStuffNow() { } // with its own recursion
My understanding is that the recursion will build the call stack until all the function calls complete, at which point they will all unwind and free up the heap.
I also want to trigger another function (syncOtherStuffNow) from within the closure. But don't want to bind it to the closure with a strong reference waiting for it's return (even though it's async too).
How can I essentially trigger the syncOtherStuffNow() selector to run, and not affect the current closure with hanging on to its return call?
I thought of using Notifications, but that seems overkill given the two functions are in the same class.

Since dal.getList() takes a callback I guess it is asynchronous and so the the first syncData starts the async call and then returns immediately which lets syncData() return.
If syncOtherStuffNow() is async it will return immediately and so dataSync() will not wait on it finishing its job and so continue with its execution to the end.
You can test whether sth builds a callstack by putting a breakpoint on every recursion and look on the callstack how many calls of the same function are ontop.

What I do is recurse with asyncAfter, which unwinds the call stack.

Related

How do I write thread-safe code that uses a completionHandler with a function that delegates code to an instance of OperationQueue?

I've been using the CloudKitShare sample code found here as a sample to help me write code for my app. I want to use performWriterBlock and performReaderBlockAndWait as found in BaseLocalCache using a completionHandler without violating the purposes of the design of the code, which focuses on being thread-safe. I include code from CloudKitShare below that are pertinent to my question. I include the comments that explain the code. I wrote comments to identify which code is mine.
I would like to be able to use an escaping completionHandler if possible. Does using an escaping completionHandler still comply with principles of thread-safe code, or does it in any way violate the purpose of the design of this sample code to be thread-safe? If I use an escaping completionHandler, I would need to consider when the completionHandler actually runs relative to other code outside of the scope of the actual perform function that uses the BaseLocalCache perform block. I would for one thing need to be aware of what other code runs in my project between the time the method executes and the time operationQueue in BaseLocalCache actually executes the block of code and thus the completionHandler.
class BaseLocalCache {
// A CloudKit task can be a single operation (CKDatabaseOperation)
// or multiple operations that you chain together.
// Provide an operation queue to get more flexibility on CloudKit operation management.
//
lazy var operationQueue: OperationQueue = OperationQueue()
// This sample ...
//
// This sample uses this dispatch queue to implement the following logics:
// - It serializes Writer blocks.
// - The reader block can be concurrent, but it needs to wait for the enqueued writer blocks to complete.
//
// To achieve that, this sample uses the following pattern:
// - Use a concurrent queue, cacheQueue.
// - Use cacheQueue.async(flags: .barrier) {} to execute writer blocks.
// - Use cacheQueue.sync(){} to execute reader blocks. The queue is concurrent,
// so reader blocks can be concurrent, unless any writer blocks are in the way.
// Note that Writer blocks block the reader, so they need to be as small as possible.
//
private lazy var cacheQueue: DispatchQueue = {
return DispatchQueue(label: "LocalCache", attributes: .concurrent)
}()
func performWriterBlock(_ writerBlock: #escaping () -> Void) {
cacheQueue.async(flags: .barrier) {
writerBlock()
}
}
func performReaderBlockAndWait<T>(_ readerBlock: () -> T) -> T {
return cacheQueue.sync {
return readerBlock()
}
}
}
final class TopicLocalCache: BaseLocalCache {
private var serverChangeToken: CKServerChangeToken?
func setServerChangeToken(newToken: CKServerChangeToken?) {
performWriterBlock { self.serverChangeToken = newToken }
}
func getServerChangeToken() -> CKServerChangeToken? {
return performReaderBlockAndWait { return self.serverChangeToken }
}
// Trial: How to use escaping completionHandler? with a performWriterBlock
func setServerChangeToken(newToken: CKServerChangeToken?, completionHandler: #escaping (Result<Void, Error>)->Void) {
performWriterBlock {
self.serverChangeToken = newToken
completionHandler(.success(Void()))
}
}
// Trial: How to use escaping completionHandler? with a performReaderBlockAndWait
func getServerChangeToken(completionHandler: (Result<CKServerChangeToken, Error>)->Void) {
performReaderBlockAndWait {
if let serverChangeToken = self.serverChangeToken {
completionHandler(.success(serverChangeToken))
} else {
completionHandler(.failure(NSError(domain: "nil CKServerChangeToken", code: 0)))
}
}
}
}
You asked:
Does using an escaping completionHandler still comply with principles of thread-safe code, or does it in any way violate the purpose of the design of this sample code to be thread-safe?
An escaping completion handler does not violate thread-safety.
That having been said, it does not ensure thread-safety, either. Thread-safety is solely a question of whether you ever access some shared resource from one thread while mutating it from another.
If I use an escaping completionHandler, I would need to consider when the completionHandler actually runs relative to other code outside of the scope of the actual perform function that uses the BaseLocalCache perform block.
Yes, you need to be aware that the escaping completion handler is called asynchronously (i.e., later). That is less of a thread-safety concern than a general understanding of the application flow. It is only a question of what you might be doing in that closure.
IMHO, the more important observation is that the completion handler is called on the cacheQueue used internally by BaseLocalCache. So, the caller needs to be aware that the closure is not called on the caller’s current queue, but on cacheQueue.
It should be noted that elsewhere in that project, they employ another common pattern, where the completion handler is dispatched back to a particular queue, e.g., the main queue.
Bottom line, thread-safety is not a question of whether a closure is escaping or not, but rather (a) from what thread does the method call the closure; and (b) what the supplied closure actually does:
Do you interact with the UI? Then you will want to ensure that you dispatch that back to the main queue.
Do you interact with your own properties? Then you will want to make sure you synchronize all of your access with them, either with actors, relying on the main queue, use your own serial queues, or a reader-writer pattern like in the example you shared with us.
If you are ever unsure about your code’s thread-safety, you might consider temporarily turning on TSAN as described in Diagnosing Memory, Thread, and Crash Issues Early

Setting a text on the label using JSON data

}
Hey guys, I have a problem setting a value for the label. The label should display the number of elements in the array inside my JSON (link - followers_url variable). I call alamo and make a request with that url. When I print the value inside parseData() method I get the right result. When I print it inside configureView() and viewDidLoad() I always get 0.
Setting the label text also works only inside parseData() method. Any ideas how I can get it to work?
Alamofire.request(url).validate().responseJSON { response in
self.parseData(data: response.data!)
}
This above request runs on another background thread.
So when you call the function callAlamo the response is received in the completion block ( { response in ). So when you call print() after callAlamo. the response has not yet been received and print is called so value is not updated. So please perform the operation on the response only through completion block.
If you want to set a label write you set label code after self.parseData in completion block ({response in). Make sure you set it in main queue as the UI operation needs to be performed on main queue only
Following question will help to set label on main thread.
In Swift how to call method with parameters on GCD main thread?
You need to understand multithreading concept to get a better understanding of this. Follow this https://medium.com/#gabriel_lewis/threading-in-swift-simply-explained-5c8dd680b9b2
You should learn something about iOS Parsing techniques. Then learn how to create Model using class or struct. Then you will get Idea.
You should look into Object Mapper as well.
You're dealing with an asynchronous operation. Asynchronous operations are "actions" that are dispatched and require you to wait before they complete. Think about loading a website in Safari. Once you type, let's say, stackoverflow.com in your browser, a loading spinner will notify that something is loading. While the page is loading, you obviously cannot see what's on the webpage. There's only an empty, white page.
The same is happening with your request. When you call the callAlamo function you're telling the app to start loading something. This is requiring you to wait until the task is done. If you count the elements in the followersAndFollowingArray right after the server call, then you'll get it empty, because the request is still waiting to be completed. It's like pretending to view the stackoverflow.com website immediately after having typed the URL. You can't.
That's where closures come in handy. You can use closures to execute something when another action has been completed. In this case, I would fire the web request, display a loading spinner to notify the user that something is loading, and finally populate the followersLabel along with stopping the animation. You can do something like that
func callAlamo(url: String, completion: #escaping ([User]) -> Void) {
if Connectivity.isConnectedToInternet {
Alamofire.request(url).validate().responseJSON { response in
let userData = self.parseData(data: response.data!)
completion(userData)
}
}
}
Additionally you need to let the parseData method to return the parsed array of Users, so the callAlamo function could use it.
func parseData(data : Data) -> [User] {
do {
return try JSONDecoder().decode([User].self, from: data)
} catch let jsonErr {
print("Error serializing", jsonErr)
return [User]()
}
}
Finally, you can execute the callAlamo function on inside the configureView method, performing an action when the server request has been completed. In our case, we want to populate the label.
private func configureView(){
followersLabel.text = String(followers)
// Starting the loading animation
startAnimation()
callAlamo(url: "Hello") { userData in
// Assigning the callAlamo result to your followers array
// once the server request has been completed
self.followersAndFollowingArray = userData
// This will return the number you'd expect
print(self.followersAndFollowingArray.count)
// Stopping the loading animation
stopAnimation()
}
}
Right now you probably won't have the startAnimation and stopAnimation methods, but you can feel free to implement them, I just wanted to give you an idea of a classic implementation.

Purpose of Disposables.create() in RxSwift

I'm learning RxSwift and I've come across the following pattern when creating Observables:
return Observable.create { observer in
let disposable = Disposables.create()
// Do some stuff with observer here
return disposable
}
As far as I can tell the returned Disposable doesn't actually do anything, does it serve a purpose other than to meet the requirements of the API to return a Disposable?
Is there any scenario where you might need to return a configured Disposable?
I suppose the thing that's confusing me the most is that the returned Disposable seems separate from the implementation of the Observable being created, i.e. it's not assigned to any properties or passed anywhere it's just created and returned.
There are two variations of the create method in relation to Disposables.
The first one, as Daniel mentioned, is used when you create a new Observable; you'll use the Disposables.create { ... } closure to "do cleanup", basically.
This is highly useful when using flatMapLatest, as your previous request will be disposed when a new ones comes in. Whenever it would be disposed, that "clean up" block will be called.
Observable<Int>.create { observer in
let someRequest = doSomeLongRunningThing { result in
observer.onNext(result)
observer.onCompleted()
}
return Disposables.create {
// How can I "cleanup" the process?
// Cancel the request, for example.
someRequest.cancel()
}
}
The second variation of Disposables.create is used for an entirely different purpose - grouping several Disposable objects as a single disposable object (a CompositeDisposable).
For example:
let disposable1 = someAction()
let disposable2 = someOtherAction()
let compositeDisposable = Disposables.create(disposable1, disposable2)
The Disposables.create function takes an optional closure. You should put any cancelation code in that closure. If you don't have any way to cancel, then the code is empty.
A good example is the wrapper around URLSession's dataTask method. In non-Rx code when you call URLRequest.shared.dataTask it returns a URLSessionDataTask object which can be used to cancel the network call. That object's cancel function gets called in the disposable.
Another common use is when you subscribe to some other observable from within your create closure. You then have to pass the disposable from that/those subscriptions by returning a Disposables.create(myDisposable) So that those subscriptions will get canceled properly when your Observable is disposed of.

Concern about memory when choosing between notification vs callback closure for network calls?

Many posts seem to advise against notifications when trying to synchronize functions, but there are also other posts which caution against closure callbacks because of the potential to inadvertently retain objects and cause memory issues.
Assume inside a custom view controller is a function, foo, that uses the Bar class to get data from the server.
class CustomViewController : UIViewController {
function foo() {
// Do other stuff
// Use Bar to get data from server
Bar.getServerData()
}
}
Option 1: Define getServerData to accept a callback. Define the callback as a closure inside CustomViewController.
Option 2: Use NSNotifications instead of a callback. Inside of getServerData, post a NSNotification when the server returns data, and ensure CustomViewController is registered for the notification.
Option 1 seems desirable for all the reasons people caution against NSNotification (e.g., compiler checks, traceability), but doesn't using a callback create a potential issue where CustomViewController is unnecessarily retained and therefore potentially creating memory issues?
If so, is the right way to mitigate the risk by using a callback, but not using a closure? In other words, define a function inside CustomViewController with a signature matching the getServerData callback, and pass the pointer to this function to getServerData?
I'm always going with Option 1 you just need to remember of using [weak self] or whatever you need to 'weakify' in order to avoid memory problems.
Real world example:
filterRepository.getFiltersForType(filterType) { [weak self] (categories) in
guard let strongSelf = self, categories = categories else { return }
strongSelf.dataSource = categories
strongSelf.filteredDataSource = strongSelf.dataSource
strongSelf.tableView?.reloadData()
}
So in this example you can see that I pass reference to self to the completion closure, but as weak reference. Then I'm checking if the object still exists - if it wasn't released already, using guard statement and unwrapping weak value.
Definition of network call with completion closure:
class func getFiltersForType(type: FilterType, callback: ([FilterCategory]?) -> ()) {
connection.getFiltersCategories(type.id).response { (json, error) in
if let data = json {
callback(data.arrayValue.map { FilterCategory(attributes: $0) } )
} else {
callback(nil)
}
}
}
I'm standing for closures in that case. To avoid unnecessary retains you just need to ensure closure has proper capture list defined.

Methods that use completion blocks and return an object

I have a method that inits the object and it has a completion block: typedef void(^initCompletionHandler)(BOOL succesful);
In this method I want to call the handler but I am not sure how to do it because if I call it before the return the object won't be finished initialising which is used immediately in the next line. I also obviously can't call the handler after the return. i,e:
if(haveError){
handler(NO);
}
else{
handler(YES);
}
return self;
Is there any way I can return and call the handler at the same time?
A couple of observations:
I'm unclear as to why you say "because ... the return object won't be finished initialising." You're doing the initialization, so just ensure it finishes all of the associated initialization before calling that handler. If the issue is that the caller won't have a valid reference to that object yet, you could always include a reference to it in the parameter of the block, e.g.
typedef void(^initCompletionHandler)(MyObject object, BOOL succesful);
and then supply that parameter, e.g.:
if (haveError){
handler(self, NO);
} else {
handler(self, YES);
}
Also, you say "I obviously can't call the handler after the return". But you can. You could just do a dispatch_async, if you wanted:
dispatch_async(dispatch_get_main_queue(), ^{
if (haveError){
handler(NO);
} else {
handler(YES);
}
});
return self;
That's a little inelegant, as if you call it from another thread, you have some potential race conditions that you might have to coordinate/synchronize, but you get the idea: You don't have to call the handler synchronously.
Having made both of those observations, I must confess that I'm not a fan of having init actually launching some asynchronous process and having its own completion block. I'd be inclined to make those two different steps. If you look at the Cocoa API, Apple has largely shifted away from this pattern themselves, generally having one method for instantiation, and another for starting the asynchronous process.

Resources