Co to jest katalog dokumentów (NSDocumentDirectory)?

123

Czy ktoś może mi wyjaśnić, czym jest katalog dokumentów w aplikacji na iOS i kiedy go używać?

Oto, w co obecnie wierzę:

Wydaje mi się, że jest to centralny folder, w którym użytkownik może przechowywać wszystkie pliki potrzebne do aplikacji.

To byłaby inna lokalizacja niż ta, w której Core Data przechowuje swoje dane?

Wygląda na to, że każda aplikacja ma swój własny katalog dokumentów.

Mogę utworzyć podkatalog w katalogu dokumentów, taki jak katalog dokumentów / obrazy lub katalog dokumentów / filmy?

user798719
źródło
Iirc, katalog NSDocumentDirectory znajduje się w tej samej ścieżce, co podstawowe dane aplikacji, a każda aplikacja ma własny katalog dokumentów. I tak, możesz tutaj dowolnie umieścić wszystkie zasoby potrzebne do swojej aplikacji. Swoją drogą, wygląda na to, że Twoje pytanie nie zostało jeszcze zakończone?
Zekareisoujin
Właśnie opublikowałem coś, co moim zdaniem odnosi się do twojego pytania tutaj stackoverflow.com/questions/5105250/ ... sprawdź to siee, jeśli działa dla ciebie.
Wytchkraft
Dla każdego, kto pochodzi z Google, zwróć uwagę, że zmieniło się to w iOS 8. Zobacz moją odpowiedź poniżej.
livingtech
jest to ta sama lokalizacja, w której zapisany jest plik sqlite.
Anurag Bhakuni

Odpowiedzi:

197

Twoja aplikacja (na urządzeniu bez jailbreaka) działa w środowisku „piaskownicy”. Oznacza to, że może uzyskiwać dostęp tylko do plików i katalogów w ramach własnej zawartości. Na przykład Documents and Library .

Zobacz przewodnik programowania aplikacji dla systemu iOS .

Aby uzyskać dostęp do katalogu Dokumenty w piaskownicy aplikacji, możesz użyć:

iOS 8 i nowsze, jest to zalecana metoda

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

jeśli potrzebujesz obsługi iOS 7 lub wcześniejszej

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

Ten katalog Documents umożliwia przechowywanie plików i podkatalogów, które tworzy lub może potrzebować aplikacja.

Aby uzyskać dostęp do plików w katalogu biblioteki w piaskownicy aplikacji, użyj (zamiast pathspowyższego):

[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0]
WrightsCS
źródło
13
Odkryłem, że [@ "~ / Documents" stringByExpandingTildeInPath] robi to samo. Czy jest jakiś powód, dla którego należy to zniechęcać?
Cthutu
35
Nie użyłbym tego podejścia z @ "~ / Documents". Twarde ścieżki nigdy nie są dobrym pomysłem. Może teraz działać, ale jeśli Apple kiedykolwiek zdecyduje się zmienić nazwę lub przenieść katalog Dokumenty, Twoja aplikacja się zepsuje. NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);zawsze poda poprawny katalog!
16
nadal powinieneś używać dostarczonego API. Dlatego tam jest! Po prostu miałeś szczęście do tej pory.
nielsbot
20
Zabawne, jak muszę to wygooglować za każdym razem, gdy muszę go użyć. Myślę, że wszystkie platformy mobilne powinny zapewniać domyślny parametr globalny dla katalogu domowego / katalogu z możliwością zapisu
Ravindranath Akila,
1
zaktualizowany link z informacjami na temat możliwości zapisywania przez aplikację w folderach: developer.apple.com/library/mac/documentation/FileManagement/ ...
phil
43

Zmieniło się to w iOS 8. Zobacz następującą uwagę techniczną: https://developer.apple.com/library/ios/technotes/tn2406/_index.html

Sankcjonowany sposób Apple (z powyższego linku) jest następujący:

// Returns the URL to the application's Documents directory.
- (NSURL *)applicationDocumentsDirectory
{
    return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
livingtech
źródło
7
To jest odpowiedź, której szukasz! Mamy prawie 2016 rok. To popularne pytanie z przestarzałymi odpowiedziami.
Jeff,
Czy mogę użyć powyższej metody, aby pobrać ścieżkę do katalogu dokumentów? jak url.path?
Abuzar Amin
21

Nie mogłem znaleźć kodu w dokumencie sugerowanym przez zaakceptowaną odpowiedź, ale znalazłem zaktualizowany odpowiednik tutaj:

Podręcznik programowania systemu plików :: Dostęp do plików i katalogów »

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

Odradza korzystanie z NSSearchPathForDirectoriesInDomain:

Funkcja NSSearchPathForDirectoriesInDomains zachowuje się jak metoda URLsForDirectory: inDomains:, ale zwraca lokalizację katalogu jako ścieżkę opartą na ciągach znaków. Zamiast tego należy użyć metody URLsForDirectory: inDomains:.

Oto kilka innych przydatnych stałych katalogowych do zabawy. Bez wątpienia nie wszystkie z nich są obsługiwane w systemie iOS. Możesz także użyć funkcji NSHomeDirectory (), która:

W systemie iOS katalog domowy to katalog piaskownicy aplikacji. W systemie OS X jest to katalog piaskownicy aplikacji lub katalog domowy bieżącego użytkownika (jeśli aplikacja nie znajduje się w piaskownicy)

Z 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

I na koniec kilka wygodnych metod w kategorii NSURL http://club15cc.com/code/ios/easy-ios-file-directory-paths-with-this-handy-nsurl-category

Hari Karam Singh
źródło
8

Swift 3 i 4 jako zmienna globalna:

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

Jako rozszerzenie 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
źródło
Dzięki. Zawsze o tym zapominam. :) Jeśli chcesz trzymać się mocno wytycznych projektowania API, możesz nazwać go documentDirectoryURLlub po prostu documentDirectoryi polegać na typie. Podoba mi się pomysł statycznego ograniczania go do FileManagerstatycznej właściwości w rozszerzeniu.
Ray Fix
1
Dzięki @RayFix, zaktualizowałem moją odpowiedź twoją sugestią!
Anton Plebanovich
6

Dodanie rozszerzenia do FileManagera dla tego rodzaju niezręcznego wywołania może być czystsze, jeśli nic więcej, dla porządku. Coś jak:

extension FileManager {
    static var documentDir : URL {
        return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    }
}
Paul Robinson
źródło
4

Możesz uzyskać dostęp do katalogu dokumentów za pomocą tego kodu, który jest zasadniczo używany do przechowywania pliku w formacie plist:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths firstObject];
return documentsDirectory;
Sumit Oberoi
źródło
Ta sama odpowiedź została udzielona 3 lata wcześniej przez WrightsCS. Również udzielenie tej odpowiedzi w 2014 roku jest dziwne, biorąc pod uwagę, że Apple zaleca metodę w odpowiedzi livingtech.
Jeff,
Jeśli uważasz, że błędnie odmawiam głosowania, proszę wyjaśnij dlaczego. Głosowanie w sprawie zemsty na jednym z moich pytań jest dziecinne. Ta strona jest o popychaniu najlepszych odpowiedzi na szczyt.
Jeff
@jeff dziękuję za wskazanie, zebrałeś informacje i miałeś rację. nowe rozwiązanie: - (NSURL *) applicationDocumentsDirectory {return [[[NSFileManager defaultManager] URLsForDirectory: NSDocumentDirectory inDomains: NSUserDomainMask] lastObject]; }
Sumit Oberoi,
1

Oto przydatna mała funkcja, która sprawia, że ​​używanie / tworzenie folderów iOS jest trochę łatwiejsze.

Przekażesz mu nazwę podfolderu, zwróci pełną ścieżkę z powrotem do Ciebie i upewni się, że katalog istnieje.

(Osobiście trzymam tę statyczną funkcję w mojej klasie AppDelete, ale być może nie jest to najmądrzejsze miejsce, aby ją umieścić.)

Oto jak można to nazwać, aby uzyskać „pełną ścieżkę” do podkatalogu MySavedImages:

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

A oto pełna funkcja:

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

Korzystając z tej małej funkcji, łatwo jest utworzyć katalog w katalogu Dokumenty aplikacji (jeśli jeszcze nie istnieje) i zapisać w nim plik.

Oto jak utworzę katalog i zapiszę w nim zawartość jednego z moich plików graficznych:

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

Mam nadzieję że to pomoże !

Mike Gledhill
źródło
0

Podobnie jak inni wspomnieli, Twoja aplikacja działa w środowisku piaskownicy i możesz używać katalogu dokumentów do przechowywania obrazów lub innych zasobów, których może używać Twoja aplikacja, np. pobieranie plików offline-d zgodnie z preferencjami użytkownika - Podstawy systemu plików - Dokumentacja Apple - Katalog, którego należy użyć do przechowywania plików specyficznych dla aplikacji

Zaktualizowany do Swift 5, możesz użyć jednej z tych funkcji, zgodnie z wymaganiami -

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

Stosowanie:

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
źródło