이번 프로젝트때 CoreData를 사용하기로 했는데, Entity를 3개 사용하게 되었다.
기존 강의나 개인플젝때는 단일 Entity를 사용해서 하나의 Entity에 대한 CRUD 함수만 만들면 됐는데, 이게 여러개면 각각 만들어야 하나...? 하는 고민을 했다.
그러다 강의에서 일반화된 네트워크 함수를 만들어서 보여주신 적이 있는데, 이번 CRUD 메서드도 그렇게 하면 좋지 않을까 싶어서 만들어 봤다.
일반적으로 어떻게 하는지는 사실 잘 모르지만...
단일엔티티를 사용할 때는 인자로 Attribute들을 받거나 Read 시에 아무것도 안 받아도 됐지만, 이번엔 어떤 Attribute들이 들어갈지 모르기 때문에 entityType를 제네릭으로 받고, 추가나 변경할 값들을 딕셔너리로 받는다.
/// 일반화된 CREATE 메서드, T는 NSManagedObject를 상속받는 타입
/// - Parameters:
/// - entityType: 어떤 엔티티를 불러올건지 결정, 예를 들어 RideData.self
/// - values: Attribute:Value 형식으로 값 생성, 예를 들어 ["id":UUID(), "date":Date(), "distance":3.2, ... ] 처럼 딕셔너리 형식으로 입력
func create<T:NSManagedObject>(entityType: T.Type, values: [String:Any]) {
guard let entity = NSEntityDescription.entity(forEntityName: String(describing: entityType), in: self.context) else { return }
let newObject = T(entity: entity, insertInto: self.context)
values.forEach {newObject.setValue($1, forKey: $0)}
saveContext()
}
guard let entity = NSEntityDescription.entity(forEntityName: String(describing: entityType), in: self.context) else { return } : 해당 entityType으로 지정된 Entity를 불러옴
let newObject = T(entity: entity, insertInto: self.context) : T(entity:, insertInfo:) 사용하여 newObject 초기화 메서드 생성 및 엔터티에 삽입
values.forEach {newObject.setValue($1, forKey: $0)} : "속성":값 순서로 newObject에 값을 set
/// 일반화된 READ 메서드
/// - Parameters:
/// - entityType: 어떤 엔티티를 불러올건지 결정, 예를 들어 RideData.self
/// - configure: 읽어온 entityType를 처리하는 클로저
func read<T:NSManagedObject>(entityType: T.Type, configure: (T) -> Void) {
let fetchRequest = NSFetchRequest<T>(entityName: String(describing: entityType))
do {
let results = try self.context.fetch(fetchRequest)
for result in results {
configure(result)
}
} catch {
print("데이터 읽기 실패")
}
}
let fetchRequest = NSFetchRequest<T>(entityName: String(describing: entityType)) : 해당 entityType으로 지정된 엔티티에 해당하는 NSFetchRequest 객체 생성
let results = try self.context.fetch(fetchRequest) : fetch 요청 실행 결과를 result로 저장 후 configure 클로저 호출하여 처리
/// 일반화된 UPDATE 메서드
/// - Parameters:
/// - entityType: 어떤 엔티티의 값을 변경할건지 결정
/// - predicate: 업데이트할 NSObject 찾기 위한 조건
/// - values: ["Attribute":변경할 값] 형식으로 작성
func update<T: NSManagedObject>(entityType: T.Type, predicate: NSPredicate, values: [String:Any]) {
let fetchRequest = NSFetchRequest<T>(entityName: String(describing: entityType))
fetchRequest.predicate = predicate
do {
let results = try self.context.fetch(fetchRequest)
for result in results {
values.forEach {result.setValue($1, forKey: $0)}
}
saveContext()
print("문맥 업데이트 성공")
} catch {
print("문맥 업데이트 실패")
}
}
fetchRequest.predicate = predicate : NSPredicate로 검색조건 설정
let results = try self.context.fetch(fetchRequest) : fetch 실행 성공시 result에 저장
values.forEach {result.setValue($1, forKey: $0)} : result에 따라 값 변경
/// 일반화된 DELETE 메서드
/// - Parameters:
/// - entityType: 어떤 엔티티의 값을 삭제할건지 결정
/// - predicate: 삭제할 NSObject 찾기 위한 조건 설정
func delete<T:NSManagedObject>(entityType: T.Type, predicate: NSPredicate) {
let fetchRequest = NSFetchRequest<T>(entityName: String(describing: entityType))
// NSPredicate로 검색조건 설정
fetchRequest.predicate = predicate
do {
// fetch 실행 성공시 result에 저장
let results = try self.context.fetch(fetchRequest)
for result in results {
// result에 대응하는 값 삭제
self.context.delete(result)
}
print("문맥 삭제 성공")
} catch {
// try문 실패시 오류메세지 출력
print("문맥 삭제 실패")
}
}
fetchRequest.predicate = predicate : NSPredicate로 검색조건 설정
let results = try self.context.fetch(fetchRequest) : fetch 실행 성공시 result에 저장
self.context.delete(result) : result에 대응하는 값 삭제
팀원들이 잘 알아봤으면 좋겠어서 문서 주석도 만들어 봤다
혹시 수정사항이 생기면 다시 와서 수정할 예정