¿Qué es el directorio de documentos (NSDocumentDirectory)?

123

¿Alguien puede explicarme cuál es el directorio de documentos en una aplicación iOS y cuándo usarlo?

Esto es lo que creo actualmente:

Para mí, parece ser una carpeta central donde el usuario puede almacenar los archivos necesarios para la aplicación.

¿Esta sería una ubicación diferente de donde Core Data almacena sus datos?

Parece que cada aplicación tiene su propio directorio de documentos.

¿Soy libre de crear un subdirectorio del directorio de documentos, como el directorio de documentos / imágenes o el directorio de documentos / videos?

usuario798719
fuente
Iirc, el NSDocumentDirectory se encuentra en la misma ruta que los datos principales de la aplicación, y cada aplicación tiene su propio directorio de documentos. Y sí, puedes poner libremente todos los recursos que necesites para tu aplicación aquí. Por cierto, ¿parece que tu pregunta aún no está completa?
Zekareisoujin
Acabo de publicar algo que creo que se relaciona con su pregunta aquí stackoverflow.com/questions/5105250/… verifíquelo para saber si funciona para usted.
Wytchkraft
Para cualquier persona que venga de Google, tenga en cuenta que esto ha cambiado en iOS 8. Vea mi respuesta a continuación.
livingtech
es la misma ubicación donde se guarda su archivo sqlite.
Anurag Bhakuni

Respuestas:

197

Su aplicación solamente (en un dispositivo sin jailbreak) se ejecuta en un entorno "sandboxed". Esto significa que solo puede acceder a archivos y directorios dentro de sus propios contenidos. Por ejemplo Documentos y Biblioteca .

Consulte la Guía de programación de aplicaciones iOS .

Para acceder al directorio Documentos de su sandbox de aplicaciones, puede usar lo siguiente:

iOS 8 y posterior, este es el método recomendado

+ (NSURL *)applicationDocumentsDirectory
{
     return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}

si necesita soportar iOS 7 o anterior

+ (NSString *) applicationDocumentsDirectory 
{    
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *basePath = paths.firstObject;
    return basePath;
}

Este directorio de Documentos le permite almacenar archivos y subdirectorios que su aplicación crea o puede necesitar.

Para acceder a los archivos en el directorio de la Biblioteca del uso de sandbox de sus aplicaciones (en lugar de lo pathsanterior):

[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0]
WrightsCS
fuente
13
He descubierto que [@ "~ / Documentos" stringByExpandingTildeInPath] hace lo mismo. ¿Hay alguna razón para desalentar esto?
Cthutu
35
No usaría el enfoque con @ "~ / Documentos". La codificación de rutas nunca es una buena idea. Puede funcionar ahora, pero si Apple alguna vez elige cambiar el nombre o mover el directorio Documentos, su aplicación se interrumpirá. NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);siempre te dará el directorio correcto!
16
aún debe usar la API proporcionada. ¡Por eso está ahí! Has tenido suerte hasta ahora.
nielsbot
20
Divertido cómo necesito googlear esto cada vez que tengo que usarlo. Creo que todas las plataformas móviles deberían proporcionar un parámetro global predeterminado para el directorio de inicio / escritura
Ravindranath Akila
1
enlace actualizado con información sobre la capacidad de una aplicación para escribir en carpetas: developer.apple.com/library/mac/documentation/FileManagement/…
phil
43

Esto ha cambiado en iOS 8. Consulte la siguiente nota técnica: https://developer.apple.com/library/ios/technotes/tn2406/_index.html

La forma autorizada por Apple (del enlace de arriba) es la siguiente:

// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory
{
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
livingtech
fuente
77
Esta es la respuesta que quieres! El año es casi 2016 ahora. Esta es una pregunta popular con respuestas desactualizadas.
Jeff
¿Puedo usar el método anterior para recuperar la ruta del directorio de documentos? como url.path?
Abuzar Amin
21

No pude encontrar el código en el documento sugerido por la respuesta aceptada, pero encontré el equivalente actualizado aquí:

Guía de programación del sistema de archivos :: Acceso a archivos y directorios »

- (NSURL*)applicationDataDirectory {
    NSFileManager* sharedFM = [NSFileManager defaultManager];
    NSArray* possibleURLs = [sharedFM URLsForDirectory:NSApplicationSupportDirectory
                                 inDomains:NSUserDomainMask];
    NSURL* appSupportDir = nil;
    NSURL* appDirectory = nil;

    if ([possibleURLs count] >= 1) {
        // Use the first directory (if multiple are returned)
        appSupportDir = [possibleURLs objectAtIndex:0];
    }

    // If a valid app support directory exists, add the
    // app's bundle ID to it to specify the final directory.
    if (appSupportDir) {
        NSString* appBundleID = [[NSBundle mainBundle] bundleIdentifier];
        appDirectory = [appSupportDir URLByAppendingPathComponent:appBundleID];
    }

    return appDirectory;
}

Desalienta el uso de NSSearchPathForDirectoriesInDomain:

La función NSSearchPathForDirectoriesInDomains se comporta como el método URLsForDirectory: inDomains: pero devuelve la ubicación del directorio como una ruta basada en cadenas. Debería utilizar el método URLsForDirectory: inDomains: en su lugar.

Aquí hay algunas otras constantes de directorio útiles para jugar. Sin duda, no todos estos son compatibles con iOS. También puede usar la función NSHomeDirectory () que:

En iOS, el directorio de inicio es el directorio sandbox de la aplicación. En OS X, es el directorio sandbox de la aplicación o el directorio de inicio del usuario actual (si la aplicación no está en un sandbox)

Desde NSPathUtilities.h

NSApplicationDirectory = 1,             // supported applications (Applications)
    NSDemoApplicationDirectory,             // unsupported applications, demonstration versions (Demos)
    NSDeveloperApplicationDirectory,        // developer applications (Developer/Applications). DEPRECATED - there is no one single Developer directory.
    NSAdminApplicationDirectory,            // system and network administration applications (Administration)
    NSLibraryDirectory,                     // various documentation, support, and configuration files, resources (Library)
    NSDeveloperDirectory,                   // developer resources (Developer) DEPRECATED - there is no one single Developer directory.
    NSUserDirectory,                        // user home directories (Users)
    NSDocumentationDirectory,               // documentation (Documentation)
    NSDocumentDirectory,                    // documents (Documents)
    NSCoreServiceDirectory,                 // location of CoreServices directory (System/Library/CoreServices)
    NSAutosavedInformationDirectory NS_ENUM_AVAILABLE(10_6, 4_0) = 11,   // location of autosaved documents (Documents/Autosaved)
    NSDesktopDirectory = 12,                // location of user's desktop
    NSCachesDirectory = 13,                 // location of discardable cache files (Library/Caches)
    NSApplicationSupportDirectory = 14,     // location of application support files (plug-ins, etc) (Library/Application Support)
    NSDownloadsDirectory NS_ENUM_AVAILABLE(10_5, 2_0) = 15,              // location of the user's "Downloads" directory
    NSInputMethodsDirectory NS_ENUM_AVAILABLE(10_6, 4_0) = 16,           // input methods (Library/Input Methods)
    NSMoviesDirectory NS_ENUM_AVAILABLE(10_6, 4_0) = 17,                 // location of user's Movies directory (~/Movies)
    NSMusicDirectory NS_ENUM_AVAILABLE(10_6, 4_0) = 18,                  // location of user's Music directory (~/Music)
    NSPicturesDirectory NS_ENUM_AVAILABLE(10_6, 4_0) = 19,               // location of user's Pictures directory (~/Pictures)
    NSPrinterDescriptionDirectory NS_ENUM_AVAILABLE(10_6, 4_0) = 20,     // location of system's PPDs directory (Library/Printers/PPDs)
    NSSharedPublicDirectory NS_ENUM_AVAILABLE(10_6, 4_0) = 21,           // location of user's Public sharing directory (~/Public)
    NSPreferencePanesDirectory NS_ENUM_AVAILABLE(10_6, 4_0) = 22,        // location of the PreferencePanes directory for use with System Preferences (Library/PreferencePanes)
    NSApplicationScriptsDirectory NS_ENUM_AVAILABLE(10_8, NA) = 23,      // location of the user scripts folder for the calling application (~/Library/Application Scripts/code-signing-id)
    NSItemReplacementDirectory NS_ENUM_AVAILABLE(10_6, 4_0) = 99,       // For use with NSFileManager's URLForDirectory:inDomain:appropriateForURL:create:error:
    NSAllApplicationsDirectory = 100,       // all directories where applications can occur
    NSAllLibrariesDirectory = 101,          // all directories where resources can occur
    NSTrashDirectory NS_ENUM_AVAILABLE(10_8, NA) = 102                   // location of Trash directory

Y, por último, algunos métodos convenientes en una categoría NSURL http://club15cc.com/code/ios/easy-ios-file-directory-paths-with-this-handy-nsurl-category

Hari Karam Singh
fuente
8

Swift 3 y 4 como var global:

var documentsDirectory: URL {
    return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last!
}

Como extensión FileManager:

extension FileManager {
    static var documentsDirectory: URL {
        return `default`.urls(for: .documentDirectory, in: .userDomainMask).last!
    }

    var documentsDirectory: URL {
        return urls(for: .documentDirectory, in: .userDomainMask).last!
    }
}
Anton Plebanovich
fuente
Gracias. Siempre me olvido de esto. :) Si desea aferrarse a las pautas de diseño de la API, puede nombrarlo documentDirectoryURLo simplemente documentDirectoryconfiar en el tipo. Me gusta la idea de tener un alcance estático a FileManagertravés de una propiedad estática en una extensión.
Ray Fix
1
¡Gracias @RayFix, actualicé mi respuesta con tu sugerencia!
Anton Plebanovich el
6

Puede ser más limpio agregar una extensión a FileManager para este tipo de llamada incómoda, por orden, si nada más. Algo como:

extension FileManager {
    static var documentDir : URL {
        return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    }
}
Paul Robinson
fuente
4

Puede acceder al directorio de documentos utilizando este código, que se utiliza básicamente para almacenar archivos en formato plist:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths firstObject];
return documentsDirectory;
Sumit Oberoi
fuente
Esta misma respuesta fue dada hace 3 años por WrightsCS. Además, dar esta respuesta en 2014 es extraño teniendo en cuenta que Apple recomienda el método en la respuesta de livingtech.
Jeff
Si cree que me equivoco al rechazar el voto, explique por qué. Un voto de venganza sobre una de mis preguntas es infantil. Este sitio trata de llevar las mejores respuestas a la cima.
Jeff
@ jeff gracias por señalar, investigé un poco y tenías razón. nueva solución: - (NSURL *) applicationDocumentsDirectory {return [[[NSFileManager defaultManager] URLsForDirectory: NSDocumentDirectory inDomains: NSUserDomainMask] lastObject]; }
Sumit Oberoi
1

Aquí hay una pequeña función útil que hace que usar / crear carpetas iOS sea un poco más fácil.

Si le pasa el nombre de una subcarpeta, le devolverá la ruta completa y se asegurará de que el directorio exista.

(Personalmente, pego esta función estática en mi clase AppDelete, pero quizás este no sea el lugar más inteligente para ponerla).

Así es como lo llamaría, para obtener la "ruta completa" de un subdirectorio MySavedImages:

NSString* fullPath = [AppDelegate getFullPath:@"MySavedImages"];

Y aquí está la función completa:

+(NSString*)getFullPath:(NSString*)folderName
{
    //  Check whether a subdirectory exists in our sandboxed Documents directory.
    //  Returns the full path of the directory.
    //
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    if (paths.count < 1)
        return nil;

    NSString *rootFolder = [paths firstObject];
    NSString* fullFolderPath = [rootFolder stringByAppendingPathComponent:folderName];

    BOOL isDirectory;
    NSFileManager* manager = [NSFileManager defaultManager];

    if (![manager fileExistsAtPath:fullFolderPath isDirectory:&isDirectory] || !isDirectory) {
        NSError *error = nil;
        NSDictionary *attr = [NSDictionary dictionaryWithObject:NSFileProtectionComplete
                                                         forKey:NSFileProtectionKey];
        [manager createDirectoryAtPath:fullFolderPath
           withIntermediateDirectories:YES
                            attributes:attr
                                 error:&error];
        if (error) {
            NSLog(@"Error creating directory path: %@", [error localizedDescription]);
            return nil;
        }
    }
    return fullFolderPath;
}

Con esta pequeña función, es fácil crear un directorio en el directorio Documentos de su aplicación (si aún no existe) y escribir un archivo en él.

Así es como crearía el directorio y escribiría el contenido de uno de mis archivos de imagen en él:

//  Let's create a "MySavedImages" subdirectory (if it doesn't already exist)
NSString* fullPath = [AppDelegate getFullPath:@"MySavedImages"];

//  As an example, let's load the data in one of my images files
NSString* imageFilename = @"icnCross.png";

UIImage* image = [UIImage imageNamed:imageFilename];
NSData *imageData = UIImagePNGRepresentation(image);

//  Obtain the full path+filename where we can write this .png to, in our new MySavedImages directory
NSString* imageFilePathname = [fullPath stringByAppendingPathComponent:imageFilename];

//  Write the data
[imageData writeToFile:imageFilePathname atomically:YES];

Espero que esto ayude !

Mike Gledhill
fuente
0

Al igual que otros mencionados, su aplicación se ejecuta en un entorno de espacio aislado y puede usar el directorio de documentos para almacenar imágenes u otros activos que su aplicación puede usar, por ejemplo. descarga de archivos offline-d como prefiera el usuario - Conceptos básicos del sistema de archivos - Documentación de Apple - Qué directorio usar, para almacenar archivos específicos de la aplicación

Actualizado a swift 5, puede usar una de estas funciones, según los requisitos:

func getDocumentsDirectory() -> URL {
    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    return paths[0]
}

func getCacheDirectory() -> URL {
        let paths = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask)
        return paths[0]
    }

func getApplicationSupportDirectory() -> URL {
        let paths = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
        return paths[0]
    }

Uso:

let urlPath = "https://jumpcloud.com/wp-content/uploads/2017/06/SSH-Keys.png" //Or string path to some URL of valid image, for eg.

if let url = URL(string: urlPath){
    let destination = getDocumentsDirectory().appendingPathComponent(url.lastPathComponent)
    do {
        let data = try Data(contentsOf: url) //Synchronous call, just as an example
        try data.write(to: destination)
    } catch _ {
        //Do something to handle the error
    }
}
akashlal.com
fuente