Use UIKit conditionally in file used by Watch app - ios

I have created a model class that I use in my iOS app and my Watch app - it is included in both targets. Now I have to use UIPasteboard in this class which is only available in UIKit, which is not available to watchOS. While I can import UIKit into this file without issue, when I go to use UIPasteboard it will not compile because the watch extension does not know about it.
How can I use UIPasteboard in a class that is available to my watch app?
I wondered if I could only run that code when the device isn't an Apple Watch by using #available, but this didn't resolve the issue.
if #available(iOS 7.0, *) {
UIPasteboard.generalPasteboard()...
//ERROR: Use of unresolved identifier 'UIPasteboard'
} else {
//don't use UIPasteboard
}

Use the existing preprocessor directive defined in Swift:
#if os(iOS)
//UIKit code here
#elseif os(watchOS)
//Watch code here
#endif
See the documentation for preprocessor directives here.

Maybe you could use an extension to factor out the UIPasteboard functionality and include the file containing the extension only in the iPhone target.
Ideally, code shared by several OS's should contain only truly shared code.
Also, if you want the conditionality, this is probably a cleaner way to do it.

There are two ways to do so.
First way is using preprocessor directives, like the following example:
#if os(iOS)
//Insert UIKit (iOS) code here
#elseif os(watchOS)
//Insert WatchKit (watchOS) code here
#endif
The second way is to determine if the code is called from the WatchKit Extension or iOS App. For example, you can set a global boolean flag to true before calling the code from WatchKit Extension, and false before calling from iOS App. Then the shared code can check the value of the flag to determine it is running on iOS or watchOS.

Related

NotificationCenter is not available when building for watchOS Simulator

I want to use NotificationCenter inside a watchOS target. But I get the following error message when building the app at import NotificationCenter:
NotificationCenter is not available when building for watchOS Simulator.
Consider using `#if !os(watchOS)` to conditionally import this framework.
How do you handle this situation? Can I no longer use the simulator?
Ok, I mixed things up. All I want to do in my watchOs framework was something like this:
NotificationCenter.default.post(...)
The class with import NotificationCenter was in the wrong framework. No need for such a class in a watchOs framework. So my problem is fixed.

In Swift, how to ignore a part of the code when running from an App Extension target?

There's a similar question that works on Objective-C, but I tried the same code in Swift and it never executes, neither in the main app, nor in the action extension.
My situation is similar to the one in the question above, that is, when running from the main app I want to use UIApplication.shared.open to open a link in Safari, but I want to ignore this part of the code on the App Extension.
The problem isn't finding out whether the app is running from an App Extension or not, but ignoring the code when building for the App Extension, so that the compiler does not give me the following error on build:
You could introduce a new Custom Flag (similar to the DEBUG flag) for the extension target. In your Build Settings look for the Custom Flags and add a new one (e.g. "EXTENSION"). Like here in the screenshot, but also do it for release.
In your Code you could then do something like
#if EXTENSION
// here goes code that is only compiled in the extension
#else
// here goes code that is only compiled outside the extension
#endif
Update: Please read the Apple provided documentation on App Extensions:
Some APIs Are Unavailable to App Extensions
Because of its focused role in the system, an app extension is ineligible to participate in certain activities. An app extension cannot:
Access a Application.shared object, and so cannot use any of the methods on that object
- Apple, App Extension Programming Guide
To programmatically find if the it the running extension via code it's really simple, just do this:
let bundleUrl: URL = Bundle.main.bundleURL
let bundlePathExtension: String = bundleUrl.pathExtension
let isAppex: Bool = bundlePathExtension == "appex"
// `true` when invoked inside the `Extension process`
// `false` when invoked inside the `Main process`

How can I conditionally compile code for Catalyst?

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

Could not build Objective-C module CoreMotion error for tvOS target

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

iOS8 extension unable to use AFNetworking library

I am unable to use AFNetworking library in my app extension due to
that was trying to access the UIApplication class methods. Does any
one knows here any other better network library that does not access
UIApplication class?. Please suggest.
quote from Guard for unsupported features of iOS 8 extensions #2589
To fix this issue i had to put AF_APP_EXTENSIONS=1 as a preprocessor
macro for the Pods-hitta.se WatchKit Extension-AFNetworking target.
Adding #define AF_APP_EXTENSIONS to the prefix header didn't work,
neither did adding it as a preprocessor macro to the watch extension
target work.
So click on Pods project in Project Navigator, then select Pods-{your-app-name-extension}-AFNetworking. Next go to Build Settings and find Preproccessor Macros where you add new line with "AF_APP_EXTENSIONS=1"
Also wrap it with +#if !defined(AF_APP_EXTENSIONS) at beginning. And end it +#endif
full example of wrapping:
- (void)updateNetworkActivityIndicatorVisibility {
+#if !defined(AF_APP_EXTENSIONS)
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:[self isNetworkActivityIndicatorVisible]];
+#endif
}
You can use AFNetworking with extension. You just need follow the instruction below.
When using AFNetworking in an App Extension, #define AF_APP_EXTENSIONS
to avoid using unavailable APIs.
https://github.com/AFNetworking/AFNetworking/issues/2119

Resources