How to append NSMutable array to existing file - objective-c

I am newbie in coding and trying to learn objective c, I tried searching my question on internet and stack overflow but not getting proper solution.
I am trying to save some data into array and after that I want to save that subarray into main array and after that main array into file.
below code is working properly for me but the data is not appending in file .
self.details = [[NSMutableArray alloc]init];
[self.details insertObject:self.firstName atIndex:0];
[self.details insertObject:self.lastName atIndex:1];
[self.details insertObject:[NSNumber numberWithLong:self.depositAmount] atIndex:2];
[self.details insertObject:[NSNumber numberWithInt:self.accountNumber] atIndex:3];
for (int i=0; i<self.details.count;i++) {
NSLog(#"%#",self.details[i]);
}
self.mainarray = [[NSMutableArray alloc]init];
[self.mainarray addObjectsFromArray:self.details];
NSString *path = #"/Users/testapp/data";
[self.mainarray writeToFile:path atomically:YES];
enter code here
for (int i=0; i<self.mainarray.count;i++) {
NSLog(#"%#",self.mainarray[i]);
}

writeToFile:path is actually deprecated (as you can see here https://developer.apple.com/documentation/foundation/nsdata/1408033-writetofile?language=objc)
In general to do this it depends on what you mean by append? If its literally just dump the data to end ( not necessarily making the file a legit array) then you can open a file in append mode, which you could use an output stream for (https://developer.apple.com/documentation/foundation/nsoutputstream/1564841-outputstreamtofileatpath?language=objc)
However, I am figuring that you want to append the new array objects to the file such that the file is structred. To do that you would need to read in the file and somehow decode that into an array. You would need a way to represent your data such as with JSON (https://developer.apple.com/documentation/foundation/nsjsonserialization) so that you can save the array into the file and vice versa.
But then you would need to read the file, and then add your new structred data so the file would still make sense. There are lots of ways to handle files (NSCoding, C files, etc..) but a simple way would to use NSOutputStream(https://developer.apple.com/documentation/foundation/nsoutputstream) and NSInputStream (https://developer.apple.com/documentation/foundation/nsinputstream?language=objc)

Related

Storing an array of CGLayer to file

I'm working on an OSX application where I need to store 446 CGLayers that get placed in a PDF context and am wondering if there's a way to write and read them from a file, rather than generating them when the application loads
I've read that CGLayer is no longer recommended, but I feel they really fit what I need. Also, if I use bitmapGraphicsContexts, they can pixelate when zooming in.
I am able to store them in NSArrays, both by storing them in NSValue and puting them into the array by bridging. I've also tried storing them in C arrays, but that didn't work out.
My problem comes when trying to store these arrays in a file. writeToFile: doesn't work with CGLayers, but NSKeyedArchiver/NSKeyedUnarchiver hasn't worked either, both when the layers are in NSValues or bridged.
Here's my method that attempts to write and read an array containing a single layer from a file.
+(CGLayerRef) colorAnnotations:(CGContextRef)context{
float symbolSize = 8;
CGRect glyphBox = CGRectMake(0,0, 8, 8);
CGLayerRef annotationLayer = CGLayerCreateWithContext (context,glyphBox.size, NULL);
CGContextRef annotaionLayerContext = CGLayerGetContext(annotationLayer);
CGMutablePathRef annot = CGPathCreateMutable();
//Drawing annotation
/*...*/
NSMutableArray *test = [[NSMutableArray alloc]init];
[test addObject:[[NSValue alloc] initWithBytes:&annotationLayer objCType:#encode(CGLayerRef)]];
//Getting file path in Documents directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:#"/annots.data"];
[NSKeyedArchiver archiveRootObject:test toFile:dataPath];
NSMutableArray *testLoads = [NSKeyedUnarchiver unarchiveObjectWithFile:dataPath];
CGLayerRef layerToReturn;
[[testLoads objectAtIndex:0]getValue:&layerToReturn];
return layerToReturn;
}
I get [NSKeyedArchiver encodeValueOfObjCType:at:]: unknown type encoding ('^')') was raisedfrom this, pretty sure because of the CGLayerRef type.
The lines needed to draw the different annotations I need are pretty long, so I've been trying to figure out a way to have them made and stored in a file without having to make them on startup each time. So far I'm not seeing a way to do this, but was hoping someone here may know of one and would appreciate any help.

NSFileWrapper, lazy loading and saving

I have an NSDocument based application that uses filewrappers to save and load its data.
The document can have all kinds of resources, so I don't want to load everything into memory. I might be doing something fundamentally wrong, but as soon as I change one (inner) file and then save, I can't read any file that hasn't been loaded into memory.
I have separated the relevant code into a separate project to reproduce this behaviour, and I get the same results. The basic flow is this:
I load an existing document from disk. The main fileWrapper is a directory filewrapper (I'll call that main) containing two other filewrappers (sub1 and sub2). The two inner filewrappers are not loaded at this point.
When the user wants to edit sub1, it is loaded from disk.
The user saves the document
If the user wants to edit the other file (sub2), it cannot load. The error that appears:
-[NSFileWrapper regularFileContents] tried to read the file wrapper's contents lazily but an error occurred: The file couldn’t be opened because it doesn’t exist.
Here is the relevant code in my project:
This code might be easier to read in this gist:
https://gist.github.com/bob-codingdutchmen/6869871
#define FileName01 #"testfile1.txt"
#define FileName02 #"testfile2.txt"
/**
* Only called when initializing a NEW document
*/
-(id)initWithType:(NSString *)typeName error:(NSError *__autoreleasing *)outError {
self = [self init];
if (self) {
self.myWrapper = [[NSFileWrapper alloc] initDirectoryWithFileWrappers:nil];
NSLog(#"Initializing new document...");
NSString *testString1 = #"Lorem ipsum first sub file";
NSString *testString2 = #"This is the second sub file with completely unrelated contents";
NSFileWrapper *w1 = [[NSFileWrapper alloc] initRegularFileWithContents:[testString1 dataUsingEncoding:NSUTF8StringEncoding]];
NSFileWrapper *w2 = [[NSFileWrapper alloc] initRegularFileWithContents:[testString2 dataUsingEncoding:NSUTF8StringEncoding]];
w1.preferredFilename = FileName01;
w2.preferredFilename = FileName02;
[self.myWrapper addFileWrapper:w1];
[self.myWrapper addFileWrapper:w2];
}
return self;
}
-(NSFileWrapper *)fileWrapperOfType:(NSString *)typeName error:(NSError *__autoreleasing *)outError {
// This obviously wouldn't happen here normally, but it illustrates
// how the contents of the first file would be replaced
NSFileWrapper *w1 = [self.myWrapper.fileWrappers objectForKey:FileName01];
[self.myWrapper removeFileWrapper:w1];
NSFileWrapper *new1 = [[NSFileWrapper alloc] initRegularFileWithContents:[#"New file contents" dataUsingEncoding:NSUTF8StringEncoding]];
new1.preferredFilename = FileName01;
[self.myWrapper addFileWrapper:new1];
return self.myWrapper;
}
-(BOOL)readFromFileWrapper:(NSFileWrapper *)fileWrapper ofType:(NSString *)typeName error:(NSError *__autoreleasing *)outError {
self.myWrapper = fileWrapper;
return YES;
}
- (IBAction)button1Pressed:(id)sender {
// Read from file1 and show result in field1
NSFileWrapper *w1 = [[self.myWrapper fileWrappers] objectForKey:FileName01];
NSString *string1 = [[NSString alloc] initWithData:w1.regularFileContents encoding:NSUTF8StringEncoding];
[self.field1 setStringValue:string1];
}
- (IBAction)button2Pressed:(id)sender {
// Read from file2 and show result in field2
NSFileWrapper *w2 = [[self.myWrapper fileWrappers] objectForKey:FileName02];
NSString *string2 = [[NSString alloc] initWithData:w2.regularFileContents encoding:NSUTF8StringEncoding];
[self.field2 setStringValue:string2];
}
The bottom two methods are only for updating the UI so I can see what happens.
To change the contents of a file, I remove the existing fileWrapper and add a new one. This is the only way I've found to change the contents of a file, and the way I've seen it done in other SO answers.
When a document is loaded from disk, I keep the fileWrapper around so I can use it (called myWrapper in the code above)
The Apple docs say that NSFileWrapper supports lazy loading and incremental saving, so I'm assuming that my code has some fundamental flaw that I can't see.
An NSFileWrapper is essentially a wrapper around a unix file node. If the file is moved the wrapper stays valid.
The problem yo seem to have is that creating a new file wrapper during saving is a new folder. And the system deletes your previous wrapper including sub2.
To achieve what you want you need to change to incremental saving, i.e. Only saving changed parts in place. See "save in place" in NSDocument.
In your -fileWrapperOfType:error: method, try building a new file wrapper that has new contents for the changed members and references the old file wrappers for the unchanged members.
Following the documentation to addFileWrapper: you add a child (subdirectory) to it, means
directory/
addfileWrapper:fileName1
directory/fileName1/
addfileWrapper:fileName2
directory/fileName1/fileName2.
That file doesn't exist.
You have to use
addRegularFileWithContents:preferredFilename:
instead.

Read and write an integer to/from a .txt file

How can I read and write an integer to and from a text file, and is it possible to read or write to multiple lines, i.e., deal with multiple integers?
Thanks.
This is certainly possible; it simply depends on the exact format of the text file.
Reading the contents of a text file is easy:
// If you want to handle an error, don't pass NULL to the following code, but rather an NSError pointer.
NSString *contents = [NSString stringWithContentsOfFile:#"/path/to/file" encoding:NSUTF8StringEncoding error:NULL];
That creates an autoreleased string containing the entire file. If all the file contains is an integer, you can just write this:
NSInteger integer = [contents integerValue];
If the file is split up into multiple lines (with each line containing one integer), you'll have to split it up:
NSArray *lines = [contents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
for (NSString *line in lines) {
NSInteger currentInteger = [line integerValue];
// Do something with the integer.
}
Overall, it's very simple.
Writing back to a file is just as easy. Once you've manipulated what you wanted back into a string, you can just use this:
NSString *newContents = ...; // New string.
[newContents writeToFile:#"/path/to/file" atomically:YES encoding:NSUTF8StringEncoding error:NULL];
You can use that to write to a string. Of course, you can play with the settings. Setting atomically to YES causes it to write to a test file first, verify it, and then copy it over to replace the old file (this ensures that if some failure happens, you won't end up with a corrupt file). If you want, you can use a different encoding (though NSUTF8StringEncoding is highly recommended), and if you want to catch errors (which you should, essentially), you can pass in a reference to an NSError to the method. It would look something like this:
NSError *error = nil;
[newContents writeToFile:#"someFile.txt" atomically:YES encoding:NSUTF8StringEncoding error:&error];
if (error) {
// Some error has occurred. Handle it.
}
For further reading, consult the NSString Class Reference.
If you have to write to multiple lines, use \r\n when building the newContents string to specify where line breaks are to be placed.
NSMutableString *newContents = [[NSMutableString alloc] init];
for (/* loop conditions here */)
{
NSString *lineString = //...do stuff to put important info for this line...
[newContents appendString:lineString];
[newContents appendString:#"\r\n"];
}

How to save apps data Xcode

I have been searching for many days on how to save my apps data. I found some stuff but it was very complicated and badly explained. I need that when I completely close my apps all the data I entered in the text field are still there when I open my apps again. I tried a tutorial but this only let me save about 8 textfields and I need to save thousands I am starting Objective-C and Xcode so if somebody want to give me an answer please make it very precise.
Alright, what I'd suggest would be putting all the data from your text fields into an array and saving that to a file, then loading it when you re-open the app.
The first thing you need is a save file. This function will create one for you.
-(NSString*) saveFilePath{
NSString* path = [NSString stringWithFormat:#"%#%#",
[[NSBundle mainBundle] resourcePath],
#"myfilename.plist"];
return path;}
Now that that's done you need to create your saving array. Hopefully you have your thousands of textfields already fitted into an array of some sort. If not, this will be a painful process regardless of how you tackle it. But anyway... (Here, labelArray will be the array of all your text fields/labels/etc.)
NSMutableArray* myArray = [[NSMutableArray alloc]init];
int i = 0;
while(i < labelArray.count){
[myArray addObject: [labelArray objectAtIndex: i].text];
i ++;
}
[myArray writeToFile:[self saveFilePath] atomically:YES];
[myArray release];
And the loading code would be something along the lines of
NSMutableArray* myArray = [[NSMutableArray arrayWithContentsOfFile:[self saveFilePath]]retain];
Then you'd simply load the data back into your array of text fields.
Hope this helps.
It sounds like your application architecture may be unsound if you are planning on saving thousands of text fields' data in the fraction of a second you get while your app is closing. It would probably be better to save these as the user enters the data instead of waiting to save all the data at once.
To get the path you are going to write ( or read from! ) to, you do the following:
NSString *writableDBPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingPathComponent:#"MyFile.extension"];
And then use a method like "writeToFile:automically:" of NSString or NSDictionary etc. to write to the path.

File Handling in Objective C

Is there anyway to do Files Handling in Objective-C? I am just trying to do simple read and write and can use 'c' but i am force to use Objective-C classes for that :#. I am looking into NSInputStream, but its going over my head. Is there any tutorial which explains how to use NSInputStream?
I had trouble with basic file i/o when I first hit it in Obj-C as well. I ended up using NSFileHandle to get C style access to my file. Here's a basic example:
// note: myFilename is an NSString containing the full path to the file
// create the file
NSFileManager *fManager = [[NSFileManager alloc] init];
if ([fManager createFileAtPath:myFilename contents:nil attributes:nil] != YES) {
NSLog(#"Failed to create file: %#", myFilename);
}
[fManager release]; fManager = nil;
// open the file for updating
NSFileHandle *myFile = [NSFileHandle fileHandleForUpdatingAtPath:myFilename];
if (myFile == nil) {
NSLog(#"Failed to open file for updating: %#", myFilename);
}
// truncate the file so it is guaranteed to be empty
[myFile truncateFileAtOffset:0];
// note: rawData is an NSData object
// write data to a file
[myFile writeData:rawData];
// close the file handle
[myFile closeFile]; myFile = nil;
If all you need to do is really simple I/O, you can just tell an object to initialize itself from, or write itself to, a filesystem path or URL. This works with several Foundation classes, including NSString, NSData, NSArray, and NSDictionary among others.
Try starting out by looking at the following two NSString methods:
- initWithContentsOfFile:encoding:error:
- writeToFile:atomically:encoding:error:
I find apple's guides short and to the point.
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Streams/Articles/ReadingInputStreams.html