Swift Programming Patterns

MVVM (Model-View-ViewModel) 패턴

MVVM 패턴 소개 (Introduction to MVVM Pattern)

MVVM (Model-View-ViewModel) 패턴은 UI의 로직과 데이터 처리를 분리하여 코드의 유지보수성과 테스트 용이성을 향상시키는 디자인 패턴입니다. 이 패턴은 세 가지 주요 구성 요소로 이루어져 있습니다:

  • Model: 애플리케이션의 데이터와 비즈니스 로직을 포함합니다. 데이터베이스와의 상호작용, 네트워크 요청 등의 기능을 담당합니다.
  • View: 사용자 인터페이스(UI) 요소를 정의하며, 사용자와의 상호작용을 처리합니다. View는 ViewModel을 통해 데이터를 표시하고 사용자 입력을 전달합니다.
  • ViewModel: View와 Model 사이의 중간자 역할을 하며, View에서 사용할 수 있는 데이터와 명령을 제공합니다. ViewModel은 Model로부터 데이터를 가져오고, 이를 가공하여 View에 전달합니다.

MVVM 패턴의 구조 (Structure of MVVM Pattern)

  1. Model: 데이터와 비즈니스 로직을 정의합니다.
// Model
struct User {
    var name: String
    var age: Int
}
  1. ViewModel: Model 데이터를 가공하고 View와의 바인딩을 처리합니다.
import Combine

// ViewModel
class UserViewModel: ObservableObject {
    @Published var userName: String = ""
    @Published var userAge: String = ""

    private var user: User?

    func loadUser() {
        // 예시 데이터
        self.user = User(name: "Alice", age: 30)
        updateViewModel()
    }

    private func updateViewModel() {
        guard let user = user else { return }
        userName = user.name
        userAge = "\(user.age)"
    }
}
  1. View: UI를 정의하고 ViewModel을 바인딩합니다.
import SwiftUI

// View
struct UserView: View {
    @StateObject private var viewModel = UserViewModel()

    var body: some View {
        VStack {
            Text("Name: \(viewModel.userName)")
            Text("Age: \(viewModel.userAge)")
        }
        .onAppear {
            viewModel.loadUser()
        }
    }
}

MVVM 패턴의 예제 (MVVM Example)

이제 MVVM 패턴을 적용하여 SwiftUI에서 간단한 사용자 정보를 표시하는 예제를 작성합니다.

import SwiftUI
import Combine

// Model
struct User {
    var name: String
    var age: Int
}

// ViewModel
class UserViewModel: ObservableObject {
    @Published var userName: String = ""
    @Published var userAge: String = ""

    private var user: User?

    func loadUser() {
        // 예시 데이터
        self.user = User(name: "Alice", age: 30)
        updateViewModel()
    }

    private func updateViewModel() {
        guard let user = user else { return }
        userName = user.name
        userAge = "\(user.age)"
    }
}

// View
struct UserView: View {
    @StateObject private var viewModel = UserViewModel()

    var body: some View {
        VStack {
            Text("Name: \(viewModel.userName)")
            Text("Age: \(viewModel.userAge)")
        }
        .onAppear {
            viewModel.loadUser()
        }
    }
}

// Preview
struct UserView_Previews: PreviewProvider {
    static var previews: some View {
        UserView()
    }
}

Delegate 및 Closure 기반 프로그래밍

Delegate 패턴 소개 (Introduction to Delegate Pattern)

Delegate 패턴은 객체 간의 통신을 위해 사용되며, 한 객체가 다른 객체의 이벤트나 상태 변화를 전달받을 수 있도록 합니다. 주로 콜백 메서드를 통해 상호작용합니다. Delegate는 주로 protocol을 사용하여 정의합니다.

Delegate 패턴의 구조 (Structure of Delegate Pattern)

  1. Delegate Protocol: 이벤트나 상태 변화를 전달받기 위한 메서드를 정의합니다.
protocol DataDelegate: AnyObject {
    func didReceiveData(_ data: String)
}
  1. Delegate 객체: 데이터를 제공하는 객체에서 delegate 프로퍼티를 통해 delegate 객체를 설정하고, 이벤트 발생 시 delegate 메서드를 호출합니다.
class DataProvider {
    weak var delegate: DataDelegate?

    func fetchData() {
        // 데이터를 가져온 후
        let data = "Sample Data"
        delegate?.didReceiveData(data)
    }
}
  1. Delegate 구현 객체: delegate 프로토콜을 채택하여 메서드를 구현합니다.
class DataReceiver: DataDelegate {
    func didReceiveData(_ data: String) {
        print("Received data: \(data)")
    }
}
  1. Delegate 설정: delegate를 설정하고 데이터를 요청합니다.
let provider = DataProvider()
let receiver = DataReceiver()

provider.delegate = receiver
provider.fetchData()  // "Received data: Sample Data"

Closure 기반 프로그래밍 소개 (Introduction to Closure-based Programming)

Closure는 코드 블록을 변수처럼 다룰 수 있는 기능으로, 함수나 메서드 내에서 사용할 수 있습니다. Closure를 사용하면 함수 호출 시 동작을 커스터마이즈하거나 콜백을 구현할 수 있습니다.

Closure의 예제 (Example of Closures)

  1. 기본 클로저 사용: 함수에 클로저를 인자로 전달하여 실행할 수 있습니다.
func performOperation(_ operation: () -> Void) {
    // 클로저 호출
    operation()
}

performOperation {
    print("Operation performed")
}
// "Operation performed"
  1. 클로저와 인자: 클로저를 사용하여 인자를 받아서 처리할 수 있습니다.
func calculate(a: Int, b: Int, operation: (Int, Int) -> Int) -> Int {
    return operation(a, b)
}

let sum = calculate(a: 5, b: 3) { $0 + $1 }
print(sum)  // 8
  1. 클로저 캡처링: 클로저는 자신이 정의된 환경을 캡처할 수 있습니다.
func makeIncrementer(incrementAmount: Int) -> () -> Int {
    var total = 0
    return {
        total += incrementAmount
        return total
    }
}

let incrementByTwo = makeIncrementer(incrementAmount: 2)
print(incrementByTwo())  // 2
print(incrementByTwo())  // 4
  1. escaping 클로저: 클로저가 함수의 실행이 끝난 후에도 실행될 수 있도록 허용합니다.
func fetchData(completion: @escaping (String) -> Void) {
    DispatchQueue.global().async {
        // 비동기 작업 시뮬레이션
        sleep(2)
        completion("Data fetched")
    }
}

fetchData { result in
    print(result)  // "Data fetched"
}

이와 같이 Swift에서 MVVM 패턴, Delegate 패턴, 그리고 Closure 기반 프로그래밍의 개념과 활용 방법을 상세히 설명하고 다양한 예제를 제공했습니다.

Leave a Reply

Your email address will not be published. Required fields are marked *