70
NSData *data;
data = [self fillInSomeStrangeBytes];

My question is now how I can write this data on the easiest way to an file.

(I've already an NSURL file://localhost/Users/Coding/Library/Application%20Support/App/file.strangebytes)

pnuts
  • 56,678
  • 9
  • 81
  • 133
papr
  • 4,647
  • 5
  • 29
  • 38

4 Answers4

101

NSData has a method called writeToURL:atomically: that does exactly what you want to do. Look in the documentation for NSData to see how to use it.

Alex
  • 26,789
  • 3
  • 54
  • 74
  • 1
    Does the source of the `NSData` object matter for this method? [In this question](http://stackoverflow.com/questions/16150196/updating-sqlite-database-without-xml) I'm saving a .sqlite database I downloaded into a `NSData` object from a URL, but it doesn't seem to be saving it correctly. The file is written, but when I try to access it (either through my app or with a 3rd party viewer) it tells me it's not a valid SQLite database. Does `writeToURL:atomically:` only work for saving `NSString`s or something like that? – GeneralMike Apr 25 '13 at 17:10
  • 1
    `NSData` is a wrapper for any kind of binary data. The original source shouldn't matter. Looking at your question, I'd recommend using `NSURLConnection` (or a library like `AFNetworking`) over `initWithContentsOfURL:`. For small downloads, `NSData` can be fine, but it doesn't provide any control over the download. When something goes wrong, this makes it hard to diagnose the problem. – Alex Apr 25 '13 at 17:28
38

Notice that writing NSData into a file is an IO operation that may block the main thread. Especially if the data object is large.

Therefore it is advised to perform this on a background thread, the easiest way would be to use GCD as follows:

// Use GCD's background queue
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
    // Generate the file path
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:@"yourfilename.dat"];

     // Save it into file system
    [data writeToFile:dataPath atomically:YES];
});
Tom Susel
  • 3,207
  • 1
  • 23
  • 23
  • 1
    how to retrieve it? –  May 29 '17 at 12:09
  • 1
    [`dataWithContentsOfFile:`](https://developer.apple.com/documentation/foundation/nsdata/1547226-datawithcontentsoffile?language=objc) – Rob Feb 15 '18 at 17:44
30

writeToURL:atomically: or writeToFile:atomically: if you have a filename instead of a URL.

Brian Campbell
  • 306,970
  • 56
  • 356
  • 335
  • 2
    +1 for NSURL. NSURL pwns NSString when you point to a file. Apple always recommends NSURL for file paths rather than NSString. :) –  Jan 04 '11 at 12:25
  • 9
    I don't think there's a difference between writeToURL vs writeToFile. According to documentation: "Since at present only file:// URLs are supported, there is no difference between this method and writeToFile:atomically:, except for the type of the first argument" – Ronnie Liew Jun 26 '11 at 16:33
  • Does the source of the `NSData` object matter for this method? [In this question](http://stackoverflow.com/questions/16150196/updating-sqlite-database-without-xml) I'm saving a .sqlite database I downloaded into a `NSData` object from a URL, but it doesn't seem to be saving it correctly. The file is written, but when I try to access it (either through my app or with a 3rd party viewer) it tells me it's not a valid SQLite database. Does `writeToURL:atomically:` only work for saving `NSString`s or something like that? – GeneralMike Apr 25 '13 at 17:10
3

You also have writeToFile:options:error: or writeToURL:options:error: which can report error codes in case the saving of the NSData failed for any reason. For example:

NSError *error;

NSURL *folder = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:true error:&error];
if (!folder) {
    NSLog(@"%s: %@", __FUNCTION__, error);        // handle error however you would like
    return;
}

NSURL *fileURL = [folder URLByAppendingPathComponent:filename];
BOOL success = [data writeToURL:fileURL options:NSDataWritingAtomic error:&error];
if (!success) {
    NSLog(@"%s: %@", __FUNCTION__, error);        // handle error however you would like
    return;
}
Rob
  • 392,368
  • 70
  • 743
  • 952