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

SwiftUI应用添加Share Extension是否可行?需改用UIKit吗?

Share Extension 在 SwiftUI 应用中的实现方案

可以用 SwiftUI 实现 Share Extension,无需完全切换到 UIKit,核心是通过 UIKit 桥接将 SwiftUI 视图嵌入到 Extension 的视图控制器中。下面是具体实现步骤:

1. 添加 Share Extension 目标

在 Xcode 中,通过 File > New > Target 选择 Share Extension,填写目标名称后完成创建。无需勾选额外的 UIKit 相关配置。

2. 配置 Extension 的 Info.plist

修改 Extension 目录下的 Info.plist,确保 NSExtension 字典中的激活规则匹配你要处理的内容类型(比如图片):

<key>NSExtension</key>
<dict>
    <key>NSExtensionAttributes</key>
    <dict>
        <key>NSExtensionActivationRule</key>
        <dict>
            <key>NSExtensionActivationSupportsImageWithMaxCount</key>
            <integer>1</integer>
        </dict>
    </dict>
    <key>NSExtensionMainStoryboard</key>
    <string>MainInterface</string>
    <key>NSExtensionPointIdentifier</key>
    <string>com.apple.share-services</string>
</dict>

如果需要支持更灵活的类型,可替换为谓词字符串:

<key>NSExtensionActivationRule</key>
<string>SUBQUERY (extensionItems, $extensionItem, SUBQUERY ($extensionItem.attachments, $attachment, ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.image").@count >= 1).@count >= 1</string>

3. 编写 SwiftUI 扩展界面

创建 SwiftUI 视图,用于展示共享图片并处理导入逻辑:

import SwiftUI

struct ShareExtensionView: View {
    let image: UIImage
    let onImportComplete: () -> Void
    
    var body: some View {
        VStack(spacing: 24) {
            Text("导入图片到我的应用")
                .font(.title)
            
            Image(uiImage: image)
                .resizable()
                .scaledToFit()
                .frame(maxHeight: 300)
                .cornerRadius(8)
            
            Button("确认导入") {
                saveImageToSharedStorage(image)
                onImportComplete()
            }
            .buttonStyle(.borderedProminent)
            .controlSize(.large)
        }
        .padding()
    }
    
    private func saveImageToSharedStorage(_ image: UIImage) {
        // 保存到 App Groups 共享容器,供主应用读取
        guard let groupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.yourcompany.yourapp"),
              let imageData = image.jpegData(compressionQuality: 0.8) else {
            return
        }
        
        let fileURL = groupURL.appendingPathComponent("imported_image_\(Date().timeIntervalSince1970).jpg")
        try? imageData.write(to: fileURL)
        
        // 标记有新导入内容,主应用启动后可检查
        UserDefaults(suiteName: "group.com.yourcompany.yourapp")?.set(true, forKey: "hasNewImport")
    }
}

4. 桥接 UIKit 与 SwiftUI

修改 Extension 默认的 ShareViewController,用 UIHostingController 包裹 SwiftUI 视图:

import UIKit
import SwiftUI
import MobileCoreServices

class ShareViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        loadSharedImage()
    }
    
    private func loadSharedImage() {
        guard let extensionItem = extensionContext?.inputItems.first as? NSExtensionItem,
              let attachment = extensionItem.attachments?.first as? NSItemProvider,
              attachment.hasItemConformingToTypeIdentifier(kUTTypeImage as String) else {
            closeExtension()
            return
        }
        
        attachment.loadItem(forTypeIdentifier: kUTTypeImage as String) { [weak self] item, error in
            guard let self = self,
                  let imageData = item as? Data,
                  let image = UIImage(data: imageData) else {
                self.closeExtension()
                return
            }
            
            DispatchQueue.main.async {
                self.showSwiftUIView(with: image)
            }
        }
    }
    
    private func showSwiftUIView(with image: UIImage) {
        let shareView = ShareExtensionView(image: image) { [weak self] in
            self?.closeExtension()
        }
        
        let hostingController = UIHostingController(rootView: shareView)
        addChild(hostingController)
        hostingController.view.frame = view.bounds
        view.addSubview(hostingController.view)
        hostingController.didMove(toParent: self)
    }
    
    private func closeExtension() {
        extensionContext?.completeRequest(returningItems: nil, completionHandler: nil)
    }
}

5. 关键注意事项

  • App Groups 配置:主应用和 Extension 必须开启同一个 App Group(在 Target > Signing & Capabilities 中添加),才能共享文件或数据。
  • 沙盒限制:Extension 无法直接访问主应用沙盒,所有共享数据必须放在 App Groups 容器内。
  • 生命周期管理:处理完导入逻辑后必须调用 extensionContext?.completeRequest 关闭 Extension,避免被系统强制终止。
  • 类型匹配:确保 Info.plist 中的激活规则与你要处理的内容类型一致,否则 Extension 不会出现在系统分享列表中。

内容的提问来源于stack exchange,提问作者Sahbi Belgacem

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 11:22:42