CoreData, transient attribute and EXC_BAD_ACCESS. - objective-c

I'm trying to build simple file browser and i'm stuck.
I defined classes, build window, add controllers, views.. Everything works but only ONE time.
Selecting again Folder in NSTableView or trying to get data from Folder.files causing silent EXC_BAD_ACCESS (code=13, address0x0) from main.
Info about files i keep outside of CoreData, in simple class, I don't want to save them:
#import <Foundation/Foundation.h>
#interface TPDrawersFileInfo : NSObject
#property (nonatomic, retain) NSString * filename;
#property (nonatomic, retain) NSString * extension;
#property (nonatomic, retain) NSDate * creation;
#property (nonatomic, retain) NSDate * modified;
#property (nonatomic, retain) NSNumber * isFile;
#property (nonatomic, retain) NSNumber * size;
#property (nonatomic, retain) NSNumber * label;
+(TPDrawersFileInfo *) initWithURL: (NSURL *) url;
#end
#implementation TPDrawersFileInfo
+(TPDrawersFileInfo *) initWithURL: (NSURL *) url {
TPDrawersFileInfo * new = [[TPDrawersFileInfo alloc] init];
if (new!=nil) {
NSFileManager * fileManager = [NSFileManager defaultManager];
NSError * error;
NSDictionary * infoDict = [fileManager attributesOfItemAtPath: [url path] error:&error];
id labelValue = nil;
[url getResourceValue:&labelValue forKey:NSURLLabelNumberKey error:&error];
new.label = labelValue;
new.size = [infoDict objectForKey: #"NSFileSize"];
new.modified = [infoDict objectForKey: #"NSFileModificationDate"];
new.creation = [infoDict objectForKey: #"NSFileCreationDate"];
new.isFile = [NSNumber numberWithBool:[[infoDict objectForKey:#"NSFileType"] isEqualToString:#"NSFileTypeRegular"]];
new.extension = [url pathExtension];
new.filename = [[url lastPathComponent] stringByDeletingPathExtension];
}
return new;
}
Next I have class Folder, which is NSManagesObject subclass
// Managed Object class to keep info about folder content
#interface Folder : NSManagedObject {
NSArray * _files;
}
#property (nonatomic, retain) NSArray * files; // Array with TPDrawersFileInfo objects
#property (nonatomic, retain) NSString * url; // url of folder
-(void) reload; //if url changed, load file info again.
#end
#implementation Folder
#synthesize files = _files;
#dynamic url;
-(void)awakeFromInsert {
[self addObserver:self forKeyPath:#"url" options:NSKeyValueObservingOptionNew context:#"url"];
}
-(void)awakeFromFetch {
[self addObserver:self forKeyPath:#"url" options:NSKeyValueObservingOptionNew context:#"url"];
}
-(void)prepareForDeletion {
[self removeObserver:self forKeyPath:#"url"];
}
-(void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (context == #"url") {
[self reload];
}
}
-(void) reload {
NSMutableArray * result = [NSMutableArray array];
NSError * error = nil;
NSFileManager * fileManager = [NSFileManager defaultManager];
NSString * percented = [self.url stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSArray * listDir = [fileManager contentsOfDirectoryAtURL: [NSURL URLWithString: percented]
includingPropertiesForKeys: [NSArray arrayWithObject: NSURLCreationDateKey ]
options:NSDirectoryEnumerationSkipsHiddenFiles
error:&error];
if (error!=nil) {NSLog(#"Error <%#> reading <%#> content", error, self.url);}
for (id fileURL in listDir) {
TPDrawersFileInfo * fi = [TPDrawersFileInfo initWithURL:fileURL];
[result addObject: fi];
}
_files = [NSArray arrayWithArray:result];
}
#end
In app delegate i defined
#interface TPAppDelegate : NSObject <NSApplicationDelegate> {
IBOutlet NSArrayController * foldersController;
Folder * currentFolder;
}
- (IBAction)chooseDirectory:(id)sender; // choose folder
and
- (Folder * ) getFolderObjectForPath: path {
//gives Folder object if already exist or nil if not
.....
}
- (IBAction)chooseDirectory:(id)sender {
//Opens panel, asking for url
NSOpenPanel * panel = [NSOpenPanel openPanel];
[panel setCanChooseDirectories:YES];
[panel setCanChooseFiles:NO];
[panel setMessage:#"Choose folder to show:"];
NSURL * currentDirectory;
if ([panel runModal] == NSOKButton)
{
currentDirectory = [[panel URLs] objectAtIndex:0];
}
Folder * folderObject = [self getFolderObjectForPath:[currentDirectory path]];
if (folderObject) {
//if exist:
currentFolder = folderObject;
} else {
// create new one
Folder * newFolder = [NSEntityDescription
insertNewObjectForEntityForName:#"Folder"
inManagedObjectContext:self.managedObjectContext];
[newFolder setValue:[currentDirectory path] forKey:#"url"];
[foldersController addObject:newFolder];
currentFolder = newFolder;
}
[foldersController setSelectedObjects:[NSArray arrayWithObject:currentFolder]];
}
Please help ;)

Ha!
_files = [NSArray arrayWithArray:result];
Should be:
_files = [[NSArray arrayWithArray:result] retain];

Related

How to pass NSArray from an NSObject class to a UIViewController class?

I am new to Objective-C. I am trying to create a weather app where I parsed data from open weather map. I have stored the parsed data to an array. Now want to access the array value from other class but getting null value.
Can anyone help me?
What I have tried:
Here is my NSObject class where I am storing data and trying to send that to view controller:
- (void)getCurrentWeather:(NSString *)query
{
NSString *const BASE_URL_STRING = #"http://api.openweathermap.org/data/2.5/weather?q=";
NSString *const API_KEY = #"&APPID=APIKEYSTRING";
NSString *weatherURLText = [NSString stringWithFormat:#"%#%#%#",
BASE_URL_STRING, query,API_KEY];
NSURL *weatherURL = [NSURL URLWithString:weatherURLText];
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL:weatherURL];
[self performSelectorOnMainThread:#selector(fetchedDataSmile | :) withObject:data waitUntilDone:YES];
});
}
- (void)fetchedData:(NSData *)responseData {
NSError* error;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
NSString* cityName = [json objectForKey:#"name"];
int currentTempCelsius = (int)[[[json objectForKey:#"main"] objectForKey:#"temp"] intValue] - ZERO_CELSIUS_IN_KELVIN;
int maxTemp = (int)[[[json objectForKey:#"main"] objectForKey:#"temp_max"] intValue] - ZERO_CELSIUS_IN_KELVIN;
int minTemp = (int)[[[json objectForKey:#"main"] objectForKey:#"temp_min"] intValue] - ZERO_CELSIUS_IN_KELVIN;
NSString *weatherDescription = [[[json objectForKey:#"weather"] objectAtIndexBlush | :O ] objectForKey:#"description"];
weatherArray = [[NSMutableArray alloc] initWithObjects:cityName, weatherDescription,
[NSString stringWithFormat:#"%d", currentTempCelsius],
[NSString stringWithFormat:#"%d", maxTemp],
[NSString stringWithFormat:#"%d", minTemp],nil];
I have NSObject.h file as:
#interface WeatherData : NSObject
#property (nonatomic) NSString *weatherDescription;
#property (strong, nonatomic) NSString *currentTemp;
#property (nonatomic) int maxTempCelsius;
#property (nonatomic) int minTempCelsius;
#property (nonatomic, retain) NSMutableArray *weatherArray;
- (void)getCurrentWeather:(NSString *)query;
#end
In my view controller:
.h file:
#property (nonatomic, retain) NSMutableArray *weatherResultArray;
.m file:
-(void)searchButtonClicked:(UIButton*)sender
{
[self.view endEditing:YES];
WeatherData *weather = [[WeatherData alloc] init];
[weather getCurrentWeather:_textField.text];
self.weatherResultArray = weather.weatherArray;
//temperatureLabel.text = [NSString stringWithFormat:#"%d°",weather.currentTempCelsius];
}
I just want to show the results in UILabel.
Have you tried returning NSMutable array in this method
- (NSMutableArray*)getCurrentWeather:(NSString *)query
instead of this,
- (void)getCurrentWeather:(NSString *)query
This would be the easiest way to verify and also value can be retrieved in single statement as:
self.weatherResultArray = [weather getCurrentWeather:_textField.text];
One more thing, Don't forget to allocate and initialise your weatherResultArray as:
self.weatherResultArray = [[NSMutableArray alloc]init];
In NSObject class, define a weather protocol.
//NSObject.h file
#protocol WeatherDelegate<NSObject>
-(void)getWeatherData:(YourNSObjectClass*)viewController getWeatherData:(NSMutableArray*)array;
#end
//NSObject.m file, in
- (void)fetchedData:(NSData *)responseData {
NSError* error;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:&error];
NSString* cityName = [json objectForKey:#"name"];
int currentTempCelsius = (int)[[[json objectForKey:#"main"] objectForKey:#"temp"] intValue] - ZERO_CELSIUS_IN_KELVIN;
int maxTemp = (int)[[[json objectForKey:#"main"] objectForKey:#"temp_max"] intValue] - ZERO_CELSIUS_IN_KELVIN;
int minTemp = (int)[[[json objectForKey:#"main"] objectForKey:#"temp_min"] intValue] - ZERO_CELSIUS_IN_KELVIN;
NSString *weatherDescription = [[[json objectForKey:#"weather"] objectAtIndexBlush | :O ] objectForKey:#"description"];
weatherArray = [[NSMutableArray alloc] initWithObjects:cityName, weatherDescription,
[NSString stringWithFormat:#"%d", currentTempCelsius],
[NSString stringWithFormat:#"%d", maxTemp],
[NSString stringWithFormat:#"%d", minTemp],nil];
id<WeatherDelegate> strongDelegate = self.delegate;
if ([strongDelegate respondsToSelector:#selector(getWeatherData:getWeatherData:)])
{
[strongDelegate getWeatherData:self getWeatherData:weatherArray];
}
}
In yourViewController class,Add this WeatherData protocol and add the delegate function in .m file to fetch the data.
#interface yourViewControllerClass()<WeatherDelegate>
{
YourNSObjectClass *nsClass;
NSMutableArray *dataArray;
}
-(void)getWeatherData:(YourNSObjectClass*)viewController getWeatherData:(NSMutableArray*)array{
dataArray = [[NSMutableArray alloc]initWithArray:array];
}
-(void)searchButtonClicked:(UIButton*)sender
{
[self.view endEditing:YES];
WeatherData *weather = [[WeatherData alloc] init];
[weather getCurrentWeather:_textField.text];
self.weatherResultArray = dataArray;
//temperatureLabel.text = [NSString stringWithFormat:#"%d°",weather.currentTempCelsius];
}

Can't retrieve data after storing to disk using NSKeyedArchiver

I'm storing a custom object in a dictionary which i'm then saving to disk using NSKeyedArchiver. In the same method where I save the data, I do a quick test at the end to make sure I can load the data but everything comes out null. Why is this happening? I've been following the instructions in this tutorial: http://cocoadevcentral.com/articles/000084.php
/** Interface of viewcontroller**/
#import "User.h"
#interface BWViewController : UIViewController
{
IBOutlet UITextField *userNameField;
IBOutlet UITextField *passwordField;
IBOutlet UILabel * loginStatus;
}
#property (nonatomic, copy) NSString *holdPassword, *holdUserName;
-(IBAction)signUpButton;
-(IBAction)loginButton;
-(NSString*) pathForDataFile;
-(void) saveDataToDisk:(User*) someUser;
-(User *) loadDataFromDisk:(NSString*) theKey;
#end
/**Implementation file **/
#import "BWViewController.h"
#interface BWViewController ()
#end
#implementation BWViewController
#synthesize holdPassword,holdUserName;
-(IBAction)signUpButton
{
User * firstUser = [[User alloc] init];
firstUser.userName = userNameField.text;
firstUser.password = passwordField.text;
/**To save to file**/
[self saveDataToDisk:firstUser];
loginStatus.text = #"Thanks for signing up";
}
-(NSString*) pathForDataFile
{
NSFileManager * fileManager = [NSFileManager defaultManager];
NSString *folder =#"~/Library/Application Support/signUpApp/";
folder = [folder stringByExpandingTildeInPath];
if([fileManager fileExistsAtPath:folder] == NO)
{
[fileManager createFileAtPath:folder contents:nil attributes:nil];
}
NSString *fileName = #"signUpApp.demo";
return [folder stringByAppendingPathComponent:fileName];
}
-(void) saveDataToDisk:(User*) someUser
{
User * savingUser = someUser;
NSString * path = [self pathForDataFile];
NSMutableDictionary * rootObject = [NSMutableDictionary dictionary];
[rootObject setValue:savingUser forKey:userNameField.text];
[NSKeyedArchiver archiveRootObject:rootObject toFile:path];
NSLog(#"saving user to disk %#", savingUser.userName);
/**Testing the load process**/
User * testLoadUser;
NSMutableDictionary *unRootObject;
unRootObject = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
testLoadUser = [unRootObject valueForKey:userNameField.text];
NSLog(#"Testing the unarchive %# and %#", testLoadUser.userName, testLoadUser.password);
}
/**User implementation file**/
#import "User.h"
#implementation User
#synthesize userName, password;
- (void) encodeWithCoder: (NSCoder *)coder
{
[coder encodeObject: userName forKey:#"username"];
[coder encodeObject: password forKey:#"password"];
}
- (id) initWithCoder: (NSCoder *)coder
{
if (self = [super init])
{
self.userName = [coder decodeObjectForKey:#"username"];
self.password = [coder decodeObjectForKey:#"password"];
}
return self;
}
#end
This looks like iOS code, but you're using a Mac OS X convention for determining the folder for the path. In iOS, you should use the Documents folder.
For iOS, the path for the filename in the Documents folder should be:
- (NSString*) pathForDataFile
{
NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *path = [documentsPath stringByAppendingString:#"signUpApp.demo"];
return path;
}
I'd also suggest checking the return code from archiveRootObject method, to confirm whether the archive succeeded or not. No point in going further if that failed.
Also, in your old pathForDataFile, you're calling createFileAtPath:contents:attributes:, but I suspect you intended createDirectoryAtURL:withIntermediateDirectories:attributes:error:. The former is for creating a file, and the latter is for creating a folder. And your code is trying to create a folder.
Quick things you can check..
1) IS file is Actually created on the location ?
2) Is there any data (random chars) in that file ?
3) Is your code contains <NSCopying> protocol in your user.h class?
4) Make sure you don't have - (id) initWithCoder: (NSCoder *)coder and - (void) encodeWithCoder: (NSCoder *)coder in your header file.

Get NSMutableDictionary from Singleton?

I created a singleton class in order to share an object inside my program. Here's the code:
SelectedRow.h
#import <Foundation/Foundation.h>
#import "TableEntry.h"
#interface SelectedRow : NSObject {
TableEntry *rowValue;
}
#property (nonatomic, retain) TableEntry *rowValue;
+ (id)sharedManager;
- (void)setVariable:(TableEntry*)value;
#end
and SelectedRow.m
#import "SelectedRow.h"
#import "TableEntry.h"
#implementation SelectedRow
#synthesize rowValue;
+ (id)sharedManager {
static SelectedRow *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (id)init {
if (self = [super init]) {
rowValue = [[TableEntry alloc] init];
}
return self;
}
- (void)setVariable:(TableEntry*)value {
rowValue = value;
}
#end
while TableEntry.h
#import <Foundation/Foundation.h>
#interface TableEntry : NSObject {
#private
NSString *videoId;
NSString *videoCategory;
NSString *videoTitle;
NSString *videoDescription;
NSDate *videoDate;
NSMutableArray *videoRelatedVideos;
NSDictionary *videoAdditionalInformation;
NSString *videoAccessControl;
NSArray *videoFields;
NSMutableDictionary *days;
NSMutableDictionary *views;
NSMutableDictionary *watchtime;
NSMutableDictionary *subscribers;
NSMutableDictionary *shares;
}
#property (copy) NSString *videoId;
#property (copy) NSString *videoCategory;
#property (copy) NSString *videoTitle;
#property (copy) NSString *videoDescription;
#property (copy) NSMutableArray *videoRelatedVideos;
#property (copy) NSDictionary *videoAdditionalInformation;
#property (copy) NSArray *videoFields;
#property (copy) NSString *videoAccessControl;
#property (copy) NSDate *videoDate;
#property (copy) NSMutableDictionary *days;
#property (copy) NSMutableDictionary *views;
#property (copy) NSMutableDictionary *subscribers;
#property (copy) NSMutableDictionary *shares;
#property (copy) NSMutableDictionary *watchtime;
- (id)setId:(NSString*)Id setCategory:(NSString*)Category setDate:(NSDate*)date setTitle:(NSString*)title setDescription:(NSString*)description setRelatedVideos:(NSMutableArray*)relatedVideos setAdditionalInformation:(NSDictionary*)additionalInformation setAccessControl:(NSString*)accessControl setFields:(NSArray*)fields setDays:(NSMutableDictionary*)days setViews:(NSMutableDictionary*)views setSubscribers:(NSMutableDictionary*)subscribers setShares:(NSMutableDictionary*)shares setWatchtime:(NSMutableDictionary*)watchtime;
- (NSString*)extractId;
- (NSString*)extractCategory;
- (NSString*)extractTitle;
- (NSString*)extractDescription;
- (NSMutableArray*)extractRelatedVideos;
- (NSDictionary*)extractAdditionalInformationVideos;
- (NSDictionary*)extractAccessControlVideos;
- (NSArray*)extractFields;
- (NSMutableDictionary*)extractDays;
- (NSMutableDictionary*)extractViews;
- (NSMutableDictionary*)extractSubscribers;
- (NSMutableDictionary*)extractShares;
- (NSMutableDictionary*)extractWatchtime;
#end
and TableEntry.m
- (id)init {
self = [super init];
if (self) {
videoId = #"9bZkp7q19f0";
videoCategory = #"Music";
videoTitle = #"Demo Title";
videoDescription = #"Demo description";
videoDate = [NSDate date];
videoAdditionalInformation = [NSDictionary alloc];
videoRelatedVideos = [NSMutableArray alloc];
videoAccessControl = #"demo accesControl";
videoFields = [NSArray alloc];
days = [NSMutableDictionary alloc];
views = [NSMutableDictionary alloc];
shares = [NSMutableDictionary alloc];
subscribers = [NSMutableDictionary alloc];
watchtime = [NSMutableDictionary alloc];
}
return self;
}
- (id)setId:(NSString*)Id setCategory:(NSString*)Category setDate:(NSDate*)date setTitle:(NSString*)title setDescription:(NSString*)description setRelatedVideos:(NSMutableArray*)relatedVideos setAdditionalInformation:(NSDictionary*)additionalInformation setAccessControl:(NSString*)accessControl setFields:(NSArray*)fields setDays:(NSMutableDictionary*)Days setViews:(NSMutableDictionary*)Views setSubscribers:(NSMutableDictionary*)Subscribers setShares:(NSMutableDictionary*)Shares setWatchtime:(NSMutableDictionary*)Watchtime {
videoId = Id;
videoCategory = Category;
videoDate = date;
videoTitle = title;
videoDescription = description;
videoRelatedVideos = relatedVideos;
videoAccessControl = accessControl;
videoAdditionalInformation = additionalInformation;
videoFields = fields;
days = Days;
views = Views;
subscribers = Subscribers;
watchtime = Watchtime;
shares = Shares;
return self;
}
- (NSString*)extractId {
return self.videoId;
}
- (NSString*)extractCategory{
return self.videoCategory;
}
- (NSString*)extractTitle{
return self.videoTitle;
}
- (NSString*)extractDescription{
return self.videoDescription;
}
- (NSMutableArray*)extractRelatedVideos{
return self.videoRelatedVideos;
}
- (NSString*)extractAccessControlVideos{
return self.videoAccessControl;
}
- (NSDictionary*)extractAdditionalInformationVideos{
return self.videoAdditionalInformation;
}
- (NSArray*)extractFields{
return self.videoFields;
}
- (NSMutableDictionary*)extractDays{
return self.days;
}
- (NSMutableDictionary*)extractSubscribers{
return self.subscribers;
}
- (NSMutableDictionary*)extractWatchtime{
return self.watchtime;
}
- (NSMutableDictionary*)extractShares{
return self.shares;
}
- (NSMutableDictionary*)extractViews{
return self.views;
}
#end
I can extract any values from the singleton with:
SelectedRow *selectedRow = [SelectedRow sharedManager];
NSString *videoID = [selectedRow.rowValue extractId];
the problem arises with any NSMutableDictionary. If I try:
SelectedRow *selectedRow = [SelectedRow sharedManager];
NSMutableDictionary *days = [selectedRow.rowValue extractDays];
or with any other NSMutableDictionary I get this error:
[NSMutableDictionary count]: method sent to an uninitialized mutable dictionary object
what I'm I doing wrong? Thanks
The [NSMutableDictionary alloc] call allocates the space for NSMutableDictionary, but it does not initialize it.
Replace it with [NSMutableDictionary dictionary] to fix the problem. Same goes for your NSArray and NSMutableArray objects (replace them with [NSMutable array] and [NSMutableArray array]).
The videoAdditionalInformation of type NSDictionary should be initialized to nil, though, because NSDictionary objects are immutable. If you are planning to set it to some dictionary later on, you might as well keep it nil on initialization.
In addition, you should reconsider the use of copy: it makes sense for NSString objects, but it hardly makes sense on NSMutableDictionary objects.

Objective C assigning a dictionary to a variable and accessing it

I'm sorry to ask this question again, but I'm still stuck.
I have a city object trying to fetch weather from a weather fetcher object
#interface WeatherFetcher : NSObject {
}
#property (nonatomic, strong) NSMutableDictionary *weatherData;
- (void)fetchWeather:(NSString *)cityName;
- (void)handleNetworkErorr:(NSError *)error;
- (void)handleNetworkResponse:(NSData *)myData;
#end
This is were I assign the value to weatherData
#import "WeatherFetcher.h"
#implementation WeatherFetcher
- (void)fetchWeather:(NSString *)cityName
{
NSString *urlString = #"http://api.openweathermap.org/data/2.5/weather?q=";
urlString = [urlString stringByAppendingString:cityName];
urlString = [urlString stringByAppendingString:#",Aus"];
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (connectionError)
{
[self handleNetworkErorr:connectionError];
}
else
{
[self handleNetworkResponse:data];
}
}];
}
#pragma mark - Private Failure Methods
- (void)handleNetworkErorr:(NSError *)error
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Network Error" message:#"Please try again later" delegate:nil cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[alert show];
}
#pragma mark - Private Success Methods
- (void)handleNetworkResponse:(NSData *)myData
{
//NSMutableDictionary *data = [NSMutableDictionary dictionary];
NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
// now we'll parse our data using NSJSONSerialization
id myJSON = [NSJSONSerialization JSONObjectWithData:myData options:NSJSONReadingMutableContainers error:nil];
// typecast an array and list its contents
NSDictionary *jsonArray = (NSDictionary *)myJSON;
//NSLog([jsonArray description]);
// take a look at all elements in the array
for (id element in jsonArray) {
id key = [element description];
id innerArr = [jsonArray objectForKey:key];
NSDictionary *inner = (NSDictionary *)innerArr;
if ([inner conformsToProtocol:#protocol(NSFastEnumeration)]) {
for(id ele in inner) {
if ([ele conformsToProtocol:#protocol(NSFastEnumeration)]) {
NSDictionary *innerInner = (NSDictionary *)ele;
for(id eleEle in innerInner) {
id innerInnerKey = [eleEle description];
[data setObject:[[inner valueForKey:innerInnerKey] description] forKey:[eleEle description]];
}
}
else {
id innerKey = [ele description];
[data setObject:[[inner valueForKey:innerKey] description] forKey:[ele description]];
}
}
}
else {
[data setObject:[inner description] forKey:[element description]];
}
}
self.weatherData = data;
NSLog([self.weatherData description]) **//there is data**
}
#end
However every time I call this from by city object I get nothing back at all.
#import <Foundation/Foundation.h>
#import "WeatherFetcher.h"
#interface City : NSObject {
}
#property (nonatomic, strong) NSString *cityName;
#property (nonatomic, strong) NSString *stateName;
#property (nonatomic, strong) UIImage *cityPicture;
#property (nonatomic, strong) NSString *weather;
#property (nonatomic, strong) NSMutableDictionary *weatherData;
-(NSString *)getWeather;
#end
UI calls getWeather by a button press to get the string value to be displayed on screen
#implementation City {
}
-(NSString *)getWeather {
//return self.weather;
NSString *info = #"";
WeatherFetcher *weatherFetcher = [[WeatherFetcher alloc] init];
[weatherFetcher fetchWeather:self.cityName];
self.weatherData = [weatherFetcher weatherData];
for (id element in self.weatherData) {
info = [info stringByAppendingString:[element description]];
info = [info stringByAppendingString:#"-->"];
info = [info stringByAppendingString:[self.weatherData valueForKey:[element description]]];
info = [info stringByAppendingString:#"\n"];
}
return info;
}
#end
What am I doing wrong here?
getWeather method in the city class gets called when a button is pressed and I'm trying to display this string in a text area. I don't have much experience with Objective C and this is my first app other than Hello World app.
Thank you!
Your WeatherFetcher is asynchronous (sendAsynchronousRequest:) - it sets a task to obtain the data and then returns (usually) before that data has been obtained. So when you try to access the weatherData immediately after the call to fetchWeather: it is not there yet.
You need to redesign your model to handle asynchronicity - getWeather cannot be synchronous. For example you could make fetchWeather: take a completion block to invoke when the data is available and have getWeather pass in a suitable block.

Issue with save file to Documents NSMutableArray with NSObjects

I'm a newbie iOS developer.
I wrote a small application that save an NSMutableArray array with my objects that derived from NSObject.
Application do the save but the file isn't created in document directory and application can't read.
this issue is both on the simulator and my iPhone 3gs 4.2.1
My NSMutableArray definition inside the appDelegate class:
#property (nonatomic,retain, readwrite) NSMutableArray *places;
My NSObject class:
#import <Foundation/Foundation.h>
#interface Place : NSObject {
NSString *name;
NSString *location;
}
-(id) init:(NSString *)name: (NSString *)location;
#property (retain,nonatomic,readwrite) NSString *name;
#property (retain,nonatomic,readwrite) NSString *location;
#end
My StorageService library class:
#import "StorageService.h"
#implementation StorageService
-(id) init {
self = [super init];
if (self != nil) {
}
return self;
}
-(void) saveArrayToFile:(NSString*) filename : (NSMutableArray *)arrayToSave{
// get full path
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *fullPath = [paths objectAtIndex:0];
fullPath = [fullPath stringByAppendingPathComponent:filename];
NSLog(#"Save in %#",fullPath);
[arrayToSave writeToFile:fullPath atomically:YES];
}
-(NSMutableArray*) readArrayFromFile:(NSString *)filename {
// get full path
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *fullPath = [paths objectAtIndex:0];
fullPath = [fullPath stringByAppendingPathComponent:filename];
if ([[NSFileManager defaultManager] fileExistsAtPath:fullPath]) {
NSMutableArray *data = [[NSMutableArray alloc] initWithContentsOfFile:fullPath];
if (data == nil) {
data = [[NSMutableArray alloc] init];
}
NSLog(#"Read from %#",fullPath);
return data;
} else {
NSMutableArray *data = [[NSMutableArray alloc] initWithContentsOfFile:fullPath];
return data;
}
}
-(void) dealloc {
[super dealloc];
}
#end
and My functions in the appDelegate:
-(void) saveApplicationData {
[self.storageService saveArrayToFile : PLACES_FILE : self.places];
}
-(void) loadApplicationData {
self.places = [self.storageService readArrayFromFile:PLACES_FILE];
}
Here is my class that holds constant to filename:
#import <Foundation/Foundation.h>
extern NSString * const PLACES_FILE = #"Places.dat";
#interface ApplicationConstants : NSObject {
}
#end
So what is wrong?
Thank you guys.
What you want is to let Place conform to the NSCoding protocol, to allow for serialization to and from files (and in memory data if wanted)
Extend Place as (I have also changed the name of the init method as your name was against every naming practice iOS has):
#interface Place : NSObject <NSCoding> {
NSString *name;
NSString *location;
}
-(id)initWithName:(NSString *)name location:(NSString *)location;
#property (retain,nonatomic,readwrite) NSString *name;
#property (retain,nonatomic,readwrite) NSString *location;
#end
Your implementation is quite simple but you also need to implement two methods defined by the NSCoding protocol:
#implementation Place
#synthesize name, location;
-(id)initWithName:(NSString *)aName location:(NSString *)aLocation {
self = [super init];
if (self) {
self.name = aName;
self.location = aLocation;
}
return self;
}
-(id)initWithWithCoder:(NSCoder)decoder {
self = [super initWithCoder:decoder];
if (self) {
self.name = [decoder decodeObjectForKey:#"name"];
self.location = [decoder decodeObjectForKey:#"location";
}
return self;
}
-(void)encodeWithCoder:(NSCoder*)encoder {
[encoder encodeObject:self.name forKey:#"name"];
[encoder encodeObject:self.location forKey:#"location"];
[super encodeWithCoder:encoder];
}
#end
With this in place, saving the places array to disk is as easy as:
[NSKeyedArchiver archiveRootObject:places toFile:path];
And decoding just as easy:
places = [[KSKeyUnarchiver unarchiveObjectWithFile:path] retain];
To use writeToFile objects in array need to be plist capable type (NSDate, NSDate, NSString, NSArray, NSDictionary)
Implement NSCoding on the objects in array and use NSKeyedArchiver to serialize/deserialize.
write:
[NSKeyedArchiver archiveRootObject:myArray toFile:self.places];
read:
[NSKeyedUnarchiver unarchiveObjectWithFile:path];
More info can be found here:
Persisting Custom Objects