Si les tutoriels sur les cas dâusage simples sont nombreux, rares sont ceux qui couvrent les subtilitĂ©s liĂ©es Ă lâimport de fichiers sur iOS. Avec UIKit, et notamment sur les anciennes versions dâiOS (iOS 12+), on peut vite se retrouver limitĂ© ou perdu.
Intro, la base, récupérer un fichier
1. Déclarer le picker et utiliser la delegation
Dans votre ViewController, déclarez un UIDocumentPickerViewController et conformez votre ViewController au protocole UIDocumentPickerDelegate.
import UIKit
class ViewController: UIViewController {
    var documentPicker: UIDocumentPickerViewController!
    override func viewDidLoad() {
        super.viewDidLoad()
        if #available(iOS 14.0, *) {
            documentPicker = UIDocumentPickerViewController(forOpeningContentTypes: importableFileTypes)
        } else {
            documentPicker = UIDocumentPickerViewController(documentTypes: getLegacyImportableFileType(), in: .open)
        }
        documentPicker.delegate = self
        documentPicker.allowsMultipleSelection = false
        present(documentPicker, animated: true)
    }
}
Ce code devrait vous permettre dâouvrir un document picker pour importer un fichier sur iOS.
2. Exploiter les URLs retournées par le systÚme
func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
    urls.forEach { url in // Ici, on aura en général un seul fichier, mais on peut en avoir plusieurs
    guard url.startAccessingSecurityScopedResource() else { return }
    defer { url.stopAccessingSecurityScopedResource() }
    do {
        try handleSelectedFileFromPicker(importedFileUrl: url)
    } catch {
       // Handle error
    }
}3. Récupérer les informations du fichier
Ici on va récupérer les informations du fichier, et le stocker dans un dossier temporaire. Les informations récupérées sont :
- Le nom du fichier
- La taille du fichier
- La date de derniĂšre modification
On peut aussi récupérer le type de fichier (
UTType), le MIME type, etc. LâAPIURLResourceValuesest suffisamment riche pour couvrir la majoritĂ© des besoins..
// handleSelectedFileFromPicker(importedFileUrl: URL) throws
let resourceValues: URLResourceValues
let mimeType: String?
let fileType: String?
var resourceKeys: Set<URLResourceKey> = [.fileSizeKey, .nameKey, .creationDateKey, .contentModificationDateKey]
// Si on est sur iOS 14 ou plus, on peut récupérer le MIME type
resourceKeys.insert(.contentTypeKey)
resourceValues = try url.resourceValues(forKeys: resourceKeys)
guard let contentType = resourceValues.contentType else { throw InvalidMimeType }
mimeType = contentType.preferredMIMEType
fileType = contentType.localizedDescription
//
guard let fileSize = resourceValues.fileSize else { throw InvalidSizeOrNull() }
guard let fileName = resourceValues.name else { throw InvalidName() }
let lastModificationDate = resourceValues.contentModificationDate ?? resourceValues.creationDate
let data = try Data(contentsOf: url, options: .mappedIfSafe)
// On peut stocker le fichier dans un dossier temporaire pour une utilisation ultérieure
let tempUrl = data.writeToTempDirectory(fileName: fileName, mimeType: MimeType.get(from: mimeType))Les subtilités
Les versions dâiOS
UTType est disponible Ă partir dâiOS 14, et il est recommandĂ© de lâutiliser pour dĂ©terminer le type de fichier.
Dans le code précédent on a utilisé importableFileTypes qui est un tableau de UTType pour déterminer les types de fichiers importables.
@available(iOS 14.0, *)
var importableFileTypes: [UTType] {
    [
        .image,
        .video,
        .audio,
        .text,
        .presentation,
        .spreadsheet,
        .epub
        .delimitedText
    ]
}Attention :
.presentationou.spreadsheetne suffisent pas Ă autoriser les fichiers.pptxou.xlsx.
Il faut créer manuellement les UTType correspondants, car ils ne sont pas fournis par défaut par Apple.
Le legacy < iOS 14.0
Pour la science le legacyImportableFileType est une fonction qui retourne un tableau de String utilisant les kUTType de lâAPI CoreFoundation.
@available(iOS, deprecated: 14.0, message: "To be removed starting iOS 14")
func getLegacyImportableFileType() -> [String] {
        return [
            String(kUTTypePDF),
            String(kUTTypeAudio),
            String(kUTTypeVideo),
            String(kUTTypeHTML),
            String(kUTTypeSpreadsheet),
            String(kUTTypeText),
            String(kUTTypePlainText),
            String(kUTTypePresentation),
            String(kUTTypeImage)
        ]
}Attention Ă  startAccessingSecurityScopedResource()
Il est important de bien appeler startAccessingSecurityScopedResource() sur lâURL retournĂ© par le document picker, pour pouvoir accĂ©der au contenu du fichier.
Attention à ne pas oublier de bien appeler stopAccessingSecurityScopedResource() pour libérer les ressources allouées par le systÚme.
Mais si vous le faites trop tĂŽt, vous nâaurez pas accĂšs au contenu du fichier.
Lâextension est reine
On pourrait croire que les OS rĂ©cents vĂ©rifient dâune maniĂšre moins triviale les types des fichiers, mais en fait ils ne vĂ©rifient pas le contenu du fichier pour dĂ©terminer le type. MĂȘme pas une en-tĂȘte de fichier, ou un checksum.
Ils utilisent lâextension du fichier pour dĂ©terminer le type (.jpeg, .jpg, .png, .docx, .pdf, etcâŠ) Et cotĂ© web le standard câest le
mimeTypedoncimage/jpegouimage/jpg
Sur les systĂšmes dâApple, ils utilisent les UTType pour dĂ©terminer le type de fichier, et ils ont une liste de type de fichier dĂ©jĂ  dĂ©clarĂ© par Apple, mais on peut aussi en dĂ©clarer nous mĂȘme, et mĂȘme les Ă©tendre.
UTType pour âUniform Type Identifierâ qui possĂšde plusieurs niveaux dâabstration pour reprĂ©senter un type de fichier :

Exemple :
- Un fichier
file.jpegest un type dâimage,que lâon pourrait noterfile.imageavec UTType, utile quand on veut importer un fichier image, on peut importer nâimporte quel type dâimage.- Mais câest aussi un fichier âbinaireâ, donc
file.dataest aussi valide en terme dâabstraction, enfin câest un fichierJPEG, doncfile.jpegest aussi valide.
On peut donc utiliser les UTType pour dĂ©terminer le type de fichier, et donc le type de fichier que lâon veut importer. Ils ont dĂ©jĂ une liste de type dĂ©clarĂ© par Apple, mais on peut aussi en dĂ©clarer nous mĂȘme, et mĂȘme les Ă©tendre.
Utiliser les documents Office Ă partir de 2007
Une plaie Ă faire ? Oui.
Il nâexiste quasiment aucune documentation officielle, ni chez Apple ni chez Microsoft, sur lâimport de fichiers .docx, .pptx ou .xlsx dans une app iOS.
docx: org.openxmlformats.wordprocessingml.document
pptx: org.openxmlformats.presentationml.presentation
xlsx: org.openxmlformats.spreadsheetml.sheetA bien mettre dans XCode dans les UTType importable.
Puis dans votre code rajouter les types comme types importable comme ceci :
import Foundation
import UniformTypeIdentifiers
@available(iOS 14.0, *)
extension UTType {
    public static var word: UTType {
        UTType(importedAs: "org.openxmlformats.wordprocessingml.document")
    }
    public static var excel: UTType {
        UTType(importedAs: "org.openxmlformats.spreadsheetml.sheet")
    }
    public static var powerpoint: UTType {
        UTType(importedAs: "org.openxmlformats.presentationml.presentation")
    }
}
Fichiers exotiques
Si vous devez gérer des fichiers exotiques, des extensions maisons, des mimeType obscurs, vous pouvez déclarer vos propres UTType. Pour cela, il suffit de déclarer un UTType comme ceci :
import Foundation
import UniformTypeIdentifiers
@available(iOS 14.0, *)
extension UTType {
    public static var myCustomType: UTType {
        UTType(importedAs: "com.mycompany.myCustomType")
    }
}Beaucoup dâinformations sur les UTType sont disponibles ici : https://developer.apple.com/videos/play/tech-talks/10696
Conclusion
Importer un fichier sur iOS avec UIKit est pas si trivial, surtout si lâon veut faire les choses proprement, de façon rĂ©tro-compatible, et en respectant les rĂšgles de sandboxing dâiOS. En comprenant bien le fonctionnement des UTType, et en gĂ©rant correctement les ressources sĂ©curisĂ©es, on peut proposer une UX robuste pour lâimport de fichiers dans nâimporte quelle app.