포스트

단어장 프로젝트 (10)

예외처리

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

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

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

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

Cloud to Coredata

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    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형식으로 만들어둬서, 그부분을 수정한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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 함수 구현

이건 심플하다

1
2
3
4
5
6
7
8
@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주 길줄알았는데, 생각보다 짧다.

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

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.