Що таке каталог документів (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.