Skip to content
This repository was archived by the owner on Mar 7, 2026. It is now read-only.

Commit 7df88f4

Browse files
authored
Merge pull request #2 from ProStore-iOS/install-progress-and-user-friendly-errors
Add Install progress
2 parents e2af31a + 1e97d5b commit 7df88f4

11 files changed

Lines changed: 261 additions & 124 deletions

File tree

README.md

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,10 @@ The **best** alternative app store for iOS!
4646
---
4747

4848
## Gallery
49-
Coming soon!
50-
<!--
5149
<img src="gallery/Screenshot1.png" width="550">&nbsp;
5250
<img src="gallery/Screenshot2.png" width="550">&nbsp;
5351
<img src="gallery/Screenshot3.png" width="550">&nbsp;
54-
<img src="gallery/Screenshot4.png" width="550">&nbsp;
55-
<img src="gallery/Screenshot5.png" width="550">
56-
-->
52+
<img src="gallery/Screenshot4.png" width="550">
5753

5854
---
5955

@@ -81,3 +77,4 @@ Coming soon!
8177

8278

8379

80+

Sources/prostore/install/installApp.swift

Lines changed: 71 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,81 @@
11
// installApp.swift
22
import Foundation
33
import 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-
}

Sources/prostore/prostore.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,12 @@ struct MainSidebarView: View {
5050
URL(string: "https://repository.apptesters.org/")!,
5151
URL(string: "https://wuxu1.github.io/wuxu-complete.json")!,
5252
URL(string: "https://wuxu1.github.io/wuxu-complete-plus.json")!,
53-
URL(string: "https://raw.githubusercontent.com/RealBlackAstronaut/CelestialRepo/main/CelestialRepo.json")!,
53+
URL(string: "https://raw.githubusercontent.com/swaggyP36000/TrollStore-IPAs/main/apps_esign.json")!,
5454
URL(string: "https://ipa.cypwn.xyz/cypwn.json")!,
5555
URL(string: "https://quarksources.github.io/dist/quantumsource.min.json")!,
5656
URL(string: "https://bit.ly/quantumsource-plus-min")!,
57-
URL(string: "https://raw.githubusercontent.com/Neoncat-OG/TrollStore-IPAs/main/apps_esign.json")!,
58-
URL(string: "https://quarksources.github.io/altstore-complete.json")!
57+
URL(string: "https://aio.zxcvbn.fyi/r/repo.esign.json")!,
58+
URL(string: "https://raw.githubusercontent.com/Neoncat-OG/TrollStore-IPAs/main/apps_esign.json")!
5959
])
6060
.navigationTitle("Apps")
6161
.navigationBarTitleDisplayMode(.large)
@@ -84,4 +84,6 @@ enum SidebarItem: Hashable {
8484
case certificates
8585
case apps
8686
case about
87-
}
87+
88+
}
89+

0 commit comments

Comments
 (0)