(작성중) 기기 데이터 다루기 (CoreData, UserDefault)

이진욱(JIN WOOK)·2024년 12월 5일
0

작성중

목록 보기
1/5

CoreData

프로젝트 생성 - coredata 선택

Entity 생성 - > 저장될 데이터들의 집합.

Entity이름 PhoneBook

Attribute(속성)에는 name과 phoneNumber를 추가한다.

→ 전화번호부라는 데이터 집합내에 내부 속성값으로 이름과 휴대폰 번호가 들어간다.

  • 오른쪽 인스펙터 영역 중 Codegen 개념
    • Code Generator 의 줄임말. Entity 를 어떤 형식의 코드로 생성할 것인지 선택하는 속성.
    • Manual/None = Entity 의 서브 클래스를 자동으로 생성하지 않고 개발자가 클래스 작성.
    • Class Definition = Entity 의 서브 클래스를 자동으로 생성.
    • Category/Extension = Entity 클래스와 함께 extension 을 위한 파일까지 생성.
    • 여기서는 Manual/None 으로 생성해보겠습니다.

Create NSManagedObject Subclass 를 생성한다.

그러면 두개의 파일이 생김

  • PhoneBook+CoreDataClass.swift

이 클래스는 Entity와의 상호작용을 관리한다. (속성값의 저장 및 검색을 처리함)

  • PhoneBook+CoreDataProperties.swift

import Foundation
import CoreData

extension PhoneBook {
		//해당 메서드는 swfit에서만 동작하는 메서드임을 명시해놓은것
    @nonobjc public class func fetchRequest() -> NSFetchRequest<PhoneBook> {
    //fetchRequest PhoneBook에 대한 여러가지 데이터 검색을 돕는다.
        return NSFetchRequest<PhoneBook>(entityName: "PhoneBook")
    }
		//CoreData에 의해 관리되는 객체를 말한다.
    @NSManaged public var name: NSObject?
    @NSManaged public var phoneNumber: NSObject?

}

extension PhoneBook : Identifiable { //PhoneBook 타입이 고유하게 식별될수 있음을 의미한다.

}

NSPersistentContainer 를 생성한다. (영구 컨테이너)

해당 객체는 CoreData에서 데이터를 저장하고 관리하는데 필요한 핵심 객체이다.

이 객체는 CoreData를 사용한다고 체크해놨으니 AppDelegate.swift에 기본적으로 NSPersistentContainer 를 셋팅하는 코드가 존재한다

 MARK: - Core Data stack //이부분

하단부 - 문맥을 저장한다.

    // MARK: - Core Data Saving support

    func saveContext () {
        let context = persistentContainer.viewContext
        if context.hasChanges { //데이터가 변경됐다면
            do {
                try context.save()
            } catch { //에러처리
                // Replace this implementation with code to handle the error appropriately.
                // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }

CRUD가 이루어졌으면 해당 메서드를 호출한다.

CoreData에 접근하는 방법

ViewController에서 데이터에 접근한다. 바로 NSPersistentConatiner에

C (Create)

    func createData(name: String, phoneNumber: String){
        guard let entity = NSEntityDescription.entity(forEntityName: "PhoneBook", in: self.container.viewContext) else {return} //entity값 설정
        let newPhoneBook = NSManagedObject(entity: entity, insertInto: self.container.viewContext) //실제 사용할 객체 ,NSManagedObject는 데이터를 다룰수있음
        newPhoneBook.setValue(name, forKey: "name")
        newPhoneBook.setValue(phoneNumber, forKey: "phoneNumber")
        
        do {
            try self.container.viewContext.save() //변경사항의 저장
            print("문맥 저장 성공")
        }catch{
            print("문맥 저장 실패")
        }
    }

흐름은 이렇다.

enitity 값을 명확하게 지정해준다. (Core Data의 데이터 구조를 명확히 정의)

그리고 실제 사용할 객체에 해당 entity를 전달하여 연결 시켜준다.

R(Read)

func readAllData() {
        do {
            let phoneBooks = try self.container.viewContext.fetch(PhoneBook.fetchRequest())
            //PhoneBook의 데이터를 가져와 저장
            for phoneBook in phoneBooks as [NSManagedObject] {
                if let name = phoneBook.value(forKey: "name") as? String,//Any타입에서 String으로 타입캐스팅
                   let phoneNumber = phoneBook.value(forKey: "phoneNumber") as? String {
                    print("이름: \(name), 전화번호: \(phoneNumber)")
                }
            }
        }catch{
            print("데이터 읽기 실패")
        }

초기화 작업

//persistentContainer를 현재 뷰컨트롤러에서 사용할수 있게 하는 작업
        let appDelgate = UIApplication.shared.delegate as! AppDelegate
        self.container = appDelgate.persistentContainer
        

주의할점 : 델리게이트 패던과는 무관하다.

UIApplication.shared.delegate는 앱의 공용 앱 델리게이트 객체를 참조한다.

PhoneBook클래스 리펙토링


import Foundation
import CoreData

@objc(PhoneBook)
public class PhoneBook: NSManagedObject {
    public static let className = "PhoneBook"
    public enum Key {
        static let name = "name"
        static let phoneNumber = "phoneNumber"
    }
}

클래스 이름과 Key를 전역 변수로 설정 함으로서 클래스 타입에 바로 접근이 가능해진다.

이렇게 되면 만약 속성명을 변경할때에 하나하나씩 변경할 필요없이
PhoneBook클래스 내부에서 딱 한번만 변경해주면 된다.

U(UPDATE)

 //MARK: - U: UPDATE
    func updateData(currentName: String, updateName: String) {
        let fetchRequest = PhoneBook.fetchRequest() //데이터를 가져오고
        fetchRequest.predicate = NSPredicate(format: "name == %@", currentName) //조건의 설정
        //Predicate는 데이터를 필터링 하기위한 조건을 정의한다. format문자열을 기반으로 필터조건을 설정한다.
        //name속성의 값이 currentName변수와 동일해야한다.
        //즉, 정리하면 입력된 이름이 위 조건에 맞는 경우에 데이터를 변경하겠다.
        
        do {
            let result = try self.container.viewContext.fetch(fetchRequest) //데이터를 가져온다.
            
            //결과 처리
            for data in result as [NSManagedObject] {
                //데이터 수정
                data.setValue(updateName, forKey: PhoneBook.Key.name)
                //변경사항저장
                try self.container.viewContext.save()
                print("데이터 수정 완료")
            }
            
        }catch{
            print("데이터 수정 실패")
        }
        
    }

기존에 존재하는 데이터와 일치한다면 데이터를 변경시킨다.

ex) 이진욱 → 삼진육 으로 교체를 원한다.

이름 : 이진욱 이 존재하면 삼진육으로 교체

fetchRequest.predicate = NSPredicate(format: "name == %@", currentName)

데이터를 가져오되, name == “교체를원하는데이터” 인경우에만 가져온다.

D(Delete)

func deleteData(name: String) {
        
        let fetchRequest = PhoneBook.fetchRequest()
        fetchRequest.predicate = NSPredicate(format:"name == %@", name)
        
        do{
            let result = try self.container.viewContext.fetch(fetchRequest)
            
            for data in result as [NSManagedObject] {
                self.container.viewContext.delete(data)
                print("삭제된 데이터: \(data)")
            }
            
            //변경사항 저장
            try self.container.viewContext.save()
            print("데이터 삭제완료")
        }catch{
            print("데이터 삭제 실패: \(error)")
        }
    }

UserDefault

UserDefault를 사용할때는 따로 프로젝트 생성시 CoreData를 체크하지 않아도 된다.

UserDefault또한 디스크에 데이터를 저장할수 있게 돕는 도구이다.

CoreData보다 사용성이 훨씬 간단하다.

key와 value값을 이용해서 값을 저장한다.

대량데이터 → CoreData (??왤까)

비교적 작은데이터 → UserDefault

코어 데이터를 제대로 활용하려면 비동기 프로그래밍에 대해서 알아야 한다.

UserDefault 사용해서 CRUD 구현하기

Create

 //MARK: - Create
        UserDefaults.standard.set("010-0000-0000", forKey: "phoneNumber")

Userdefault의 경우 여러공간에 저장가능하지만 가장 일반적인 공간에 저장한다의 의미인 standard를 사용하는것이다.

Read

//여러 타입이 존재하지만 우리는 value를 string으로 선언했으니
//string타입으로 읽으라는것이다.
let phoneNumber = UserDefaults.standard.string(forKey: "phoneNumber")
        print("저장된 전화번호: \(phoneNumber!)")
        
 //해당 값은 언래핑을 하지 않으면 Optonal("")이 출력된다.
 //값이 존재하지 않을 가능성이 있기 때문에

Update

//MARK: - Update
        UserDefaults.standard.set("010-5555-0055", forKey: "phoneNumber")
        let newPhoneNumber = UserDefaults.standard.string(forKey: "phoneNumber")
        print("바뀐전화번호: \(newPhoneNumber!)")

Delete

  //MARK: - Delete
        UserDefaults.standard.removeObject(forKey: "phoneNumber")
        print("전화번호가 남아있나요: \(UserDefaults.standard.string(forKey: "phoneNumber"))")
    }

Create와 Update에서 사용했던 set메서드는 원시타입 (정수,문자,실수..)만 가능하다.

만약 클래스나 구조체의 경우 Json을 사용해 인코딩하여 넣을수 있다.

profile
기술로부터 소외 되는 사람이 없도록 우리 모두를 위한 서비스를 만들고 싶습니다.

0개의 댓글

관련 채용 정보