Skip to content

Commit f2f9ae6

Browse files
Add update alert for homebrew and github release install
1 parent 585da81 commit f2f9ae6

5 files changed

Lines changed: 145 additions & 44 deletions

File tree

AppBox/AppDelegate.m

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,11 @@ - (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
6060
[UpdateHandler isNewVersionAvailableCompletion:^(bool available, NSURL *url) {
6161
if (available){
6262
if (![UserData updateAlertEnable]){
63-
[UpdateHandler showUpdateAlertWithUpdateURL:url];
63+
if (url == nil && [UpdateHandler isInstalledViaHomebrew]) {
64+
[UpdateHandler showHomebrewUpdateAlert];
65+
} else {
66+
[UpdateHandler showUpdateAlertWithUpdateURL:url];
67+
}
6468
}
6569
}
6670
}];

AppBox/Common/Constants.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
#define abDocumentationURL @"https://docs.getappbox.com"
1919
#define abLicenseURL @"https://github.com/getappbox/AppBox-iOSAppsWirelessInstallation/blob/master/LICENSE.md"
2020
#define abGitHubLatestRelease @"https://api.github.com/repos/getappbox/AppBox-iOSAppsWirelessInstallation/releases/latest"
21+
#define abGitHubLatestReleaseURL @"https://github.com/getappbox/AppBox-iOSAppsWirelessInstallation/releases/latest"
22+
#define abHomebrewCaskAPI @"https://formulae.brew.sh/api/cask/appbox.json"
2123
#define abTwitterURL @"https://twitter.com/AppBoxHQ"
2224
#define abSlackImage @"https://getappbox.com/images/AppBoxIcon.png"
2325
#define abWebHookSetupURL @"https://my.slack.com/apps/new/A0F7XDUAZ-incoming-webhooks"

AppBox/Common/UpdateHandler/UpdateHandler.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010

1111
@interface UpdateHandler : NSObject
1212

13+
+ (BOOL)isInstalledViaHomebrew;
1314
+ (void)showAlreadyUptoDateAlert;
1415
+ (void)showUpdateAlertWithUpdateURL:(NSURL *)url;
16+
+ (void)showHomebrewUpdateAlert;
1517
+ (void)isNewVersionAvailableCompletion:(void (^)(bool available, NSURL *url))completion;
1618

1719
@end

AppBox/Common/UpdateHandler/UpdateHandler.m

Lines changed: 131 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,76 @@
1010

1111
@implementation UpdateHandler
1212

13+
//MARK: - Homebrew Detection
14+
15+
+ (BOOL)isInstalledViaHomebrew {
16+
NSFileManager *fileManager = [NSFileManager defaultManager];
17+
// Check Apple Silicon Homebrew path
18+
if ([fileManager fileExistsAtPath:@"/opt/homebrew/Caskroom/appbox"]) {
19+
return YES;
20+
}
21+
// Check Intel Homebrew path
22+
if ([fileManager fileExistsAtPath:@"/usr/local/Caskroom/appbox"]) {
23+
return YES;
24+
}
25+
return NO;
26+
}
27+
1328
//MARK: - Check for update
1429

30+
+ (void)showHomebrewUpdateAlert {
31+
NSString *command = @"brew upgrade --cask appbox";
32+
33+
NSAlert *alert = [[NSAlert alloc] init];
34+
[alert setMessageText:@"New Version Available"];
35+
[alert setInformativeText:@"A newer version of \"AppBox\" is available.\n\nSince you installed AppBox via Homebrew, please run this command in Terminal:"];
36+
[alert setAlertStyle:NSAlertStyleInformational];
37+
[alert setAccessoryView:[self commandTextFieldWithString:command width:250]];
38+
[alert addButtonWithTitle:@"Copy & Close"];
39+
[alert addButtonWithTitle:@"Release Notes"];
40+
[alert addButtonWithTitle:@"Close"];
41+
NSModalResponse response = [alert runModal];
42+
if (response == NSAlertFirstButtonReturn) {
43+
[[NSPasteboard generalPasteboard] clearContents];
44+
[[NSPasteboard generalPasteboard] setString:command forType:NSPasteboardTypeString];
45+
} else if (response == NSAlertSecondButtonReturn) {
46+
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:abGitHubLatestReleaseURL]];
47+
}
48+
}
49+
1550
+ (void)showUpdateAlertWithUpdateURL:(NSURL *)url{
51+
NSString *command = @"curl -s https://getappbox.com/install.sh | bash";
52+
1653
NSAlert *alert = [[NSAlert alloc] init];
17-
[alert setMessageText: @"New Version Available"];
18-
[alert setInformativeText:@"A newer version of the \"AppBox\" is available. Do you want to update it? \n\n\n"];
19-
[alert setAlertStyle:NSAlertStyleInformational];
20-
[alert addButtonWithTitle:@"YES"];
21-
[alert addButtonWithTitle:@"NO"];
22-
if ([alert runModal] == NSAlertFirstButtonReturn){
23-
[[NSWorkspace sharedWorkspace] openURL:url];
54+
[alert setMessageText:@"New Version Available"];
55+
[alert setInformativeText:@"A newer version of \"AppBox\" is available.\n\nTo update, run this command in Terminal:"];
56+
[alert setAlertStyle:NSAlertStyleInformational];
57+
[alert setAccessoryView:[self commandTextFieldWithString:command width:400]];
58+
[alert addButtonWithTitle:@"Copy & Close"];
59+
[alert addButtonWithTitle:@"Release Notes"];
60+
[alert addButtonWithTitle:@"Close"];
61+
NSModalResponse response = [alert runModal];
62+
if (response == NSAlertFirstButtonReturn){
63+
[[NSPasteboard generalPasteboard] clearContents];
64+
[[NSPasteboard generalPasteboard] setString:command forType:NSPasteboardTypeString];
65+
} else if (response == NSAlertSecondButtonReturn) {
66+
[[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString:abGitHubLatestReleaseURL]];
2467
}
2568
}
2669

70+
+ (NSView *)commandTextFieldWithString:(NSString *)command width:(CGFloat)width {
71+
NSTextField *textField = [[NSTextField alloc] initWithFrame:NSMakeRect(0, 0, width, 20)];
72+
[textField setStringValue:[NSString stringWithFormat:@"%@", command]];
73+
[textField setEditable:NO];
74+
[textField setSelectable:YES];
75+
[textField setBordered:YES];
76+
[textField setBezelStyle:NSTextFieldRoundedBezel];
77+
[textField setFont:[NSFont monospacedSystemFontOfSize:12.0 weight:NSFontWeightMedium]];
78+
[textField setBackgroundColor:[NSColor controlBackgroundColor]];
79+
[textField setAlignment:NSTextAlignmentCenter];
80+
return textField;
81+
}
82+
2783
+ (void)showAlreadyUptoDateAlert{
2884
NSString *versionString = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBundleShortVersionString"];
2985
[Common showAlertWithTitle:@"You’re up-to-date!" andMessage:[NSString stringWithFormat:@"AppBox %@ is currently the newest version available.", versionString]];
@@ -32,47 +88,80 @@ + (void)showAlreadyUptoDateAlert{
3288
+ (void)isNewVersionAvailableCompletion:(void (^)(bool available, NSURL *url))completion{
3389
@try {
3490
DDLogDebug(@"Checking for new version...");
35-
[NetworkHandler requestWithURL:abGitHubLatestRelease withParameters:nil andRequestType:RequestGET andCompletetion:^(id responseObj, NSInteger httpStatus, NSError *error) {
36-
//handle error and check for all required keys
37-
@try {
38-
if (error == nil && [responseObj isKindOfClass:[NSDictionary class]] &&
39-
[((NSDictionary *)responseObj).allKeys containsObject:@"tag_name"] &&
40-
[((NSDictionary *)responseObj).allKeys containsObject:@"html_url"]){
41-
42-
//get tag name, because it's always be latest version
43-
NSString *tag = [responseObj valueForKey:@"tag_name"];
44-
45-
//get version string from bundle info.plist
46-
NSString *versionString = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBundleShortVersionString"];
47-
if (!versionString || !tag) {
48-
completion(false, nil);
49-
return;
50-
}
51-
52-
//log current and latest version
53-
DDLogDebug(@"Current Version - %@ <=> Latest Version - %@", versionString, tag);
54-
55-
// Use proper numeric version comparison
56-
NSString *latestVersion = [self extractVersionString:tag];
57-
NSString *currentVersion = [self extractVersionString:versionString];
58-
NSComparisonResult result = [latestVersion compare:currentVersion options:NSNumericSearch];
59-
60-
//return result based on version strings
61-
completion((result == NSOrderedDescending), [NSURL URLWithString:[responseObj valueForKey:@"html_url"]]);
62-
}else{
63-
completion(false, nil);
64-
}
65-
}
66-
@catch (NSException *exception) {
67-
DDLogError(@"Exception %@",exception.abDescription);
68-
}
69-
}];
91+
92+
if ([self isInstalledViaHomebrew]) {
93+
// Check Homebrew API for latest available version
94+
[NetworkHandler requestWithURL:abHomebrewCaskAPI withParameters:nil andRequestType:RequestGET andCompletetion:^(id responseObj, NSInteger httpStatus, NSError *error) {
95+
@try {
96+
if (error == nil && [responseObj isKindOfClass:[NSDictionary class]] &&
97+
[((NSDictionary *)responseObj).allKeys containsObject:@"version"]) {
98+
99+
NSString *latestVersion = [responseObj valueForKey:@"version"];
100+
NSString *versionString = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBundleShortVersionString"];
101+
if (!versionString || !latestVersion) {
102+
completion(false, nil);
103+
return;
104+
}
105+
106+
DDLogDebug(@"Current Version - %@ <=> Homebrew Latest Version - %@", versionString, latestVersion);
107+
108+
NSString *cleanLatest = [self extractVersionString:latestVersion];
109+
NSString *cleanCurrent = [self extractVersionString:versionString];
110+
NSComparisonResult result = [cleanLatest compare:cleanCurrent options:NSNumericSearch];
111+
112+
completion((result == NSOrderedDescending), nil);
113+
} else {
114+
// Homebrew API failed — fallback to GitHub release check
115+
DDLogInfo(@"Homebrew API failed, falling back to GitHub release check.");
116+
[self checkGitHubReleaseWithCompletion:completion];
117+
}
118+
}
119+
@catch (NSException *exception) {
120+
DDLogError(@"Exception %@",exception.abDescription);
121+
[self checkGitHubReleaseWithCompletion:completion];
122+
}
123+
}];
124+
} else {
125+
[self checkGitHubReleaseWithCompletion:completion];
126+
}
70127
} @catch (NSException *exception) {
71128
completion(false, nil);
72129
DDLogInfo(@"Exception %@",exception.abDescription);
73130
}
74131
}
75132

133+
+ (void)checkGitHubReleaseWithCompletion:(void (^)(bool available, NSURL *url))completion {
134+
[NetworkHandler requestWithURL:abGitHubLatestRelease withParameters:nil andRequestType:RequestGET andCompletetion:^(id responseObj, NSInteger httpStatus, NSError *error) {
135+
@try {
136+
if (error == nil && [responseObj isKindOfClass:[NSDictionary class]] &&
137+
[((NSDictionary *)responseObj).allKeys containsObject:@"tag_name"] &&
138+
[((NSDictionary *)responseObj).allKeys containsObject:@"html_url"]){
139+
140+
NSString *tag = [responseObj valueForKey:@"tag_name"];
141+
NSString *versionString = [[[NSBundle mainBundle] infoDictionary] valueForKey:@"CFBundleShortVersionString"];
142+
if (!versionString || !tag) {
143+
completion(false, nil);
144+
return;
145+
}
146+
147+
DDLogDebug(@"Current Version - %@ <=> GitHub Latest Version - %@", versionString, tag);
148+
149+
NSString *latestVersion = [self extractVersionString:tag];
150+
NSString *currentVersion = [self extractVersionString:versionString];
151+
NSComparisonResult result = [latestVersion compare:currentVersion options:NSNumericSearch];
152+
153+
completion((result == NSOrderedDescending), [NSURL URLWithString:[responseObj valueForKey:@"html_url"]]);
154+
} else {
155+
completion(false, nil);
156+
}
157+
}
158+
@catch (NSException *exception) {
159+
DDLogError(@"Exception %@",exception.abDescription);
160+
completion(false, nil);
161+
}
162+
}];
163+
}
164+
76165
+ (NSString *)extractVersionString:(NSString *)input {
77166
if (!input) return @"0";
78167
// Remove everything except digits and dots

AppBox/ViewController/MenuHandler/NSApplication+MenuHandler.m

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ @implementation NSApplication (MenuHandler)
1515
- (IBAction)checkForUpdateTapped:(NSMenuItem *)sender {
1616
[UpdateHandler isNewVersionAvailableCompletion:^(bool available, NSURL *url) {
1717
if (available){
18-
[UpdateHandler showUpdateAlertWithUpdateURL:url];
18+
if (url == nil && [UpdateHandler isInstalledViaHomebrew]) {
19+
[UpdateHandler showHomebrewUpdateAlert];
20+
} else {
21+
[UpdateHandler showUpdateAlertWithUpdateURL:url];
22+
}
1923
}else{
2024
[UpdateHandler showAlreadyUptoDateAlert];
2125
}

0 commit comments

Comments
 (0)