What's the Swift equivalent of Objective-C's #ifdef __IPHONE_11_0?
The iOS 11 SDK comes with Swift 3.2 (or Swift 4), so you can use a Swift version check to accomplish the same thing:
#if swift(>=3.2)
if #available(iOS 11.0, *) {
…
}
#endif
Swift: How to use preprocessors to add an extension method for *below* a certain iOS version?
I don't think that this is possible just relying on compile-time operations in Swift. If you care, by writing an extension in Objective-C that defines the method, your implementation will override the platform's when it exists, so that may be a viable and simple solution.
If you only want your implementation to kick in only if there is no native one, you should be able to do that somewhat easily in Objective-C, too. First, I have two warnings:
- You are modifying CoreData, which itself is already quite dynamic, and I'm not sure how it will react.
- I can't verify any of what I'm saying on this computer, so comment if it doesn't work :)
In your bridging header, ensure that the compiler knows that init(context:)
is available regardless of the iOS version:
@interface NSManagedObject ()
+(void)initialize;
-(instancetype)initWithContext:(NSManagedObjectContext* _Nonnull)ctx;
@end
+initialize
methods declared in categories are executed independently of whether the class has a +initialize
method itself or if any other category has one.
The implementation will then look like this:
@implementation NSManagedObject ()
static id initWithContext(NSManagedObject* self, SEL sel, NSManagedObjectContext* context) {
// your implementation goes here
}
+(void)initialize {
SEL init = @selector(initWithContext:);
if (![self instancesRespondToSelector:init]) {
class_addMethod(self, init, (IMP)initWithContext, "@:@");
}
}
@end
Which is rather straightforward in my opinion: check if NSManagedObject
supports initWithContext:
, and if not, add your own implementation of it. You don't need to provide an explicit implementation of initWithContext:
.
#if check (preprocessor macro) to differentiate between iPhone and iPad
There is no way to determine whether your app is built for iPhone or iPad. Preprocessor #if
directives are resolved during build. Once your app is built and flagged as Universal, it has to run correctly on both devices. During building nobody knows where it will be installed later and one build can be installed on both.
However you may want to do one of these:
Detect device model during runtime.
To do this, use
[[UIDevice currentDevice] model]
and compare toiPhone
,iPod touch
oriPad
strings. This will return you correct device even when running in compatibility mode on iPad (for iPhone-only apps). This can be usefull for usage analytics.Detect user interface idiom during runtime.
This is what everyone checks for, when providing different content for iPhone and iPad. Use
[[UIDevice currentDevice] userInterfaceIdiom]
and compare toUIUserInterfaceIdiomPhone
orUIUserInterfaceIdiomPad
. You may want to make convenience methods like this:@implementation UIDevice (UserInterfaceIdiom)
- (BOOL)iPhone {
return (self.userInterfaceIdiom == UIUserInterfaceIdiomPhone);
}
+ (BOOL)iPhone {
return [[UIDevice currentDevice] iPhone];
}
- (BOOL)iPad {
return (self.userInterfaceIdiom == UIUserInterfaceIdiomPad);
}
+ (BOOL)iPad {
return [[UIDevice currentDevice] iPad];
}
@endThen you can use:
if ([[UIDevice currentDevice] iPhone]) { }
// or
if ([UIDevice iPhone]) { }
// or
if (UIDevice.iPhone) { }
How to check iOS version?
The quick answer …
As of Swift 2.0, you can use #available
in an if
or guard
to protect code that should only be run on certain systems.
if #available(iOS 9, *) {}
In Objective-C, you need to check the system version and perform a comparison.
[[NSProcessInfo processInfo] operatingSystemVersion]
in iOS 8 and above.
As of Xcode 9:
if (@available(iOS 9, *)) {}
The full answer …
In Objective-C, and Swift in rare cases, it's better to avoid relying on the operating system version as an indication of device or OS capabilities. There is usually a more reliable method of checking whether a particular feature or class is available.
Checking for the presence of APIs:
For example, you can check if UIPopoverController
is available on the current device using NSClassFromString
:
if (NSClassFromString(@"UIPopoverController")) {
// Do something
}
For weakly linked classes, it is safe to message the class, directly. Notably, this works for frameworks that aren't explicitly linked as "Required". For missing classes, the expression evaluates to nil, failing the condition:
if ([LAContext class]) {
// Do something
}
Some classes, like CLLocationManager
and UIDevice
, provide methods to check device capabilities:
if ([CLLocationManager headingAvailable]) {
// Do something
}
Checking for the presence of symbols:
Very occasionally, you must check for the presence of a constant. This came up in iOS 8 with the introduction of UIApplicationOpenSettingsURLString
, used to load Settings app via -openURL:
. The value didn't exist prior to iOS 8. Passing nil to this API will crash, so you must take care to verify the existence of the constant first:
if (&UIApplicationOpenSettingsURLString != NULL) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]];
}
Comparing against the operating system version:
Let's assume you're faced with the relatively rare need to check the operating system version. For projects targeting iOS 8 and above, NSProcessInfo
includes a method for performing version comparisons with less chance of error:
- (BOOL)isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion)version
Projects targeting older systems can use systemVersion
on UIDevice
. Apple uses it in their GLSprite sample code.
// A system version of 3.1 or greater is required to use CADisplayLink. The NSTimer
// class is used as fallback when it isn't available.
NSString *reqSysVer = @"3.1";
NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending) {
displayLinkSupported = TRUE;
}
If for whatever reason you decide that systemVersion
is what you want, make sure to treat it as an string or you risk truncating the patch revision number (eg. 3.1.2 -> 3.1).
How to determine device type from Swift? (OS X or iOS)
If you're building for both iOS and macOS (and maybe for watchOS and tvOS, too), you're compiling at least twice: once for each platform. If you want different code to execute on each platform, you want a build-time conditional, not a run-time check.
Swift has no preprocessor, but it does have conditional build directives — and for the most part, they look like the C equivalent.
#if os(iOS) || os(watchOS) || os(tvOS)
let color = UIColor.red
#elseif os(macOS)
let color = NSColor.red
#else
println("OMG, it's that mythical new Apple product!!!")
#endif
You can also use build configurations to test for architecture (x86_64
, arm
, arm64
, i386
), Target environment (iOS simulator or Mac Catalyst), or -D
compiler flags (including the DEBUG
flag defined by the standard Xcode templates). Don’t assume that these things go together — Apple has announced macOS on arm64 to ship in 2020, so arm64 doesn’t imply iOS, and iOS Simulator doesn’t imply x86, etc.
See Compiler Control statements in The Swift Programming Language.
(If you want to distinguish which kind of iOS device you're on at runtime, use the UIDevice
class just like you would from ObjC. It's typically more useful and safe to look at the device attributes that are important to you rather than a device name or idiom — e.g. use traits and size classes to lay out your UI, check Metal for the GPU capabilities you require, etc.)
Negate #available statement
In Swift 5.6, you can now do the following:
if #unavailable(iOS 15) {
// Under iOS 15
} else {
// iOS 15+
}
Or just the following depending on your case:
if #unavailable(iOS 15) {
// Under iOS 15
}
This is part of SE-0290: Negative availability.
iOS/Objective-c: How to get a macro that says the version is higher or equal to 13.0 in the .h file?
But you are allowed to use the preprocessor variables when defining your API
@property (nonatomic, retain) UIViewController *viewController NS_DEPRECATED_IOS(2.0, 13.0);
@property (nonatomic, retain) UIPopoverController *popoverController API_AVAILABLE(ios(13.0));
same for methods
-(void)halleluja:(Evil)brain API_AVAILABLE(ios(13.0)) {
//do evil stuff with brain
}
do not forget, your Apps targeted system version is meant here.
You will find plenty of different variations on how to in the documentations of (upper file part)
#import "Availability.h"
But you don't need to include it, Xcode does that for you.
You can also tell your pre-compiler
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_13
// code starting with ios(13.0)
[self halleluja:nope];
#else
// code earlier ios(13.0)
[self halleluja:papa]; //as defined above should throw a warning
#endif
and finally you can ask at runtime
if ([[[UIDevice currentDevice] systemVersion] isEqualToString:@"13.0"]) {
// code only on ios(13.0) and only ios(13.0)!
}
or
NSString *versionString = [[UIDevice currentDevice] systemVersion];
if ([versionString floatValue] >= 13.0) {
// not sexy - but works.
}
How can I programmatically find Swift's version?
Swift 3.1 extends the @available
attribute to support specifying Swift version numbers in addition to its existing platform versions.
// Swift 3.1
@available(swift 3.1)
func intVersion(number: Double) -> Int? {
return Int(exactly: number)
}
@available(swift, introduced: 3.0, obsoleted: 3.1)
func intVersion(number: Double) -> Int {
return Int(number)
}
Related Topics
Get Cellid, Mcc, Mnc, Lac, Signal Strength, Quality and Network in iOS 8.3
How to Unwind Through Multiple Views Without Displaying Intermediate Views
How to Post on Twitter by Hiding Slcomposeviewcontroller *Tweetsheettweet Instance
Wkwebview and Uimenucontroller
Nsdateformatter Datefromstring Always Returns Nil
How to Crop a Uiimage in Swift
How to Get the Scnview Camera Position When Using Allowscameracontrol
Programmatically Access Image Assets
I Added a Localization in Xcode, Removed It and Now My Storyboard Is Gone
Sending Latitude and Longitude to Server When App Is in Background
How to Read Incoming Sms by Using Application in iOS
Clip Image to Square in Swiftui
Perform Action by Clicking on Some Word in Uitextview or Uilabel
Ios/C: Convert "Integer" into Four Character String
How to Fix Status Bar Overlap Issue in iOS 7
<Googlemaps/Googlemaps.H> File Not Found Google Maps Sdk for iOS