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
  • 58,317
  • 11
  • 87
  • 139
papr
  • 4,717
  • 5
  • 30
  • 38

5 Answers5

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,829
  • 3
  • 55
  • 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,397
  • 1
  • 24
  • 25
  • 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
31

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

Brian Campbell
  • 322,767
  • 57
  • 360
  • 340
  • 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
4

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
  • 415,655
  • 72
  • 787
  • 1,044
0

The easiest way, if you want to do this a few times manually instead of coding, is to only use your mouse:

  1. Put a breakpoint one line after your NSdata are fulfilled.

enter image description here

  1. Hold your mouse over your NSData variable, click on the Eye button, and then export.

  2. After that chose the storage details (name/extension/location of the file).

enter image description here

  1. Click on "Save" and you are done!