iOS/Toy project

[iOS : Toy Project] Todo List 만들기 (2)

yevdev 2022. 8. 16. 16:04

이전 Todo List (1)

 

[iOS : Toy Project] Todo List 만들기 (1)

이번주차 스터디 과제로, 클론 프로젝트를 간단하게 해보기로 했었다! 스터디장님이 올려주신 것 중 나는 흔히 간단하게 다들하는,, Todo List 를 만들어보기로 결정! 이미 만들어진 Project를 따라

yexjinitlog.tistory.com

 

 

자세한 코드는 여기로!

 

GitHub - yexjin/iOS_Study: iOS 토이프로젝트 모음집📱

iOS 토이프로젝트 모음집📱. Contribute to yexjin/iOS_Study development by creating an account on GitHub.

github.com

 

 

 


 

 

8/16

어제 했던 Storage에 이어, 이번엔 싱글톤 객체를 만들 Todo.swift 파일을 작성해보겠다.

 

Todo.swift 파일에는 Todo를 작성하는데 필요한 데이터를 묶어놓은 구조체와 싱글톤 객체를 위한 클래스가 생성될 것이다.

 

싱글톤 객체?

- 만들어진 인스턴스를 only 최초로 전역에 두고, 그 이후로 그냥 이 인스턴스만 사용! 접근!

- 여기서는 TodoManager가 싱글톤 객체

- 이 TodoManager안에는 Todo를 만들고 삭제, 업데이트, 저장, 획복시키는 로직이 있어야 함

 

이렇게 만들어진 구조체와 클래스(Todo.swift)

import UIKit

// TODO: Codable과 Equatable 프로토콜 추가
// Codable : JSON 파일을 Swift가 알아들을 수 있게 바꿔줌
// Equatable : 여기서는 '=='의 조건을 정의해 줄 수 있음( 값이 동등한지 ), 사용자가 손쉽게 정의할 수 있도록 해줌
struct Todo: Codable, Equatable {
    let id: Int
    var isDone: Bool
    var detail: String
    var isToday: Bool
    
    // 구조제에 선언된 프로퍼티를 변경하기 위해서는 함수앞에 mutating 키워드 붙여주기
    mutating func update(isDone: Bool, detail: String, isToday: Bool) {
        //값타입인 struct는 속성을 인스턴스 메서드 내에서 수정할 수 없음
        // 수정하려면 mutating 키워드 추가
        // TODO: update 로직 추가
        self.isDone = isDone
        self.detail = detail
        self.isToday = isToday
    }
    
    static func == (lhs: Self, rhs: Self) -> Bool {
        // TODO: 동등 조건 추가
        return lhs.id == rhs.id
    }
}

class TodoManager {
    
    static let shared = TodoManager()
    
    static var lastId: Int = 0
    
    var todos: [Todo] = []
    
    // TODO: create 로직 추가
    func createTodo(detail: String, isToday: Bool) -> Todo {
        let nextId = TodoManager.lastId + 1
        TodoManager.lastId = nextId
        
        return Todo(id: nextId, isDone: false, detail: detail, isToday: isToday)    // 왜 isToday는 Bool 값으로 안주지?
    }
    
    // TODO: add 로직 추가
    func addTodo(_ todo: Todo) {
        todos.append(todo)
        saveTodo()
    }
    
    // TODO: delete 로직 추가
    func deleteTodo(_ todo: Todo) {
        todos = todos.filter{ $0.id != todo.id }
        saveTodo()
    }
    
    // TODO: update 로직 추가
    func updateTodo(_ todo: Todo) {
        // firstIndex(of:) 는 배열의 앞에서부터 조회해서 todo와 첫번째 일치하는 값의 index를 반환
        guard let index = todos.firstIndex(of: todo) else { return }
        todos[index].update(isDone: todo.isDone, detail: todo.detail, isToday: todo.isToday)
        saveTodo()
    }
    
    // TODO: save 로직 추가
    func saveTodo() {
        Storage.store(todos, to: .documents, as: "todo.json")
    }
    
    // TODO: retrieve 로직 추가
    func retrive() {
        todos = Storage.retreive("todos.json", from: .documents, as: [Todo].self) ?? []
        
        let lastId = todos.last?.id ?? 0
        
        TodoManager.lastId = lastId
    }
}

 

 

이제 이어서 TodoViewModel도 Todo.swift에 만들어줄건데, 

TodoViewModel은 뭘하는 애냐면,, (이걸보는 중엽님.. 뭔가 이상하다면 댓글이나 디코..ㅋㅋㅋㅋㅎ)

앞서 만든 TodoManager 객체를 private 인스턴스로 가져와서 화면에 말그대로 view model! 화면에 뿌려주는 모델 역할을 한다..!

todos를 가져와 Today todos, Upcoming todos, 각 today와 upcomming의 섹션 개수를 나타내주고

화면에서의 todo 추가, 삭제, 업데이트, 회복도 나타나게 해주는 것 

즉, viewController에서는 얘만 다루면 오케이~

class TodoViewModel {
    
    enum Section: Int, CaseIterable {
        case today
        case upcoming
        
        var title: String {
            switch self {
            case .today: return "Today"
            default: return "Upcoming"
            }
        }
    }
    
    private let manager = TodoManager.shared
    
    var todos: [Todo] {
        return manager.todos
    }
    
    var todayTodos: [Todo] {
        return todos.filter { $0.isToday == true }
    }
    
    var upcompingTodos: [Todo] {
        return todos.filter { $0.isToday == false }
    }
    
    var numOfSection: Int {
        return Section.allCases.count
    }
    
    func addTodo(_ todo: Todo) {
        manager.addTodo(todo)
    }
    
    func deleteTodo(_ todo: Todo) {
        manager.deleteTodo(todo)
    }
    
    func updateTodo(_ todo: Todo) {
        manager.updateTodo(todo)
    }
    
    func loadTasks() {
        manager.retrieveTodo()
    }
}

 

 

 

이렇게 정신없게 쓰고 있는 와중, 머릿속도 정신없어서 중엽님의 깔끔한 로직 정리를 가져와보겠다 (감사합니다..🥺)

 

📌Todo.swift 정리

  1. Todo 구조체 : 가장 작은 단위의 todo
  2. TodoManager 클래스 : 실질적인 모든 기능을 수행하는 싱글톤 객체 정의
  3. TodoViewModel : TodoList (최종) 모델, TodoManager를 이용하여 Todo 리스트에 접근, 수정 등 가능

 

 

 

자, 이제 Todo.swift 와 Storage.swift 파일을 통해 Todo를 관리하고 디테일한 기능을 정리할 수 있게되었다.

 

다음은 저번에 화면구성하다가만 collectionViewCell을 정의하고 마무리 하겠다.

(음 .. 근데 cell 파일을 항상 나는 따로 만들었는데 내가 지금 공부하는 파일에는 cell의 업데이트 로직이 그냥 view controller에 다 있어서 나도 TodoListViewController.swift 파일에 다 코드를 넣음!)

- collectionView의 Cell 의 identifier와 class name을 TodoListCell 로 설정

- ViewController에서 UICollectionViewCell 클래스 만들어주기

- 컴포넌트 연결

 

 

 

내일은 tabbar controller 해봐야지