Make own class able to be saved in NSMutableArray - ios

I use this code to get the path of a not existing file in documents directory of my application
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:#"restaurants.xml"];
getting this path:
/var/mobile/Containers/Data/Application/50BC3507-39BA-4F7A-86BA-254AB9DA6184/Documents/restaurants.xml
My question: How can I make my class being able to be saved in an array?
function to get path:
- (NSString *)restaurantListPath {
NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:#"restaurants.xml"];
return path;
}
function to write array:
- (void)writeRestaurantListToFile:(NSMutableArray *)restaurantList {
[restaurantList writeToFile:[self restaurantListPath] atomically:YES];
}
function to load array:
- (NSMutableArray *)restaurantListOfFile {
NSMutableArray *restaurantList = [NSMutableArray arrayWithContentsOfFile:[self restaurantListPath]];
if (!restaurantList) {
restaurantList = [NSMutableArray array];
}
return restaurantList;
}
I created an array containing 1 JFRestaurant item and saved it with the function above. Then I loaded it with the function above and the array was empty (initialized but with no content).
If there would be an error while writing the file or reading the file the array would be nil not empty. So I think that the content of the array is the problem.
May my class JFRestaurant cannot be wrote to file. I looked at a tutorial and saw, that I need to implement NSCoding protocol to make my class being able to be written to file and did that, but the array is still empty. I also tried with NSKeyedArchiver and NSKeyedUnarchiver, but then I get another error
2014-08-25 16:52:23.969 Restaurants[5577:934385] -[__NSCFArray objectForKey:]: unrecognized selector sent to instance 0x170050860
2014-08-25 16:52:23.970 Restaurants[5577:934385] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFArray objectForKey:]: unrecognized selector sent to instance 0x170050860'
*** First throw call stack:
(0x185866084 0x1954180e4 0x18586d094 0x185869e48 0x18576f08c 0x185741d98 0x18670c9e0 0x18676e170 0x10007d3a0 0x100077cdc 0x189eb39ec 0x189f510b8 0x189f50b0c 0x189f5082c 0x189f507ac 0x189e997a0 0x1897f22a4 0x1897ece90 0x1897ecd34 0x1897ec534 0x1897ec2b8 0x18a125b54 0x18a126a00 0x18a124b84 0x18d96c6ac 0x18581e360 0x18581d468 0x18581b668 0x185749664 0x189f07140 0x189f02164 0x10007b518 0x195a8ea08)
libc++abi.dylib: terminating with uncaught exception of type NSException
JFRestaurant.h:
//
// JFRestaurant.h
// Restaurants
//
// Created by Jonas Frey on 15.08.14.
// Copyright (c) 2014 Jonas Frey. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "JFUtils.h"
#import "JFItem.h"
#interface JFRestaurant : NSObject <NSCoding>
#property (strong, nonatomic) NSString *name;
#property float score;
- (void)addItem:(JFItem *)item;
- (void)removeItem:(JFItem *)item;
- (void)removeItemAtIndex:(NSUInteger)index;
- (void)setItem:(JFItem *)item atIndex:(NSUInteger)index;
- (JFItem *)itemAtIndex:(NSUInteger)index;
- (NSUInteger)indexOfItem:(JFItem *)item;
- (NSInteger)itemCount;
- (NSMutableArray *)fullItemArray;
- (id)initWithName:(NSString *)name;
- (id)initWithName:(NSString *)name items:(NSMutableArray *)items;
#end
JFRestaurant.m
//
// JFRestaurant.m
// Restaurants
//
// Created by Jonas Frey on 15.08.14.
// Copyright (c) 2014 Jonas Frey. All rights reserved.
//
#import "JFRestaurant.h"
#interface JFRestaurant ()
#property (strong, nonatomic) NSMutableArray *items;
- (float)scoreOfItems;
#end
#implementation JFRestaurant
- (id)initWithCoder:(NSCoder *)aDecoder {
NSString *name = [aDecoder decodeObjectForKey:JFKeyRestaurantCoderName];
NSMutableArray *items = [aDecoder decodeObjectForKey:JFKeyRestaurantCoderItems];
return [self initWithName:name items:items];
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_name forKey:JFKeyRestaurantCoderName];
[aCoder encodeObject:_items forKey:JFKeyRestaurantCoderItems];
}
- (id)init {
self = [super init];
if (self) {
self.name = #"Neues Restaurant";
self.items = [[[JFUtils alloc] init] defaultItems];
self.score = [self scoreOfItems];
}
return self;
}
- (id)initWithName:(NSString *)name {
self = [super init];
if (self) {
self.name = name;
self.items = [[[JFUtils alloc] init] defaultItems];
self.score = [self scoreOfItems];
}
return self;
}
- (id)initWithName:(NSString *)name items:(NSMutableArray *)items {
self = [super init];
if (self) {
self.name = name;
self.items = items;
self.score = [self scoreOfItems];
}
return self;
}
- (void)addItem:(JFItem *)item {
[self.items addObject:item];
self.score = [self scoreOfItems];
}
- (void)removeItem:(JFItem *)item {
[self.items removeObject:item];
self.score = [self scoreOfItems];
}
- (void)removeItemAtIndex:(NSUInteger)index {
[self.items removeObjectAtIndex:index];
self.score = [self scoreOfItems];
}
- (void)setItem:(JFItem *)item atIndex:(NSUInteger)index {
[self.items setObject:item atIndexedSubscript:index];
self.score = [self scoreOfItems];
}
- (JFItem *)itemAtIndex:(NSUInteger)index {
return [self.items objectAtIndex:index];
}
- (NSUInteger)indexOfItem:(JFItem *)item {
return [self.items indexOfObject:item];
}
- (NSInteger)itemCount {
return self.items.count;
}
- (NSMutableArray *)fullItemArray {
return self.items;
}
- (float)scoreOfItems {
float value = 0.0f;
float totalPossible = 0.0f;
for (JFItem *item in self.items) {
value += (4 - (float)item.segmentIndex) * 25.0f;
totalPossible += 100.0f;
}
return value / totalPossible * 100.0f;
}
#end
Thanks for your help
iComputerfreak

Related

Troubles storing and loading custom object into NSUserDefaults Objective-C

I've been trying to store and recover from the NSUserDefaults an Array of custom objects in my project but it seems something is going wrong. I've researched a lot about this issue but I couldn't find any answer to my problem.
It seems that the storing it's done OK because I don't get any error when saving data in NSUserDefaults, the problem comes when I try to get back those data: the app will crash completely with libc++abi.dylib: terminating with uncaught exception of type NSException error.
Here's my code:
Wine.h
#import <Foundation/Foundation.h>
#import UIKit;
#define NO_RATING -1
#interface WineModel : NSObject <NSCoding>
#property(copy, nonatomic) NSString *type;
#property(strong, nonatomic) UIImage *photo;
#property(strong, nonatomic) NSURL *photoURL;
#property(strong, nonatomic) NSURL *wineCompanyWeb;
#property(copy, nonatomic) NSString *notes;
#property(copy, nonatomic) NSString *origin;
#property(nonatomic) int rating;
#property(strong, nonatomic) NSArray *grapes;
#property(copy, nonatomic) NSString *name;
#property(copy, nonatomic) NSString *wineCompanyName;
- (id) initWithCoder: (NSCoder *) decoder;
- (void) encodeWithCoder: (NSCoder *) encoder;
//SOME OTHER METHODS...//
-(id) initWithName: (NSString *) aName
wineCompanyName: (NSString *) aWineCompanyName
type: (NSString *) aType
origin: (NSString *) anOrigin
grapes: (NSArray *) arrayOfGrapes
wineCompanyWeb: (NSURL *) aURL
notes: (NSString *) aNotes
rating: (int) aRating
photoURL: (NSURL *) aPhotoURL;
//For JSON
-(id) initWithDictionary: (NSDictionary *) aDict;
#end
Wine.m
#import "WineModel.h"
#implementation WineModel
#synthesize photo = _photo;
#pragma mark - Properties
-(UIImage *) photo {
//SOME MORE CODE...
return _photo;
}
- (id) initWithCoder:(NSCoder *)decoder {
if (self = [super init]) {
self.name = [decoder decodeObjectForKey:#"name"];
self.wineCompanyName = [decoder decodeObjectForKey:#"company"];
self.type = [decoder decodeObjectForKey:#"type"];
self.origin = [decoder decodeObjectForKey:#"origin"];
self.grapes = [self extractGrapesFromJSONArray:[decoder decodeObjectForKey:#"grapes"]];
self.wineCompanyWeb = [NSURL URLWithString:[decoder decodeObjectForKey:#"wine_web"]];
self.notes = [decoder decodeObjectForKey:#"notes"];
self.rating = [[decoder decodeObjectForKey:#"rating"] intValue];
self.photoURL = [NSURL URLWithString:[decoder decodeObjectForKey:#"picture"]];
}
return self;
}
- (void) encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.name forKey:#"name"];
[encoder encodeObject:self.wineCompanyWeb forKey:#"company"];
[encoder encodeObject:self.type forKey:#"type"];
[encoder encodeObject:self.origin forKey:#"origin"];
[encoder encodeObject:self.grapes forKey:#"grapes"];
[encoder encodeObject:self.wineCompanyWeb forKey:#"wine_web"];
[encoder encodeObject:self.notes forKey:#"notes"];
[encoder encodeInt:self.rating forKey:#"rating"];
[encoder encodeObject:self.photoURL forKey:#"picture"];
}
#pragma mark - Init
-(id) initWithName: (NSString *) aName
wineCompanyName: (NSString *) aWineCompanyName
type: (NSString *) aType
origin: (NSString *) anOrigin
grapes: (NSArray *) arrayOfGrapes
wineCompanyWeb: (NSURL *) aURL
notes: (NSString *) aNotes
rating: (int) aRating
photoURL: (NSURL *) aPhotoURL {
if(self==[super init]) {
_name = aName;
_wineCompanyName = aWineCompanyName;
_type = aType;
_origin = anOrigin;
_grapes = arrayOfGrapes;
_wineCompanyWeb = aURL;
_notes = aNotes;
_rating = aRating;
_photoURL = aPhotoURL;
}
return self;
}
#pragma mark - JSON
-(id) initWithDictionary:(NSDictionary *)aDict {
return [self initWithName:[aDict objectForKey:#"name"]
wineCompanyName:[aDict objectForKey:#"company"]
type:[aDict objectForKey:#"type"]
origin:[aDict objectForKey:#"origin"]
grapes:[self extractGrapesFromJSONArray:[aDict objectForKey:#"grapes"]]
wineCompanyWeb:[NSURL URLWithString:[aDict objectForKey:#"wine_web"]]
notes:[aDict objectForKey:#"notes"]
rating:[[aDict objectForKey:#"rating"]intValue]
photoURL:[NSURL URLWithString:[aDict objectForKey:#"picture"]]
];
}
-(NSArray *) extractGrapesFromJSONArray: (NSArray *)JSONArray {
//SOME MORE CODE...
return grapes;
}
#end
This is the wine class. It has the <NSCoding> protocol and both methods (id) initWithCoder: (NSCoder *) decoder; and (void) encodeWithCoder: (NSCoder *) encoder;. So far I looks OK, lets move on to the next class:
Winery.h
#import <Foundation/Foundation.h>
#import "Wine.h"
#define RED_WINE_KEY #"Red"
#define WHITE_WINE_KEY #"White"
#define OTHER_WINE_KEY #"Others"
#interface WineryModel : NSObject
#property (strong, nonatomic) NSMutableArray *redWines;
#property (strong, nonatomic) NSMutableArray *whiteWines;
#property (strong, nonatomic) NSMutableArray *otherWines;
#property(readonly, nonatomic) int redWineCount;
#property(readonly, nonatomic) int whiteWineCount;
#property(readonly, nonatomic) int otherWineCount;
-(WineModel *) redWineAtIndex: (NSUInteger) index;
-(WineModel *) whiteWineAtIndex: (NSUInteger) index;
-(WineModel *) otherWineAtIndex: (NSUInteger) index;
#end
Winery.m
#import "Winery.h"
#implementation WineryModel
#pragma mark - Properties
-(int) redWineCount {
return [self.redWines count];
}
-(int) whiteWineCount {
return [self.whiteWines count];
}
-(int) otherWineCount {
return [self.otherWines count];
}
-(id) init {
if(self == [super init]) {
NSUserDefaults *userDefault=[NSUserDefaults standardUserDefaults];
//Check if there is data stored locally
if(([[[userDefault dictionaryRepresentation] allKeys] containsObject:#"redWines"])
&&([[[userDefault dictionaryRepresentation] allKeys] containsObject:#"whiteWines"])
&&([[[userDefault dictionaryRepresentation] allKeys] containsObject:#"otherWines"])) {
if([userDefault objectForKey:#"redWines"] != nil && [userDefault objectForKey:#"whiteWines"] != nil && [userDefault objectForKey:#"otherWines"] != nil) {
//Try to load data from NSUserDefaults
NSData *decodedRedWines = [userDefault objectForKey:#"redWines"];
self.redWines = [[NSKeyedUnarchiver unarchiveObjectWithData: decodedRedWines] mutableCopy]; //IT WILL CRASH HERE
NSData *decodedWhiteWines = [userDefault objectForKey:#"whiteWines"];
self.whiteWines = [[NSKeyedUnarchiver unarchiveObjectWithData: decodedWhiteWines] mutableCopy];
NSData *decodedOtherWines = [userDefault objectForKey:#"otherWines"];
self.otherWines = [[NSKeyedUnarchiver unarchiveObjectWithData: decodedOtherWines] mutableCopy];
}
} else {
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://url.com/wines.json"]]; //JSON URL
NSURLResponse *response = [[NSURLResponse alloc]init];
NSError *error;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if(data != nil) { //No errors
//Passing from JSON to an NSArray
NSArray * JSONObjects = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:&error];
if (JSONObjects != nil) {
//No errors
for(NSDictionary *dict in JSONObjects){
WineModel *wine = [[WineModel alloc] initWithDictionary:dict];
if(wine.name != nil && wine.wineCompanyName != nil && wine.type != nil && wine.origin != nil ) {
if ([wine.type isEqualToString:RED_WINE_KEY]) {
if (!self.redWines) {
self.redWines = [NSMutableArray arrayWithObject:wine];
}
else {
[self.redWines addObject:wine];
}
}
else if ([wine.type isEqualToString:WHITE_WINE_KEY]) {
if (!self.whiteWines) {
self.whiteWines = [NSMutableArray arrayWithObject:wine];
}
else {
[self.whiteWines addObject:wine];
}
}
else {
if (!self.otherWines) {
self.otherWines = [NSMutableArray arrayWithObject:wine];
}
else {
[self.otherWines addObject:wine];
}
}
}
}
} else {
NSLog(#"JSON parsing error: %#", error.localizedDescription);
}
} else {
NSLog(#"Server error: %#", error.localizedDescription);
}
//Storing the array of wine objects in the NSUserDefaults
NSData *encodedRedWines = [NSKeyedArchiver archivedDataWithRootObject:_redWines];
[userDefault setObject:encodedRedWines forKey:#"redWines"];
NSData *encodedWhiteWines = [NSKeyedArchiver archivedDataWithRootObject:_whiteWines];
[userDefault setObject:encodedWhiteWines forKey:#"whiteWines"];
NSData *encodedOtherWines = [NSKeyedArchiver archivedDataWithRootObject:_otherWines];
[userDefault setObject:encodedOtherWines forKey:#"otherWines"];
}
}
return self;
}
-(WineModel *) redWineAtIndex: (NSUInteger) index {
return [self.redWines objectAtIndex:index];
}
-(WineModel *) whiteWineAtIndex: (NSUInteger) index{
return [self.whiteWines objectAtIndex:index];
}
-(WineModel *) otherWineAtIndex: (NSUInteger) index{
return [self.otherWines objectAtIndex:index];
}
#end
So, the first time you launch the app it will download the data from a JSON file that is in the web, then store the info in the NSUserDefaults. It seems like this step it's done correctly (at least doesn't crash at this point). The problem comes after launching the app the second time. It will check if there are local data store under the NSUserDefault, if so, it'll try to load the data and store into an NSMutableAtray. Unfortunately it won't do so, It crashes here self.redWines =[NSKeyedUnarchiver unarchiveObjectWithData: decodedRedWines]; with the error code I wrote before. When debugging, I can see that there is data when retrieving the redWineskey, but it seems like something it's going wrong.
Mind that I'm using a customized initializer (initWithDictionary) for creating my wines object instead of the default init method. I don't know if it could be the reason of the crash...
Here's the full log:
2017-05-22 20:31:30.354640+0200 App[1905:891526] -[NSTaggedPointerString objectForKey:]: unrecognized selector sent to instance 0xa5c064950b08843b
2017-05-22 20:31:30.354932+0200 App[1905:891526] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSTaggedPointerString objectForKey:]: unrecognized selector sent to instance 0xa5c064950b08843b'
*** First throw call stack:
(0x18e0bafe0 0x18cb1c538 0x18e0c1ef4 0x18e0bef54 0x18dfbad4c 0x1000591d8 0x100057dec 0x18eb0a430 0x18eb10f10 0x18eaa684c 0x18eb0a430 0x18eb09b68 0x18eb08d94 0x100061118 0x1000621d0 0x10005c120 0x19425d204 0x194469738 0x19446f1e0 0x194483d18 0x19446c474 0x18fc63884 0x18fc636f0 0x18fc63aa0 0x18e06942c 0x18e068d9c 0x18e0669a8 0x18df96da4 0x194256384 0x194251058 0x100060b90 0x18cfa559c)
libc++abi.dylib: terminating with uncaught exception of type NSException
Any ideas??
Thanks in advance!!
There is a typo in your initWithCoder method:
self.wineCompanyName = [decoder decodeObjectForKey:#"comapny"];
If that does not fix it, I would look at the NSUserDefaults documentation more closely - it says "Values returned from NSUserDefaults are immutable, even if you set a mutable object as the value." Your redWines property is defined as an NSMutableArray.
To make an immutable object mutable just call mutableCopy
#property (strong, nonatomic) NSMutableArray *redWines;
...
self.redWines = [[NSKeyedUnarchiver unarchiveObjectWithData: decodedRedWines] mutableCopy];

Using NSKeyedArchiver throw Exception:[NSConcreteValue length]: unrecognized selector sent to instance

I tried to use NSKeyedArchiver and NSKeyedUnarchiver.The code throw exception: "[NSConcreteValue length]: unrecognized selector sent to instance".
#interface DBTLine() <NSCoding>
#end
#implementation DBTLine
-(void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeCGPoint:self.begin forKey:#"begin"];
[aCoder encodeCGPoint:self.end forKey:#"end"];
[aCoder encodeObject:self.color forKey:#"color"];
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super init];
if (self) {
//Throw Exception Here.
_begin = [coder decodeCGPointForKey:#"begin"];
_end = [coder decodeCGPointForKey:#"end"];
_color = [coder decodeObjectForKey:#"color"];
}
return self;
}
#end
#interface DBTDrawView()
#property (nonatomic)NSMutableArray *finishedLines;
#end
#implementation DBTDrawView
-(instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if(self){
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSData* dataFinishedLines = [defaults objectForKey:#"finishedLines"];
NSMutableArray *defaultLines = [NSKeyedUnarchiver unarchiveObjectWithData:dataFinishedLines];
if (defaultLines) {
self.finishedLines = defaultLines;
}
}
return self;
}
-(void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
//some code,"self.finishedLines" add line
NSData *dataFinishedLines = [NSKeyedArchiver archivedDataWithRootObject:self.finishedLines];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:dataFinishedLines forKey:#"finishedLines"];
[self setNeedsDisplay];
}
#end
I searched the Internet for a long time,and I've tried multiple variations of my code.Finally,It works.Here is my changed code:
-(void)encodeWithCoder:(NSCoder *)aCoder {
NSValue *begin = [NSValue valueWithCGPoint:self.begin];
NSValue *end = [NSValue valueWithCGPoint:self.end];
[aCoder encodeObject:begin forKey:#"begin"];
[aCoder encodeObject:end forKey:#"end"];
[aCoder encodeObject:self.color forKey:#"color"];
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super init];
if (self) {
NSValue *begin = [coder decodeObjectForKey:#"begin"];
NSValue *end = [coder decodeObjectForKey:#"end"];
_begin = [begin CGPointValue];
_end = [end CGPointValue];
_color = [coder decodeObjectForKey:#"color"];
}
return self;
}
But the problem is:
It seems the problem appeared in the process of using "-
encodeCGPoint:forKey: - decodeCGPointForKey:".Was there anything
wrong with what I did?
What is "[NSConcreteValue length]"?I can't find it in the document.
Thanks in advance.

iOS NSKeyedUnarchiver not get correct value

I simplify the code for ask question.
I want to save more class and load class model file.
But now I simplify parameters to one part (lim) in the model(FlightSettingModel.h).
In the FlightSettingModel.h I have set some parameters in here.
FlightSettingModel.h code below:
#import <Foundation/Foundation.h>
#interface FlightSettingModel : NSObject<NSCoding>
// lim property part
#property float limCurrentVal;
#property float limDefaultVal;
#property float limMaxVal;
#property float limMinVal;
// .... other property part
#end
FlightSettingModel.m code below
#import "FlightSettingModel.h"
#interface FlightSettingModel()
{
}
#end
#implementation FlightSettingModel
-(instancetype)init
{
self = [super init];
if(self)
{
self.limDefaultVal = 3.0;
self.limCurrentVal = 4.0;
self.limMaxVal = 10;
self.limMinVal = 0;
// ... other property part value init .....
}
return self;
}
- (void)setFlightSettingToDefaultValue
{
self.limCurrentVal = self.limDefaultVal;
}
- (void) encodeWithCoder: (NSCoder *)encoder
{
[encoder encodeFloat:self.limCurrentVal forKey:#"limCurrentVal"];
}
- (id)initWithCoder:(NSCoder *)decoder {
if((self = [super init])) {
self.limCurrentVal = [decoder decodeFloatForKey:#"limCurrentVal"];
}
return self;
}
#end
Then I have set the singleton SettingData file to initial the FlightSettingModel and other model class.
The SettingData model header like below:
#import <Foundation/Foundation.h>
#import "FlightSettingModel.h"
#interface SettingData : NSObject
#property (nonatomic,strong) FlightSettingModel *flightSettingModel;
+(SettingData*) sharedInstance;
#end
SettingData.m code below:
#import "SettingData.h"
#implementation SettingData
SettingData *sharedInstance;
+(SettingData*) sharedInstance{
if( sharedInstance == nil )
{
sharedInstance = [SettingData new];
}
return sharedInstance;
}
-(id) init{
self = [super init];
if( self )
{
self.flightSettingModel = [FlightSettingModel new];
}
return self;
}
#end
In my storyboard (UI) is like below:
When I click the save button , I want to save the custom class model(FlightSettingModel.h) in the NSKeyedArchiver. When I click the load, I want to load the model from the archiver using NSKeyedUnarchiver and resetting to the slider.
But now,when I drag the slider to other value(ex:10), then I click the save, then I close the app restart the app. I click the load, the slider value will become 0.
I don't know why when I load the value the all value will become 0;
My view controller code .m below:
#import "ViewController.h"
#import "SettingData.h"
#interface ViewController ()
{
NSString *path;
NSString *fileName;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// file manage
path =[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES) objectAtIndex:0];
fileName = [path stringByAppendingPathComponent:#"flightFile"];
[self setUIValue];
NSLog(#"---");
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
- (IBAction)saveAction:(UIButton *)sender {
[NSKeyedArchiver archiveRootObject:[SettingData sharedInstance].flightSettingModel toFile:fileName];
}
- (IBAction)loadAction:(UIButton *)sender {
[SettingData sharedInstance].flightSettingModel = (FlightSettingModel*) [NSKeyedUnarchiver unarchiveObjectWithFile: fileName];
[self setUIValue];
NSLog(#"current Value:%.f",[SettingData sharedInstance].flightSettingModel.limCurrentVal);
}
- (IBAction)sliderChangedAction:(UISlider *)sender {
[SettingData sharedInstance].flightSettingModel.limCurrentVal = sender.value;
self.theTextField.text = [NSString stringWithFormat:#"%.f",self.theSlider.value];
}
-(void) setUIValue
{
// setting slider property
self.theSlider.maximumValue = [SettingData sharedInstance].flightSettingModel.limMaxVal;
self.theSlider.minimumValue = [SettingData sharedInstance].flightSettingModel.limMinVal;
self.theSlider.value = [SettingData sharedInstance].flightSettingModel.limCurrentVal;
self.theTextField.text = [NSString stringWithFormat:#"%.f",self.theSlider.value];
}
#end
Have anyone know where my problem in my code?
thank you very much.
If you want to download the complete code(the question code) , I have upload in github
This behavior happens because the [SettingData sharedInstance].flightSettingModel.limMaxVal and the [SettingData sharedInstance].flightSettingModel.limMinVal are zero:
-(void) setUIValue
{
// setting slider property
self.theSlider.maximumValue = [SettingData sharedInstance].flightSettingModel.limMaxVal;
// self.theSlider.maximumValue = 0
self.theSlider.minimumValue = [SettingData sharedInstance].flightSettingModel.limMinVal;
// self.theSlider.minimumValue = 0
self.theSlider.value = [SettingData sharedInstance].flightSettingModel.limCurrentVal;
// [SettingData sharedInstance].flightSettingModel.limCurrentVal = 10
self.theTextField.text = [NSString stringWithFormat:#"%.f",self.theSlider.value];
}
EDIT: You can fix it by adding this:
- (void) encodeWithCoder: (NSCoder *)encoder {
[encoder encodeFloat:self.limCurrentVal forKey:#"limCurrentVal"];
[encoder encodeFloat:self.limMaxVal forKey:#"limMaxVal"];
[encoder encodeFloat:self.limMinVal forKey:#"limMinVal"];
}
- (id)initWithCoder:(NSCoder *)decoder {
if((self = [super init])) {
self.limCurrentVal = [decoder decodeFloatForKey:#"limCurrentVal"];
self.limMaxVal = [decoder decodeFloatForKey:#"limMaxVal"];
self.limMinVal = [decoder decodeFloatForKey:#"limMinVal"];
}
return self;
}

Singleton Array Won't Store Data Model

I'm using a singleton class (contactStorage) and a data model (contactModel) to store a list of contacts. I have created a contact object in my viewdidload of my root view controller and attempted to add it to the NSMutableArray but it will not "stick". I have logged the incoming object inside the addContact procedure and it produces accurate output, however, the addObject:c does not add it to the array. Any insight on this?
#import "contactListViewController.h"
#import "contactDetailScreenViewController.h"
#import "ContactModel.h"
#import "contactStorage.h"
#interface contactListViewController ()
#end
#implementation contactListViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
ContactModel* c = [[ContactModel alloc] initWithfName:#"Mike" andlName:#"Deasy" andEmail:#"mid31#pitt.edu" andPhone:#"4127154194"];
[c logContact];
[[contactStorage shared]addContact:c];
[[contactStorage shared]saveToFile];
[c release];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
The code for my singleton:
//
// contactStorage.m
// contactList
//
// Created by dev on 10/23/13.
// Copyright (c) 2013 Deasy, Michael William. All rights reserved.
//
#import "contactStorage.h"
#implementation contactStorage
{
}
#synthesize cList = _cList;
static contactStorage* _myOnlyInstance = nil;
#pragma mark Storage Methods
-(void)addContact: (ContactModel*) c
{
[c logContact];
[self.cList addObject:c];
NSLog(#"%#", _cList);
}
-(ContactModel*)getContact: (NSIndexPath*) index
{
return [self.cList objectAtIndex:index.row];
}
-(NSMutableArray*)deleteContact: (NSIndexPath*) index
{
[self.cList removeObjectAtIndex:index.row];
return self.cList;
}
-(NSMutableArray*)getAllContacts
{
return self.cList;
}
-(void)saveToFile
{
NSString* path = [[self documentsPath] stringByAppendingPathComponent:#"data.txt"];
NSLog(#"%#",path);
[_cList writeToFile:path atomically:YES];
NSLog(#"%#", self.cList);
}
#pragma mark Singleton Create
-(id)init
{
self = [super init];
if (self)
{
NSLog(#"Initing the array");
_cList = [[NSMutableArray alloc] init];
}
return self;
}
+(contactStorage*)shared
{
if (_myOnlyInstance == nil)
{
_myOnlyInstance = [[contactStorage alloc] init];
}
return _myOnlyInstance;
}
-(NSString*) documentsPath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
return documentsDir;
}
#end
The code for my contactModel:
//
// ContactModel.m
// contactList
//
// Created by dev on 10/23/13.
// Copyright (c) 2013 Deasy, Michael William. All rights reserved.
//
#import "ContactModel.h"
#implementation ContactModel
{
}
#synthesize fName = _fName;
#synthesize lName = _lName;
#synthesize email = _email;
#synthesize phone = _phone;
-(void)logContact
{
NSLog(#"%#", self.fName);
NSLog(#"%#", self.lName);
NSLog(#"%#", self.email);
NSLog(#"%#", self.phone);
}
-(void)dealloc
{
[_fName release];
[_lName release];
[_email release];
[_phone release];
[super dealloc];
}
-(id) initWithfName: (NSString*) fName
andlName: (NSString*) lName
andEmail: (NSString*) email
andPhone: (NSString*) phone
{
self = [super init];
_fName = [[NSString alloc] initWithString:fName];
_lName = [[NSString alloc] initWithString:lName];
_email = [[NSString alloc] initWithString:email];
_phone = [[NSString alloc] initWithString:phone];
return self;
}
#end
NSLog Output:
2013-10-24 12:50:35.573 contactList[3097:a0b] Mike
2013-10-24 12:50:35.574 contactList[3097:a0b] Deasy
2013-10-24 12:50:35.575 contactList[3097:a0b] mid31#pitt.edu
2013-10-24 12:50:35.575 contactList[3097:a0b] 4127154194
2013-10-24 12:50:35.576 contactList[3097:a0b] Initing the array
2013-10-24 12:50:35.576 contactList[3097:a0b] Mike
2013-10-24 12:50:35.576 contactList[3097:a0b] Deasy
2013-10-24 12:50:35.577 contactList[3097:a0b] mid31#pitt.edu
2013-10-24 12:50:35.577 contactList[3097:a0b] 4127154194
2013-10-24 12:50:35.578 contactList[3097:a0b] (
"<ContactModel: 0x8d72720>"
)
2013-10-24 12:50:35.578 contactList[3097:a0b] /Users/dev/Library/Application Support/iPhone Simulator/7.0/Applications/7CFD98F0-C502-49E5-953B-FD43B61EDC38/Documents/data.txt
2013-10-24 12:50:35.579 contactList[3097:a0b] (
"<ContactModel: 0x8d72720>"
)
Clearly, your singleton is successfully adding the ContactModel object to the array of your singleton (as evidenced by your NSLog statement). I assume your question stems from the fact that you're not seeing your file saved.
That's because you're trying to use writeToFile of your NSMutableArray (which tries to save a plist file). If you check the return code of writeToFile, you'll see it failed. This is because you cannot write a plist with an array consisting of custom objects. You might want to use NSKeyedArchiver instead, e.g.:
- (void)saveToFile
{
NSString* path = [[self documentsPath] stringByAppendingPathComponent:#"cList.dat"];
BOOL success = [NSKeyedArchiver archiveRootObject:_cList toFile:path];
NSAssert(success, #"write failed");
}
Anticipating the logical follow-up question, how to read the file, you would use NSKeyedUnarchiver, like so:
-(void)loadFromFile
{
NSString* path = [[self documentsPath] stringByAppendingPathComponent:#"cList.dat"];
self.cList = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
NSAssert(_cList, #"read failed");
}
But, for this to work, you have to make your contact model conform to the NSCoding protocol, namely adding the following methods to that class:
#pragma mark - NSCoding methods
- (NSArray *)propertyNames
{
return #[#"fName", #"lName", #"email", #"phone"];
}
- (id) initWithCoder:(NSCoder *)aDecoder
{
// if `super` conforms to `NSCoding`, then use
//
// self = [super initWithCoder:aDecoder];
//
// in this case, `super` is `NSObject`, so just call `init`
self = [super init];
if (self) {
for (NSString *key in [self propertyNames]) {
[self setValue:[aDecoder decodeObjectForKey:key] forKey:key];
}
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
// if `super` conforms to `NSCoding`, itself, then call `encodeWithCoder` for `super`:
//
// [super encodeWithCoder:aCoder];
//
// in this case, `super` is `NSObject`, so that is not needed
for (NSString *key in [self propertyNames]) {
[aCoder encodeObject:[self valueForKey:key] forKey:key];
}
}
For more information about using archives, see the Archives and Serializations Programming Guide.

Which one is the right method of initializing a NSMutableDictionary . Please anyone correct me here

I am trying to create a NSMutableDictionary in my class. I have read many post in stackoverflow to understand the difference. But now i am totally confused. So any one correct me , which one is the correct way of initialing a NSMutableDictionary in my class . I have to access this dictiionary in many areas of my application .So suggest me the good way of using the variable initialization ...
/// .h file
#interface ActiveFeeds : NSObject {
}
#property (nonatomic, copy) NSMutableDictionary *invDictionary;
#property (nonatomic, retain) NSString *filePath;
#end
#implementation ActiveFeeds
#synthesize filePath;
#synthesize invDictionary;
- (id)init{
self = [super init];
if (self != nil){
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:self.filePath];
self.invDictionary = [dictionary mutableCopy];
dictionary release];
}
return self;
}
/* And use self.invDictionary all in the application */
- (void)setObjectAtKey:(NSMutableDictionary *)objectDic atKey:(NSString *)setKey{
[self.invDictionary setObject:objectDic forKey:setKey];
}
- (void)dealloc {
[self.invDictionary release];
[self.filePath release];
[super dealloc];
}
#end
or like this ....
#interface ActiveFeeds : NSObject {
NSMutableDictionary *invDictionary;
NSString *filePath;
}
#end
#implementation ActiveFeeds
- (id)init{
self = [super init];
if (self != nil){
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];
invDictionary = [dictionary mutableCopy];
[dictionary release];
}
}
return self;
}
/* And use invDictionary all in the application */
- (void)setObjectAtKey:(NSMutableDictionary *)objectDic atKey:(NSString *)setKey{
[invDictionary setObject:objectDic forKey:setKey];
}
- (void)dealloc {
[invDictionary release];
[filePath release];
[super dealloc];
}
#end
Please any one help me to get the correct way of using the variables ....
- (id)initWithFilePath:(NSString *)path{
self = [super init];
if (self != nil){
self.filePath = path;
self.invDictionary = [NSMutableDictionary dictionaryWithContentsOfFile:path];
}
return self;
}
also
- (void)dealloc {
[invDictionary release];
[filePath release];
[super dealloc];
}

Resources