Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.
You can't. If your code causes a crash the OS just kills you. It won't risk your misbehaving code running on. So the best solution is don't crash. Being serious if you know your code can crash work out where and why and fix it. Don't ever ship code with known crashing bugs.

Also don't write garbage into the cache. Check the quality of what you're writing before you write it!

Personally I'd want the cache to persist across application runs: it'll make the application seem faster and better over time.

Of course, I would never send up a code with known problems and my app does not crash if I do not create errors in my code to provoke crashes. I just wanted to be sure that if some problem occurs later on which I havent thought about, the app will behave allright... Perhaps that is over kill...?
I check errors and handle them through the app and that might be enough?

I also agree on the cache and the app being faster. The problem is that I do not have any chance to know when and what in the data being updated. It is done by someone else and an img could (of course it wont be like that irl) be updated once a year or onci in a minute.. This forces me to start with an empty cache everytime the app is running. Timestamps does not help me in this case. But caching the img during the app run is atleast better as letting the user wait for the download of each and every img on app-start or to get the img everytime the user would like to look at it...

MACloop
 
I'm not sure why the timestamps don't help but you could work around this: timestamp the time you download the image and re-download it (when it's requested) after a certain amount of time (say an hour).
 
I'm not sure why the timestamps don't help but you could work around this: timestamp the time you download the image and re-download it (when it's requested) after a certain amount of time (say an hour).

Well, I have to think about that... I am sure there is always some work around and it makes sense...hmm... thanks for the advice!
MACloop
 
Hello again,
regarding the dataAccess and how data actually lives in the app... I have discouvered something in my app which I do not understand...

I initially get all the data in the app delegate class and I have chosen to save the data into arrays in the app delegate. In the app different views use this data in different ways and I call the arrays with the following code:
Code:
theListInTheClassUsingIt = [(AppDelegate*)[[UIApplication sharedApplication] delegate] theList];

I thought this makes the local variable in the class using the data, kind of copying the content of the array from the appDelegate array. My idea was to get the data and to use it and perhaps even delete it in the specific view using it. I was suprised though, when the following code deleted the content of the array in the appDelegate aswell?

Code:
if([theListInTheClassUsingIt count] > 0) {
	[theListInTheClassUsingIt removeAllObjects];
}

It seems like the assignment I did to the local variable (the array in this case) is a pointer to the original object in the appDelegate? Should I define a copy of the original array in order to isolate the actions on the data in the class using the data? I only want the appDelegate to hold the data as it has been parsed and the use it in different ways later on in the app flow....
I have retained the theListInTheClassUsingIt and also tried to use self.theListInTheClassUsingIt but it did not make any difference...

MACloop
 
Erm, I have no idea why you thought the variable contents would be copied in this case. They never are. You basically have two options:

1) Write your own accessor (instead of synthesizing it) in your app delegate that returns an auto-released copy.

2) Copy it when the classes get it. If you are setting a property in the object that gets the data then you can synthesize a copy accessor that will do this for you.
 
Erm, I have no idea why you thought the variable contents would be copied in this case. They never are. You basically have two options:

1) Write your own accessor (instead of synthesizing it) in your app delegate that returns an auto-released copy.

2) Copy it when the classes get it. If you are setting a property in the object that gets the data then you can synthesize a copy accessor that will do this for you.

Me to - I have no idea why I thought that and til now it worked out, because I have not been working with apps where I use data this way. Thanks for the advice! I will read more about it. I am reading this again to look if I did missunderstand somthing...

Thanks!
MACloop
 
If I were you I'd try and pass immutable (NSArray instead of NSMutableArray) objects out of your data model. That way if the controller layer wants to alter it it has to create a mutable copy.
 
If I were you I'd try and pass immutable (NSArray instead of NSMutableArray) objects out of your data model. That way if the controller layer wants to alter it it has to create a mutable copy.

Ah, that is clever! I think I'll try that out...why didn't I think of that before :eek:
 
If I were you I'd try and pass immutable (NSArray instead of NSMutableArray) objects out of your data model. That way if the controller layer wants to alter it it has to create a mutable copy.

Works! Thanks alot! Sometimes it is simpler than you think... :eek:
 
regadring my struggle with the img dowload. It works fine for now, BUT one problem came up. If the image is not available on an url address, I have tried to look if the data received == nil. The problem is - the data does never seem to be nil... I am always getting some bytes delivered. If I try to create an img with those bytes, the app crashes. So the question is:
How do I do this properly? And where in the code (the code as in the posts above) is it ok to do this?
Please help!
MACloop
 
regadring my struggle with the img dowload. It works fine for now, BUT one problem came up. If the image is not available on an url address, I have tried to look if the data received == nil. The problem is - the data does never seem to be nil... I am always getting some bytes delivered. If I try to create an img with those bytes, the app crashes. So the question is:
How do I do this properly? And where in the code (the code as in the posts above) is it ok to do this?
Please help!
MACloop

The problem, as I assume, might be that the server returns a 404 error msg when I try to reach for a non excisting URL. This is data, but not the data i want to use for my image and I assume that when I use this data for creating an UIImage, the app crashes? Should I look for error msg when building the connection?

Thanks in advance for any advice!
MACloop
 
Also you should not crash if you can't turn it into an image anyway: you should be checking if the UIImage is nil. This could easily happen if the image can't be understood for any reason (corrupt data transfer for example).
 
I thought of that (see my post below) and I will try to detect the error msg from the server.

You can read the status code from the NSHTTPURLResponse:

Code:
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
	
if ([response statusCode] != 200)
{
	// Error-handling code here.
}
 
Also you should not crash if you can't turn it into an image anyway: you should be checking if the UIImage is nil. This could easily happen if the image can't be understood for any reason (corrupt data transfer for example).

I am checking if the img is nil, but the crash appeared when creating the UIImage with data, not possible to use for that. Now I look if the response fromt he server is an error message and that prevents the app to crash if an url is not available anymore, for some reason.

Another question: what format of an url is allowed? The url i get has the following form http://www.something.com/_somedirectory/something/Images/theImg.jpg. Is it a problem to have _ and/or big letters in the url? I use the url as name for the images and when comparing the names if a image already been dowloaded, this seems to be a problem. When I use the [[url path]lastPathComponent] all is fine. The problem is that I cannot control what the images names are and situation may appear where two images have the same name but are palced into different directories.

Any ideas?
MACloop
 
You can read the status code from the NSHTTPURLResponse:

Code:
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
	
if ([response statusCode] != 200)
{
	// Error-handling code here.
}

Thanks for your comment! I have implemented a solution using this and it works just fine!
MACloop
 
URLs are described by RFC 1738.

The case-sensitiveness of URLs is, in practice, regardless of what the RFC says, server-specific. Some servers will treat URLs as case sensitive (so http://example.xxx/Image.jpg will return an image and http://example.xxx/image.jpg will not), others will treat the URL as case-insensitive.

Thanks for the answer!
hmmm... that would rather indicate that my assumption is wrong. What could be the reason for my comparison problem? I use NSLog in order to see what values sent into my functions and it shows exactly the same value for the two image-names compared. But the image is dowloaded even if the images with the same name already is in the cache....

This is how I do it:
Code:
- (void) getCachedImage: (NSString *) ImageURLString{
	
	NSFileManager *fileManager = [NSFileManager defaultManager];
	NSString *newDirPath = [[NSString alloc]initWithString:[NSHomeDirectory() stringByAppendingPathComponent:@"Documents/imageCache"]];
	NSURL *u = [NSURL URLWithString:ImageURLString];
	NSString *fileName = [[NSString alloc]initWithString:[u path]];
	NSString *filePath = [[NSString alloc]initWithString:[newDirPath stringByAppendingPathComponent:fileName]];
	NSString *plist_filePath = [[NSString alloc]initWithString:[newDirPath stringByAppendingPathComponent:@"ImgCache"]];
	
	NSLog(@"getCachedImage >>>> fileName %@",fileName);
	NSLog(@"getCachedImage >>>> filePath %@",filePath);
	
	if ([[NSFileManager defaultManager] fileExistsAtPath:newDirPath] == NO){//look if dir excists
		//if NO -> create dir
		BOOL success = [fileManager createDirectoryAtPath:newDirPath attributes:nil];//creating directory
		if(success == NO){//creating directory failed
			NSString *errStr = [[NSString alloc]initWithString:[NSString stringWithFormat:@"Failed to load image"]];
			[delegate handleError:errStr];
			[errStr release];
		}
	}
	if ([[NSFileManager defaultManager] fileExistsAtPath:newDirPath] == YES){//dir excists
		//if not - look up the size of the dict and add img
		if([[NSFileManager defaultManager] fileExistsAtPath:filePath] == NO){//File does not excist
[COLOR="Red"]THIS IS ALWAYS TRUE!!!![/COLOR]
			//if it does not excists - create it and cahce the img
			//NSLog(@"Directory excists");
			NSLog(@"file does not excist");
			NSLog(@"Downloading img and puts it into the cache");
			[self cacheImage: ImageURLString];//downloading and caching a new img
		}
else if([[NSFileManager defaultManager] fileExistsAtPath:filePath] == YES){//File does excist
[COLOR="Red"]NEVER HAPPENDS!!![/COLOR]
			NSLog(@"file does excist");
			NSData *d = [[NSData alloc]initWithData:[fileManager contentsAtPath:filePath]];//getting data saved at specific path
			UIImage *i = [[UIImage alloc]initWithData:d];//creating img from data
			[d release];
			
			//ie get the data and save into local img object
			//if the delegate has didFinishDownloadImg method
			if ([delegate respondsToSelector:@selector(didFinishDownloadImg:)]){ //the delegate has the method defined
				[self.delegate performSelector:@selector(didFinishDownloadImg:) withObject:i];//send the img to the delegate aka the detailed view
			}
			[i release];
...

The print from NSLog is:
Code:
//the first time an images is opened in the app
//looking if it is in cache using name and path as follows
fileName /web/_bilder/unt/Something/img.jpg
filePath /var/mobile/Applications/xxxxx/Documents/imageCache/web/_bilder/unt/Something/img.jpg

//file does not excist
//Downloading img and puts it into the cache using name and path as follows

fileName /web/_bilder/unt/Something/img.jpg
filePath /var/mobile/Applications/xxxxx/Documents/imageCache/web/_bilder/unt/Something/img.jpg

//Opening the same images again
fileName /web/_bilder/unt/Something/img.jpg
filePath /var/mobile/Applications/xxxxx/Documents/imageCache/web/_bilder/unt/Something/img.jpg

// file does not excist
// Downloading img and puts it into the cache
fileName /web/_bilder/unt/Something/img.jpg
filePath /var/mobile/Applications/xxxxx/Documents/imageCache/web/_bilder/unt/Something/img.jpg

I do not understand why this does not work... :-( and why it seem to work when I use
Code:
NSString *fileName = [[NSString alloc]initWithString:[[u path] lastPathComponent]]

help....

MACloop
 
Your file path is "/var/mobile/Applications/xxxxx/Documents/imageCache/web/_bilder/unt/Something/img.jpg". I can see you create the directory "/var/mobile/Applications/xxxxx/Documents/imageCache" if required. What about all the other directories (web, web/_bilder, etc)? I don't see you create those and none of the API methods that I am aware of to save files will create them if they don't exist: in this case the file save will fail. I'm pretty sure that is well documented.

Can you post the code where you save the image?
 
Your file path is "/var/mobile/Applications/xxxxx/Documents/imageCache/web/_bilder/unt/Something/img.jpg". I can see you create the directory "/var/mobile/Applications/xxxxx/Documents/imageCache" if required. What about all the other directories (web, web/_bilder, etc)? I don't see you create those and none of the API methods that I am aware of to save files will create them if they don't exist: in this case the file save will fail. I'm pretty sure that is well documented.

Can you post the code where you save the image?

ok, the name is "/web/_bilder/unt/Something/img.jpg" and as you write this is most likley the problem... I intend to save an image named /web/_bilder/unt/Something/img.jpg into the /var/mobile/Applications/xxxxx/Documents/imageCache and that is seen as a path lateron in the code...

I save the image like this:
Code:
NSString *plist_filePath = [newDirPath stringByAppendingPathComponent:@"ImgCache"];//plist path
		
		if([[NSFileManager defaultManager] fileExistsAtPath:filePath] == NO){//current img does not excist
			//NSLog(@"img does not excist");
			BOOL success = [fileManager createFileAtPath:filePath contents:d attributes:nil];//create img file
			if(success == YES){//the img was successfully created
				//NSLog(@"the img was successfully created");
				if([[NSFileManager defaultManager] fileExistsAtPath:plist_filePath] == NO){//no plist excists
					//plist has to be created
					[fileManager createFileAtPath:plist_filePath contents:d attributes:nil];//create plist file
				}	
				NSArray *array = [[NSArray alloc] initWithContentsOfFile:plist_filePath];//saving content from plist into local array
				NSMutableArray *cacheArray = [[NSMutableArray alloc] init];//creating local cache array for sorting
				for (NSMutableDictionary *element in array) {//every element in the plist array is set into the cache array
					[cacheArray addObject:element];
				}
				[array release];
			
				NSMutableDictionary *imgDict = [NSMutableDictionary dictionaryWithObject:fileName forKey:@"Name"];//new dict with the fileName and key=Name
				//getting access to the default attributes for the current dictionary - we want to use the attribute fileSize
				NSDictionary *fileDictionary = [[NSFileManager defaultManager] fileAttributesAtPath:[newDirPath stringByAppendingPathComponent:fileName] traverseLink:YES];
				[imgDict setValue:[NSNumber numberWithLong:[fileDictionary fileSize]] forKey:@"Size"];//saving the current filesize value to the key=size for the current dictionary
				[cacheArray addObject:imgDict];//add object to cacheArray
				[cacheArray writeToFile:plist_filePath atomically:YES];//write cacheArray to file
				[cacheArray removeAllObjects];
				[cacheArray release];
			}
		}
		[newDirPath release];
		[fileName release];
		[filePath release];
 
You should be checking the return value of writeToFile:atomically: (you should be checking the return value of any call that can fail). I expect it's returning NO. It is your responsibility to create all the directories up to the file you want to create.
 
You should be checking the return value of writeToFile:atomically: (you should be checking the return value of any call that can fail). I expect it's returning NO. It is your responsibility to create all the directories up to the file you want to create.

So I have to create the same tree on the iPhone as on the server . I thought that would be a bit ineffective... How would it be to send an id with the url like blabla.jpg#theIdFromTheDatabase to give the images different names? and then only set the name to be the last compontnt of the url?

MACloop
 
So I have to create the same tree on the iPhone as on the server . I thought that would be a bit ineffective... How would it be to send an id with the url like blabla.jpg#theIdFromTheDatabase to give the images different names? and then only set the name to be the last compontnt of the url?

MACloop

Are you familiar with the concept of hashing? As I would suggest you hash the entire URL and save as the hash value. NSString implements a hash function for you. There is some worry on hash collision though.

Your other option would be to simply replace the / components of the URL with another character that does not have meaning to the filesystem. So say replace every instance of / with _ to produce the filename to save as.
 
Are you familiar with the concept of hashing? As I would suggest you hash the entire URL and save as the hash value. NSString implements a hash function for you. There is some worry on hash collision though.

Your other option would be to simply replace the / components of the URL with another character that does not have meaning to the filesystem. So say replace every instance of / with _ to produce the filename to save as.

Thanks for the advice! I think both sounds interesting. If the hashing one is a bit buggy (if I understand you correctly) I think I will give the replace-part-of-url solution a chance!
Thanks alot!
MACloop
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.