You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 13:49:54