web-dev-qa-db-fra.com

iOS: Impossible d'enregistrer le fichier dans le dossier "Application support", mais peut sur "Documents"

Je peux télécharger et enregistrer un fichier binaire dans le dossier "Documents" avec un nom personnalisé parfaitement bien.

Si je modifie simplement l'URL dans le dossier "Support d'application" au lieu du dossier "Documents", il ne parvient pas à écrire à cette URL en disant qu'il n'existe pas.

Voici le code de construction de l'URL:

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

    if ( [possibleURLs count] >= 1 )
    {
        appSupportDir = [possibleURLs objectAtIndex:0];
    }

    if ( appSupportDir != nil)
    {
        NSString * appBundleID = [[NSBundle mainBundle] bundleIdentifier];
        appDirectory           = [appSupportDir URLByAppendingPathComponent:appBundleID];
    }

    return appSupportDir;
}

Voici le code d'économie:

- ( void ) writeOutDataToFile:( NSData * )data
{

    NSURL * finalURL = [self.rootPathURL URLByAppendingPathComponent:self.aFileName];

    [data writeToURL:finalURL atomically:YES];
}

Si je change le Nsarray à:

NSArray * possibleURLs   = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask];

ensuite, ça sauve bien.

J'ai lu le Apple Docs sur le fichier de fichiers et ne peut pas résoudre ce problème - qu'est-ce que je manque?

33

Contrairement au répertoire Documents, le Application Support Le répertoire n'existe pas dans la boîte de sable de l'application par défaut. Vous devez le créer avant de pouvoir l'utiliser.

Et un moyen beaucoup plus simple d'obtenir une référence au répertoire est:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSString *appSupportDirectory = paths.firstObject;
46
rmaddy

Au cas où n'importe qui n'est pas sûr de faire ce que Rmaddy décrit:

NSString *appSupportDir = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) lastObject];
//If there isn't an App Support Directory yet ... 
if (![[NSFileManager defaultManager] fileExistsAtPath:appSupportDir isDirectory:NULL]) {
    NSError *error = nil;
//Create one 
    if (![[NSFileManager defaultManager] createDirectoryAtPath:appSupportDir withIntermediateDirectories:YES attributes:nil error:&error]) {
        NSLog(@"%@", error.localizedDescription);
    }
    else {
// *** OPTIONAL *** Mark the directory as excluded from iCloud backups 
        NSURL *url = [NSURL fileURLWithPath:appSupportDir];
        if (![url setResourceValue:@YES
                            forKey:NSURLIsExcludedFromBackupKey
                             error:&error])
        {
            NSLog(@"Error excluding %@ from backup %@", url.lastPathComponent, error.localizedDescription);
        }
        else {
            NSLog(@"Yay");
        }
    }
}
47
chrysAllwood

Je suis tombé sur le même problème et j'ai décidé d'utiliser une approche plus concise:

let fileManager = NSFileManager.defaultManager()
let urls = fileManager.URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask) as! [NSURL]
if let applicationSupportURL = urls.last {
    fileManager.createDirectoryAtURL(applicationSupportURL, withIntermediateDirectories: true, attributes: nil, error: nil)
}

Cela fonctionne parce que createDirectoryAtURL utilisant withIntermediateDirectories: true ne crée que le dossier s'il n'existe pas.

7
csch

Voici quelque chose Swift Code pour iOS qui peut écrire un fichier de données binaires dans le répertoire de support d'application. Les parties de ceci ont été inspirées par la réponse de Chrysallwood.

   /// Method to write a file containing binary data to the "application support" directory.
   ///
   /// - Parameters:
   ///   - fileName: Name of the file to be written.
   ///   - dataBytes: File contents as a byte array.
   ///   - optionalSubfolder: Subfolder to contain the file, in addition to the bundle ID subfolder.
   ///                        If this is omitted no extra subfolder is created/used.
   ///   - iCloudBackupForFolder: Specify false to opt out from iCloud backup for whole folder or
   ///                            subfolder. This is only relevant if this method call results in
   ///                            creation of the folder or subfolder, otherwise it is ignored.
   /// - Returns: Nil if all OK, otherwise text for a couple of non-Error errors.
   /// - Throws: Various errors possible, probably of type NSError.
   public func writeBytesToApplicationSupportFile(_ fileName : String,
                                                  _ dataBytes : [UInt8],
                                                  optionalSubfolder : String? = nil,
                                                  iCloudBackupForFolder : Bool = true)
                                                 throws -> String? {

      let fileManager = FileManager.default

      // Get iOS directory for "application support" files
      let appSupportDirectory =
                  fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first
      if appSupportDirectory == nil {
         return "Unable to determine iOS application support directory for this app."
      }

      // Add "bundle ID" as subfolder. This is recommended by Apple, although it is probably not
      // necessary.
      if Bundle.main.bundleIdentifier == nil {
         return "Unable to determine bundle ID for the app."
      }
      var mySupportDirectory =
                  appSupportDirectory!.appendingPathComponent(Bundle.main.bundleIdentifier!)

      // Add an additional subfolder if that option was specified
      if optionalSubfolder != nil {
         mySupportDirectory = appSupportDirectory!.appendingPathComponent(optionalSubfolder!)
      }

      // Create the folder and subfolder(s) as needed
      if !fileManager.fileExists(atPath: mySupportDirectory.path) {
         try fileManager.createDirectory(atPath: mySupportDirectory.path,
                                         withIntermediateDirectories: true, attributes: nil)

         // Opt out from iCloud backup for this subfolder if requested
         if !iCloudBackupForFolder {
            var resourceValues : URLResourceValues = URLResourceValues()
            resourceValues.isExcludedFromBackup = true
            try mySupportDirectory.setResourceValues(resourceValues)
         }
      }

      // Create the file if necessary
      let mySupportFile = mySupportDirectory.appendingPathComponent(fileName)
      if !fileManager.fileExists(atPath: mySupportFile.path) {
         if !fileManager.createFile(atPath: mySupportFile.path, contents: nil, attributes: nil) {
            return "File creation failed."
         }
      }

      // Write the file (finally)
      let fileHandle = try FileHandle(forWritingTo: mySupportFile)
      fileHandle.write(NSData(bytes: UnsafePointer(dataBytes), length: dataBytes.count) as Data)
      fileHandle.closeFile()

      return nil
   }
4
RenniePet

Une doublure - créera si nécessaire aussi:

[[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:nil]
2
RunLoop

Version SWIFT 4.2

    let fileManager = FileManager.default
    let urls = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask)
    if let applicationSupportURL = urls.last {
        do{
            try fileManager.createDirectory(at: applicationSupportURL, withIntermediateDirectories: true, attributes: nil)
        }
        catch{
            print(error)
        }
    }
0
Ozgur Sahin