Search
⤴️

[FireBase - iOS] 데이터 쓰기

부제
카테고리
네트워킹
세부 카테고리
FireBase
Combine 카테고리
최종편집일
2022/07/16 15:17
작성중
관련된 포스팅
생성 일시
2022/07/16 15:14
태그

레퍼런스 가져오기

var ref: DatabaseReference! ref = Database.database().reference()
Swift
복사

데이터 추가 및 수정

1. 모든 하위 데이터 덮어쓰기 : .setValue()

전체 객체를 하위 노드를 포함한 모든 기존 데이터를 바꿈
self.ref.child("users").child(user.uid).setValue(["username": username])
Swift
복사
하위 항목만 덮어쓰기
self.ref.child("users/\(user.uid)/username").setValue(username)
Swift
복사
사용가능한 데이터 유형
NSString
NSNumber
NSDictionary
NSArray

2. 특정 필드 업데이트 : .updateChildValues()

키 경로를 지정하여 더 낮은 수준의 하위 항목 값을 업데이트
예 : 소셜 블로깅 앱에서의 게시물,
확장성 개선을 위해 데이터가 여러군데 저장되어 있는 경우,
데이터 팬아웃을 사용해 해당 데이터의 모든 인스턴스를 업데이트 가능
예 : 소셜 블로깅 앱에서의 게시물 → 사용자 피드, 최근 활동 피드 동시 업데이트 가능.
guard let key = ref.child("posts").childByAutoId().key else { return } let post = ["uid": userID, "author": username, "title": title, "body": body] // 동시에 게시물을 등록할 노드를 딕셔너리의 Key로 생성 Value 에는 게시물에 해당하는 딕셔너리 정보 삽입. let childUpdates = ["/posts/\(key)": post, "/user-posts/\(userID)/\(key)/": post] ref.updateChildValues(childUpdates)
Swift
복사
다만 이렇게 동시에 업데이트 하는 경우. 모든 업데이트가 성공하거나 모든 업데이트가 실패함.

콜백 클로져

데이터가 커밋된 시점을 파악 → completion handler 추가 가능 → 오류의 원인 파악 또한 가능
ref.child("users").child(user.uid).setValue(["username": username]) { (error:Error?, ref:DatabaseReference) in if let error = error { print("Data could not be saved: \(error).") } else { print("Data saved successfully!") } }
Swift
복사

데이터 삭제

1. 데이터 삭제 메서드 사용: removeValue()

삭제하고자 하는 데이터 위치의 참조에 해당 메서드 호출.

2. nil 값을 업데이트

동시에 여러 하위 항목 삭제 가능
let childUpdates = ["/posts/\(key)": nil, "/user-posts/\(userID)/\(key)/": nil] ref.updateChildValues(childUpdates)
Swift
복사

동시 수정으로 인해 손상될 수 있는 데이터

트랜잭션 작업 사용

ref.runTransactionBlock({ (currentData: MutableData) -> TransactionResult in if var post = currentData.value as? [String: AnyObject], let uid = Auth.auth().currentUser?.uid { var stars: [String: Bool] stars = post["stars"] as? [String: Bool] ?? [:] var starCount = post["starCount"] as? Int ?? 0 if let _ = stars[uid] { // Unstar the post and remove self from stars starCount -= 1 stars.removeValue(forKey: uid) } else { // Star the post and add self to stars starCount += 1 stars[uid] = true } post["starCount"] = starCount as AnyObject? post["stars"] = stars as AnyObject? // Set value and report transaction success currentData.value = post return TransactionResult.success(withValue: currentData) } return TransactionResult.success(withValue: currentData) }) { error, committed, snapshot in if let error = error { print(error.localizedDescription) } }
Swift
복사
트랜잭션을 사용하면 여러 사용자가 같은 게시물에 동시에 별표를 주거나 클라이언트 데이터의 동기화가 어긋나도 별표가 잘못 집계되지 않습니다. FIRMutableData  클래스에 포함되는 값은 애초에 해당 경로에 대해 클라이언트에 마지막으로 알려진 값이거나 값이 없는 경우 nil 입니다. 서버는 초기 값과 현재 값을 비교하여 값이 일치하면 트랜잭션을 수락하고, 그렇지 않으면 거부합니다. 트랜잭션이 거부되면 서버에서 현재 값을 클라이언트에 반환하며, 클라이언트는 업데이트된 값으로 트랜잭션을 다시 실행합니다. 트랜잭션이 수락되거나 시도가 일정 횟수를 초과할 때까지 이 과정이 반복됩니다.

서버 측 동기화 처리

충돌하는 업데이트가 있어도 사용자에게 피드백 X → 잘못된 정보 표시가능
단 서버 측에서는 충돌이 발생하지 않음.
let updates = [ "posts/\(postID)/stars/\(userID)": true, "posts/\(postID)/starCount": ServerValue.increment(1), "user-posts/\(postID)/stars/\(userID)": true, "user-posts/\(postID)/starCount": ServerValue.increment(1) ] as [String : Any] Database.database().reference().updateChildValues(updates);
Swift
복사
이 코드는 트랜잭션 작업을 사용하지 않으므로 충돌하는 업데이트가 있는 경우 코드가 자동으로 다시 실행되지 않습니다. 하지만 증분 작업은 데이터베이스 서버에서 직접 이루어지므로 충돌이 발생할 가능성은 없습니다. 사용자가 이전에 이미 별표표시한 게시물에 별표를 표시하는 상황처럼 애플리케이션별 충돌을 감지하고 거부하려면 해당 사용 사례에 대한 커스텀 보안 규칙을 작성해야 합니다.

오프라인에서 제대로 동작하기

클라이언트의 네트워크 연결이 끊겨도 앱은 계속 정상적으로 작동합니다.
Firebase 데이터베이스에 연결된 모든 클라이언트는 자체적으로 활성 데이터의 내부 버전을 유지합니다. 데이터를 쓰면 우선 로컬 버전에 기록됩니다. 그런 다음 Firebase 클라이언트가 해당 데이터를 원격 데이터베이스 서버 및 다른 클라이언트와 '최선을 다해' 동기화합니다.
이와 같이 데이터베이스에 대한 모든 쓰기 작업은 로컬 이벤트를 즉시 트리거하며, 그 이후에 서버에 데이터가 기록됩니다. 따라서 앱은 네트워크 지연 또는 연결 여부에 관계없이 응답성을 유지합니다.
네트워크에 다시 연결되면 앱에서 적절한 이벤트 세트를 수신하여 클라이언트와 현재 서버 상태를 동기화하므로 커스텀 코드를 별도로 작성할 필요가 없습니다.
오프라인 동작에 대한 자세한 내용은 온라인 및 오프라인 기능에 대해 자세히 알아보기에서 살펴보겠습니다.