Object passed by reference will not exist. Swift - ios

I have an array.
var array:[customType] = [] // pseudo code
func Generate_New_Array(){
//initialization of generatedNewArray
array = generatedNewArray
for (index,element) in array{
async_process({
Update_Data_From_Web(&array[index])
})
}
})
}
func Update_Data_From_Web(inout object:customType){
download_process{
object = downloadedData
}
}
The question is , what will should I do if I call Generate_New_Array before Update_Data_From_Web will finish for each of elements. They will store value back to not-existing index in array. How to avoid problems with that.

You have a couple of options:
Make the Generate_New_Array process cancelable, and then cancel the old one before starting the new one.
Make the Generate_New_Array serial so that when you make a subsequent call to this method, it will finish the calls first. For example, you could have this enqueue an operation on a serial queue.
Regardless of which approach you adopt, if this is multithreaded code, make sure you synchronize your interaction with the model object (via GCD queues or locks or whatever).

Related

Swift's way to handle this situation instead of using bools

I have a function that I want only to execute if not executing currently.
I have used a bool variable to check the current execution.
Is there any other solution provided by Swift to handle this instead of using Bool?
guard
!isExecuting,
let currentNavVC = tabBarController.selectedViewController as? UINavigationController
else { return }
isExecuting = true
let first = currentNavVC.viewControllers.first,
let last = currentNavVC.viewControllers.last
var controllers = [first]
if first != last {
controllers = [first, last]
}
DispatchQueue.main.async {
currentNavVC.viewControllers = controllers
isExecuting = false
}
Bool variable: isExecuting
Note:
Tried using Semaphores(DispatchSemaphore) but they are of no help.
Also I am calling the above function in didReceiveMemoryWarning()
Any help will be appreciated and thanks in advance!!
I have a function that I want only to execute if not executing currently
You're looking for a lock. But locks of themselves are tricky and dangerous. The easy, safe way to get a lock is to use a serial queue. As we say, a serial queue is a form of lock. So:
If your function is called on the main queue, then it cannot execute if it is executing currently, and there is nothing to do. The main queue is a serial queue and there can be Only One.
If your function is called on a background queue, then make sure that your queue is a serial queue. For example, if you create your own DispatchQueue, it is serial by default.
I believe you also can use Operation with OperationQueue in this case.
Operation supports cancellation as well as checking if it is executing.
Ref:
OperationQueue: https://developer.apple.com/documentation/foundation/operationqueue
Operation: https://developer.apple.com/documentation/foundation/operation

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.

How to "loosely" trigger a function?

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.

Periodically save an object in Realm

I have a Realm object called Trip. It stores data of a user's movement.
class Trip: Object {
dynamic var id: Int = 0
dynamic var startTimestamp: Int64 = 0
dynamic var endTimestamp: Int64 = 0
dynamic var distance: Double = 0.0
dynamic var calories: Double = 0.0
dynamic var averageSpeed: Double = 0.0
}
In the view controller, I keep a class-level variable called trip.
fileprivate var trip: Trip?
Whenever a user starts a trip, I initialize a Trip object and assigns it to this variable.
trip = Trip()
And throughout the user's movements, I keep updating this trip object with the data.
I need to save this data to the Realm database every 1 minute. So I run a timer.
Timer.scheduledTimer(timeInterval: 60, target: self, selector: #selector(save()), userInfo: nil, repeats: true)
Which executes a function to save this object in a background thread.
fileprivate func save() {
do {
DispatchQueue(label: "RealmBackgroundThread").async {
autoreleasepool {
let realm = try! Realm()
try! realm.write {
realm.add(self.trip!, update: true)
}
}
}
} catch {
}
}
Up to here, it works fine. The problem is after the first save, when I try to access that trip object again, it crashes with the following error.
libc++abi.dylib: terminating with uncaught exception of type realm::IncorrectThreadException: Realm accessed from incorrect thread.
I think this happens because I open a new Realm to save this object in a background thread. I know that Realm isn't thread-safe.
But I'm not sure how to resolve this. How do I keep using the same trip object after saving it?
To quote from Realm's documentation about Passing Instances Across Threads:
Instances of Realm, Results, or List, or managed instances of Object are thread-confined, meaning that they can only be used on the thread on which they were created, otherwise an exception is thrown.
In your case, self.trip is a managed instance of an Object subclass, and you appear to be accessing it from both the main thread, and repeatedly from the serial dispatch queue you create within your save() method. It's important to keep in mind that the call to DispatchQueue.async will result in your code being executed on different threads depending on the whims of Grand Central Dispatch. That is to say that two consecutive calls like …
DispatchQueue(label: "RealmBackgroundThread").async {
// work
}
… will often result in the work being performed on two different threads.
Realm's documentation on Passing Instances Across Threads contains an example of how to pass a thread-safe reference to an object across threads and resolve it in the Realm instance on the target thread.
It's hard to provide a more specific suggestion in your case as I'm not clear what you're trying to do with your save() method and why you're calling it on a timer. Managed Object instances (instances retrieved from a Realm, or that have already been added to a Realm) can only be modified within a write transaction, so any modifications to self.trip would need to be performed within the write transaction opened by your save() method for it to serve any purpose. For the pattern of using a timer to make sense you'd need to leave self.trip as an unmanaged object, at which point save() would update the Trip instance with the same ID in the Realm file. To do this you'd want to use Realm.create(_:value:update:) instead of Realm.add(_:update:), as create does not convert unmanaged instances to managed instances like add does.

Capturing closure values in Swift

My question is very similar to several others here but I just can't get it to work. I'm making an API call via a helper class that I wrote.
First I tried a standard function with a return value and the result was as expected. The background task completed after I tired to assign the result.
Now I'm using a closure and I can get the value back into my view controller but its still stuck in the closure, I have the same problem. I know I need to use GCD to get the assignment to happen in the main queue.
this is what I have in my view controller
var artists = [String]()
let api = APIController()
api.getArtistList("foo fighters") { (thelist) -> Void in
if let names = thelist {
dispatch_async(dispatch_get_main_queue()) {
artists = names
print("in the closure: \(artists)")
}
}
}
print ("method 1 results: \(artists)")
as the results are:
method 1 results: []
in the closure: [Foo Fighters & Brian May, UK Foo Fighters, John Fogerty with Foo Fighters, Foo Fighters, Foo Fighters feat. Norah Jones, Foo Fighters feat. Brian May, Foo Fighters vs. Beastie Boys]
I know why this is happening, I just don't know how to fix it :( The API calls need to be async, so what is the best practice for capturing these results? Based on what the user selects in the table view I'll be making subsequent api calls so its not like I can handle everything inside the closure
I completely agree with the #Craig proposal of the use of the GCD, but as your question involves the request of the API call every time you select a row, you can do the following:
Let's suppose you use the tableView:didSelectRowAtIndexPath: method to handle the selection, then you can do the following inside it:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// it is just a form to get the item
let selectedItem = items.objectAtIndex(indexPath.row) as String
api.getArtistList(selectedItem) { (thelist) -> Void in
if let names = thelist {
dispatch_async(dispatch_get_main_queue()) {
artists = names
}
}
}
}
And then you can observe the property and handle do you want inside it :
var artists: [String] = [] {
didSet {
self.tableView.reloadData() // or anything you need to handle.
}
}
It just another way to see it. I hope this help you.
The easy solution is to do whatever you're doing at your print(), inside the closure.
Since you're already dispatch_asyncing to the main queue (the main/GUI thread), you can complete any processing there. Push a new view controller, present some modal data, update your current view controller, etc.
Just make sure that you don't have multiple threads modifying/accessing your local/cached data that is being displayed. Especially if it's being used by UITableViewDelegate / UITableViewDataSource implementations, which will throw fits if you start getting wishy-washy or inconsistent with your return values.
As long as you can retrieve the data in the background, and the only processing that needs to occur on the main thread is an instance variable reassignment, or some kind of array appending, just do that on the main thread, using the data you retrieved on the back end. It's not heavy. If it is heavy, then you're going to need more sophisticated synchronization methods to protect your data.
Normally the pattern looks like:
dispatch_async(getBackgroundQueue(), {
var theData = getTheDataFromNetwork();
dispatch_async(dispatch_get_main_queue() {
self.data = theData // Update the instance variable of your ViewController
self.tableView.reloadData() // Or some other 'reload' method
});
})
So where you'd normally refresh a table view or notify your ViewController that the operation has completed (or that local data has been updated), you should continue your main-thread processing.

Resources