Swift - 'sharedApplication is unavailable.' Use view controller based solutions where appropriate instead - ios

I am opening an external URL in my Swift app using the line:
UIApplication.sharedApplication().openURL(url)
This was working fine until I added Realm to my project via CocoaPods. At that point, it started giving me the two compile errors below:
'sharedApplication()' is unavailable: Use view controller based
solutions where appropriate instead.
'openURL' is unavailable
These errors indicate that the API is unavailable to Application Extensions, but my code is in a normal application, not an extension. Why would adding Realm result in the compiler thinking it's in an App Extension?

You can't use sharedApplication from an app extension.
But note that the apple documentation states:
IMPORTANT
Apple allows any Today widget to use the
openURL:completionHandler: method to open the widget’s own containing
app.
ExtensionOverview.

This is an issue that can occur when adding Realm to a project that is using a version of CocoaPods prior to v0.39. To fix it, update to the latest version of CocoaPods using:
sudo gem install cocoapods
This problem is due to CocoaPods issue #3906, which results in portions of Realm's configuration settings being applied to your application's targets. One of the settings that Realm enables is "Allow app extension API only", which is why you end up seeing this particular error.

I still have the same issue, even with CP v1.8.3.
A quick fix is to select the offending pod > Build Settings > Require Only App-Extension-Safe API > Set to No.

Related

How to set APPLICATION_EXTENSION_API_ONLY on a Swift Package Manager (SPM) target?

Context:
We have an Xcode projet that builds an iOS app and a few app extensions.
We also have some shared code in Xcode frameworks, that have the flag APPLICATION_EXTENSION_API_ONLY on (so that we can use it in the app extensions).
We are currently in the process of modularising the app through SPM modules, and this implies converting these frameworks into SPM modules.
Problem:
The SPM module is not marked as app extension safe, and therefore a warning is emitted for every SPM module linked to an app extension, for each app extension (that makes quite a few warnings).
Comment:
I haven't found a way to set this flag on an SPM module.
The only thing I have found so far is a way to disable the warning completely, by setting the no_application_extension linker flag to the SPM target — but then I don't get any warning or error if I use an API unavailable in an app extension (I have tried to use UIApplication.shared in the module and I didn't get any warning or error; if I try to do that on my existing framework I immediately get a compiler error).
And if on the contrary I set the application_extension linker flag to the SPM target, then not only do I not get any warning or error either when using an API unavailable in app extensions, but I also get a whole lot of warnings when building the app, as many other frameworks that the app uses are not available for use in app extensions.
Question:
How do I get my SPM module to have the same behaviour as we used to have when setting the APPLICATION_EXTENSION_API_ONLY flag in the Xcode target?
This means:
not getting any warning when using in an app extension a module marked as safe for use in app extensions
while still getting an error when such module uses an API unavailable in app extensions
Ideally, there is a simple way to just set the APPLICATION_EXTENSION_API_ONLY flag on an SPM module and get back the familiar behaviour.
Thanks
Although I'm not sure about this, but I read something here that it'll be a pain to integrate everything using SPM
Basically as stated in the Xcode 13 beta 3 release notes:
Linking Swift packages from application extension targets or watchOS
applications no longer emits unresolvable warnings about linking to
libraries not safe for use in application extensions. This means that
code referencing APIs annotated as unavailable for use in app
extensions must now themselves be annotated as unavailable for use in
application extensions, in order to allow that code to be used in both
apps and app extensions. (66928265)
You can add the:
#available(iOSApplicationExtension, unavailable)
attribute to declarations using app extension unavailable APIs in order to get them to compile in a way that works for both apps and app extensions.

How can I properly share the Amplify framework with my Main App and my App Extension for an iOS app?

I have an iOS app using AWS amplify for the backend. I am using Xcode 13 and SwiftUI.
I previously added the Amplify framework using Cocoapods however I was unable to get the framework to work with the extension.
So I recently switched to Swift Package Manager. I added the package to my main project. Then I went to my Share Extension and in General - Frameworks and Libraries and manually added the libraries.
After doing this the app builds and runs and both the main app and extension work fine and are able to use the libraries.
The problem is that I cannot archive and upload the app to the App Store. I get the following error:
CFBundleIdentifier Collision. There is more than one bundle with the
CFBundleIdentifier value 'com.amazonaws.AWSAuthCore' under the iOS
application 'MyAPP.app'. With error code
STATE_ERROR.VALIDATION_ERROR...
I went on many forums and spent many hours trying to fix this but to no avail. The suggestions are to click 'do not embed' for the libraries but that option is not available for me.
I wonder if there is anyway to resolve this?
Below are my General and Build Phases for the Share Extension:
Just remove explicit Amplify dependency from extension, ie. next section should be empty
Make sure (it should be automatically, but anyway)
Link Frameworks Automatically parameter (in Build Settings) is true
Runpath Search Path parameter is related to main bundle
So as targets from SPM are built in same location as product and extension and automatic framework linking is enabled the imported modules in extension will available and linked automatically and due to run paths are set the frameworks will be found in run-time as well.
Note: of course in main app target all should be included
Tested with Xcode 13.1 / iOS 15.1

Swift youtube ios helper podfile 'sharedApplication' is unavailable: not available on iOS (App Extension)

After adding OneSignal to the project I get the following errors. I have tried Require Only AppExtension-Safe API to No in this pod file's settings and build successed but youtube pod file's functionality has broken, it does not open youtube videos after setting to no.
Could anyone give advice on how to solve this error? Thanks.
OneSignal target build phases.
It looks like you are building both an app and an app extension. An app extension is standalone, it doesn’t have access for example to the app delegate. Solution: Don’t try to use the app delegate. It won’t compile. Check what files you include in your app extension target. Don’t include files that use the app delegate.

WatchOS 2 App fails to launch on device with dyld_fatal_error for my Framework Library not loaded: Image not found

I've just followed the apple transition guide to upgrade my ObjectiveC app to WatchOS 2
https://developer.apple.com/library/watchos/documentation/General/Conceptual/AppleWatch2TransitionGuide/ConfiguretheXcodeProject.html
With the section "Sharing Code Between an iOS App and a watchOS App" describing how to duplicate an existing iOS framework into a WatchOS framework target for use by WatchOS as follows.
"If you already have a watchOS 1 app that shares a framework with your iOS app, duplicate your iOS framework target and modify it to support watchOS 2.
To duplicate and configure your framework target for watchOS 2
Open the project editor pane of Xcode. (The pane is normally closed.)
Control-click the target to display a context menu with a Duplicate command.
Change the name of the target so that you can identify it easily later.
In Build settings, change the following values:
Change the Supported Platforms setting to watchOS.
Change the Base SDK setting to the latest watchOS.
Change the Product Name setting so that it matches the name of your iOS framework. You want both frameworks to be built with the same name.
Add the framework to your WatchKit extension’s list of linked frameworks."
I've followed these steps to clone my framework which with the iOS framework was called MyFramework, and now the new WatchOS framework is called MyFrameworkWatch. But as described above the Product Name is set to MyFramework instead of MyFrameworkWatch. I presume this naming shift is so that I can include from my framework using
#import <MyFramework/SharedUtils.h>
instead of presumably having to change it to
#import <MyFrameworkWatch/SharedUtils.h>
I wouldn't mind the latter but I'll admit its nicer keeping the framework name the same.
After a bit more work beyond the initial automatic transition I managed to get my app working perfectly well on the simulator, but now switching to device i just can't get it to launch.
Launching my app on the device causes it to spin for a few seconds and then just crash. Launching the glance just causes it to spin indefinitely. Trying to run it from Xcode and running the app causes the app to eventually launch and spin, the spinning can last indefinitely sometimes, but eventually it gets through and i get the actual error reported as follows
dyld_fatal_error - dyld: Library not loaded: #rpath/MyFramework.framework/MyFramework referenced from WatchKit Extension Reason: image not found
So this is my Watch App Extension trying to link to the Watch framework, and doing so looking for the MyFramework name, not the MyFrameworkWatch name. I wonder if this name clashing has caused it to get confused?
When I try to find the frameworks referenced under the Products folder in Xcode I can see two frameworks
MyFramework
MyFramework
they're both reference the same path
/Users/jim/Library/Developer/Xcode/DerivedData/MyApp-byegspjumgwlfpahhwjgzpmfkcdx/Build/Products/Debug-iphoneos/MyFramework.framework
though you can see the target membership is separating the two frameworks, the top MyFramework is associated with the main app, today widget and framework tests project. The lower MyFramework is just associated with my Watch extension. It doesn't feel right that these are referencing the exact same path surely?
Finally when googling for this issue I've found reference of CocoaPods having similar problems though according to their site here
https://github.com/CocoaPods/CocoaPods/issues/4180
It's been fixed since September in version 0.39.0, which is the version that pod --version reports. So I presume I have their fix. I'm tempted to remove the cocoa pods from my framework to see if it helps.
Has anyone else followed the transition guide suggestions for creating a duplicate framework and then managed to get the app and framework actually installing on their watch ok?
Is there any way to analyse the build that is done to try and see if i can see problems with the built files so i don't have to wait for the watch to fail to launch the app to debug it.
Any help is really appreciated as always! Cheers!
Edit: I think I've managed to remove cocoa pods from the offending MyFrameworkWatch target by commenting out the target section in my pod file and running pod update/pod install... it didn't seem to clean up the target very well so i had to manually remove the cocoa pod steps in the post build step. Maybe i've not removed it properly, i find it a little confusing to know what is going on under the hood with cocoa pods. Regardless the same error occurred, so either i didn't remove it properly or it has no effect on this particular problem
Ok, this was caused by the apple documentation for transitioning being wrong, or me interpreting it wrong. It says
Add the framework to your WatchKit extension’s list of linked frameworks.
Where as actually the correct fix was to add the framework to the embedded binaries section (which automatically adds an entry to the linked frameworks anyways) and correctly places the framework within the watch extension under a Frameworks directory. Which the app then loads fine! So much frustration, I lost hours to this across a few days!!

iOS Xcode: Warn about methods not in minimum target SDK

Is there a way to have Xcode tell me when I'm calling a method that isn't available in the SDK of the minimum supported target?
For example, the method [NSURLConnection sendAsynchronousRequest:queue:completionHandler:]. This method is available on iOS5 and up. But my application's minimum target is iOS4.
If I use that method (sendAsync), I'd like Xcode to tell me that that method isn't available for the minimum target I'm trying to support.
I've tried putting __IPHONE_OS_VERSION_MAX_ALLOWED=40000 in the preprocessor settings, but that just triggers a bunch of Apple SDK errors that aren't helpful. (Probably because my active SDK is iOS5.1)
Is the only solution to get ahold of old SDKs and install them in Xcode?
Are there any easier solutions?
There is unfortunately no standard way of doing this. By setting the target OS to a lower number than the base SDK, Xcode will weakly link the libraries and frameworks. When doing that Xcode will not warn you for using methods that may not be available on the target OS.
You could temporarily set the base SDK lower, but that might not always work. Since you want to ignore most of the errors and warnings produced (because they are only called conditionally in your code path), and many warnings and errors are dependant on other error that you may need to resolve before the compiler will give any meaningful output.
I do not think there exist any static analysis tools for this, neither from Apple nor third party.
After doing some research, reading the Apple Doc about it, and trying a number of things. The solution is downloading an old Xcode DMG from Apple, grab the .pkg file for the same SDK as your deployment target and install it in your version of Xcode. Here's how:
Download older Xcode.dmg from Apple
Open the DMG
In Terminal, go into packages: "cd /Volumes/[DMG]/Packages; open ."
Find the SDK you want, something like iPhoneSDK_4.0.pkg
Install that package, but change the install directory to /Applications/Xcode/Contents/Developer
Restart Xcode if it was open.
Now that you have the same SDK as your deployment target, set your BaseSDK to the same. When you build you'll get warnings about missing methods. Your project may or may not successfully build with an older BaseSDK in a new version of Xcode, but that doesn't matter - you've just found the method calls you need to wrap in a feature check with respondsToSelector:.
As of Xcode 7.3, the compiler can now generate these warnings for you. All you need to do is set the -Wpartial-availability warning flag in the Build Settings, as described in this answer.

Resources