#define ARM #include #include #include #include #ifdef ARM #include // NSObject #endif @interface LSApplicationWorkspaceRemoteObserver : NSObject -(id)localObservers; @end @interface LSApplicationWorkspace : NSObject + (id) defaultWorkspace; @property (readonly) LSApplicationWorkspaceRemoteObserver * remoteObserver; - (BOOL) _LSPrivateSyncWithMobileInstallation; @end @interface PBAppDepot : NSObject + (id)sharedInstance; @property(retain, nonatomic) NSMutableDictionary *internalAppState; - (id)_addAppStateForIdentifier:(id)arg1; - (void)_save; - (void)_setNeedsNotifyAppStateDidChange; @end //#import "KBHelperClass.h" // // LSDTrip: A simple tool to demonstrate LaunchServices in OS X *and* iOS // ------- // // Jonathan Levin, http://NeWOSXBook.com - @Morpheus______/@TechnoloGeeks // // License: free, but if you GitHub it or mod it, at least leave a mention. // And maybe get the book when it's finally out :-) // // Compile: gcc ls.m -o lsdtrip -lobjc -framework Foundation // or // gcc-iphone -DARM ls.m -o lsdtrip -lobjc -framework Foundation -framework MobileCoreServices // // (that "MobileCoreServices" is required for the dlopen(3) to work) // // // To run: // Usage: ls [apps|plugins|publicurls|privateurls] [-v] // whohas _url_ // types // dump <<-- Try this // // Explanation: // // Shows what are (IMHO) the most useful of the LaunchServices APIs, as exported by // [Mobile]CoreServices. In iOS most of the good stuff isn't exported (t), instead wrapped // by LSApplicationWorkSpace, LSApplicationProxy, and friends. Even though the straight LS // and _LS calls are much nicer, I chose to use objective-C here in order to maintain // write-once-run-everywhere, and demonstrate yet again just how OS X and iOS really are // so similar. // // How this works: // // Over rather nice XPC to lsd (10.11/9.0) and friends. Full explanation in MOXiI 2. // (Chapter 5, "Promenade, A Tour of the OS X and iOS Frameworks", to be exact, where // many more "private" frameworks are exposed). But since I'm running behind with it, // I thought people would appreciate a hands-on preview :-) This is just one of the many // open source examples I have planned. // // PLEASE BE PATIENT. This should give you an idea of why MOXiI is delayed. At least I try // to keep everyone updated and drop examples along the way. And there's plenty more where // this came from. Just wait. // // // Improvements: // // - Can be made into pure C (i.e. not objective-C, .m), but that would make // the already ugly [] syntax even worse.. // // - There's a whole undocumented side to LaunchServices (even as an allegedly // "public" framework that it is) one can add here. And I left the other // methods out in an #if 0 block, for future use. // // - The UUIDs aren't actually CFUUIDs. They're some $%#$%#$ NS...UUID, which // isn't compatible, so the UUIDs come with some 0s at the end. Meh. // // Ok. Let's begin: // My own prototypes and globals: CFStringRef dumpApp (NSObject *AppRef, int Verbose) ; NSObject * workspace; // Intentionally void * so as to NOT involve @interface files // OS X and iOS APIs are virtually identical, but the framework name is different #ifdef ARM #define CORE_SERVICE_FRAMEWORK "/System/Library/Frameworks/MobileCoreServices.framework/MobileCoreServices" #else #define CORE_SERVICE_FRAMEWORK "/System/Library/Frameworks/CoreServices.framework/CoreServices" #endif CFStringRef serializeCFArrayToCFString (CFArrayRef Array, CFStringRef Delimiter) { if (!Array) { return (CFSTR("(null)"));} CFMutableStringRef returned = CFStringCreateMutable(kCFAllocatorDefault, // CFAllocatorRef alloc, 4096); //CFIndex maxLength); int len = CFArrayGetCount(Array); int i = 0; for (i = 0; i < len ; i++) { CFTypeRef val = (CFTypeRef) CFArrayGetValueAtIndex(Array, i); if (i > 0) CFStringAppend(returned, Delimiter); // UGLY, I know. But PoC, people. PoC if (CFGetTypeID(val) == CFStringGetTypeID()) CFStringAppend(returned, val); else if (CFGetTypeID(val) == CFUUIDGetTypeID()){ CFStringRef UUID = CFUUIDCreateString(kCFAllocatorDefault, val); CFStringAppend(returned, UUID); } else { CFStringAppend(returned, dumpApp (val, 0)); }; } return (returned); } // serializeCFArrayToCFstring CFStringRef dumpApp (NSObject *AppRef, int Verbose) { // App is an LSApplicationProxy object CFStringRef appID = (CFStringRef) [AppRef performSelector:@selector( applicationIdentifier)]; CFStringRef appName = (CFStringRef)[AppRef performSelector:@selector(localizedName)]; if (!appName) { appName = CFSTR("Not Set");} CFMutableStringRef out = CFStringCreateMutable(kCFAllocatorDefault, // CFAllocatorRef alloc, 4096); //CFIndex maxLength); CFStringAppendFormat(out, // CFMutableStringRef theString, NULL, // CFDictionaryRef formatOptions, CFSTR("\t%@ (%@)\n"), appName, appID); #if 0 // Can also use objective-C to enumerate ivars.. unsigned int ivarCount; Ivar *ivars = class_copyIvarList([AppRef class], &ivarCount); int i = 0; for (i = 0; i < ivarCount; i++) { fprintf(stderr,"\t%s: \n" , ivar_getName(ivars[i])); // etc. } #endif if (Verbose) { // Dump more CFStringAppendFormat(out, // CFMutableStringRef theString, NULL, // CFDictionaryRef formatOptions, CFSTR("\t\tExecutable: %@\n"), [AppRef performSelector:@selector(bundleExecutable)]); #ifdef ARM // Set on iOS. Can also use versionIdentifier here, but that requires working back from the // number to a version string (which LaunchServices lets you do with // LSVersionNumberCopyStringRepresentation/LSVersionNumberGetXXX() // But why bother when you have a short version string.. CFStringAppendFormat(out, // CFMutableStringRef theString, NULL, // CFDictionaryRef formatOptions, CFSTR("\t\tVersion: %@\n"), (CFStringRef)[AppRef performSelector:@selector(shortVersionString)]); #endif // This is apparently unset.. CFStringAppendFormat(out, // CFMutableStringRef theString, NULL, // CFDictionaryRef formatOptions, CFSTR("\t\tVendor Name: %@\n"), (CFStringRef)[AppRef performSelector:@selector(vendorName)]); CFStringAppendFormat(out, // CFMutableStringRef theString, NULL, // CFDictionaryRef formatOptions, CFSTR("\t\tMach-O UUIDs: %@\n"), serializeCFArrayToCFString((CFArrayRef)[AppRef performSelector:@selector(machOUUIDs)], CFSTR(","))); CFStringAppendFormat(out, // CFMutableStringRef theString, NULL, // CFDictionaryRef formatOptions, CFSTR("\t\tDisk Usage (Static): %@\n"), (CFStringRef)[AppRef performSelector:@selector(staticDiskUsage)]); #if 0 // This apparently doesn't work in 9.2 anymore. Not sure about this.. CFStringAppendFormat(out, // CFMutableStringRef theString, NULL, // CFDictionaryRef formatOptions, CFSTR("\t\tDisk Usage (Dynamic): %@\n"), (CFStringRef)[AppRef performSelector:@selector(dynamicDiskUsage)]); #endif CFArrayRef UIBackgroundModes = (CFArrayRef) [AppRef performSelector: @selector(UIBackgroundModes)]; // This is a CFArray if (!CFArrayGetCount(UIBackgroundModes)) { CFStringAppend (out,CFSTR("\t\tno BackgroundModes")); } else CFStringAppendFormat(out, // CFMutableStringRef theString, NULL, // CFDictionaryRef formatOptions, CFSTR("\t\t BackgroundModes: %@"), serializeCFArrayToCFString((CFArrayRef)UIBackgroundModes, CFSTR(","))); CFStringAppend(out, CFSTR("\n")); #ifdef ARM // Only on iOS CFStringAppendFormat(out, // CFMutableStringRef theString, NULL, // CFDictionaryRef formatOptions, CFSTR("\t\tApplication DSID: %@\n"), (CFStringRef)[AppRef performSelector:@selector(applicationDSID)]); CFStringAppendFormat(out, // CFMutableStringRef theString, NULL, // CFDictionaryRef formatOptions, CFSTR("\t\tPurchaser DSID: %@\n"), (CFStringRef)[AppRef performSelector:@selector(purchaserDSID)]); CFStringAppendFormat(out, // CFMutableStringRef theString, NULL, // CFDictionaryRef formatOptions, CFSTR("\t\tDownloader DSID: %@\n"), (CFStringRef)[AppRef performSelector:@selector(downloaderDSID)]); #endif #if 0 uint64_t modTime = (uint64_t)([AppRef performSelector:@selector( bundleModTime)]); fprintf(stderr, "\t\tBundle Mod Time: %llu\n", modTime); #endif int cont = (int)([AppRef performSelector:@selector( isContainerized)]); int restricted = (int)([AppRef performSelector:@selector( isRestricted)]); CFStringAppendFormat(out, NULL, CFSTR("\t\tContainerized: %@\n\t\tRestricted: %@\n"), (cont ? CFSTR("YES (q.v. App-Store Receipt URL for container)") : CFSTR("NO")), (restricted ? CFSTR("YES") : CFSTR("NO"))); CFStringAppendFormat(out, // CFMutableStringRef theString, NULL, // CFDictionaryRef formatOptions, CFSTR("\t\tApp Store Receipt URL: %@\n"), (CFStringRef)[AppRef performSelector:@selector( appStoreReceiptURL)]); // These are from LSBundleProxy, which is the parent of LSApplicationProxy CFStringAppendFormat(out, // CFMutableStringRef theString, NULL, // CFDictionaryRef formatOptions, CFSTR("\t\tContainer URL: %@\n"), [AppRef performSelector:@selector(containerURL)]); CFDictionaryRef entitlements = (CFDictionaryRef) [AppRef performSelector:@selector(entitlements)]; if (entitlements && CFDictionaryGetCount(entitlements) ) { CFDataRef xml = CFPropertyListCreateXMLData(kCFAllocatorDefault, (CFPropertyListRef)entitlements); CFStringRef xmlAsString = CFStringCreateFromExternalRepresentation(NULL, xml, kCFStringEncodingUTF8); if (xmlAsString) { CFStringAppendFormat(out, // CFMutableStringRef theString, NULL, // CFDictionaryRef formatOptions, CFSTR("\t\tEntitlements:\n-----\n %@\n-----\n"), xmlAsString); } else { CFStringAppend (out, CFSTR("\t\tEntitlements: Internal error\n"));} } // entitlements else CFStringAppend(out,CFSTR("\t\tEntitlements: None\n")); } // Verbose return (out); } /* * Soooo much more, courtesy of JTool: -[LSApplicationProxy applicationIdentifier]: -[LSApplicationProxy roleIdentifier]: -[LSApplicationProxy bundleModTime]: -[LSApplicationProxy registeredDate]: -[LSApplicationProxy deviceFamily]: -[LSApplicationProxy minimumSystemVersion]: -[LSApplicationProxy sdkVersion]: -[LSApplicationProxy applicationType]: -[LSApplicationProxy itemName]: -[LSApplicationProxy sourceAppIdentifier]: -[LSApplicationProxy companionApplicationIdentifier]: -[LSApplicationProxy applicationVariant]: -[LSApplicationProxy storeCohortMetadata]: -[LSApplicationProxy shortVersionString]: -[LSApplicationProxy preferredArchitecture]: -[LSApplicationProxy familyID]: -[LSApplicationProxy groupContainers]: -[LSApplicationProxy directionsModes]: -[LSApplicationProxy UIBackgroundModes]: -[LSApplicationProxy audioComponents]: -[LSApplicationProxy externalAccessoryProtocols]: -[LSApplicationProxy VPNPlugins]: -[LSApplicationProxy plugInKitPlugins]: -[LSApplicationProxy appTags]: -[LSApplicationProxy requiredDeviceCapabilities]: -[LSApplicationProxy deviceIdentifierForVendor]: -[LSApplicationProxy ODRDiskUsage]: -[LSApplicationProxy storeFront]: -[LSApplicationProxy externalVersionIdentifier]: -[LSApplicationProxy betaExternalVersionIdentifier]: -[LSApplicationProxy appStoreReceiptURL]: -[LSApplicationProxy installProgress]: -[LSApplicationProxy installProgressSync]: -[LSApplicationProxy resourcesDirectoryURL]: -[LSApplicationProxy privateDocumentIconNames]: -[LSApplicationProxy setPrivateDocumentIconNames:]: -[LSApplicationProxy privateIconsDictionary]: -[LSApplicationProxy privateDocumentIconAllowOverride]: -[LSApplicationProxy setPrivateDocumentIconAllowOverride:]: -[LSApplicationProxy iconDataForVariant:]: -[LSApplicationProxy privateDocumentTypeOwner]: -[LSApplicationProxy setPrivateDocumentTypeOwner:]: -[LSApplicationProxy localizedName]: -[LSApplicationProxy localizedShortName]: -[LSApplicationProxy localizedNameForContext:]: -[LSApplicationProxy iconIsPrerendered]: -[LSApplicationProxy fileSharingEnabled]: -[LSApplicationProxy profileValidated]: -[LSApplicationProxy isAdHocCodeSigned]: -[LSApplicationProxy isPlaceholder]: -[LSApplicationProxy isAppUpdate]: -[LSApplicationProxy isNewsstandApp]: -[LSApplicationProxy isRestricted]: -[LSApplicationProxy supportsAudiobooks]: -[LSApplicationProxy supportsExternallyPlayableContent]: -[LSApplicationProxy supportsOpenInPlace]: -[LSApplicationProxy hasSettingsBundle]: -[LSApplicationProxy isInstalled]: -[LSApplicationProxy isWhitelisted]: -[LSApplicationProxy isBetaApp]: -[LSApplicationProxy isPurchasedReDownload]: -[LSApplicationProxy isWatchKitApp]: -[LSApplicationProxy hasMIDBasedSINF]: -[LSApplicationProxy missingRequiredSINF]: -[LSApplicationProxy isEqual:]: -[LSApplicationProxy hash]: -[LSApplicationProxy description]: -[LSApplicationProxy iconStyleDomain]: -[LSApplicationProxy userActivityStringForAdvertisementData:]: -[LSApplicationProxy populateNotificationData]: -[LSApplicationProxy itemID]: -[LSApplicationProxy installType]: -[LSApplicationProxy originalInstallType]: -[LSApplicationProxy groupIdentifiers]: -[LSApplicationProxy teamID]: -[LSApplicationProxy isContainerized]: */ void dumpURL (CFStringRef URL, int Verbose) { CFMutableStringRef out = CFStringCreateMutable(kCFAllocatorDefault, // CFAllocatorRef alloc, 4096); //CFIndex maxLength); CFStringAppend(out, URL); if (Verbose) { CFArrayRef appsForURL = (CFArrayRef) [workspace performSelector:@selector(applicationsAvailableForHandlingURLScheme:) withObject:(id)URL]; // This is a CFArray if (CFGetTypeID(appsForURL) != CFArrayGetTypeID()) { fprintf(stderr, "Was expecting a CFArray of Apps!\n"); exit(2); } if (!CFArrayGetCount (appsForURL)) CFStringAppend(out,CFSTR(" - Not claimed by anyone")); else { CFStringRef apps = serializeCFArrayToCFString (appsForURL, CFSTR("\n\t\t\t")); CFStringAppend (out, apps); CFRelease (apps); } } CFShow(out); CFRelease(out); } void dumpPlugin (NSObject *PluginRef, int Verbose) { CFStringRef pluginName = (CFStringRef) [PluginRef performSelector:@selector( localizedName)]; CFStringRef pluginID = (CFStringRef) [PluginRef performSelector:@selector( pluginIdentifier)]; CFUUIDRef pluginUUID = (CFUUIDRef)[PluginRef performSelector:@selector(pluginUUID)]; CFStringRef out = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("\t%@ (%@) - %@"), pluginName,pluginID, CFUUIDCreateString(kCFAllocatorDefault, pluginUUID)); CFShow(out); } // end Dump int main (int argc, char **argv) { int verbose = 0; // So - why dup? because CFShow(), which I use extensively, write to stderr. // And I really CANNOT stand the whole CFStringToCstr $%#$%#$ just to get a stupid // printout! So instead, I save stderr, and then reopen stderr to stdout. This // way, the output can be grep(1)-ed easily. dup2(2,3); dup2(1,2); if (argc < 2) { fprintf(stderr, "Usage: %s [apps|plugins|publicurls|privateurls] [-v]\n", basename(argv[0])); fprintf(stderr, " app _bundle_id_ (implies -v for this app)\n"); #ifdef ARM fprintf(stderr, " launch _bundle_id_\n"); #endif fprintf(stderr, " whohas _url_ \n"); fprintf(stderr, " types\n"); fprintf(stderr, " dump\n"); exit(0); } if (argv[2] && strcmp(argv[2], "-v")==0) verbose++; // Getting the LS* classes and functions we need here: // --------------------------------------------------- Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace"); if (!LSApplicationWorkspace_class) {fprintf(stderr,"Unable to get Workspace class\n"); exit(1);} workspace = [LSApplicationWorkspace_class performSelector:@selector (defaultWorkspace)]; if (!workspace) {fprintf(stderr,"Unable to get Workspace\n"); exit(2);} NSLog(@"workspace: %@", workspace); LSApplicationWorkspaceRemoteObserver *observer = [workspace remoteObserver]; NSArray *localObservers = [observer localObservers]; NSLog(@"local observers: %@", localObservers); // KBHelperClass *helperClass = [KBHelperClass new]; //[helperClass doMagic]; void *CS_handle = dlopen (CORE_SERVICE_FRAMEWORK, RTLD_NOW); if (!CS_handle) { fprintf(stderr,"Can't find %s!\n", CORE_SERVICE_FRAMEWORK); exit(2); }; if (strncmp(argv[1], "app",3) == 0) { CFStringRef wantedBundleID = NULL; if (strcmp(argv[1], "apps")) // that is, not all apps { // expecting a bundle identifier here if (!argv[2]) { fprintf(stderr,"app option requires a specific bundle id. Try 'apps' to get a list\n"); exit(5); } wantedBundleID = CFStringCreateWithCString(kCFAllocatorDefault, // CFAllocatorRef alloc, argv[2], // const char *cStr, kCFStringEncodingUTF8); //CFStringEncoding encoding); } CFArrayRef apps = (CFArrayRef) [workspace performSelector:@selector(allApplications)]; // This is a CFArray if (CFGetTypeID(apps) != CFArrayGetTypeID()) { fprintf(stderr, "Was expecting a CFArray of Apps!\n"); exit(2); } int len = CFArrayGetCount(apps); int i = 0; for (i = 0; i < len ; i++) { CFTypeRef app = CFArrayGetValueAtIndex(apps,i); // Got app: Dump if want all, or if matches id. // I'm sure there's some Workspace method I missed to get an app by bundle ID, instead // of iterating over all of them.. CFStringRef appID = (CFStringRef) [(id)app performSelector:@selector(applicationIdentifier)]; if (appID && wantedBundleID) { if (CFEqual(appID, wantedBundleID)) { CFStringRef dump = dumpApp(app, 1); CFShow(dump); CFRelease(dump); exit(0); // only one match here. } } else { CFStringRef dump = dumpApp(app, verbose); CFShow(dump); CFRelease(dump); } } if ( wantedBundleID) { fprintf(stderr,"Application with Bundle ID %s not found. Try '%s apps' first, and remember case-sensitivity\n", argv[2], argv[0]); exit(2); } exit(0); } if (strcmp(argv[1], "publicurls") == 0) { CFArrayRef puburls = (CFArrayRef) [workspace performSelector:@selector(publicURLSchemes)]; // This is a CFArray if (CFGetTypeID(puburls) != CFArrayGetTypeID()) { fprintf(stderr, "Was expecting a CFArray of publicURLSchemes!\n"); exit(2); } int len = CFArrayGetCount(puburls); int i = 0; for (i = 0; i < len ; i++) { CFStringRef url = CFArrayGetValueAtIndex(puburls,i); dumpURL(url, verbose); } exit(0); } if (strcmp(argv[1], "privateurls") == 0) { CFArrayRef (privurls) = (CFArrayRef) [workspace performSelector:@selector(privateURLSchemes)]; // This is a CFArray if (CFGetTypeID(privurls) != CFArrayGetTypeID()) { fprintf(stderr, "Was expecting a CFArray of privateURLSchemes!\n"); exit(2); } int len = CFArrayGetCount(privurls); int i = 0; for (i = 0; i < len ; i++) { CFStringRef url = CFArrayGetValueAtIndex(privurls,i); dumpURL(url, verbose); } exit(0); } if (strcmp(argv[1], "plugins") == 0) { CFArrayRef plugins = (CFArrayRef) [workspace performSelector:@selector(installedPlugins)]; // This, too, is a CFArray if (CFGetTypeID(plugins) != CFArrayGetTypeID()) { fprintf(stderr, "Was expecting a CFArray of plugins!\n"); exit(2); } int len = CFArrayGetCount(plugins); int i = 0; for (i = 0; i < len ; i++) { CFTypeRef plugin = CFArrayGetValueAtIndex(plugins,i); dumpPlugin(plugin, verbose); } exit(0); } if (strcmp(argv[1], "types") == 0) { // I resort to dlopen/dlsym here to make linking simpler. You don't have to. typedef CFArrayRef (*UTCopyDeclaredTypeIdentifiersFunc)(void); UTCopyDeclaredTypeIdentifiersFunc UTCopyDeclaredTypeIdentifiers = dlsym (CS_handle, "_UTCopyDeclaredTypeIdentifiers"); CFArrayRef utis = UTCopyDeclaredTypeIdentifiers(); // As usual, this is an array.. int len = CFArrayGetCount(utis); int i = 0; for (i = 0; i < len ; i++) { // Seriously, AAPL - couldn't you find a better acronym? CFTypeRef uti = CFArrayGetValueAtIndex(utis,i); CFShow(uti); } exit(0); } if (strcmp(argv[1], "whohas") == 0) { if (!argv[2]) { fprintf(stderr,"whohas: Option requires an URL Scheme or UTI as an argument!\n"); exit(3); } CFStringRef url = CFStringCreateWithCString(kCFAllocatorDefault, // CFAllocatorRef alloc, argv[2], // const char *cStr, kCFStringEncodingUTF8); //CFStringEncoding encoding); dumpURL(url,1); exit(0); } #if 0 // ignore this part for now if (strcmp(argv[1],"claims") == 0) { typedef CFStringRef (*LSCopyClaimedActivityIdentifiersAndDomainsFunc)(CFStringRef, CFStringRef); LSCopyClaimedActivityIdentifiersAndDomainsFunc LSCopyClaimedActivityIdentifiersAndDomains = dlsym (CS_handle, "_LSCopyClaimedActivityIdentifiersAndDomains"); if (!LSCopyClaimedActivityIdentifiersAndDomains ) { fprintf(stderr,"Unable to find LSCopyClaimedActivityIdentifiersAndDomains "); exit(3); } CFStringRef result = LSCopyClaimedActivityIdentifiersAndDomains (NULL, NULL); CFShow(result); exit(0); } #endif if (strcmp(argv[1],"launch") == 0) { if (!argv[2]) { fprintf(stderr,"launch: Option requires an argument!\n"); exit(3); } CFStringRef bundleID = CFStringCreateWithCString(kCFAllocatorDefault, // CFAllocatorRef alloc, argv[2], // const char *cStr, kCFStringEncodingUTF8); //CFStringEncoding encoding); int t = (int) [workspace performSelector:@selector(openApplicationWithBundleID:) withObject:(id) bundleID ]; fprintf(stderr, "%s %s\n", t ? "Launched" : "Unable to launch", argv[2]); exit(0); } // And this is the real fun part: // // The little known "lsregister" utility in OS X is closed source, and un-man(1)ned, // But one of its coolest features is "dump" - to dump the LaunchServices Database. // Turns out this is a *single* line of code. And guess what -- it works on iOS too :-) // if (strcmp(argv[1], "dump") == 0) { typedef void (*LSDisplayDataFunc)(char *); LSDisplayDataFunc LSDisplayData; // If you want to get the raw format of the file, you can dump with this, instead: //extern _LSDisplayRawStoreData(char *, int); //_LSDisplayRawStoreData("/var/mobile/Library/Caches/com.apple.LaunchServices-134.csstore", (void *) 0xff); LSDisplayData = dlsym (CS_handle, "_LSDisplayData"); if (!LSDisplayData) { fprintf(stderr, "Can't find LSDisplayData! Has Apple removed it by now? :-P"); exit(1);} // The argument expected here is the name of the CoreServices (LaunchServices) // DataStore - in iOS "/var/mobile/Library/Caches/com.apple.LaunchServices-###.csstore" // but turns out NULL works on both OS X and iOS! LSDisplayData("/private/var/containers/Data/System/97D6E4BA-C0BF-408B-AF63-1836844381AE/Library/Caches/com.apple.LaunchServices-175-v2.csstore"); exit(0); } }