I'm trying to update an app of mine to have a more modern look, and the last part of it is the Action Extension in Safari.
My info.plist file has the correct NSExtension details to use a storyboard, but storyboards look so old and I'd like to use a nicer SwiftUI-based look. Is this even possible?
This is the relevant bit from the Info.plist:
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
<integer>1</integer>
</dict>
<key>NSExtensionJavaScriptPreprocessingFile</key>
<string>GetURL</string>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.ui-services</string>
<key>NSExtensionActionWantsFullScreenPresentation</key>
<false/>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
</dict>
I see I can use NSExtensionPrincipalClass
instead of NSExtensionMainStoryboard
but then I get stuck.
If I remove this:
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
and replace it with this:
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).ActionViewController</string>
I get this error when I run the extension:
Rejecting view controller creation request due to invalid extension storyboard or principal class: Error Domain=NSCocoaErrorDomain Code=967223 "(null)" UserInfo={Invalid Configuration=Either NSExtensionMainStoryboard or NSExtensionPrincipalClass must be specified in the extension's Info.plist file but not both.}
According to that error the two keys are mutually-exclusive, which is fine as I'm using just one of them, so why do I get this error?
Is it something to do with the actual code in ActionViewController
?
I have this, and nothing here ever runs:
class ActionViewController: UIViewController {
var theUrl: String = ""
@objc override func viewDidLoad() {
super.viewDidLoad()
if let inputItem = extensionContext!.inputItems.first as? NSExtensionItem {
if let itemProvider = inputItem.attachments?.first {
itemProvider.loadItem(forTypeIdentifier: UTType.propertyList.identifier as String) { [unowned self] (dict, error) in
let itemDictionary = dict as! NSDictionary
let javaScriptValues = itemDictionary[NSExtensionJavaScriptPreprocessingResultsKey] as! NSDictionary
self.theUrl = javaScriptValues["URL"] as! String
// Build the SwiftUI view, wrap it in a UIHostingController then send to the main thread to update the UI
let contentView = ActionExtensionView(theUrl: self.theUrl, clickedCancel: self.cancel, clickedDone: self.done)
let childView = UIHostingController(rootView: contentView)
self.view.addSubview(childView.view)
// Set the place where your view will be displayed
let constraints = [
childView.view.topAnchor.constraint(equalTo: view.topAnchor),
childView.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
childView.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
childView.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
childView.view.widthAnchor.constraint(equalTo: view.widthAnchor),
childView.view.heightAnchor.constraint(equalTo: view.heightAnchor)
]
childView.view.translatesAutoresizingMaskIntoConstraints = false
view.addConstraints(constraints)
DispatchQueue.main.async {
self.present(childView, animated: true)
}
}
}
}
}
Apple really don't make it easy to develop for their platforms, do they?
Ah, it seems to be an issue with Xcode. You have to build and run your app containing the extension, then you can run the extension itself.
Seems a bit daft, and really slows you down.