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

iOS分享扩展问题:实现YouTube链接转发至指定URL

我来帮你搞定Share Extension的问题,一步步梳理怎么实现把YouTube分享链接发送到指定URL的需求~

问题分析

你要做的核心是:通过Share Extension捕获YouTube的分享链接,再把这个链接POST到Web应用的指定接口,让Web应用添加到播放列表。Share Extension的坑主要集中在异步回调处理、配置权限和避免Extension提前退出这几点,我们逐个解决。

完整代码实现&关键说明

首先先补全你没写完的代码,同时把核心逻辑补全:

import UIKit
import MobileCoreServices

class ShareViewController: UIViewController {
    
    private let activityIndicator = UIActivityIndicatorView(style: .large)

    override func viewDidLoad() {
        super.viewDidLoad()
        // 添加载提示,给用户反馈
        activityIndicator.center = view.center
        view.addSubview(activityIndicator)
        
        // 开始捕获分享内容
        fetchSharedURL()
    }
    
    private func fetchSharedURL() {
        guard let item = extensionContext?.inputItems.first as? NSExtensionItem,
              let itemProvider = item.attachments?.first as? NSItemProvider,
              itemProvider.hasItemConformingToTypeIdentifier(kUTTypeURL as String) else {
            // 没有获取到有效URL,直接结束Extension
            extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
            return
        }
        
        // 异步加载URL(这里是关键,不能同步处理)
        itemProvider.loadItem(forTypeIdentifier: kUTTypeURL as String, options: nil) { [weak self] (url, error) in
            guard let self = self,
                  let shareURL = url as? URL,
                  error == nil else {
                print("获取分享URL失败:\(error?.localizedDescription ?? "未知错误")")
                self.extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
                return
            }
            
            // 可选:只处理YouTube链接,过滤其他无关分享
            guard self.isValidYouTubeURL(shareURL) else {
                print("不是YouTube链接,忽略")
                self.extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
                return
            }
            
            // 发送URL到Web应用的指定接口
            self.sendURLToWebApp(shareURL: shareURL)
        }
    }
    
    // 验证是否为YouTube链接
    private func isValidYouTubeURL(_ url: URL) -> Bool {
        let allowedHosts = ["youtube.com", "youtu.be", "www.youtube.com", "www.youtu.be"]
        return allowedHosts.contains(url.host ?? "")
    }
    
    // 发送URL到Web应用API
    private func sendURLToWebApp(shareURL: URL) {
        DispatchQueue.main.async {
            self.activityIndicator.startAnimating()
        }
        
        // 替换成你的Web应用接口地址
        guard let apiURL = URL(string: "https://your-web-app-url/add-to-playlist") else {
            print("API地址无效")
            self.finishExtension()
            return
        }
        
        var request = URLRequest(url: apiURL)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        
        // 按Web应用要求构造请求体
        let requestBody: [String: Any] = [
            "youtube_url": shareURL.absoluteString,
            // 如果需要用户认证,这里要加token(后面会说怎么获取)
            // "auth_token": self.getAuthToken()
        ]
        
        do {
            request.httpBody = try JSONSerialization.data(withJSONObject: requestBody)
        } catch {
            print("构造请求体失败:\(error.localizedDescription)")
            self.finishExtension()
            return
        }
        
        // 发起网络请求
        let task = URLSession.shared.dataTask(with: request) { [weak self] data, response, error in
            guard let self = self else { return }
            
            DispatchQueue.main.async {
                self.activityIndicator.stopAnimating()
            }
            
            if let error = error {
                print("发送请求失败:\(error.localizedDescription)")
            } else if let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) {
                print("链接已成功发送到Web应用")
            } else {
                print("请求返回错误状态码:\((response as? HTTPURLResponse)?.statusCode ?? -1)")
            }
            
            // 必须调用这个方法结束Extension,不然会卡住
            self.finishExtension()
        }
        task.resume()
    }
    
    // 统一处理Extension结束逻辑
    private func finishExtension() {
        extensionContext?.completeRequest(returningItems: [], completionHandler: nil)
    }
    
    // 如果需要用户认证,从App Groups读取主App的token(需要先配置App Groups)
    private func getAuthToken() -> String? {
        let sharedDefaults = UserDefaults(suiteName: "com.your.app.group")
        return sharedDefaults?.string(forKey: "user_auth_token")
    }
}
关键配置&踩坑点
  1. Share Extension的Info.plist配置
    一定要确保Extension能接收URL类型的分享内容,在Info.plist的NSExtension节点下添加:

    <key>NSExtensionAttributes</key>
    <dict>
        <key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
        <integer>1</integer>
        <key>NSExtensionActivationSupportsURLWithMaxCount</key>
        <integer>1</integer>
    </dict>
    

    如果想更精准,只显示在YouTube的分享列表里,可以用谓词规则,但先按上面的配置测试。

  2. 避免Extension提前退出
    loadItem和网络请求都是异步操作,绝对不能在异步回调外调用completeRequest,否则Extension会提前终止,导致网络请求中断。上面的代码里统一在finishExtension里处理结束逻辑,确保所有异步操作完成后再调用。

  3. 用户认证问题
    Share Extension是独立于主App的进程,无法直接读取主App的UserDefaults或Keychain。如果Web应用的接口需要用户认证,你需要:

    • 在开发者后台给主App和Extension配置相同的App Groups
    • 主App把用户的认证token存到App Groups的UserDefaults里
    • Extension通过UserDefaults(suiteName: "你的App Groups ID")读取token,加到请求头里
  4. UI反馈
    Share Extension的界面空间很小,加个加载指示器能让用户知道正在处理,避免以为卡住。

内容的提问来源于stack exchange,提问作者Ozan Gümstas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:50:06