4

I've struggled with this all day using answers from here, but I cannot find a solution that is working for me, so I thought I'd ask for help:

I have an array of objects that I've extracted from an entity.

All I want to do is to write the objects to a file.

What I've come up with so far is this:

    NSLog(@" Exported Records: %i", [exportArray count]); 
    // the count here is 4 records.
    //Each record has about 8 elements. 
//I'm just trying to get this working with the first two elements right now

        NSString *writeString = nil;
        NSError *error = nil;
        int i = 0;
        NSString *key = nil;
        NSString *tempString = nil;
        for (i=0; i<[exportArray count]; i++)
        {
            tempString = [tempString stringByAppendingString: @" \n "];

            for (int j=0; j<3; j++)
            {
                if (j == 0)
                {
                    key = [[exportArray objectAtIndex:i]  valueForKey:@"title"];
                    //[NSString stringWithFormat:tempString2, [[exportArray objectAtIndex:i]  valueForKey:@"title"]];
                }
                if (j == 1)
                {
                    key = [[exportArray objectAtIndex:i]  valueForKey:@"username"];
                    //[NSString stringWithFormat:tempString2, [[exportArray objectAtIndex:i]  valueForKey:@"username"]];
                }
                writeString = [NSString stringWithFormat:tempString, key];
            }
        }

        // Write to the file
        [writeString writeToFile:dataFile atomically:YES
                    encoding:NSUTF8StringEncoding error:&error];
        if (error)
        {
            NSLog(@"%@", error);
        }

Right now, all I'm getting the the last item, so I'm overwriting the string. But, there must be a better method to achieve this. Please let me know if there is another answer here matches my question, or please, post some ideas.

UPDATE

When I log the exportArray, I get this:

"<ItemEntity: 0x1ddf6560> (entity: ItemEntity; id: 0x1ddf46d0 <x-coredata://78FBC4A5-AE1A-4344-98AB-978126457D96/ItemEntity/p2> ; data: <fault>)",
    "<ItemEntity: 0x1ddf6800> (entity: ItemEntity; id: 0x1ddf46e0 <x-coredata://78FBC4A5-AE1A-4344-98AB-978126457D96/ItemEntity/p2051> ; data: <fault>)",
    "<ItemEntity: 0x1ddf6860> (entity: ItemEntity; id: 0x1ddf45d0 <x-coredata://78FBC4A5-AE1A-4344-98AB-978126457D96/ItemEntity/p3075> ; data: <fault>)",
    "<ItemEntity: 0x1ddf68c0> (entity: ItemEntity; id: 0x1ddf45e0 <x-coredata://78FBC4A5-AE1A-4344-98AB-978126457D96/ItemEntity/p5124> ; data: <fault>)"
)

When I log the actual values:

NSLog(@" Exported titles: %@", [exportArray valueForKey:@"title"]);
NSLog(@" Exported usernames: %@", [exportArray valueForKey:@"username"]);

I get correct results. I just don't know how to tie these and the other attributes together..

  Exported titles: (
    1,
    2,
    4,
    6
)
  Exported usernames: (
    ellis,
    david,
    bea,
    ian
)
ICL1901
  • 7,632
  • 14
  • 90
  • 138
  • 2
    Are there any custom objects in the array? – zaph Jan 15 '13 at 00:09
  • 1
    "All I want to do is to write the objects to a file." Are you trying to save your objects themselves, or, as your code would suggest, a string with values from your objects? – rdelmar Jan 15 '13 at 00:18
  • @Zaph, only data objects (UItextFields, a UItextView, and an NSDate). There is one field that comes from a coredata relationship. – ICL1901 Jan 15 '13 at 01:20
  • @rdelmar, ideally, I'd like to export these as a plist (xml) so that if my app goes kaput, the user still has their data in a form that can be read elsewhere. – ICL1901 Jan 15 '13 at 01:21
  • @David `UItextFields` and `UItextView` are not supported by the plist format so this will not work. If what you want to save is just the text from them put the text in the array. – zaph Jan 15 '13 at 01:52
  • I think I'm onto something. While exportArray contains 4 records, they are still linked to the core data entity. I'm updating my question with a log of the whole array.. @ACB, is this ok for you? – ICL1901 Jan 15 '13 at 01:55
  • @DavidDelMonte, Looks like you already got the answer. You can just use the `array1` and `array2` mentioned in my answer to form the new array and then you can write to a file. – iDev Jan 15 '13 at 19:27

5 Answers5

8

If you just have NSDictionaries in your array, you can use writeToFile:atomically: which will save a file in .plist format (which is much easier to read than a CSV).

Check out the Apple Docs for NSArray, and a good write up on plists.

So you would end up having:

[exportArray writeToFile:fileName atomically:YES];

Edit: You cannot save a core data object to a file. So what you want to do is create an interim array with just the data you want, then write that to the file.

NSMutableArray* arrayToSave = [NSMutableArray arrayWithCapacity:exportArray.count];
[exportArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    NSDictionary* exportDict = [NSDictionary dictionaryWithObjectsAndKeys:
                                [obj objectForKey:@"username"], @"username",
                                [obj objectForKey:@"title"], @"title", nil];
    [arrayToSave addObject:exportDict];
}];

[arrayToSave writeToFile:fileName atomically:YES];
MishieMoo
  • 6,620
  • 2
  • 25
  • 35
  • I wish this worked. Maybe I'm doing something wrong, as I only get the last item (username in record 4) written to the file.. But plist would be perfect as output. – ICL1901 Jan 15 '13 at 01:28
  • This replaces all of your code above, no iteration necessary. And I just saw your edit! You cannot save objects directly to a plist. I'll edit my answer, which will hopefully help you more. – MishieMoo Jan 15 '13 at 03:44
  • looks promising but I get a run error: [ItemEntity objectForKey:]: unrecognized selector sent... – ICL1901 Jan 15 '13 at 04:14
  • Now that's progress! Are these NSManagedObject subclasses, with `username` and `title` declared as properties? If so, you can replace the `objectForKey:` method call with a normal property accessor (i.e. `obj.username` or `[obj username]` which you may have to cast). – MishieMoo Jan 15 '13 at 04:17
7

Use this,

[exportArray writeToFile:dataFile atomically:YES];

NSArray has the method writeToFile: to do this. Check the documentation here. This works fine if there are no custom objects inside the array.

In case you want to save only title or username(Even if it has custom objects in array), you can do it as:

NSArray *array1 = [exportArray valueForKey:@"title"];
[array1 writeToFile:dataFile atomically:YES];

or

NSArray *array2 = [exportArray valueForKey:@"username"];
[array2 writeToFile:dataFile atomically:YES];
iDev
  • 23,310
  • 7
  • 60
  • 85
  • Gosh I wish this worked! This gives me the last item in the exportArray. – ICL1901 Jan 15 '13 at 01:26
  • @DavidDelMonte, Which line are you mentioning? Can you try printing `array1` and `array2` and check the values. – iDev Jan 15 '13 at 01:48
4
[exportArray writeToFile: dataFile atomically:YES];

Creates and writes a .plist file format.

Unless you have objects in the array that are not supported by a plist.

The supported object are: the property list objects `NSString, NSNumber, NSDate, NSData, NSArray and NSDictionary.

UItextField and UItextView are not supported by the plist format so this will not work. If what you want to save is just the text from them put the text in the array.

zaph
  • 111,848
  • 21
  • 189
  • 228
2

Try this:


    NSMutableString *string = [NSMutableString string];
    for (NSDictionary* dict in exportArray) {
        for (NSString* key in [dict allKeys]) {
            NSObject* value = [dict objectForKey:key];
            [string appendFormat:@"%@ = %@\n", key, value];
        }
    }

    // Write to the file
    NSError* error = nil;
    [string writeToFile:dataFile atomically:YES encoding:NSUTF8StringEncoding error:&error];
    if (error)
        NSLog(@"%@", error);
Alejandro
  • 3,726
  • 1
  • 23
  • 29
2

I would consider converting it to NSData first and then saving it as a binary plist rather than a human-readable plist as binary plists are much faster to read (2-3x I believe but I can't remember which WWDC video it was explained in). To do this, you first need to convert the NSArray to an NSData object:

(Source: Rob Napier's answer to this SO question)

NSData *data = [NSKeyedArchiver archivedDataWithRootObject:array];
// "Note that all the objects in array must conform to the NSCoding protocol."

NSString *error;
NSData *data = [NSPropertyListSerialization dataWithPropertyList:array
format:NSPropertyListBinaryFormat_v1_0 options:NULL error:&error];

Then you can write the NSData to file using:

[data writeToFile:path atomically:YorN];

To read it back, you then use:

NSData *rawdata = [[NSData alloc] initWithContentsOfFile:/*file*/];
NSData *arrayData = [NSPropertyListSerialization propertyListWithData:rawdata
options:NULL format:NULL error:&error];
NSArray *array = [NSKeyedUnarchiver unarchiveObjectWithData:data];

You only really need to use the binary process for extremely large .plist files, but it is handy to know about.

Community
  • 1
  • 1
Ephemera
  • 8,672
  • 8
  • 44
  • 84
  • Thank everyone for answering. I need to go through each answer carefully, and I'll report back asap. – ICL1901 Jan 15 '13 at 01:18