Working with static text data on iPhone - ios

So,
I have a large set of static text that I will be using over and over again in the app such as postcodes, suburb names and etc which will get occasionally downloaded. To ease the need to download data whenever I need it, I'm downloading all the data and saving it locally in a plist format which is around 3MB size. I'm now thinking of a better way to handle that. So, I'm just wondering what would be the best way to handle large data in my case.
For plist, I have a static class which loads all the data into array for example,
+(instanceof) sharedInstance {
.....
self.myarray = [NSKeyedUnarchiver unarchiveObjectWithFile:PLISTPATH(#"suburbs.plist")]
.....
}
Thanks

I would suggest as 3 MB file when load with tons of objects every time it make your app very poor performing and might get killed by iOS, you should create a sqlite database for all this info and load the specific data based on ids.

Related

Should I use NSUserDefault, dictionaries, core data - or something else?

I'm having some issues with the app, that I'm making, which I thought would be a lot easier to explain with some photos, so ... :
Ofcourse the "Create New Person-button" in nr. 1 leads you to number two.
Now, I'm having issues figuring out how to save this data about the person in the "People Diary". The goal is, that when you enter a person's name, add a photo (an enable-camera feature, I will struggle with at a later time...) and add an answer to the question - then you only need to press "Save this person", and then you will be redirected to the AllPersonsInYourDiaryViewController, where there is now a new tableViewCell with this new person's name (maybe with a subtitle containing the answer and the photo shown in miniature in the cell too).
(Naturally you can then enter this cell with the data about the person too - but that comes next.)
So far in the app, I have used NSUserDefault, when allowing the user to create this specifik Diary by the Name "Antons Diary" with the specifik question and so on. But now it came to my attention, that maybe it is smarter to use something else? I tried with dictionaries, but couldn't get this to work properly.
So...: Before I spend hours and hours playing around with one of these ways, will someone smarter than me, tell me what the best approach would be?
If I can give my two cents, the first thing you have to do is to “design” how to represent a person programmatically. You can create a struct or class to do so, even though a struct is more suitable:
struct Person {
var name: String?
var answer: String?
var photo: String?
}
Then you can decide how to save the data of such an object persistently. If you want to use a database, then I would recommend using SQLite with FMDB library. It’s really easy and fast to learn how to use it, and it's also quite handy. I've used it big projects and it works smoothly. I find CoreData too complicated and an overkill based on what you need.
If you don’t want to use a database, your only other way is to save to files, but still, you’ve got options here too. If you encode (see Codable protocol in Swift), you can use NSKeyedArchiver to convert to Data object and write then to disk. If you like using dictionaries, and since the properties you’re going to have for a person are not going to be too many, you could create a dictionary by assigning the properties and their values, and then convert and save as JSON data, or even Plist files. Without any intension to do promotion here, but just to provide some additional help, if you want take a look to a library that I’ve written and that can do all these automatically for you. It’s a protocol that you have to adopt, and then you can instantly convert your struct to a dictionary, JSON or plist and save to files.
No matter which way you’re going to select, save the images as single files to documents directory, and keep their file names only stored to database/file. Based on them, you can build the path to each image (or the URL) easily when needed. Warning: Do not save the full path to the documents directory, especially if you’re testing on Simulator; paths are changing on each build. Save the file name only.
Additionally, if you’re going to use a struct like the one shown above, you could implement small but super convenient functions that will be responsible for saving, loading, or updating your data to the solution (database/file) you’ll eventually select. That way, you’ll have related stuff gathered in one place, and easily accessible (i.e., person.save()).
struct Person {
var name: String?
var answer: String?
var photo: String?
func save() {
…
}
func load() {
…
}
// More functions…
}
Lastly, avoid using UserDefaults, or at least keep just a few non-critical data there. UserDefaults are not meant to keep all data produced by your app. Most importantly, do not use it for saving sensitive data, especially passwords or other stuff like that.
I hope the above will help you make your mind.
I can give you the logic behind coreData and NSUserDefaults, but you will decide which one should be used.
CoreData is usually used as a database. you can create entities and attributes for every entity. Moreover, you can create relations between these entities.
When extracting data from coreData, you can arrange this data using NSSortDescriptor or select a specific record using NSPredicate.
So as you can see CoreData is a database.
While NSUserDefaults is usually used to save a password, username, userID... and such issues that you will regularly use in the app. NSUserDefaults gives you a direct access to the saved variables at any time. However, CoreData will take more time and lines of code to access the entity and make the query.
Now, check which method suits your case more.

Alternative to loading large text file - Swift

I am attempting to make a word game for iOS where I need to check words against a dictionary to determine if the word is valid.
Currently I am loading the dictionary from a text file and storing it in a set. However it takes a few seconds to load every time I start the app. The text file has approximately 250K lines.
Can I have the data available in an array or set without having to read a text file each time the app starts?
You should consider persisting your word file using, for example, Core Data.
On the first load of your app, you can populate your Core Data store (maybe in the background while the rules are being explained to the user).
Once this has been done you can query your words store using an NSFetchRequest
To use a pre-populated Core Data database you could start by populating in development…
let count = try! moc.count(for: request)
if count = 0 {
loadWords()
}
func loadWords() {
// run through your words.txt file and create object(s)
// for each word
}
Once you're ready to release, add your database files into the app's resources, then add amend your loadWords() func to copy these files from the app bundle to the Core Data store's url

What are ways to store complex dynamic objects locally (iOS, swift)?

I have iOS app that takes data from the server as json and then serializes them into objects of different types. Types can be complicated, can contain subtypes, can inherit, so there is no any limitations. Another thing that makes everything even more complicated is some of types are stored as AnyObject? and only in run time they are being serialized into real types accordingly to the specific rules. Something like that:
class A {
var typeName: String?
var b: AnyObject?
}
Then when it's serialized it can be done something like that:
if let someClass = NSClassFromString(typeName) as? SomeGenericType.Type{
b = someClass.init()
}
Also querying should be done on all the data. Currently I'm trying to store all of them locally, then load into memory and query there from the code. I'm using User defaults, but they have some limitations, also I needed to provide custom coding to make it work, and each time when I add a new field it turned out that I missed something in coding and nothing works. So it's pain.
Ideally I would just do some magic command and all the objects are sent to local storage no matter how complicated they are. The same to extract them from this storage. Also, user change data so I can't just store primary Json. And I don't want to covert objects back to Jason as for it's pain too.
Any suggestions?
If you want to use sqlite then You can store whole object in one row! I means you can create table with 2 columns one is id and second is your dataobject(it's data type should be blob). Then convert your whole object into data. Then store in sqlite table and retrieve it as data then convert it to object when want to use. By this way your object will remains in same format as you asked
Firebase while meant for online synching and storage can also cache everything locally in case you are offline and perform query's against the local cache. It uses JSON.
CouchDB also has a mobile version for iOS.
Both of those are over kill if your dataset is small; you can just store it as a text file and read the JSON back in. See performance characteristics here. The graph is for a 7MB file so if you are significantly less than that your load time may be minimal.
NSKeyedArchiver.archivedData(withRootObject:) is great for storing custom objects as Data objects. The only thing you need to do to be able to use this is to make your custom objects conform to NSCoding. A great example can be found here:
Save custom objects into NSUserDefaults
Once you have the Data version of the object, it can easily be stored in UserDefaults, as a property in CoreData, or even in the app's keychain entries. Depending on your use case, sensitivity of data, and how much data you intend to store, you might want to use any number of storage methods. NSKeyedArchiver.archivedData(withRootObject:) allows you to pretty much use any of them.

Best practice to process big plists?

I'm using a plist file which contains all app my data. The file is quite big and currently I'm loading all the stuff into Arrays and Dictionaries at first launch and save them into UserDefaults so that I don't have to touch the plist again. As this takes about 10 secs (iP4) I wonder if there is an even faster (better) way to process the plist. I checked the whole startup with Instruments and going through the hundreds of entries is actually the fastest part. It takes very long to save these processed stuff into NSUserDefaults.
You might benefit from saving the plist to your own file. That way you control the reading/writing, don't have any overhead associated with NSUserDefaults, and, most importantly, can ensure the format. That is, if reading/writing is producing the slow down, then you'll have to minimize the plist file size. Likely using a plist format of NSPropertyListBinaryFormat_v1_0 will do that:
See:
+ (NSInteger) writePropertyList: (id) plist
toStream: (NSOutputStream *) stream
format: (NSPropertyListFormat)format
options: (NSPropertyListWriteOptions) opt
error: (NSError **) error
From Apple's Property List Programming Guide:
The first approach [using NSDictionary or NSArray writeToFile] is
simpler—it requires only one method invocation instead of two—but the
second approach [as above] has its advantages. It allows you to convert the
runtime property list to binary format as well as an XML property
list. When you convert a static representation of a property list
back into a graph of objects, it also lets you specify with more
flexibility whether those objects are mutable or immutable.
Several points.
NSUserDefaults is probably just a big plist, so why use it? Stick your entries into a singleton that holds the in-memory structure.
If you're doing this on first load because you want it to be mutable, put the defaults into your resource folder. When you want to load it, check if you have it in the documents folder, and if you don't ( first load), copy it from the resource bundle to the documents.
If you're using NSUserDefaults for persistence, just write out your data to your plist in applicationShouldResignActive, and at any other times where you make important changes.
Write it in a background thread, but you probably need to do some locking here.
Best practise when load and save times become to big is probably move to core data, but 1-4 should give you some more mileage before you need to do that.

Best alternative for 'long term' storing model-view-controller data in Objective-C/ios

I am planning on creating my app in a 'Model-View-Controller'(MVC)-style, and in the end, for me at least, this means that all data is stored in the controller-class. Let's say I have a class Player, and the player has several objects of class Weapons or Equipment or whatever. The initialization of Controller* stores the player(s), so if I can store/save only the Controller-object over time, even if the app or the device restarts, that would be nice. I did this in Java one, I put in Serialization = 100L;(or something like it) in the top of the file of every object that would be included when saving the Controller-object, and it worked perfectly. Is this possible in ios/cocoa-touch/objective-c?
I have read and used Core Data (not very much), but that is just a database-table, sql?, which would have me extract every piece of information of every object?
For instance, if the object Player* has a member NSString *name;, I would have to save the actual string in Core Data, instead of saving the object of the player? Like, varchar.
If there is any way to store an entire custom object on the device for further use, I would very much like to know what it's called, and where I can read about it/tutorials.
Read up on the NSCoding protocol. You can make your object complient to it, then serialized it and save it to a file. Later you can restore it to the same state by using a decoder. For sure some other posts that cover this topic on SO.

Resources