예외처리
게임을 설정하고도 시작이 안되는 상황이 발생하여 코드를 다듬어야한다.
코어데이터에 값이 하나도 없는 상태에서 값이 들어오고 설정하기를 하면 데이터가 넘어가지 않는 상황이 발생하는걸로 보인다.
이전에 메모리를 고려하여 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
}
}
그래도 얼추 매커니즘은 알듯.
주말에 느긋하게 분석해보는걸로.
로그인 상태가 아닐때는 로딩이나, 저장이 안되게 해야하므로
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개이므로 두개를 구현해준다.
이렇게하면 두개의 버튼이 생성
해당 기능을 사용할 objc 함수 구현
이건 심플하다
@objc func doneButtonTapped() {
self.view.endEditing(true)
}
@objc func showDict() {
let url = URL(string: "https://dict.naver.com/")
UIApplication.shared.open(url!)
}
이렇게 프로젝트가 끝.
2주 길줄알았는데, 생각보다 짧다.
그만큼 재미있게 했다는 뜻.
PREVIOUS단어장 프로젝트 (9)
NEXT단어장 프로젝트 (11)