apple/Docs, iOS, Swift

[iOS] SwiftData in UIKit

lgvv 2023. 12. 15. 03:05

SwiftData in UIKit

 

이번에는 SwiftData를 UIKit에서 사용해보자!

CoreData를 학습하기 전에는 SwiftData가 진짜 어려웠는데, 지금은 훨씬 배우기 쉽다.

 

SwiftData를 사용하면 CoreData와 개념적으로 부합하고 있어서 학습하는데 더 쉽게 느껴졌다.

또한 SwiftData가 가진 장점이겠지만, CoreData보다 검색이나 등등이 더 스위프트 언어와 잘 어울린다는 점.

 

다만 고려해야할 점으로는 @objc를 사용하는 서비스의 경우에는 도입하기가 조금 어려울 수 있다.

 

 

이번 실습 결과물 영상

- create: 아이템 추가

- read: 전체 아이템 reloadData

- update: 트리 -> 산타의 선물로 데이터 갱신

- delete: 모든 데이터 삭제

실습 결과물

 

실습용 코드

//
//  ThirdViewController.swift
//  CoreDataExample
//
//  Created by 🏝️ GeonWoo Lee on 12/15/23.
//

import UIKit
import RxSwift
import RxCocoa
import SwiftData

final class ThirdViewController: UIViewController {
    
    /// 모델 컨테이너
    var modelContainer: ModelContainer?
    
    private var disposeBag = DisposeBag()
    
    private func bind() {
        tableView.rx.itemSelected
            .bind(with: self) { this, indexPath in
                print("indexPath \(indexPath)")
            }.disposed(by: disposeBag)
        
        createButton.rx.tap
            .debug("createButton")
            .bind(with: self) { this, _ in
                let user = User(name: "🎄트리", age: 1225)
                this.modelContainer?.mainContext.insert(user)
                this.tableView.reloadData()
            }.disposed(by: disposeBag)
        
        readButton.rx.tap
            .debug("readButton")
            .bind(with: self) { this, _ in
                this.tableView.reloadData()
            }.disposed(by: disposeBag)
        
        updateButton.rx.tap
            .debug("updateButton")
            .bind(with: self) { this, _ in
                let descriptor = FetchDescriptor<User>()
                let users = (try? this.modelContainer?.mainContext.fetch(descriptor)) ?? []
                
                let filteredUsers = users.filter { $0.name == "🎄트리" }
                filteredUsers.forEach { $0.name = "🎁 산타의 선물"}
                
                try? this.modelContainer?.mainContext.save()
                this.tableView.reloadData()
            }.disposed(by: disposeBag)
        
        deleteButton.rx.tap
            .debug("deleteButton")
            .bind(with: self) { this, _ in
                let descriptor = FetchDescriptor<User>()
                
                try? this.modelContainer?.mainContext.delete(model: User.self)
                this.tableView.reloadData()
            }.disposed(by: disposeBag)
    }
    
    // MARK: - Initialize
    
    init() {
        super.init(nibName: nil, bundle: nil)
        
        configureUI()
        bind()
        
        modelContainer = try? ModelContainer(for: User.self)
    }
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    // MARK: - UIComponents
    
    private lazy var tableView: UITableView = {
        let tv = UITableView()
        tv.delegate = self
        tv.dataSource = self
        return tv
    }()
    
    private lazy var container: UIStackView = {
        let stackView = UIStackView()
        stackView.distribution = .fillProportionally
        stackView.spacing = 3
        return stackView
    }()
    
    private lazy var createButton: UIButton = {
        let button = makeButton("create")
        return button
    }()
    
    private lazy var readButton: UIButton = {
        let button = makeButton("read")
        return button
    }()

    private lazy var updateButton: UIButton = {
        let button = makeButton("update")
        return button
    }()
    
    private lazy var deleteButton: UIButton = {
        let button = makeButton("delete")
        return button
    }()
    
    private func makeButton(_ title: String) -> UIButton {
        let button = UIButton()
        button.setTitle(title, for: .normal)
        button.backgroundColor = .blue
        button.layer.cornerRadius = 6
        return button
    }
    
    private func configureUI() {
        view.addSubview(container)
        container.snp.makeConstraints {
            $0.top.leading.trailing.equalTo(view.safeAreaLayoutGuide)
        }
        container.addArrangedSubview(createButton)
        container.addArrangedSubview(readButton)
        container.addArrangedSubview(updateButton)
        container.addArrangedSubview(deleteButton)
        
        view.addSubview(tableView)
        tableView.snp.makeConstraints {
            $0.top.equalTo(container.snp.bottom)
            $0.leading.trailing.bottom.equalToSuperview()
        }
    }
}

extension ThirdViewController: UITableViewDelegate, UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        let descriptor = FetchDescriptor<User>()
        let users = (try? modelContainer?.mainContext.fetch(descriptor)) ?? []
        return users.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = UITableViewCell()
        
        let descriptor = FetchDescriptor<User>()
        let users = (try? modelContainer?.mainContext.fetch(descriptor)) ?? []

        var content = cell.defaultContentConfiguration()
        content.image = UIImage(systemName: "bookmark")
        content.imageProperties.tintColor = .green
        
        content.text = users[indexPath.row].name
        cell.contentConfiguration = content

        return cell
    }
    
}

@Model
final class User {
    var name: String
    var age: Int

    init(name: String, age: Int) {
        self.name = name
        self.age = age
    }
}

 

 

다음에는 SwiftUI에서 SwiftData를 사용하는걸 정리해보도록 하자~!