Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Firebase

WooSeok Suh edited this page Aug 2, 2021 · 12 revisions

2021-07-22

by Seok

Step.1

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 ""
    }
}

2021-07-23

by Seok

Step.2

Storage 구조 수정

  • Storage 데이터 불러오는 속도 개선을 위한 구조 수정
  • 앱 서비스 안정성(네트워크 불안정 등으로 부터)을 위한 구조 수정
  • DB I/O 최소화를 위한 구조 수정
  • 하기 구조를 바탕으로 기존 코드 수정 필요
스크린샷 2021-07-23 오후 12 47 58

2021-07-28

by Seok

Step.3

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)
    }
}

2021-08-02

by Seok

Step.4

메서드 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()
    }
}

Clone this wiki locally

Morty Proxy This is a proxified and sanitized view of the page, visit original site.