Address book data persistence? - ios

(storyboard image)
http://i.stack.imgur.com/DUZ12.png
Have 3 text fields where user inputs data and saves it. Upon opening the application, if there is any save data, the previous input is displayed within the text fields. Problem is, there is only one set of data, while it needs to be an array with multiple people's information. I would like to instead create a navigation controller with cells with names and upon clicking on them it displays the correlating contact info.
viewcontroller.h
#interface ArchiveViewController : UIViewController
#property (strong, nonatomic) IBOutlet UITextField *name;
#property (strong, nonatomic) IBOutlet UITextField *address;
#property (strong, nonatomic) IBOutlet UITextField *phone;
#property (strong, nonatomic) NSString *dataFilePath;
- (IBAction)saveData:(id)sender;
#end
viewcontroller.m
#interface ArchiveViewController ()
#end
#implementation ArchiveViewController
- (void)viewDidLoad
{
[super viewDidLoad];
NSFileManager *filemgr;
NSString *docsDir;
NSArray *dirPaths;
filemgr = [NSFileManager defaultManager];
// Get the documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = dirPaths[0];
// Build the path to the data file
_dataFilePath = [[NSString alloc] initWithString: [docsDir
stringByAppendingPathComponent: #"data.archive"]];
// Check if the file already exists
if ([filemgr fileExistsAtPath: _dataFilePath])
{
NSMutableArray *dataArray;
dataArray = [NSKeyedUnarchiver
unarchiveObjectWithFile: _dataFilePath];
_name.text = dataArray[0];
_address.text = dataArray[1];
_phone.text = dataArray[2];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)saveData:(id)sender {
NSMutableArray *contactArray;
contactArray = [[NSMutableArray alloc] init];
[contactArray addObject:self.name.text];
[contactArray addObject:self.address.text];
[contactArray addObject:self.phone.text];
[NSKeyedArchiver archiveRootObject:
contactArray toFile:_dataFilePath];
}
#end
Thank you for your time.

Instead of having an array with 3 elements of text in it and using NSKeyedArchiver, have an array with dictionaries in it and save it with writeToFile:atomically:. This will use the array as a list of 'entries' rather than a list of fields and will save the data in a plist instead of a binary file.
Now, when you read in the array you can display a table view of the entries (just showing the name for example) and then when you show the archive view you would pass the controller the a appropriate dictionary.
For saving, it would be good to use delegation to pass the edit back to the master controller. But it could also be done directly (requires more knowledge in the detail controller) or by notification.

Related

Can't write NSDictionary parameters into custom Object

I was stuck on writing NSDictionary into Object process, I am sure that problem is simple as I imagine but would be great to get assistant. Here is my code:
my custom object:
#interface User : NSObject
#property (nonatomic, retain) NSString *cId;
#property (nonatomic, retain) NSString *firstName;
#property (nonatomic, retain) NSString *lastName;
....
-(instancetype) initWithParameters:(NSDictionary*) parameters;
#end
#import "User.h"
#implementation User
-(instancetype) initWithParameters:(NSDictionary*) parameters
{
self = [super init];
if (self) {
[self setParameters:parameters];
}
return self;
}
- (void) setParameters:(NSDictionary*) parameters{
_cId = parameters[#"cId"];
_firstName = parameters[#"first_name"];
_lastName = parameters[#"last_name"];
....
}
and writing process:
id userObjects = [resultData objectForKey:#"data"];
NSMutableArray* mUsers = [[NSMutableArray alloc] init];
for (NSDictionary* userParameters in userObjects) {
User *user = [[User alloc] initWithParameters:userParameters];
[mUsers addObject:user];
}
userObjects - NSArray got from JSON object from server data.
The problem is : nothing happening and user object still empty after initialization, then I have tried - setValuesForKeysWithDictionary after I called variables same as keys in dictionary and nothing changed.
after adding in mUsers:
Could anybody tell me what I am doing wrong? Thank you!
I believe you think those objects are uninitialized because you are seeing 0 key/value pairs next to each User object.
Your code looks good and I think things will change once you implement [NSObject description] (or [NSObject debugDescription]) like this:
- (NSString *)description
{
return [NSString stringWithFormat:#"cId=%#, firstName=%#, lastName=%#",
_cId, _firstName, _lastName];
}

Unable to store a NSMutableArray(each instance of array contains a custom object) in NSUserDefaults

It's a custom class:
#import <Foundation/Foundation.h>
#interface timeTable : NSObject
#property (nonatomic) int ID;
#property (nonatomic) NSString * type;
#property (nonatomic) NSString * time;
#property (nonatomic) NSString * busno;
#property (nonatomic) NSString * stops;
// nothing is done in it's .m file not even synthesise
// thats an other class
#import <Foundation/Foundation.h>
#import "timeTable.h"
#interface refreshDatabase : NSObject
#property (strong, nonatomic) NSMutableArray * arrayTimeTable;
#property (strong, nonatomic) timeTable * objectTimeTable;
// in it's .m file i am downloading a JSON formatted array using a
service then i am saving it to NsMutbaleArray
// downloading a json array which contains a rows of data
NSError * error;
NSArray * jsonArray = [NSJSONSerialization JSONObjectWithData:
[safeString dataUsingEncoding:NSUTF8StringEncoding]
options:NSJSONReadingAllowFragments error:&error];
NSLog(#"json Array %#", jsonArray);
// for getting an instance of array
NSDictionary * jsonElement;
for (int i=0; i<jsonArray.count ; i++)
{ // each row will be saved in an object of timetable class then that
// object will be saved to nsmutablearray
jsonElement = [jsonArray objectAtIndex:i];
objectTimeTable = [[timeTable alloc]init];
objectTimeTable.ID = [[jsonElement objectForKey:#"id"]intValue];
objectTimeTable.type = [jsonElement objectForKey:#"type"];
objectTimeTable.time = [jsonElement objectForKey:#"time"];
objectTimeTable.busno = [jsonElement objectForKey:#"busno"];
objectTimeTable.stops = [jsonElement objectForKey:#"stops"];
// adding an instance from JSON Array to our NSmutablearray
[arrayTimeTable addObject:objectTimeTable];
}//end of json Array FOR loop
// our array containnig all the objects will be saved using
//NSUserDefualts
// userDefaults is an object of NSUserDefaults
if(userDefaults)
{ // its not saving it to userdefaults
[userDefaults setObject:arrayToStore forKey:#"ArrayOfTimeTables"];
[userDefaults synchronize];
}
// retrieving the saved array from NSUSerDefaults and printing it
// using slog
timeTable *objPrint = [[timeTable alloc]init];
NSMutableArray *arrayLoader = [userDefaults arrayForKey:#"ArrayOfTimeTables"];
for (int i=0; i<arrayLoader.count ; i++)
{
objPrint = [arrayLoader objectAtIndex:i];
NSLog(#"outSide Printing For LOOP After Loading of tim # %d times havind id =%d type = %# time = %# busno = %# stops = %#",i,objPrint.ID,objPrint.type,objPrint.time,objPrint.busno,objPrint.stops);
}
Thanx a lot in helping me in advance.
Please tell me how to save that array which contains object of timetable class into nsUseDefaults and then how to load it back.
Please help me. I read a lot of similar question and answers, but don't know how to make them work for me.
Use NScoding to encode each of your custom object then add that custom object into an array then encode other and then add it to the array then save that array into NSUserDefaults
encoding and decoding of upper given question
is
the custom class .h file
#import <Foundation/Foundation.h>
#interface timeTable : NSObject<NSCoding>
#property (nonatomic) NSString * ID;
#property (nonatomic) NSString * type;
#property (nonatomic) NSString * time;
#property (nonatomic) NSString * busno;
#property (nonatomic) NSString * stops;
the custom class .m file
#import "timeTable.h"
#implementation timeTable
#synthesize ID;
#synthesize type;
#synthesize time;
#synthesize busno;
#synthesize stops;
-(void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeObject:self.ID forKey:#"ID"];
[aCoder encodeObject:self.type forKey:#"type"];
[aCoder encodeObject:self.time forKey:#"time"];
[aCoder encodeObject:self.busno forKey:#"busno"];
[aCoder encodeObject:self.stops forKey:#"stops"];
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
if((self = [super init])) {
//decode properties, other class vars
self.ID = [aDecoder decodeObjectForKey:#"ID"];
self.type = [aDecoder decodeObjectForKey:#"type"];
self.time = [aDecoder decodeObjectForKey:#"time"];
self.busno = [aDecoder decodeObjectForKey:#"busno"];
self.stops = [aDecoder decodeObjectForKey:#"stops"];
}
return self;
}
#end
where you encode each custom object one by one and adding it to the array then save that NSMutableArray or NSArray
into NSUserDefaults
encoding a custom object then adding it to array and saving it into user defaults
// encoding a custom object before saving it to array
NSData *encodeTimeTableObj = [NSKeyedArchiver
archivedDataWithRootObject:objectTimeTable];
addObject:encodeTimeTableObj];
//saving it to user Defaults
if(userDefaults)
{
[userDefaults setObject:arrayTimeTable
forKey:#"ArrayOfTimeTables"];
[userDefaults synchronize];
NSLog(#"saving to usedefaults");
}
retriving an array either mutable or non mutable then decoding each of its object
NSMutableArray *arrayLoader = [userDefaults
objectForKey:#"ArrayOfTimeTables"];
NSData * decode = [arrayLoader objectAtIndex:0];
  // in case of upper given custom class Time Table
timeTable *objPrint = [NSKeyedUnarchiver unarchiveObjectWithData:decode];
Use NSArray to get array from NSUSerDefaults as NSUSerDefaults return immuttable array.
If you need NSMutableArray, then convert this NSArray to NSMutableArray.
// retrieving the saved array from NSUSerDefaults and printing it
// using slog
timeTable *objPrint = [[timeTable alloc]init];
NSArray *arrayLoader = [userDefaults arrayForKey:#"ArrayOfTimeTables"];
for (int i=0; i

Creating Plist iOS

I'm trying to make a restaurant queue app whose first page will be with three buttons, one of them being 'New Reservation'. When clicked on it, it opens a new view that takes in the customers name,phone number and total number of people, the waiter will enter the estimated time for their order and press enter which will save all these values in a plist. I'm referring to youtube videos to create plist, but I have a view controller class in which I initialize the plist and also a NewReservation class that controls the saving data. According to the video I create the plist in viewDidLoad method and also copy few lines of it in the IBAction of the button "enter details". I'm jumbled up and can't access the values in my NewReservation class, please clarify.
Thank you.
ViewController.h
#import <UIKit/UIKit.h>
#class NewReservation;
#interface ViewController : UIViewController
#property (nonatomic , strong) NewReservation *Res;
#end
ViewController.m
#import "ViewController.h"
#import "NewReservation.h"
#interface ViewController ()
#end
#implementation ViewController{
NSMutableArray *phoneNumbers , *name , *noOfPeople , *estTime;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.Res = [[NewReservation alloc] init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"Property List.plist"];
if(![[NSFileManager defaultManager] fileExistsAtPath:plistPath]){
plistPath = [[NSBundle mainBundle] pathForResource:#"Property List" ofType:#"plist"];
}
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSString *errorDesc = nil;
NSPropertyListFormat format;
NSDictionary *temp = (NSDictionary *) [NSPropertyListSerialization propertyListWithData:plistXML options:NSPropertyListMutableContainersAndLeaves format:&format error:&errorDesc];
if(!temp){
NSLog(#"Error Reading plist: %# , format: %lu",errorDesc , format);
}
name = [NSMutableArray arrayWithArray:[temp objectForKey:#"name"]];
phoneNumbers = [NSMutableArray arrayWithArray:[temp objectForKey:#"phoneNumbers"]];
noOfPeople = [NSMutableArray arrayWithArray:[temp objectForKey:#"noOfPeople"]];
estTime = [NSMutableArray arrayWithArray:[temp objectForKey:#"estTime"]];
//Confused how to go further
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
NewReservation.h
#import <UIKit/UIKit.h>
#interface NewReservation : UIViewController{
}
#property (weak, nonatomic) IBOutlet UITextField *name;
#property (weak, nonatomic) IBOutlet UITextField *phoneNumber;
#property (weak, nonatomic) IBOutlet UITextField *noOfPeople;
#property (weak, nonatomic) IBOutlet UITextField *estTime;
- (IBAction)nameReturn:(id)sender;
- (IBAction)enterDetails:(id)sender;
#end
NewReservation.m
#import "NewReservation.h"
#implementation NewReservation
- (IBAction)enterDetails:(id)sender{
if([self.name.text isEqualToString:#""] || [self.phoneNumber.text isEqualToString:#""] || [self.noOfPeople.text isEqualToString:#""] || [self.estTime.text isEqualToString:#""]) {
UIAlertView *error = [[UIAlertView alloc] initWithTitle:#"Oops" message:#"You must complete all fields!" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[error show];
}
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"Property List.plist"];
//Cant continue here cause its confusing me, i need to add the values and update the list everytime a new 'name,noOfPersons,phoneNumber,estTime
//is entered.
}
#end
An instance variable is unique to a class. By default, only the class and subclasses can access it. Therefore, as a fundamental principal of object-oriented programming, instance variables (ivars) are private—they are encapsulated by the class.
By contrast, a property is a public value that may or may not correspond to an instance variable. If you want to make an ivar public, you'd probably make a corresponding property. But at the same time, instance variables that you wish to keep private do not have corresponding properties, and so they cannot be accessed from outside of the class. You can also have a calculated property that does not correspond to an ivar…
Without a property, ivars can be kept hidden. In fact, unless an ivar is declared in a public header it is difficult to even determine that such an ivar exists.
Your child class have a properties that does not correspond to an ivar
To access the ivar with properties the ivar need to be synthesise properties in from child class.
#implementation NewReservation
#synthesize phoneNumbers, name, noOfPeople, estTime;
// ****
#end

iCloud - How to save archive containing array of custom objects

I have developed a small app that stores locally in iOS through archiving an array of custom objects containing:
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *dateCreated;
#property (nonatomic, copy) NSString *desc;
#property (nonatomic, copy) NSString *url;
I want to sync said archive using iCloud and I believe the recommended mechanism is through a UIDocument subclass.
All UIDocument examples I found utlilized a single instance with 1 single NSString, so I am a little confused how to go about syncing a whole array of custom objects but utilizing UIDocument (like I do today locally through NSCoding).
Should I create an array of UIDocument objects containing the properties listed above, should I create an instance of UIDocument containing 1 instance of the data object described above and then create an array containing all the instances, or should 1 single UIDocument contain the complete array of custom objects ?
I have done some research but I am still confused.
In the end I would need to sync just 1 file containing an array of said custom objects.
Thanks in advance for your help
What I have today is a custom class as described above with 4 strings called Snippet and in my Root view Controller I have an NSMutableArray called list where I add each new instance of that Snippet Class.
self.list = [[NSMutableArray alloc] init];
Snippet *newEntry = [[Snippet alloc] init];
[self.list addObject:newEntry];
Should I create an UI Document subclass that owns the array of custom objects ?
The example in the docs does indeed show a UIDocument subclass that just has one string, but it returns a NSData from -contentsForType:error:. You can store as many objects as you like in an NSData using an NSKeyedArchiver. Read Serializing Objects to learn how to encode objects using NSKeyedArchiver (and keep reading to learn how to get them back!).
Using your properties as an example...
#interface MyDocument : UIDocument
{
}
#property (nonatomic, copy) NSString *name;
#property (nonatomic, copy) NSString *dateCreated;
#property (nonatomic, copy) NSString *desc;
#property (nonatomic, copy) NSString *url;
#end
#implementation MyDocument
//...
- (id)contentsForType:(NSString *)typeName error:(NSError **)outError
{
NSMutableData *data = [NSMutableData data];
NSKeyedArchiver *archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];
[archiver encodeObject:name forKey:#"name"];
[archiver encodeObject:dateCreated forKey:#"created"];
[archiver encodeObject:desc forKey:#"desc"];
[archiver encodeObject:url forKey:#"url"];
[archiver finishEncoding];
// release archiver if you're not using ARC
return data;
}
#end;
WARNING: I haven't compiled the code above, so no guarantees. This should serve as an example to illustrate using an archiver to store multiple objects in a single data object which you can return as your document's content.

iPhone NSMutableArray and NSKeyedUnarchiver unarchiveObjectWithFile release oddity

I archive an array (NSMutableArray) of custom objects that implement the .
Once i load it froma file to a retaining property
#property (nonatomic, retain) NSMutableArray *buddies;
the release count of the object is 2 (correct, it's 1of autorelease + 1 of retain of the property) but then noone releases it and the retain count becames 1, so when i release it i get
-[__NSArrayM retainCount]: message sent to deallocated instance
(i think because the 1 retain count is the autorelease)
Here's the full code:
BuddieListViewController.h
#import <UIKit/UIKit.h>
#import "Buddie.h"
#interface BuddieListViewController : UITableViewController {
IBOutlet NSMutableArray *buddies;
[...]
}
[...]
#property (nonatomic, retain) NSMutableArray *buddies;
[...]
#end
BuddieListViewController.m
#import "BuddieListViewController.h"
#import "Buddie.h"
#import "PreviewViewController.h"
#implementation BuddieListViewController
#synthesize buddies;
[...]
- (id)initWithStyle:(UITableViewStyle)style
{
self = [super initWithStyle:style];
if (self) {
[self loadFromDisk];
}
return self;
}
- (void)loadFromDisk {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *appFile = [documentsPath stringByAppendingPathComponent:#"BuddieArchive.ark"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:appFile]) {
self.buddies = [NSKeyedUnarchiver unarchiveObjectWithFile:appFile];
NSLog(#"1- buddies retain count %d (should be 2, 1 + 1autorelease)", [buddies retainCount]);
} else {
self.buddies = [NSMutableArray arrayWithCapacity:1];
}
}
[...]
- (IBAction)cancelledBuddie:(NSNotification *)notification
{
[editingBuddie release];
NSLog(#"2- buddies retain count %d (should be 2, 1 + 1autorelease)", [buddies retainCount]);
[buddies release];
[self loadFromDisk];
[self.tableView reloadData];
}
Has anyone some idea of why this happens?
I can't say it better than this:
The number returned by retainCount is
useless.
Don't rely on it for anything. Use the Leaks tool in Instruments to determine if you're leaking objects.
If you're crashing, it's most likely that you have a zombie. See this video to find out how to use Instruments to find zombies.
If you need to nullify the array, use the property accessor to set it to nil:
self.buddies = nil;
The synthesized implementation takes care of the memory management issues. Try to avoid sending -retain/-release messages directly to instance variables wherever possible and instead allow the property accessors to take care of things for you. It'll save you a lot of trouble.
Rather than releasing buddies why not just do a [self.buddies removeAllObjects] at the beginning of loadFromDisk.

Resources