mirror of
https://github.com/xtool-org/xtool.git
synced 2026-02-04 11:53:30 +01:00
129 lines
3.6 KiB
Swift
129 lines
3.6 KiB
Swift
//
|
|
// AppInstaller.swift
|
|
// XKit
|
|
//
|
|
// Created by Kabir Oberai on 13/11/19.
|
|
// Copyright © 2019 Kabir Oberai. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import SwiftyMobileDevice
|
|
|
|
public actor AppInstaller {
|
|
|
|
public enum Error: LocalizedError {
|
|
case userCancelled
|
|
case invalidURL
|
|
|
|
public var errorDescription: String? {
|
|
switch self {
|
|
case .userCancelled:
|
|
return NSLocalizedString(
|
|
"app_installer.error.user_cancelled",
|
|
value: "The user cancelled the operation",
|
|
comment: ""
|
|
)
|
|
case .invalidURL:
|
|
return NSLocalizedString(
|
|
"app_installer.error.invalid_url",
|
|
value: "Invalid URL",
|
|
comment: ""
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
public enum Stage: Sendable {
|
|
case connecting(Double)
|
|
case uploading(Double)
|
|
case installing(String, Double?)
|
|
|
|
public var displayName: String {
|
|
switch self {
|
|
case .connecting:
|
|
return NSLocalizedString(
|
|
"app_installer.stage.connecting",
|
|
value: "Connecting",
|
|
comment: ""
|
|
)
|
|
case .uploading:
|
|
// installing is a better name although this is
|
|
// technically uploading
|
|
return NSLocalizedString(
|
|
"app_installer.stage.installing",
|
|
value: "Installing",
|
|
comment: ""
|
|
)
|
|
case .installing:
|
|
return NSLocalizedString(
|
|
"app_installer.stage.verifying",
|
|
value: "Verifying",
|
|
comment: ""
|
|
)
|
|
}
|
|
}
|
|
|
|
public var displayProgress: Double {
|
|
switch self {
|
|
case .connecting(let progress):
|
|
return progress
|
|
case .uploading(let progress):
|
|
return progress
|
|
case .installing(_, let progress):
|
|
return progress ?? 0
|
|
}
|
|
}
|
|
}
|
|
|
|
public let ipa: URL
|
|
public let udid: String
|
|
public let connectionPreferences: Connection.Preferences
|
|
public init(
|
|
ipa: URL,
|
|
udid: String,
|
|
connectionPreferences: Connection.Preferences
|
|
) {
|
|
self.ipa = ipa
|
|
self.udid = udid
|
|
self.connectionPreferences = connectionPreferences
|
|
}
|
|
|
|
// synchronous
|
|
public func install(progress: @escaping @Sendable (Stage) -> Void) async throws {
|
|
let connection = try await Connection.connection(
|
|
forUDID: udid,
|
|
preferences: connectionPreferences
|
|
) {
|
|
progress(.connecting($0 * 4/6))
|
|
}
|
|
|
|
try Task.checkCancellation()
|
|
|
|
let uploader = try await IPAUploader(connection: connection)
|
|
progress(.connecting(5/6))
|
|
|
|
try Task.checkCancellation()
|
|
|
|
// we need to start the installer quickly because sometimes it fails if we do it after
|
|
// uploading the ipa to the device
|
|
let installer = try await IPAInstaller(connection: connection)
|
|
progress(.connecting(6/6))
|
|
|
|
try Task.checkCancellation()
|
|
|
|
let uploaded = try await uploader.upload(app: ipa) { currentProgress in
|
|
progress(.uploading(currentProgress))
|
|
}
|
|
defer { uploaded.delete() }
|
|
|
|
try Task.checkCancellation()
|
|
|
|
try await installer.install(uploaded: uploaded) { currentProgress in
|
|
progress(.installing(currentProgress.details, currentProgress.progress))
|
|
}
|
|
|
|
_ = connection
|
|
}
|
|
|
|
}
|