-
Notifications
You must be signed in to change notification settings - Fork 0
Firebase
WooSeok Suh edited this page Aug 2, 2021
·
12 revisions
by Seok
Firebase관리 객체 생성
- 첫 로그인 유저(!snapshot.exists())의 경우 default values 넣어주는 메서드 추가
- 화면에 보여줄 정보 firebase에서 가져오는 메서드 추가
import Foundation
import RxSwift
import Firebase
final class DatabaseManager: DatabaseManagerType {
private var ref: DatabaseReference
let data = [Unit]()
init(_ ref: DatabaseReference) {
self.ref = ref
}
func initializeDatabase(_ uuid: String) {
ref.child("users").child(uuid).getData { [unowned self] error, snapshot in
guard error == nil else { return }
if !snapshot.exists() {
let jsonString = FirebaseDataManager.transformToString(uuid)
self.ref.child("users").child(uuid).setValue(["units": jsonString])
}
}
}
@discardableResult
func getFirebaseData(_ uuid: String) -> Observable<[Unit]> {
Observable.create { [unowned self] observer in
self.ref.child("users").child(uuid).getData { error, snapshot in
if let error = error {
observer.onError(error)
}
if let data = snapshot.value as? [String: Any] {
observer.onNext(FirebaseDataManager.transformToStruct(data))
}
}
return Disposables.create()
}
}
}Firebase와 통신하기 위한 Data 형식을 바꿔주는 객체 생성
- Firebase에 객체 저장을 위해 특정형태로(NSString, NSDictionary ...) 변경필요
- Firebase에서 가져온 데이터를 필요한 객체(Unit)으로 변경필요
import Foundation
import Firebase
struct Units: Decodable {
let units: String
}
final class FirebaseDataManager {
static func transformToStruct(_ data: [String: Any]) -> [Unit] {
do {
let jsonData = try JSONSerialization.data(withJSONObject: data, options: [])
let units = try JSONDecoder().decode(Units.self, from: jsonData)
let unit = try JSONDecoder().decode([Unit].self, from: Data(units.units.utf8))
return unit
} catch let error {
print(error)
}
return []
}
static func transformToString(_ uuid: String) -> String? {
let data = Unit.initialValues()
do {
let encodedData = try JSONEncoder().encode(data)
let storedData = String(data: encodedData, encoding: .utf8)
return storedData
} catch let error {
print(error)
}
return ""
}
}by Seok
Storage 구조 수정
- Storage 데이터 불러오는 속도 개선을 위한 구조 수정
- 앱 서비스 안정성(네트워크 불안정 등으로 부터)을 위한 구조 수정
- DB I/O 최소화를 위한 구조 수정
- 하기 구조를 바탕으로 기존 코드 수정 필요
by Seok
background진입 / app 종료 시에 firebase에 데이터 저장
- 네트워크와 잦은 접촉을 막기위해 coredata에 먼저 저장
- 무료서버이기 때문에 잦은 접촉은 네트워크 error유발 가능성이 있음, 네트워크 불안정시에도 data 보존을 위해 coredata에 먼저 저장
- 백그라운드 진입 또는 앱 종료시에 해당 데이터를 firebase에 저장
- app을 실행할 때는 firebase로 부터 storedData fetch
import Foundation
import RxSwift
import Firebase
final class DatabaseManager: DatabaseManagerType {
private var ref: DatabaseReference
private let uid = Auth.auth().currentUser?.uid ?? ""
let data = [Unit]()
init(_ ref: DatabaseReference) {
self.ref = ref
}
func updateDatabase(_ info: NetworkDTO) {
let unitData = DataFormatManager.transformToString(info.units)
let moneyData = DataFormatManager.transformToString(info.money)
let scoreData = DataFormatManager.transformToString(info.score)
let adsData = DataFormatManager.transformToString(info.ads)
ref.child("users").child(uid).setValue(["info": ["units": unitData, "money": moneyData, "score": scoreData, "ads": adsData]])
}
@discardableResult
func getFirebaseData() -> Observable<NetworkDTO> {
Observable.create { [unowned self] observer in
self.ref.child("users").child(uid).getData { error, snapshot in
if let error = error {
observer.onError(error)
}
if let data = snapshot.value as? [String: Any] {
observer.onNext(DataFormatManager.transformToLocalData(data))
observer.onCompleted()
}
}
return Disposables.create()
}
}
}DataFormat을 맞추기 위해 DataFormatManager 객체 메서드 추가 및 DTO 생성
import Foundation
struct UnitInformation: Decodable {
let info: [String: String]
enum Codingkeys: String, CodingKey {
case info
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Codingkeys.self)
let decodedInfo = try container.decode([String: String].self, forKey: .info)
info = decodedInfo
}
}
struct AdsInformation: Codable {
let ads: [Bool]
let lastUpdated: Date
let gift: Int?
static func empty() -> AdsInformation {
return AdsInformation(ads: [], lastUpdated: Date(), gift: nil)
}
}
struct NetworkDTO: Codable {
let units: [Unit]
let money: Int
let score: Int
let ads: AdsInformation
static func empty() -> NetworkDTO {
return NetworkDTO(units: [], money: 0, score: 0, ads: AdsInformation.empty())
}
}import Foundation
import Firebase
final class DataFormatManager {
static func transformToLocalData(_ data: [String: Any]) -> NetworkDTO {
do {
let jsonData = try JSONSerialization.data(withJSONObject: data, options: [])
let info = try JSONDecoder().decode(UnitInformation.self, from: jsonData)
let units = try JSONDecoder().decode([Unit].self, from: Data(info.info["units"]!.utf8))
let money = try JSONDecoder().decode(Int.self, from: Data(info.info["money"]!.utf8))
let score = try JSONDecoder().decode(Int.self, from: Data(info.info["score"]!.utf8))
let ads = try JSONDecoder().decode(AdsInformation.self, from: Data(info.info["ads"]!.utf8))
let result = NetworkDTO(units: units, money: money, score: score, ads: ads)
return result
} catch let error {
print(error)
}
return NetworkDTO.empty()
}
static func transformToString<T: Encodable>(_ data: T) -> String? {
do {
let encodedData = try JSONEncoder().encode(data)
let storedData = String(data: encodedData, encoding: .utf8)
return storedData
} catch let error {
print(error)
}
return ""
}
static func transformToUnit(_ info: ItemInformation) -> Unit {
let uuid = Int(info.uuid)
let image = info.image ?? ""
let level = Int(info.level)
return Unit(uuid: uuid, image: image, level: level)
}
static func transformToMoney(_ info: MoneyInformation) -> Int {
return Int(info.myMoney)
}
}by Seok
메서드 refactoring
- 기존 UserDefaults가 확인했던 hasLaunchedOnce 기능을 firebase로 이전
- 이전하면서 initValues 메서드 firebase에서 기능 구현 필요
- 아래와 같이 로그인정보 확인하여 어떤 data를 return할지 변경하는 코드로 로직 수정
@discardableResult
func getFirebaseData() -> Observable<NetworkDTO> {
Observable.create { [unowned self] observer in
self.ref.child("users").child(uid).getData { error, snapshot in
if let error = error {
observer.onError(error)
}
if !snapshot.exists() {
let initData = NetworkDTO(units: Unit.initialValues(), money: 0, score: 0, ads: AdsInformation.empty())
observer.onNext(initData)
observer.onCompleted()
}
if let data = snapshot.value as? [String: Any] {
observer.onNext(DataFormatManager.transformToLocalData(data))
observer.onCompleted()
}
}
return Disposables.create()
}
}created by 우송