I have a class (SpendrList) that has an NSMutableArray property that acts a list that the user saves multiple things to while using an app (it's a datasource for a UITableView). Using the NSCoding protocol, I encode/decode this class as needed.
This works fine, as the array property holds list items from a class I created, SpendrListItem, (also adhering to NSCopying protocol) and I encode it as the user makes any edit in the UITableView, like so:
NSURL *dataFile = [FileSystemHelper pathForDocumentsFile:kFilePathList];
NSString *filePath = [dataFile path];
[NSKeyedArchiver archiveRootObject:_list toFile:filePath];
Right now, I am just dealing with one list so this was relatively easy to set up after a few tutorials on NSCoding. Now, what I want to do is code in support for the user to have multiple lists in my app.
I have a collection view in another ViewController set up where I want to display all the lists created, and I am wondering if I can iterate through all objects I have encoded contained in the app sandbox.
Psuedocode:
for (encodedObject in App Sandbox){
if([encodedObject isTypeOfClass: SpendrList]){
//Add to iVar array to show in collection view
}
}
Multiple Lists [in isolation]?
Sounds like documents. Specifically, if you have multiple chunks of data that are held in relative isolation, even if identical in structure, you should likely manage and persist them separately. Potentially, you might have a master document that keeps track of the inventories of other documents.
That is, persist those lists to separate files.
Fortunately, iOS has an entire infrastructure for helping you with this; see UIDocument.
Related
I am working on an app that display photos of buildings along with some information about them. The photos are jpegs that all come in the app bundle, and the Building objects associated with them have a few properties such as name, address, and of course, a photos array. I'm loading the Building objects from a plist when the app launches. Everything's pretty straight forward, except that when the user scrolls through the images of buildings they have the option to add the ones they like to a favourite's list for viewing later.
Originally I was going to save each Building object they favorited using Core Data, but now that I think about it it seems a little redundant because all of the Building objects are already loaded from the plist. When the user is scrolling through the images of each building the photos have to display an indication of weather that building has been added to the favourite's list yet, which means I'll have to continuously compare an array of objects taken from CoreData with an array of objects loaded from a plist, which seems like it could be a memory wasting task.
I was wondering if, as an alternative to CoreData, I could add a unique ID property to the Building object and store it in the plist with them. Then when the user adds a Building to their favourites list I can just add it's UID to an array in NSUSerDefaults and make the comparison that way. Does that seem like a sensible way to tackle this?
Storing data like this in NSUserDefaults is a really bad habit to get into. Save user preferences in NSUserDefaults, not sets of data.
Much better idea is to save the user's favorites in a file in the Documents folder. If you've added an "ID property" to your Building object, you can maintain an array of selected IDs. That array can be saved to and read from disk with:
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [documentPaths objectAtIndex:0];
NSString *myFile = [documentsDirectory stringByAppendingPathComponent:#"myFavs"];
and then
// save the array contents to file named "myFavs"
[a writeToFile:myFile atomically:YES];
or
// read the saved array when you launch the app
NSArray *b = [NSArray arrayWithContentsOfFile:myFile];
Of course, you'll want to add error handling, but this is a much, much better idea than stuffing an array into NSUserDefaults.
Perhaps using CoreData for favourites is OK, although, consider using Sets which stores distinct values of the same type in a collection with no defined ordering
Assumptions:
- Your buildings data isn't coming from a dynamic/external source
- You don't have a massive amount of data.
why not add a property to the buildings to flag it as a favorite and store that local when the app goes to the background. (NSUserDefaults or document directory as plist)?. Then in future app loads use the local one that has the flags.
That way you don't need an additional data structure to store identifiers and a list of favorites.
CoreData seems like overkill for the problem you're describing.
I'm new to Parse and Swift.
I have an app where people play against each other.
I want to create an activity feed where game results, cheers, heckles (anything really) can show up in a list.
My thought is to create an Activity class that subclasses PFObject and I'd like to have an enum ActivityType to determine what kind of Activity is being created.
Can I set up the Activity object in Parse and the PFObject in Swift so that each Activity is set up with the correct ActivityType?
My thinking is that I need a "Type" column in Parse that's just a number and an init method that reads that number and sets the correct type.
Does that sound about right?
Thanks
You could use a type column in parse, though I'd expect each of your different kinds of feed item to be different classes in parse as they all have different data and relationships, so you could use the class type (name).
In either case this is just a way to identify the type coming from the server. Once you have that you want an organised and common approach to displaying the feed items. To do that you should have a protocol which defines what a feed item needs to provide in order to be displayed on the feed. Then you have a set of classes, each conforming to that protocol, and each dealing with one of the different types of feed item to 'mutate' them into the common format for display.
Using an enum in your app would work, but it could lead you to have one big switch statement dealing with everything. So long as you just use the enum and switch to deal with deciding which class to create to handle the feed item then your code should be well structured.
I've solved this problem. It was 'free' functionality from Parse. I think first of all, you should consider subclassing PFObject (for many reasons). Once you do this, all you have to do is add the enum as a property to your subclass. It's taken care of automatically by Parse.
Parse knows how to convert to NSNumber and vice versa for an enum, no need to worry about that.
Note, in your .m file:
#implementation MyParseObjectSubclass
#dynamic aPropertyIWantPersisted; // declare your properties as dynamic to be managed by Parse
#synthesize aLocalTransientProperty; // if you have transient properties that you don't want persisted to the server.
+ (void)load
{
[self registerSubclass];
}
+ (NSString*)parseClassName
{
return "MySubclass";
}
#end
I am very confused on how to use NSMutableArrays to store multiple dimensions of data.
I'm trying to store a car's make, model, year, and a description of a car (which is an NSString) in an array. The app plays a sound, loads the description, and shows a picture of the car. Originally, I was using a PLIST file to store this information. This was then loaded into a dictionary as the view loaded. However, I am no longer implementing the PLIST file, since it would be much easier for the program to automatically generate this data at runtime, based on the folders in the project.
The folders are broken down as follows:
(Main Resources Folder) -> Makes -> Models -> Years -> Folder containing picture, .mp3, and file with description
For example:
Cars -> Ford -> Mustang -> 1965 -> Pic, .mp3, description in file
//Go to folder
NSString * resourcePath = [[NSBundle mainBundle] resourcePath];
NSString * documentsPath = [resourcePath stringByAppendingPathComponent:#"Cars"];
NSError *error;
//Load names of all makes in folder into memory
NSArray * listOfMakes = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:documentsPath error:&error];
I then use multiple 'for' loops to iterate through this and all other folders, and to load everything else in to NSArrays.
However, I need one single array that is accessible program-wide. So, I am doing this within a singleton. I would like to add these arrays to an NSMutableArray, so that I can access it anywhere. I have viewed many other questions, like these:
objective-c accessing arrays, Adding objects in Multidimensional NSMutableArray, How to add string objects to NSMutableArray, But am still very confused since these only cover up to three dimensions. Having multiple arrays of arrays is very confusing...
Any help would be very much appreciated.
when it comes to multi-dimension...
I suggest you to use NSMutableDictionary or NSDictionary.
But the worst thing I see in your implementation is you are trying to load/hold the whole app data at once, Which will remain in memory till the app is not quit, since its store in singleton.
It will be better if you use CoreData and load only content which is required.
I don't know how you are building your app's logic, but if I were you, I would do something like this:
1. Make navigation based app.
2. In the first view show the list of models (load the list of models from Makes folder).
3. When particular model is selected, in the next view show the list of years.
etc.
And, no need to read all the contents of all folders in to the array. Good Luck!
If you want a 2 dimensional array, than just one car array to it for each car:
[2dimArray addObject:#[make, model, year, description]];
I am making an iPad app where the user can create graphic content with images and text. I am storing this in memory in an array of custom UIView subclasses. Each of these view subclasses can have any number of subviews containing images or text.
Now I need to save these in the device. As I explore, there seem to be many ways to do this and would like to know what would be the best for this case.
It looks like you are asking for the architectural design of what will end up being a Drawing app. This means that best it's really dependent on you specific use-cases, and cannot be answered completely unless you provide a quite detailed list of requirement.
But in general, I could try to give you some general tips that will have anyway to be integrated with you own specific nitty-gritty implementation.
This description will make some assumptions regarding the basic use cases that an app like this may need:
The user can create an image using multiple tools to achieve the result. These can be anything, from a brush to a textfield and so on
The information regarding which tools have been used to create the picture and how this tools have influenced the picture current look, can be saved in order to allow the user to later on edit the picture
Said this, the main problem is: how to store your drawing state in order to recover it later?
There are indeed many ways to achieve it, but I believe 2 of them are what would be considered "clean and famous enough".
NSKeyedArchiver
This wouldn't be my favourite (difficult to maintain), but if you have to deal with UIView, it's probably gonna be the quickest.
The NSKeyedArchiver is
.. a concrete subclass of NSCoder, provides a way to encode objects
(and scalar values) into an architecture-independent format that can
be stored in a file.
It implements the Memento design pattern and It's the same pattern described in Pro Objective-C Design Patterns, that, incidentally, presents a case study that has many of the most important use-cases matching yours:
A drawing pad allows scribbling with the user’s finger.
[...]
It allows the user to save a scribble.
It allows the user to open a saved scribble.
[...]
It's an app for having a drawing pad, where you can draw lines with your finger.
Yours looks like a simplified version of this, with images and texts instead of the scribble.
So, what's the pro, in this specific case, of using the NSKeyedArchiver? The UIView already implements the NSCoding protocol, the one needed to archive the object. So, for most of the information you need to store (coordinates, frame size, background color ...), you don't have to do anything but... archiving the object.
For any additional attribute on top of the UIView (for instance: the local path of your image, because archiving an UIImageView is really expensive), you can take a look at this article that explains with proper detail what you have to do in order to take advantage of the NSKeyedArchiver to store your object states.
This all boils down to:
implement the NSCoding protocol for each of the tools your drawing app is gonna provide
keep track of the subviews that the user create (images, text...)
when the user hit "save", loop through them, create an archive, and store them to a sensful path. The first component of the path could be the name of the Drawing, the second one the name of the tool and the third an id for each time the tool has been used. Like
// A mountain image
/<path to you Document dir>/Mountains/Image/1
// A sun
/<path to you Document dir>/Mountains/Image/2
// The text "Mountain is awesome"
/<path to you Document dir>/Mountains/Text/1
Then of course you will have to save the list of Drawing names somewhere, either in a plist file or in a NSUserDefault, so to be able to show them to the user in case they want to restore them for editing.
Core data
This is probably the cleanest and more maintainable way to store you object states, but is gonna be a bit tough and cumbersome, in particular if it's the first time you use core data. I'm not gonna dig into Core Data, but I can give you some guidelines of the whole procedure. Basically:
You create a db schema that represents each of the tools your are gonna let the user use. Like: a table for Image, a table for Text and so on
On each table you put the attributes you need to remember (location, text color for "Text", image URL for "Image" and so on)
You create a table for the Drawing that the user create, with a 1-many relationship to the tool tables. This relations represents the object shown in the drawing.
Initialize you drawing canvas and each component according to what's stored in the db
Every time the user hit "save", create or update the proper db tables in order to reflect the current drawing configuration in the storage.
One of the advantages of this approach is that, if one day you want to change a tool component property or add new ones, you can take advantage of schema migrations in order to deliver backward compatibilities with new updates. So the users will still be able to use their old drawings.
And so on and so forth...
These are two of the zilions of possibilities. You could use also use:
NSUSerDefault to store states, that I suggest to avoid. It's gonna be really hard to maintain
Mix of the two aforementioned techniques
If you plan to deliver >= iOS6 only support, you can check this
etc
The two I described are just what I feel are the usual and most discussed way of doing this. You find them in books, tutorials and they let you quite a lot of flexibility for anything you have to do.
If you need more explanatory links, let me know.
As I mentioned in a comment, you might want to look into iOS's state preservation API's. However, if you want to build your own system to do this it'd be pretty simple using some clever categories and dictionaries. Then you can serialize/deserialize your dictionaries using NSKeyedArchiver and NSKeyedUnarchiver.
eg:
#interface UIButton (MyAppCategory)
- (NSDictionary *)viewProperties;
- (void)configureFromProperties: (NSDictionary *) properties;
#end
#implementation UIButton (MyAppCategory)
- (NSDictionary *)viewProperties {
return #{ #"class" : NSStringFromClass([self class]),
#"frame" : [NSValue valueWithRect:self.frame],
#"titleLabelText" : self.titleLabel.text,
// etc...
};
}
- (void)configureFromProperties: (NSDictionary *) properties {
NSValue * value = properties[#"frame"];
if ([value isKindOfClass:[NSValue class]]) {
self.frame = value.rectValue;
}
NSSString * titleLabelText = properties[#"titleLabelText"];
if ([titleLabelText isKindOfClass:[NSString class]]) {
self.titleLabel.text = titleLabelText;
}
}
#end
// replicate the above pattern for other view objects you need to support
#implementation MyViewFactory
- (UIView)recreateViewFromProperties: (NSDictionary *) properties {
NSString * className = properties[#"class"];
if ([className isKindOfClass:[NSString class]]) {
Class viewClass = NSClassFromString(className);
id viewObject = [[viewClass alloc] init];
if ([viewObject respondsToSelector:#selector(configureFromProperties:)]]) {
[viewObject performSelector:#selector(configureFromProperties:) withObject:properties];
return viewObject;
}
}
return nil;
}
// exercise for the reader: iterate your views and use the viewProperties: method to collect your views' configuration info...
#end
If you want to allow for future session editing and loading etc. I would suggest designing a data structure and create a core data model out of it.
Some structure holding the session metadata e.g. sessionID, creationDate, dictionary of key:imageName value:imageFrame (CGRect wrapped in NSValue, use setObjectForKey).
Loading images for the session would work by calling the keys into an array using e.g.[sessionImageDictionary allKeys], iterating through the keys and asynchronously (NSOperationQueue with maxConcurrentOperationCount) loading the image at some Macro path to e.g. the library directory, and appending the key, which is the imageName.
In the same iteration you can set its frame by calling [sessionImageDictionary valueForKey:[arrayOfKeys objectAtIndex:currentIteration]; Converting the previously stored NSValue back to CGRect.
The datastructure all depends on the amount of features you want, but the good thing is it allows for expansion and with core data as the backing store, you could do things like sync between devices, enable multiple sessions for loading and saving like a "My projects" feature. It will help if lets say the user builds up a library of images (all stored in your apps library directory) and then the user uses the same image in the same session or in multiple sessions, only one copy of the image needs to exist, with zero duplicate write outs to disk and the core data object will have the filename stored in the session.
The most important part would be building a correct Core-Data model and writing an extractor that can accept these custom subclasses, strip out the data to create, populate and save an NSManagedObject to the persistent store.
Your best option is to use UIDocument with NSFileWrapper folder. Then you can store all your files in one folder which is saved automatically when the contents change.
Refer to:http://developer.apple.com/library/ios/#documentation/DataManagement/Conceptual/DocumentBasedAppPGiOS/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011149-CH1-SW1
I hope this question isn't too general/ambiguous...
I'm writing an iphone quiz game app and am having trouble figuring out the best way to handle data. Currently I am thinking of having a single Model class that holds an array of "User" classes which each have an array of user-specific "Question" classes. I'd like to be able to access the overarching Model from any of my view controllers, but that means I'll probably have to pass the model object to any new view controller, use a singleton, or do something else. What is the best way to access my Model object from other classes? Another factor I'm not sure about is being able to save the data - would I have to use Core Data/SQLite to save my single Model object, or is there a simpler way?
I'd start by designing a schema using CoreData. IMO, its best to start out using CoreData because then you'll never have to convert your data layer to CoreData, in the event that your app scales beyond a simple object or two.
The other route would be to create a web service that returns your data... so you just call the service and it returns a collection of user objects. You can either send down the entire object graph with the questions, or create another service to return a collection of questions for a specific user. If you have a web server handy, this method scales the best because you don't have to rely on app updates to get new questions into your system. I would still use CoreData to cache the results... so that way you're not downloading the same information all the time.
So when it comes to accessing CoreData objects, I use a repository class that's a singleton. This makes it easy for any view controller to grab an instance of the repository and get some data. Here's what something like that might look like;
[[Repository defaultRepository] findFirst:[User class]
where:#"name == 'John'"]
There's a lot of redundant code to fetch data so wrapping that up in an object will help get all that nasty code, like predicates and sorting, out of your view controllers. You can see where I leverage a va_list in the where clause so I can inject that string right into my predicate. Here are some other methods you could implement:
- (NSArray *) findAll:(Class)entity
sortByKey:(NSString *)key
ascending:(BOOL)ascending;
- (NSArray *) findAll:(Class)entity
sortByKey:(NSString *)key
ascending:(BOOL)ascending
where:(NSString *)format, ...;
- (id) findFirst:(Class)entity
where:(NSString *)format, ...;
I'm not sure if this is the preferred way, but I've had a lot of success with this method. Hope this helps!
Check this link, this will help you a lot
Link: http://mobile.tutsplus.com/tutorials/iphone/iphone-sdk_store-data/
This cover 4 major ways to store data in iPhone with sample code.
1) NSUserDeafult
2) Property Lists
3) SQLLite
4) Core Data