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
Related
This situation is a bit too elaborate to describe in mere words, so I've created a minimal demo project for you to download and run:
https://github.com/mattneub/DefaultsDemo
You will need Xcode 10 to test. This is what the project looks like when it runs:
The project code will appear to have quite a bit of unnecessary kerfuffle, but that is because I've included the minimum material from my real app (a card game) to reproduce the issue I'm seeing. There's a deck of cards and a layout of cards, and the idea is when the user clicks the Home button, we get a notification that the app will resign active, and we save the deck and the card layout into UserDefaults. When we relaunch, we retrieve the deck and the card layout and the user can resume playing.
But it's not working correctly, as I shall explain. And actually testing to see the phenomenon is very simple:
Run the project on the simulator.
Hit the Shuffle button three to five times or so. (If you start to run out of cards, hit the New Deck button. But in my testing, that's not usually necessary.)
Hit the Home button on the simulator. This causes us to save into UserDefaults. Look at the Xcode console; it tells you have many cards are in the deck that was just saved into UserDefaults. Remember that number!
Back in Xcode, stop the project and run it again, starting again at Step 1. As we launch, you are told how many cards are in the deck that was just retrieved from UserDefaults.
Keep repeating those four steps. What happens to me is that after about two or three cycles, the number in step 4 differs from the number in step 3. We are not getting back from UserDefaults the same deck that we saved into UserDefaults a moment ago!
Moreover, I can say something more about this wrong number: it is the number of cards in the deck that we saved into UserDefaults on some earlier occasion. It is as if UserDefaults has become corrupt silently in some way and has stopped accepting new versions of the deck.
To illustrate, I have just cleaned the simulator and opened the project, and I'll run it and tell you what I see in the console (with comments explaining what I did):
81 // launch
65
starting fresh 65
62 // shuffle
59 // shuffle
56 // shuffle
saving 56 // Home button
Okay, let's launch again from Xcode:
retrieving 56 // launch
53 // shuffle
50 // shuffle
47 // shuffle
44 // shuffle
saving 44 // Home button
Fine, so let's launch again from Xcode:
retrieving 56 // launch
That's the bug! We've fallen behind; we're retrieving the deck from the previous save, not the one from the save we just did. (I can actually prove this by printing out the deck; it has a description property for just this purpose, and I can actually see that it's the deck from the previous save.)
So the question is: why? I know how to work around it; save into files instead of UserDefaults. But I'd like to know what's up with UserDefaults here that's causing this problem. I suspect that one of the objects I'm saving into UserDefaults is causing some sort of silent corruption, but I don't see how that can be, as they are just Data objects.
(By the way, calling synchronize changes nothing, and in any case it is deprecated according to the header, even though not yet formally so.)
I'm going to suggest this entire project is a red herring and that the problem is your testing procedure.
I theorize that UserDefaults saves slowly: it just takes a really long time for a change to UserDefaults to "take", even after every signal has occurred that the change has happened (synchronize has returned, the didChange notification has arrived, and so on).
Thus, I'm betting that you're just not allowing enough time between hitting the Home button and running the app again. To see that this might be so, make the following change: Instead of going directly from Step 3 to Step 4, interpose Step 3a, where you count to 10, slowly. Now the bug doesn't manifest itself.
(One could also argue that saving at willResignActiveNotification time is just wrong. The time to save is when the relevant data changes! Thus, in the test example, we should be saving at the end of every shuffle-and-deal operation — not when the user hits the Home button.)
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.
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)
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.
I have an app where the users enters certain information. Each time the user wants to, he adds a new item to a table list. When the user is done, they click the finalize button to finish the list and they can start another list.
In the AppDelegate.swift file, I see a few preexisting functions. applicationDidEnterBackground says to save information before the application enters the background (I'm assuming it's when the user presses the home button while using the app...).
In my app, each time the user adds items to the list, then goes to home screen and locks the phone, after a while the items are gone. So the information they added don't save. I'm thinking to use the functions in AppDelegate.swift to save information and load information each time the app is minimized or maximized. My question is, what is the best way to save this information? Would I just write to a file and read from a file or is there a way to automatically save and load this information? I've read something about saving objects to a .plist but I don't necessarily know how it works since when I click the info.plist it just takes me to table of settings and no code.
Thank you in advance for the help!
Disclaimer: I am an experienced Objective-C developer, but quite new with Swift. My answers are going to be Objective-C oriented.
You have lots of options. The other poster suggested Core Data, but that is likely overkill unless you have complex information to save. Core Data is a big, complex framework with a steep learning curve. It's very powerful, but expect to spend a week or two of intense study before you're able to use it with any fluency.
The easiest and cleanest way is often to use NSUserDefaults. It's a Cocoa class that reads/writes key/value pairs to a system-maintained file in your app's sandbox. (Essentially a dictionary that is automatically read/written to a system file.) You don't need to know about the details of how it works - it just works. See the Xcode docs on NSUserDefaults for more information.
Another option is indeed to use a property list. Cocoa container objects like NSArray and NSDictionary include methods to save and read themselves to/from property lists. Look for methods like writeToFile:atomically:.
For saving to both NSUserDefaults and property lists, the object you save, and any object(s) it contains (if it's a container object) must be members of a very short list of "property list" objects (dictionaries, arrays, strings, numbers (integer and float), dates, binary data, and Boolean values.) Do a search in the Xcode docs on "Property List Types and Objects" for more information on property lists and the supported data types.
A third option is to implement the NSCoding protocol in all the objects that you want to save (your entire "object graph") and then to save the root object (often a container object) to disk using the NSKeyedArchiver method archiveRootObject:toFile:
With all of these options, you can implement the app delegate method applicationDidEnterBackground, and save your state data when it's called. You'd then load the data back in in your applicationDidFinishLaunching method. (You don't need to load your saved state data back when you return to the foreground, because if you are returning to the foreground, your app is still running and it's state data is still in memory.)
Various of the Cocoa touch classes also include support for automatic state restoration, but that's more than I have time to cover here.
I will suggest use core data for data persistance
Please go through ray wenderlich tutorial for core data