本文整理「从系统外把内容分享到 / 打开到某个 iOS App」相关的底层机制与典型实现,适合作为设计入口、排查问题和扩展新分享场景的参考。
1. 常见入口类型概览
-
文件类(Word / PDF / TXT / 本地图片等)通过“在其他应用中打开”
- 系统复制文件到目标 App 的沙盒
- 通过
application(_:open:options:)传入一个file://URL
-
URL / Deep Link(
myapp://.../https://.../ 登录回调等)- 通过 URL Scheme / Universal Link 进入
- 由
application(_:open:options:)或application(_:continue:restorationHandler:)处理
-
Share Extension(分享扩展)
- 系统用
NSItemProvider把文本 / 图片 / URL 等数据交给扩展进程 - 扩展通过 App Group + 自定义 URL(例如
myapp://ShareExtension)唤醒主 App 并传递数据
- 系统用
-
Universal Link + 特殊业务场景(如 NFC、H5 落地页)
- 通过
NSUserActivity的webpageURL进入 - 由
application(_:continue:restorationHandler:)处理
- 通过
2. 文件类分享(Word / PDF / TXT / 图片等)
2.1 系统层:从外部 App 到目标 App 的文件交接
当用户在外部 App(Files / Word / WPS / 邮件附件等)中选择「在其他应用中打开到某 App」时:
-
源 App 发起打开请求
- 通过
UIDocumentInteractionController、UIDocumentPickerViewController或 Files 等文档浏览入口 - 告诉系统:「这里有一个文件,可以由支持该类型的 App 打开」
- 通过
-
iOS 匹配目标 App
- 查目标 App 的
Info.plist中CFBundleDocumentTypes/ UTI / UTType 配置 - 判断该 App 是否支持此文件类型(
.docx/.pdf/.txt/ 图片等)
- 查目标 App 的
-
复制/授权文件到目标 App 沙盒
- 系统将文件复制或授权到目标 App 可访问的路径(常见在
tmp/Inbox/),例如:/var/mobile/Containers/Data/Application/<App_UUID>/tmp/Inbox/xxx.docx
- 用这个路径构造一个本地
file://URL
- 系统将文件复制或授权到目标 App 可访问的路径(常见在
-
调用 App 入口
- 进入
AppDelegate:func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool - 参数
url就是上述file://...路径
- 进入
这个 URL 是「复制到当前 App 沙盒中的本地文件路径」,不是公网链接,也不是所有 App 可见的共享 URL。
2.2 App 内对文件 URL 的典型处理流程
在 application(_:open:options:) 内,典型文件处理流程可以设计为:
-
优先交给第三方 SDK(登录回调 / 统计 / 分享 SDK 等)
if someSDK.handle(url) { return true } -
记录“从文档打开”的状态(可选)
openedFromDocument = true -
主页尚未初始化(冷启动)时先缓存 URL
guard let rootVC = UIApplication.shared.keyWindow?.rootViewController else { UserDefaults.standard.set(url.absoluteString, forKey: "ShareContentURL") return true } -
内部 URL Scheme 路由(如
myapp://,与文件分享无关时可提前返回)if url.absoluteString.hasPrefix("myapp:") { // 执行内部路由 return true } 其它特殊链接(如某些 H5 活动)可单独判断(可选)
-
兜底:视为文件分享 → 进入文档打印 / 预览模块
// 直接或稍作延迟,等待 UI / 导航栈稳定 documentRouter.openDocument(from: rootVC, fileURL: url) return true
2.3 是否要再复制到自定义目录?
-
一次性使用(只打印 / 预览一次,不做历史记录)
- 可以直接使用系统给的
file://.../tmp/Inbox/...路径。
- 可以直接使用系统给的
-
需要长期保存 / 历史记录 / 再次编辑
- 建议在第一次拿到
url时:let destURL = ... // 比如 Documents/ImportedDocs/xxx.docx try? FileManager.default.copyItem(at: url, to: destURL) - 后续所有逻辑以
destURL为准,而不是继续依赖tmp/Inbox(该目录会被系统清理)。
- 建议在第一次拿到
3. 文本类分享:文本文件 vs 纯文本内容
3.1 文本文件(.txt)作为文件分享
当 .txt 作为文件被“打开到 App”时:
- 走的是文件分享机制:系统复制文件 →
file://...xxx.txt→application(_:open:options:)。 - 文档模块可以定义统一入口:
func loadDocument(_ url: URL) { let lower = url.absoluteString.lowercased() switch true { case lower.hasSuffix("doc"), lower.hasSuffix("docx"): wordToImage(url) case lower.hasSuffix("txt"): txtFileToImage(url) // 从 txt 文件路径读取文本 case lower.hasSuffix("pdf"): pdfToImage(url) default: break } } -
txtFileToImage(_ url: URL)内部典型实现:- 使用适当编码(GBK / UTF-8 等)读取文本;
- 用
WKWebView.loadHTMLString把内容排版为 HTML; - 再渲染为图片,适配打印页面尺寸。
3.2 纯文本内容(不是文件,不是 URL):通过 Share Extension
当用户在其他 App 中选中一段文本,通过系统分享面板分享到目标 App:
3.2.1 系统层:Share Extension + NSItemProvider
- 源 App 使用
UIActivityViewController触发分享面板; - 将文本封装为
NSItemProvider,UTType 为public.text/public.plain-text; - 系统根据各 Share Extension 的
NSExtensionActivationRule判断哪些扩展支持该类型; - 用户选中某 App 的 Share Extension 后:
- 系统启动该扩展进程;
- 通过
NSExtensionContext.inputItems把NSExtensionItem列表传给扩展; - 扩展再通过
NSItemProvider.loadItem(forTypeIdentifier: ...)异步拿到实际的String文本。
3.2.2 扩展 → 主 App:App Group + 自定义 URL
典型做法:
- 扩展将解析出的数据打包成一个字典,比如:
let payload: [String: Any] = [ "type": 4, // 自定义业务类型:例如“文本+文档打印” "txt": textContent, // 文本内容 "url": fileURLString // 可选:源文件名/URL,或其它元数据 ] - 通过 App Group 的
UserDefaults(suiteName:)或共享文件写入该字典; - 通过固定 URL Scheme 唤醒主 App,例如:
let url = URL(string: "myapp://ShareExtension")! extensionContext.open(url, completionHandler: nil)
3.2.3 主 App:从共享容器读取文本并处理
- 主 App 在
application(_:open:options:)中识别:if url.absoluteString == "myapp://ShareExtension" { // 从 App Group 容器读取扩展存入的字典 } - 从
UserDefaults(suiteName:)或共享文件中读出字典,根据type分流:- 纯文本 → 文本编辑页;
- 文本+文档 → 文档打印页;
- 图片数组 → 图片相关业务等。
- 对于「文本 + 文档打印」场景,一个常见模式是:
- 把
txt传给txtToImage(contents:)(直接按字符串排版成图片); - 将
txt及文件名/元信息落盘到自定义目录,以供后续再次打开。
- 把
4. URL / Deep Link 入口
4.1 URL Scheme / 深链
示例:
UIApplication.shared.open(URL(string: "myapp://page/editNote?id=123")!)
流程:
- 系统根据
Info.plist中CFBundleURLTypes匹配 App; - 调用:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool - App 内可按以下顺序处理:
- 优先交给第三方 SDK(登录、统计、分享等);
- 按 URL 前缀/路径分发到路由层,例如:
if url.scheme == "myapp" { router.route(url) return true } - 其它不识别的 URL 可以选择忽略或记录日志。
4.2 Universal Link(含 H5 落地页 / NFC 场景)
- App 为某域名配置 Associated Domains(如
applinks:example.com); - 用户在 Safari / 邮件 / 其它 App 中点击该域名链接;
- 系统创建
NSUserActivity:activityType = NSUserActivityTypeBrowsingWebwebpageURL = 对应的 https:// 链接
- 调用:
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool - App 内可以:
- 检查
userActivity.activityType是否为NSUserActivityTypeBrowsingWeb; - 根据
webpageURL判断是否为某个业务场景(如 NFC、H5 落地页、营销活动页); - 结合当前状态(隐私协议是否同意、当前流程是否允许跳转)决定是否拦截或跳转到目标页面。
- 检查
5. Share Extension 能力与支持类型
Share Extension 能处理的数据类型取决于扩展 target 的 Info.plist 中:
-
NSExtensionAttributes→NSExtensionActivationRule(以及支持的 UTType)
常见可配置类型包括:
- 文本:
public.text/public.plain-text - 图片:
public.image - URL:
public.url - 各类文档:如 PDF、Office 文档等对应的 UTType
在扩展代码里,可以统一将这些类型抽象成内部结构(比如 type + payload),再使用 App Group 传递给主 App,由主 App 按业务类型分发。
6. FAQ 总结
Q:分享 Word / PDF / TXT / 图片到 App 时,底层是什么机制?
A:文件分享 / Open In。系统复制到 App 沙盒 →file://URL →application(_:open:options:)→ 文档/图片业务。Q:分享的是一段“文本内容”(不是 URL,不是 txt 文件),系统怎么传给 App?
A:通过 Share Extension。系统用NSItemProvider(public.text)把文本交给扩展 → 扩展写入 App Group 容器 → 通过固定 URL 唤醒主 App → 主 App 从共享容器读取文本并处理。-
Q:拿到
URL后要不要再复制到自定义目录?
A:- 只用一次:可以直接用系统给的
file://路径; - 需要长期保存/再编辑/历史记录:应该复制到自己控制的沙盒目录,再以新路径为准。
- 只用一次:可以直接用系统给的
Q:Share Extension 只能分享图片吗?
A:不是。它可以支持文本、URL、图片、各种文档等,具体取决于扩展 target 中声明的支持类型(UTType)以及扩展代码如何从NSItemProvider中抽取数据。