์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
- SnapKit
- node.js
- rxcocoa
- Xcode
- tableView
- combine
- ๋ฐฑ์ค
- arkit
- Lv2
- swift
- designpattern
- XCTest
- TCA
- SwiftUI
- BOJ
- CollectionView
- ํจ์คํธ์บ ํผ์ค
- RxSwift
- BFS
- realm
- ios
- UIKit
- Kuring
- Swfit
- ํ๋ก๊ทธ๋๋จธ์ค
- reactorkit
- raywenderlich
- MVVM
- visionOS
- Flutter
- Today
- Total
lgvv98
ch13 Todo ๋ฆฌ์คํธ ์ฝ๋๋ฆฌ๋ทฐ ๋ณธ๋ฌธ
ch13 Todo ๋ฆฌ์คํธ ์ฝ๋๋ฆฌ๋ทฐ
๐ฅ ์บ๋ฟ๋งจ 2021. 6. 26. 18:17โ ์ด๋ฒ์๊ฐ์๋ Todo ๋ฆฌ์คํธ์ ๋ํด์ ๋ฆฌ๋ทฐํ๋๋ก ํด๋ณผ๊ฒ.
์ด๊ฒ ๊ธ๋ฐฉ ๋๋ ์ค ์์๋๋ฐ, ์๊ฐ๋ณด๋ค ๋ฐฐ์ธ๊ฒ ๋ง์ ์๊ฐ์ด์๋ค. (๋ชจ๋ฅด๋๊ฒ ์ ์ ์ ๋ ๋ง์์ง์ง?)
๐คฆโ๏ธ ๊น์ ๋ํด ์ฌ์ฉ์ด ๋ฏธ์ํ๋ฐ ์ค์๋ฅผ ํ๋ ๋ฐ๋์,, ๊นํ๋ธ๋ฅผ ๋ค์ ํ^_^_^_^_^_
-> ๊นํ๋ธ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ํฌ์คํ
๐คฆโ๏ธ git ์๊ฒฉ ์ ์ฅ์์ ์ฌ๋ผ๊ฐ commit ๋๋๋ฆฌ๊ธฐ
์ด๋ฒ์๋ ๊น ์๊ฒฉ ์ ์ฅ์์ ์ฌ๋ผ๊ฐ commit ๋๋๋ฆฌ๋ ๋ฒ์ ๋ํด์ ์์๋ณด์. (๋ฌธ์ ) ๊นํ๋ธ์ ์๋์ผ๋ก ํ์ผ์ ์ถ๊ฐํ์๋๋ฐ, ๋์ค์ ์๊ณ ๋ณด๋๊น ์ฝ๋๊ฐ ์๋ชป๋์์์. ๋ฐ๋ผ์ ๊น ํ๋ธ์์ ๋ค์ ์
rldd.tistory.com
๊ทธ๋ผ ์ด์ ๋ค์ ์ฝ๋ ๋ฆฌ๋ทฐ๋ฅผ ์์ํด ๋ณด๋๋ก ํ ๊น
https://github.com/lgvv/fastCampus/tree/main/TodoList
lgvv/fastCampus
Contribute to lgvv/fastCampus development by creating an account on GitHub.
github.com
(๋ชฉ์ฐจ)
1. ViewDidAppear ์๋ ์ธ์๊ฐ์ด ๋ค์ด๊ฐ๋ค!
2. guard๋ฌธ์ ๋ํด์ ๋ค์๊ธ ์ง๊ณ ๊ฐ์.
3. collectionView ์น์ ์ ๋ฐ๋ผ ๋ค๋ฅด๊ฒ ๊ตฌ์ฑํ๋ ๋ฒ
4. collectionView ์น์ ์ ๋ฐ๋ฅธ ์์ดํ ์ ์ด๋ป๊ฒ ๊ตฌ์ฑํ ๊น?
5. Todo - MVVM ๊ด์ ์์์ ๋ถ์
6. Storage ํ์ผ์ ๋ํ ๋ถ์
โ ViewDidAppear ์๋ ์ธ์๊ฐ์ด ๋ค์ด๊ฐ๋ค!
// viewDidLoad - ๋ฉ๋ชจ๋ฆฌ์ ์ฌ๋ผ์์ ๋
override func viewDidLoad() {
super.viewDidLoad()
}
// viewDidAppear - ํ๋ฉด์ ๋ณด์ฌ์ง ๋
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
๋ณด์ด๋๊ฐ..? viewDidAppear์๋ animated๋ผ๋ ์ธ์๊ฐ์ด ๋ค์ด๊ฐ๋ค. ํฌ๊ฒ ์ค์ํ๊ฑด ์๋์ง๋ง, ์๋ฌ๋ฅผ ํ๋ ๋ด์... ๊ณต์๋ฌธ์๋ฅผ ์ฝ์ด๋ณด๋ฉด ์ฝ๊ฒ ์ดํดํ ์ ์๋ค.
<viewDidLoad>
https://developer.apple.com/documentation/uikit/uiviewcontroller/1621495-viewdidload
<ViewDidAppear>
https://developer.apple.com/documentation/uikit/uiviewcontroller/1621423-viewdidappear
โ guard๋ฌธ์ ๋ํด์ ๋ค์๊ธ ์ง๊ณ ๊ฐ์.
guard let detail = inputTextField.text, detail.isEmpty == false else { return }
์ด๋ฐ ์ฝ๋๊ฐ ์์๋ค. guard๋ฌธ์ ๋ํด์๋ ๋งค๋ฒ ํท๊ฐ๋ฆฐ๋ค...
guard๋ฌธ์ let ์์ ์กฐ๊ฑด์ด true ์ด๋ฉด ์ง๋๊ฐ๊ณ , ๊ทธ๋ ์ง ์์ผ๋ฉด return ๊ตฌ๋ฌธ์ ์คํํจ์ผ๋ก์จ ์ฌ์ฉ ๋ผ
ํ๋ก๊ทธ๋จ์ ์ฃฝ๋ ์ํฉ์ ๋ง์ ์ ์์ด์ ๋น๊ต์ ์ผ๋ก ์์ ํ๊ฒ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ, if let๊ณผ ๋น๊ต๋๊ณค ํ๋ค.
์ด์ ๋ํ ๋ ์ดํด๋ ์ฐธ๊ณ ์๋ฃ๋ก ๋จ๊ฒจ๋๋๋ก ํ ๊ฒ
์ ๊ทธ๋์ ์ ์ฝ๋๋ฅผ ํด์ํด๋ณด๋ฉด, inputTextField.text๊ฐ ์กด์ฌํด์ text์ ๋ฌด์์ด๋ผ๋ ์ ๋ ฅ๋์ด ์๋ค๋ฉด! ์ค๋ฌด์คํ๊ฒ ๋์ด๊ฐ๊ณ ๊ทธ๋ ์ง ์๋ค๋ฉด return ํ๋ผ๋ ์๋ฏธ์ด๋ค.
โ collectionView ์น์ ์ ๋ฐ๋ผ ๋ค๋ฅด๊ฒ ๊ตฌ์ฑํ๋ ๋ฒ
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
// TODO: ์น์
๋ณ ์์ดํ
๋ช๊ฐ
if section == 0 {
return todoListViewModel.todayTodos.count
} else {
return todoListViewModel.upcompingTodos.count
}
}
์์ ์ฝ๋์์ ์ฒ๋ผ ์น์ ์ ๋ฐ๋ผ ๋ค๋ฅด๊ฒ ์ค ์ ์์ด.
โ collectionView ์น์ ์ ๋ฐ๋ฅธ ์์ดํ ์ ์ด๋ป๊ฒ ๊ตฌ์ฑํ ๊น?
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
// TODO: ์ปค์คํ
์
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TodoListCell", for: indexPath) as? TodoListCell else {
return UICollectionViewCell()
}
var todo: Todo
if indexPath.section == 0 {
todo = todoListViewModel.todayTodos[indexPath.item]
} else {
todo = todoListViewModel.upcompingTodos[indexPath.item]
}
cell.updateUI(todo: todo)
// TODO: todo ๋ฅผ ์ด์ฉํด์ updateUI
// TODO: doneButtonHandler ์์ฑ
// TODO: deleteButtonHandler ์์ฑ
cell.doneButtonTapHandler = { isDone in
todo.isDone = isDone
self.todoListViewModel.updateTodo(todo)
self.collectionView.reloadData()
}
cell.deleteButtonTapHandler = {
self.todoListViewModel.deleteTodo(todo)
self.collectionView.reloadData()
}
return cell
}
MVVM ํจํด์ด๋ผ์ var todo : Todo ํ์ ์ธ ๊ฒ์ ํ์ธํ ์ ์์ด.
๋ํ ๋ทฐ ๋ชจ๋ธ์ ๋ฐ๋ผ์ todo์ ์๋ก ๋ค๋ฅธ ์ ๋ณด๋ฅผ ๋ถ์ฌํจ์ผ๋ก์จ, ์น์ ์ ๋ง๊ฒ ์์ดํ ์ ๋ฐฐ์นํ๋ ๊ฒ์ ํ์ธํ ์ ์์ง.
MVC ํจํด์์ cell.titleLabel ์ด๋ฐ์์ผ๋ก ์ ๊ทผํ๋ ๊ฒ๊ณผ๋ ๋ค๋ฅด๊ฒ ์ฐ์ ๊ฐ ์น์ ์์ todo์ ๋ฃ๋ ๋ชจ์ต์ ๋ณผ ์ ์์ด.
๐ก์ฌ๊ธฐ์ ์๋ฌธ!! todo๋ ๋ฐฐ์ด์ด ์๋๋ฐ, ์ด๋ป๊ฒ ์ฌ๋ฌ๊ฐ์ ์์ดํ ์ ๋ณด์ฌ์ค ์๊ฐ ์๋๊ฑธ๊น?
-> ํด๋ต์ cellForItem ๋ฉ์๋๋ ์ ์ ๊ฐฏ์๋งํผ ๋ฐ๋ณต๋์ ์คํ ๋ผ
์ฆ, ์ ์ด 10๊ฐ๋ฉด ์ด ํจ์ ์์ฒด๊ฐ 10๋ฒ ์คํ๋๋๊ฑฐ๋ผ๊ณ ์๊ฐํ๋ฉด ๋ผ.
cell์ด UI๋ฅผ ๊ตฌ์ฑํ๋ ๋ถ๋ถ์ ํ๋ฒ ๋ด๋ณผ๊น?
func updateUI(todo: Todo) {
// TODO: ์
์
๋ฐ์ดํธ ํ๊ธฐ
checkButton.isSelected = todo.isDone
descriptionLabel.text = todo.detail
descriptionLabel.alpha = todo.isDone ? 0.2 : 1 // ์๋ฃ๋์์ผ๋ฉด ํฌ๋ช
๊ทธ๋ ์ง ์์ผ๋ฉด ๋ถํฌ๋ช
deleteButton.isHidden = todo.isDone == false
showStrikeThrough(todo.isDone) // ๋ ์ด์์์ ๊ธธ์ด ์กฐ์
}
updateUI๋ ์ด๋ ๊ฒ ๊ตฌ์ฑ๋์๋๋ค.
checkButton์ todo์ isDone์ ํตํด true, false๋ฅผ ํ์ธํ๋ค.
descriptionLabel์ alpha๋ฅผ ์ค์ผ๋ก์จ isDone ์ํ์ ๋ฐ๋ผ ํฌ๋ช ๋ถํฌ๋ช ์ ์ค์ ํ๊ฒ ํ๋ค.
deleteButton์ ๊ฒฝ์ฐ์๋ ์ฝ๋๊ฐ ์กฐ๊ธ ํน์ดํ๋ฐ, ๋ณ์๋ฅผ ํ๋ ๋ฐ๋ก ๋์ด์ ๊ด๋ฆฌํ๋ ๋ฐฉ๋ฒ๋ ์์ง๋ง ๊ตณ์ด ๊ทธ๋ ๊ฒ ํ์ง ์์๋, todo.isDone์ด true์ธ ๊ฒฝ์ฐ ์ ํํ๋ค๋ ์๋ฏธ๋ผ ์ญ์ ๋ฒํผ์ ํ์ฑํ ํด์ผํ๋๋ฐ, ๋ถ๋ฆฌ์ธ ๋ณ์๋ฅผ ์ฌ์ฉํ์ฌ ์์ฑํ์๋ค.
์ฆ, todo.isDone์ด false์ ๊ฐ์๊ฒฝ์ฐ true๋ฅผ ๋ฐํ ๊ทธ๋ ์ง ์์ผ๋ฉด false๋ฅผ ๋ฐํํ๋ค.
๋ ผ๋ฆฌํ๋ก ์๊ฐ์ ๋ฐฐ์ด ๋ ผ๋ฆฌ์์ ๋ ์ฌ๋ฆฌ๋ฉด ์ฝ๋ค.
๋ง์ง๋ง์ผ๋ก showStrikeThrough๋ ๋ ์ด์์์ ๊ธธ์ด ์กฐ์ ํ๋ ๊ฑด๋ฐ, ์ด๊ฑด ๋ค์ ์ฝ๋์ ํจ๊ป ๋ณด์.
showStrikeThrough ๋ฉ์๋์ ๋ํด์ ์์๋ณด์.
private func showStrikeThrough(_ show: Bool) {
if show {
strikeThroughWidth.constant = descriptionLabel.bounds.width
} else {
strikeThroughWidth.constant = 0
}
}
โญ๏ธ showStrikeThrough ๋ฉ์๋๋ ๊ทธ๋ฅ ๋ทฐ๋ฅผ ํ๋ ํ์์ผ๋ก ๊ฝ ์ฑ์์ ํ๋์ ์ ๊ณผ ๊ฐ์ ํจ๊ณผ๋ฅผ ์ฐ์ถํ๋ค. (๋๋ฆ skill)
๊ทธ๋ผ ๋ง์ง๋ง์ผ๋ก ํธ๋ค๋ฌ๋ฅผ ๋ณผ๊น?
์ด๊ฑด ๋ฌธ๋ฒ๊ณผ ๊ด๋ จํ ๋ถ๋ถ์ธ๋ฐ ์๋ ์ฐธ๊ณ ๋ถ๋ถ์ ์ฐ์ ๋ฃ์ด๋๊ธฐ๋ ํ๋ค
var doneButtonTapHandler: ((Bool) -> Void)?
var deleteButtonTapHandler: (() -> Void)?
ํธ๋ค๋ฌ์ ๋ํ ๋ถ๋ถ์ด๋ค.
completion Handler์ ๋ํ ๋ถ๋ถ์ ๊ผญ ์ฐธ๊ณ ๋ฅผ ๋ณด์์ผ๋ฉด ์ข๊ฒ ๋ค. ์ดํด๊ฐ ํจ์ฌ ์ฝ๋ค...!
โ Todo - MVVM ๊ด์ ์์์ ๋ถ์
// TODO: Codable๊ณผ Equatable ์ถ๊ฐ
struct Todo: Codable, Equatable {
let id: Int
var isDone: Bool
var detail: String
var isToday: Bool
mutating func update(isDone: Bool, detail: String, isToday: Bool) {
// TODO: update ๋ก์ง ์ถ๊ฐ
self.isDone = isDone
self.detail = detail
self.isToday = isToday
}
static func == (lhs: Self, rhs: Self) -> Bool {
// TODO: ๋๋ฑ ์กฐ๊ฑด ์ถ๊ฐ
return lhs.id == rhs.id
}
}
๋์ ๋ค๋ฅธ ํฌ์คํ ์์ Equatable์ ๋ํด์ ํฌ์คํ ํ์ง๋ง, ์ฝ๊ฒ ๋งํด์ ์ค์ํํธ์์ ๊ธฐ๋ณธ ํ์ ์ด ์๋ ๊ตฌ์กฐ์ฒด๋ ํด๋์ค์ ์ธ์คํด์ค๊ฐ์ ๊ฐ ๋น๊ต๋ฅผ ์ํด์ ๋ด๊ฐ ๋ฐ๋ก ์ ์ํด์ผ ํ๋๋ฐ ๊ทธ์ ํ์ํ ๊ฒ์ด๋ค.
๋ค์์ โญ๏ธCodable!! ์ด ์น๊ตฌ๋ ๋ด๊ฐ ๋ค๋ฅธ ํ๋ก์ ํธ๋ ๊ณต๋ถ์์ ๋ง์ด ๋ณด์์๋๋ฐ, ์ ๋๋ก ์ดํด๊ฐ ์ด๋ ค์ ๋ค...
์๋ฌดํผ ์ด๋ฒ ์๊ฐ์๋ ๊ฐ๋ ์ ๋ํด์ ๊ฐ๋ตํ ์ง๊ณ ๋์ด๊ฐ๊ณ ๋ค์์๋ ์ง์ ์ฝ๋๋ก ํ์ฑํ๋ ์ฐ์ต๊น์ง ํด๋ณด๋๋ก ํ์.
Codable์ด๋, JSONํํ๋ก ์๋ ํ์ผ์ key๋ฅผ ๋ณ์๋ช ์ผ๋ก ์ผ์์, ์ฐ๋ฆฌ๊ฐ ์์ฝ๊ฒ ์ฌ์ฉํ ์ ์๊ฒ ๋ง๋ค์ด์ฃผ๋ ๊ฒ์ด๋ค.
๋ค์์ mutating ํค์๋์ ๋ํด์ ๋ณด์.
์ฝ๊ฒ ๋งํด์ ๊ตฌ์กฐ์ฒด ๋ด์์ ๋ฐ์ดํฐ์ ๊ฐ์ ์์ ํด ์ฃผ๊ธฐ ์ํด์ ์์ด์ผ ํ๋ค๊ณ ํ๋ค..!
๋ง์ง๋ง func == ์ ๋์ ํฌ์คํ ์ ์ฐธ๊ณ ํ๊ธฐ ๋ฐ๋.
๐ ch13 swift Equatable?!
โ ์ด๋ฒ ์๊ฐ์๋ Equtable์ ๋ํด์ ์์๋ณด๋๋ก ํ ๊ฒ. Equatable์ด ๋ญ๋? ๋ ๊ฐ์ด ๋์ผํ์ง ํ์ธํ ์ ์๋ ํ๋กํ ์ฝ์ด์ผ. ์ฐ๋ฆฌ๊ฐ ์ฝ๋ฉ์ ํ๋ฉด์ "abc" == "abc" ํน์ 33 != 33 ๋ฑ ์ค์ํํธ์ ๊ธฐ๋ณธ ํ์ ์
rldd.tistory.com
๋ค์์ ViewModel์ ๋ํด์ ๋ณด์
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()
}
}
CaseIterable์ enum ํ์ ์ ์ฐธ์กฐํ ๋ ์์๋๋ก ์ฐธ์กฐํ ์ ์๋ ๋ฉ์๋์ด๋ค.
๋ค์์ todoManager์ shared์ธ๋ฐ ์ฑ๊ธํค ํจํด์ด๋ค. ํ๋์ ๋ณ์๋ฅผ ์ฌ๋ฌ๊ณณ์์ ๊ณต์ ํด์ ์ฌ์ฉํ๋ ๊ฒ!
๋ณ์ todo๋ฅผ ์ฐธ์กฐํ ๋๋ todoManager์๊ฒ ๋ฌผ์ด๋ณด๊ฒ ์ค๊ณ๋์ด ์๋ค.
์ค์ํํธ์ filter๋ ๋ค์ ์กฐ๊ฑด์ ๋ง๋ ๊ฒ๋ง ์ฐพ์์ ๋ฝ์์ค๊ฒ ๋๋ค.
๋๋จธ์ง ํจ์๋ค์ manager์๊ฒ์ ๋ฐ์์ค๊ฒ๋ ์ค๊ณ๋์ด ์๋ค.
๊ทธ๋ผ manager์ ๋ํด์๋ ์์๋ณผ๊น?
class TodoManager {
static let shared = TodoManager()
static var lastId: Int = 0
var todos: [Todo] = []
func createTodo(detail: String, isToday: Bool) -> Todo {
//TODO: create๋ก์ง ์ถ๊ฐ
let nextId = TodoManager.lastId + 1 // ์๋ก์ด ์์ด๋๊ฐ ๋๊ฒ ๋ค
TodoManager.lastId = nextId
return Todo(id: nextId, isDone: false, detail: detail, isToday: isToday)
}
func addTodo(_ todo: Todo) {
//TODO: add๋ก์ง ์ถ๊ฐ
todos.append(todo)
saveTodo()
}
func deleteTodo(_ todo: Todo) {
//TODO: delete ๋ก์ง ์ถ๊ฐ
todos = todos.filter{ existingTodo in
return existingTodo.id != todo.id
} // filter๋ ์ผ์นํ๋ ๊ฒฐ๊ณผ๋ฌผ๋ง ๋ฐํ
saveTodo()
}
func updateTodo(_ todo: Todo) {
//TODO: updatee ๋ก์ง ์ถ๊ฐ
guard let index = todos.firstIndex(of: todo) else { return } // ๋ฌธ๋ฒ firstIndex๋ of: ๋ก ์ฃผ์ด์ง ๊ฐ์ด ๊ฐ์ฅ ์ฒซ๋ฒ์งธ๋ก ๋์ค๋ ๊ฒ์ ์กฐํํ๋ค
todos[index].update(isDone: todo.isDone, detail: todo.detail, isToday: todo.isToday)
saveTodo()
}
func saveTodo() {
Storage.store(todos, to: .documents, as: "todos.json")
}
func retrieveTodo() {
todos = Storage.retrive("todos.json", from: .documents, as: [Todo].self) ?? []
let lastId = todos.last?.id ?? 0
TodoManager.lastId = lastId
}
}
shared๋ ์ฑ๊ธํค ํจํด์ผ๋ก ๋งค๋์ ์์ ์ ๊ฐ๋ฅดํค๊ณ ์๋ค.
lastId๋ ์ฐ๋ฆฌ๊ฐ json ํ์ผ์ ์ ์ฅํ ๋ ์์ด๋๋ก ๊ตฌ๋ถ์ง์ ๊ฑด๋ฐ, ๊ทธ๋ ๊ฒ ํ๊ธฐ ์ํ ๋ณ์์ด๋ค.
createTodo์ ๊ตฌ์กฐ์ฒด ํํ๋ก ๋ฆฌํดํ๋ค.
createTodo์ add์์ ์ฐจ์ด๋ ๋ค์ ์คํ ๋ฆฌ์ง๋ฅผ ๋ณด๋ฉด์ ์ค๋ช ํ์.
deleteTodo๋ filter๋ฅผ ์ด์ฉํ์ฌ ํ์ฌ ์ผ์นํ๋ ๊ฒฐ๊ณผ๋ง ๋ฐํํจ์ผ๋ก์จ ๊ทธ ๋ฐ์ดํฐ๋ฅผ ์ญ์ ํ ์ ์๋ค.
saveTodo ๋ฐ retreieveTodo๋ ์คํ ๋ฆฌ์ง์ ์ ์ฅํ๋๋ฐ, ์ด๊ฑด ์คํ ๋ฆฌ์ง์ ํจ๊ป ๋ณด๋๋ก ํ์.
โ Storage ํ์ผ์ ๋ํ ๋ถ์
๊ฐ์์ Storage๋ฅผ ์์ ์์ ๋ฐ๋ก ์์ฑํ์ง๋ ์์์ง๋ง, ๋๋ ๊ทธ์ ์ ๊ณต๋ถ๋ฅผ ํด์ ์๊ณ ์์์์ผ๋ก ๋ด ์ง์์ ๋ค์ ์ ๊ฒํด๋ณด๋ ๊ฒธ ๋ค์ ํด๋ณด๋ฉด์ ์ญ ์ฝ์ด๋ณด์. ํ์ํ๋ฉด ๊ตฌ๊ธ๋งํด์ ์จ๋ ๋ ๋ฏ..?
public class Storage {
private init() { }
// TODO: directory ์ค๋ช
// TODO: FileManager ์ค๋ช
enum Directory {
case documents
case caches
var url: URL {
let path: FileManager.SearchPathDirectory
switch self {
case .documents:
path = .documentDirectory
case .caches:
path = .cachesDirectory
}
return FileManager.default.urls(for: path, in: .userDomainMask).first!
}
}
// TODO: Codable ์ค๋ช
, JSON ํ์
์ค๋ช
// TODO: Codable encode ์ค๋ช
// TODO: Data ํ์
์ ํ์ผ ํํ๋ก ์ ์ฅ ๊ฐ๋ฅ
static func store<T: Encodable>(_ obj: T, to directory: Directory, as fileName: String) {
let url = directory.url.appendingPathComponent(fileName, isDirectory: false)
print("---> save to here: \(url)")
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
do {
let data = try encoder.encode(obj)
if FileManager.default.fileExists(atPath: url.path) {
try FileManager.default.removeItem(at: url)
}
FileManager.default.createFile(atPath: url.path, contents: data, attributes: nil)
} catch let error {
print("---> Failed to store msg: \(error.localizedDescription)")
}
}
// TODO: ํ์ผ์ Data ํ์
ํํ๋ก ์ฝ์์ ์์
// TODO: Data ํ์
์ Codable decode ๊ฐ๋ฅ
static func retrive<T: Decodable>(_ fileName: String, from directory: Directory, as type: T.Type) -> T? {
let url = directory.url.appendingPathComponent(fileName, isDirectory: false)
guard FileManager.default.fileExists(atPath: url.path) else { return nil }
guard let data = FileManager.default.contents(atPath: url.path) else { return nil }
let decoder = JSONDecoder()
do {
let model = try decoder.decode(type, from: data)
return model
} catch let error {
print("---> Failed to decode msg: \(error.localizedDescription)")
return nil
}
}
static func remove(_ fileName: String, from directory: Directory) {
let url = directory.url.appendingPathComponent(fileName, isDirectory: false)
guard FileManager.default.fileExists(atPath: url.path) else { return }
do {
try FileManager.default.removeItem(at: url)
} catch let error {
print("---> Failed to remove msg: \(error.localizedDescription)")
}
}
static func clear(_ directory: Directory) {
let url = directory.url
do {
let contents = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: [])
for content in contents {
try FileManager.default.removeItem(at: content)
}
} catch {
print("---> Failed to clear directory ms: \(error.localizedDescription)")
}
}
}
// MARK: TEST ์ฉ
extension Storage {
static func saveTodo(_ obj: Todo, fileName: String) {
let url = Directory.documents.url.appendingPathComponent(fileName, isDirectory: false)
print("---> [TEST] save to here: \(url)")
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
do {
let data = try encoder.encode(obj)
if FileManager.default.fileExists(atPath: url.path) {
try FileManager.default.removeItem(at: url)
}
FileManager.default.createFile(atPath: url.path, contents: data, attributes: nil)
} catch let error {
print("---> Failed to store msg: \(error.localizedDescription)")
}
}
static func restoreTodo(_ fileName: String) -> Todo? {
let url = Directory.documents.url.appendingPathComponent(fileName, isDirectory: false)
guard FileManager.default.fileExists(atPath: url.path) else { return nil }
guard let data = FileManager.default.contents(atPath: url.path) else { return nil }
let decoder = JSONDecoder()
do {
let model = try decoder.decode(Todo.self, from: data)
return model
} catch let error {
print("---> Failed to decode msg: \(error.localizedDescription)")
return nil
}
}
}
- ์ฐธ๊ณ
https://velog.io/@dev-lena/guard-let%EA%B3%BC-if-let%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90
guard let๊ณผ if let์ ์ฐจ์ด์
guard let๊ณผ if let์ ์ฐจ์ด์
velog.io
https://duwjdtn11.tistory.com/520
[iOS] Completion Handler
Completion Handler ๋ณธ ๋ฌธ์์๋ ํ์์ ๊ณต๋ถ๋ฅผ ์งํํ๋ฉฐ ํ๋ฒ ์ ๋ฆฌ๊ฐ ํ์ํ๋ค๊ณ ์๊ฐํ๋ Completion Handler ์ ๋ํ ๋ด์ฉ์ ๊ธฐ์ฌํ๋ค. Prerequisite Completion Handler ๊ฐ๋ ์ ์๋ฉด ์์๋ก ์ด๋ ค์ด ๊ฐ๋ ์ด๋ค....
duwjdtn11.tistory.com
https://devmjun.github.io/archive/Mutating_Struct
Swift. ๊ตฌ์กฐ์ฒด Mutating ์ ๋ฆฌ
devmjun.github.io
https://www.swiftbysundell.com/articles/enum-iterations-in-swift-42/
Enum iterations in Swift | Swift by Sundell
With each new release, Swift keeps getting better and better at creating compiler-generated implementations of common boilerplate. One such new feature in Swift 4.2 is the new CaseIterable protocol - that enables us to tell the compiler to automatically sy
www.swiftbysundell.com
'โ ๏ธ deprecated โ ๏ธ > ํจ์บ (์ฌ์ธ์)' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๐ก ch14 URLSession (feat. Codable) (0) | 2021.06.28 |
---|---|
๐ก ch14 Networking (0) | 2021.06.28 |
โจ๏ธ ch13 ํค๋ณด๋์ ๋ฐ๋ฅธ ๋ ์ด์์ ์กฐ์ ํ๊ธฐ (0) | 2021.06.26 |
๐คช ch13 ๋ฒํผ ์ํ์ ๋ฐ๋ผ ๋ฐ๊ฟ์ฃผ๊ธฐ (0) | 2021.06.26 |
๐ ch13 swift Equatable?! (0) | 2021.06.25 |