Updating tableview with UIRefresh - ios

The title of my question makes this look like a duplicate, but it's much more complicated of an issue than the title suggests, so please read through this post before marking it as a duplicate.
I have a tableview that uses UIRefresh for pull-to-refresh. When the tableview refreshes, it gets info from CloudKit. As the first answer to this post (Swift pull to reload duplicates objects in table instead of updating) suggests, I clear my old list before loading data from CloudKit. This means that, while the UIRefresh is active, my tableview goes through three states:
[populated with old data] -> [no data at all] -> [populated with new data]
If the user tries to tap a cell during the second phase, unsurprisingly the app crashes because the view controller I segue to depends on the info from the cloud.
I can think of 3 potential solutions to this problem, and I am hoping to get advice from more experienced iOS programmers on what the best approach would be (if any of these).
1) Save the old table list so that if the user taps a cell in phase 2 of the loading process, I can somehow have the app access that list.
2) Don't let the user tap cells while UIRefresh is active. (This seems unideal because I'd like to add an auto-refresh feature at some point.)
3) As the StackOverflow post linked to above mentioned in its second answer, I could pull down from the cloud only the most recently modified items...but this, I think, would be messy for my situation because any of the cells could have been modified, and all of them will most likely need to be refreshed in my case.
Any advice on best practices here would be much appreciated! Thanks!

My suggestion would be to only modify your data once the new data arrives. After replacing the data immediately call tableView.reloadData()

My solution #1 seems to work pretty well. Not sure if there are pitfalls to it that I haven't discovered yet, but it seems to work pretty well.

Related

What problem viewContext.setQueryGenerationFrom is trying to solve in CoreData project which involves transaction history?

I have came across 2 demo CoreData projects, which involves transaction history.
Both are using
viewContext.setQueryGenerationFrom(.current)
when they initialize their CoreData stack.
FireballWatch demo from raywenderlich
The demo is picked from https://www.raywenderlich.com/14958063-modern-efficient-core-data
The author is trying to demonstrate, how to make use of transaction history, to update UI correctly after batch insertion.
However, it isn't clear on what problem viewContext.setQueryGenerationFrom(.current) is trying to solve.
Code : https://github.com/yccheok/FireballWatch_Materials/blob/main/final/FireballWatch/Model/Persistence.swift#L100
Brief explanation of the article https://www.raywenderlich.com/14958063-modern-efficient-core-data doesn't tell much about the idea behind setQueryGenerationFrom.
You are pinning the view context to the most recent transaction in the
persistent store with the call to setQueryGenerationFrom(_:). However,
because setting query generation is only compatible with an SQLite
store, you do so only if inMemory is false.
Synchronizing a Local Store to the Cloud from Apple
The demo is picked from https://developer.apple.com/documentation/coredata/synchronizing_a_local_store_to_the_cloud
It is trying to demonstrate, how to use transaction history, to prevent data duplication after syncing with CloudKit.
However, it is still not clear on what problem viewContext.setQueryGenerationFrom(.current) is trying to solve.
Code: https://github.com/yccheok/SynchronizingALocalStoreToTheCloud/blob/main/CoreDataCloudKitDemo/DataProvider/CoreDataStack.swift#L89
Not much explanation is given behind the idea on setQueryGenerationFrom.
Experiment
No matter whether I have included viewContext.setQueryGenerationFrom(.current), or excluded viewContext.setQueryGenerationFrom(.current) in my CoreData stack, I am having the same observation in both situations.
Able to observe UI update immediately, after I save a new NSManagedObject, with context.save called.
Able to observe UI update immediately, after I edit an existing NSManagedObject, with context.save called.
Able to observe UI update immediately, after I perform batch NSBatchUpdateRequest operation, with mergeChanges called.
Able to observe UI update immediately, after I perform batch NSBatchDeleteRequest operation, with mergeChanges called.
There are some good graphical explanation on what is doing by setQueryGenerationFrom
https://cocoacasts.com/what-are-core-data-query-generations
However, I fail to relate it to, what kind of real problem setQueryGenerationFrom is trying to solve.
Does anyone know, what problem viewContext.setQueryGenerationFrom is trying to solve in CoreData project which involves transaction history? Would be appreciate, if there is a solid demo code example, to show what kind of problem is solved by setQueryGenerationFrom. Thank you.
It just pins some snapshot of context, so all your following queries work with exactly that snapshot, independently of what's happened after pin-moment. It's like detached checkout from GitHub - everyone goes ahead but you work with out sandbox.
This is guaranty of consistency which could be needed for some sequence of requests between which no changes should happen.
To pin we use viewContext.setQueryGenerationFrom(.current)
To unpin and continue with kind-of-HEAD we use viewContext.setQueryGenerationFrom(nil)
Additional description is in Apple's article
Taking the demo project CoreDataCloudKitDemo, it is to support the case where device1 is editing a post, and device2 deletes it. You can take look for this piece of code:
// The selected post was changed, and the user isn’t editing it.
// Show an alert, and go back to the main view to reload everything after the user taps reload.
let alert = UIAlertController(title: "Core Data CloudKit Alert",
message: "This post has been deleted by a peer!",
preferredStyle: .alert)
With the magic of query generation, we will be able to immediately pop up this alert on the device1 which is editing the post, after device2 device deletes it.
On the main listing view, there are no issues, as the item will probably just be animated out of the list. The issue is for the details view, what is going to be shown, and what happens if the user edits the value the details view.
In samples codes, this is usually following
container.viewContext.automaticallyMergesChangesFromParent = true
As in the details view, usually we will be holding on to an object, and once it is deleted, the object itself should become invalid, and accessing it or the attributes will cause unexpected behaviours.

Deletion of information stored in data.archive

I'm new to coding in Swift, and I'm currently creating quite a simple app, where users can type information in textfields, which in the end is saved through dataArray, and later on sent to a server.
I currently have 2 problems, which I can't seem to find a proper solution to:
When I use dataArray, and connect text-fields to these Arrays, I need to do it for several ViewControllers. It works perfectly fine on the first Viewcontroller i did it, but on the second one, it says that there's some kind of "double-booking" on there Arrays. Is it possible to save 2 textfields to dataArray [0] and [1] and then later on, using the same dataArray for other textfields?
When I stored information from the first Viewcontroller (that worked), it, of course, keep showing the textfield information (like a specific type of material), whenever I run the simulation. But what I'd like to do is:
When text has been entered in a textfield on a viewcontroller, it has to save it to the data.archive if the app crashes or a call comes in (so the already entered data isn't deleted). (this works for the first viewcontroller)
When the data has been entered in the textfields, the general idea is, that the information is sent to a server, when a UIButton in the app is pressed. (this is where I'd like to remove the information from the data.archive, so the next time they open the app,the textfields are empty.
Sorry for the long explanation. Please feel free to ask question, if some of all this text isn't clear enough :)
Thank you
- Christian

Using Parse findObjectInBackground() to set up a View in iOS

I'm stuck between two ways of developing my application and am not sure which is best. I was hoping that somebody with a bit more experience or more understanding of Parse could help me.
I am building an iOS app with Swift and using Parse for my back-end. I really enjoy Parse and it's going well.
My question: Say I'm loading a new view. The view is driven by a Parse object, meaning I am setting up Labels, tables, buttons, etc. with data from the object. I load the object in the page load. In this scenario, should I be using the findObjectInBackgroundWithBlock() method? Or should I just be retrieving it, and not moving forward until I do?
Should I just be doing things in the background when the results do not drive the immediate next steps in my code? I am hoping this makes sense. I am running into an issue where if I find an object in the background, then I can't set a label on my view with data from that object until it is found and I have to set it inside the block.
Doesn't this kind of defeat the purpose of finding the data in the background?
The purpose of find data in background is not to block the thread. As I understood you, you have to wait for parse to finish getting all the informations, because you have to create your interface with these informations.
So what I would recommend is that you let the user wait, until the interface is ready. For example with a wait-screen or something like that. Or you block some elements which take some time to load. For example a large tableview takes quite some time to load from parse. Especially if your internetconnection isn't that good.
So you should use findObjectInBackgroundWithBlock whenever possible but only allow the user to access the view, after you've loaded all necessary data to create your view.
What you also could do is do an initial access to parse. So that you set everything up at app-start. That way, you don't have to bother the user later and the user has to wait only one time at the start of your application(or If he wants to reload the tableview. for example)

NSAsychronousFetchResult after the app is DidEnterBackground

I have a client app with coredata as its back end. Its simple enough, with two entities.
UPDATE: Using CloudKit as the sync service. I'm not really sure what is going on there. Except I can query and get results, incase things don't automatically work. The problem is, as I noticed with most third-party sync-service providers. 95% of the time, they all work. Its when I test it with a more than a few devices / simultaneous calls that some undesired change comes in.
This question is more about iOS and coredata than the actual syncing architecture.
there are times when there is definite sync data loss. I really can't tell when and how. That im still figuring out. Sometimes initial sync takes a long time (if theres existing data) and the user might close the app, (some people double press the home button and actually close apps!).
But no matter what I do, sometimes i miss an object, sometimes an attribute.
So I saw this NSAsynchronousFetchRequest and I thought about giving me an option to check if all local-data (coredata) is okay, to see if theres anything missing.
Perhaps i could use a simple predicate to see of some managedObject.title == nil and fetch its identifier. Collect those faulty objects and request the truth server for data for these objects? Is this a good use of NSAsynchronousFetchRequest?
If yes, when during the lifetime of the app would this be good?
Im thinking maybe after applicationDidEnterBackround would be a good time..? Then If I do get it, will need a good way to manage CoreData in the background!
If no, well.. Really don't know wat to do then.
Im trying to actually do this, will update with my results.
UPDATE: Question updated to reflect the use of Cloudkit

How is data typically reloaded/displayed on a device when only a tiny amount of data changes after updating the database? (Conceptually)

Here's and example of what I am talking about:
Take Twitter for iOS. Whenever you tweet, the tweet is sent to the database, and then it is also displayed on your device as part of the list of tweets.
How is the list of tweets that you see on your device updated after just sending one tweet? Here are some possible ways that I thought of how it could be done, but what Im asking for is which one is the best method of doing so:
The whole list of recent Tweets is re-downloaded from the remote Twitter server after sending a tweet (I highly doubt this, as this would take a relatively long time, when it really is just appending one Tweet to the array of Tweets displayed)
The local array that holds the Tweet objects is updated separately from the database (For example, it updates the database, and then updates its array with the same data you sent to the database, and never downloads the Tweet you just sent since you don't need to, because you already have it locally, since you composed it)
Is Core Data capable of updating the remote data server AND the array all in one (or relatively few) step(s)? (Sorry, if this is the obvious answer and if it sounds like I didn't look into it, but I did read about Core Data and started a tutorial. Its just that there is so much content that it would take me a whole day or two just to figure out if its appropriate for my application)
Is there an alternative way of managing this?
Also, if its one of the latter two ideas above, are you able to update the table view cells by just updating the local array and reloading the cells from that array without loading your one tweet from the database? I'm just curious about what would be the most efficient way of doing this.
So again, my main question reworded is: how do you keep data that you sent to a remote database and the local data (stored in a mutable array) in sync whenever you do a tiny single update (such as sending a Tweet) without having to reload all of the data from the database (when there is other content [i.e. other Tweets]) already loaded.
(I am aware that no one except Twitter developers know exactly how Twitter actually done, but I'm just using this Twitter functionality as an example. This same concept could be applied to any similar app.)
(Also, this is a conceptual question about dataflow, so I don't need to see any code, but suggestions to use different technologies like Core Data, or just updating an array will be appreciated.)
(I've been looking into this, and all the different ways of doing it, and it is becoming very time consuming, so I figured to ask you guys who have experience. Additionally, this could help someone else who has similar questions.)
(Sorry if it looks like I'm asking a bunch of questions, but I'm basically asking the same question in different ways, and offering possible solutions.)
Any insight is appreciated!
Immutable messages like tweets are actually quite easy to handle -- server side, and in your app.
When you send a tweet from your client to the server, you also update your "main context" (see "Managed Object Context") which in turn sends notifications to your controller (see NSFetchedResultsController which in turns updates your table view according your local model residing in the Managed Object Context.
Updating from the server is just merging the local tweets with the new ones added in the meantime.
Since there is no mutable tweet, synchronization is really no big deal. As mentioned in the comment, if there were mutable tweets (or any kind of messages) the synchronization will become much more complex.
Core Data will NOT automatically update a remote server. But there are solutions to "view" a remote database through Core Data - see NSIncrementalStore and a related third party libraries (AFIncrementalStore).
This is ridiculously trivial. You update your local database and send off the remote update at the same time.
You use the remote response to mark your local record as synched or try updating again later.

Resources