world-manager/World Manager for Minecraft/SourceAccess/ConnectedDevice/AppleMobileDevice/AppleMobileDeviceAccess.swift

287 lines
10 KiB
Swift

//
// AppleMobileDeviceAccess.swift
// World Manager for Minecraft
//
// Created by OpenAI on 2026-05-26.
//
import Foundation
struct AppleMobileDeviceSummary: Sendable {
let deviceName: String
let deviceIdentifier: String
let productType: String
let productVersion: String
let trustState: DeviceTrustState
}
struct AppleMobileDeviceApplicationSummary: Sendable {
let bundleIdentifier: String
let displayName: String
let fileSharingEnabled: Bool
let supportsOpeningDocumentsInPlace: Bool
}
struct AppleMobileMinecraftLibraryItemSummary: Sendable {
let contentType: String
let collectionFolderName: String
let relativePath: String
let folderName: String
let displayName: String
let packUUID: String?
let packVersion: String?
let minimumEngineVersion: String?
}
struct AppleMobileDevicePathMetrics: Sendable {
let sizeBytes: Int64?
let modifiedDate: Date?
}
enum AppleMobileDeviceAccess {
static func firstConnectedDevice() async throws -> AppleMobileDeviceSummary {
try await Task.detached(priority: .userInitiated) {
var error: NSError?
guard let response = WMMCopyFirstConnectedDeviceSummary(&error) else {
throw error ?? NSError(
domain: "AppleMobileDeviceAccess",
code: 1,
userInfo: [NSLocalizedDescriptionKey: "No connected device could be read from MobileDevice.framework."]
)
}
guard
let deviceName = response["deviceName"] as? String,
let deviceIdentifier = response["deviceIdentifier"] as? String,
let productType = response["productType"] as? String,
let productVersion = response["productVersion"] as? String,
let trustStateRawValue = response["trustState"] as? String,
let trustState = DeviceTrustState(rawValue: trustStateRawValue)
else {
throw NSError(
domain: "AppleMobileDeviceAccess",
code: 2,
userInfo: [NSLocalizedDescriptionKey: "The MobileDevice summary returned an unexpected payload."]
)
}
return AppleMobileDeviceSummary(
deviceName: deviceName,
deviceIdentifier: deviceIdentifier,
productType: productType,
productVersion: productVersion,
trustState: trustState
)
}.value
}
static func mirrorSubtree(
bundleIdentifier: String,
relativePath: String,
destinationDirectoryURL: URL
) async throws {
try await Task.detached(priority: .userInitiated) {
var error: NSError?
let didCopy = WMMCopyFirstConnectedDeviceAppSubtreeToLocalDirectory(
bundleIdentifier,
relativePath,
destinationDirectoryURL,
&error
)
if !didCopy {
throw error ?? NSError(
domain: "AppleMobileDeviceAccess",
code: 2,
userInfo: [NSLocalizedDescriptionKey: "The MobileDevice subtree mirror failed."]
)
}
}.value
}
static func listApplications() async throws -> [AppleMobileDeviceApplicationSummary] {
try await Task.detached(priority: .userInitiated) {
var error: NSError?
guard let response = WMMCopyFirstConnectedDeviceApplicationList(&error) else {
throw error ?? NSError(
domain: "AppleMobileDeviceAccess",
code: 3,
userInfo: [NSLocalizedDescriptionKey: "The MobileDevice application listing failed."]
)
}
guard let rawApplications = response["applications"] as? [[String: Any]] else {
throw NSError(
domain: "AppleMobileDeviceAccess",
code: 4,
userInfo: [NSLocalizedDescriptionKey: "The MobileDevice application listing returned an unexpected payload."]
)
}
return rawApplications.compactMap { application in
guard
let bundleIdentifier = application["bundleIdentifier"] as? String,
let displayName = application["displayName"] as? String
else {
return nil
}
return AppleMobileDeviceApplicationSummary(
bundleIdentifier: bundleIdentifier,
displayName: displayName,
fileSharingEnabled: flexibleBool(from: application["uiFileSharingEnabled"]),
supportsOpeningDocumentsInPlace: flexibleBool(from: application["supportsOpeningDocumentsInPlace"])
)
}
}.value
}
static func listDirectory(
bundleIdentifier: String,
relativePath: String
) async throws -> [String] {
try await Task.detached(priority: .userInitiated) {
var error: NSError?
guard let response = WMMCopyFirstConnectedDeviceAppDirectoryListing(
bundleIdentifier,
relativePath,
&error
) else {
throw error ?? NSError(
domain: "AppleMobileDeviceAccess",
code: 7,
userInfo: [NSLocalizedDescriptionKey: "The MobileDevice directory listing failed."]
)
}
return (response["entries"] as? [String] ?? []).filter { $0 != "." && $0 != ".." }
}.value
}
static func fileData(
bundleIdentifier: String,
relativePath: String
) async throws -> Data {
try await Task.detached(priority: .userInitiated) {
var error: NSError?
guard let data = WMMCopyFirstConnectedDeviceAppFileData(
bundleIdentifier,
relativePath,
&error
) else {
throw error ?? NSError(
domain: "AppleMobileDeviceAccess",
code: 8,
userInfo: [NSLocalizedDescriptionKey: "The MobileDevice file read failed."]
)
}
return data as Data
}.value
}
static func minecraftLibrarySnapshot(
bundleIdentifier: String,
relativePath: String
) async throws -> [AppleMobileMinecraftLibraryItemSummary] {
try await Task.detached(priority: .userInitiated) {
var error: NSError?
guard let response = WMMCopyFirstConnectedDeviceMinecraftLibrarySnapshot(
bundleIdentifier,
relativePath,
&error
) else {
throw error ?? NSError(
domain: "AppleMobileDeviceAccess",
code: 5,
userInfo: [NSLocalizedDescriptionKey: "The MobileDevice Minecraft library scan failed."]
)
}
guard let rawItems = response["items"] as? [[String: Any]] else {
throw NSError(
domain: "AppleMobileDeviceAccess",
code: 6,
userInfo: [NSLocalizedDescriptionKey: "The MobileDevice Minecraft library scan returned an unexpected payload."]
)
}
return rawItems.compactMap { item in
guard
let contentType = item["contentType"] as? String,
let collectionFolderName = item["collectionFolderName"] as? String,
let relativePath = item["relativePath"] as? String,
let folderName = item["folderName"] as? String,
let displayName = item["displayName"] as? String
else {
return nil
}
return AppleMobileMinecraftLibraryItemSummary(
contentType: contentType,
collectionFolderName: collectionFolderName,
relativePath: relativePath,
folderName: folderName,
displayName: displayName,
packUUID: (item["packUUID"] as? String)?.lowercased(),
packVersion: item["packVersion"] as? String,
minimumEngineVersion: item["minimumEngineVersion"] as? String
)
}
}.value
}
static func pathMetrics(
bundleIdentifier: String,
relativePath: String
) async throws -> AppleMobileDevicePathMetrics {
try await Task.detached(priority: .utility) {
var error: NSError?
guard let response = WMMCopyFirstConnectedDeviceAppPathMetrics(
bundleIdentifier,
relativePath,
&error
) else {
throw error ?? NSError(
domain: "AppleMobileDeviceAccess",
code: 9,
userInfo: [NSLocalizedDescriptionKey: "The MobileDevice path metrics lookup failed."]
)
}
let rawSize = response["sizeBytes"]
let sizeBytes: Int64?
switch rawSize {
case let number as NSNumber:
sizeBytes = number.int64Value
case let value as Int64:
sizeBytes = value
case let value as Int:
sizeBytes = Int64(value)
default:
sizeBytes = nil
}
return AppleMobileDevicePathMetrics(
sizeBytes: sizeBytes,
modifiedDate: response["modifiedDate"] as? Date
)
}.value
}
private static func flexibleBool(from value: Any?) -> Bool {
switch value {
case let value as Bool:
return value
case let value as NSNumber:
return value.boolValue
case let value as NSString:
return value.boolValue
case let value as String:
return NSString(string: value).boolValue
default:
return false
}
}
}