Xcode 中 Share Extension 原理
核心原理
Share Extension 机制
Share Extension 是 iOS 的系统级扩展(App Extension),允许应用在系统分享菜单中接收其他应用(如相册、Safari)的数据。当用户选择截图并点击分享按钮时,系统会唤起你的 Share Extension。
数据传递流程
宿主应用(如相册)提供 NSExtensionItem 对象,包含截图的附件数据(NSItemProvider)。
Share Extension 通过 extensionContext 获取这些数据,解析为 UIImage 或文件 URL。
与主应用共享数据:通过 App Group 的共享容器(FileManager)或 UserDefaults 传递数据。
沙盒隔离与通信
Share Extension 运行在独立进程,需通过以下方式与主应用通信:
App Groups:共享文件容器(如 containerURL(forSecurityApplicationGroupIdentifier:))。
Keychain Sharing:共享敏感数据(如用户凭证)。
使用流程详解
步骤 1:创建 Share Extension Target
在 Xcode 项目中:
File → New → Target → iOS → Share Extension
输入扩展名称(如 ScreenshotShareExtension),激活 Scheme。
步骤 2:配置 App Group
启用 App Group
主应用 Target → Signing & Capabilities → + Capability → App Groups
Share Extension Target → 重复上述操作。
创建共享 Group ID
格式:group.com.yourcompany.appname
确保主应用和扩展勾选相同的 Group ID。
步骤 3:配置 Share Extension 的 Info.plist
修改 Info.plist 中的 NSExtension 字段:
步骤 4:实现 Share Extension 逻辑
在 ShareViewController.swift 中处理截图数据:
class ShareViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
handleSharedImage()
}
private func handleSharedImage() {
guard let extensionItem = extensionContext?.inputItems.first as? NSExtensionItem,
let itemProvider = extensionItem.attachments?.first else {
extensionContext?.cancelRequest(withError: NSError(domain: "com.your.app", code: 0))
return
}
// 检查是否为图片
if itemProvider.hasItemConformingToTypeIdentifier("public.image") {
itemProvider.loadItem(forTypeIdentifier: "public.image", options: nil) { [weak self] (item, error) in
guard let self = self, error == nil else { return }
DispatchQueue.main.async {
if let url = item as? URL, let image = UIImage(contentsOfFile: url.path) {
self.saveImageToAppGroup(image) // 保存到共享容器
} else if let image = item as? UIImage {
self.saveImageToAppGroup(image)
}
self.extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
}
}
}
}
private func saveImageToAppGroup(_ image: UIImage) {
guard let data = image.pngData(),
let groupContainer = FileManager.default.containerURL(
forSecurityApplicationGroupIdentifier: "group.com.yourcompany.appname"
) else { return }
let imagePath = groupContainer.appendingPathComponent("screenshot_\(Date().timeIntervalSince1970).png")
try? data.write(to: imagePath)
// 可选:通过 UserDefaults 通知主应用
UserDefaults(suiteName: "group.com.yourcompany.appname")?.set(imagePath.lastPathComponent, forKey: "latestScreenshot")
}
}
步骤 5:主应用读取截图
在主应用中监听共享数据:
// 在 AppDelegate 或 ViewController 中
func checkForNewScreenshot() {
guard let groupContainer = FileManager.default.containerURL(
forSecurityApplicationGroupIdentifier: "group.com.yourcompany.appname"
) else { return }
// 从 UserDefaults 获取文件名
if let fileName = UserDefaults(suiteName: "group.com.yourcompany.appname")?.string(forKey: "latestScreenshot") {
let screenshotURL = groupContainer.appendingPathComponent(fileName)
if let image = UIImage(contentsOfFile: screenshotURL.path) {
// 显示截图
DispatchQueue.main.async {
self.imageView.image = image
}
// 清理记录
UserDefaults(suiteName: "group.com.yourcompany.appname")?.removeObject(forKey: "latestScreenshot")
}
}
}
步骤 6:测试流程
运行 Share Extension Scheme:
Xcode 顶部 Scheme 选择扩展 Target → 运行。
在模拟器或设备上:
截屏并保存到相册。
打开相册 → 选择截图 → 点击分享按钮 → 选择你的应用扩展。
检查主应用是否接收到截图。