설 연휴가 끝나고 다시 시작된 ios 공부에 대한 기록.
5일이나 되는 긴 연휴였지만, 진행중인 프로젝트가 있기에 이 시간을 살려 작업을 진행하고싶었다. 때문에, 거의 쉬지못했지만, 완성도 있는 결과물을 만들어내기 위해 속도를 올릴 수 있는 시간이었다. 현재 진행중인 캠프를 제외하고도 본인이 가지고 있을 수 있는 경험이 있어야 한다고 생각하여 남는 시간에 꾸준히 진행하고 있다. 조금씩 만들어지고 있는 모습을 보고있으니, 조급한 마음에 퀄리티가 부실한 결과물보다 지금처럼 조금씩 만들어나가는 것에 중심을 두는 것이 좋을 것 같다고 생각한다.
연휴가 시작 되기 전까지 레벨6(MVC -> MVVM)을 구현했었다. 오늘은 레벨7을 완성하고자 했으며, MVVM 적용에 부족함을 느껴 제법 시간이 걸렸지만, 완성된 결과와 겪은 문제점들에 대해 얘기해보고자 한다.
일단, 레벨7의 주요 구현 사항은 TableView의 각 Cell에 좋아요 기능을 담당하는 버튼을 생성하여 클릭할 경우 CoreData에 해당 Cell의 code를 저장하는 것이다.(토글 기능으로 구현하여 클릭시 code를 저장, 이후 다시 클릭 시 삭제하도록 구현)
때문에, CoreData에 데이터를 저장하고 읽을 수 있는 메서드를 생성한다.
toggleFavorite는 CoreData에 데이터를 저장하는 메서드이다.
기본적인 구상안으로는 fetchRequest, predicate를 사용하여 code를 저장하고 있는 데이터를 찾는다. 만약 데이터가 있을 경우(즐겨찾기 된 상태), 해당 데이터를 삭제해주면 되고 데이터가 없을 경우 code를 추가해주면 된다.
고려해야 하는 사항으로는 swift에서 fetchRequest를 사용하여 받아온 데이터는 배열로 넘어오기에 response의 first를 이용하여 데이터에 접근해야 한다. 이후 save를 사용하여 DB 파일에 반영한다.
// 검색
let fetchRequest = Favorite.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "code == %@", code)
// 실제 동작
do {
let favorites = try self.container.viewContext.fetch(fetchRequest)
if let favorite = favorites.first {
container.viewContext.delete(favorite)
} else { //NSManagedObject 사용하지않고 Favorite 클래스 바로 사용
let newFavorite = Favorite(context: self.container.viewContext)
newFavorite.code = code
}
try self.container.viewContext.save()
}
ViewModel에서 CoreData에 저장된 리스트를 사용하기 위한 메서드이다. 그냥 CoreData에 저장되어있는 리스트를 반환한다. 허나, 데이터가 없을 경우 빈 배열
func fetchFavorites() -> [Favorite] {
let fetchRequest = Favorite.fetchRequest()
do {
let favorites = try self.container.viewContext.fetch(fetchRequest)
return favorites
} catch {
print("Favorite fetch 실패")
return []
}
}
이제 FavoriteManager를 통해 CoreData에 데이터를 저장/삭제하고 리스트를 받아오는 로직은 구현이 되어있다. 그렇다면 해당 메서드를 어떻게 사용할 것인가? ViewController에서는 직접적으로 Model에 접근을 하지 못하게 할 것이다. 때문에, 해당 데이터들을 받아와서 가공하고 적용하는 로직은 ViewModel에서 담당한다.
데이터를 받아와서 저장해둘 favoriteList를 생성한다. 타입은 [Favorite]이다.
좋아요 버튼을 눌려있는 상태
즉, favoriteList에 있는가, 없는가를 기준으로 정렬하는 메서드이다. favoriteList를 받아와서 사용하며 해당 리스트의 각각의 code를 map을 이용하여 codes에 담아준다. 이후, viewData의 데이터를 통해 codes에 해당 viewData의 비교 대상 데이터가 포함되어있는지, 포함되어 있다면 이름 순으로 정렬하고 둘 중 하나만 포함이 되어있다면 포함된 데이터가 우선순위로 정렬되도록 로직을 구현한다.
func sortByFavorite() {
self.favoriteList = favoriteManager?.fetchFavorites() ?? []
let codes = favoriteList.map { $0.code }
self.viewData.sort { a, b in
let isAFavorite = codes.contains(a.code)
let isBFavorite = codes.contains(b.code)
if isAFavorite != isBFavorite {
return isAFavorite
}
return a.code < b.code
}
}
ViewController에서 버튼을 클릭했을 때, FavoriteManager에 직접적으로 넘겨줄 수 없기에 거쳐가기 위한 메서드이다. 버튼이 클릭되면 viewModel에서 FavoriteManager로 해당 code를 전달하고 sortByFavorite을 호출하여 다시한번 정렬한다.
func toggleFavorite(code: String) {
favoriteManager?.toggleFavorite(code: code)
sortByFavorite()
}
ViewController에서 해당 code가 favoriteList에 저장되어있는지 확인하기 위한 메서드이다.
func isFavorite(code: String) -> Bool {
return favoriteList.contains { $0.code == code}
}
FavoriteManager
- 버튼을 눌렀을 때, 해당하는 code를 저장하는 함수
- toggleFavorite(code: String)
- ViewModel에서 저장된 리스트를 불러오기 위한 함수
- fetchFavorites()
ExchangeRateViewModel
- 좋아요 목록 받아둘 변수 생성
- favoriteList: [Favorite] = []
- 좋아요 기준으로 정렬하는 메서드
- sortByFavorite
- Manager의 toggleFavorite에 연결되기 위한 메서드
- toggleFavorite(code: String)
- 현재 아이템의 code가 좋아요 목록에 포함되어있는지 확인하는 메서드
- isFavorite(code: String)
로직 흐름(버튼 클릭 시)
1. ViewController에서 버튼을 클릭할 경우 viewModel.toggleFavorite 호출
2. ViewModel의 favoriteManager.toggleFavorite 호출하여 DB 수정
3. 수정된 DB를 다시 불러와서 favoriteList 최신화, sortByFavorite 호출하여 viewData 순서 변경
4. viewData가 변경되면 updateData 호출
5. View에서 다시 그릴 때 viewModel.isFavorite 호출하여 결정하여 그림
로직 흐름(앱 실행)
1. ViewController의 viewDidLoad에서 viewModel.getCurrencyData 호출
2. viewModel에서 fetchData 성공 시 allData에 저장 이후 sortByFavorite 호출
3. favoriteList를 받아와서 정렬하여 viewData 순서 변경
4,5. 동일