Subclass UIApplication with Swift
Asked Answered
B

3

91

In Objective C it was simple: it was sufficient to update the main.m file and change the UIApplicationMain() parameters

return UIApplicationMain(argc, argv, NSStringFromClass([CustomUIApplication class]), NSStringFromClass([AppDelegate class]));

But in swift there is no main.m file, since the guide says

“Code written at global scope is used as the entry point for the program, so you don’t need a main function.”

So, how to subclass UIApplication in swift?? Any suggestion?

Boyes answered 3/6, 2014 at 16:12 Comment(1)
Why would it be preferrable to change UIApplicationMain() parameters, to adding the class name under NSPrincipalClass in the app-info.plist?Marelda
B
173

NOTE the syntax has been updated for XCode 10.1 and Swift 5 in Jun 2019 ( credits to matt's answer here && Tung Fam's answer here ), if you are looking for the previous syntaxes look at the edit section.

Ok, I've found the solution

First, I've noticed that, at the top of the AppDelegate.swift file, there is this line

@UIApplicationMain

Since this line is outside any scope (it's at file level), it's executed immediately, and I assume that the compiler translate it in a standard main function.

So, I did this, starting from a new Swift-Only application:

  • commented out @UIApplicationMain
  • added a main.swift file like this (FLApplication is my subclass).
    IMPORTANT the file MUST BE NAMED main.swift, since top level statements are not supported on other files! You can't add the UIApplicationMain() call inside any other file, otherwise you'll receive this error:

Expressions are not allowed at the top level

This is the main.swift file

UIApplicationMain(
    CommandLine.argc, CommandLine.unsafeArgv, 
    NSStringFromClass(FLApplication.self), NSStringFromClass(AppDelegate.self)
)

Then, create a swift file for the UIApplication subclass, FLApplication.swift, with this code:

import UIKit
import Foundation

class FLApplication: UIApplication {
    override func sendEvent(_ event: UIEvent) {
        super.sendEvent(event)
        print("send event")
    }
}

now, UIApplication is correctly subclassed and you'll see the "send event" messages in the log


OLD EDITS
For reference, since this has changed a lot from version 1 to version 3, I leave here all the previous edits


EDIT - MARCH 2015

As commented by Hu Junfeng now the explanations about UIApplicationMain and the main.swift file are documented in the Attributes section of The Swift Language Reference: Link

As commented by Thomas Verbeek In the XCode 6.3 Beta, you might find that C_ARGC and C_ARGV have been renamed to Process.argc and Process.unsafeArgv respectively. Your UIApplicationMain call in the main.swift file will need updating to:

UIApplicationMain(Process.argc, Process.unsafeArgv, NSStringFromClass(FLApplication), NSStringFromClass(AppDelegate))

The pre-XCode 8 syntax was

import Foundation
import UIKit

UIApplicationMain(C_ARGC, C_ARGV, NSStringFromClass(FLApplication), NSStringFromClass(AppDelegate))

EDIT - DEC 2016

Solution for Xcode 8, before beta 6

import Foundation
import UIKit

UIApplicationMain(
    CommandLine.argc,
    UnsafeMutableRawPointer(CommandLine.unsafeArgv)
        .bindMemory( 
            to: UnsafeMutablePointer<Int8>.self, 
            capacity: Int(CommandLine.argc)),
    NSStringFromClass(FLApplication.self),
    NSStringFromClass(AppDelegate.self)
)
Boyes answered 3/6, 2014 at 17:16 Comment(11)
I'm trying to understand if it's possible to call UIApplicationMain() directly in the AppDelegate.swift file, since it seems that there is a "swift version" of this method, but I have a compiler error, I'll do some tests searching for a "swift only" solutionBoyes
Incredibly cool. I'm curious as to what the use-case is for overriding sendEvent: nowadays (I remember having to do it in iOS 3...)Notice
In case someone wants to know, UIApplicationMain and main.swift are documented in the Attributes section of The Swift Language Reference. developer.apple.com/library/ios/documentation/Swift/Conceptual/…Grand
Thanks for the hint, I'm 99% sure that in the first release of the Swift manual it wasn't documented :-) however I'll update the answer adding your informations.Boyes
Great answer. In the XCode 6.3 Beta, you might find that C_ARGC and C_ARGV have been renamed to Process.argc and Process.unsafeArgv respectively. Your UIApplicationMain call in the main.swift file will need updating to UIApplicationMain(Process.argc, Process.unsafeArgv, NSStringFromClass(KBApplication), NSStringFromClass(AppDelegate))Matos
In addition to @hujunfeng, main.swift is explained in Files and Initialization developer.apple.com/swift/blog/?id=7Ritter
Latest version of calling UIApplicationMain can be found here for Swift 3. https://mcmap.net/q/245838/-xcode-8-beta-6-main-swift-won-39-t-compileAlidia
@Alidia thanks, I updated the answer adding the credits to the answer you linkedBoyes
@Boyes In main.swift, UIApplicationMain() returns an Int32. Therefore, shouldn't it be wrapped in an exit() call so the process can report its exit status back to the OS? Like exit(UIApplicationMain(...))Redneck
@Boyes Never mind my previous comment about using exit(). The documentation for UIApplicationMain() says the "function never returns. When users exit an iOS app ... the app moves to the background." Therefore, it seems iOS is "wrapping" the launched iOS app process, and ignoring whatever exit code the process would terminate with.Redneck
in Xcode 10 those first two arguments would be CommandLine.argc and CommandLine.unsafeArgvAgitate
M
4

One alternative is to extend UIApplication instead of subclassing it. According to the iBook released by Apple, extensions in Swift can:

Add computed properties and computed static properties Define instance methods and type methods Provide new initializers Define subscripts Define and use new nested types Make an existing type conform to a protocol

Excerpt From: Apple Inc. “The Swift Programming Language.

If your needs in subclassing UIApplication are satisfied by those capabilities, an Extension might be the way to go.

Merola answered 3/6, 2014 at 17:15 Comment(1)
nice advice - not answer imho :)Liquidation
E
2

Swift Subclass UIApplication

  1. Create a subclass of UIApplication and add your logic

    import UIKit
    
    class CustomUIApplication: UIApplication {
        override func sendEvent(_ event: UIEvent) {
            super.sendEvent(event)
        }
    }
    
  2. Create a main.swift file that calls the UIApplicationMain() global function[About], which is the new entry point of your application that is called by the OS. This function receives argument from called function, UIApplication class name, UIApplicationDelegate class name and starts the main RunLoop[About].

    import UIKit
    
    UIApplicationMain(
        CommandLine.argc,
        CommandLine.unsafeArgv,
    
        NSStringFromClass(CustomUIApplication.self), //was created at step 1
        NSStringFromClass(AppDelegate.self)
    )
    
  3. Remove/comment the @UIApplicationMain annotation on the default AppDelegate.

@UIApplicationMain generates main.swift.

If you don't you will get a compile error:

UIApplicationMain attribute cannot be used in a module that contains top-level code

//@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    //...
}
Excalibur answered 29/4, 2020 at 14:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.