I'll ask in the form of a hypothetical, which might make it easier for me to explain.
I have a class called Person, and in this has three fields:
NSString *name;
NSDate *dateOfBirth; and
NSMutableArray *friends.
An example object is this:
name = "John Smith"
dateOfBirth = 01/04/1985
friends = "Simon Scott"; "Jennifer Lane"; "Mary Firth"
Once the user has filled the NSMutableArray with the data they want, what would be the best way to save this data to the iPhone? I would anticipate that there could be up to 100 instances of the Person object, and all that will be required is the displaying of this data in a UITableView and giving the user the ability to add and remove entries at their will.
I have seen multiple suggestions on this site, which include NSDictionaries and using the writeToFile method, but before I research one of these, I was hoping someone could point me in the right direction? I would like to ensure that I'll be using the easiest and most appropriate method that's out there.
Many thanks.
Please take a look at the Property List Programming Guide. As long as you stick with a core set of object types for your data, you can write and read your data from a file or URL in one line, like this:
[people writeToURL:someURL atomically:NO];
The types you've mentioned in your question (strings, dates, arrays, dictionaries) can all be written to a property list.
#Achiral,
It really depends on what you want to do with the data and how concerned you are with the flexibility of your code.
However, my recommendation would be to use CoreData to make an SQLlite database and make a 'Person' entity with the properties that you list above. I don't know if you are familiar with CoreData, but it is highly optimized on iOS and is pretty easy to use, since it has a pretty simple 'fill in the blanks' style form for creating the data models. You should also note that CoreData is a well accepted and supported way to store data in an iOS and OS X app.
I hope this helps.
Related
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.
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'm about to work on a Objective-C game projects, and I'm curious to what's the options for getting constant data into the game. I've worked for many years as a Flash dev and in Flash I would just simply setup a class file and put in it objects like this...
static var arrowData:Object = {
elementType:"projectile",name:"arrow",sourceType:"library", imgSource:"arrow", imageSourceSub:"",cellsX:1, cellsY:1,
bounderyType:"none",bounderyLeft:0,bounderyRight:1200, bounderyTop:80, bounderyBottom:580,
position:{rotateToDir:false,positionType:"movieclip", randomX:false, randomY:false, endRandomX:600, endRandomY:400, startX:0, startY:0},
visual:{imgLabel1:"", imgNumFrames1:15,imgFramesLoop1:true,runOnFrame1:0,runOnFrameFN1:"",runOnLastFN1:""},
movement:{moveType:"arc", maxDistance:400, atTarget:"remove", target:"mouse", targetData1:"mouseX", targetData2:"none"},
physical:{physData:"spritesLayer"},
interactive:{interactionType:"rect", removeOnInteraction:true, interactionResult:"ingame", interactionData1:"badGuysArray", interactionData2:"projectileCollision"},
character:{damage:10}
};
I've Objects inside Objects, but there doesn't seem to be any clear way to do the same with Objective-C, so my questions are...
Is there any way to do the same as the above in Objective-C?
What are the other methods for getting data into Objective-C? XML?
How do iOS game devs usually store their constant game data?
I'm going to be using the parse hosting service with this project, are there
options for using that to store the data and loading it from there?
As the comment by #tc touched on, the common way to maintain groups of related info is in NSDictionaries (or their mutable cousins). Dictionaries can also nest. You have two ways to save these easily: as plists per comment or in the 'defaults' system. The latter is more restrictive on the types of data it stores. Apple also has a class that will convert between JSON and native objects. I have no experience with question 4 - hope others can answer that.
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
I'm new in iPhone development, can you advice me how to serialize AdressBook records?
I have read stackoverflow - How do I serialize a simple object in iPhone sdk, but I still have questions:
should I extend ABRecordRef? Is it possible? Or should I implement own class NSObject?
(similar question was on Mac forums and was left without answer)
Thank you!
ABRecord is an opaque C type. It is not an object in the sense of Objective-C. That means you can not extend it, you can not add a category on it, you can not message it. The only thing you can do is call functions described in ABRecord Reference with the ABRecord as a parameter.
You could do two things to be able to keep the information referenced by the ABRecord arround:
Get the ABRecords id by ABRecordGetRecordID(). The ABRecordID is defined as int32_t so you can cast it to an NSInteger and store it wherever you like. You can later get the record back from ABAddressBookGetPersonWithRecordID () or ABAddressBookGetGroupWithRecordID(). But aware, the record could be changed or even deleted by the user or another app meanwhile.
Copy all values inside the record to a standard NSObject subclass and use NSCoding or other techniques to store it. You will then of cause not benefit from changes or additions to the record the user could have made.
Of cause you can combine both approaches.