black screen when I run my iOS application - ios

I'm trying to do a new iOS app in Xcode. I made a main storyboard and I added a label on my ViewController. When I run my application, first second it show the label and then become the screen black without any errors.
I'm working on Xcode 11 (Swift 5) and this message appears on output:
[SceneConfiguration] Info.plist configuration "Default Configuration" for UIWindowSceneSessionRoleApplication contained UISceneDelegateClassName key, but could not load class with name "gina.SceneDelegate"
I don't know where my mistake is.

iOS 13 & above
Only if target is 13 or greater.
SceneDelegate is not supported before iOS 13. If you want to use SceneDelegate and also want to support iOS prior to iOS 13 then you a have to add some changes to your project.
Add availability attribute to the whole class in SceneDelegate.swift file.
#available(iOS 13.0, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
...
}
AppDelegate.swift file has two new SceneDelegate method. Add availability attribute to them as well.
#available(iOS 13.0, *)
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
...
}
#available(iOS 13.0, *)
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
...
}
Lastly, add UIWindow object in AppDelegate.swift.
class AppDelegate: UIResponder, UIApplicationDelegate {
//Add this line
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
...
}
iOS 12 and earlier
AppDelegate needs a UIWindow property. iOS 13 uses SceneDelegate in new projects. Specify the UIWindow object and remove the SceneDelegate.swift file.
If you have removed the SceneDelegate from project, then you must remove the Application Scene Manifest dictionary from Info.plist.

You need to initialize the window like this:
let window = UIWindow(windowScene: scene as! UIWindowScene)
and add these in info.plist:
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<true/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
That's all you need to do.

Related

Xcode 12 project with iOS 13 SwiftUI support: SceneDelegate functions not invoked

I had created a project in Xcode 12 with iOS 14 as deployment target (bigger project). After some time I realized that I had to also be able to support iOS 13 devices. My project uses SwiftUI.
It is apparently not very straight-forward to simply change the deployment target to iOS 13 as some of the code generated by Xcode 12 is not backwards compatible with iOS 13.
So I essentially need to convert my Xcode 12-based project (currently with deployment target iOS 14.0) to also support iOS 13.
Here's what I did to prepare for my project's downgrade:
Install Xcode 11.7, create new project with SwiftUI support and ensure the deployment target is iOS 13.0. Thereby I know what Xcode believes the "iOS 13"-way should be, i.e., using AppDelegate and SceneDelegate.
Run that app - works fine on my iOS 14 device.
Open Xcode 12, create new project with SwiftUI support, lower the deployment target to iOS 13.0 (because it's default set to 14.x), build it, and be presented with a bunch of compilation issues (as expected).
Changes then made to the Xcode 12 project:
Comment out everything in the <project>App.swift file.
Add AppDelegate.swift file with the same contents from the Xcode 11.7-generated project mentioned above.
Add SceneDelegate.swift file with the same contents from the Xcode 11.7-generated project mentioned above.
Add a LaunchScreen.storyboard file (because Xcode warned about its non-existence).
Run the app.
The app runs, but the contents of ContentView is not shown. Just a black screen (black is probably because my device is in dark mode). Also, as you'll notice in the code below, I've inserted debug logs but only "didFinishLaunchingWithOptions" is printed in the console - that is, the scene delegate methods are not invoked, it seems.
I've (of course) Googled a lot, tried various things with Info.plist, but nothing seems to help.
Code:
AppDelegate.swift
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
print("didFinishLaunchingWithOptions")
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
print("configurationForConnecting")
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
print("didDiscardSceneSessions")
}
}
SceneDelegate.swift
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
print("scene willConnectTo")
guard let windowScene = scene as? UIWindowScene else { return }
let window = UIWindow(windowScene: windowScene)
window.rootViewController = UIHostingController(rootView: ContentView())
self.window = window
window.makeKeyAndVisible()
}
func sceneDidDisconnect(_ scene: UIScene) {
print("sceneDidDisconnect")
}
func sceneDidBecomeActive(_ scene: UIScene) {
print("sceneDidBecomeActive")
}
func sceneWillResignActive(_ scene: UIScene) {
print("sceneWillResignActive")
}
func sceneWillEnterForeground(_ scene: UIScene) {
print("sceneWillEnterForeground")
}
func sceneDidEnterBackground(_ scene: UIScene) {
print("sceneDidEnterBackground")
}
}
ContentView.swift
struct ContentView: View {
var body: some View {
Text("Hello, world!")
.foregroundColor(.blue)
.padding()
.onAppear {
print("ContentView")
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Info.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDisplayName</key>
<string></string>
<key>LSApplicationCategoryType</key>
<string></string>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchScreen</key>
<dict/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
</dict>
</plist>
If you've already built app using SwiftApp before implementation SceneDelegate, just uninstall and rebuild it. And then, you can resolve this problem.
persistentIdentifier will be generated by the system for creating new Scene when an app is launched at the first time. After that, the identifier will be same one every app open. So the app doesn't need to create new Scene.
That's why configurationForConnecting wasn't invoke but set up no rootview so it happens that your app shows black window.
The following WWDC19 session video(around 31:40) says about the identifier.
https://developer.apple.com/videos/play/wwdc2019/212/?time=1900.
Hope it will help your problem.
Thank you.
I'm going to guess that you forgot to add the scene delegate file to the app target. When you build and run, look in the console and see if there's a complaint about not finding the scene delegate. That would explain the phenomenon.

Project for iOS 12.x using Xcode 11 [duplicate]

In Xcode 11, I created a new app project from the Single View App template. I want this app to run in iOS 12 as well as iOS 13. But when I switch the deployment target to iOS 12, I got a lot of error messages like this one:
UIWindowScene is only available in iOS 13 or newer
What should I do?
The template in Xcode 11 uses a scene delegate. Scene delegates and the related classes are new in iOS 13; they don't exist in iOS 12 and before, and the launch process is different.
To make a project generated from an Xcode 11 app template backward compatible, you need to mark the entire SceneDelegate class, and any methods in the AppDelegate class that refer to UISceneSession, as #available(iOS 13.0, *).
You also need to declare a window property in the AppDelegate class (if you don't do that, the app will run and launch but the screen will be black):
var window : UIWindow?
The result is that when this app runs in iOS 13, the scene delegate has the window, but when it runs in iOS 12 or before, the app delegate has the window — and your other code may then need to take account of that in order to be backward compatible.
Could you please add this line code like the following
STEP1:-
#available out the SceneDelegate.swift
#available(iOS 13.0, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
//...
}
STEP2:-
#available out some methods in AppDelegate.swift
// AppDelegate.swift
#available(iOS 13.0, *)
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
#available(iOS 13.0, *)
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
STEP3:-
You should declare window property in AppDelegate.swift file
like var window: UIWindow?
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}

iOS 13 app requests a new scene each time the app icon is tapped

I'm setting up my app with multiple windows. It's working well. But when I open my app from the springboard, it creates a new window every time.
I'm on the latest Xcode & iPadOS 13.0 betas. All my viewcontrollers, views, etc, are made programmatically. My only storyboard is the LaunchScreen.
Info.plist
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<true/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default</string>
<key>UISceneDelegateClassName</key>
<string>ComicReader.SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
AppDelegate.swift
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
UISceneConfiguration(name: "Default", sessionRole: connectingSceneSession.role)
}
SceneDelegate.swift
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let scene = scene as? UIWindowScene else { return }
window = UIWindow(windowScene: scene)
window?.rootViewController = DelegateHelper.rootViewController()
window?.makeKeyAndVisible()
}
}
In the Apple's Gallery sample, if open the app, swipe to the home screen, then open the app again, I'm back where I were, without calling scene(_:willConnectTo) again. On my own app, scene(_:willConnectTo) is called each time that I open the app, and putting breakpoints shows me that I indeed receive differents UIScene & UISceneSession objects at each launch.
I didn't show any NSUserActivity code because I first though it was because I didn't have any state restoration yet. Implemeting it doesn't change a thing.
If you have some ideas, I'm happy to read you!
So, I'm looking for this since last week. Today, I decided to comment all my AppDelegate, SceneDelegate, & keep only one scene configuration in Info.plist. Rewrite AppDelegate & SceneDelegate from the default template to mine progressively.
It works on first try with default template. I rewrite everything identical… stills works.
The problem? The "Default" configuration in Info.plist in UIWindowSceneSessionRoleApplication array was "Item 1", not "Item 0". git stash everything and only reorder that made it work too.
I hope this helps someone.

userDidAcceptCloudKitShareWith not being called

func application(_ application: UIApplication, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {...}
does not get called after clicking "OK" on the link to a shared cloudkit record. The app will open, but this function (which is supposed to be called) is not.
CKSharingSupported:YES is listed in my info.plist
I've put the record into a zone called "sharedZone"
SharedZone is showing up in my zone list, on my Private database.
The shared record shows me as being "invited"
Tried deleting app and cloud data and reinstalling, no change
func application(_ application: UIApplication, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {
print("you accepted something")
let acceptShareOperation: CKAcceptSharesOperation = CKAcceptSharesOperation(shareMetadatas:[cloudKitShareMetadata])
//acceptShareOperation.qualityOfService = .userInteractive
acceptShareOperation.perShareCompletionBlock = {meta, share,
error in
print("share was accepted")
}
acceptShareOperation.acceptSharesCompletionBlock = {
error in
/// Send your user to where they need to go in your app
print("share accepted completion block!")
}
CKContainer(identifier: cloudKitShareMetadata.containerIdentifier).add(acceptShareOperation)
}
Expected result: AT LEAST seeing the line "you accepted something" printed to the console.
UPDATE: Reverting back to IOS11.3 doesn't make it work.
UPDATE: I downloaded a tutorial someone had built that uses cloudkit and (after some finagling, got theirs to work. I then gutted their code and put mine in instead, and NOW the function works properly, so I think the issue may have something to do with either:
XCODE 11 beta, or
The new coredata + cloudkit syncing feature apple added.
If you use fresh Xcode 11 project, there is SceneDelegate.swift.
You need to implement there:
internal func windowScene(_ windowScene: UIWindowScene, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) { }
If you want to get userDidAcceptCloudKitShareWith called inside AppDelegate.swift you have to remove from Info.plist file next lines:
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
UPDATED
If you are trying to accept share on cold start using Scene Manifest, CKShare.Metadata will be passed to func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) in connectionOptions.
To make this work you have to use SceneDelegate.
There are 3 steps to get scenedelegate working.
1 - Add Appdelegate
class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
print("abc")
return true
}
}
Don't forget to use a delegate adaptor if you use SwiftUI way of entering the program.
struct ExampleApp: App {
#UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
var body: some Scene {
MainView()
}
}
Also don't forget to add this to your appdelegate class:
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
if options.userActivities.first?.activityType == "newWindow" {
let configuration = UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
configuration.delegateClass = SceneDelegate.self
return configuration
} else {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
}
2 - Configure info.plist to handle scenes through a SceneDelegate
add these to your info.plist
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
3 - Now add SceneDelegate and get the delegate callbacks that you need
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func windowScene(_ windowScene: UIWindowScene, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShare.Metadata) {
print("abcd")
}
}
}
note: in info.plist and in your appdelegate code there is a reference to "default configuration". You can change this to whatever you want (it's meant to enable management of more than one scene), but they should be the same off course.
After 2 days of trial and error I got it working.
The issue seems to be related to Apples new Core Data + Cloudkit syncing abilities. They seem to gloss over the appdelegate function I need to call.
I fixed the issue by rebuilding the app in Xcode 10, then opening it in XCode 11 without changing anything. I'll let Apple know too.
Thank me. I'm welcome.

Xcode 11 backward compatibility: "UIWindowScene is only available in iOS 13 or newer"

In Xcode 11, I created a new app project from the Single View App template. I want this app to run in iOS 12 as well as iOS 13. But when I switch the deployment target to iOS 12, I got a lot of error messages like this one:
UIWindowScene is only available in iOS 13 or newer
What should I do?
The template in Xcode 11 uses a scene delegate. Scene delegates and the related classes are new in iOS 13; they don't exist in iOS 12 and before, and the launch process is different.
To make a project generated from an Xcode 11 app template backward compatible, you need to mark the entire SceneDelegate class, and any methods in the AppDelegate class that refer to UISceneSession, as #available(iOS 13.0, *).
You also need to declare a window property in the AppDelegate class (if you don't do that, the app will run and launch but the screen will be black):
var window : UIWindow?
The result is that when this app runs in iOS 13, the scene delegate has the window, but when it runs in iOS 12 or before, the app delegate has the window — and your other code may then need to take account of that in order to be backward compatible.
Could you please add this line code like the following
STEP1:-
#available out the SceneDelegate.swift
#available(iOS 13.0, *)
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
//...
}
STEP2:-
#available out some methods in AppDelegate.swift
// AppDelegate.swift
#available(iOS 13.0, *)
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
#available(iOS 13.0, *)
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
STEP3:-
You should declare window property in AppDelegate.swift file
like var window: UIWindow?
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}

Resources