本文整理「從系統(tǒng)外把內(nèi)容分享到 / 打開到某個 iOS App」相關(guān)的底層機制與典型實現(xiàn),適合作為設(shè)計入口、排查問題和擴展新分享場景的參考。
1. 常見入口類型概覽
-
文件類(Word / PDF / TXT / 本地圖片等)通過“在其他應(yīng)用中打開”
- 系統(tǒng)復(fù)制文件到目標(biāo) App 的沙盒
- 通過
application(_:open:options:)傳入一個file://URL
-
URL / Deep Link(
myapp://.../https://.../ 登錄回調(diào)等)- 通過 URL Scheme / Universal Link 進入
- 由
application(_:open:options:)或application(_:continue:restorationHandler:)處理
-
Share Extension(分享擴展)
- 系統(tǒng)用
NSItemProvider把文本 / 圖片 / URL 等數(shù)據(jù)交給擴展進程 - 擴展通過 App Group + 自定義 URL(例如
myapp://ShareExtension)喚醒主 App 并傳遞數(shù)據(jù)
- 系統(tǒng)用
-
Universal Link + 特殊業(yè)務(wù)場景(如 NFC、H5 落地頁)
- 通過
NSUserActivity的webpageURL進入 - 由
application(_:continue:restorationHandler:)處理
- 通過
2. 文件類分享(Word / PDF / TXT / 圖片等)
2.1 系統(tǒng)層:從外部 App 到目標(biāo) App 的文件交接
當(dāng)用戶在外部 App(Files / Word / WPS / 郵件附件等)中選擇「在其他應(yīng)用中打開到某 App」時:
-
源 App 發(fā)起打開請求
- 通過
UIDocumentInteractionController、UIDocumentPickerViewController或 Files 等文檔瀏覽入口 - 告訴系統(tǒng):「這里有一個文件,可以由支持該類型的 App 打開」
- 通過
-
iOS 匹配目標(biāo) App
- 查目標(biāo) App 的
Info.plist中CFBundleDocumentTypes/ UTI / UTType 配置 - 判斷該 App 是否支持此文件類型(
.docx/.pdf/.txt/ 圖片等)
- 查目標(biāo) App 的
-
復(fù)制/授權(quán)文件到目標(biāo) App 沙盒
- 系統(tǒng)將文件復(fù)制或授權(quán)到目標(biāo) App 可訪問的路徑(常見在
tmp/Inbox/),例如:/var/mobile/Containers/Data/Application/<App_UUID>/tmp/Inbox/xxx.docx
- 用這個路徑構(gòu)造一個本地
file://URL
- 系統(tǒng)將文件復(fù)制或授權(quán)到目標(biāo) App 可訪問的路徑(常見在
-
調(diào)用 App 入口
- 進入
AppDelegate:func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool - 參數(shù)
url就是上述file://...路徑
- 進入
這個 URL 是「復(fù)制到當(dāng)前 App 沙盒中的本地文件路徑」,不是公網(wǎng)鏈接,也不是所有 App 可見的共享 URL。
2.2 App 內(nèi)對文件 URL 的典型處理流程
在 application(_:open:options:) 內(nèi),典型文件處理流程可以設(shè)計為:
-
優(yōu)先交給第三方 SDK(登錄回調(diào) / 統(tǒng)計 / 分享 SDK 等)
if someSDK.handle(url) { return true } -
記錄“從文檔打開”的狀態(tài)(可選)
openedFromDocument = true -
主頁尚未初始化(冷啟動)時先緩存 URL
guard let rootVC = UIApplication.shared.keyWindow?.rootViewController else { UserDefaults.standard.set(url.absoluteString, forKey: "ShareContentURL") return true } -
內(nèi)部 URL Scheme 路由(如
myapp://,與文件分享無關(guān)時可提前返回)if url.absoluteString.hasPrefix("myapp:") { // 執(zhí)行內(nèi)部路由 return true } 其它特殊鏈接(如某些 H5 活動)可單獨判斷(可選)
-
兜底:視為文件分享 → 進入文檔打印 / 預(yù)覽模塊
// 直接或稍作延遲,等待 UI / 導(dǎo)航棧穩(wěn)定 documentRouter.openDocument(from: rootVC, fileURL: url) return true
2.3 是否要再復(fù)制到自定義目錄?
-
一次性使用(只打印 / 預(yù)覽一次,不做歷史記錄)
- 可以直接使用系統(tǒng)給的
file://.../tmp/Inbox/...路徑。
- 可以直接使用系統(tǒng)給的
-
需要長期保存 / 歷史記錄 / 再次編輯
- 建議在第一次拿到
url時:let destURL = ... // 比如 Documents/ImportedDocs/xxx.docx try? FileManager.default.copyItem(at: url, to: destURL) - 后續(xù)所有邏輯以
destURL為準(zhǔn),而不是繼續(xù)依賴tmp/Inbox(該目錄會被系統(tǒng)清理)。
- 建議在第一次拿到
3. 文本類分享:文本文件 vs 純文本內(nèi)容
3.1 文本文件(.txt)作為文件分享
當(dāng) .txt 作為文件被“打開到 App”時:
- 走的是文件分享機制:系統(tǒng)復(fù)制文件 →
file://...xxx.txt→application(_:open:options:)。 - 文檔模塊可以定義統(tǒng)一入口:
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)內(nèi)部典型實現(xiàn):- 使用適當(dāng)編碼(GBK / UTF-8 等)讀取文本;
- 用
WKWebView.loadHTMLString把內(nèi)容排版為 HTML; - 再渲染為圖片,適配打印頁面尺寸。
3.2 純文本內(nèi)容(不是文件,不是 URL):通過 Share Extension
當(dāng)用戶在其他 App 中選中一段文本,通過系統(tǒng)分享面板分享到目標(biāo) App:
3.2.1 系統(tǒng)層:Share Extension + NSItemProvider
- 源 App 使用
UIActivityViewController觸發(fā)分享面板; - 將文本封裝為
NSItemProvider,UTType 為public.text/public.plain-text; - 系統(tǒng)根據(jù)各 Share Extension 的
NSExtensionActivationRule判斷哪些擴展支持該類型; - 用戶選中某 App 的 Share Extension 后:
- 系統(tǒng)啟動該擴展進程;
- 通過
NSExtensionContext.inputItems把NSExtensionItem列表傳給擴展; - 擴展再通過
NSItemProvider.loadItem(forTypeIdentifier: ...)異步拿到實際的String文本。
3.2.2 擴展 → 主 App:App Group + 自定義 URL
典型做法:
- 擴展將解析出的數(shù)據(jù)打包成一個字典,比如:
let payload: [String: Any] = [ "type": 4, // 自定義業(yè)務(wù)類型:例如“文本+文檔打印” "txt": textContent, // 文本內(nèi)容 "url": fileURLString // 可選:源文件名/URL,或其它元數(shù)據(jù) ] - 通過 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:)或共享文件中讀出字典,根據(jù)type分流:- 純文本 → 文本編輯頁;
- 文本+文檔 → 文檔打印頁;
- 圖片數(shù)組 → 圖片相關(guān)業(yè)務(wù)等。
- 對于「文本 + 文檔打印」場景,一個常見模式是:
- 把
txt傳給txtToImage(contents:)(直接按字符串排版成圖片); - 將
txt及文件名/元信息落盤到自定義目錄,以供后續(xù)再次打開。
- 把
4. URL / Deep Link 入口
4.1 URL Scheme / 深鏈
示例:
UIApplication.shared.open(URL(string: "myapp://page/editNote?id=123")!)
流程:
- 系統(tǒng)根據(jù)
Info.plist中CFBundleURLTypes匹配 App; - 調(diào)用:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool - App 內(nèi)可按以下順序處理:
- 優(yōu)先交給第三方 SDK(登錄、統(tǒng)計、分享等);
- 按 URL 前綴/路徑分發(fā)到路由層,例如:
if url.scheme == "myapp" { router.route(url) return true } - 其它不識別的 URL 可以選擇忽略或記錄日志。
4.2 Universal Link(含 H5 落地頁 / NFC 場景)
- App 為某域名配置 Associated Domains(如
applinks:example.com); - 用戶在 Safari / 郵件 / 其它 App 中點擊該域名鏈接;
- 系統(tǒng)創(chuàng)建
NSUserActivity:activityType = NSUserActivityTypeBrowsingWebwebpageURL = 對應(yīng)的 https:// 鏈接
- 調(diào)用:
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool - App 內(nèi)可以:
- 檢查
userActivity.activityType是否為NSUserActivityTypeBrowsingWeb; - 根據(jù)
webpageURL判斷是否為某個業(yè)務(wù)場景(如 NFC、H5 落地頁、營銷活動頁); - 結(jié)合當(dāng)前狀態(tài)(隱私協(xié)議是否同意、當(dāng)前流程是否允許跳轉(zhuǎn))決定是否攔截或跳轉(zhuǎn)到目標(biāo)頁面。
- 檢查
5. Share Extension 能力與支持類型
Share Extension 能處理的數(shù)據(jù)類型取決于擴展 target 的 Info.plist 中:
-
NSExtensionAttributes→NSExtensionActivationRule(以及支持的 UTType)
常見可配置類型包括:
- 文本:
public.text/public.plain-text - 圖片:
public.image - URL:
public.url - 各類文檔:如 PDF、Office 文檔等對應(yīng)的 UTType
在擴展代碼里,可以統(tǒng)一將這些類型抽象成內(nèi)部結(jié)構(gòu)(比如 type + payload),再使用 App Group 傳遞給主 App,由主 App 按業(yè)務(wù)類型分發(fā)。
6. FAQ 總結(jié)
Q:分享 Word / PDF / TXT / 圖片到 App 時,底層是什么機制?
A:文件分享 / Open In。系統(tǒng)復(fù)制到 App 沙盒 →file://URL →application(_:open:options:)→ 文檔/圖片業(yè)務(wù)。Q:分享的是一段“文本內(nèi)容”(不是 URL,不是 txt 文件),系統(tǒng)怎么傳給 App?
A:通過 Share Extension。系統(tǒng)用NSItemProvider(public.text)把文本交給擴展 → 擴展寫入 App Group 容器 → 通過固定 URL 喚醒主 App → 主 App 從共享容器讀取文本并處理。-
Q:拿到
URL后要不要再復(fù)制到自定義目錄?
A:- 只用一次:可以直接用系統(tǒng)給的
file://路徑; - 需要長期保存/再編輯/歷史記錄:應(yīng)該復(fù)制到自己控制的沙盒目錄,再以新路徑為準(zhǔn)。
- 只用一次:可以直接用系統(tǒng)給的
Q:Share Extension 只能分享圖片嗎?
A:不是。它可以支持文本、URL、圖片、各種文檔等,具體取決于擴展 target 中聲明的支持類型(UTType)以及擴展代碼如何從NSItemProvider中抽取數(shù)據(jù)。