[iOS][Swift] iOS 숙련 강의 - 2

팔랑이·2024년 7월 10일
0

iOS/Swift

목록 보기
44/71
post-thumbnail

iOS 숙련강의 목차

1) ViewController 생명주기
2) 메모리 관리 이해
3) Core Data 와 UserDefaults
4) 네트워크 통신 이해
5) 날씨 앱 만들기

1, 2에 관련한 내용은 여기
4는 여기


CRUD

CRUD 개념자체는 익숙하니 후딱 넘어가기...
데이터 CRUD는 네이티브(앱)에서도 일어날 수 있고 서버에서도 일어날 수 있다.

CoreData

: 앱에서 기기의 디스크에 데이터를 읽고 쓸 수 있게 해주는 프레임워크

CoreData 프로젝트 설정

1. 프로젝트 생성시 Storage -> Core Data 선택하여 생성

2. 프로젝트 안에서 Entity 생성


좌측 하단 Add Entity 눌러 빈 Entity 생성, 엔티티 이름 변경

3. attribute (속성/열) 추가

위 사진 우측 하단의 Add Attribute 눌러 입력하고자 하는 속성, Type 추가

4. Entity의 오른쪽 인스펙터 영역 중 Codegen 선택

: Code Generator의 줄임말로, 엔티티를 어떤 형식의 코드로 생성할 것인지 선택하는 속성

  • Manual/None : 엔티티의 서브클래스 자동으로 생성하지 않고 개발자가 클래스 작성
  • Class Definition : 엔티티 서브클래스 자동생성
  • Category/Extension : 엔티티 클래스와 함께 extension 위한 파일까지 생성

우선 Manual/None 으로 적용

5. Code Generate

4번까지 세팅해두고, Editor -> Create NSManagedObject Subclass를 누르면

원래 없었던 두 가지 파일이 생성된다.

📞 PhoneBook+CoreDataClass.swift

import Foundation
import CoreData

@objc(PhoneBook)
public class PhoneBook: NSManagedObject {

}

: NSManagedObject는 Core Data 프레임워크에서 관리되는 객체를 나타내는 기본 클래스로,Core Data 엔티티와의 상호작용을 관리하고 속성 값의 저장 및 검색을 처리한다.

📞 PhoneBook+CoreDataProperties.swift

import Foundation
import CoreData


extension PhoneBook {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<PhoneBook> {
        return NSFetchRequest<PhoneBook>(entityName: "PhoneBook")
    }

    @NSManaged public var name: String?
    @NSManaged public var phoneNumber: String?

}

extension PhoneBook : Identifiable {

}
  • @nonobjc = Objective-C 에서는 동작하지 않고 Swift 에서만 동작하는 메서드임을 명시.
  • fetchRequest() = PhoneBook 에 대한 여러가지 데이터 검색을 도움.
  • @NSManaged = CoreData 에 의해 관리되는 객체를 의미.
  • Identifiable = PhoneBook 타입이 고유하게 식별될 수 있음을 의미.

6. NSPersistantContainer 관리

: CoreData에서 데이터 저장하고 관리하는데 필요한 핵심 객체
프로젝트 생성시 CoreData 사용에 체크했기 때문에, Appdelegate.swift에 이미 NSPersistantContainer를 세팅하는 코드가 존재한다.

그 밑에 saveContext() 메서드도 자동 생성되어 있는데, 데이터의 업데이트가 일어날 때 이를 호출해서 문맥을 저장해줘야 한다.

그럼 이제 직접 써보며 코드 사용법을 알아보자.

CoreData 코드

코드를 짜기 전에 PhoneBook+CoreDataClass에 들어가서 다음과 같이 클래스를 세팅한다.

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"
    }
}
  • static 프로퍼티: 그 타입에 대고 호출할 수 있는 프로퍼티
  • 이렇게 클래스로 생성해두면...
    1. 자동완성 기능 사용 가능
    2. 손으로 phoneNumber 라고 타자를 치지 않기 때문에 휴먼 에러가 줄어들음
    3. 값을 수정해야 할 때 여기서만 고치면 됨
    4. 성격이 같은 프로퍼티끼리 모아 관리 가능

Create

//shCoreData에 데이터 Create
func createData(name: String, phoneNumber: String) {
    guard let entity = NSEntityDescription.entity(forEntityName: PhoneBook.className, in: self.container.viewContext) else {
        return
    }
    let newPhoneBook = NSManagedObject(entity: entity, insertInto: self.container.viewContext)
    newPhoneBook.setValue(name, forKey: PhoneBook.Key.name)
    newPhoneBook.setValue(phoneNumber, forKey: PhoneBook.Key.phoneNumber)
    
    do {
        try self.container.viewContext.save()
        print("문맥 저장 성공")
    } catch {
        print("문맥 저장 실패")
    }
}

Read

//shCoreData에서 데이터 Read
func readAllData() {
    do {
        let phoneBooks = try self.container.viewContext.fetch(PhoneBook.fetchRequest())
        
        for phoneBook in phoneBooks as [NSManagedObject] {
            if let name = phoneBook.value(forKey: PhoneBook.Key.name) as? String,
               let phoneNumber = phoneBook.value(forKey: PhoneBook.Key.phoneNumber) as? String {
                    print("이름: \(name), 전화번호: \(phoneNumber)")
                }
            }
    } catch {
        print("데이터 읽기 실패")
    }
}

Update

//shCoreData에서 데이터 Update
func updateData(currentName: String, updateName: String) {
    let fetchRequest = PhoneBook.fetchRequest()
    fetchRequest.predicate = NSPredicate(format: "name == %@", currentName)
    do {
        let result = try self.container.viewContext.fetch(fetchRequest)
        
        for data in result as [NSManagedObject] {
            //data 중 name의 값을 updateName으로 update
            data.setValue(updateName, forKey: PhoneBook.Key.name)
        }
        try self.container.viewContext.save()
        print("데이터 수정 성공")
    } catch {
        print("데이터 수정 실패")
    }
}

Delete

//shCoreData에서 데이터 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)
        }
        try self.container.viewContext.save()
    } catch {
        print("데이터 삭제 실패")
    }
}

ViewController.swift

import UIKit
import CoreData

class ViewController: UIViewController {
    
    var container: NSPersistentContainer!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        self.container = appDelegate.persistentContainer
        
        createData(name: "SH", phoneNumber: "010-0000-1111")
        updateData(currentName: "SH", updateName: "승희")
        deleteData(name: "승희")
        readAllData()
    }    
    //shCoreData에서 데이터 Create
    //shCoreData에서 데이터 Read
    //shCoreData에서 데이터 Update
    //shCoreData에서 데이터 delete
}

❗️ 위에서 CoreData는디스크에 데이터를 저장한다고 했으므로, 코드를 삭제하고 다시 실행해도 데이터가 남아있다.

UserDefault

: CoreData와 마찬가지로 디스크에 데이터를 저장할 수 있게 돕는 도구

  • CoreData보다 사용성 간단
  • key, value 이용해 값을 저장
  • 대량의 데이터는 CoreData로, 비교적 단순한 데이터는 UserDefault로 다루는 것이 적절

UserDefault 코드

CoreData같이 엔티티를 만들고 복잡하게 할 필요 없이 간단하다.

import UIKit

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //Create
        UserDefaults.standard.set("010-0000-0000", forKey: "phoneNumber")
        
        //Read
        var phoneNumber = UserDefaults.standard.string(forKey: "phoneNumber")
        print("저장된 전화번호: \(phoneNumber)") // optional로 출력됨 (key 일치값 없을수 있으니)
        
        //Update
        UserDefaults.standard.set("010-3333-4444", forKey: "phoneNumber")
        phoneNumber = UserDefaults.standard.string(forKey: "phoneNumber")
        print("저장된 전화번호: \(phoneNumber)")
        
        //Delete
        UserDefaults.standard.removeObject(forKey: "phoneNumber")
        phoneNumber = UserDefaults.standard.string(forKey: "phoneNumber")
        print("전화번호가 남아있는지 확인 \(phoneNumber)")
    }
}
  • UserDefaults.standard.set() 메서드로 Create, Update
  • UserDefaults.standard.string(forKey: "") 메서드를 통해 Read, 뒤의 자료형에 따라 string, bool, integer 변경
  • UserDefaults.standard.removeObject(forKey:"") 메서드를 통해 Delete

결과

key에 대응하는 값이 있는지 없는지 모르기 때문에 Optional로 출력, 코드에도 주의표시 생성

주의표시 없애려면 옵셔널 바인딩 하여 사용하면 된다!

profile
정체되지 않는 성장

0개의 댓글