Multidimensional NSMutableArrays in Objective-C - ios

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]];

Related

iOS - Best way to save a list of user's favourite objects in memory

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.

List of images with their properties

My problem:
In my app there are a lot of images. I have a list of categories which user can choose for filtering all images by chosen category. User can't delete/edit these images, and can't add smth in it, so I guess I don't need the Core Data.
So my question is:
How and where in my project should I store and manage images' names with their properties, so I could use this list with files' names and their properties from any ViewController?
Finally, if you didn't get it: It should look like this:
1.Name: "imagename.jpg", Category: "Somecategory")
2.Name: "imagename2.jpg", Category: "Anothercategory")
Thank you.
If you want to store your images under NSDocuments with a susbfolder , I think you can do it without using database and Core Data .
Let's think a scenerio for you based on Model View Controller(MVC) structure:
You want to store images with an special properties like NSString *categories and NSString *filePath under NSFileManager with a subfolder . Encode your object and convert it to NSData. You need an Model class(it's subclass of NSObject)
that keeps your properties. You need to learn how to save a file to your Documents path. This blog is very good but it's Objective C. This is your Model and your image object.
Create a View that shows your images UICollectionView (just an example) and collect them with your image object. Modify it how you want!
Create your Controller class and do all actions here. Like saving image to path, getting image from path, all UIButton actions etc... Add your View to this class,decode your NSData to your image object show it.
This is the logic of an example, you can do it according to your necessities.
The way you should following is saving your image data to NSDocuments , search it. I advice you to learn MVC Stucture
Hope this give you an idea to start.

Saving NSArray data to disk

I have an NSMutableArray that contains NSMutableDictionary's. Each dictionary has an AVAsset, an NSURL, an NSString, and two UIImages. I want to save my array to disk so that each time I close and open my app, I can load the array and convert the URLs's to NSData objects in order to play audio and use the AVAssets for some other actions. I know I can save and load my array using initWithContentsOfFile and writeToFile:atomically and this answer is pretty informative: Saving a NSArray. However, that answer was from 2009. Is there a better way of saving and loading an array these days?.
As for the answer you linked, the answer is still valid. And according to it, you cannot store it the way it mentions. This is because array must be plist format compatible in order to be saved like that. When you parse your array down to lowest element hierarchy, you have UIImage which is just an object pointer and doesn't make sense.
One practical way would be store UIImages as separate files, and store their paths as part of your NSMutableDictionary objects. Same holds true for AVAssets. Off course you need to engineer the solution to fully accomplish this goal.
One more way to store non-plist compatible objects is to use archiving and unarchiving feature. Refer to the documentation. Here, make sure that each object in the tree follows protocol NSCoding (Probably, AVAsset in your question does not conform to it, so you need a way to work around it). For an example, see this answer and search the likes of it.

Iterate through all objects that have been encoded using NSCoding iOS

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.

Core Data and textfiles in iOS applications

I'm creating a simple iOS application consisting of a few UITableViewControllers. The information displayed in the view controllers will come from a text file (that I'll include in the project's Resources). The text file's contents will come from a spreadsheet.
Since this is my first time working with Core Data I have a few questions:
What format is most common for the text file? CSV, XML or something else?
What's the easiest way to import the data?
A few notes:
The data is static. Ideally the app will load the data into "Core Data" just once (1st time the app is run).
Each additional run of the app will just pull data from some Core Data source (that I'm not completely familiar w/ yet) instead of re-loading it from the textfile.
If the data is structured in a relational way then XML or JSON allows that structure to be easily preserved and then easily parsed and saved in your Core Data store. You'll need to use an XML or JSON parser, which will turn your data into an array of dictionaries (or multiple levels thereof if your data structure requires it). You'll simply iterate through the array and dig into the dictionaries (and sub-arrays and sub-dictionaries, if appropriate) and add objects to your store as you go.
If it's flat data, a simple single table that will become a single entity in Core Data, then tab-delimited or CSV text files are fine (and tab-delimited is even easier to parse if there wouldn't be any tabs within the data itself). You can then grab individual rows, break the rows down into an array of data bits (this is where tab delimiting makes is super-simple), create a new object for each row, set its properties to the array elements, and save the context.
The XML/JSON version is more complex than is worth writing out here -- search SO and you'll find lots of examples -- but here's the tab-delimited version (this assumes you don't have a gigantic ball of data that can't reasonably be held in memory):
// Standard Core Data setup here, grabbing the managedObjectContext,
// which is what I'll call it
// Then parse your text
NSString *path = [[NSBundle mainBundle] pathForResource:#"YourTextFileName" ofType:#"txt"];
NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:NULL];
NSArray *rows = [content componentsSeparatedByString:#"\n"];
// Now that we have rows we can start creating objects
YourManagedObject *yourManagedObject = nil;
for (NSString *row in rows) {
NSArray *elements = [row componentsSeparatedByString:#"\t"];
YourManagedObject *yourManagedObject = (YourManagedObject *)[NSEntityDescription insertNewObjectForEntityForName:#"YourManagedObject" inManagedObjectContext:managedObjectContext;
[YourManagedObject setName:[elements objectAtIndex:0]];
[YourManagedObject setCountry:[elements objectAtIndex:1]];
// Etc. You may need an NSNumberFormatter and/or an NSDateFormatter to turn
// your strings into dates and numbers, depending on your data types
[managedObjectContext save];
}
Poof, all done.
If the data doesn't change, why bother including the text file in the app? Instead, create Core Data file on your Mac and include that as a resource in the app. I presume it's a lot of data that'll take a while to parse, so there's no sense in making your users each wait for that to happen when you could do the parsing once and distribute the result.
To make that happen, take the data model and the parsing code from your app and use them to build a small command-line app that just reads the text file, writes the Core Data file, and exits.

Resources