I'm trying to get some photos (PHAsset
's) from a user's Photos library, and convert them to UIImage
's.
Here's the code for fetching the assets from the library:
func fetchPhotos() {
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "(mediaSubtype & %d) != 0", PHAssetMediaSubtype.photoScreenshot.rawValue)
//Fetching Screen Shots
fetchedResult = PHAsset.fetchAssets(with: .image, options: fetchOptions)
let f: FetchResult = FetchResult(fetchedResult!)
var fetchCount = 0
for i in f {
fetchCount += 1
print("Fetch count \(fetchCount)")
let img = i.getAssetThumbnail()
testImages.append(img)
}
}
Below is the code for getting a UIImage
out of a PHAsset
:
extension PHAsset {
func getAssetThumbnail() -> UIImage {
let manager = PHImageManager.default()
let option = PHImageRequestOptions()
var thumbnail = UIImage()
option.isSynchronous = true
manager.requestImage(for: self,
targetSize: CGSize(width: self.pixelWidth, height: self.pixelHeight),
contentMode: .aspectFit,
options: option,
resultHandler: {(result, info) -> Void in
thumbnail = result!
})
return thumbnail
}
}
I'm currently getting this crash output when trying to run the code:
Details
The app “AppClassificationDemo” on Nick’s iPhone quit unexpectedly.
Domain: IDEDebugSessionErrorDomain
Code: 11
Failure Reason: Message from debugger: Terminated due to memory issue
User Info: {
DVTErrorCreationDateKey = "2022-11-14 22:19:19 +0000";
IDERunOperationFailingWorker = DBGLLDBLauncher;
}
--
Analytics Event: com.apple.dt.IDERunOperationWorkerFinished : {
"device_model" = "iPhone14,2";
"device_osBuild" = "16.1.1 (20B101)";
"device_platform" = "com.apple.platform.iphoneos";
"launchSession_schemeCommand" = Run;
"launchSession_state" = 2;
"launchSession_targetArch" = arm64;
"operation_duration_ms" = 6369;
"operation_errorCode" = 11;
"operation_errorDomain" = IDEDebugSessionErrorDomain;
"operation_errorWorker" = DBGLLDBLauncher;
"operation_name" = IDEiPhoneRunOperationWorkerGroup;
"param_consoleMode" = 0;
"param_debugger_attachToExtensions" = 0;
"param_debugger_attachToXPC" = 1;
"param_debugger_type" = 5;
"param_destination_isProxy" = 0;
"param_destination_platform" = "com.apple.platform.iphoneos";
"param_diag_MainThreadChecker_stopOnIssue" = 0;
"param_diag_MallocStackLogging_enableDuringAttach" = 0;
"param_diag_MallocStackLogging_enableForXPC" = 1;
"param_diag_allowLocationSimulation" = 1;
"param_diag_checker_tpc_enable" = 1;
"param_diag_gpu_frameCapture_enable" = 0;
"param_diag_gpu_shaderValidation_enable" = 0;
"param_diag_gpu_validation_enable" = 0;
"param_diag_memoryGraphOnResourceException" = 0;
"param_diag_queueDebugging_enable" = 1;
"param_diag_runtimeProfile_generate" = 0;
"param_diag_sanitizer_asan_enable" = 0;
"param_diag_sanitizer_tsan_enable" = 0;
"param_diag_sanitizer_tsan_stopOnIssue" = 0;
"param_diag_sanitizer_ubsan_stopOnIssue" = 0;
"param_diag_showNonLocalizedStrings" = 0;
"param_diag_viewDebugging_enabled" = 1;
"param_diag_viewDebugging_insertDylibOnLaunch" = 1;
"param_install_style" = 0;
"param_launcher_UID" = 2;
"param_launcher_allowDeviceSensorReplayData" = 0;
"param_launcher_kind" = 0;
"param_launcher_style" = 0;
"param_launcher_substyle" = 0;
"param_runnable_appExtensionHostRunMode" = 0;
"param_runnable_productType" = "com.apple.product-type.application";
"param_runnable_type" = 2;
"param_testing_launchedForTesting" = 0;
"param_testing_suppressSimulatorApp" = 0;
"param_testing_usingCLI" = 0;
"sdk_canonicalName" = "iphoneos16.1";
"sdk_osVersion" = "16.1";
"sdk_variant" = iphoneos;
}
--
System Information
macOS Version 13.0.1 (Build 22A400)
Xcode 14.1 (21534.1) (Build 14B47b)
Timestamp: 2022-11-14T14:19:19-08:00
I'm fairly certain it's do the size of the image thumbnail being generated; when I set width
and height
to both be 100, the processing works as expected.
When changing it self.pixelWidth
and self.pixelHeight
, the app crashes.
Don't try to convert the PHAsset
to UIImage
and store in array. Because UIImage
will use the full memory size of the image. So too many images in an array will cause a memory leak.
Hence just fetch the required PHAsset
on demand and request for the UIImage
when needed. Please see an example below.
func loadImageAt(_ index:Int) -> UIImage?{
let asset = fetchResult.object(at: index)
let manager = PHImageManager.default()
let option = PHImageRequestOptions()
var thumbnail:UIImage?
option.isSynchronous = true
manager.requestImage(for: asset,
targetSize: CGSize(width: asset.pixelWidth, height: asset.pixelHeight),
contentMode: .aspectFit,
options: option,
resultHandler: {(result, info) -> Void in
thumbnail = result
})
return thumbnail
}
Also I will suggest you to use the asynchronous image request to avoid the UI performance issues.
Hope this helps!