Writing and reading text files on the iPhone - iphone

As a practice, I am trying to write an app similar to the built-in notes app.
But I cannot figure out how to save the file and display it in a UITableView.
Right now, I have a UITextView that the user can type in. I also have a save button.
When the user taps the save button, I want to save it, and later have it displayed in a table view.
I am very lost so if you know of any relevant tutorials etc. it would be greatly appreciated.

As noted by the commenters in the real world, you're definitely going to want to look at Core Data or some other data persistence strategy. If you're dead set on pursuing this as a learning experience, something like this should solve your problem:
- (void)writeStringToFile:(NSString*)aString {
// Build the path, and create if needed.
NSString* filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* fileName = #"myTextFile.txt";
NSString* fileAtPath = [filePath stringByAppendingPathComponent:fileName];
if (![[NSFileManager defaultManager] fileExistsAtPath:fileAtPath]) {
[[NSFileManager defaultManager] createFileAtPath:fileAtPath contents:nil attributes:nil];
}
// The main act...
[[aString dataUsingEncoding:NSUTF8StringEncoding] writeToFile:fileAtPath atomically:NO];
}
- (NSString*)readStringFromFile {
// Build the path...
NSString* filePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* fileName = #"myTextFile.txt";
NSString* fileAtPath = [filePath stringByAppendingPathComponent:fileName];
// The main act...
return [[[NSString alloc] initWithData:[NSData dataWithContentsOfFile:fileAtPath] encoding:NSUTF8StringEncoding] autorelease];
}

The easiest way to save text is using NSUserDefaults.
[[NSUserDefaults standardUserDefaults] setObject:theText forKey:#"SavedTextKey"];
or, if you want to have the user name each "file" or be able to have multiple files
NSMutableDictionary *saveTextDict = [[[[NSUserDefaults standardUserDefaults] objectForKey:#"SavedTextKey"] mutableCopy] autorelease];
if (saveTextDict == nil) {
saveTextDict = [NSMutableDictionary dictionary];
}
[saveTextDict setObject:theText forKey:fileName];
[[NSUserDefaults standardUserDefaults] setObject:saveTextDict forKey:#SavedTextKey"];

Related

Data persistance tableView issue iPhone(reading and writing to plist)

In my application I want to implement a simple Alarm function. I know how to use UILocalNotifications, but I came across this source code with a like UI of the iPhone's native Clock app alarm area as well as it having a believe a type of data persistence. Two things I am not good at interface design and data persistence this source code has. But I downloaded it and started playing around with it to find the alarms are not persistent.
Download
Does anyone know how the source code can be adjusted so that it is persistent and the plist can be saved and read to and from? I am open to learning too, this area is somewhat unknown to me too. Thanks
I review your code and find issue that you not moved your "Alarms.plist" file form resource to document directory. we are not able to edit file which is in resource folder. so write following code in app delegate file.
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *theFileName = #"Alarms.plist"; //Change this appropriately
NSString *oldPath = [[NSBundle mainBundle] pathForResource:#"Alarms" ofType:#"plist"];//[NSString stringWithFormat:#"%#/Inbox/%#", documentsDirectory, theFileName];
NSString *newPath = [NSString stringWithFormat:#"%#/%#", documentsDirectory, theFileName];
if (![[NSFileManager defaultManager] fileExistsAtPath:newPath])
[[NSFileManager defaultManager] moveItemAtPath:oldPath toPath:newPath error:nil];
Perform save operation on file which is in Document directory folder.
try this code... to save plist from bundle to Document Directory
Notice that you will have "Unable to read... " just at the first app launch
- (NSMutableArray *)displayedObjects
{
if (_displayedObjects == nil)
{
NSString *path = [[self class] pathForDocumentWithName:#"Alarms.plist"];
NSArray *alarmDicts = [NSMutableArray arrayWithContentsOfFile:path];
if (alarmDicts == nil)
{
NSLog(#"Unable to read plist file: %#", path);
NSLog(#"copy Alarms.plist to: %#", path);
NSString *pathToSetingbundle = [[NSBundle mainBundle] pathForResource:#"Alarms" ofType:#"plist"];
[[NSFileManager defaultManager]copyItemAtPath:pathToSetingbundle toPath:path error:nil];
}
_displayedObjects = [[NSMutableArray alloc]
initWithCapacity:[alarmDicts count]];
for (NSDictionary *currDict in alarmDicts)
{
Alarm *alarm = [[Alarm alloc] initWithDictionary:currDict];
[_displayedObjects addObject:alarm];
NSLog(#"#disply obj %#", alarm);
}
}
return _displayedObjects;
}

How to use pList in iOS Programming

Recently, I'm a senior in high school, and I'm interested in making apps for iPhone. Recently, one of my apps came out: NBlock. It's a puzzle app and it's very challenging. However, it has a few problems. The high scores are not saved. I've been told to use a plist. Any tips?
The URL based method for this:
// Get the URL for the document directory
NSFileManager *fileManager = [[NSFileManager alloc] init];
NSURL *documentDirectoryURL = [[fileManager URLsForDocumentDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] objectAtIndex:0];
// Turn the filename into a string safe for use in a URL
NSString *safeString = [#"scores.plist" stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
// Create an array for the score
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:[NSNumber numberWithInt:score]];
// Write this array to a URL
NSURL *arrayURL = [NSURL URLWithString:safeString relativeToURL:documentDirectoryURL];
[array writeToURL:arrayURL atomically:YES];
I'd avoid using a plist. The easiest way to save simple data in an application, by far, is NSUserDefaults.
Check out this tutorial for a simple guide on how to use NSUserDefaults. Always be sure to synchronize NSUserDefaults when you're done writing to them.
If you're looking for a more powerful (but more complex) way to save data, check out Apple's guide to using Core Data.
Heres what you want:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"scores.plist"];
NSMutableArray *array = [[NSMutableArray alloc] init];      
[array addObject:[NSNumber numberWithInt:score]];        
[array writeToFile:path atomically:YES];
And to add new scores do initWithContentsOfFile:#"scores.plist" instead of init in the declaration of array. You can optionally use NSUserDefaults.
Take a look into NSKeyedArchiver/Unarchiver. You can save pretty much anything you want; NSUserDefaults, in my experience, dumps your data if you kill your app from the tray. Core data is really used better if you're managing large amounts of data with databases such as sqlite.
I would say the below code will work and pretty straight forward unless custom object data types(Its a different story again) are used:
NSString* plistPath = nil;
NSFileManager* manager = [NSFileManager defaultManager];
if ((plistPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"PathTo.plist"]))
{
if ([manager isWritableFileAtPath:plistPath])
{
NSMutableDictionary* infoDict = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath];
[infoDict setObject:#"foo object" forKey:#"fookey"];
[infoDict writeToFile:plistPath atomically:NO];
[manager setAttributes:[NSDictionary dictionaryWithObject:[NSDate date] forKey:NSFileModificationDate] ofItemAtPath:[[NSBundle mainBundle] bundlePath] error:nil];
}
}
setting the date attribute might be helpful to check when is the last time score was updated.

Troubles with NSString writeToFile

I am using the following code to open a file's contents and save it to another file.
when it runs the original file length is 793 but the saved file is 0. I have also tried just to copy the file. Nothing seems to work.
Is there some kind of permissions I'm missing on the documents directory?
NSError *error;
NSString *basePath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString* nGram = [basePath stringByAppendingPathComponent:#"contacts.gram"];
NSString *oGram = [basePath stringByAppendingPathComponent:#"/../vText.app/model/lm/TAR9230/contacts.gram"];
NSString *gramString = [[NSString alloc] initWithContentsOfFile:oGram encoding:NSUTF8StringEncoding error:&error];
BOOL ok = [gramString writeToFile:nGram atomically:NO encoding:NSUnicodeStringEncoding error:&error];
if(!ok) NSLog(#"Mayday!");
NSLog(#"%d",[gramString length]);
gramString = [[NSString alloc] initWithContentsOfFile:nGram encoding:NSUTF8StringEncoding error:&error];
NSLog(#"%d",[gramString length]);
This entire block is unnecessary. All you need is:
NSString *fp=[[NSBundle mainBundle] pathForResource:#"contacts" ofType:#"gram"];
NSString *gramString = [[NSString alloc] initWithContentsOfFile:fp
encoding:NSUTF8StringEncoding
error:&error];
You certainly don't want to try to directly access a file in the app bundle using a hardcoded path because the file isn't guaranteed to be in the same exact place in every build.
In the code you do have, you want to use the same encoding constant for reading as you did for writing. You write with NSUnicodeStringEncoding but you read with NSUTF8StringEncoding. These should overlap but why take the chance if you know the exact coding used?

Loading a web page in iphone and check history

Pseudo code
pageUrl = "http://www.google.com"
if(pageUrl == never Downloaded)
Download pageUrl
else
{
Display Saved Version
Mean while download pageUrl
When done display new version
}
How can I do something like this in objective C for a UIWebview?
Also what's the best way to save web pages for this scenario? PList, SQLite?
Plist is the best way to solve your problem.
iPhone/Objective-c can access very quickly to plist file as compare to SQLite Database.
Let me give you some sample code.
See - edit After some time.
Edit :
Steps for Creating project & connecting web-view
Create New Project -> View Based Application.
Give name "yourProjName" ( up to you what you give )
Open "yourProjNameViewController.xib"
Drag & drop UIWebView
Open "yourProjNameViewController.h" File
Place a variable IBOutlet UIWebView *wView;
Connect in interface builder
Steps for adding Property list file to your project
Expand Resources Group under your project tree
Right click on "Resources" -> Add -> New File
Select Template Category - osx -> Resource
Select Property List file
Give file name "LoadedURL.plist"
Change Root type to Array
Save "LoadedURL.plist" file
Now place following code to "yourProjNameViewController.m" file.
#import "yourProjNameViewController.h"
#define documentsDirectory_Statement NSString *documentsDirectory; \
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); \
documentsDirectory = [paths objectAtIndex:0];
#implementation WebViewLoadViewController
- (void)viewDidLoad {
[super viewDidLoad];
// your url to load
NSString *strToLoad=#"http://www.mail.yahoo.com";
// file management code
// copy file to documents directory
documentsDirectory_Statement;
NSFileManager *fm=[NSFileManager defaultManager];
if(![fm fileExistsAtPath:[documentsDirectory stringByAppendingPathComponent:#"LoadedURL.plist"]]){
[fm copyItemAtPath:[[NSBundle mainBundle] pathForResource:#"LoadedURL" ofType:#"plist"]
toPath:[documentsDirectory stringByAppendingPathComponent:#"LoadedURL.plist"]
error:nil];
}
// array from doc-dir file
NSMutableArray *ar=[NSMutableArray arrayWithContentsOfFile:[documentsDirectory stringByAppendingPathComponent:#"LoadedURL.plist"]];
// check weather file has url data or not.
BOOL fileLocallyAvailable=NO;
NSString *strLocalFileName=nil;
NSUInteger indexOfObject=0;
if([ar count]>0){
for (NSDictionary *d in ar) {
if([[d valueForKey:#"URL"] isEqualToString:strToLoad]){
fileLocallyAvailable=YES;
strLocalFileName=[d valueForKey:#"FileName"];
break;
}
indexOfObject++;
}
}
if(fileLocallyAvailable){
NSDictionary *d=[ar objectAtIndex:indexOfObject];
strLocalFileName=[d valueForKey:#"FileName"];
} else {
NSMutableDictionary *d=[NSMutableDictionary dictionary];
[d setValue:strToLoad forKey:#"URL"];
NSString *str=[[NSString alloc] initWithContentsOfURL:[NSURL URLWithString:strToLoad]];
[str writeToFile:[documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%02i.htm",[ar count]]]
atomically:YES
encoding:NSUTF8StringEncoding error:nil];
strLocalFileName=[NSString stringWithFormat:#"%02i.htm",[ar count]];
[d setValue:[NSString stringWithFormat:#"%02i.htm",[ar count]] forKey:#"FileName"];
[ar addObject:d];
[ar writeToFile:[documentsDirectory stringByAppendingPathComponent:#"LoadedURL.plist"]
atomically:YES];
}
NSURL *u=[[NSURL alloc] initFileURLWithPath:[documentsDirectory stringByAppendingPathComponent:strLocalFileName]];
NSURLRequest *re=[NSURLRequest requestWithURL:u];
[wView loadRequest:re];
[u release];
}
`

iPhone: Can access files in documents directory in Simulator, but not device

I'm writing an app that copies some contents of the bundle into the applications Document's directory, mainly images and media. I then access this media throughout the app from the Document's directory.
This works totally fine in the Simulator, but not on the device. The assets just come up as null. I've done NSLog's and the paths to the files look correct, and I've confirmed that the files exist in the directory by dumping a file listing in the console.
Any ideas? Thank you!
EDIT
Here's the code that copies to the Document's directory
NSString *pathToPublicationDirectory = [NSString stringWithFormat:#"install/%d",[[[manifest objectAtIndex:i] valueForKey:#"publicationID"] intValue]];
NSString *manifestPath = [[NSBundle mainBundle] pathForResource:#"content" ofType:#"xml" inDirectory:pathToPublicationDirectory];
[self parsePublicationAt:manifestPath];
// Get actual bundle path to publication folder
NSString *bundlePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:pathToPublicationDirectory];
// Then build the destination path
NSString *destinationPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%d", [[[manifest objectAtIndex:i] valueForKey:#"publicationID"] intValue]]];
NSError *error = nil;
// If it already exists in the documents directory, delete it
if ([fileManager fileExistsAtPath:destinationPath]) {
[fileManager removeItemAtPath:destinationPath error:&error];
}
// Copy publication folder to documents directory
[fileManager copyItemAtPath:bundlePath toPath:destinationPath error:&error];
I am figuring out the path to the docs directory with this method:
- (NSString *)applicationDocumentsDirectory {
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
}
And here's an example of how I'm building a path to an image
path = [NSString stringWithFormat:#"%#/%d/%#", [self applicationDocumentsDirectory], [[thisItem valueForKey:#"publicationID"] intValue], [thisItem valueForKey:#"coverImage"]];
It turned out to be an issue where the bundle was not being updated on the device and apparently didn't have the same set of files that the Simulator had. This blog post helped a bit: http://majicjungle.com/blog/?p=123
Basically, I cleaned the build and delete the app and installed directly to the device first instead of the simulator. Interesting stuff.
I don't see where documentsDirectory is defined.
NSString *destinationPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"%d", [[[manifest objectAtIndex:i] valueForKey:#"publicationID"] intValue]]];
Perhaps the following will do the trick
NSString *destinationPath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent:[NSString stringWithFormat:#"%d", [[[manifest objectAtIndex:i] valueForKey:#"publicationID"] intValue]]];
Your copy code looks fine, and you say you're getting no errors.
But I'm intrigued by "The assets just come up as null." Are you sure you're accessing the file name later with the exact same name?
Unlike the simulator, a real iPhone has a case sensitive file system.