Що таке каталог документів (NSDocumentDirectory)?


123

Чи може хтось мені пояснити, що таке каталог документів у додатку для iOS та коли ним користуватися?

Ось що я вважаю в даний час:

Мені здається, це центральна папка, де користувач може зберігати будь-які файли, необхідні для програми.

Це було б інше місце, ніж там, де Core Data зберігає свої дані?

Схоже, кожен додаток отримує власну довідку документів.

Я вільний створити підкаталог каталогів документів, наприклад, каталог / образи документів або каталог / відеодокументи?


Iirc, NSDocumentDirectory лежить таким же шляхом, як і основні дані програми, і для кожного додатка є своя директорія документів. І так, ви можете вільно помістити сюди всі необхідні ресурси для свого додатка. До речі, здається, ваше питання ще не закінчено?
Zekareisoujin

Я тільки що відправив що - то я думаю , що має відношення до вашого запитання тут stackoverflow.com/questions/5105250 / ... перевірити його SIEE , якщо він працює для вас.
Wytchkraft

Для всіх, хто приходить з google, зверніть увагу, що це змінилося в iOS 8. Дивіться мою відповідь нижче.
livingtech

це те саме місце, де зберігається ваш файл sqlite.
Анураг Бхакуні

Відповіді:


197

Ваш додаток (лише на пристрої, що не працює з в'язницею) працює в середовищі "пісочного". Це означає, що він може отримувати доступ лише до файлів і каталогів у межах власного вмісту. Наприклад, Документи та бібліотека .

Дивіться Посібник з програмування додатків iOS .

Для доступу до каталогу Документи пісочної програми ваших програм, ви можете використовувати наступне:

iOS 8 і новіші - це рекомендований метод

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

якщо вам потрібно підтримувати iOS 7 або новішу версію

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

Це Документи каталог дозволяє зберігати файли та підкаталоги, які ваш додаток створює або може знадобитися.

Щоб отримати доступ до файлів у каталозі бібліотеки вашої програми, використовуйте пісочницю (замість pathsвище):

[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0]

13
Я виявив, що [@ "~ / Documents" stringByExpandingTildeInPath] робить те саме. Чи є якась причина, що це слід відмовити?
Cthutu

35
Я б не використовував підхід із @ "~ / Documents". Шляхи жорсткого кодування ніколи не є хорошою ідеєю. Це може працювати зараз, але якщо Apple коли-небудь вирішить перейменувати або перемістити каталог Документи, ваш додаток буде зламано. NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);завжди надасть вам правильний каталог!

16
ви все одно повинні використовувати наданий API. Ось чому це там! Вам просто пощастило до цих пір.
nielsbot

20
Веселий, як мені потрібно це гугл кожен раз, коли мені доведеться його використовувати. Я думаю, що всі мобільні платформи мають забезпечити глобальний параметр за замовчуванням для домашнього / записуваного каталогу
Ravindranath Akila

1
оновлене посилання з інформацією про можливість програми писати в папки: developer.apple.com/library/mac/documentation/FileManagement/…
phil

43

Це змінилося в iOS 8. Див. Наступну технічну примітку: https://developer.apple.com/library/ios/technotes/tn2406/_index.html

Яблучний санкціонований спосіб (за посиланням вище) полягає в наступному:

// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory
{
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}

7
Це відповідь, яку ви хочете! Рік майже 2016 рік. Це популярне запитання із застарілими відповідями.
Джефф

Чи можу я використовувати вищевказаний метод для отримання шляху до каталогу документів? як url.path?
Абузар Амін

21

Я не зміг знайти код у документі, запропонованому прийнятою відповіддю, але я знайшов оновлений еквівалент тут:

Посібник з програмування файлової системи :: Доступ до файлів і каталогів »

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

Це перешкоджає використанню NSSearchPathForDirectoriesInDomain:

Функція NSSearchPathForDirectoriesInDomains поводиться як метод URLsForDirectory: inDomains:, але повертає розташування каталогу як шлях на основі рядків. Ви повинні використовувати метод URLsForDirectory: inDomains:.

Ось кілька інших корисних констант каталогів, з якими можна грати. Без сумніву, не все це підтримується в iOS. Також ви можете використовувати функцію NSHomeDirectory (), яка:

В iOS домашній каталог - це каталог пісочниці програми. В OS X це каталог пісочних програм або домашній каталог поточного користувача (якщо програма не знаходиться в пісочниці)

Від 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

І нарешті, деякі методи зручності в категорії NSURL http://club15cc.com/code/ios/easy-ios-file-directory-paths-with-this-handy-nsurl-category


8

Swift 3 і 4 як глобальний варіант:

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

Як розширення 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!
    }
}

Дякую. Я завжди це забуваю. :) Якщо ви хочете швидко дотримуватися рекомендацій щодо дизайну API, ви можете назвати його documentDirectoryURLабо просто documentDirectoryта покластися на тип. Мені подобається ідея ввести його FileManagerв статичне значення, хоч статичне властивість у розширенні.
Рей Фікс

1
Дякую @RayFix, оновив мою відповідь вашою пропозицією!
Антон Плебанович


6

Можна чистіше додати розширення до FileManager для такого роду незручних дзвінків, для охайності, якщо нічого іншого. Щось на зразок:

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

4

Ви можете отримати доступ до каталогу документів за допомогою цього коду, він в основному використовується для зберігання файлу у форматі плістів:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths firstObject];
return documentsDirectory;

Таку саму відповідь WrightsCS отримав за три роки. Крім того, давати цю відповідь у 2014 році дивно, оскільки Apple рекомендує метод у відповіді Livingtech.
Джефф

Якщо ви вважаєте, що я неправильно проголосував, тоді поясніть, чому. Голосова помста за одне з моїх питань - по-дитячому. Цей веб-сайт про те, щоб підняти найкращі відповіді до початку.
Джефф

@jeff дякую, що вказали, зробили кілька досліджень і ви мали рацію. нове рішення: - (NSURL *) applicationDocumentsDirectory {return [[[[NSFileManager defaultManager] URLsForDirectory: NSDocumentDirectory inDomains: NSUserDomainMask] lastObject]; }
Суміт Оберой

1

Ось маленька корисна функція, яка полегшує використання / створення папок iOS.

Ви передасте йому ім'я підпапки, вона поверне вам повний шлях і переконається, що каталог існує.

(Особисто я зберігаю цю статичну функцію в своєму класі AppDelete, але, можливо, це не найрозумніше місце для цього.)

Ось як би ви його назвали, щоб отримати "повний шлях" підкаталога MySavedImages:

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

І ось повна функція:

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

Використовуючи цю маленьку функцію, легко створити каталог у каталозі Документи програми (якщо він ще не існує) та записати файл у нього.

Ось як я створив би каталог і записував у нього вміст одного з моїх файлів зображень:

//  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];

Сподіваюся, це допомагає!


0

Як і інші згадані, ваш додаток працює в середовищі з піском, і ви можете використовувати каталог документів для зберігання зображень або інших активів, які може використовувати ваш додаток, наприклад. завантаження файлів офлайн-d як користувач віддає перевагу - Основи файлової системи - Документація Apple - Який каталог використовувати для зберігання конкретних файлів програми

Оновлений до швидкого доступу 5, ви можете використовувати одну з цих функцій відповідно до вимог -

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]
    }

Використання:

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
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.