The #available compiler check in Swift (introduced in Swift 2.0) doesn't seem to work with Target Membership.
For example, I have different targets in my project that are for iOS, watchOS, tvOS, OS X, etc. Some files are included in more than one target membership. I'm trying to do this:
if #available(iOS 8.0, *) {
locationManager.startUpdatingLocation()
} else if #available(watchOS 1.0, *) {
locationManager.requestLocation()
}
The error in Xcode says: 'startUpdatingLocation()' is unavailable. However, this file is common to both platforms and targets. Am I approaching this wrong? How can I mix platform code within the same file?
You can conditionally compile code for each platform, using the os() directive:
#if os(iOS)
if #available(iOS 8.0, *) {
locationManager.startUpdatingLocation()
}
#elseif os(watchOS)
locationManager.requestLocation()
#else
...
#endif
Valid arguments are OSX, iOS, watchOS, and tvOS.
This way, your common code which is used by more than one target will only contain compiled code specific to that build configuration.
For more information, see Build Configurations in Using Swift with Cocoa and Objective-C.
Related
When archiving/building the app which uses SwiftUI for platforms <= iOS 10, the compiler throws errors "Use of undeclared type".
This happens even if the enclosing type is marked as #available(iOS 13.0, *) and also using #if canImport(SwiftUI).
SwiftUI framework is also weakly linked.
Everything works fine if you are running(debug) on an iOS 11+ device, or archiving for a target with minimum supported version <= iOS 11.
This failure occurs because builds with a deployment target earlier than iOS 11 will also build for the armv7 architecture, and there is no armv7 swiftmodule for SwiftUI in the iOS SDK because the OS version in which it was first introduced (iOS 13) does not support armv7 anymore.
I have managed to solve the archiving issue by wrapping the SwiftUI Code/Files in the preprocessor of #if arch(arm64).
Example -
#if arch(arm64)
#available(iOS 13.0, *)
struct MyCustomView: View {
var myStrings: [String]
var body: some View {
List {
ForEach(myStrings) { str in
Text(str)
}
}
}
}
#endif
This does disable previews in cases if your deployment target is <= iOS 10.
But if used only when archiving, this does work.
If anyone knows of a better solution. Please share.
Adding this answer so that someone in my situation can atleast make it work with SwiftUI.
Cheers!
RealityKit is only available on iOS 13.0 and up and when I try to integrate it to my project and run it on iOS 12.0 or below, the app crashes with abort_with_payload error.
I still want my app to run on iOS 9.0 - iOS 12.0 without the AR feature. Is there a way I can do that?
This happens because the framework RealityKit always imports even though the device does not have that framework. The solution is to make it optional based on the device you are using.
Go to you project's Build Settings.
Go to Linking -> Other linker Flags
Double click Other linker Flags values to add a new one.
Press add button then type -weak_framework RealityKit
That should do it. Enter then run your app.
Hope this helps. Happy Coding. :D
Have a look at the canImport(module) directive
#if canImport(RealityKit)
import RealityKit
#endif
#available(iOS 13.0, *)
class MyClassWhichUsesRealityKit {
func do() {
let v = ARView(frame: .zero)
}
}
Source: https://github.com/apple/swift-evolution/blob/master/proposals/0075-import-test.md
I'm working on porting an iOS application to Catalyst. The Catalyst (Mac) version will have its own target.
Is there an official way to conditionally compile code just for Catalyst? Otherwise, I can add a target-specific define, but it would be better to use something more general.
As seen in the documentation Creating a Mac Version of Your iPad App, you do:
Swift:
#if targetEnvironment(macCatalyst)
// Code specific to Mac.
#else
// Code to exclude from Mac.
#endif
Objective-C:
#if TARGET_OS_MACCATALYST
// Code specific to Mac.
#else
// Code to exclude from Mac.
#endif
It is also possible to run code that is excluded from macCatalyst without having to use the #else. Note the use of ! (not).
#if !targetEnvironment(macCatalyst)
print("This code will not run on macCatalyst")
#endif
I'm getting a couple of errors when creating my tvOS target from my Sprite Kit iOS game.
The first is: Could not build Objective-C module CoreMotion
Second: Umbrella header "CoreMotion.h" not found
In order to port the game, I've selected most of my files and assets from the iOS target and checked the TVOS target.
If I comment out all of the CoreMotion code, then the errors go away. I've searched Google and here to find an answer, but didn't find anything substantial.
Anyone know how to solve this?
The problem was the tvOS doesn't support CoreMotion.
I had to use iOS specific code to make it work for all relevant code.
Like so:
#if os(iOS)
import CoreMotion
#endif
You could also make tvOS specific code:
#if os(iOS)
let gameGain: CGFloat = 2.5
#elseif os(tvOS) // tvOS
let gameGain: CGFloat = 2.0
#endif
I have a Swift class which is linked against several targets with different deployment targets, the main project has iOS 7 minimum requirement and there is an extension with iOS 8 target.
Now when I compile project, the compiler throws warning on this line of code:
if #available(iOS 8.0, *) { ... }
"Unnecessary check for 'iOSApplicationExtension'; minimum deployment target ensures guard will always be true"
I have checked build settings options and found no switch to kill swift warnings.
I tried to define iOSApplicationExtension version target separately by this line but without success:
if #available(iOS 8.0, iOSApplicationExtension 8.0, *) { ... }
Is there any way to suppress this annoying message?
Found an ugly workaround to silence warning, but I hope there is a better way:
In iOS 8+ targets build settings, I defined a precompile flag in Build Settings -> Swift Compiler - Custom Flags -> Other Swift Flags:
-D iOS8target
Then I changed code to this way:
#if iOS8target
// iOS 8+ compatible code
#else
if #available(iOS 8.0, *) {
// repeat iOS 8+ compatible code again!
} else {
// iOS 7 code
}
#endif
It's not refactored and ugly, but it works!
UPDATE:
There is a swift compiler switch -suppress-warnings to omit all warnings. But it also suppress useful warnings.
Also if there is only one specific file which emits warnings, you can use -w flag in Building Phases pane. It will also suppress useful warnings but limited to one file.
The next release of Cocoapod (after 0.39.0) should have this issue addressed. Check this for more details.
Please check your deployment target into your General and set from 9.0
to 7.0 or less. this warning will remove automatically.