I'm just trying to understand the general architecture of UIApplication. My understanding of using a delegate works something like following:
protocol MyDelegate {
func someProtocolMethod()
}
class SomeClass {
var delegate: MyDelegate!
init(){
}
func someClassMethod(){
self.delegate.someProtocolMethod()
}
}
class ClassConformingToDelegate: NSObject, MyDelegate {
let someClass: SomeClass
override init(){
someClass = SomeClass()
super.init()
someClass.delegate = self // self has to be assigned so that SomeClass's delegate property knows what the conforming class is
}
func someProtocolMethod(){}
}
In a similar fashion, AppDelegate conforms to UIApplicationDelegate by having a number of protocol methods implemented.
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
// MARK: UISceneSession Lifecycle
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)
}
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.
}
}
UIApplication declares the delegate as following in its class:
unowned(unsafe) var delegate: UIApplicationDelegate?
But, in order for this delegate to know that AppDelegate.swift is the true delegate, UIApplication has to be instantiated and AppDelegate.swift be assigned to the instance, similar to the example above. So something like the following should happen within AppDelegate.swift:
let application = UIApplication()
application.delegate = self
But, how is this step omitted and AppDelegate still works?
The answer to this question varies a little depending on which version of Xcode/Swift/iOS you are talking about, but the essential process is the same.
If you create a project in Xcode that uses the UIKit AppDelegate lifecycle then you will see the line #main at the start of the AppDelegate.swift file.
This tells the compiler that this file contains the UIApplicationDelegate implementation. The compiler then synthesises a main function for you that performs all of the required setup, including creating an instance of the AppDelegate and assigning it to the UIApplication instance.
In earlier versions of Swift you would see #UIApplicationMain that does essentially the same thing.
You can omit the #main/#UIApplicationMain and create your own main that does all of the required work, but this generally isn't required.
With SwiftUI you now have the option of using SwiftUI lifecycle rather than UIKit lifecycle when you create the project. In this case you have an App struct. This file still contains the #main and is used to launch your app's view hierarchy.
I have an app where the UI is setup programmatically instead of using Storyboards. I am stuck on how to know when a tab has been switched... and when it has been switched I want to be able to assign a property called "userSettings" in each view controller so that I can pass this object around and call methods on it appropriately.
Here is my app delegate:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var contentVC: BMContentViewController?
var settingsVC: BMSettingsViewController?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
contentVC = BMContentViewController()
contentVC?.view.backgroundColor = .clear
contentVC?.tabBarItem.title = "Content"
settingsVC = BMSettingsViewController()
settingsVC?.view.backgroundColor = .clear
settingsVC?.tabBarItem.title = "Settings"
let tabbarController = UITabBarController()
tabbarController.viewControllers = [(contentVC ?? UIViewController()), settingsVC ?? UIViewController()]
self.window?.rootViewController = tabbarController
self.window?.makeKeyAndVisible()
return true
}
func applicationWillResignActive(_ application: UIApplication) {}
func applicationDidEnterBackground(_ application: UIApplication) {}
func applicationWillEnterForeground(_ application: UIApplication) {}
func applicationDidBecomeActive(_ application: UIApplication) {}
}
BMContentViewController() and BMSettingsViewController() each have a property called:
var userSettings: BMUserSettings?
I start the app off in BMContentViewController(). When I switch tabs, I want to be able to assign the userSettings in BMSettingsViewController() to the userSettings that is in BMContentViewController(). userSettings is a central object in my app and instead of creating a singleton, I'm trying to get the app to work by just passing a reference to the object around to other view controllers.
I'm not sure if this is possible by detecting when the tab was switched and passing the value or if I need to use the delegate pattern instead?
How can I accomplish this? I'm trying to avoid using singletons.
You can pass data between view controllers in 6 ways:
Instance property (A → B)
Segues (for Storyboards)
Instance properties and functions (A ← B)
Delegation pattern - you can try delegate pattern, this will be my first best option
Closure or completion handler
NotificationCenter and the Observer pattern - this will be the second best option in your case.
Hey I am trying to initialise a realm object in my app outside the app delegate.
Now, Since a lot of file in my app are accessing the realm file, i decide to declare it globally.
Now, by doing that I got an app rejection, reasoning crash due to this object. My code is as follows
AppDelegate.swift
import UIKit
import RealmSwift
var uiRealm = try! Realm()
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
return true
}
In the above code the object is initialised as the app starts and any other methods loads.
Is it possible that I just declare an object or type Realm and the initialise it under didFinishLaunching as follows:
import UIKit
import RealmSwift
var uiRealm : Realm!
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// I initialise it here
uiRealm = try! Realm()
return true
}
Will it make a difference if its declare in such form. I am a little timid about this.
Any help is highly appreciated.
P.S: I already tried declaring the realm variable in app delegate and the calling it in other classes by UIapplication.shared.delegatemethod, and it always crashes.
I am trying to Access the MDM using AppConnect SDK in swift 1.2 but it is giving following error :
[AppConnect:Error] AppConnect cannot be instantiated directly.
Instead, call +initWithDelegate: and then +sharedInstance.
Code Snippet :
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, AppConnectDelegate {
var window: UIWindow?
var appct = AppConnect()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
// Initialize the AppConnect library
AppConnect.initWithDelegate(self)
self.appct = AppConnect.sharedInstance()
self.appct.startWithLaunchOptions(launchOptions)
return true
}
}
Application is crashing while accessing the Keys of MobileIron Backend Config file.
Any other way to implement this?
You are initializing AppConnect without delegate
change
var appct = AppConnect();
to
var appct : AppConnect!;
My solution was to set a new key/value in the plist:
Principal class AppConnectUIApplication
or in source mode:
<key>NSPrincipalClass</key>
<string>AppConnectUIApplication</string>
Hope it help you
I have just developed an app, but when running in the simulator the debugger console says:
The app delegate must implement the window property if it wants to use
a main storyboard file.
I have an app delegate file. What does the message mean, and how can I get my app working?
Make sure you have the following property declaration in your AppDelegate class:
var window: UIWindow?
If you run your project on earlier than iOS 13.0, in that case you will face the problem. Because of iOS 13 and later, app launch differently than earlier versions.
In iOS 13 and later, use UISceneDelegate objects to respond to life-cycle events in a scene-based app
In iOS 12 and earlier, use the UIApplicationDelegate object to respond to life-cycle events.
When you launch the app in iOS 12 and earlier then UIApplicationMain class expect a window property in your AppDelegate class as like SceneDelegate has. So your problem will be solved if you add the following line in your AppDelegate class.
var window: UIWindow?
For Objective-C
#property (strong, nonatomic) UIWindow *window;
You can find more here App's Life Cycle.
Just in case anyone comes across this again and is programming in Objective-C make sure you have this line of code in your AppDelegate.h file:
#property (strong, nonatomic) UIWindow *window;
I have received this error, when I created new project in XCode 11. I have not used SwiftUI. Here are the steps, I have considered to fix this.
Deleted Application Scene Manifest entry from Info.plist
Deleted SceneDelegate.swift file
Deleted all scene related methods in AppDelegate.swift class
added var window: UIWindow? property in AppDelegate.swift class
After these steps, I am able to run the app on version prior to iOS 13.
[EDIT]
Finally, your AppDelegate.swift file should look something like the following.
import UIKit
#UIApplicationMain
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
}
}
Add the following window declaration in Appdelegate file
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window:UIWindow?
...
Implementation of this property is required if your app’s Info.plist file contains the UIMainStoryboardFile key.
The default value of this synthesized property is nil, which causes the app to create a generic UIWindow object and assign it to the property. If you want to provide a custom window for your app, you must implement the getter method of this property and use it to create and return your custom window.
I had the same issue, just add var window: UIWindow? as the debug error says.
#UIApplicationMain
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
}
You can check your app delegate class:
import UIKit
#UIApplicationMain
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
}
// MARK: UISceneSession Lifecycle
#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.
}
}
Error: The app delegate must implement the window property if it wants to use a main storyboard file
Swift 5 & Xcode 11
Make sure that SceneDelegate contains UIWindow property
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
//...
}
Setting in Info.plist
Application Scene Manifest > Enable Mutliple Windows > false.
This solved the problem for me.
Long ago answered, but to help understand the questions above about why simply adding the window property solves the problem, note that the app delegate conforms to the UIApplicationDelegate protocol which defines a property, #property (nullable, nonatomic, strong) UIWindow *window; that classes need to provide to specify the window to use when presenting a storyboard. Failure to provide that is causing the Xcode log warnings.
For Swift:
var window: UIWindow?
For Objective-C:
#property (strong, nonatomic) UIWindow *window;
In addition, you might have an Application Scene Manifest entry in your Info.plist file - if you do not use scenes, but only a window and view controllers (eg because you want to test something in the old UI setup), you should remove that entry to be able to see your views.