Xcode에서 프로젝트를 생성하게 되면, AppDelegate.Swift 파일에 아래와 같이 선언된 것을 볼수가 있다. 

@UIApplicationMain	//Swift 5.3이전
class AppDelegate: UIResponder, UIApplicationDelegate {
	...
}
@main	//Swift 5.3이전
class AppDelegate: UIResponder, UIApplicationDelegate {
	...
}

우선, 이 @UIApplicationMain@main에 대해서 알아보자!

 

기존의 C언어를 사용해 봤다면, main 함수를 통해 앱의 시작점을 알렸었던 경험이 있을 것이다. 

하지만, Xcode에서 iOS 프로젝트를 생성하게 되면, main함수를 찾아볼수가 없는데, 

이는, UIKit Framework가 이를 숨겨서 관리하게 된다. 

따라서, 우리는 앱의 Entry Point(시작점)를 @UIApplicationMain, @main  어노테이션을 통해 알리거나,

main.swift 파일을 생성하여 알리게 된다.

 

 

 

UIApplicationMain()

  • 우선, 앱이 시작되면 main() 함수가 호출이 된다. 
  • main() 함수는 UIApplicationMain() 함수를 호출하게 된다.

UIApplicationMain() 함수UIApplication 인스턴스를 싱글톤으로 생성하게 되는데 이에 대해 먼저 간략히 알아보자

 

 

UIApplication 이란?

UIApplication은 모든 iOS앱에 하나씩 생성이 된다 (아주 드문 경우 Subclassing한 인스턴스를 사용한다)

이들은 SingleTone으로 생성이 되며,

UIApplication의 intance 들은 UIApplicationDelegate에게 App Life Cycle(앱의 종료, 시작등 앱)에 대한 event들을 전달한다.

또한, Main Run Loop(Main Thread 에서 실행되는 run loop)을 생성하고 관리한다. 

 

Main Run Loop 작업 스케쥴링과 전달된 이벤트를 처리한다.

UIControl을 통해 들어온 action message들을 적절한 객체들에게 전달한다.

 

 

 

 

  • principalClassName: App 객체가 될 클래스의 이름이며, nil일 경우 UIApplication 으로 지정된다. 
  • appDelegateClassName: AppDelegate가 될 class 이름!

 

해당 함수는! main()에 의해 호출 되며, 

UIApplication 객체의 인스턴스를 생성하고,

Application run loop와 main event loop 을 설정하고, event들을 처리한다.

 

 

이러한 main() 함수는 UIApplicationDelegate 프로토콜 내부에 구현이 되어있다.(자세한 구현은 볼수 없는듯 ㅠ)

extension UIApplicationDelegate {

    @MainActor public static func main()
}

따라서, 우리가 AppDelegate@main 혹은 @UIApplicationMain 어노테이션을 써주게 되면, 

  • UIApplicationDelegate 프로토콜 내부에 구현된 main() 함수를 호출하게 되고, 
  • main() 함수는 UIApplicationMain() 함수를 호출하게 된다. 
  • UIApplicationMain() 함수AppDelegate를 지정, UIApplication 객체의 싱글톤 인스턴스 생성, Main Run Loop를 생성한다.

위의 과정을 거쳐 App은 실행준비를 마치게 되고, application:didFinishLaunchingWithOptions: 를 호출한다.

 

 

 

@main vs. @UIApplicationMain 

Swift 5.3이후부터는 Entry Point를 알리는 어노테이션이 @UIApplicationMain에서 @main으로 바뀌게 되었다. 

 

그렇다면 이둘의 차이점은 무엇이 있을까 알아보자 

@main은 Type-base 이기때문에, struct, class, enum등에 적용이 가능한 반면,

@UIApplicationMain은 class에서만 적용이 가능하다.

이를 통해, type 기반의 Swift 언어에서 @main 어노테이션을 통한 커스텀이 더욱더 편리할 것이라 생각이 든다.

 

 

 

 

Custom Entry-Point

@main의 경우

main static function of type() -> Voidthrows -> Void 만 선언해주면 가능하다

import UIKit
//@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        print("app-application")

        return true
    }
	...
}

custom-entry point를 지정하기 위해 AppDelegate의 @main은 주석처리 해준다. 

 

//  EntryPoint.swift
import UIKit

@main
class EntryPoint{
    
    static func main() {
        print("entry")
    }
}

EntryPoint.swift 파일을 위와 같이 생성하게 되면!

"entry"만 출력되고, 프로그램이 종료된다! 

이는 application:didFinishLaunchingWithOptions: 안에 print("app-application")가 실행되지 않는 것을 통해 알 수 있는데,

어노테이션을 통해 지정한 entry point 인 EntryPoint.swift 의 main() 함수에서,

UIApplicationMain() 을 호출하지 않았기 때문에, run-loop도 생성이 안되었고, UIApplication 객체의 인스턴스도 생성이 되지 않았기 때문에, 프로그램이 바로 종료가 된다.

 

추가로, @UIApplicationMain의 경우에는 UIApplicationDelegate를 상속받아야 한다.

 

 

 

AppDelegate Method

UI 관련 LifeCycle method들이 사라진것은 아니지만, Scene-based Life Cycle에서는

UIApplicationDelegate의 해당 method들은 동작하지 않고, 

SceneDelegate의 해당 method들이 동작하게 된다! 

 

또한, 저번 포스팅에서 Scene의 정보를 넘겨 받기 위해, SceneSession이란것 AppDelegate에 있다고 하였는데,

 

 

해당 두 method는 scene이 생성될때, scene이 삭제될때 AppDelegate에 알린다. 

또한, SceneSession은 생성된 모든 Scene에대한 정보를 관리한다.

 

 

AppDelegate는 Process LifeCycle인 didFinishLanching을 호출하고, 

configurationForSession을 통해 session이 생성되었음을 알린다. 

756

이후 Scene Delegate에서 UILife Cycle을 관리하게 되는데, Scene이 삭제가 되면,

AppDelegate의 didDiscardSceneSession method를 통해 scene의 정보를 전달한다.

 

이후 앱이 완전히 종료 되게 되면,

AppDelegate의 applicationWillTerminate 를 호출하고 종료된다.

 

 

위의 과정과 같이 AppDelegate는 processLife Cycle과 Scene Life Cycle만 가지게 되며,

UILife Cycle은 SceneDelegate가 담당하게 되었다. 

 

 

 

iOS 13 이전에서의 AppDelegate 사용

그렇다면, iOS13 이전 버전에서는,

SceneDelegate 파일을 삭제

AppDelegate에서 SceneSession LifeCycle method를 삭제

AppDelegate 에 window property 추가

var window: UIWindow?

info.plist 에서 Application Scene Manifest 삭제

 

위와 같은 과정을 거치면, iOS13 미만에서도 사용이 가능하다! 

이렇게 되면, UI Life Cycle과 관련된 method들은 사용이 가능하게 되며, app-based life cycle이 된다.

 

 

Reference

https://github.com/apple/swift-evolution/blob/main/proposals/0281-main-attribute.md

What is @main in Swift

 

Apple Documentation

복사했습니다!