SwiftUI에서 Core Data를 사용해보려고 아래 코드를 튜토리얼을 참고하여 작성했다.
import SwiftUI
import CoreData
@main
struct DoneListApp: App {
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "Model")
container.loadPersistentStores { description, error in
if let error = error {
fatalError("Failed to load persistent stores: \(error)")
}
}
return container
}()
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Swift 문법은 정말 신기한(?) 것들이 많은데, 한줄 한줄 살펴보기로 하자. 첫째로 이해가 안 간 라인은 아래 코드다.
@main
struct DoneListApp: App {
lazy var persistentContainer: NSPersistentContainer = {
...
// wft... 이 신기한 문법은 무엇?
container.loadPersistentStores { description, error in
...
}
...
}()
}
... 당췌 이게 무슨 코드란 말인가.
정답은 Closure 였다. Swift 에서 Closure의 뜻을 보면,
Closures are self-contained blocks of functionality that can be passed around and used in your code. (swift.org)
JavaScript, Go 언어와 마찬가지로 Swift 도 함수를 변수로 취급하여 다른 함수의 argument 로 넘겨줄 수 있다. 그리고 함수 안에 또 다른 함수를 정의하고, 내부에 정의된 함수가 외부의 변수를 참조하는 등의 행위를 할 수 있다.
즉, Closure 는 결국 그냥 함수라고 할 수 있다. 그래서 위의 코드는 아래와같이 정의하고 쓸 수도 있다.
@main
struct DoneListApp: App {
lazy var persistentContainer: NSPersistentContainer = {
...
func handler (description: NSPersistentStoreDescription, error: Error?) -> Void {
...
}
container.loadPersistentStores(completionHandler: handler)
...
}()
...
}
결국 Swift의 Closure expression 인 아래 문법은 Closure 문법을 간편하게 쓰기 위한 일종의 syntactic sugar 인 셈이다.
{ (parameters) -> return type in
statements
}
Swift가 정말 이런 문법들이 많고, 저 Closure 문법과 함수 정의 문법 사이의 유사도가 거의 없어서 처음에 참 어렵다. (도대체 in
이라는 키워드를 쓰는건 누구 아이디어인지... in
때문에 난 처음에 for
루프 문법인줄 알았고, 실제로 for
루프 문법에서도 in
키워드를 쓴다)
참고로, DoneListApp
구조체의 변수인 persistentContainer
를 정의하는 것도 Closure로 되어 있다.
@main
struct DoneListApp: App {
lazy var persistentContainer: NSPersistentContainer = {
...
return container
}()
}
NSPersistentContainer
타입의 변수를 반환하는 Closure 함수를 바로 정의하고, 정의하자마자 바로 사용 ()
함으로써, persistentContainer
변수를 정의하는 식이다. 이는 아래와 같다.
@main
struct DoneListApp: App {
func initializeContainer() -> NSPersistentContainer {
...
return container
}
lazy var persistentContainer: NSPersistentContainer = initializeContainer()
}
원래는 다른 언어라면, persistentContainer
변수의 초기값은 nil
일 것이고, 생성자(constructor 또는 init) 함수에서 처리하는 것이 일반적일 것이다.