├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── control ├── src ├── Tweak.m ├── zxUpdateManager.h └── zxUpdateManager.m └── zxUpdateNotifier.plist /.gitignore: -------------------------------------------------------------------------------- 1 | .theos/ 2 | packages/ 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [year] [fullname] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGET := iphone:clang:latest:9.0 2 | INSTALL_TARGET_PROCESSES = WhatsApp # used as testing app 3 | ARCHS = arm64 4 | 5 | include $(THEOS)/makefiles/common.mk 6 | 7 | TWEAK_NAME = zxUpdateNotifier 8 | 9 | $(TWEAK_NAME)_FILES = $(wildcard src/*.m) 10 | $(TWEAK_NAME)_CFLAGS = -fobjc-arc 11 | 12 | include $(THEOS_MAKE_PATH)/tweak.mk 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zxUpdateNotifier 2 | 3 | an injectable tweak that notifies you of app updates; useful when sideloading 4 | 5 | ## info 6 | 7 | completed for a [bounty](https://www.reddit.com/r/TweakBounty/comments/1cvl74s/1016_can_anyone_make_a_appstore_notifications/) ! 8 | 9 | ~~i'll totally open source it if someone decides to send me $5 through btc/ltc, tho :p (yes i am that desperate for money)~~ 10 | 11 | ~~or maybe i'll open source it soon enough if no one donates!!! maybe.~~ 12 | 13 | here you go guys! please still donate oh my god servers are expensive 14 | 15 | -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | Package: fyi.zxcvbn.updatenotifier 2 | Name: zxUpdateNotifier 3 | Version: 1.2 4 | Architecture: iphoneos-arm 5 | Description: an injectable tweak that detects when your IPA is out of date! 6 | Author: zxcvbn 7 | Maintainer: zxcvbn 8 | Section: Tweaks 9 | Depends: firmware (>= 9.0) 10 | -------------------------------------------------------------------------------- /src/Tweak.m: -------------------------------------------------------------------------------- 1 | #import "zxUpdateManager.h" 2 | 3 | __attribute__((constructor)) static void init() { 4 | @autoreleasepool { 5 | NSLog(@"[zxUpdateNotifier] calling validityCheck"); 6 | dispatch_async(dispatch_get_main_queue(), ^{ 7 | [zxUpdateManager validityCheck]; 8 | }); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/zxUpdateManager.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface zxUpdateManager : NSObject 4 | + (void)validityCheck; 5 | + (void)getAppInfoWithBundleId:(NSString *)bundleId currentVersion:(NSString *)cVersion; 6 | + (void)markInvalidWithMsg:(NSString *)msg text:(NSString *)text; 7 | + (void)notifyWithMsg:(NSString *)msg buttonText:(NSString *)bText handler:(void (^)(UIAlertAction *action))handler; 8 | @end 9 | -------------------------------------------------------------------------------- /src/zxUpdateManager.m: -------------------------------------------------------------------------------- 1 | #import "zxUpdateManager.h" 2 | 3 | @implementation zxUpdateManager 4 | + (void)validityCheck { 5 | // iirc some apps dont have CFBundleShortVersionString 6 | if ([[NSUserDefaults standardUserDefaults] boolForKey:@"zxAvoidUpdates"]) return; 7 | 8 | NSString *version = [NSBundle.mainBundle.infoDictionary objectForKey:@"CFBundleShortVersionString"]; 9 | if (version == nil) return 10 | [self markInvalidWithMsg:@"this app does not contain CFBundleShortVersionString and is incompatible with zxUpdateManager." 11 | text:@"okay"]; 12 | 13 | // avoid prompting twice 14 | NSDictionary *latestInfo = [[NSUserDefaults standardUserDefaults] objectForKey:@"zxAppInfo"]; 15 | if (latestInfo == nil || ![latestInfo[@"lastSeen"] isEqualToString:version]) { 16 | NSLog(@"[zxUpdateManager] checking for updates with version: %@", version); 17 | [self getAppInfoWithBundleId:NSBundle.mainBundle.bundleIdentifier currentVersion:version]; 18 | } 19 | } 20 | 21 | + (void)getAppInfoWithBundleId:(NSString *)bundleId currentVersion:(NSString *)cVersion { 22 | // a random param (`hi`) is needed to avoid getting a cached response 23 | NSString *reqURL = 24 | [NSString stringWithFormat:@"https://itunes.apple.com/lookup?limit=1&hi=%@&bundleId=%@", NSUUID.UUID, bundleId]; 25 | NSURLRequest *req = [NSURLRequest requestWithURL:[NSURL URLWithString:reqURL]]; 26 | 27 | [[[NSURLSession sharedSession] dataTaskWithRequest:req completionHandler:^(NSData *data, id httpResp, NSError *error) { 28 | if (error) return 29 | [self notifyWithMsg:[NSString stringWithFormat:@"error checking for updates:\n%@", error] 30 | buttonText:@"strange" 31 | handler:nil]; 32 | 33 | NSDictionary *resp = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; 34 | 35 | if ([resp[@"resultCount"] isEqual:@0]) return 36 | [self markInvalidWithMsg:@"this app was not found on the app store.\n\np.s. did you change the bundle id?" 37 | text:@"okay"]; 38 | 39 | NSDictionary *latestInfo = @{ 40 | @"id": resp[@"results"][0][@"trackId"], 41 | @"version": resp[@"results"][0][@"version"], 42 | @"lastSeen": cVersion // used for preventing dupe notifs 43 | }; 44 | 45 | NSLog(@"[zxUpdateManager] latestInfo: %@", latestInfo); 46 | if (![latestInfo[@"version"] isEqualToString:cVersion]) { 47 | [[NSUserDefaults standardUserDefaults] setObject:latestInfo forKey:@"zxAppInfo"]; 48 | 49 | NSString 50 | *updMsg = 51 | [NSString stringWithFormat:@"an update is available!\n\nv%@ -> v%@", cVersion, latestInfo[@"version"]], 52 | *storeLink = 53 | [NSString stringWithFormat:@"https://apps.apple.com/app/id%@", latestInfo[@"id"]]; 54 | 55 | [self notifyWithMsg:updMsg 56 | buttonText:@"let me see!" 57 | handler:^(UIAlertAction *action) { 58 | [[UIApplication sharedApplication] 59 | openURL:[NSURL URLWithString:storeLink] 60 | options:@{} 61 | completionHandler:nil]; 62 | }]; 63 | } 64 | }] resume]; 65 | } 66 | 67 | + (void)markInvalidWithMsg:(NSString *)msg text:(NSString *)text { 68 | [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"zxAvoidUpdates"]; 69 | return [self notifyWithMsg:msg 70 | buttonText:text 71 | handler:nil]; 72 | } 73 | 74 | + (void)notifyWithMsg:(NSString *)msg buttonText:(NSString *)bText handler:(void (^)(UIAlertAction *action))handler { 75 | NSLog(@"[zxUpdateManager] making popup with msg: %@", msg); 76 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 77 | UIAlertController *alert = 78 | [UIAlertController alertControllerWithTitle:@"zxUpdateNotifier" 79 | message:msg 80 | preferredStyle:UIAlertControllerStyleAlert]; 81 | 82 | UIAlertAction *act = 83 | [UIAlertAction actionWithTitle:bText 84 | style:UIAlertActionStyleDefault 85 | handler:handler]; 86 | 87 | [alert addAction:act]; 88 | 89 | UIViewController *rvc = UIApplication.sharedApplication.keyWindow.rootViewController; 90 | while (rvc.presentedViewController) rvc = rvc.presentedViewController; 91 | [rvc presentViewController:alert animated:YES completion:nil]; 92 | }); 93 | } 94 | @end 95 | -------------------------------------------------------------------------------- /zxUpdateNotifier.plist: -------------------------------------------------------------------------------- 1 | { Filter = { Bundles = ( "net.whatsapp.WhatsApp" ); }; } 2 | --------------------------------------------------------------------------------