web-dev-qa-db-fra.com

Comment obtenir un chemin accessible en écriture sur l'iPhone?

Je poste cette question parce que j'avais une réponse complète écrite pour un autre post, quand j'ai trouvé que cela ne s'appliquait pas à l'original mais que je pensais que c'était trop utile pour être gaspillé. J'ai donc aussi fait de cela une communauté wiki, afin que d'autres puissent étoffer la question et les réponses. Si vous trouvez la réponse utile, veuillez voter pour la question - étant un wiki communautaire, je ne devrais pas obtenir de points pour ce vote mais cela aidera les autres à le trouver

Comment puis-je obtenir un chemin dans lequel les écritures de fichiers sont autorisées sur l'iPhone? Vous pouvez (de manière trompeuse) écrire n'importe où sur le simulateur, mais sur l'iPhone, vous n'êtes autorisé à écrire qu'à des endroits spécifiques.

Il existe trois types de chemins accessibles en écriture - le premier est Documents, où vous stockez les éléments que vous souhaitez conserver et mettre à la disposition de l'utilisateur via iTunes (à partir de 3.2):

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];

Deuxièmement, et très similaire au répertoire Documents, il y a le dossier Library, où vous stockez les fichiers de configuration et les bases de données inscriptibles que vous souhaitez également conserver, mais vous ne voulez pas que l'utilisateur puisse jouer avec iTunes:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *libraryDirectory = [paths objectAtIndex:0];

Notez que même si l'utilisateur ne peut pas voir les fichiers dans iTunes à l'aide d'un appareil antérieur à 3.2 (l'iPad), la constante NSLibraryDirectory est disponible depuis iPhoneOS 2.0, et peut donc être utilisée pour les builds ciblant 3.0 (ou même plus tôt si vous le faites encore) cette). De plus, l'utilisateur ne pourra rien voir à moins que vous ne marquiez une application comme permettant aux utilisateurs de modifier des documents, donc si vous utilisez Documents aujourd'hui, tout va bien tant que vous changez d'emplacement lors de la mise à jour pour la prise en charge des documents utilisateur.

Enfin, il y a un répertoire de cache, où vous pouvez mettre des images dont vous ne vous souciez pas d'exister à long terme ou non (le téléphone peut les supprimer à un moment donné):

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cachePath = [paths objectAtIndex:0];
BOOL isDir = NO;
NSError *error;
if (! [[NSFileManager defaultManager] fileExistsAtPath:cachePath isDirectory:&isDir] && isDir == NO) {
    [[NSFileManager defaultManager] createDirectoryAtPath:cachePath withIntermediateDirectories:NO attributes:nil error:&error];
}

Notez que vous devez réellement y créer le répertoire Caches, donc lors de l'écriture, vous devez vérifier et créer à chaque fois! Une sorte de douleur, mais c'est comme ça.

Ensuite, lorsque vous avez un chemin accessible en écriture, vous ajoutez simplement un nom de fichier comme ceci:

NSString *filePath =  [documentsDirectory stringByAppendingPathComponent:@"SomeDirectory/SomeFile.txt"];

ou

NSString *filePath =  [cachePath stringByAppendingPathComponent:@"SomeTmpFile.png"];

Utilisez ce chemin pour lire ou écrire.

Notez que vous pouvez créer des sous-répertoires dans l'un de ces chemins accessibles en écriture, que l'un des exemples de chaîne ci-dessus utilise (en supposant qu'un a été créé).

Si vous essayez d'écrire une image dans la photothèque, vous ne pouvez pas utiliser d'appels de système de fichiers pour ce faire - à la place, vous devez avoir un UIImage en mémoire et utiliser l'appel de fonction UIImageWriteToSavedPhotosAlbum() défini par UIKit. Vous n'avez aucun contrôle sur le format de destination ou les niveaux de compression et ne pouvez pas attacher aucun EXIF ​​de cette manière.

Merci à Kendall et Dave, ci-dessus, et j'ai pensé que cet amendement était utile à évoquer. Lors de l'utilisation pour un code de débogage unique, j'ai utilisé cette astuce de Mike Ash NSBlog pour éliminer les variables temporaires isDir & error, minimisant le nombre de lignes et rendant la verbosité presque supportable:

NSFileHandle *dumpFileHandle = nil;
#ifdef DEBUG
    NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    if (![[NSFileManager defaultManager] fileExistsAtPath:cachePath isDirectory:&(BOOL){0}])
        [[NSFileManager defaultManager] createDirectoryAtPath:cachePath withIntermediateDirectories:YES attributes:nil error:&(NSError*){nil}];
    NSString *dumpPath = [cachePath stringByAppendingPathComponent:@"dump.txt"];
    [[NSFileManager defaultManager] createFileAtPath:dumpPath contents:nil attributes:nil];
    [(dumpFileHandle = [NSFileHandle fileHandleForWritingAtPath:dumpPath]) truncateFileAtOffset:0];
#endif

if (dumpFileHandle) [dumpFileHandle writeData:blah];
5
Pierre Houston