逐步解說:系結 iOS Swift 連結庫
重要
我們目前正在調查 Xamarin 平臺上的自定義系結使用方式。 請接受 這項調查 ,以通知未來的開發工作。
Xamarin 可讓行動開發人員使用 Visual Studio 和 C# 建立跨平臺原生行動體驗。 您可以使用現用的 iOS 平臺 SDK 元件。 但在許多情況下,您也想要使用針對該平台開發的第三方 SDK,Xamarin 可讓您透過系結來執行此動作。 若要將第三方 Objective-C 架構併入您的 Xamarin.iOS 應用程式,您必須先為其建立 Xamarin.iOS 系結,才能在應用程式中使用它。
iOS 平臺及其原生語言和工具不斷演進,Swift 是目前 iOS 開發世界中最具動態的領域之一。 有一些第三方 SDK 已經從 Objective-C 移轉至 Swift,它讓我們面臨新的挑戰。 雖然 Swift 系結程式類似於 Objective-C,但它需要額外的步驟和組態設定,才能成功建置並執行 AppStore 可接受的 Xamarin.iOS 應用程式。
本文件的目標是概述解決此案例的高階方法,並提供詳細的逐步指南,並提供簡單的範例。
背景
Swift 最初由Apple 於2014年推出,目前為5.1版,第三方架構採用速度迅速。 您有幾個選項可用來系結 Swift 架構,本檔概述使用 Objective-C 產生的介面標頭的方法。 當架構建立時,Xcode 工具會自動建立標頭,並用來做為從受控世界與 Swift 世界通訊的方式。
必要條件
若要完成此逐步解說,您需要:
建置原生連結庫
第一個步驟是建置已啟用標頭的 Objective-C 原生 Swift 架構。 架構通常是由第三方開發人員提供,且標頭內嵌在下列目錄中的套件:FrameworkName.framework>/Headers/<FrameworkName-Swift.h>。<
此標頭會公開公用介面,其將用來建立 Xamarin.iOS 系結元數據,併產生公開 Swift 架構成員的 C# 類別。 如果標頭不存在或有不完整的公用介面(例如,您看不到類別/成員),您有兩個選項:
- 更新 Swift 原始碼以產生標頭,並使用 屬性標記必要的成員
@objc
- 建置 Proxy 架構,以控制公用介面和 Proxy 對基礎架構的所有呼叫
在本教學課程中,第二種方法描述為對第三方原始程式碼的相依性較少,但並非一律可用。 避免第一種方法的另一個原因是支援未來架構變更所需的額外工作。 當您開始將變更新增至第三方原始程式碼之後,您必須負責支援這些變更,並可能將它們與每個未來的更新合併。
例如,在本教學課程中,會建立 Gigya Swift SDK 的系結:
開啟 Xcode 並建立新的 Swift 架構,這會是 Xamarin.iOS 程式代碼和第三方 Swift 架構之間的 Proxy。 按兩下 [ 檔案 > 新 > 專案 ],然後遵循精靈步驟:
從開發人員網站下載 Gigya xcframework 並解壓縮。 撰寫本文時,最新版本是 Gigya Swift SDK 1.5.3
從項目檔總管選取 SwiftFrameworkProxy,然後選取 [一般] 索引標籤
將 Gigya.xcframework 套件拖放到 [一般] 索引卷標下的 [Xcode Frameworks 和連結庫] 清單,並在新增架構時檢查 [視需要 複製專案] 選項:
確認 Swift 架構已新增至專案,否則將無法使用下列選項。
確定 已選取 [不要內嵌 ] 選項,稍後將手動控制:
確定 [建置 設定] 選項 [一律內嵌 Swift 標準連結庫],其中包含具有架構的 Swift 連結庫設定為 [否]。 稍後將手動控制,Swift 動態連結庫會包含在最終套件中:
確定 [ 啟用 Bitcode ] 選項已設定為 [否]。 從現在起,Xamarin.iOS 不包含 Bitcode,而 Apple 需要所有連結庫支援相同的架構:
您可以針對架構執行下列終端機命令,確認結果架構已停用 Bitcode 選項:
otool -l SwiftFrameworkProxy.framework/SwiftFrameworkProxy | grep __LLVM
輸出應該是空的,否則您想要檢閱特定組態的項目設定。
確定 Objective-C [產生的介面標頭名稱 ] 選項已啟用,並指定標頭名稱。 默認名稱為 FrameworkName-Swift.h>:<
提示
如果無法使用此選項,請先確定將檔案新增
.swift
至專案,如下所示,然後返回Build Settings
,而且應該可以探索設定。公開所需的方法,並使用 屬性加以標記,
@objc
並套用下面定義的其他規則。 如果您在沒有此步驟的情況下建置架構,產生的 Objective-C 標頭會是空的,而 Xamarin.iOS 將無法存取 Swift 架構成員。 藉由建立新的 Swift 檔案 SwiftFrameworkProxy.swift 並定義下列程式代碼,公開基礎 Gigya Swift SDK 的初始化邏輯:import Foundation import UIKit import Gigya @objc(SwiftFrameworkProxy) public class SwiftFrameworkProxy : NSObject { @objc public func initFor(apiKey: String) -> String { Gigya.sharedInstance().initFor(apiKey: apiKey) let gigyaDomain = Gigya.sharedInstance().config.apiDomain let result = "Gigya initialized with domain: \(gigyaDomain)" return result } }
上述程式代碼的一些重要注意事項:
Gigya
從原始的第三方 Gigya SDK 匯入模組可讓您存取架構的任何成員。- 使用指定名稱的屬性標記 SwiftFrameworkProxy 類別
@objc
,否則會產生唯一無法讀取的名稱,例如_TtC19SwiftFrameworkProxy19SwiftFrameworkProxy
。 應該清楚定義類型名稱,因為它稍後會以其名稱使用。 - 從繼承 Proxy 類別
NSObject
,否則不會在 Objective-C 頭檔中產生。 - 將所有要公開的成員標示為
public
。
將配置組建組態從 [偵錯 ] 變更為 [發行]。 若要這樣做,請開啟 [Xcode > 目標 > 編輯配置 ] 對話框,然後將 [ 建置組態 ] 選項設定為 [ 發行]:
此時,架構已準備好建立。 建置模擬器和裝置架構的架構,然後將輸出結合為單一二進位架構組合 (
.xcframework
)。 使用下列命令執行組建:xcodebuild -project "Swift/SwiftFrameworkProxy/SwiftFrameworkProxy.xcodeproj" archive \ -scheme "SwiftFrameworkProxy" \ -configuration Release \ -archivePath "build/SwiftFrameworkProxy-simulator.xcarchive" \ -destination "generic/platform=iOS Simulator" \ -derivedDataPath "build" \ -IDECustomBuildProductsPath="" -IDECustomBuildIntermediatesPath="" \ ENABLE_BITCODE=NO \ SKIP_INSTALL=NO \ BUILD_LIBRARY_FOR_DISTRIBUTION=YES
xcodebuild -project "Swift/SwiftFrameworkProxy/SwiftFrameworkProxy.xcodeproj" archive \ -scheme "SwiftFrameworkProxy" \ -configuration Release \ -archivePath "build/SwiftFrameworkProxy-ios.xcarchive" \ -destination "generic/platform=iOS" \ -derivedDataPath "build" \ -IDECustomBuildProductsPath="" -IDECustomBuildIntermediatesPath="" \ ENABLE_BITCODE=NO \ SKIP_INSTALL=NO \ BUILD_LIBRARY_FOR_DISTRIBUTION=YES
提示
請參閱 建立二進位架構的詳細資訊
提示
如果您有工作區而非專案,請建置工作區,並將目標指定為必要參數。 您也想要指定輸出目錄,因為針對工作區,此目錄會不同於項目組建。
提示
您也可以使用 協助程式腳本 來建置所有適用架構的架構,或只是從目標選取器中的 Xcode 切換模擬器和裝置建置架構。
產生的架構有兩個封存,其中一個用於每個平臺,將它們合併為單一二進位架構套件組合,以便稍後內嵌至 Xamarin.iOS 系結專案。 若要建立結合這兩個架構的二進位架構套件組合,您必須執行下列步驟。 .xcarchive 套件只是資料夾,因此您可以執行所有類型的作業,例如新增、移除和取代檔案:
xcframework
使用封存中先前建置的架構建立 :xcodebuild -create-xcframework \ -framework "build/SwiftFrameworkProxy-simulator.xcarchive/Products/Library/Frameworks/SwiftFrameworkProxy.framework" \ -framework "build/SwiftFrameworkProxy-ios.xcarchive/Products/Library/Frameworks/SwiftFrameworkProxy.framework" \ -output "build/SwiftFrameworkProxy.xcframework"
提示
如果您想要只支援單一平臺(例如,您要建置只能在裝置上執行的應用程式),您可以略過建立 .xcframework 連結庫的步驟,並使用先前裝置組建的輸出架構。
提示
您也可以使用 協助程式腳本 來建立 .xcframework,以自動化上述所有步驟。
準備元數據
此時,您應該具有 .xcframework, Objective-C 且產生的介面標頭已可供 Xamarin.iOS 系結取用。 下一個步驟是準備由系結專案用來產生 C# 類別的 API 定義介面。 這些定義可由 Objective Sharpie 工具和產生的頭檔手動或自動建立。 使用 Sharpie 來產生元數據:
從官方下載網站下載最新的 Objective Sharpie 工具,並遵循精靈加以安裝。 安裝完成後,您可以執行sharpie命令來驗證它:
sharpie -v
使用 sharpie 和自動產生的 Objective-C 標頭檔產生元資料:
sharpie bind --sdk=iphoneos16.4 --output="XamarinApiDef" --namespace="Binding" --scope="build/SwiftFrameworkProxy.xcframework/ios-arm64/SwiftFrameworkProxy.framework/Headers/" "build/SwiftFrameworkProxy.xcframework/ios-arm64/SwiftFrameworkProxy.framework/Headers/SwiftFrameworkProxy-Swift.h"
輸出會反映產生的元數據檔案: ApiDefinitions.cs。 儲存此檔案以供下一個步驟納入 Xamarin.iOS 系結專案,以及原生參考:
Parsing 1 header files... Binding... [write] ApiDefinitions.cs
此工具會為每個公開 Objective-C 的成員產生 C# 元數據,其看起來會類似下列程式代碼。 如您所見,可以手動定義,因為它具有人類可讀取的格式和直接的成員對應:
[Export ("initForApiKey:")] string InitForApiKey (string apiKey);
提示
如果您變更標頭名稱的預設 Xcode 設定,則標頭名稱可能會不同。 根據預設,它具有具有 -Swift 後綴的項目名稱。 您可以瀏覽至架構套件的標頭資料夾,來檢查檔案及其名稱。
提示
在自動化程式中,您可以使用 協助程式腳本 在建立 .xcframework 之後自動產生元數據。
建置系結連結庫
下一個步驟是使用 Visual Studio 系結範本建立 Xamarin.iOS 系結專案、新增必要的元數據、原生參考,然後建置專案以產生消費性連結庫:
開啟 Visual Studio for Mac 並建立新的 Xamarin.iOS 系結連結庫專案,併為其命名,在此案例中為 SwiftFrameworkProxy.Binding 並完成精靈。 Xamarin.iOS 系結樣本位於下列路徑:iOS > 連結庫>系結連結庫:
刪除現有的元數據檔案 ApiDefinition.cs ,因為它將完全取代為 Objective Sharpie 工具所產生的元數據。
複製 Sharpie 在上述其中一個步驟中產生的元數據,在屬性視窗中選取下列建置動作:ApiDefinitions.cs 檔案的 ObjBindingApiDefinition 和 StructsAndEnums.cs 檔案的 ObjBindingCoreSource:
元數據本身會使用 C# 語言來描述每個公開的 Objective-C 類別和成員。 您可以看到原始 Objective-C 標頭定義以及 C# 宣告:
// @interface SwiftFrameworkProxy : NSObject [BaseType (typeof(NSObject))] interface SwiftFrameworkProxy { // -(NSString * _Nonnull)initForApiKey:(NSString * _Nonnull)apiKey __attribute__((objc_method_family("none"))) __attribute__((warn_unused_result)); [Export ("initForApiKey:")] string InitForApiKey (string apiKey); }
雖然它是有效的 C# 程式代碼,但不會像 現在一樣使用,而是由 Xamarin.iOS 工具用來根據此元數據定義產生 C# 類別。 因此,您不會使用介面 SwiftFrameworkProxy 來取得具有相同名稱的 C# 類別,而此類別可由 Xamarin.iOS 程式代碼具現化。 這個類別會取得元數據所定義的方法、屬性和其他成員,您會以 C# 方式呼叫。
將原生參考新增至先前產生的二進位架構套件組合,以及該架構的每個相依性。 在此情況下,請將 SwiftFrameworkProxy 和 Gigya 架構原生參考新增至系結專案:
- 若要新增原生架構參考,請開啟 finder 並流覽至具有架構的資料夾。 將架構拖放到 方案總管 的 [原生參考] 位置底下。 或者,您可以使用 [原生參考] 資料夾上的操作功能表選項,然後按兩下 [新增原生參考 ] 來查閱架構並加以新增:
更新每個原生參考的屬性,並檢查三個重要選項:
- 設定 Smart Link = true
- 設定強制載入 = false
- 設定用來建立原始架構的架構清單。 在此情況下,每個架構只有兩個相依性:Foundation 和 UIKit。 將它設定為 [Frameworks] 欄位:
如果您有任何其他連結器旗標要指定,請在 [鏈接器旗標] 字段中設定它們。 在此情況下,請將它保留空白。
視需要指定其他連結器旗標。 如果您要系結的連結庫只會 Objective-C 公開 API,但在內部使用 Swift,您可能會看到如下的問題:
error MT5209 : Native linking error : warning: Auto-Linking library not found for -lswiftCore error MT5209 : Native linking error : warning: Auto-Linking library not found for -lswiftQuartzCore error MT5209 : Native linking error : warning: Auto-Linking library not found for -lswiftCoreImage
在原生連結庫的系結項目屬性中,必須將下列值新增至連結器旗標:
L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphonesimulator/ -L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/iphoneos -Wl,-rpath -Wl,@executable_path/Frameworks
前兩個選項(其中
-L ...
一個選項)會告訴原生編譯程式在哪裡尋找 swift 連結庫。 原生編譯程式會忽略沒有正確架構的連結庫,這表示可以同時傳遞模擬器連結庫和裝置連結庫的位置,使其適用於模擬器和裝置組建(這些路徑僅適用於iOS;若是 tvOS 和 watchOS,則必須更新它們)。 其中一個缺點是,此方法要求正確的 Xcode 位於 /Application/Xcode.app,如果系結連結庫的取用者在不同的位置有 Xcode,它將無法運作。 替代解決方案是在可執行專案的 iOS 建置選項 (--gcc_flags -L... -L...
) 中,於其他 mtouch 自變數中新增這些選項。 第三個選項會讓原生連結器將 swift 連結庫的位置儲存在可執行檔中,讓 OS 可以找到它們。
最後一個動作是建置連結庫,並確定您沒有任何編譯錯誤。 您通常會發現 Objective Sharpie 所產生的系結元數據會加上
[Verify]
屬性的批注。 這些屬性表示您應該藉由比較系結與原始 Objective-C 宣告來驗證 Objective Sharpie 執行正確的動作(將在系結宣告上方的批注中提供)。 您可以透過下列連結深入瞭解以 屬性標示的成員。 建置項目之後,Xamarin.iOS 應用程式就可以取用該專案。
取用系結連結庫
最後一個步驟是在 Xamarin.iOS 應用程式中取用 Xamarin.iOS 系結連結庫。 建立新的 Xamarin.iOS 專案、新增系結連結庫的參考,以及啟動 Gigya Swift SDK:
建立 Xamarin.iOS 專案。 您可以使用 iOS > 應用程式 > 單一檢視應用程式 作為起點:
將系結項目參考新增至目標專案或先前建立.dll。 將繫結連結庫視為一般 Xamarin.iOS 連結庫:
更新應用程式的原始程式碼,並將初始化邏輯新增至啟動 Gigya SDK 的主要 ViewController
public override void ViewDidLoad() { base.ViewDidLoad(); var proxy = new SwiftFrameworkProxy(); var result = proxy.InitForApiKey("APIKey"); System.Diagnostics.Debug.WriteLine(result); }
建立名稱 為 btnLogin 的按鈕,並新增下列按鈕按兩下下列按鈕按下列按鈕按下下列按鈕按下下列按鈕按下處理程式來啟動驗證
private void btnLogin_Tap(object sender, EventArgs e) { _proxy.LoginWithProvider(GigyaSocialProvidersProxy.Instagram, this, (result, error) => { // process your login result here }); }
在偵錯輸出中執行應用程式,您應該會看到下列這一行:
Gigya initialized with domain: us1.gigya.com
。 按鍵以啟動驗證流程:
恭喜! 您已成功建立 Xamarin.iOS 應用程式和系結連結庫,這會取用 Swift 架構。 上述應用程式將會在 iOS 12.2+ 上成功執行,因為從這個 iOS 版本 Apple 引進了 ABI 穩定性 ,而每個從 12.2+ 開始的 iOS 都包含 Swift 運行時間連結庫,可用來執行以 Swift 5.1+ 編譯的應用程式。 如果您需要新增對舊版 iOS 的支持,還有一些可完成的步驟:
若要新增 iOS 12.1 和更早版本的支援,您想要提供用來編譯架構的特定 Swift 動態連結庫。 使用 Xamarin.iOS.SwiftRuntimeSupport NuGet 套件來處理和複製 IPA 所需的連結庫。 將 NuGet 參考新增至目標專案並重建應用程式。 不需要任何進一步的步驟,NuGet 套件會安裝使用建置程式執行的特定工作、識別必要的 Swift 動態連結庫,並使用最終 IPA 加以封裝。
若要將應用程式提交至您想要使用 Xcode 和散發選項的應用程式市集,它會更新 IPA 檔案和 SwiftSupport 資料夾動態連結庫,讓 AppStore 接受它:
事後封存應用程式。 從 Visual Studio for Mac 功能表中,選取 [ 建 > 置封存以供發佈] :
此動作會建置專案,並將其達成給召集人,Xcode 可供散發存取。
Java 透過 Xcode 散發。 開啟 Xcode 並瀏覽至 [ 視窗 > 召集人] 選單選項:
選取在上一個步驟中建立的封存,然後按兩下 [散發應用程式] 按鈕。 遵循精靈將應用程式上傳至AppStore。
此步驟是選擇性的,但請務必確認您的應用程式可以在iOS 12.1和更早版本以及12.2上執行。 您可以使用測試雲端和 UITest 架構的說明來執行此作業。 建立 UITest 專案與執行應用程式的基本 UI 測試:
建立 UITest 專案,並為 Xamarin.iOS 應用程式進行設定:
提示
您可以透過下列連結,找到如何建立UITest專案並針對您的應用程式進行設定的詳細資訊。
建立基本測試以執行應用程式,並使用一些 Swift SDK 功能。 此測試會啟動應用程式、嘗試登入,然後按 [取消] 按鈕:
[Test] public void HappyPath() { app.WaitForElement(StatusLabel); app.WaitForElement(LoginButton); app.Screenshot("App loaded."); Assert.AreEqual(app.Query(StatusLabel).FirstOrDefault().Text, "Gigya initialized with domain: us1.gigya.com"); app.Tap(LoginButton); app.WaitForElement(GigyaWebView); app.Screenshot("Login activated."); app.Tap(CancelButton); app.WaitForElement(LoginButton); app.Screenshot("Login cancelled."); }
提示
請透過下列連結深入瞭解UITests架構和 使用者介面自動化。
在應用程式中心建立 iOS 應用程式,建立新的測試回合,並將新的裝置設定為執行測試:
提示
透過下列連結深入瞭解AppCenter Test Cloud。
安裝 appcenter CLI
npm install -g appcenter-cli
重要
請確定您已安裝節點 v6.3 或更新版本
使用下列命令執行測試。 也請確定您的 appcenter 命令行目前已登入。
appcenter test run uitest --app "Mobile-Customer-Advisory-Team/SwiftBinding.iOS" --devices a7e7cb50 --app-path "Xamarin.SingleView.ipa" --test-series "master" --locale "en_US" --build-dir "Xamarin/Xamarin.SingleView.UITests/bin/Debug/"
確認結果。 在 AppCenter 入口網站中,瀏覽至 應用程式 > 測試 > 回合:
然後選取所需的測試回合,並確認結果:
您已開發基本的 Xamarin.iOS 應用程式,其會透過 Xamarin.iOS 系結連結庫使用原生 Swift 架構。 此範例提供簡單的方式來使用選取的架構,並在實際應用程式中,您必須公開更多 API 並準備這些 API 的元數據。 產生元數據的腳本將簡化架構 API 的未來變更。