@@ -19,6 +19,8 @@ @interface ModManagerViewController ()
1919@property (nonatomic ) NSArray <NSMutableDictionary *> *mods;
2020@property (nonatomic ) BOOL checkingUpdates;
2121@property (nonatomic ) BOOL checkingDependencies;
22+ @property (nonatomic ) NSInteger updateCheckCompleted;
23+ @property (nonatomic ) NSInteger updateCheckTotal;
2224@end
2325
2426@implementation ModManagerViewController
@@ -73,6 +75,20 @@ - (void)actionAddMod {
7375 [self .navigationController pushViewController: vc animated: YES ];
7476}
7577
78+ - (void )updateBusyPrompt {
79+ if (self.checkingDependencies ) {
80+ self.navigationItem .prompt = @" Checking dependencies..." ;
81+ } else if (self.checkingUpdates ) {
82+ if (self.updateCheckTotal > 0 ) {
83+ self.navigationItem .prompt = [NSString stringWithFormat: @" Checking updates %ld /%ld ..." , (long )self .updateCheckCompleted, (long )self .updateCheckTotal];
84+ } else {
85+ self.navigationItem .prompt = @" Checking updates..." ;
86+ }
87+ } else {
88+ self.navigationItem .prompt = nil ;
89+ }
90+ }
91+
7692- (void )setCheckingUpdates : (BOOL )checkingUpdates {
7793 _checkingUpdates = checkingUpdates;
7894 UIBarButtonItem *updateItem = self.navigationItem .rightBarButtonItems .lastObject ;
@@ -85,36 +101,80 @@ - (void)setCheckingUpdates:(BOOL)checkingUpdates {
85101 updateItem.customView = nil ;
86102 updateItem.image = [UIImage systemImageNamed: @" arrow.triangle.2.circlepath" ];
87103 }
104+ [self updateBusyPrompt ];
88105}
89106
90107- (void )setCheckingDependencies : (BOOL )checkingDependencies {
91108 _checkingDependencies = checkingDependencies;
92109 self.tableView .userInteractionEnabled = !checkingDependencies;
93- self.navigationItem .prompt = checkingDependencies ? @" Checking dependencies..." : nil ;
110+ [self updateBusyPrompt ];
111+ }
112+
113+ - (BOOL )modCanCheckForUpdate : (NSDictionary *)mod {
114+ NSString *source = mod[@" source" ];
115+ return [source isKindOfClass: NSString .class] &&
116+ ![source isEqualToString: @" manual" ] &&
117+ mod[@" projectId" ] &&
118+ ![mod[@" missing" ] boolValue ];
94119}
95120
96121- (void )actionCheckUpdates {
97122 if (self.checkingUpdates ) {
98123 return ;
99124 }
100125 NSArray *mods = [self .store installedMods ];
126+ NSMutableArray *updatedMods = [NSMutableArray arrayWithCapacity: mods.count];
127+ NSMutableArray <NSNumber *> *updateIndexes = [NSMutableArray new ];
128+ for (NSUInteger i = 0 ; i < mods.count ; i++) {
129+ NSDictionary *mod = mods[i];
130+ [updatedMods addObject: mod.mutableCopy];
131+ if ([self modCanCheckForUpdate: mod]) {
132+ [updateIndexes addObject: @(i)];
133+ }
134+ }
135+ self.updateCheckCompleted = 0 ;
136+ self.updateCheckTotal = updateIndexes.count ;
101137 self.checkingUpdates = YES ;
102- dispatch_async (dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT , 0 ), ^{
103- NSMutableArray *updatedMods = [NSMutableArray new ];
104- for (NSMutableDictionary *mod in mods) {
138+ if (updateIndexes.count == 0 ) {
139+ self.checkingUpdates = NO ;
140+ self.mods = updatedMods;
141+ [self .tableView reloadData ];
142+ return ;
143+ }
144+
145+ dispatch_queue_t queue = dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT , 0 );
146+ dispatch_group_t group = dispatch_group_create ();
147+ dispatch_semaphore_t limit = dispatch_semaphore_create (4 );
148+ NSDictionary *profileInfo = self.store .profileInfo ;
149+ for (NSNumber *indexNumber in updateIndexes) {
150+ dispatch_group_async (group, queue, ^{
151+ dispatch_semaphore_wait (limit, DISPATCH_TIME_FOREVER );
152+ NSUInteger index = indexNumber.unsignedIntegerValue ;
153+ NSDictionary *mod = mods[index];
154+ ModManagerAPI *api = [ModManagerAPI new ];
105155 NSError *error = nil ;
106- NSDictionary *update = [self . api latestVersionForInstalledMod: mod profileInfo: self .store. profileInfo error: &error];
156+ NSDictionary *update = [api latestVersionForInstalledMod: mod profileInfo: profileInfo error: &error];
107157 NSMutableDictionary *copy = mod.mutableCopy ;
108158 if (update) {
109159 copy[@" availableUpdate" ] = update;
160+ } else {
161+ [copy removeObjectForKey: @" availableUpdate" ];
110162 }
111- [updatedMods addObject: copy];
112- }
113- dispatch_async (dispatch_get_main_queue (), ^{
114- self.checkingUpdates = NO ;
115- self.mods = updatedMods;
116- [self .tableView reloadData ];
163+ @synchronized (updatedMods) {
164+ updatedMods[index] = copy;
165+ }
166+ dispatch_async (dispatch_get_main_queue (), ^{
167+ self.updateCheckCompleted += 1 ;
168+ [self updateBusyPrompt ];
169+ });
170+ dispatch_semaphore_signal (limit);
117171 });
172+ }
173+
174+ dispatch_group_notify (group, dispatch_get_main_queue (), ^{
175+ self.checkingUpdates = NO ;
176+ self.mods = updatedMods;
177+ [self .tableView reloadData ];
118178 });
119179}
120180
0 commit comments