How to accept CloudKit shares with the new SwiftUI app lifecycle? - swiftui

In the iOS 13 world, I had code like this:
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
func windowScene(_ windowScene: UIWindowScene, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {
// do stuff with the metadata, eventually call CKAcceptSharesOperation
}
}
I am migrating my app to the new SwiftUI app lifecycle, and can’t figure out where to put this method. It used to live in AppDelegate pre-iOS13, and I tried going back to that, but the AppDelegate version never gets called.
There doesn’t seem to be a SceneDelegateAdaptor akin to UIApplicationDelegateAdaptor available, which would provide a bridge to the old code.
So, I’m lost. How do I accept CloudKit shares with SwiftUI app lifecycle? 🙈

You can still use AppDelegate with SwiftUI's new life-cycle until Apple releases APIs to handle this natively in SwiftUI’s App Life-cycle.
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
return true
}
}
#main
struct MyApp: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Read this for more

Related

Strokes disappearing in SwiftUI PencilKit

I was really comfortable using PencilKit in SwiftUI, however I revisited a project and apparently there is a bug in Xcodes new version where strokes disappear after drawing them in the simulator.
I am running Version 14.1 of Xcode.
I set up a minimal code example to show my problem:
import SwiftUI
import PencilKit
struct ContentView: View {
var body: some View {
PKCanvasRepresentation()
}
}
struct PKCanvasRepresentation : UIViewRepresentable {
func makeUIView(context: Context) -> PKCanvasView {
var canvas = PKCanvasView()
canvas.drawingPolicy = .anyInput
return canvas
}
func updateUIView(_ uiView: PKCanvasView, context: Context) {
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
This does not draw correctly in the preview or simulator but it works if I run it on a non-virtual testing device. Does anyone found a solution to this, as it makes debugging really uncomfortable.
this is an Xcode 14 bug that specifically happens with an iOS 16 simulator.
At first I suspected the hardware, but this was not confirmed. With the same project, hardware, Xcode and simulator, it is simply random whether it works.

How to access application and options with SwiftUI openUrl

I'm trying to integrate Shopify SDK on a SwiftUI project and I'm having some troubles with the authentication flow.
The code provided on the documentation is pretty straight forward:
func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
spotifyConnection.sessionManager.application(application, open: url, options: options)
return true
}
with SwiftUI though this delegate method is never called so I'm trying to use openUrl
var body: some Scene {
WindowGroup {
MenuView()
.onOpenURL { (url) in
spotifyConnection.sessionManager.application(??, open: url, options: ??)
}
}
}
Question is how do I access the parameters application and options from here?
I am also having the same issue, for Spotify SDK particularly, I tried passing in UIApplication.shared and an empty options dictionary, which seems to work. I also tried examining the options dictionary when I use a UIKit AppDelegate lifecycle. It shows it's returning openInPlace to be false in the options dictionary in the Spotify url callback.
You need an UIApplicationDelegateAdaptor
#main
struct MyApp: App {
#UIApplicationDelegateAdaptor private var appDelegate: MyAppDelegate
var body: some Scene { ... }
}
class MyAppDelegate: NSObject, UIApplicationDelegate, ObservableObject {
func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
spotifyConnection.sessionManager.application(application, open: url, options: options)
return true
}
}

What is the alternative of applicationDidReceiveMemoryWarning in UIKit for SwiftUI?

I want to control low memory state in my SwiftUI app.
What is the alternative of applicationDidReceiveMemoryWarning in UIKit for SwiftUI when you choose SwiftUI lifecycle?
I found didReceiveMemoryWarningNotification for Notification Center, but it is also implemented on UIKit, not on Foundation.
Or if there is no alternative, should I use UIKit lifecycle?
Thanks
You can use a Combine publisher which detects UIApplication.didReceiveMemoryWarningNotification notifications.
Example:
struct ContentView: View {
private let memoryWarningPublisher = NotificationCenter.default.publisher(for: UIApplication.didReceiveMemoryWarningNotification)
var body: some View {
Text("Hello world!")
.onReceive(memoryWarningPublisher) { _ in
print("memory warning")
}
}
}
Result:

Can't push from programmatically created UINavigationController

In a Swift 3 project using Xcode 8.3 and iOS 10.3, I get a navigation controller to push. The app runs with a navigation controller until I try to use it to push. All works until the last line which cause an app crash, fatal error: unexpectedly found nil while unwrapping an Optional value. I don't want to use the storyboard, the point of this question is how to accomplish this task without the storyboard.
App Delegate code
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
window?.rootViewController = UINavigationController(rootViewController: MainViewController())
}
View Controller:
import UIKit
class MainViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let navController = navigationController
let myProfileViewController = MyProfileViewController()
navController!.pushViewController(myProfileViewController, animated: true)
}
}
Move
let navController = navigationController
let myProfileViewController = MyProfileViewController()
navController!.pushViewController(myProfileViewController, animated: true)
from viewDidLoad to viewDidAppear.
In viewDidLoad there is no parent navigation controller set up and you should not start animations from there.
You can add navigation controller in storyboard,
in Storyboard click on MainViewController & from editor menu add navigation controller & push via navigation controller as below code.
let vc = self.storyboard?.instantiateViewController(withIdentifier: "MyProfileViewController") as! MyProfileViewController
self.navigationController?.pushViewController(vc, animated: true)
Make sure storyboard identifier is correct. & remove navigation controller code form AppDelegate class.
first give name to your navigation controller like this
then in your code initialise it like this
let nav = (UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "yourNavigation") as! UINavigationController);
nav.pushFrontViewController(MainViewController(), animated: true);
window?.rootViewController = nav

Swift 3 trying to create and use a UINavigationController programmatically

I'm working on an existing application which uses the storyboard but I want to continue development without using the storyboard at all, or nib files.
The existing left menu is a UITableView which is not the root controller and does not have a navigation controller associated with it. I have created a UINavigationController in the app delegate and want to use this navigation controller to push a new controller. I am able to present a view controller but I want to use push in order to conform with the current UIX.
In the following code nothing happens because navigationController? returns nil.
Here is my new code in the app delegate.
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var navController: UINavigationController?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
navController = UINavigationController()
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window!.rootViewController = navController
self.window!.backgroundColor = .red
self.window!.makeKeyAndVisible()
let menuViewController = SideMenuViewController()
menuViewController.navController = navController
Then in my side menu controller:
import UIKit
class SideMenuViewController: UIViewController {
var navController: UINavigationController?
.......
let myViewController = myViewController()
self.navigationController?.pushViewController(myViewController, animated: true )
From the UIViewController documentation:
var navigation​Controller:​ UINavigation​Controller?
The nearest
ancestor in the view controller hierarchy that is a navigation
controller.
Which means when you call self.navigationController in a view controller you get the (nearest) navigation controller that self is embedded in. Apparently your menu controller is not embedded in the navigation controller hierarchy. Using your app delegate's navController member is one way to make this work. Then you can say in your menu view controller:
func menuItemFooSelected()
{
let navController = (UIApplication.shared.delegate as! AppDelegate).navController
let myViewController = myViewController()
navController?.pushViewController(myViewController, animated: true)
}
Alternatively (and probably preferably), when you set up your initial structure in your app delegate, inject the navigation controller into the side menu controller:
class MenuViewController: UIViewController
{
var navController: UINavigationController?
...
func menuItemFooSelected()
{
let myViewController = myViewController()
self.navController?.pushViewController(myViewController, animated: true)
}
}
In application(_:didFinishLaunchingWithOptions:)
...
let menuViewController = MenuViewController(...)
menuViewController.navController = navController