11// installApp.swift
22import Foundation
33import IDeviceSwift
4+ import Combine
45
56/// Installs a signed IPA on the device using InstallationProxy
6- public func installApp( from ipaURL: URL ) async throws {
7+ public func installApp( from ipaURL: URL ) async throws -> AsyncThrowingStream < ( progress : Double , status : String ) , Error > {
78 print ( " Installing app from: \( ipaURL. path) " )
89
9- // Start heartbeat to keep connection alive during long install
10- HeartbeatManager . shared. start ( )
10+ return AsyncThrowingStream { continuation in
11+ Task {
12+ // Start heartbeat to keep connection alive during long install
13+ HeartbeatManager . shared. start ( )
1114
12- // Create view model to receive installation status updates
13- let viewModel = InstallerStatusViewModel ( )
15+ // Create view model to receive installation status updates
16+ let viewModel = InstallerStatusViewModel ( )
17+
18+ // Observe progress updates
19+ var cancellables = Set < AnyCancellable > ( )
20+
21+ // Combine progress updates into a single stream
22+ viewModel. $uploadProgress
23+ . combineLatest ( viewModel. $installProgress)
24+ . sink { uploadProgress, installProgress in
25+ let overallProgress = ( uploadProgress + installProgress) / 2.0
26+ let currentStage : String
27+
28+ if uploadProgress < 1.0 {
29+ currentStage = " 📤 Uploading... "
30+ } else if installProgress < 1.0 {
31+ currentStage = " 📲 Installing... "
32+ } else {
33+ currentStage = " 🏁 Finalizing... "
34+ }
35+
36+ continuation. yield ( ( progress: overallProgress, status: currentStage) )
37+ }
38+ . store ( in: & cancellables)
39+
40+ // Handle completion
41+ viewModel. $status
42+ . sink { installerStatus in
43+ switch installerStatus {
44+ case . completed( . success) :
45+ continuation. yield ( ( progress: 1.0 , status: " Successfully installed app! " ) )
46+ continuation. finish ( )
47+ cancellables. removeAll ( )
48+
49+ case . completed( . failure( let error) ) :
50+ continuation. finish ( throwing: error)
51+ cancellables. removeAll ( )
52+
53+ case . broken( let error) :
54+ continuation. finish ( throwing: error)
55+ cancellables. removeAll ( )
56+
57+ default :
58+ break
59+ }
60+ }
61+ . store ( in: & cancellables)
1462
15- // Create the installation proxy
16- let installer = await InstallationProxy ( viewModel: viewModel)
63+ do {
64+ // Create the installation proxy
65+ let installer = await InstallationProxy ( viewModel: viewModel)
66+
67+ // Perform the actual installation
68+ try await installer. install ( at: ipaURL)
69+
70+ // Wait a moment for completion
71+ try await Task . sleep ( nanoseconds: 1_000_000_000 ) // 1 second
72+
73+ print ( " Installation completed successfully! " )
74+ } catch {
75+ continuation. finish ( throwing: error)
76+ cancellables. removeAll ( )
77+ }
78+ }
79+ }
80+ }
1781
18- // Perform the actual installation
19- try await installer. install ( at: ipaURL)
20-
21- print ( " Installation completed successfully! " )
22- }
0 commit comments