iOS 13/Facebook SDK error "Unable to find a valid UIWindow" - facebook

(To forestall well-intended suggestions, yes I posted this question last week on Facebook's developer forum. No responses yet.)
TL;DR
Facebook SDK 5.8 complains at startup FBSDKLog: Unable to find a valid UIWindow.
The Main Story
In a from-scratch, one-view Xcode 11/iOS 13 project, there is no longer a default UIWindow member associated with the application. (The window per se is still around; you can see it, contained in a UIWindowScene, using the View Hierarchy Debugger in Xcode, or the Reveal app.)
FBSDK 5.8 does seem to be iOS-13-aware, and looks around for it. The relevant code is at line 498 of
https://github.com/facebook/facebook-ios-sdk/blob/master/FBSDKCoreKit/FBSDKCoreKit/Internal/FBSDKInternalUtility.m.
Facebook's code iterates over the application's connectedScenes member, which for me is an empty set. How do I modify my code so that FBSDK finds the window?
Some Hacking
I tried adding the following to scene(_:willConnectTo:options:) but it seems to be too late — the FBSDKLog message has already appeared by then. (So I'm flailing...)
guard let s = (scene as? UIWindowScene) else { return }
self.window = UIWindow(windowScene: s)
The following also failed, but it was just a shot in the dark:
guard let s = (scene as? UIWindowScene) else { return }
self.window = UIWindow(frame: s.coordinateSpace.bounds)
self.window?.windowScene = s
self.window?.rootViewController = ViewController(nibName: nil, bundle: nil)
self.window?.makeKeyAndVisible()

If you don't use the new behaviour and don't mind reverting to the old way, you can try the following
Delete Application Scene Manifest key from Info.plist
Delete SceneDelegate.swift
Add var window: UIWindow?to AppDelegate.swift
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow? // <-- Here
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// window!.makeKeyAndVisible()
return true
}
}

Add window variable to AppDelegate class
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
Assign UIWindow inside didFinishLaunchingWithOptions
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
if let window = window {
window.makeKeyAndVisible()
self.window = window
}
ApplicationDelegate.shared.application(
application,
didFinishLaunchingWithOptions: launchOptions
)
return true
}

Related

Split view equally Master Detail iOS13

Apparently I'll go full Ubuntu by the end of the week and bin my iPhone.
iOS13 has come to my Xcode and I can't find a way to split screen equally Master and Detail view. All tutorials and guides refers to a previous iOS.
The code I tried in my AppDelegate is:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if let split = window?.rootViewController as? UISplitViewController {
split.delegate = self
split.preferredDisplayMode = .allVisible
split.maximumPrimaryColumnWidth = .greatestFiniteMagnitude
split.preferredPrimaryColumnWidthFraction = 0.5
}
return true
}
but the screen is still splitted 30-70 or whatever the default is.
Figure out in iOS13 is SceneDelegate responsible for UIWindow so the code above should go in the sceneDidBecomeActive function in the SceneDelegate.

Can I expose a Swift function to React Native without using Objective-C (pure swift)?

I wonder if I can expose my func navigateToLoginWidget to React Native. So that it can be triggered from RN.
I have managed to change the Objective-C template that comes with React Native to Swift like so:
import Foundation
import IBMCloudAppID
import BMSCore
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var bridge: RCTBridge!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// initializing App ID SDK
let region = AppID.REGION_SOMEWHERE
let backendGUID = "MY_TENANT_ID_FROM_IBM"
AppID.sharedInstance.initialize(tenantId: backendGUID, region: region)
// Initializing React Native front-end with Swift instead of Obj-c template
let jsCodeLocation: URL
jsCodeLocation = RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index.js", fallbackResource:nil)
let rootView = RCTRootView(bundleURL: jsCodeLocation, moduleName: "MY_PROJECT_NAME", initialProperties: nil, launchOptions: launchOptions)
let rootViewController = UIViewController()
rootViewController.view = rootView
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = rootViewController
self.window?.makeKeyAndVisible()
return true
}
func application(_ application: UIApplication, open url: URL, options :[UIApplication.OpenURLOptionsKey : Any]) -> Bool {
return AppID.sharedInstance.application(application, open: url, options: options)
}
func navigateToLoginWidget(_ sender : Any) {
print("clicked")
AppID.sharedInstance.loginWidget?.launch(delegate: SigninDelegate)
}
}
I would normally have this function in another module called SigninDelegate.swift, but I have included it in the same class for explanatory purposes.
According to the official documentation:
Swift doesn't have support for macros so exposing it to React Native
requires a bit more setup but works relatively the same.
So yes, it is possible, but some setup is needed. Check the docs in the link above for the steps you need to follow to set it up.

How do I rename the initial programmatic ViewController class for my iOS app without involving storyboards

I have been looking for a while and cannot find an answer to this question that does not involve storyboards.
It's quite simple. When you build a new iOS app in Xcode you are given the ViewController.swift file that contains the initial ViewController() class for the app. This view controller is the main view for the app.
If I rename the class to say ViewControllerTest(), when I build and run the app now only loads a blank screen since it cannot find the ViewController class as I renamed it.
How do I set the project in Xcode to use the new ViewControllerTest() class as the initial view controller?
Thanks
If you want to disregard using the storyboard entirely, which I prefer because then all code is in one place, you need to add the following in your AppDelegate or SceneDelegate depending on what version Xcode you are creating your project with.
Xcode 10 and earlier
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let window = UIWindow(frame: UIScreen.main.bounds)
// Or whatever you want to name your view controller class
window.rootViewController = ViewController()
window.makeKeyAndVisible()
self.window = window // retain instance
return true
}
}
for Xcode 10 and earlier you should also change the value of the UIMainStoryboardFile property to an empty string in your Info.plist.
Xcode 11 and later
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = scene as? UIWindowScene else { return }
let window = UIWindow(windowScene: windowScene)
// Or whatever you want to name your view controller class
window.rootViewController = ViewController()
window.makeKeyAndVisible()
self.window = window // retain instance
}
}
for Xcode 11 projects and newer, you should populate the Application Scene Manifest property as shown here:

How to create UI programmatically in Xcode 11? [duplicate]

This question already has an answer here:
Xcode 11 & iOS13, using UIKIT can't change background colour of UIViewController
(1 answer)
Closed 3 years ago.
Update: Finally got it working. See below code for SceneDelegate.swift
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(frame: windowScene.coordinateSpace.bounds)
window?.windowScene = windowScene
window?.rootViewController = ViewController()
window?.makeKeyAndVisible()
}
In contrast to the tutorials and the articles online, I was not able to create a working UI with the latest Xcode. This was also the case with Xcode 10, but I did not care at the time.
My steps are as follows:
Delete Main.storyboard
Delete Main from project settings
Write basic UIWindow code:
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
window?.makeKeyAndVisible()
let mainVC = ViewController()
window?.rootViewController = mainVC
return true
}
At this point I am getting an error saying cannot find the storyboard named Main in the bundle. If I go ahead and clear the entry from Info.plist, this time it complains that there are not enough characters in the storyboard name.
Any ideas?
These are the steps I followed to get UI working programmatically in Xcode 11.
1) Created a new project as usual
2) I deleted the Main.Storyboard file from the project navigator on the left-hand side.
3) In General tab I removed the Main option from the Main Interface drop-down list
4) Next, go to your Info.plist file and completely remove the Storyboard Name from the list
5) Now if you run the app, it should not crash and it should show you a black screen.
6) Next, go to the SceneDelegate.swift file and initialise your UIWindow object as usual. There is a catch here and is new in Xcode 11. You also set the windowScene property of the window object, or else it won't work.
7) Once that's all done, Go to your ViewController class and set the background colour of the view to some colour and you should be all good.
You can also use templates:
Xcode 11 Programmatic UIKit templates
Hope this helped you!!!
This works for me on Xcode 10.3 Swift 5, and Xcode 11 Swift 5.1.
On your new Xcode project, on the Info.plist file, delete the launch screen and main interface file name entries, (don't leave the entry there with an empty string)
Remove the #UIApplicationMain attribute from your AppDelegate class.
Also, this link may help you, you can find info on UIApplicationMain
https://docs.swift.org/swift-book/ReferenceManual/Attributes.html
See code below:
// Created by Juan Miguel Pallares Numa on 9/16/19.
// Copyright © 2019 Juan Miguel Pallares Numa. All rights reserved.
import UIKit
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var myViewController = ViewController()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = myViewController
window?.makeKeyAndVisible()
return true
}
}
import UIKit
class ViewController: UIViewController {
convenience init() {
self.init(nibName: nil, bundle: .main)
view = UIView(frame: UIScreen.main.bounds)
view.backgroundColor = UIColor(
displayP3Red: 0.0, green: 0.7, blue: 0.0, alpha: 1.0)
}
}
// call this file "main.swift"
import Foundation
import UIKit
UIApplicationMain(
CommandLine.argc, CommandLine.unsafeArgv, nil, NSStringFromClass(AppDelegate.self))

Unexpected non-void return value in void function for return true

I am doing a test based on a tutorial (https://www.youtube.com/watch?v=B6W2Rp8Uumo&feature=youtu.be) before i start my school based major project coding. Everything went well until I faced an error.
Unexpected non-void return value in void function.
I have read other posts about this but was not able to find specifically what i need to change. The code is shown below:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var MainVC:MainViewController?
func application(_application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?)
{
MainVC = MainViewController(nibName: "MainViewController", bundle: nil)
let frame = UIScreen.main.bounds
window = UIWindow(frame: frame)
window!.rootViewController = MainVC
window!.makeKeyAndVisible()
return true //this is where the error occurs
}
if someone could help me fix this and explain what this error means that would be awesome!
The method signature should have -> Bool at the end. This tells the compiler that the method will return a boolean value. Check this.