I want to add a UIRefreshControl for iOS6 users and a button for iOS5 users. I only want to have one package I deliver to the app store (obviously) so how can I do this?
I can sense the OS version
float ver = [[[UIDevice currentDevice] systemVersion] floatValue];
if (ver >= 6.0) {
// Programmatically add UIRefreshControl.
}
But if I want to support iOS5, the compiler won't let me use the UIRefreshControl?
If the compiler don't let you use it, then let you fool him. You can (and should) also switch from OS version detection to feature detection. All in all:
if (NSClassFromString(#"UIRefreshControl") != Nil) {
id control = [[NSClassFromString(#"UIRefreshControl") alloc] init];
}
Related
Does anybody knows if the technique used to ask the user to rate our app and open for him the App Store directly on the rating page is still working on iOS 7 ?
I used to open this url from my app :
itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=353372460&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software
But it looks like it's not working anymore (AppStore show a blank page). I have also tried this url wihout luck:
http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?pageNumber=0&sortOrdering=1&type=Purple+Software&mt=8&id=353372460
Starting with iOS7 the URL has changed and cannot direct for the review page but only to the app
itms-apps://itunes.apple.com/app/idAPP_ID
Where APP_ID need to be replaced with your Application ID. Based on the App ID from the question it would be the following
itms-apps://itunes.apple.com/app/id353372460
Notice the id in front of the number ... that string is is id353372460, not just 353372460
For anything pre iOS7 the 'old' URL needs to be used, only those could get you straight to the review page. You should also take note that these calls will only work on devices. Running them in the simulator will do nothing since the simulator does not have the App Store app installed.
Have a look at for instance Appirater for an implementation. https://github.com/arashpayan/appirater
Can't help you with phonegap specifics (never used it). But it basically comes down to checking the iOS version your user is running and then either use the old URL or then new iOS7 URL.
The following URL works perfectly on iOS 7.1:
http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=xxxxxxxx&pageNumber=0&sortOrdering=2&type=Purple+Software&mt=8
where the xxxxxxxx is your app ID.
UPDATE. Works on iOS 9.3.4 and iOS 10 GM (by Jeet)
This works on my end (Xcode 5 - iOS 7 - Device!):
itms-apps://itunes.apple.com/app/idYOUR_APP_ID
For versions lower than iOS 7 use the old one:
itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=YOUR_APP_ID
One-Line-of-Code Simple Alternative: ** Also See Simulator Comments Below **
http://itunes.apple.com/app/idAPP_ID
EDIT: Now that iOS 7.1 allows direct-link to the Reviews tab in the App Store, it's worth investing the extra lines of code to get there directly: see other answers for the rest.
here we are using http: instead of itms-apps:, let iOS do the rest of the work
I get the same results testing on iOS 6.1 & 7 devices (iPad/iPhone/iPod touch 4)
Specifically, this shortcut, for iOS 6 takes the user to the Details tab and not the Reviews tab.
The Purple+Software link gets the user all the way to the Reviews tab in iOS 6, which is obviously preferred if you know how to check the OS.
Important note: This will cause error in the simulator for iOS 5.1, 6.1 and 7.Cannot Open Page Safari can not open the page because the address is invalid (we know it is a valid URL outside the simulator, on any browser)
Just to be clear: On iOS 7: http:// provides the same experience as itms-apps: with no noticeable delay. * keep in mind that the simulator behavior noted above. This is not entire dissimilar from trying to access the camera via a simulator: the simulator is not the place to test it. *
It's not clear which versions of iOS this is supported by, but as part of iOS 10.3 there's a new query parameter that can be added to the URL: action=write-review. I have tested this on iOS 10.2 and 9.3.5 and it works. However, it does not work on iOS 7.1.2, so support was added between iOS 8.0 and 9.3.5. Further investigation is required!
Example: https://itunes.apple.com/app/id929726748?action=write-review&mt=8
This will open the "Write a Review" dialogue, rather than just showing the review tab.
Opening review page directly from app is possible in iOS7.
Please use the following url...
itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=YOUR_APP_ID
This will definitely works.. :)
+ (NSString *)getReviewUrlByAppId:(int)appId
{
NSString *templateReviewURL = #"itms-apps://ax.itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?type=Purple+Software&id=APP_ID";
NSString *templateReviewURLiOS7 = #"itms-apps://itunes.apple.com/app/idAPP_ID";
NSString *templateReviewURLiOS8 = #"itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=APP_ID&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software";
//ios7 before
NSString *reviewURL = [templateReviewURL stringByReplacingOccurrencesOfString:#"APP_ID" withString:[NSString stringWithFormat:#"%d", appId]];
// iOS 7 needs a different templateReviewURL #see https://github.com/arashpayan/appirater/issues/131
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0 && [[[UIDevice currentDevice] systemVersion] floatValue] < 7.1)
{
reviewURL = [templateReviewURLiOS7 stringByReplacingOccurrencesOfString:#"APP_ID" withString:[NSString stringWithFormat:#"%d", appId]];
}
// iOS 8 needs a different templateReviewURL also #see https://github.com/arashpayan/appirater/issues/182
else if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0)
{
reviewURL = [templateReviewURLiOS8 stringByReplacingOccurrencesOfString:#"APP_ID" withString:[NSString stringWithFormat:#"%d", appId]];
}
return reviewURL;
}
The review link has once again broken in iOS9. In doing some experimenting, I figured out that Apple reverted it back to how it was before iOS7. So you have to do:
itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=247423477&onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software
Where 247423477 is your 9 digit app ID (the main difference is you have to append &onlyLatestVersion=true&pageNumber=0&sortOrdering=1&type=Purple+Software after the app ID).
All the answers above have now been deprecated (iOS 7, but may work) thus, I provide the new way Apple recommends to provide links to the Apps.
The link for your App is the one from iTunes (use Copy Link), this one is recommended for use in code:
Swift 3.0
let path = URL(string: "https://itunes.apple.com/us/app/calcfast/id876781417?mt=8")
UIApplication.shared.open(path!)
Or better -- treat the optional correctly and handle the possibility of not being able to reach the link:
if let path = URL(string: "https://itunes.apple.com/us/app/calcfast/id876781417?mt=8") {
UIApplication.shared.open(path) {
(didOpen:Bool) in
if !didOpen {
print("Error opening:\(path.absoluteString)")
}
}
}
Objective-C
#define APP_URL_STRING #"https://itunes.apple.com/us/app/calcfast/id876781417?mt=8"
then you can call APP_URL_STRING in your code:
[[UIApplication sharedApplication] openURL:[NSURL URLWithString: APP_URL_STRING] options:#{} completionHandler:nil];
Note, that this is the recommended way now by Apple, as the previous method of processing redirect links has been deprecated and are not supported.
The link for all your Apps, if you have more than one:
#define MYCOMPANY_URL_PATH #"http://appstore.com/mycompany"
[[UIApplication sharedApplication] openURL:[NSURL URLWithString: MYCOMPANY_URL_PATH] options:#{} completionHandler:nil];
The App link above is recommended for use in code or links that are not seen directly by the user. If you want to provide link that may be seen and remembered by the user then use the following:
http://appstore.com/calcfast
Using this URL was the perfect solution for me. It takes the user directly to the Write a Review section. Credits to #Joseph Duffy.
For a sample code try this :
Swift 3, Xcode 8.2.1 :
let openAppStoreForRating = "itms-apps://itunes.apple.com/gb/app/id1136613532?action=write-review&mt=8"
if UIApplication.shared.canOpenURL(URL(string: openAppStoreForRating)!) {
UIApplication.shared.openURL(URL(string: openAppStoreForRating)!)
} else {
showAlert(title: "Cannot open AppStore",message: "Please select our app from the AppStore and write a review for us. Thanks!!")
}
Here showAlert is a custom function for an UIAlertController.
I have this to get the Product ID automatically and generate App Store Review and Product page links.
- (void) getAppStoreLinks {
productID = [[NSUserDefaults standardUserDefaults] objectForKey:#"productID"]; //NSNumber instance variable
appStoreReviewLink = [[NSUserDefaults standardUserDefaults] objectForKey:#"appStoreReviewLink"]; //NSString instance variable
appStoreLink = [[NSUserDefaults standardUserDefaults] objectForKey:#"appStoreLink"]; //NSString instance variable
if (!productID || !appStoreReviewLink || !appStoreLink) {
NSString *iTunesServiceURL = [NSString stringWithFormat:#"https://itunes.apple.com/lookup?bundleId=%#", [NSBundle mainBundle].bundleIdentifier];
NSURLSession *sharedSes = [NSURLSession sharedSession];
[[sharedSes dataTaskWithURL:[NSURL URLWithString:iTunesServiceURL]
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode;
if (data && statusCode == 200) {
id json = [[NSJSONSerialization JSONObjectWithData:data options:(NSJSONReadingOptions)0 error:nil][#"results"] lastObject];
//productID should be NSNumber but integerValue also work with NSString
productID = json[#"trackId"];
if (productID) {
appStoreReviewLink = [NSString stringWithFormat:#"itms-apps://itunes.apple.com/WebObjects/MZStore.woa/wa/viewContentsUserReviews?id=%d&pageNumber=0&sortOrdering=2&type=Purple+Software&mt=8",productID.integerValue];
appStoreLink = [NSString stringWithFormat:#"itms-apps://itunes.apple.com/app/id%d",productID.integerValue];
[[NSUserDefaults standardUserDefaults] setObject:productID forKey:#"productID"];
[[NSUserDefaults standardUserDefaults] setObject:appStoreReviewLink forKey:#"appStoreReviewLink"];
[[NSUserDefaults standardUserDefaults] setObject:appStoreLink forKey:#"appStoreLink"];
}
} else if (statusCode >= 400) {
NSLog(#"Error:%#",error.description);
}
}
] resume];
}
}
Open app's Review Page
- (IBAction) rateButton: (id)sender {
NSString *appStoreReviewLink = appStoreReviewLink;
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:appStoreReviewLink]];
}
Open app's App Store page
- (IBAction) openAppPageButton: (id)sender {
NSString *appStoreLink = appStoreLink;
[[UIApplication sharedApplication] openURL:[NSURL URLWithString: appStoreLink]];
}
It is said that this bug will be fixed on iOS7.1. Read here on the corona forum, and here on the iPhoneDevSDK.
How can I check and conditionally only compile / run code if iOS5 is available ?
You can either check the systemVersion property of UIDevice like so:
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 5.0f) {
// Do something
}
But personally I don't like that method as I don't like the parsing of the string returned from systemVersion and the comparison done like that.
The best way is to check that whatever class / method it is that you want to use, exists. For example:
If you want to use TWRequest from the Twitter framework:
Class twRequestClass = NSClassFromString(#"TWRequest");
if (twRequestClass != nil) {
// The class exists, so we can use it
} else {
// The class doesn't exist
}
Or if you want to use startMonitoringForRegion: from CLLocationManager which was brought in in iOS 5.0:
CLLocationManager *locationManager = [[CLLocationManager alloc] init];
...
if ([locationManager respondsToSelector:#selector(startMonitoringForRegion:)]) {
// Yep, it responds
} else {
// Nope, doesn't respond
}
In general it's better to do checks like that than to look at the system version.
Try out this code:
if([[[UIDevice currentDevice] systemVersion] floatValue] >= 5.0)
{
//Do stuff for iOS 5.0
}
Hope this helps you.
i want to know the user uses the iphone or ipad,if the user uses the iphone i want to open the camera,if he uses the ipad or runs in simulator i want to open the library. how it is possible?
how to find the details of devices?
how to know current using device by user through xcode?
NSString *deviceType = [UIDevice currentDevice].model;
if([deviceType isEqualToString:#"iPhone"])
{
//your code
}
.....
Hope this helps.
EDIT:
See this thread -determine-device-iphone-ipod-touch-with-iphone-sdk .
[[UIDevice currentDevice].model hasPrefix:#"iPhone"]
Use the "hasPrefix" so that it works in simulator.
You should not determine whether there is a camera by looking at the model. This is not future proof - for instance, you would not be supporting the iPad 2's camera.
UIImagePickerController has a special method to determine whether a camera in available:
+ (BOOL)isSourceTypeAvailable:(UIImagePickerControllerSourceType)sourceType
With sourceType being one of
UIImagePickerControllerSourceTypePhotoLibrary,
UIImagePickerControllerSourceTypeCamera,
UIImagePickerControllerSourceTypeSavedPhotosAlbum
Make use of this to identify devices.
// If iPhoneOS is 3.2 or greater then __IPHONE_3_2 will be defined
#ifndef __IPHONE_3_2
typedef enum {
UIUserInterfaceIdiomPhone, // iPhone and iPod touch
UIUserInterfaceIdiomPad, // iPad
} UIUserInterfaceIdiom;
#define UI_USER_INTERFACE_IDIOM() UIUserInterfaceIdiomPhone
#endif // ifndef __IPHONE_3_2
but if you want to check if camera is available I think you can make use of UIImagePickerController's static method
+ (BOOL)isSourceTypeAvailable:(UIImagePickerControllerSourceType)sourceType
Working on Vaibhav Tekam's answer, I used this
NSString *deviceType = [UIDevice currentDevice].model;
if([deviceType hasPrefix:#"iPhone"])
{
//your code
}
or
NSString *deviceType = [UIDevice currentDevice].model;
if([deviceType hasPrefix:#"iPad"])
{
//your code
}
etc.
It's much easier that way as it covers all models.
This is to be expected but I can't seem to find a runtime that works properly as it seems it was a private API before!!!!
At the moment I have and OS3.1.3 responds to the addGestureRecognizer selector!!!!
if ( [self.view respondsToSelector:#selector(addGestureRecognizer:)] ) {
UIGestureRecognizer *recognizer;
recognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self action:#selector(morePress)];
[self.view addGestureRecognizer:recognizer];
recognizer.delegate = self;
[recognizer release];
}
You should check for the system version explicitly:
NSString *currentSystemVersion = [[UIDevice currentDevice] systemVersion];
if([currentSystemVersion compare:#"3.2"] == NSOrderedAscending) {
//add gesture recognizer
} else {
// :(
}
UIGestureRecognizer is not supported prior to iOS 3.2. Even if the method addGestureRecognizer: exists, that doesn't mean it's safe to use.
It was indeed a private api and not supported in versions prior to 3.2.
Apple's doc says:
To determine at runtime whether you can use gesture recognizers in
your application, test whether the class exists and, if it does,
allocate an instance and see check if it responds to the selector
locationInView:. This method was not added to the class until iOS 3.2.
Sample code:
UIGestureRecognizer *gestureRecognizer = [[UIGestureRecognizer alloc] initWithTarget:self action:#selector(myAction:)];
if (![gestureRecognizer respondsToSelector:#selector(locationInView:)]) {
[gestureRecognizer release];
gestureRecognizer = nil;
}
// do something else if gestureRecognizer is nil
Explenation:
To determine whether a class is available at runtime in a given iOS
release, you typically check whether the class is nil. Unfortunately,
this test is not cleanly accurate for UIGestureRecognizer. Although
this class was publicly available starting with iOS 3.2, it was in
development a short period prior to that. Although the class exists in
an earlier release, use of it and other gesture-recognizer classes are
not supported in that earlier release. You should not attempt to use
instances of those classes.
Check out the full text here.
The reason I need to find out is that on an iPad, a UIPickerView has the same height in landscape orientation as it does in portrait. On an iPhone it is different. The iPad programming guide introduces an "idiom" value to UIDevice:
UIDevice* thisDevice = [UIDevice currentDevice];
if(thisDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad)
{
// iPad
}
else
{
// iPhone
}
which works OK while you're in iPad (3.2) but not iPhone (3.1.3) - so it looks like there also needs to be an ifdef to conditionally compile that check, like:
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 30200
UIDevice* thisDevice = [UIDevice currentDevice];
if(thisDevice.userInterfaceIdiom == UIUserInterfaceIdiomPad)
{
// etc.
}
#endif
To me that's starting to look very clumsy. What's a better way?
Checking at runtime (your first way) is completely different from #if at compile time. The preprocessor directives won't give you a universal app.
The preferred way is to use Apple's Macro:
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
// The device is an iPad running iPhone 3.2 or later.
}
else
{
// The device is an iPhone or iPod touch.
}
Use 3.2 as the base SDK (because the macro is not defined pre 3.2), you can target prior OS versions to get it running on the iPhone.
I'm answering this now (and at this late date) because many of the existing answers are quite old, and the most Up Voted actually appears to be wrong according to Apples current docs (iOS 8.1, 2015)!
To prove my point, this is the comment from Apples header file (always look at the Apple source and headers):
/*The UI_USER_INTERFACE_IDIOM() macro is provided for use when
deploying to a version of the iOS less than 3.2. If the earliest
version of iPhone/iOS that you will be deploying for is 3.2 or
greater, you may use -[UIDevice userInterfaceIdiom] directly.*/
Therefore, the currently APPLE recommended way to detect iPhone vs. iPad, is as follows:
1) (DEPRECATED as of iOS 13) On versions of iOS PRIOR to 3.2, use the Apple provided macro:
// for iPhone use UIUserInterfaceIdiomPhone
if(UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
2) On versions of iOS 3.2 or later, use the property on [UIDevice currentDevice]:
// for iPhone use UIUserInterfaceIdiomPhone
if([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad)
I like my isPad() function. Same code but keep it out of sight and in only one place.
My solution (works on 3.2+):
#define IS_IPHONE (!IS_IPAD)
#define IS_IPAD (UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPhone)
then,
if (IS_IPAD)
// do something
or
if (IS_IPHONE)
// do something else
In Swift use userInterfaceIdiom instance property as-
if UIDevice.current.userInterfaceIdiom == .phone {
print("iPhone")
}
& For other devices -
switch UIDevice.current.userInterfaceIdiom {
case .pad:
print("iPad")
case .phone:
print("iPhone")
case .tv:
print("TV")
case .carPlay:
print("carPlay")
default: break;
}
Put this method in your App Delegate so that you can call it anywhere using [[[UIApplication sharedApplication] delegate] isPad]
-(BOOL)isPad
{
BOOL isPad;
NSRange range = [[[UIDevice currentDevice] model] rangeOfString:#"iPad"];
if(range.location==NSNotFound)
{
isPad=NO;
}
else {
isPad=YES;
}
return isPad;
}
If you are using features that are not backwards compatible, I found the best way for me is to create a #define in the pre-compiled header. Example:
#if __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_3_2
#define USING_4_X
#endif
Then in your code, you can do this:
BOOL exists = NO;
#ifdef USING_4_X
exists = [SomeObject someMethod:[url lastPathComponent]];
#else
exists = [SomeObject someMethod:[[url path] lastPathComponent]];
#endif
If
1- you already have the app installed into your device,
2- you change its build settings to be a 'Universal' app,
3- install the app to your device on top of the pre-existing app (without deleting the previous one)
You might find that the solutions provided here to detect iPhone/iPad do not work. First, delete the app that was 'only' for iPad/iPhone and install it fresh to your device.
BOOL isIpad()
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
return YES;
}
return NO;
}
extension UIDevice {
var isIPad: Bool {
return UIDevice.current.userInterfaceIdiom == .pad
}
}