Swift自定义IntentHandler批量设置INImage后选项加载失败
解决方案:Widget Intent动态选项加载失败(大量带图选项)
问题根源
这种情况大概率是Intent扩展的内存配额限制或Intent响应payload数据量超限导致的:Widget和Intent扩展的内存预算远低于主App,大量带图片的选项会快速耗尽内存;同时Intent传递的数据有大小上限,全量带图的选项集合容易超出阈值,触发加载失败。
具体解决方向&代码优化
1. 压缩图片大小,减少单图数据量
Widget Intent选项的图标不需要高清,优先压缩到最小可用尺寸(比如24x24/32x32),并降低图片质量:
func provideSelectedProjectsOptionsCollection(for intent: SpecificWidgetIntent) async throws -> INObjectCollection<SelectableProject> { // 加载图片并压缩到指定尺寸 guard let originalImage = UIImage(named: "AvengersIcon"), let resizedImage = originalImage.resize(to: CGSize(width: 32, height: 32)), let imageData = resizedImage.jpegData(compressionQuality: 0.3), let smallINImage = INImage(data: imageData) else { throw NSError(domain: "IntentError", code: 1, userInfo: [NSLocalizedDescriptionKey: "图片加载失败"]) } var projects = await ProjectService.getAll(populate: .populateNone) .compactMap { let proj = SelectableProject(identifier: "mcu-\($0.id)", display: $0.attributes.title, subtitle: "MCU", image: smallINImage) proj.sortKey = "MCU-\($0.attributes.title)" return proj } projects += await ProjectService.getAllOther(populate: .populateNone) .compactMap { let proj = SelectableProject(identifier: "other-\($0.id)", display: $0.attributes.title, subtitle: "Related to MCU", image: smallINImage) proj.sortKey = "Other-\($0.attributes.title)" return proj } projects.sort { $0.sortKey ?? "" < $1.sortKey ?? "" } return INObjectCollection(items: projects) } // UIImage尺寸调整扩展 extension UIImage { func resize(to size: CGSize) -> UIImage? { UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale) defer { UIGraphicsEndImageContext() } draw(in: CGRect(origin: .zero, size: size)) return UIGraphicsGetImageFromCurrentImageContext() } }
2. 复用图片实例,避免内存冗余
确保所有选项共用同一个INImage实例(上述代码已实现),不要为每个选项重新创建图片对象,减少内存中重复的资源占用。
3. 检查Intent扩展的资源访问权限
确保AvengersIcon图片资源被添加到Intent扩展的Target中,而不是仅存在于主App里——扩展无法直接访问主App的私有资源。
4. 用App Group共享图片,延迟加载
如果压缩后还是超限,可以把图片放到App Group共享容器,用INImage(url:)指向共享路径,让系统按需加载图片,而非一次性打包所有图片数据:
// 主App提前将压缩后的图片写入共享目录 let sharedContainerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "your.app.group.id")! let imageURL = sharedContainerURL.appendingPathComponent("AvengersIcon_small.jpg") // try imageData.write(to: imageURL) // Intent扩展中使用URL创建图片 let sharedImage = INImage(url: imageURL) sharedImage.shouldSaveInStore = true // 让系统缓存图片,避免重复加载
5. 排查内存溢出
用Xcode的Instruments > Memory Graph Debugger运行Intent扩展,查看加载70个选项时的内存峰值——如果内存超过30MB左右,基本就是内存触发了扩展的崩溃限制,需要进一步精简数据(比如去掉不必要的属性)。
内容的提问来源于stack exchange,提问作者Bas
相关产品推荐
相关产品推荐

