Qual é o diretório de documentos (NSDocumentDirectory)?

123

Alguém pode me explicar qual é o diretório de documentos em um aplicativo iOS e quando usá-lo?

Aqui está o que eu acredito atualmente:

Para mim, parece ser uma pasta central onde o usuário pode armazenar todos os arquivos necessários para o aplicativo.

Este seria um local diferente daquele em que o Core Data armazena seus dados?

Parece que cada aplicativo recebe seu próprio diretório de documentos.

Sou livre para criar um subdiretório do diretório de documentos, como diretório / imagens de documentos ou diretório / vídeos de documentos?

user798719
fonte
Iirc, o NSDocumentDirectory está no mesmo caminho que os dados principais do aplicativo e cada aplicativo tem seu próprio diretório de documentos. E sim, você pode colocar livremente todos os recursos necessários para o seu aplicativo aqui. A propósito, parece que sua pergunta ainda não está concluída?
Zekareisoujin
Acabei de publicar algo que acho que se relaciona à sua pergunta aqui: stackoverflow.com/questions/5105250/… verifique para verificar se funciona para você.
Wytchkraft
Para quem vem do google, observe que isso mudou no iOS 8. Veja minha resposta abaixo.
livingtech
é o mesmo local em que seu arquivo sqlite é salvo.
Anurag Bhakuni

Respostas:

197

Seu aplicativo apenas (em um dispositivo sem jailbreak) é executado em um ambiente "protegido por sandbox". Isso significa que ele pode acessar apenas arquivos e diretórios dentro de seu próprio conteúdo. Por exemplo Documentos e Biblioteca .

Consulte o Guia de programação de aplicativos do iOS .

Para acessar o diretório Documents da sua caixa de proteção de aplicativos, você pode usar o seguinte:

iOS 8 e mais recente, este é o método recomendado

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

se você precisar oferecer suporte ao iOS 7 ou anterior

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

Esse diretório de documentos permite armazenar arquivos e subdiretórios que seu aplicativo cria ou pode precisar.

Para acessar arquivos no diretório Biblioteca da caixa de proteção de seus aplicativos, use (no lugar do pathsacima):

[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0]
WrightsCS
fonte
13
Eu descobri que [@ "~ / Documents" stringByExpandingTildeInPath] faz a mesma coisa. Existe alguma razão para que isso deva ser desencorajado?
Cthutu
35
Eu não usaria a abordagem com @ "~ / Documents". Caminhos codificados nunca são uma boa ideia. Pode funcionar agora, mas se a Apple optar por renomear ou mover o diretório Documents, seu aplicativo será interrompido. NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);sempre lhe dará o diretório correto!
16
você ainda deve usar a API fornecida. É por isso que está lá! Você apenas teve sorte até agora.
Nielsbot
20
Hilariante como preciso pesquisar isso no google toda vez que preciso usá-lo. Eu acho que todas as plataformas móveis deve fornecer parâmetro global padrão para o diretório home / gravável
Ravindranath Akila
1
link atualizado com informações sobre a capacidade de um aplicativo grave pastas: developer.apple.com/library/mac/documentation/FileManagement/...
phil
43

Isso mudou no iOS 8. Veja a seguinte nota técnica: https://developer.apple.com/library/ios/technotes/tn2406/_index.html

A maneira sancionada pela Apple (no link acima) é a seguinte:

// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory
{
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
livingtech
fonte
7
Esta é a resposta que você deseja! O ano é quase 2016 agora. Esta é uma pergunta popular com respostas desatualizadas.
61115 Jeff
Posso usar o método acima para recuperar o caminho do diretório de documentos? como url.path?
Abuzar Amin 7/03/16
21

Não encontrei o código no documento sugerido pela resposta aceita, mas encontrei o equivalente atualizado aqui:

Guia de programação do sistema de arquivos :: Acessando arquivos e diretórios »

- (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;
}

Desencoraja o uso de NSSearchPathForDirectoriesInDomain:

A função NSSearchPathForDirectoriesInDomains se comporta como o método URLsForDirectory: inDomains:, mas retorna o local do diretório como um caminho baseado em string. Você deve usar o método URLsForDirectory: inDomains:.

Aqui estão algumas outras constantes de diretório úteis para brincar. Sem dúvida, nem todos esses são suportados no iOS. Além disso, você pode usar a função NSHomeDirectory () que:

No iOS, o diretório inicial é o diretório de área restrita do aplicativo. No OS X, é o diretório de área restrita do aplicativo ou o diretório inicial do usuário atual (se o aplicativo não estiver em uma área restrita)

De 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

E, finalmente, alguns métodos de conveniência em uma categoria NSURL http://club15cc.com/code/ios/easy-ios-file-directory-paths-with-this-handy-nsurl-category

Hari Karam Singh
fonte
8

Swift 3 e 4 como var global:

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

Como extensão 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
fonte
Obrigado. Eu sempre esqueço isso. :) Se você deseja manter-se firme nas diretrizes de design da API, pode nomeá-lo documentDirectoryURLou simplesmente documentDirectorye confiar no tipo. Eu gosto da idéia de definir o escopo estaticamente para FileManageruma propriedade estática em uma extensão.
Ray Fix
1
Obrigado @RayFix, atualizei minha resposta com sua sugestão!
Anton Plebanovich 23/10
6

Pode ser mais fácil adicionar uma extensão ao FileManager para esse tipo de chamada incômoda, para limpeza, se nada mais. Algo como:

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

Você pode acessar o diretório de documentos usando este código, que é basicamente usado para armazenar arquivos no formato plist:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths firstObject];
return documentsDirectory;
Sumit Oberoi
fonte
Essa mesma resposta foi dada três anos antes pela WrightsCS. Além disso, dar essa resposta em 2014 é estranho, considerando que a Apple recomenda o método na resposta da livingtech.
Jeff
Se você acha que estou errado em votar, explique por que. Uma vingança contra uma das minhas perguntas é infantil. Este site é sobre empurrar as melhores respostas para o topo.
Jeff
@jeff obrigado por apontar, fiz algumas pesquisas e você estava certo. nova solução: - (NSURL *) applicationDocumentsDirectory {return [[[NSFileManager defaultManager] URLsForDirectory: NSDocumentDirectory inDomains: NSUserDomainMask] lastObject]; }
Sumit Oberoi
1

Aqui está uma pequena função útil, que facilita a utilização / criação de pastas do iOS.

Você passa o nome de uma subpasta, retornará o caminho completo para você e verifique se o diretório existe.

(Pessoalmente, uso essa função estática na minha classe AppDelete, mas talvez esse não seja o lugar mais inteligente para colocá-la.)

Aqui está como você o chamaria, para obter o "caminho completo" de um subdiretório MySavedImages:

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

E aqui está a função 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;
}

Usando essa pequena função, é fácil criar um diretório no diretório Documents do seu aplicativo (se ele ainda não existir) e gravar um arquivo nele.

Aqui está como eu criaria o diretório e gravaria nele o conteúdo de um dos meus arquivos de imagem:

//  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 isto ajude !

Mike Gledhill
fonte
0

Como outros mencionados, seu aplicativo é executado em um ambiente em área restrita e você pode usar o diretório de documentos para armazenar imagens ou outros recursos que seu aplicativo possa usar, por exemplo. download de arquivos offline-d como o usuário preferir - Noções básicas do sistema de arquivos - Documentação da Apple - Qual diretório usar, para armazenar arquivos específicos do aplicativo

Atualizado para o swift 5, você pode usar uma dessas funções, conforme o requisito -

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
fonte