HI all
I want to make one app for iPhone 2.2.* and for version 3.0.
Some method in 2.2* is deprecated in 3.0. ( like UITableViewCell setText and setLabel )
Is there any way to check which firmware version is used on iPhone and to set different method to use
You will need to use pre-processor directives for the conditional compilation such as __IPHONE_3_0 and build two separate executables.
For example:
#ifdef __IPHONE_3_0
// code specific to version 3
#else
// code specific to version 2
#end
If you need to detect the version at run-time you can use [[UIDevice currentDevice] systemVersion]. It returns the string with the current version of the iPhone OS.
As mentioned in the other referenced thread, while you can use pre-processor directives to generate two applications from one code base, you will still need two applications (one for 2.x and one for 3.x)
A compile time directive cannot be used to make a run time decision.
There's more detail in the other thread.
Alternate solution, just check using respondsToSelector. For example-
CGSize expectedLabelSize;
if ([subTitle respondsToSelector:#selector(sizeWithAttributes:)])
{
expectedLabelSize = [subTitle sizeWithAttributes:#{NSFontAttributeName:subTitleLabel.font}];
}else{
expectedLabelSize = [subTitle sizeWithFont:subTitleLabel.font constrainedToSize:subTitleLabel.frame.size lineBreakMode:NSLineBreakByWordWrapping];
}
Related
I have the following code for reading the unique device id. Since this will be used in more than 20 different views (.m files), I'm just questioning myself that is there a cleaning and efficient way of doing this? So I just comes up with 3 options:
Option One: Just do a copy/paste of these codes into any place I need them to be executed. I think this would be the worst way of doing this.
Option Two: Put it into AppDelegate.m. This will just run these once per launch (may save a tiny amount of time if I'm right). Then just call the string "stringDeviceID" whenever I need.
Option Three: Create another class and get these codes into a class function. However, this still have the "problem" of executing the code every time.
And my question is which is the best/better option I have to go with? And if there is another option that would even better than any of these, please let me know. Thanks in advance.
NSString *stringDeviceID;
if ([UIDevice instancesRespondToSelector:#selector(identifierForVendor)])
{
stringDeviceID = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
}
else
{
stringDeviceID = [[UIDevice currentDevice] uniqueIdentifier];
}
I would implement it as a category on UIDevice:
#iumplementation UIDevice (backwardCompatibleIdentifier)
- (NSString *)backwardCompatibleIdentifier {
// your code
}
#end
Then, all you have to do is:
NSString *myid = [[UIDevice currentDevice] backwardCompatibleIdentifier];
I seriously doubt the performance cost of calling this 20 times will make any real performance difference. If you're worried, test it. If it is a problem, you can stick a cache inside the implementation. If even calling currentDevice is too slow, you can make it a class method instead of an instance method.
Your other alternatives are mostly reasonable, except for one: do not copy and paste this 20 times. At some point, you're going to want to remove the uniqueIdentifier call (whether because Apple forces you, or just because you want to drop iOS 5 compatibility). You may want to add OpenUDID or some other third-party library. Whatever you do, you want to be able to change it in 1 place, not change it in 19 places and then 6 months later debug the 1 place you forgot…
Consider an app that needs to be compatible with iOS 5 and iOS 6.
Is there a way to mark the code that is there purely for iOS 5 compatibility, so that it appear as a compile error (or warning) when -eventually- the deployment target changes to iOS 6?
Something like this:
#IF_DEPLOYMENT_TARGET_BIGGER_THAN_IOS_5
#OUTPUT_ERROR_MESSAGE
#ENDIF
- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return YES;
}
If not, what is the best alternative?
Try this:
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 60000
#warning This pre-6.0 code isn't needed anymore
#endif
- (BOOL)shouldAutorotateToInterfaceOrientation(UIInterfaceOrientation)toInterfaceOrientation {
return YES;
}
This code will cause a compiler warning once the Deployment Target is set to 6.0 or later.
#define MY_CONDITIONAL_DEPRECATED_ATTRIBUTE __deprecated
use it on all methods BUT until you need it turn it quiet
#define MY_CONDITIONAL_DEPRECATED_ATTRIBUTE
Consider looking at how Apple marks that sort of thing in their framework classes. It seems they're making use of the Availability.h and AvailabilityInternal.h classes in the SDK.
I read from the header file (UIApplication.h):
typedef NSUInteger UIBackgroundTaskIdentifier;
UIKIT_EXTERN const UIBackgroundTaskIdentifier UIBackgroundTaskInvalid __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_4_0);
It seems that UIBackgroundTaskInvalid is just an ordinary NSUInteger with a given value. Now that I'm writing code that has to be compatible with jailbreaking iOS 3.1.3 (where UIBackgroundTaskInvalid is not defined yet). I might need the real value behind it. Anyone can help?
Thanks in advance.
Di
You can check it with
#if !defined UIBackgroundTaskIdentifier
// Pre iOS 4 Fallback
#endif
The sample above only checks the value on compile time. If you need these kind of checks on run-time you can check this great article.
I have a code with two targets that uses functions specific to iOS 4 and others compatible with 3.0+.
I would like to make a final revision on the code to see if everything is fine. In other words, to see if there's no function meant for iOS4 being called when compiling for iOS 3.x target.
Is there a way to list, during compilation, any functions being called that doesn't belong to the desired version for the target?
thanks in advance.
1) turn the compiler warnings all the way up
2) treat warnings as errors
3) change the base/target SDK to the earliest version you will support
4) clean
5) build
things such as implicit functions and undeclared selectors should now generate errors.
to avoid errors going forward, create a shim static library for the functions or objc methods you will need. this shim will implement two variations of the code, and will ultimately build against the latest headers. it will contain conditional runtime checks. for the objc class methods you can fake, use categories and use the category implementation where any warning is generated.
to illustrate using two versions of the code:
/*
illustration:
- sortByCoordinates: was added in OS 4, but we need to implement or approximate it for our projects targeting OS 3 (or throw out an alert in some cases)
- as long as you have to support OS 3 and build against the SDKs from time to time, the compiler will produce errors or warnings when a selector has not been declared, or if an object may not respond to a selector (assuming you have directed the compiler to inform you of this)
- so we put our conditionals/runtime checks in a library here, and update our calls from sortByCoordinates: to mon_sortByCoordinates:
*/
- (void)mon_sortByCoordinates:(EECoordinate*)coordinates
{
/* MONLib_OSVersion_4_0 implies some constant, indicating OS 4 */
/* MONLibGetCurrentRuntimeVersion() is a call we make at runtime which returns the OS version, such as MONLib_OSVersion_4_0 or MONLib_OSVersion_3_2 */
if (MONLib_OSVersion_4_0 <= MONLibGetCurrentRuntimeVersion()) {
/* they are using OS 4 (or greater) - call the OS version */
[self sortByCoordinates:coordinates];
}
else {
/*
%%%%%% < else implement our approximation here >
*/
}
}
finally, the compiler can't catch everything for you, due to objc's dynamic nature and the way objc programs are often written. sometimes it helps to be more verbose. simple example:
#interface MONBox
/* added in version 4 */
- (NSUInteger)count;
#end
NSUInteger beispiel() {
if (0) {
/* less safe -- the compiler will not flag this */
/* if [myNSArrayInstace objectAtIndex:0] is a MONBox then this will go undetected since the selector count may be matched to -[NSArray count] */
return [[myNSArrayInstaceOfMONBoxObjects objectAtIndex:0] count];
}
else {
/* more safe -- the compiler will flag this */
MONBox * box = [myNSArrayInstaceOfMONBoxObjects objectAtIndex:0];
return [box count];
}
}
I don't think there's an automated way to do this, as far as I know. I keep an old first-gen iPhone around for this purpose -- I'll run the app on the device and see what crashes. Not ideal but it works OK for smaller apps.
As my iPhone app can run on both OS 3 and 4, I need a way to safely test for iOS 4 SDK features.
I like to avoid checking the [UIDevice ... systemVersion] string (I wonder why Apple failed to provide a numeric value here for easy testing, as it's available on OS X).
Anyway. The usual clean way to test for SDK features is to check if a class reponds to a selector, like this:
if ([UIApplication instancesRespondToSelector:...
And for C methods, one simply checks if the function pointer is NULL:
if (newFunction != NULL) ...
But my problem is that I need to check if a global variable exists. E.g. this one:
extern NSString *const UIApplicationDidEnterBackgroundNotification __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_4_0);
Any idea how one can test their existence at runtime?
Extracted from the help document "SDK Compatibility guide":
Check the availability of an external (extern) constant or a notification name by explicitly comparing its address—and not the symbol’s bare name—to NULL or nil.
For instance, if you wanted to check for the key UIKeyboardFrameBeginUserInfoKey in a keyboard notification dictionary, which is only available in 3.2 and later, you could write:
if (&UIKeyboardFrameBeginUserInfoKey) {
blah;
} else {
blah;
}
the default method is
UIDevice* device = [UIDevice currentDevice];
BOOL backgroundSupported = NO;
if ([device respondsToSelector:#selector(isMultitaskingSupported)])
backgroundSupported = device.multitaskingSupported;
When the test is passed your "it exists in 4.x" global variable shoudl be available.
By the way - it answers if OS4 is there an if the device supports multitasking.
Manfred