How to manage data in a iOS app - objective-c

I'm developing an app where I have some data. It would be some strings I want to use.
The issue is I would like to initialize with the data of a file. something like:
#property (strong, nonatomic) Cow *cow;
_cow = [[Cow alloc] init (NSString *) #"Some string"];
I have the proper constructor for cow that accepts an string .
What I want is the way I can initialize severals cows with different strings and repeat some values if I want.
I was thinking in a text file where I have all the strings, and just pick those I want.
Another possibility is a db like SQlite.

Reading your comments to other answers, it seems that you are interested in an object graph persistence mechanism for your application. The answer given by #Greg - object graph serialization - is excellent. In the default case, you'd be reading your entire object graph into memory, which may or may not be what you want. (There are ways of dealing with that too.) You asked about how to choose data you want. In the case where your Cow's are in memory, you can find your Cow of choice in a lot of ways. For example:
#import <Foundation/Foundation.h>
#interface Cow : NSObject
#property (nonatomic,copy) NSString *color;
#end
#implementation Cow
#end
int main(int argc, char *argv[]) {
#autoreleasepool {
Cow *brownCow = [Cow new];
brownCow.color = #"brown";
Cow *whiteCow = [Cow new];
whiteCow.color = #"white";
NSArray *cows = #[brownCow,whiteCow];
NSUInteger cowIndex = [cows indexOfObjectPassingTest:^BOOL(Cow *obj, NSUInteger idx, BOOL *stop) {
return [[obj color] isEqualToString:#"brown"];
}];
if( cowIndex != NSNotFound )
NSLog(#"Your brown cow is: %#",cows[cowIndex]);
else
NSLog(#"No brown cow");
}
}
But there are other object persistence frameworks that you may consider. For example, Core Data is commonly used. sqlite3 is another possibility, which when used with an object-oriented abstraction layer like FMDB is another good choice.
The answer boils down to your requirements. How many Cow objects might there be? What are the performance requirements? And so on.

You can make your object Cow conform to a NSCoding protocol:
//Cow.h file
#interface Cow : NSObject <NSCoding>
#property(nonatomic, copy) NSString *someString;
#end
//Cow.m
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super init];
if (self) {
_someString = [aDecoder decodeObjectForKey:#"someString"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.someString forKey:#"someString"];
}
And after that you can save it to a file (you can create array of Cow objects and save it to a file):
NSrray *data = #[cow1, cow2, cow3];
[NSKeyedArchiver archiveRootObject:data toFile:[self pathToFile]];
And you can read it like that:
NSFileManager* fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:[self pathToData]])
{
NSData *data = [NSData dataWithContentsOfFile:[self pathToData]];
NSMutableArray *dataArray = [NSKeyedUnarchiver unarchiveObjectWithData:data];
// dataArray -> array with your data
}
Hopefully I understood your question.
//EDITED
Get file to path:
-(NSString*)pathToData
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"MyData"];
NSFileManager* fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:filePath])
{
[fileManager createDirectoryAtPath:filePath withIntermediateDirectories:NO attributes:nil error:nil];
}
return [filePath stringByAppendingPathComponent:#"mydata.plist"];
}
Save and remove data:
-(BOOL)save
{
NSMutableArray *data = [self cachedDataFromFile];
if (!data)
{
NSLog(#"data = nil");
data = [NSMutableArray arrayWithArray:#[self]];
}
else
[data addObject:self];
return [NSKeyedArchiver archiveRootObject:data toFile:[self pathToData]];
}
-(void)removeFromFile
{
NSMutableArray *data = [self cachedDataFromFile];
if (!data)
return;
else
{
[data removeObject:self];
[NSKeyedArchiver archiveRootObject:data toFile:[self pathToData]];
}
}
-(NSMutableArray*)cachedDataFromFile
{
NSFileManager* fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:[self pathToData]])
{
NSData *data = [NSData dataWithContentsOfFile:[self pathToData]];
NSMutableArray *dataArray = [NSKeyedUnarchiver unarchiveObjectWithData:data];
return dataArray;
}
return nil;
}
Let me know is it work for you.

Related

"The data couldn’t be written because it isn’t in the correct format" occurs while archiving a custom class with NSKeyedArchiver

I was trying a new API in iOS12:
[NSKeyedArchiver archivedDataWithRootObject:<#(nonnull id)#> requiringSecureCoding:<#(BOOL)#> error:<#(NSError * _Nullable __autoreleasing * _Nullable)#>]
What I was trying to do is very simple, archive a custom class, here is the code:
A class named Cat:
#interface Cat : NSObject <NSCoding>
#property (nonatomic, copy) NSString *name;
#property (nonatomic, assign) NSInteger age;
+ (void)saveThisCat:(Cat *)cat;
+ (Cat *)getThisCat;
#end
#implementation Cat
- (void)encodeWithCoder:(nonnull NSCoder *)aCoder {
[aCoder encodeObject:self.name forKey:#"name"];
[aCoder encodeInteger:self.age forKey:#"age"];
}
- (nullable instancetype)initWithCoder:(nonnull NSCoder *)aDecoder {
if (self = [super init]) {
self.name = [aDecoder decodeObjectForKey:#"name"];
self.age = [aDecoder decodeIntegerForKey:#"age"];
}
return self;
}
+ (void)saveThisCat:(Cat *)cat {
NSError *error = nil;
NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *filePath = [docPath stringByAppendingPathComponent:#"cat.plist"];
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:cat requiringSecureCoding:YES error:&error];
// ***Error occurs here!!!***
NSLog(#"=== Error Info: %# ===", [error localizedDescription]);
[data writeToFile:filePath atomically:YES];
}
+ (Cat *)getThisCat {
NSString *docPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *filePath = [docPath stringByAppendingPathComponent:#"cat.plist"];
Cat *retVal = [NSKeyedUnarchiver unarchivedObjectOfClass:[Cat class] fromData:[NSData dataWithContentsOfFile:filePath] error:nil];
return retVal;
}
#end
Usage:
Cat *totoro = [Cat new];
totoro.name = #"totoro";
totoro.age = 1;
NSLog(#"=== The cat's name is %#", totoro.name);
NSLog(#"=== The cat's age is %d", totoro.age);
[Cat saveThisCat:totoro];
Cat *resultCat = [Cat getThisCat];
NSLog(#"=== The cat's name is %#", resultCat.name);
NSLog(#"=== The cat's age is %d", resultCat.age);
And the error info (Generated by using archivedDataWithRootObject while executing saveThisCat method)
=== Error Info: The data couldn’t be written because it isn’t in the correct format. ===
Is there anything wrong? Please point it out, thanks a lot!
You have to adopt NSSecureCoding
#interface Cat : NSObject <NSSecureCoding>
and in the implementation add the required class property
+ (BOOL)supportsSecureCoding {
return YES;
}
When you call unarchivedObjectOfClass, use the error parameter and it will supply an NSError:
Error Domain=NSCocoaErrorDomain Code=4864 "This decoder will only decode classes that adopt NSSecureCoding. Class 'Cat' does not adopt it." UserInfo={NSDebugDescription=This decoder will only decode classes that adopt NSSecureCoding. Class 'Cat' does not adopt it.}
As vadian said, adopt NSSecureCoding.
Whenever you have a failure, always avail yourself of available NSError parameters.

What is the best practices for sharing methods across classes in an app?

I'm still trying to wrap my head around how things should be done in the object-oriented world and I think my problem is that I don't understand how to best utilize encapsulation. Specifically, I have lots of small bits of code that I use in several classes in my project. For example:
+ (NSString *)getFormattedDate;
+ (NSString *)getResultsFilePath;
+ (NSError *)removeFileFromCache:(NSString *)fileName;
These are all 3-5 line methods that I use in more than one class. My standard practice has been to put these snippets into a Utility.inc file and call them when I need them. Is that appropriate in the object-oriented world or should each class be self-contained? And if it's appropriate, would you put the code into a singleton or just a regular class file and [[Utilities alloc] init] in each class where you want to use the methods?
Look into using Categories. For the examples you gave, these are methods related to objects of a particular class that happen to be used in several of your own classes. Categories will allow you to park these often used methods where they can be associated with the common factors.
Create a utitity singelton which will be created only ones and then used by the other classes.
Thanks for the answers. I'm not sure that this the right way to do things, but this is what I've done on the projects I just submitted.
I made two classes, one for Utility methods and one for globals. The methods in the Utilities class are all class methods since they operate on files and constants or globals. Then I made a singleton for global variables. I have all of my global constants in the .pch file. Also in the .pch file I put the following two lines of code so that the utilities and globals are available everywhere.
// File for utilities like file delete, find Documents file
#import "Utilities.h"
#import "Globals.h"
Accessing the methods is straightforward. Here's an example of a call to both methods to generate an HTML header for an email.
NSString *gameNameHeader = [NSString stringWithFormat:#"<p>&nbsp</p><h1>%# Results</h1><h2>%#%#</h2>",GAME_NAME_TITLE,[Utilities formattedClientName], [Utilities formattedDate]];
In case anyone can use it, here is my current version of the code. (Sorry for the formatting-I can't seem to get the wiki to cooperate.)
#interface Utilities : NSObject {
}
+ (NSString *)formattedDate;
+ (NSString *)formattedClientName;
+ (NSString *)cachedResultsFilePath;
+ (NSString *)cachedResultsFileContents;
+ (NSString *)resultsFileName;
+ (NSError *)removeFileFromCache:(NSString *)fileName;
+ (NSString *)applicationCachesDirectory;
+ (NSString *)applicationDocumentsDirectory;
+ (NSString *)applicationLibraryDirectory;
+ (NSError *)copyCachedResultsToFile;
#end
#import "Utilities.h"
#implementation Utilities {
}
+ (NSString *)formattedDate {
NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd"];
NSString *todaysDate = [dateFormatter stringFromDate:[NSDate date]];
return todaysDate;
}
+ (NSString *)formattedClientName {
NSString *client = [NSString stringWithFormat:#" "];
if( [Globals sharedInstance].currentClient ) client = [NSString stringWithFormat:#" %# ",[Globals sharedInstance].currentClient];
return client;
}
+ (NSString *)cachedResultsFilePath {
NSString *resultsFilePath = [[self applicationCachesDirectory] stringByAppendingPathComponent:#"Results.txt"];
return resultsFilePath;
}
+ (NSString *)cachedResultsFileContents {
NSStringEncoding encoding; NSError* error = nil;
NSString *resultsText = [NSString stringWithContentsOfFile:[self cachedResultsFilePath] usedEncoding:&encoding error:&error];
return resultsText;
}
+ (NSString *)resultsFileName {
return [NSString stringWithFormat:#"%# Results%#%#.html",GAME_NAME_TITLE,[self formattedClientName],[self formattedDate] ];
}
+ (NSError *)removeFileFromCache:(NSString *)fileName {
NSError *error = nil;
NSFileManager *localFileManager=[[NSFileManager alloc] init];
NSString *fullPath = [NSString stringWithFormat:#"%#/%#", [self applicationCachesDirectory],fileName];
[localFileManager removeItemAtPath: fullPath error:&error ];
return error;
}
+ (NSString *)applicationCachesDirectory {
return [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) lastObject];
}
+ (NSString *)applicationDocumentsDirectory {
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
}
+ (NSString *)applicationLibraryDirectory {
return [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
}
+ (NSError *)copyCachedResultsToFile {
// Grab the header and footer and put it around the cached data
NSStringEncoding encoding; NSError *error = nil;
NSString *htmlHeaderTextPath = [[NSBundle mainBundle] pathForResource:#"HTML_header" ofType:#"html" ];
NSString *htmlHeaderText = [NSString stringWithContentsOfFile:htmlHeaderTextPath usedEncoding:&encoding error:&error];
NSString *cachedResultsText = [NSString stringWithContentsOfFile:[self cachedResultsFilePath] usedEncoding:&encoding error:&error];
// Write the results to a file if there are any
if (cachedResultsText) {
NSString *htmlFooterTextPath = [[NSBundle mainBundle] pathForResource:#"HTML_footer" ofType:#"html" ];
NSString *htmlFooterText = [NSString stringWithContentsOfFile:htmlFooterTextPath usedEncoding:&encoding error:&error];
NSString *gameNameHeader = [NSString stringWithFormat:#"<h1>%# Results for%#%#</h1>",GAME_NAME_TITLE,[self formattedClientName],[self formattedDate] ];
NSString *tempStringP1 = [htmlHeaderText stringByAppendingString:gameNameHeader];
NSString *tempStringP2 = [tempStringP1 stringByAppendingString:cachedResultsText];
NSString *formattedTextForPrinting = [tempStringP2 stringByAppendingString:htmlFooterText];
NSString *resultsFilePath = [ [Utilities applicationDocumentsDirectory] stringByAppendingPathComponent:[self resultsFileName] ];
if ( !([[NSFileManager defaultManager] fileExistsAtPath:resultsFilePath]) ) {
if (! ([[NSFileManager defaultManager] createFileAtPath:resultsFilePath contents:nil attributes:nil]) ) {
NSLog(#"Error was code: %d - message: %s", errno, strerror(errno));
}
}
NSFileHandle *fileHandler = [NSFileHandle fileHandleForUpdatingAtPath:resultsFilePath];
[fileHandler writeData:[formattedTextForPrinting dataUsingEncoding:NSUTF8StringEncoding]];
[fileHandler closeFile];
}
return error;
}
#end
Globals in a singleton. Probably not thread-safe, but I don't care right now.
#interface Globals : NSObject {
}
#property (nonatomic, strong) NSString *currentClient;
#property (nonatomic, strong) NSString *showmePict;
#property BOOL checkBoxes;
+ (Globals *)sharedInstance;
- (void)resetClient;
#end
#implementation Globals {
}
static Globals *singleton = nil;
#synthesize currentClient = _currentClient;
#synthesize showmePict = _showmePict;
#synthesize checkBoxes = _checkBoxes;
+(Globals *) sharedInstance {
NSLog (#"sharedInstance of Globals called.");
if (nil != singleton) return singleton;
static dispatch_once_t pred; // lock
dispatch_once(&pred, ^{ // this code is at most once
singleton = [[Globals alloc] init];
});
return singleton;
}
- (void)resetClient {
self.currentClient = nil;
}
#end

Where can I find a generic game state singleton for Objective C?

I'm wanting to use, and store a game state singleton inside NSCoder, but I am finding it quite difficult to find a generic state management that saves and loads its data using the NSKeyedArchiver/NSCoder routines.
I'm wondering if someone can direct me to a good tutorial, or generic code for use as a game state singleton, with it saving/loading in NSCoder?
Thanks
I've been able to get a basic game state manager working from watching tutorials from 71squared.com
I use the SynthesizeSingleton.h from CocoaWithLove and am able to save and load states using NSCoder / archiving.
As it uses the singelton, I can reference it by writing:
GameStateManager *gsm = [GameStateManager sharedGameStateManager];
As below:
// GameStateManager.h file
#interface GameStateManager : NSObject
{
NSMutableArray *listOfPlayers;
}
#property (nonatomic, retain) NSMutableArray *listOfPlayers;
+ (GameStateManager *)sharedGameStateManager;
-(void)loadGameState;
-(void)saveGameState;
// GameStateManager.m
#import "GameStateManager.h"
#import "SynthesizeSingleton.h"
#import "Player.h"
#implementation GameStateManager
SYNTHESIZE_SINGLETON_FOR_CLASS(GameStateManager)
#synthesize listOfPlayers;
#pragma mark - Init
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
self.listOfPlayers = [NSMutableArray array];
}
return self;
}
#pragma mark - Memory management
-(void) dealloc
{
[super dealloc];
}
#pragma mark - Save/Load
-(void)saveGameState
{
NSLog(#"saveGameState");
// Set up the game state path to the data file that the game state will be save to
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *gameStatePath = [documentsDirectory stringByAppendingPathComponent:#"gameState.dat"];
// Set up the encoder and storage for the game state data
NSMutableData *gameData;
NSKeyedArchiver *encoder;
gameData = [NSMutableData data];
encoder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:gameData];
// Archive the object
[encoder encodeObject:self.listOfPlayers forKey:#"playerObjects"];
// Finish encoding and write to disk
[encoder finishEncoding];
[gameData writeToFile:gameStatePath atomically:YES];
[encoder release];
}
-(void)loadGameState
{
NSLog(#"loadGameState");
// Set up the game state path to the data file that the game state will be save to
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *gameStatePath = [documentsDirectory stringByAppendingPathComponent:#"gameState.dat"];
NSMutableData *gameData;
NSKeyedArchiver *decoder;
gameData = [NSData dataWithContentsOfFile:gameStatePath];
// Check to see if the .dat file exists, and load contents
if (gameData)
{
decoder = [[[NSKeyedUnarchiver alloc] initForReadingWithData:gameData] retain];
self.listOfPlayers = [[[decoder decodeObjectForKey:#"playerObjects"] retain] autorelease];
NSLog(#"Returned %d players", [self.listOfPlayers count]);
for (Player *p in self.listOfPlayers)
{
NSLog(#"p.name = %#", p.fname);
}
// Finished decoding, release
[decoder release];
[decoder autorelease];
} else {
}
}
In my Player.h/.m file I do this:
#interface Player : NSObject
<NSCoding>
{
NSString *fname;
}
#property(nonatomic,retain) NSString *fname;
// Player.m file
#pragma mark - Encoding
-(id)initWithCoder:(NSCoder *)aDecoder
{
//[self initWithObject:[aDecoder decodeObject];
self = [super init];
if (self) {
// Initialization code here.
self.fname = [aDecoder decodeObjectForKey:#"fname"];
}
return self;
}
-(void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.fname forKey:#"fname"];
}
// ---
My follow-up questions are:
1) Am I meant to put a listOfPlayers in the GameStateManager?
--> Moved to a new question
2) In my encoder, I have to store each field individually, is it not possible to just store the actual object itself?
3) Does encoding of an object also encode all its children?
Lets say I have a Parent class (Player) and a Child class (Weapons). If I encode the Player object only, will it encode all the Weapons too automatically because they are linked?
Thanks.

Saving data with NSMutableDictionary

I had a method to save a dic to the disk:
+(BOOL) writeApplicationData:(NSDictionary *)data
bwriteFileName:(NSString *)fileName
{
NSLog(#"writeApplicationData");
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
if (!documentsDirectory) {
NSLog(#"Documents directory not found!");
return NO;
}
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:fileName];
return ([data writeToFile:appFile atomically:YES]);
}
And I tested it with:
NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
NSMutableDictionary *d1 = [[NSMutableDictionary alloc] init];
NSMutableDictionary *d2 = [[NSMutableDictionary alloc] init];
[d1 setObject:#"d11"
forKey:#"d11"];
[d1 setObject:#"d12"
forKey:#"d12"];
[d1 setObject:#"d13"
forKey:#"d13"];
[d2 setObject:#"d21"
forKey:#"d21"];
[d2 setObject:#"d22"
forKey:#"d22"];
[d2 setObject:#"d23"
forKey:#"d23"];
[dic setObject:d1
forKey:#"d1"];
[dic setObject:d2
forKey:#"d2"];
[self writeApplicationData:dic
bwriteFileName:#"testSave"];
And the data is saved correctly.
Then I tried to save d1 with class obj in it:
LevelInfoData *levelInfoData = [[LevelInfoData alloc] init];
[levelInfoDictionary setObject:levelInfoData
forKey:#"test"];
[dic setObject:levelInfoDictionary
forKey:#"LevelInfoDictionary"];
But this time, even no plist file was generated in the disk.
Here is the LevelInfoData class:
#interface LevelInfoData : NSObject {
int levelNum;
}
#property (nonatomic) int levelNum;
#end
#implementation LevelInfoData
#synthesize levelNum;
#synthesize isLevelLocked;
#synthesize isLevelCleared;
#synthesize levelHighScore;
-(id)init
{
if( (self = [super init]) ) {
levelNum = 0;
}
return self;
}
#end
I'm really confused, hope somebody could help me out, thanks.
The contents of the dictionary need to be property list type objects.
From the NSDictionary Class Reference:
This method recursively validates that all the contained objects are property list objects (instances of NSData, NSDate, NSNumber, NSString, NSArray, or NSDictionary) before writing out the file, and returns NO if all the objects are not property list objects, since the resultant file would not be a valid property list.
https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/Reference/Reference.html
You may want to try making your custom class a subclass of NSData rather than NSObject.
I'm not sure how attached you are to NSDictionary, but this may be a situation where NSCoder will better serve you.
See nscoder vs nsdictionary when do you use what
More details here:
NSCoder Class Reference
Some code snippets
A tutorial

How do you save data with NSKeyedArchiver?

Hi, I am trying to save an object from a class I have created. It is called shot and Contains 5 variables I wish to save. Here is the .h file--- It cut off NSCoding and NSMutableCopying Protocals and my imports, but they are there.
#import <Foundation/Foundation.h>
#interface Shot : UIButton <NSCoding, NSMutableCopying> {
int x;
int y;
int location;
int quarter;
bool made;
int miss;
int make;
}
#property()int x;
#property()int y;
#property()int location;
#property()int quarter;
#property()bool made;
-(void)set_x:(int)my_x set_y:(int)my_y set_quarter:(int)my_quarter set_made:(bool)my_made set_location:(int)my_location;
-(void)set_miss_AND_set_make;
#end
**Here are the methods I made to save the data in my .m file---**
-(void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeInt:x forKey:#"x"];
[aCoder encodeInt:y forKey:#"y"];
[aCoder encodeInt:location forKey:#"location"];
[aCoder encodeInt:quarter forKey:#"quarter"];
[aCoder encodeBool:made forKey:#"made"];
}
-(id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
x = [aDecoder decodeIntForKey:#"x"];
y = [aDecoder decodeIntForKey:#"y"];
location = [aDecoder decodeIntForKey:#"location"];
quarter = [aDecoder decodeIntForKey:#"quarter"];
made = [aDecoder decodeBoolForKey:#"made"];
}
return self;
}
-(id)mutableCopyWithZone:(NSZone *)zone {
Shot *newShot = [[Shot allocWithZone:zone]init];
[newShot set_x:x set_y:y set_quarter:quarter set_made:made set_location:location];
return newShot;
}
I can't seem to get my data to saved when I use these methods
-(NSString *)getPath {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentFolder = [paths objectAtIndex:0];
NSFileManager *fm = [[NSFileManager alloc]init];
if ([fm fileExistsAtPath:documentFolder] == NO) {
[fm createDirectoryAtPath:documentFolder withIntermediateDirectories:YES attributes:nil error:nil];
}
return [documentFolder stringByAppendingFormat:#"iStatTrackInfo.archive"];
}
-(void)saveData {
NSString *path = [self getPath];
[NSKeyedArchiver archiveRootObject:shotArray toFile:path];
}
-(void)loadData {
NSString *path = [self getPath];
NSFileManager *fm = [[NSFileManager alloc]init];
if ([fm fileExistsAtPath:path] == YES) {
shotArray = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
return;
}
NSEnumerator *enumOne = [shotArray objectEnumerator];
Shot *shotObject = [[Shot alloc]init];
while (shotObject = [enumOne nextObject]) {
Shot *shotShot = [[Shot alloc]init];
shotShot = [shotObject mutableCopy];
shotShot.frame = CGRectMake(0, 0, 50, 50);
shotShot.backgroundColor = [UIColor whiteColor];
[self addSubview:shotShot];
}
}
I have set the save and load methods up to buttons, but my data still won't save. Please help!!!
I have finally solved the problem. Other than the above help, .archive isn't a file type. You can't save a .archive, you can only save a .arch or a .plist That was my main problem.
First, you are leaking memory like a sieve. Lots of alloc/init & mutableCopy, no releases. I'd suggest reading the memory management guide.
Next, your method names are not following the Cocoa conventions. These:
-(void)set_x:(int)my_x set_y:(int)my_y set_quarter:(int)my_quarter set_made:(bool)my_made set_location:(int)my_location;
-(void)set_miss_AND_set_make;
Should be something like:
-(void) resetMissAndMake; // or just missAndMake or activateMissAndMake
-(void) setX:(NSInteger)xValue y:(NSInteger)yValue quarter:(NSInteger)quarterValue location:(NSInteger)aLocation;
Also, getPath should just be path. Methods prefixed with get are both very rare and have a very specific meaning.
This is also nonsense:
Shot *shotObject = [[Shot alloc]init];
while (shotObject = [enumOne nextObject]) {
Shot *shotShot = [[Shot alloc]init];
shotShot = [shotObject mutableCopy];
There is no need for either of the [[Shot alloc]init] calls (those are both leaks).
Finally, your encoding methods are implemented incorrectly. As the documentation states, you need to call super as appropriate.
Specifically, your encodeWithCoder: must invoke super's implementation of same and initWithCoder: should call super's initWithCoder:, not init.