在雲端 Mac 上驗收 iOS 拖放資料交換

CI/CD 實踐 ·約 7 分鐘閱讀

在雲端 Mac 上驗收 iOS 拖放資料交換

發版前,團隊把清單中的一段任務編號拖進編輯區。拖曳預覽正常,放下後也沒有報錯,存下來的卻是空字串。問題不在動畫:拖放來源宣告的資料類型、接收端接受的類型,以及非同步讀取完成的時機,只要有一處對不上,介面就可能給出誤導性的成功回饋。遠端使用雲端 Mac 時,可以先讓 XCTest 檢查資料契約,再開啟模擬器驗收實際手勢;兩者不能互相取代。

先把拖放協定寫成可檢查的約定

針對每一種可拖曳內容,列出三件事:提供者宣告的 UTType、接收端允許的類型,以及成功讀取後應取得的內容。以任務編號為例,如果它只是要貼進文字欄位的字串,雙方可以約定使用 UTType.utf8PlainText。不要讓傳送端註冊純文字,接收端卻只查詢圖片類型,然後以「放置動作已觸發」判定成功。

錯誤處理也要寫清楚:不支援的類型不應顯示為可接收;讀取失敗時不應插入空白任務;取消拖曳不應修改原始資料。若業務需求必須保留任務編號以外的欄位,就應定義自己的資料格式,讓兩端共同解析,而不是把結構化內容偷偷塞進顯示文字。

拖放完成是一個介面事件,不等於資料已提交。只有類型相符、非同步讀取成功,且內容通過驗證後,才能更新業務狀態。

用 XCTest 驗證類型與實際酬載

在應用程式的測試 target 新增一個測試檔案。以下提供者會註冊 UTF-8 文字;測試先檢查類型,再等待读取回呼,並驗證讀到的位元組。它不依賴滑鼠或圖形桌面,適合先作為建置流程中的快速檢查。

import XCTest
import UniformTypeIdentifiers

final class DragPayloadTests: XCTestCase {
    func testPlainTextPayload() {
        let provider = NSItemProvider()
        provider.registerDataRepresentation(
            forTypeIdentifier: UTType.utf8PlainText.identifier,
            visibility: .all
        ) { completion in
            completion(Data("ticket-42".utf8), nil)
            return nil
        }

        XCTAssertTrue(provider.hasItemConformingToTypeIdentifier(
            UTType.plainText.identifier
        ))
        XCTAssertFalse(provider.hasItemConformingToTypeIdentifier(
            UTType.image.identifier
        ))

        let loaded = expectation(description: "Load drag payload")
        provider.loadDataRepresentation(
            forTypeIdentifier: UTType.utf8PlainText.identifier
        ) { data, error in
            XCTAssertNil(error)
            XCTAssertEqual(data.flatMap { String(data: $0, encoding: .utf8) },
                           "ticket-42")
            loaded.fulfill()
        }
        wait(for: [loaded], timeout: 5)
    }
}

這裡用 plainText 檢查相容類型,再用 utf8PlainText 讀取已註冊的具體表示。測試通過後,也要把同樣的類型判斷與解碼邏輯用在產品程式碼中,避免測試接受一種類型,介面卻使用另一套允許清單。正式程式碼還應在回呼中處理 error、空資料和 UTF-8 解碼失敗,不要只憑 hasItemConformingToTypeIdentifier 就修改介面。

指定模擬器目標後再執行

先在 VMCommit 控制台確認目前可選的配置;交付後透過 SSH 進入 Mac,查看已安裝的模擬器。將以下的專案、Scheme 和裝置識別碼換成自己的值。裝置識別碼應取自第一條指令,不要把範例字串當成實際裝置。

xcrun simctl list devices available
xcodebuild -list -project App.xcodeproj
xcodebuild test \
  -project App.xcodeproj \
  -scheme App \
  -destination 'platform=iOS Simulator,id=YOUR_DEVICE_ID' \
  -only-testing:AppTests/DragPayloadTests

-only-testing 的路徑必須與專案中的測試 target 和類別名稱一致。如果指令提示 Scheme 不存在,先檢查是否已將它設為共用;如果目標裝置無法使用,就重新讀取這台 Mac 的清單,不要沿用另一台機器的識別碼。測試失敗時,先保留原始輸出,區分是「模擬器沒有啟動」還是「酬載斷言失敗」。

在圖形工作階段驗收實際放置操作

契約測試略過了手勢處理,不能證明 UIDragInteraction 或 UIDropInteraction 已正確接上。在圖形桌面開啟模擬器、安裝測試應用程式,再依照以下順序完成一次操作:

  1. 長按有效的來源項目並開始拖曳,確認預覽內容與所選項目一致。
  2. 移到允許放置的目標,檢查是否出現可放置的回饋;再移到不接受該類型的區域,確認回饋消失。
  3. 放下後等待读取完成,再檢查編輯區出現的任務編號是否正確,以及是否只插入一次。
  4. 再拖曳一次,並在放下前取消,確認清單和編輯區都沒有變動。

如果酬載較大,應把「讀取中」視為獨立狀態:禁止重複提交,完成後再更新內容;失敗時恢復可操作狀態,並顯示明確提示。不要為了讓動畫更順暢,就在非同步回呼返回前先顯示「已匯入」。圖形驗收關注使用者看到的狀態轉換,XCTest 關注底層資料,兩份結果應分別記錄。

排查失敗時沿著資料流追查

「目標沒有亮起」先查類型:在開發環境中印出來源端註冊的識別碼與接收端允許的識別碼,確認使用的是類型相容性判斷,而不是只比較顯示名稱。「目標亮了卻沒有內容」則檢查載入回呼是否執行,以及錯誤、資料長度和解碼結果。不要把整份酬載寫進共用日誌;任務內容可能包含業務資料,記錄類型與長度通常就足以定位問題邊界。

「偶爾插入兩次」則要檢查是否同時在放置回呼與載入完成回呼更新模型,或對同一個提供者重複發起讀取。為每次放置分配一個操作識別碼,僅在驗證成功後提交一次;取消或失敗時丟棄待提交的結果。改用圖片等其他酬載時,應針對每種接受的類型分別撰寫測試,不能因為純文字通過,就假定所有拖放路徑都成立。

交付前核對兩份結果清單

自動化結果清單應包含:類型相符、拒絕不支援的類型、位元組內容一致、空值與讀取錯誤獲得處理,以及逾時會使測試失敗。圖形結果清單應包含:來源預覽正確、目標回饋正確、成功後僅更新一次、取消後不修改內容,以及失敗提示不會誤報成功。前者適合每次提交時執行;後者則應在互動實作或系統版本變更後重點複查。

最後,在一台初始狀態乾淨的模擬器上重複操作,確認結果不依賴上次執行時留在編輯區的內容。把專案實際接受的類型與這兩份檢查清單一起保留,下次變更拖放格式時,就能判斷問題出在資料契約、非同步讀取,還是手勢回饋。

常見問題

只測 NSItemProvider,就能確認拖放介面正常嗎?

不能。它可驗證類型與資料讀取;拖曳預覽、放置目標提示和取消回饋仍須在圖形介面檢查。

接收端何時可以讀取拖放資料?

先確認提供者宣告了可接受的 UTType,再以非同步方式載入;完成前不可將資料視為已匯入。

租期自由選

租用專屬實體 Mac mini,驗證你的下一步

VMCommit 提供 M4 專屬實體 Mac mini,可按日、週、月或季租用。選擇機型和節點後,即可在控制台查看即時供應狀況。

立即租用 Mac mini