Extending UIApplicationDelegate Protocol - ios

I would like to extend UIApplicationDelegate protocol and provide a default implementation for application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool method. However, default implementation I have provided does not get called.
Is it possible at all to extend UIApplicationDelegate protocol (with relation to UIApplication being a singleton, or protocol method being an optional), or am i doing something wrong?
thanks
AppDelegate.swift:
import UIKit
extension UIApplicationDelegate{
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
print("does not print anything on launch.")
return true
}
}
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
}

turns out you can't provide default implementations for Objective-C protocols via extensions. See below link for detailed list of limitations on protocol extensions.
https://www.captechconsulting.com/blogs/ios-9-tutorial-series-protocol-oriented-programming-with-uikit
What we CAN'T do: Provide default implementations for Objective-C protocols.

I ran into the same problem and for this particular file I reverted to Objective-C to achieve this functionality.
#import <UIKit/UIKit.h>
#interface NSObject (BasicMethods) <UIApplicationDelegate>
#end
with
#import "UIApplicationDelegate+BasicMethods.h"
#implementation NSObject (BasicMethods)
- (void)applicationDidFinishLaunching:(UIApplication *)application {
NSLog(#"I'm getting called");
}
#end
works.

There is no use of doing extension for protocols.
Reasons are:
Protocol contains only function declaration but extension needs function definition.
Protocol is nothing but just set of rules(methods). It doesn't allocate any memory.
Protocol function definitions will be in delegate class. So function call will never comes to function definition which you
written in extension.

Related

How does AppDelegate become UIApplication's delegate?

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.

Objective-C AppAuth OIDAuthorizationService class in Swift, and Objective-C -> Swift translation

I need to declare a variable of type OIDAuthorizationService, from the AppAuth (https://github.com/openid/AppAuth-iOS) library, in Swift while translating the following Objective-C to Swift to use an Objective-C library in a project.
Pre-translated objective-c
.h
+ #protocol OIDAuthorizationFlowSession;
#interface AppDelegate : UIResponder <UIApplicationDelegate>
+ #property(nonatomic, strong, nullable)
id<OIDAuthorizationFlowSession> currentAuthorizationFlow;
#property (nonatomic, strong) UIWindow *window;
#end
.m
- (BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary<NSString *, id> *)options {
if ([_currentAuthorizationFlow resumeAuthorizationFlowWithURL:url]) {
_currentAuthorizationFlow = nil;
return YES;
}
return NO;
}
So far I have the following translation:
var currentAuthorizationFlow: OIDAuthorizationFlowSession?
...
func application(_ application: UIApplication, openURL: NSURL,
didFinishLaunchingWithOptions launchOptions:
[UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if currentAuthorizationFlow!.resumeAuthorizationFlow(with: openURL as URL) {
return true
}
return false
}
Is this code correct?
Error is: Use of undeclared type: 'OIDAuthorizationFlowSession', as espected, how do I do this?
Much appreciated
I understand that OIDAuthorizationFlowSession protocol is defined in Objective-C and you're trying to use it in Swift, if that's the case then you need a Bridging-Header.h, there you can import the corresponding header for your OIDAuthorizationFlowSession.
How do you create a Bridging Header file? Well it should be created automatically when you create a new swift file, if that's not the case, take a look here: https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html
If the easy way doesn't work, see this one: Xcode not automatically creating bridging header?
LE: if you integrated the AppAuth-iOS as a pod, here's what's working 100%:
import UIKit
import AppAuth
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var currentAuthorizationFlow: OIDAuthorizationFlowSession?
func application(_ application: UIApplication, openURL: NSURL,
didFinishLaunchingWithOptions launchOptions:
[UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if currentAuthorizationFlow!.resumeAuthorizationFlow(with: openURL as URL) {
return true
}
return false
}
So don't forget the "import AppAuth" part.

Could not init a PFObject subclass

After upgrading to Xcode 6.3 6D570 (and Swift 1.2), the init of a subclassed object does not compile.
Let's say I have a class called Armor, which inherits from PFObject, PFSubclassing (exactly as Parse documentation says).
When I try to create an instance, like var armor = Armor(), I get the following compile error:
Missing argument for parameter 'className' in call
Then I read in Parse docs that I should use the 'object' class method to init a subclassed object. So I tried to init like this: var armor = Armor.object().
But then I get the following compile error:
'object()' is unavailable: use object construction 'PFObject()'
I'm using Parse SDK version 1.7.1.
I also override the parseClassName method as follows:
class func parseClassName() -> String {
return "Armor"
}
I registered the subclass inside the initialise method and on app delegate before I setup Parse:
override class func initialize() {
var onceToken : dispatch_once_t = 0;
dispatch_once(&onceToken) {
self.registerSubclass()
}
}
How should I properly init a subclassed object?
=========================================================================
THIS HAS BEEN FIXED IN 1.7.3 PARSE SDK
You can download the new version here:
https://parse.com/docs/downloads
=========================================================================
Though docs are not clear on that, using .object() in Swift is not required anymore.
as said here and in the Swift code snippet found here
Now, this is weird but to make Armor() work you need to reference PFUser class somehow in the same file. Maybe it doesn't have to be PFUser but I havent dug deeper into it.
So, this won't work
import UIKit
import Parse
import Bolts
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
Parse.setApplicationId("appID", clientKey: "clientKey")
let myArmor = Armor()
return true
}
}
But this will
import UIKit
import Parse
import Bolts
private let fix = PFUser.hash() // here's the weird trick
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
Parse.setApplicationId("appID", clientKey: "clientKey")
let myArmor = Armor()
return true
}
}
Hope this helps and Parse pushes a fix out soon.
Also reported as a bug
register the class as a subclass in the AppDelegate, but before Parse.initialize is called.
Then you can delete the overwritten function initialize
This would make:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]!) -> Bool {
Armor.registerSubclass()
// Further initialization
return true
}
then just initialize the class by calling the constructor:
var myArmor = Armor()

The app delegate must implement the window property if it wants to use a main storyboard file swift

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.

Unresolved identifier "PFFacebookUtils" in swift in Xcode6

I am trying to have users login to facebook with parse but am receiving the compiler error "Use of unresolved identifier" for PFFacebookUtils, Parse, FBAppCall, and an invalid DidBecomeActive
This question has been posted before and the solution was to add the parsefacebookutils framework into your project and import it into the header file. However, I have already added the framework to my project and imported it into my header file by adding the following to the my header.
#import <Parse/Parse.h>
#import <ParseFacebookUtils/PFFacebookUtils.h>
#import <FacebookSDK/facebookSDK.h>
I am still getting the error. The following is the code from my appdelegate file:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
PFFacebookUtils.initializeFacebook()
Parse.setApplicationId("parseAppId", clientKey:"parseClientKey")
}
return true
}
func application(application: UIApplication,
openURL url: NSURL,
sourceApplication: String,
annotation: AnyObject?) -> Bool {
return FBAppCall.handleOpenURL(url, sourceApplication:sourceApplication,
withSession:PFFacebookUtils.session())
}
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
FBAppCall.handleDidBecomeActiveWithSession(PFFacebookUtils.session())
}
Could you please take a look at my code above? Not sure what the solution is.
Figured it out. You have to go into the Build Settings - Objective C Header and then type the path to your header in the debug name. For example ProjectName/HeaderName.h as according to where I had my file within my project. Normally you wouldn't have to do this, but it didn't populate for me when I created the header.

Resources