트러블 슈팅 Core Data - donggeonoh/bithumb-techcamp-ios-1st GitHub Wiki
1. CoreData의 loadPersistentStores를 호출할 때 Error handling에 대한 문제 - DonggeonOh, z3rosmith, 허황
// AppDelegate.swift
lazy var persistentContainer: NSPersistentContainer = {
var container: NSPersistentContainer = NSPersistentContainer(name: "BithumbYagomAcamedy")
container.loadPersistentStores { storeDescription, error in
if let nsError = error as NSError? {
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
return container
}()
// CoreDataManager.swift
private(set) lazy var context: NSManagedObjectContext = {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
fatalError("Not down cast AppDelegate")
}
return appDelegate.persistentContainer.viewContext
}()
App delegate에서 Persistent container를 호출할 때, completion으로 error에 대한 처리를 할 수 있도록 하고 있습니다.
만약 Core Data의 migration을 실패할 경우엔 Alert을 띄워 사용자에게 알려주도록 Error를 처리를 하는 것이 맞다고 생각하지만 현재 migration 기능까지 사용하지 않고 있습니다.
따라서 나머지 경우에서 Error가 나는 상황의 경우 개발자 수준에서 처리해야 할 error라고 생각 되어 fatalError로 처리하였습니다.
2. Core Data가 Thread safe하지 않아 생기는 문제 - 동건
// CoinChartCoreDataManager.swift
private func insertContextCandlestickChart(
to candlestick: Candlestick,
dateFormat: ChartDateFormat,
description: NSEntityDescription,
context: NSManagedObjectContext
) {
context.performAndWait {
let chart = CandlestickChart(entity: description, insertInto: context)
chart.time = candlestick.time
chart.highPrice = candlestick.highPrice
chart.lowPrice = candlestick.lowPrice
chart.openPrice = candlestick.openPrice
chart.closePrice = candlestick.closePrice
chart.symbol = symbol
chart.timeInterval = dateFormat.description
}
}
// CoreDataManager.swift
func saveContext() {
context.performAndWait {
if context.hasChanges {
do {
try context.save()
} catch {
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
}
}
NSManagedObject를 만들기 위해서나 save 하기 위해서 Persistent container의 viewContext에 접근할 때, 비동기로 처리하면서 발생하는 쓰레드 관련 문제들이 생겼습니다. (EXC_BAD_ACCESS와 같은 오류)
그래서, context가 사용되는 곳(conext를 save 할 때와 NSManagedObject를 만들 때)에서 동기로 처리하기 위해 performAndWait을 호출하여 처리 하였습니다.
3. Core data에서 저장된 코인 차트 데이터를 가져와 적용하면 차트가 엉망으로 표시되는 문제 - 동건
// CandlestickChart+CoreDataProperties.swift
@nonobjc class func fetchRequest(
symbol: String,
dateFormat: ChartDateFormat
) -> NSFetchRequest<CandlestickChart> {
let request = fetchRequest()
let symbolPredicate = NSPredicate(format: "symbol == %@", symbol)
let dateFormatPredicate = NSPredicate(format: "timeInterval == %@", dateFormat.description)
let sortDescriptor = NSSortDescriptor(keyPath: \CandlestickChart.time, ascending: true)
request.predicate = NSCompoundPredicate(
andPredicateWithSubpredicates: [symbolPredicate, dateFormatPredicate]
)
request.sortDescriptors = [sortDescriptor]
return request
}
처음에는 Candlestick API의 데이터 배열을 저장될 때 순서대로 저장되는 줄 알았습니다.
하지만, 데이터를 다음에 다시 불러올 때 순서대로 데이터를 저장하지 않는다는 사실을 알게되었습니다.
이 방법은 쉽게 해결할 수 있었는데, FetchRequest에서 SortDescriptor를 시간 순으로 설정한 후 데이터를 가져올 때 정렬된 데이터를 가져오도록 하였습니다.
4. Core data candlestick의 가장 최신 data를 갱신해주기 위한 문제 - 동건
// CandlestickChart+CoreDataProperties.swift
@nonobjc class func fetchRequest(
symbol: String,
dateFormat: ChartDateFormat,
time: Double
) -> NSFetchRequest<CandlestickChart> {
let request = fetchRequest()
let symbolPredicate = NSPredicate(format: "symbol == %@", symbol)
let dateFormatPredicate = NSPredicate(format: "timeInterval == %@", dateFormat.description)
let timePredicate = NSPredicate(format: "time == %lf", time)
request.predicate = NSCompoundPredicate(
andPredicateWithSubpredicates: [symbolPredicate, dateFormatPredicate, timePredicate]
)
return request
}
Bithumb WebSocket ticker API를 호출하여 최신 차트 데이터를 갱신시켜주고 있었습니다.
Core data에서도 최신 데이터를 반영해주기 위해선 최신 차트 데이터를 알아야하지만, 배열과 같은 인덱스로는 접근 할 수 없었습니다.
그래서 NSPredicate를 이용하여 Epoch timestamp를 기준으로 하여 같은 경우의 Object를 가져와 갱신시킨 후 저장 해주었습니다.