views:

54

answers:

1

I am in this situation where I have to display a button which says "Open myApp" (if myApp is installed on the device) or it says "Download myApp" (if myApp is not installed on the device) in an iphone app. To do this, I need to detect whether an app (with a known custom URL) has been installed on the device. How can I do this? Thanks in advance.

+2  A: 

Although it won't help you to "browse" the App directory and see what's installed on the iPhone, canOpenURL will let you test to see whether or not you can use a specific App URL scheme before actually trying it out in your code.

anyway all this is taken from this website:

http://www.iphonedevsdk.com/forum/iphone-sdk-development/37103-finding-out-what-apps-installed.html

There is a way to check if an application is installed or not, however, it does violate the Sandbox rules and Apple *may reject your app for using this. But it has been done before by other Apps that are available in the App Store, so feel free to try it

Sometimes you may want to check if a specific app is installed on the device, in case you use custom URL schemes that require some other app to be installed (you could just gray out/disable some buttons then). Unfortunately, Apple apparently does not have any function that checks this for you, so I whipped one up. It does not enumerate every single app, instead it uses the MobileInstallation cache which is always up-to-date with SpringBoard and holds the Info dictionaries of all apps installed. Although you're not "supposed" to access the cache, it's readable by App Store apps. Here is my code which at least works perfectly fine with the Simulator 2.2.1: Code:

// 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;
}
Here is an example of this, assuming that your app is named "yourselfmadeapp" and is an app in the app store. 
Code:
NSArray *bundles2Check = [NSArray arrayWithObjects: @"com.apple.mobilesafari", @"com.yourcompany.yourselfmadeapp", @"com.blahblah.nonexistent", nil];
for (NSString *identifier in bundles2Check)
    if (APCheckIfAppInstalled(identifier))
        NSLog(@"App installed: %@", identifier);
    else
        NSLog(@"App not installed: %@", identifier);

Log Output: Code:

2009-01-30 12:19:20.250 SomeApp[266:20b] App installed: com.apple.mobilesafari
2009-01-30 12:19:20.254 SomeApp[266:20b] App installed: com.yourcompany.yourselfmadeapp
2009-01-30 12:19:20.260 SomeApp[266:20b] App not installed: com.blahblah.nonexistent

Try this out before using it, I think Apple changed where the MobileInstallation.plist is located and if you do change it, try it out on an actual device not the simulator. Good Luck!

Hope this helps you. If it does let me know and click this post as answered. If you need any help let me know. Thanks Pk

Pavan