@@ -37,57 +37,66 @@ final class RepoViewModel: ObservableObject {
3737 @Published var isLoading : Bool = false
3838 @Published var errorMessage : String ? = nil
3939
40- private let sourceURL : URL
40+ private let sourceURLs : [ URL ] // <-- multiple sources
4141
42- init ( sourceURL : URL ) {
43- self . sourceURL = sourceURL
44- Task { await load ( ) }
42+ init ( sourceURLs : [ URL ] ) {
43+ self . sourceURLs = sourceURLs
44+ Task { await loadAllSources ( ) }
4545 }
4646
47- func load ( ) async {
47+ func loadAllSources ( ) async {
4848 isLoading = true
4949 errorMessage = nil
5050 defer { isLoading = false }
5151
52- do {
53- var request = URLRequest ( url: sourceURL)
54- request. setValue ( " AppTestersListView/1.0 (iOS) " , forHTTPHeaderField: " User-Agent " )
55-
56- let ( data, response) = try await URLSession . shared. data ( for: request)
57- if let http = response as? HTTPURLResponse , !( 200 ... 299 ) . contains ( http. statusCode) {
58- throw NSError ( domain: " RepoFetcher " , code: http. statusCode, userInfo: [ NSLocalizedDescriptionKey: " HTTP \( http. statusCode) " ] )
59- }
60-
61- let decoder = JSONDecoder ( )
62-
63- if let source = try ? decoder. decode ( AltSource . self, from: data) , let apps = source. apps {
64- self . apps = apps
65- return
66- }
67-
68- if let appsArray = try ? decoder. decode ( [ AltApp ] . self, from: data) {
69- self . apps = appsArray
70- return
71- }
72-
73- if let jsonObject = try JSONSerialization . jsonObject ( with: data) as? [ String : Any ] ,
74- let appsFragment = jsonObject [ " apps " ] {
75- let fragmentData = try JSONSerialization . data ( withJSONObject: appsFragment)
76- let appsArray = try decoder. decode ( [ AltApp ] . self, from: fragmentData)
77- self . apps = appsArray
78- return
52+ var combinedApps : [ AltApp ] = [ ]
53+ var errors : [ String ] = [ ]
54+
55+ for url in sourceURLs {
56+ do {
57+ var request = URLRequest ( url: url)
58+ request. setValue ( " AppTestersListView/1.0 (iOS) " , forHTTPHeaderField: " User-Agent " )
59+
60+ let ( data, response) = try await URLSession . shared. data ( for: request)
61+ if let http = response as? HTTPURLResponse , !( 200 ... 299 ) . contains ( http. statusCode) {
62+ throw NSError ( domain: " RepoFetcher " , code: http. statusCode, userInfo: [ NSLocalizedDescriptionKey: " HTTP \( http. statusCode) " ] )
63+ }
64+
65+ let decoder = JSONDecoder ( )
66+
67+ if let source = try ? decoder. decode ( AltSource . self, from: data) , let apps = source. apps {
68+ combinedApps. append ( contentsOf: apps)
69+ continue
70+ }
71+
72+ if let appsArray = try ? decoder. decode ( [ AltApp ] . self, from: data) {
73+ combinedApps. append ( contentsOf: appsArray)
74+ continue
75+ }
76+
77+ if let jsonObject = try JSONSerialization . jsonObject ( with: data) as? [ String : Any ] ,
78+ let appsFragment = jsonObject [ " apps " ] {
79+ let fragmentData = try JSONSerialization . data ( withJSONObject: appsFragment)
80+ let appsArray = try decoder. decode ( [ AltApp ] . self, from: fragmentData)
81+ combinedApps. append ( contentsOf: appsArray)
82+ continue
83+ }
84+
85+ throw NSError ( domain: " RepoFetcher " , code: - 1 , userInfo: [ NSLocalizedDescriptionKey: " Unexpected JSON format. " ] )
86+
87+ } catch {
88+ errors. append ( " Failed \( url) : \( error. localizedDescription) " )
7989 }
80-
81- throw NSError ( domain: " RepoFetcher " , code: - 1 , userInfo: [ NSLocalizedDescriptionKey: " Unexpected JSON format. " ] )
82-
83- } catch {
84- self . errorMessage = " Failed to load repository: \( error. localizedDescription) "
85- self . apps = [ ]
90+ }
91+
92+ self . apps = combinedApps
93+ if !errors. isEmpty {
94+ self . errorMessage = errors. joined ( separator: " \n " )
8695 }
8796 }
8897
8998 func refresh( ) {
90- Task { await load ( ) }
99+ Task { await loadAllSources ( ) }
91100 }
92101}
93102
@@ -98,8 +107,8 @@ public struct AppsView: View {
98107 @FocusState private var searchFieldFocused : Bool
99108 @State private var selectedApp : AltApp ? = nil
100109
101- public init ( repoURL : URL = URL ( string: " https://repository.apptesters.org/ " ) !) {
102- _vm = StateObject ( wrappedValue: RepoViewModel ( sourceURL : repoURL ) )
110+ public init ( repoURLs : [ URL ] = [ URL ( string: " https://repository.apptesters.org/ " ) !] ) {
111+ _vm = StateObject ( wrappedValue: RepoViewModel ( sourceURLs : repoURLs ) )
103112 }
104113
105114 private var filteredApps : [ AltApp ] {
0 commit comments