Is it possible to programatically find out name of all apps installed on my iOS device ?
Is there any API available for same ?
Thanks for the help
No, on iOS applications has no access to information of/about other applications due to sandboxed environment.
Yes it is possible to get list of all installed app
-(void) allInstalledApp
{
NSDictionary *cacheDict;
NSDictionary *user;
static NSString *const cacheFileName = #"com.apple.mobile.installation.plist";
NSString *relativeCachePath = [[#"Library" stringByAppendingPathComponent: #"Caches"] stringByAppendingPathComponent: cacheFileName];
NSString *path = [[NSHomeDirectory() stringByAppendingPathComponent: #"../.."] stringByAppendingPathComponent: relativeCachePath];
cacheDict = [NSDictionary dictionaryWithContentsOfFile: path];
user = [cacheDict objectForKey: #"User"];
NSDictionary *systemApp=[cacheDict objectForKey:#"System"];
}
systemApp Dictionary contains the list of all system related app
and user Dictionary contains other app information.
Not from the device. However, from the desktop you could peek into the iTunes library.
There are ways to do this without a jailbroken device and not get your app rejected.
1. get a list of currently running processes see this SO answer. You will need to translate from process name to app name.
2. Check to see if any apps have registered a unique URL scheme with UIApplicationDelegate canOpenURL. There are a few sites cataloging known url schemes, this is the best one.
If an app is not currently running and does not register a custom url scheme then it will not be detected by these methods. I am interested in hearing a method that will be allowed in the app store that works better than this.
try this, it will work even with non-jailbroken devices:
#include <objc/runtime.h>
Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace");
SEL selector=NSSelectorFromString(#"defaultWorkspace");
NSObject* workspace = [LSApplicationWorkspace_class performSelector:selector];
SEL selectorALL = NSSelectorFromString(#"allApplications");
NSLog(#"apps: %#", [workspace performSelector:selectorALL]);//will give you all **Bundle IDS** of user's all installed apps
You can do it by checking whether an application is installed or not by using canOpenURL method or by checking the background processes and matching them with the name of the app you are interested in.
You can use runtime objective c to get the list of all installed apps. It will give you an array of LSApplicationProxy objects.
Following is a code snippet that prints Name of all applications installed in your device.
Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace");
NSObject* workspace = [LSApplicationWorkspace_class performSelector:NSSelectorFromString(#"defaultWorkspace")];
NSMutableArray *array = [workspace performSelector:NSSelectorFromString(#"allApplications")];
NSMutableArray *mutableArray = [[NSMutableArray alloc] init];
for (id lsApplicationProxy in array) {
if(nil != [lsApplicationProxy performSelector:NSSelectorFromString(#"itemName")]){
[mutableArray addObject:[lsApplicationProxy performSelector:NSSelectorFromString(#"itemName")]];
}
}
NSLog(#"********* Applications List ************* : \n %#",mutableArray);
Don't forget to include <objc/runtime.h> .
Related
I want to get all installed app icon and it's detail parameter, how can I get it and open that app via my app page.
I can get list of installed app using this code:
Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace");
NSObject* workspace = [LSApplicationWorkspace_class performSelector:NSSelectorFromString(#"defaultWorkspace")];
NSMutableArray *array = [workspace performSelector:NSSelectorFromString(#"allApplications")];
NSMutableArray *Applist = [[NSMutableArray alloc] init];
for (id lsApplicationProxy in array) {
if(nil != [lsApplicationProxy performSelector:NSSelectorFromString(#"itemName")]){
[Applist addObject:[lsApplicationProxy performSelector:NSSelectorFromString(#"itemName")]];
}
}
NSLog(#" Applications List : \n %#",Applist);
In order to open an other app from your app, you need to find the URL scheme of this app. Each URL scheme is different and can't be found if app developer don't send it to you. For example, for Twitter:
NSURL *twitterURL = [NSURL URLWithString:#"fb://profile/<profile_id>"];
if ([[UIApplication sharedApplication] canOpenURL:twitterURL]) {
[[UIApplication sharedApplication] openURL:twitterURL];
}
I've written some UI tests in Xcode 7 and when I need to refer to a button I use its accessibility.identifier. This logic worked correctly for all the languages.
app.buttons["signin"].tap()
With Xcode 7.3 when I try to launch this code the test fails because the button cannot be found if the simulator language is not English. I've also tried to record the navigation to check how Xcode reads this button when language is different by English and I found out that it use the translations as key... it doesn't make any sense at all!
Those test were really useful to create screenshots... but obviously with this issue I cannot run tests (and create screens) for all the languages.
How can I point to a button in absolute way if it cannot be recognized by identifier!?
----EDIT
I found the main issue. The company that did the translation has translated the labelidentifier fields :/
I'm trying to get the element using app.buttons.elementBoundByIndex(1) but it doesn't seem to work correctly
Accessibility identifiers should not be localized strings, otherwise they will change when you are running your app in a different language.
Just hard-code the accessibility identifiers to make them persistent regardless of language.
button.accessibilityIdentifier = "myButton"
You can have actual accessibilityIdentifier assigned to button and then try accessing it. Accessing buttons with text is always a bad idea as text may change anytime.
E.g. : XCUIApplication().buttons["AppSignInIdentifier"]
I hope your project is maintaining the localization file. I faced the same issue. If so then localization doesn't work directly with UIAutomation yet.
There is a workaround for this, I am sharing the code snippet with you.
You need to find out the xcodeproj file in your bundle first. As these files are not bundled in the UIAutomation target.
- (NSString *)getLocalizedString:(NSString *)string withComments:(NSString *)comments {
NSString *userLocale = [[NSLocale currentLocale] localeIdentifier];
NSString *userLanguage = [userLocale substringToIndex:2];
NSString *path = [self getProjectPath];
path = [path stringByAppendingPathComponent:[NSString stringWithFormat:#"/YourProjectFileName/Resources/Localization/%#.lproj",userLanguage]];
NSBundle *bundle = [NSBundle bundleWithPath:path];
NSString *localizedString = NSLocalizedStringFromTableInBundle(string, #"Localizable", bundle, comments);
return localizedString;
}
- (NSString *)getProjectPath {
NSString *currentSourceFilePath = [[NSString stringWithCString:__FILE__ encoding:NSUTF8StringEncoding] stringByDeletingLastPathComponent];
NSString *currentPath = [currentSourceFilePath copy];
BOOL foundIt = NO;
do {
NSString *testPath = [currentPath stringByAppendingPathComponent:#"XYZ.xcodeproj"];
if ([[NSFileManager defaultManager] fileExistsAtPath:testPath]) {
// found it
foundIt = YES;
break;
}
if ([currentPath isEqualToString:#"/"]) {
// cannot go further up
break;
}
currentPath = [currentPath stringByDeletingLastPathComponent];
} while ([currentPath length]);
if (!foundIt) {
return nil;
}
return currentPath;
}
And to use this //
NSString *loginStr = [self getLocalizedString:#"Login" withComments:#""];
And moreover for the better use of Automation, please set the accessibility labels for your controls, so that it would be easy for the automation process to find out that, which is less overhead on processor and less prone to crashes and issues.
Summary (iOS 8, Xcode 6.4)
First question:- Can i share my app's Documents Directory's data with my other app?
If Yes, I've seen many questions related to this;
Move data/images between two iOS apps using custom URL handler,
http://code.tutsplus.com/tutorials/ios-sdk-working-with-url-schemes--mobile-6629
But I found that these example only send text or URLs. Then I tried myself as below:
NSString* path = [NSString stringWithFormat:#"MY_URL_SCHEME://"];
NSURL* url = [NSURL URLWithString:path];
if([[UIApplication sharedApplication]canOpenURL:url]) {
[[UIApplication sharedApplication]openURL:url];
}
The above code works well to open my other app. But when I try like below, I can't open my other app.
NSArray* mainPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *sourcePath = [mainPath objectAtIndex:0];
NSString* path = [NSString stringWithFormat:#"MY_URL_SCHEME://%#",sourcePath];
NSURL* url = [NSURL fileURLWithPath:path isDirectory:YES];
if([[UIApplication sharedApplication]canOpenURL:url]) {
[[UIApplication sharedApplication]openURL:url];
}
So, please help me, what am I missing?
EDIT:-
i forgot to mention that,iOS7 support is important in my App.so, i think extension might not work.
Use NSUserDefaults with app group to share the data between apps
NSUserDefaults *defaults=[[NSUserDefaults
alloc]initWithSuiteName:#"app group name"];
[defaults setObject:filedata forKey:#"keyfordata"];
[defaults synchronize];
in the app delegate of consuming app fetch the data from NSUserDefaults
another way is to use share extension-
http://easynativeextensions.com/how-to-launch-your-app-from-the-ios-8-share-menu/
You can refer MGInstagram files as Instagram mobile app works same. You can pass image from your application to Instagram application.
You can download it from here: https://github.com/mglagola/MGInstagram
Hope this helps.
I'm using skobbler and skmaps for an app that download for offline use some regions of the map. I'm using the code i have found in the example of the framework package, in this case
MapJSONViewController
MapDownloadViewController
I have implemented also the app delegate code, so every time i start the app, it download and parse a json of about 1mb
- (void)mapsVersioningManager:(SKMapsVersioningManager *)versioningManager loadedWithMapVersion:(NSString *)currentMapVersion
{
[[XMLParser sharedInstance] downloadAndParseJSON];
}
It's possible to avoid this behaviour? I don't want to download 1mb of json data every app init if not necessary... Maybe i can download and include a physic map json file in my app to have a start version ? Or this "local behaviour" will bring my app to work with an outdated json version very soon? Maybe another behaviour is to maintain a local version with a data and redownload it only once a week for example... It seems at me a common problem, there's someone how achive a convenient behaviour?
Yes, you can include the json file in your app & read it from disk.
In the XMLParser.m replace the code in downloadAndParseJson with:
- (void)downloadAndParseJSON
{
[self parseJSON];
NSString *libraryFolderPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSLog(#"%#",libraryFolderPath);
}
and parseJSON with:
- (void)parseJSON
{
NSString *jsonString = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"Maps" ofType:#"json"] encoding:NSUTF8StringEncoding error:nil];
SKTMapsObject *skMaps = [SKTMapsObject convertFromJSON:jsonString];
AppDelegate *appDelegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
[appDelegate setSkMapsObject:skMaps];
self.isParsingFinished = YES;
[[NSNotificationCenter defaultCenter]postNotificationName:kParsingFinishedNotificationName object:nil];
}
Here you can find a modified demo project that reads the Maps.json file from resources (the .json file is included in the resources folder).
I have a Data plist (conveniently named Data.plist) that is updated on launch of the app:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// Determile cache file path
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *filePath = [NSString stringWithFormat:#"%#/%#", [paths objectAtIndex:0],#"Data.plist"];
NSString *dataURLString = #"http://link/to/Data.plist";
NSURL *dataURL = [[NSURL alloc] initWithString:dataURLString];
NSData *plistData = [NSData dataWithContentsOfURL:dataURL];
[plistData writeToFile:filePath atomically:YES];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:filePath];
NSLog(#"The bundle is %#", filePath);
self.data = dict;
// Configure and show the window
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
return YES;
}
I'd like to be able to have some way of checking the saved plist against the server plist - I've seen some implementations that use external libraries but there has to be something in the original iOS SDK. Any ideas? I've read whatever code I do end up using needs to be implemented in viewWillAppear but I'm not sure what that code is exactly.
Two things... first, dataWithContentsOfURL: and generally any of Apple's (temptingly convenient) <anything>WithContentsOfURL: methods are extremely unsafe in the real world. It's blocking which means that no other code will execute until your request succeeds or fails. That means that if the server isn't available or your device doesn't have internet or for some other reason cannot retrieve your data, your phone will sit there until either the iOS watchdog process kills your app for freezing for too long, or it just fails. Then the rest of your app that is expecting data will freak out because suddenly you have no data when your code assumes you should. This is one of many problems with synchronous requests.
I won't go into how to implement asynchronous requests, but head over to Apple's documentation or you can use a wrapper framework like http://allseeing-i.com/ASIHTTPRequest/ that does it for you. Also have a look at http://www.cocoabyss.com/foundation/nsurlconnection-synchronous-asynchronous/
To answer your actual question, you could have a tiny text file on your server with a version number or time stamp and download that along with your plist. on subsequent launches, you can pull down the time stamp/version number and compare it against the one you've got stored, and if the version on the server is more recent, then you pull it and save the new time stamp/version number.