iOS Share Extension Se Fermait Après 2 Secondes : La Solution au OOM Kill
En développant SnapPress (une app pour uploader des photos depuis l'iPhone directement vers WordPress), j'ai ajouté une Share Extension pour que les utilisateurs puissent partager des photos depuis l'app Photos sans ouvrir SnapPress au préalable.
L'extension fonctionnait. La plupart du temps. Parfois, elle s'ouvrait, affichait un spinner de chargement pendant 2 secondes, puis disparaissait sans laisser de trace. Aucun crash log dans Xcode. Aucun stack trace. Aucun message d'erreur. Juste disparu.
Share Extension vs Action Extension
Une Action Extension (com.apple.ui-services) apparaît dans la rangée d'icônes du share sheet. Elle est conçue pour transformer ou agir sur le contenu sur place.
Une Share Extension (com.apple.share-services) apparaît dans la liste des activités du share sheet. Elle est conçue pour envoyer du contenu quelque part (un réseau social, un CMS, une app de prise de notes). Elle s'exécute comme une mini-app indépendante avec sa propre UI, son propre processus et ses propres limites mémoire.
Le Crash : « Invalidation Requested »
Le premier indice est venu de Console.app sur Mac. Avec l'iPhone connecté et en filtrant par le nom du processus de l'extension, j'ai trouvé ceci dans sharingd :
MetricEvent 'com.apple.sharing.sharesheetCompleted' : {
"success" : false,
"totalShareTimeMs" : 2700,
"activityType" : "app.snappress.SnapPress.ShareExtension",
} Et dans Photos :
View service session ended with error:
_UIViewServiceHostSessionErrorDomain Code=4
UserInfo={Message=Invalidation requested} Invalidation requested signifie que le processus de l'extension s'est terminé de manière inattendue. totalShareTimeMs: 2700 signifie qu'il a vécu exactement 2,7 secondes. C'est la signature d'un OOM kill. iOS termine silencieusement les processus qui dépassent la limite mémoire, sans crash log ni signal.
Cause Racine : UIImage(data:) Décode en Pleine Résolution en Mémoire
La Share Extension charge des images depuis NSItemProvider, les traite et les uploade vers WordPress. L'étape de traitement appelait cette méthode :
static func process(_ imageData: Data, quality: ImageQuality) -> Data? {
guard let image = UIImage(data: imageData) else { return nil }
let resized = resize(image, maxDimension: quality.maxDimension)
return resized.jpegData(compressionQuality: quality.compressionQuality)
} Le problème est UIImage(data: imageData). Cela décode le fichier image compressé en un buffer de pixels pleine résolution. Une photo iPhone de 12 mégapixels (4032 × 3024 pixels) s'étend à :
4032 × 3024 × 4 octets (RGBA) = ~47 Mo Avec la copie redimensionnée, le buffer de sortie JPEG, le container SwiftData et les miniatures, un seul upload peut atteindre 60 à 80 Mo. iOS donne aux Share Extensions environ 120 à 150 Mo. Avec quelques grandes photos HEIC, cette limite est vite dépassée.
La Solution : Décoder Directement à la Taille Cible avec CGImageSource
CGImageSource peut faire du downsampling lors du décodage, produisant l'image à la taille cible sans jamais créer le buffer pleine résolution en mémoire.
static func process(_ imageData: Data, quality: ImageQuality) -> Data? {
let maxDim = Int(quality.maxDimension)
let options: [CFString: Any] = [
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceThumbnailMaxPixelSize: maxDim,
kCGImageSourceCreateThumbnailWithTransform: true,
kCGImageSourceShouldCacheImmediately: false,
]
guard
let source = CGImageSourceCreateWithData(imageData as CFData, nil),
let cgImage = CGImageSourceCreateThumbnailAtIndex(source, 0, options as CFDictionary)
else { return nil }
return UIImage(cgImage: cgImage).jpegData(compressionQuality: quality.compressionQuality)
} L'utilisation mémoire par image passe de ~60 Mo à ~6 Mo. Une réduction de 10x. La Share Extension n'a plus planté depuis.
Résumé
Si votre Share Extension iOS plante après 2 à 3 secondes sans crash log, c'est presque certainement un OOM kill. Remplacez UIImage(data:) par CGImageSourceCreateThumbnailAtIndex et définissez kCGImageSourceThumbnailMaxPixelSize à votre dimension cible.
SnapPress est disponible sur l'App Store.