已越狱的iOS上查看app是否已安装

想在MoeApps里显示该app是否已经安装。如果已安装则是直接显示“已安装”而不是程序的价格。
首先是看到了iHasApp这个库。实际测试发现这个只能看到app被他们收录而且是带有URL Scheme的才能够显示,也就是说并不能显示全部的app。不过这个倒是能通过审核上架。

但是咱的要求不是上架而是要显示完全部的app。
网上查了下,找到了这个
不过如果说知道BundleID(比如com.apple.mobilesafari)的话倒是能直接用,可惜MoeApps只知道纯数字的AppID,不过倒是可以通过先用AppID从iTuneslookupAPI获取到BundleID再进行判断是否已安装。


于是整个代码如下:

CheckAppInstalled.h

#import 
@class CheckAppInstalled;
@protocol CheckAppInstalledDelegate
@optional
- (void)CheckApp:(CheckAppInstalled *)app isInstalled:(BOOL)installed;
@end
@interface CheckAppInstalled : NSObject
-(CheckAppInstalled *)initWithdelegate:(NSObject *)delegate;
-(void)checkappinstalledWithAppID:(NSString *)appid;
-(void)checkappinstalledWithBundleID:(NSString *)bundleid;
-(void)cancelRequest;
@end

CheckAppInstalled.m

#import "CheckAppInstalled.h"
 @interface CheckAppInstalled ()
 @property (assign, nonatomic) NSObject  *delegate;
 @property (retain, nonatomic) NSURLConnection *connection;
 @end
 @implementation CheckAppInstalled
 -(CheckAppInstalled *)initWithdelegate:(NSObject  *)delegate{
     self = [super init];
     self.delegate = delegate;
     return self;
 }
 -(void)checkappinstalledWithAppID:(NSString *)appid{
     NSString *path = [NSString stringWithFormat:@iTunesSearshBaseURL,appid,[[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode],[[NSLocale currentLocale] objectForKey:NSLocaleCountryCode]];
     NSURL *url = [NSURL URLWithString:path];
     NSURLRequest *urlrequest = [NSURLRequest requestWithURL:url];
     self.connection = [[NSURLConnection alloc] initWithRequest:urlrequest delegate:self];
     [self.connection start];
 }
 -(void)checkappinstalledWithBundleID:(NSString *)bundleid{
     if (APCheckIfAppInstalled(bundleid)){
         NSLog(@"App installed: %@", bundleid);
         if([self.delegate respondsToSelector:@selector(CheckApp:isInstalled:)]){
             [self.delegate CheckApp:self isInstalled:YES];
         }
     }else{
         NSLog(@"App not installed: %@", bundleid);
         if([self.delegate respondsToSelector:@selector(CheckApp:isInstalled:)]){
             [self.delegate CheckApp:self isInstalled:NO];
         }
     }
 }
 -(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
     NSLog(@"%@",response);
 }
 -(void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
     NSLog(@"%@,%@",error.localizedDescription,connection);
 }
 -(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
     NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
     NSDictionary* json = [NSJSONSerialization JSONObjectWithData:data
                                                          options:kNilOptions
                                                            error:NULL];
     if ([[json objectForKey:@"resultCount"] integerValue] >= 1) {
         NSString *identifier = [[[json objectForKey:@"results"] objectAtIndex:0] valueForKey:@"bundleId"];
         if (APCheckIfAppInstalled(identifier)){
             NSLog(@"App installed: %@", identifier);
             if([self.delegate respondsToSelector:@selector(CheckApp:isInstalled:)]){
                 [self.delegate CheckApp:self isInstalled:YES];
             }
         }else{
             NSLog(@"App not installed: %@", identifier);
             if([self.delegate respondsToSelector:@selector(CheckApp:isInstalled:)]){
                 [self.delegate CheckApp:self isInstalled:NO];
             }
         }
     }
 }
 -(void)cancelRequest{
     [self.connection cancel];
     self.connection = nil;
 }
 // Declaration
 BOOL APCheckIfAppInstalled(NSString *bundleIdentifier); // Bundle identifier (eg. com.apple.mobilesafari) used to track apps
 // Implementation
 BOOL APCheckIfAppInstalled(NSString *bundleIdentifier){
 static NSString *const cacheFileName = @"com.apple.mobile.installation.plist";
 NSString *relativeCachePath = [[@"Library" stringByAppendingPathComponent: @"Caches"] stringByAppendingPathComponent: cacheFileName];
 NSDictionary *cacheDict = nil;
 NSString *path = nil;
 // Loop through all possible paths the cache could be in
 for (short i = 0; 1; i++)
 {
 switch (i) {
             case 0: // Jailbroken apps will find the cache here; their home directory is /var/mobile
                 path = [NSHomeDirectory() stringByAppendingPathComponent: relativeCachePath];
                 break;
             case 1: // App Store apps and Simulator will find the cache here; home (/var/mobile/) is 2 directories above sandbox folder
                 path = [[NSHomeDirectory() stringByAppendingPathComponent: @"../.."] stringByAppendingPathComponent: relativeCachePath];
                 break;
             case 2: // If the app is anywhere else, default to hardcoded /var/mobile/
                 path = [@"/var/mobile" stringByAppendingPathComponent: relativeCachePath];
                 break;
             default: // Cache not found (loop not broken)
                 return NO;
             break; }
 BOOL isDir = NO;
 if ([[NSFileManager defaultManager] fileExistsAtPath: path isDirectory: &isDir] && !isDir) // Ensure that file exists
 cacheDict = [NSDictionary dictionaryWithContentsOfFile: path];
 if (cacheDict) // If cache is loaded, then break the loop. If the loop is not "broken," it will return NO later (default: case)
 break;
 }
 NSDictionary *system = [cacheDict objectForKey: @"System"]; // First check all system (jailbroken) apps
 if ([system objectForKey: bundleIdentifier]) return YES;
 NSDictionary *user = [cacheDict objectForKey: @"User"]; // Then all the user (App Store /var/mobile/Applications) apps
 if ([user objectForKey: bundleIdentifier]) return YES;
 // If nothing returned YES already, we'll return NO now
 return NO;
 }
 @end



使用方法就是初始化:

CheckAppInstalled *checkapp = [[CheckAppInstalled alloc] initWithdelegate:self];//不要忘记引用CheckAppInstalledDelegate
     [checkapp checkappinstalledWithAppID:self.appid];


然后返回的时候如果是已经安装了的程序那么将按钮显示成APP INSTALLED:

-(void)CheckApp:(CheckAppInstalled *)app isInstalled:(BOOL)installed{
     if (installed == YES) {
         [self.storebtn setTitle:@"APP INSTALLED" forState:UIControlStateNormal];
     }
 }


不过别忘了当viewWillDisappear的时候记得:
[checkapp cancelRequest];
不然会导致未加载完就切换其他页面导致崩溃喔~

效果就是加载完app详细页面之后,如果app已经安装过那么稍等一会就能看到已安装的提示了。
不过因为需要访问一次iTunes的API所以速度较慢,如果说本身就已经知道了BundleID那么直接就可以显示该程序是否已经安装了。

另外关于app内开启其他app,这个似乎需要root权限也就是说app必须不能sandbox而且是在/Applications下才行(一般ipa包安装的app都是存在/var/mobile/Applications/下的,而且都是用UUID来区分。/Applicaitons下的一般都为系统级别程序,如iTunes、AppStore、Safari、Settings等,Cydia也是一样)。而越狱机器要这么装的话只能依靠Cydia…便利程度自然没有直接用ipa那么好。所以也就没有继续往这方面考虑。

发表评论

您的电子邮箱地址不会被公开。

*

:b1 :b2 :b3 :b4 :b5 :b6 more »

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据