Using Parse findObjectInBackground() to set up a View in iOS - 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)

Related

iOS - Constant listening to database update implementation

I have a project where I have 4 tab bars, and when i switched tabs, my api to get the API request to update my view is in the method viewDidAppear().
However this will make a very bad UX experience for user as whenever they switch tab, a loading icon and some times it load for 0.5 seconds but that instant appearance and disappearance of the loading icon is really bad for UX perspective. How can i implement a view where whenever the database value change, my view is automatically updating itself?
I have thought of using Timer() to implement where it calls the api every second but this is not practical as it will be constantly calling the API every second.
Does anyone have any suggestion? Thank you
There's a lot to unpack here, but I'll try to provide a generalized answer and point to several possible solutions to the UX problem.
There's several aspects that can play a role here:
Do you control the backend the data comes from, i.e. is it possible to follow paiv's comment and implement push notification? Or perhaps use some other means than pure HTTP(S) requests, like a websocket?
How frequently does data change? Is it realistic that it does so in between switching from one tab to another?
On that note, what do you do when data changes while your user is on one tab? Does that get refreshed in any way? Is it a problem if they don't get informed about this immediately? Is there a refresh button?
Do you persist the data beyond a view's on-screen time at all? Is it scrapped when a user re-visits a tab, i.e. does a tab always start "empty" until the API response is handled or do users see the values from the last time they visited that tab?
What best to do depends a lot on the exact answers to these questions. If you cannot enable the backend to do anything besides serving HTTP requests AND have to keep all data up-to-date constantly then obviously you have to do something like the polling you described. The update-interval depends on how important this is for your users. Even if it is 1 second, be aware that this means they could visit a tab before the next update has been fetched.
If you can adapt the backend and up-to-date data is a must, then push notifications or perhaps websockets are a way to go (keep in mind that websockets mean one open-connection per active user then!).
Push notifications would probably break down when the update interval is too high (i.e. if data gets changed in the backend very quickly and you keep spamming them), but would work nicely otherwise, plus they would update views that are already on screen if used correctly.
In 90 % of the cases I've seen, however, going with regular HTTP requests and a properly designed UI is the best choice. Properly decoupling the UI code from the data loading code and the code that displays the data is also very important. Here's how I would handle this:
Properly persist the data in some way in the app that does not require on views owning the data alone. If you want to persist beyond app runtime that means CoreData (or an equivalent), otherwise an in memory storage may be fine (that is kind of a global state then).
In your UI, show any data you currently have, even if it is "outdated" (in your case: "left over from the last time the user was on this tab"). Design the UI so that this is clearly apparent (grey it out, put tiny activity indicators next to the data while it is being loaded anew/updated, or something similar).
As said in 2. do show if the app is loading data. Initiating the loading when the users go to a tab is okay, but don't make a UI that is completely dominated by it (like one giant, empty view with a spinner on it).
Functionally, the loading should not directly update any views, instead it writes new data to your storage and just informs views and view controllers that they need to update themselves (if you're using CoreData and/or Combine you get a lot of this logic basically for free, especially for a SwiftUI app).
Such a design also lends itself to later being adapted for push notifications: While at first the re-loading is triggered by a tab switch (or a "refresh" button, for example), once (silent) PNs are available, they trigger the reloading. Even websockets can be integrated into this more or less, though there's probably a bit more to do that.
In any way, as said it all depends on your use-case and also available resources, but the gist of it is probably "If the current loading icon you have destroys the UX, redesign it properly". My gut feeling here would be that anything else might be over-engineering for your needs...

How to structure the network requests on an iOS App

So, I'm still an unexperienced developer and I need some help with an app I'm currently developing.
The app consists of different tab-views. The first two of them rely on data that is stored on a database. The first database I created myself with Firebase, because it is a news-section, which I need to update regularly myself. The second tab-view needs to request simple data from two different databases, which both return data in the JSON format. The App then should save the data of both databases as custom objects in two different arrays, which both will be used to compare the data sets and display some of the data in a table view.
The thing I'm struggeling with is where to trigger those network requests without compromising the user's experience. What is the best practice to do so? At the moment, the first network request is in the viewWillLoad() method of the associated viewcontroller. But I'm not quite happy with that because there is a small delay between opening the app and displaying those news. Additionally is it better to download the data and then save it locally on the device and compare it to the data online or to download it everytime? It's not a lot of data and just text - no pictures, videos whatsoever - and little pieces of the data change regularly and most of the data changes only twice a year)
Thanks for helping me out here, because I somehow did not find a lot of information on how to structure those requests and I hope that some more experienced programmers might be able to help me here.
You can kick off network tasks in your AppDelegate in:
func applicationDidBecomeActive(_ application: UIApplication) {
}
Just make sure you throttle it so you don't do it too often.
Generally, you want to cache/store your network results and display the cached values. This is especially important if the user is in an area with limited or no network availability when they launch your app.
If you're using CoreData then updating the data will update your table view automatically. If not, then you can call reload() on your table view in the completion block of your network update, or post a notification that your view controller is listening for, and update then.
Either way be sure to update using a URLSession data task on a background queue.

Add UIProgressView for [PFObject pinInBackground] to monitor when pin is complete

I think this is a fairly simple question...but we'll see about that.
My setup:
Xcode: 6.3.2;
Parse: 1.7.4;
Language: Obj-C
I have a bunch of PFObjects that are displayed in a TableView and within each Cell there is a button that the use can tap to pin that individual PFObject to localDatastore, so if they were to lose WiFi later on he/she could still access that object and view its contents.
What I would like to do is display a UIProgressView/UIProgressBar to monitor the progress of the object being pinned (some of my objects contain large files that may take up to 30 seconds to pin). This way the user knows for sure that the object has been saved completely and there is smaller chance they will assume the object save immediately and turned off WiFi or something else happens to jeopardize his/her internet connection.
Is there straightforward way to do this with Parse??
Negative.
PFObject currently doesn't have a 'save' with a progress block like a PFFile does (https://parse.com/docs/ios/api/Classes/PFFile.html#//api/name/saveInBackgroundWithProgressBlock:). See SDK reference here for future references. This is a great resource I suggest you save it to your bookmarks, not specifically the PFObject page but the SDK API reference in general: https://parse.com/docs/ios/api/Classes/PFObject.html
There are ways to do this, mostly personal preference, however its just like any other async call, you will have to populate a progress HUD yourself, since its not API friendly yet. This is a fairly simple process to start yourself, and as a developer you should learn how to do this anyways, you learn a lot about network calls and how tasks operate on threads etc. In the meantime, as a quick fix you could simply just populate a UIActivityIndicator collectively with pinInBackgroundWithBlock: and set userInteractionEnabled to No for whatever views you want simply as a visual aide on the users end, and then hide it when complete and set userInteractionEnabled back to Yes.

Dealing with asynchronous download delays before displaying data

My app connects to a Parse server asynchronously and downloads the necessary data into the app's Core Data store . I would then like to display this data in a tableview. But in most cases -- as the connection is asynchronous -- the table view can access the data store much faster than the downloaded does . In this situation, I get an empty table view cell, and just after that the data is ready in the data store.
What is the best way to deal with the delays caused by asynchronous downloads? Is there a concept that I'm missing? Is it NSFetchedResultsController?
What do you think is the best way to deal with the delays caused by
asynchronous downloads?
It depends on requirements you have. In particular, if the user can interact with UI during async downloads, you can do nothing on it, otherwise you could use just a spinner to alert him something is downloading and stop the interaction until the sync as finished.
Anyway, in both cases, you should say something about the download. In particular, are you saving data in a different thread (different from the main one)? If so you should merge the changes from the context you use in background to the context associated with the NSFetchedResultsController (always the main one since NSFetchedResultsController manages UI elements).
Is there a "concept" that I miss and is it NSFetchedResultsController?
Did you setup correctly the delegate NSFetchedResultsControllerDelegate? If so, the NSFetchedResultsController tracks changes on the entity you registered on your fetch request. Not changes will happen for other entities.
Asynchronous is a design problem that you will need to work with. Look to some of the other popular applications in your field and see how they solve it. Do they show a spinner (I personally hate that) or do they show some unobtrusive indicator that data is being downloaded (better)?
If you use a NSFetchedResultsController (which I am guessing by your question you are not currently) you will get the data displayed once it is saved in Core Data with no additional effort on your part. So you can at least show the data as soon as possible.
In the meantime, I recommend let the cells/table be empty and let the user know that your app is working. Display the data as soon as possible. Perhaps consider downloading the data in pieces so that they can start to see it asap.

GCD for network task iOS

I am using GCD for networking task. I have a table view controller loading data from a server but once I leave this controller and launch another view controller the application crashes. I cannot find a solution to this problem. The response from the serve is an NSArray that is the data source for the table view. I set a data source property in the controller when I get the response. I override the setter and check if the current view is the table view and if it is I reload the table. Am I approaching this the incorrectly?
Thanks
Sounds like the error could be in a wide variety of places. My advice is to break the problem down into progressively smaller pieces. This is not necessarily fast and easy. But you will find the issue (and learn a lot in the process).
As a first step try replacing the data coming from the network with some local data. If you still have problems then you can probably rule out code handling the network response.
Or you can go the other way: just the examine the data returned in the request. Don't pass it along to the app. Perhaps you'll notice empty, improperly formatted etc data in the response.
Note: I don't think your server is returning an NSArray. Make sure the distinction is clear (you're probably receiving some json formatted data and parsing it).

Resources