3 분 소요

예외처리

게임을 설정하고도 시작이 안되는 상황이 발생하여 코드를 다듬어야한다.

코어데이터에 값이 하나도 없는 상태에서 값이 들어오고 설정하기를 하면 데이터가 넘어가지 않는 상황이 발생하는걸로 보인다.

이전에 메모리를 고려하여 notificationCenter의 observer를 다 지웠는데 거기서 문제가 생긴듯하다.

지우고나니 잘되는걸 확인.

Cloud to Coredata

func syncDataFromCloudKit() {
        syncEntityFromCloudKit(recordType: "BookCase", entityType: BookCase.self)
        syncEntityFromCloudKit(recordType: "WordEntity", entityType: WordEntity.self)
    }

func syncEntityFromCloudKit<T: NSManagedObject>(recordType: String, entityType: T.Type) {
        let database = CKContainer(identifier: "iCloud.com.teamproject.Vocabularytest").publicCloudDatabase
        let query = CKQuery(recordType: recordType, predicate: NSPredicate(value: true))
        
        database.perform(query, inZoneWith: nil) { records, error in
            if let error = error {
                print("Error fetching records from CloudKit: \(error)")
                return
            }
            
            guard let records = records else { return }
            
            let context = self.managedContext!
            
            context.perform {
                for record in records {
                    self.updateOrInsertRecord(record, entityType: entityType, context: context)
                }
                
                do {
                    try context.save()
                    print("\(recordType) records synced to Core Data successfully")
                } catch {
                    print("Error saving context: \(error)")
                }
            }
        }
    }

func updateOrInsertRecord<T: NSManagedObject>(_ record: CKRecord, entityType: T.Type, context: NSManagedObjectContext) {
        let fetchRequest = T.fetchRequest()
        fetchRequest.predicate = NSPredicate(format: "uuid == %@", record.recordID.recordName)
        
        do {
            let results = try context.fetch(fetchRequest)
            
            if let existingObject = results.first as? T {
                populateManagedObject(existingObject, withRecord: record)
            } else {
                let newObject = T(context: context)
                populateManagedObject(newObject, withRecord: record)
            }
        } catch {
            print("Error fetching object: \(error)")
        }
    }

func populateManagedObject(_ object: NSManagedObject, withRecord record: CKRecord) {
        if let bookCase = object as? BookCase {
            bookCase.uuid = record.recordID.recordName
            bookCase.name = record["name"] as? String
            bookCase.explain = record["explain"] as? String
            bookCase.meaning = record["meaning"] as? String
            bookCase.image = record["image"] as? Data
            bookCase.word = record["word"] as? String
        } else if let wordEntity = object as? WordEntity {
            wordEntity.uuid = record.recordID.recordName
            wordEntity.antonym = record["antonym"] as? String
            wordEntity.bookCaseName = record["bookCaseName"] as? String
            wordEntity.date = record["date"] as? Date
            wordEntity.definition = record["definition"] as? String
            wordEntity.detail = record["detail"] as? String
            wordEntity.memory = (record["memory"] as? Bool)!
            wordEntity.pronunciation = record["pronunciation"] as? String
            wordEntity.synonym = record["synonym"] as? String
            wordEntity.word = record["word"] as? String
        }
    }

그래도 얼추 매커니즘은 알듯.

May-23-2024 07-16-24

주말에 느긋하게 분석해보는걸로.

로그인 상태가 아닐때는 로딩이나, 저장이 안되게 해야하므로


    func checkiCloudLoginStatus(completion: @escaping (Bool) -> Void) {
        let container = CKContainer.default()
        container.accountStatus { status, error in
            if let error = error {
                print("Error checking iCloud account status: \(error)")
                completion(false)
                return
            }

            if status == .available {
                print("iCloud account is available and logged in.")
                completion(true)
            } else {
                print("No iCloud account is logged in.")
                completion(false)
            }
        }
    }

CoreDataManager.shared.checkiCloudLoginStatus { loginStatus in
                if loginStatus {
                    ProgressHUD.animate("데이터를 저장하는 중 입니다.")
                    DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                        CoreDataManager.shared.syncData()
                        ProgressHUD.succeed("데이터 저장에 성공했습니다.")
                    }
                } else {
                    ProgressHUD.failed("로그인 상태를 확인해 주세요.")
                }
            }

이렇게 처리를 한다.

Toolbar 추가

정확하게는 키보드 위에 버튼을 추가하여 약간의 interface를 제공한다.

textfield에 대해 추가를 하는데, textfield는 팀원분이 factory형식으로 만들어둬서, 그부분을 수정한다.

func makeTextField(placeholder: String, action: Selector, dictAction: Selector) -> UITextField {
        
        let textField = UITextField()
        
        textField.placeholder = placeholder
        textField.autocorrectionType = .no
        textField.spellCheckingType = .no
        textField.autocapitalizationType = .none
        textField.clearButtonMode = .always
        textField.clearsOnBeginEditing = false
        textField.returnKeyType = .done
        
        //테두리 색상 추가
        textField.layer.borderColor = ThemeColor.mainCgColor
        textField.layer.borderWidth = 2
        textField.layer.cornerRadius = 8
        
        // 텍스트 필드 높이 설정
        let heightConstraint = NSLayoutConstraint(item: textField, attribute: .height, relatedBy: .equal, toItem: nil, attribute: .notAnAttribute, multiplier: 1, constant: 46)
        textField.addConstraint(heightConstraint)
        
        // Placeholder 왼쪽에 여백 추가
        let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 20, height: 0))
        textField.leftView = paddingView
        textField.leftViewMode = .always
        
        // Toolbar 추가
        let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: UIView().frame.size.width, height: 36))
        toolBar.barStyle = .default
        toolBar.sizeToFit()
        
        let doneButton = UIBarButtonItem(
            title: "Done",
            style: .plain,
            target: self,
            action: action) // 매개변수를 직접 사용
        
        
        let dictButton = UIBarButtonItem(
            image: UIImage(systemName: "character.book.closed"),
            style: .plain,
            target: self,
            action: dictAction)
 
        toolBar.items = [
            UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil),
            dictButton,doneButton
        ]
        
        toolBar.isUserInteractionEnabled = true
        textField.inputAccessoryView = toolBar
        
        return textField
    }

Toolbar가 내가 추가한 부분

Selector함수가 필요해서 파라미터로 받게 한다.

버튼이 2개이므로 두개를 구현해준다.

simulator_screenshot_30AB5A8B-2981-447D-A1BB-EBAD533382FF

이렇게하면 두개의 버튼이 생성

해당 기능을 사용할 objc 함수 구현

이건 심플하다

@objc func doneButtonTapped() {
        self.view.endEditing(true)
    }
    
@objc func showDict() {
        let url = URL(string: "https://dict.naver.com/")
        UIApplication.shared.open(url!)
    }

May-23-2024 23-20-01

이렇게 프로젝트가 끝.

2주 길줄알았는데, 생각보다 짧다.

그만큼 재미있게 했다는 뜻.