CoreData Background Fetch with Webservice? - ios

I would like to update some data on my device in the background. For example:
Ill got a webservice, which returns me some changes that i need to store into my core data. So my goal is to load the JSON data with Alamofire, loop through all my json objects, and store them into my database. When everything is finished, i would like to update my UI.
The changes could be some hundred rows, so i would like to perform this on the background thread. When ill use:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
var context = appDel.managedObjectContext
do {
tasks = try context.executeFetchRequest(fetchRequest) as! [Tasks]
}
catch {
fatalError("Fetching from the store failed")
}
for task in tasks {
// check something here, or set some new values
}
// save them in background? But how do i know if save is completed?
dispatch_async(dispatch_get_main_queue()) {
// update my UI
}
});
Will this all run in background, or do i need multiple contexts here? Ill read somewhere else, that when ill load my managedObjectContext inside a Thread, the whole Fetch runs in background. And how do i know when my context has finished saving, so i can refresh my UITableView?
Thanks in advance!

You should perform the background code within context.performBlock {} instead of dispatch_async
Call completionHandler(.NewData) when you're done

Related

NSAsynchronousFetchRequest - should update be explicitly done on main thread

I am creating an NSAsynchronousFetchRequest which has a completion block inside it.
I have seen various examples where some include using dispatch queue on the main thread and others don't. For example the Ray Wenderlich core data book doesn't call the result on the main thread.
Should I go back on the main thread when executing the result. Initially I thought I had to but now I don't. Some definitive clarity would be great.
fun exampleFetch(_ completionHandler: #escaping () -> () {
let fetchRequest = NSFetchRequest<NSDictionary>(entityName: "Example")
let asyncFetchRequest = NSAsynchronousFetchRequest<NSDictionary>(fetchRequest: fetchRequest) { result in
// DispatchQueue.main.async { // is this needed
completion()
//}
}
managedContext.performChanges {
do {
try self.managedContext.execute(asyncFetchRequest)
} catch let error {
print("error trying to fetch saving objects:", error.localizedDescription)
}
}
}
You should not explicitly call the completion handler on the main queue. Let the caller decide how to handle it. If anything, document that the completion handler will be called on an arbitrary queue. Then the client calling your exampleFetch method knows that it is their responsibility to be sure that process the result on whatever queue it needs.
This gives the client more control.
This also prevents a lot of needless thread switching. A client may call exampleFetch from a background queue and it may want to process the results in the background. If you explicitly put the completion on the main queue, the client then needs to explicitly switch back to a background queue to process the result. That's two needless queue switches and it's wasted effort on the main queue.

Download Firebase Storage Synchronously

I am working on downloading an image from Firebase Storage and displaying it on a table view. I have been using this:
referenceOfImage.data(withMaxSize: 100 * 1024 * 1024) { data, error in
if let error = error {
print(error)
} else {
guard let data = data else {
print("no data")
return
}
guard let image = UIImage(data: data) else {
print("no image")
return
}
//use image
}
}
However, according to documentation, the task
Asynchronously downloads the object at the FIRStorageReference to an NSData object in memory.
I am currently using a loop to download multiple images and it would work better if I could synchronously download the images (otherwise loop would continue and the task would be incomplete). How can I download the image synchronously? Thanks!
You can not. These methods are asynchronous because they require server calls, and making them synchronous would block the main thread and cause very poor UX and performance. You could set up your completion call to do a bit of recursion, perhaps?
Stick the image load into a function that takes an array of things to fetch, the current index, and a selector to call when finished. Have a terminating condition (index == array.count), which calls the selector you want to happen when all images are loaded, otherwise fetch the image at the index, and in the completion handler, increment the index and fetch the next image by calling the same method.

How is that possible to insert data in asynchronous mode using FMDB in swift?

I'm just developing an application about to save a notes. I have single view. on a view I have a 3 page. just like swiping from 1 to second, second to third, and i'll just load the data from the database.. something like that. I have large number of data of single note. because in a noteview so many functionality that can not I describe here. so I just want insert data in a background mode or asynchronous mode using FMDB.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0) { // 1
dispatch_async(dispatch_get_main_queue()) { // 2
}
}
this is not work for me when i move to from one view to second view. it wait to insert data from one view and then go after to second view.
You should use background thread. In your thread it's just run on the main queue, after the previous code in outer block. So to solve your problem :-
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
//This is run on the background queue
})

Keep my function from blocking functionality while getting info

I have a function with a completion handler that works to download videos from youtube's API.
On the first run, it works great. On subsequent runs, since the videos are stored in the device, and shown before the array of videos is updated, I would like for the user to be able to interact with the table, while it is updated (if required). However, while the information is being downloaded, the interaction with the table or the app is blocked.
I guess this has something to do with Grand Central Dispatch, but I don't know how to use it.
Networking().getPlaylists() { (result: String) -> () in
self.activityView.removeFromSuperview()
print("should reload data")
self.tableView.reloadData()
}
}
Could somebody give me some pointers?
Ideally your network code would execute on a background thread:
func getPlaylists() {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
//Do network stuff
})
}
but also before you call your completion, make sure you flip back to the main thread before you do UI logic like updating your tableview:
func getPlaylists() {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
//Do network stuff
dispatch_async(dispatch_get_main_queue()) {
completion(results)
}
})
}
It was actually not Alamofire that was blocking my UI (it is actually an async function), but it was SwiftyJSON while I was parsing. I solved it by dispatching_async but over the parsing section and not the rest of the function!
See more here: Alamofire http json request block ui
Thanks!

Bad access when trying to clear and save different contexts

I need to perform periodic updates of the data I persist with Core Data. I get such data from asynchronous calls to REST services. To firstly retrieve all the data, I create a full core data stack in a private queue and then I do this:
- (void)updateDataFromServices
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
self.dataUpdatePrivateContext = [MyCoreDataStackHelper getPrivateContext];
if (self.dataUpdatePrivateContext != nil) {
[self.dataUpdatePrivateContext performBlockAndWait: ^{
// Asynchronous operations
[self callService1];
[self callService2];
[self callService3];
}];
}
}
In the callback of each service that is called, I check that the rest of services have already finished too, and if all have finished I call a method (manageInfoUpdate) to handle updates between the data I have in my main context (in main thread) and the data I now have in the private context (in the private queue):
- (void)manageInfoUpdate
{
const char* UpdateInfoQueue = "com.comp.myapp.updateinfo";
dispatch_queue_t queue = dispatch_queue_create(UpdateInfoQueue, NULL);
dispatch_async(queue,^{
// Handle updates from private context:
// Here I compare objects in the main context with the objects
// in the private context and I delete objects from both
// by calling:
[mainContext deleteObject:object];
[self.dataUpdatePrivateContext deleteObject:object];
// This seems to work...
// Save and clear private context
[self saveContext:self.dataUpdatePrivateContext];
[self clearContext:self.dataUpdatePrivateContext];
dispatch_async(dispatch_get_main_queue(), ^{
// Re-fetch from main context to get
// the updated data
// Save main context
[self saveContext:mainContext];
// Notify end of updates
});
});
}
I try to perform the manageInfoUpdate operations in another async thread. I'm getting EXEC_BAD_ACCESS exceptions when trying to clear / save the contexts... Could somebody help me to find why?
Thanks in advance
You won't get an outright "error" for using Core Data in a multi-threaded environment incorrectly. The app just will crash, sometimes.
To confirm you are using it correctly, turn on the debug flag com.apple.CoreData.ConcurrencyDebug 1 in your runtime arguments. Then it will crash EVERY time you touch a MOC or MO from the wrong queue.
As it stands you code is not correct at all with regard to threading. A MO that is created on one queue can only be accessed from that queue. Likewise, a MOC that is configured for the main queue must be accessed on the main queue and a MOC configured as a private queue must be accessed on ITS private queue.
Your "UpdateInfoQueue" is violating the threading rules completely.
Turn on the debug flag, correct the errors it shows you and your save issue will be corrected.

Resources