brunch

You can make anything
by writing

C.S.Lewis

by Tilltue Oct 03. 2016

Firebase #iOS,데이터베이스 이벤트

realtime database, ios, query

* 이글은 Swift 3.0 , Firebase 3.6.0 를 기준으로 작성하였습니다.

Google firebase realtime database 에 대한 설명

Firebase 에서는 실시간 데이터 베이스를 지원한다, firebase iOS 시작하기 에서 맛보기로 진행했던 부분인데, 무료로 지원하는 부분은 다음과 같다. 실시간 접속은 100 건이므로, 무료 계정으로 할수 있는 부분은 한계가 있으니, 개발하고자 하는 앱에서 필요로 하는 부분을 한번 체크해보자. 

( 사족: 개인앱이라면 충분해 보인다 )

firebase 요금 정책

자 이제 firebase Database 를 활용하는 방법에 대해서 알아보자.

https://brunch.co.kr/@tilltue/7 를 통해 기본적인 write 방법에 대해서는 설명했었는데,

이 포스트 에서는 실시간 데이터 베이스이벤트들의 종류에 대해 알아보고 어떤식으로 동작하는지 살펴보겠다.



샘플 JSON은 https://api.hearthstonejson.com/v1/14406/enUS/cards.json 를 사용하겠다.

하스스톤이라는 게임의 카드 json 이다. Cards.json 으로 저장해두자.


firebase 에서는 데이터 베이스 위치(Path)를 통해 여러 데이터 테이블을 구성해 둘 수 있다.

이런식으로 Cards 를 만들고  JSON을 가져오기를 통해 파일로 저장해둔 Cards.json 파일을 불러오자.

이렇게 Cards 의 JSON이 등록된 것을 확인 할 수 있다.

테스트를 위해 인증이 없어도 데이터를 읽을 수 있도록 규칙을 재설정하자.

XCode 로 돌아오자. 프로젝트내 기본 firebase 의 설정은 끝난 상태로 본다.

모든카드의 값을 가져와 보자.


import FirebaseDatabase


class ViewController: UIViewController {

    override func viewDidLoad() {

        super.viewDidLoad()

        // Do any additional setup after loading the view, typically from a nib.

        let ref = FIRDatabase.database().reference()

        _ = ref.child("Cards").observe(.value, with: { snapshot in

            print(snapshot.value)

        })

    }

}

결과값으로 firebase 저장된 cards.json 의 값을 정상적으로 읽어온 것을 확인 할 수 있다.

이제 이 포스트의 목적인 firebase 에서 데이터를 읽어오는 방법들에 대해서 확인해보자.

ref.child("Cards") 를 통해 이벤트를 받을 데이터 모델을 지정할 수 있다.

observe 함수를 통해 firebase 에서 지원하는 이벤트들을 수신 할 수 있다.

지원하는 이벤트는 다음과 같다.


public enum FIRDataEventType : Int {

    case childAdded // 0, fired when a new child node is added to a location

    case childRemoved // 1, fired when a child node is removed from a location

    case childChanged // 2, fired when a child node at a location changes

    case childMoved // 3, fired when a child node moves relative to the other child nodes at a location

    case value // 4, fired when any data changes at a location and, recursively, any children

}

주석을 보면 이해할 수 있듯, Cards 에 새로운 자식 node 가 추가, 삭제,변경,이동에 대한 이벤트를 받을 수 있고, 모든 이벤트를 받을 수 있는 value 가 있다.

Remove 이벤트를 테스트 해보자.


예제

        _ = ref.child("Cards").observe(.childRemoved, with: { snapshot in

            print("remove!!")

            print(snapshot.value)

        })

firebase console 에서 데이터를 하나 지워보자.


결과

remove!!

Optional({

    cost = 2;

    id = "DS1h_292";

    name = "Steady Shot";

    playRequirements =     {

        "REQ_MINION_OR_ENEMY_HERO" = 0;

        "REQ_STEADY_SHOT" = 0;

    };

    playerClass = HUNTER;

    rarity = FREE;

    set = CORE;

    text = "<b>Hero Power</b>\nDeal $2 damage to the enemy hero.";

    type = "HERO_POWER";

})


데이터를 지우면 childRemoved 이벤트가 발생해서 지워진 카드가 전달 된 것을 확인 할 수있다.


자 이제 카드 목록 테이블을 구성하고, 이벤트를 받으면 테이블이 갱신되도록 구성해보자.


class Card: Equatable {

    var name = ""

    var code = ""

    var ref: FIRDatabaseReference!

    init(snapshot: FIRDataSnapshot){

        let snapshotValue = snapshot.value as! NSDictionary

        code = snapshotValue["id"] as? String ?? ""

        name = snapshotValue["name"] as? String ?? ""

        ref = snapshot.ref

    }

}

func ==(lhs: Card, rhs: Card) -> Bool {

    return lhs.code == rhs.code

}


class ViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!

    var cards = [Card]()

    override func viewDidLoad() {

        super.viewDidLoad()

        // Do any additional setup after loading the view, typically from a nib.

        let ref = FIRDatabase.database().reference()

        let query = ref.child("Cards")

        query.observeSingleEvent(of: .value, with: { [weak self] snapshot in

            guard let wself = self else { return }

            print(snapshot.value)

            for childSnapshot in snapshot.children {

                if let childSnapshot = childSnapshot as? FIRDataSnapshot {

                    let card = Card(snapshot: childSnapshot)

                    wself.cards.append(card)

                }

            }

            wself.tableView.reloadData()

        })

        _ = ref.child("Cards").observe(.childRemoved, with: { [weak self] snapshot in

            print("remove!!")

            print(snapshot.value)

            guard let wself = self else { return }

            let card = Card(snapshot: snapshot)

            if let removeIndex = wself.cards.index(of: card) {

                wself.tableView.beginUpdates()

                wself.cards.remove(at: removeIndex)

                wself.tableView.deleteRows(at: [IndexPath(row: removeIndex, section: 0)], with: .left)

                wself.tableView.endUpdates()

            }

        })

    }


    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

        // Dispose of any resources that can be recreated.

    }


}


extension ViewController: UITableViewDataSource {

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return cards.count

    }

    func numberOfSections(in tableView: UITableView) -> Int {

        return 1

    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

        var cell = tableView.dequeueReusableCell(withIdentifier:"CELL")

        if cell == nil {

            cell = UITableViewCell(style: UITableViewCellStyle.value1, reuseIdentifier: "CELL")

        }

        let card = cards[indexPath.row]

        cell?.textLabel?.text = card.name

        return cell!

    }

}

예제 코드 설명

Card  : 카드 데이터 snapshot에서 모델을 만든다.

observeSingleEvent 는 전체 데이터를 한번만 로드하기 위해 single event 를 사용했다.

데이터를 로드해서 테이블을 구성하고 delete 이벤트가 발생하면 코드값을 통해 아이템을 찾아 지운다.

카드 데이터를 로드하면 아래와 같다.

첫번째 row 의 카드 데이터를 지워보자.

테이블에서 첫번째 카드가 삭제된 것을 확인 할 수있다.


여기까지 firebase 의 실시간 데이터 베이스의 이벤트를 처리하는 기본적인 방법에 대해 알아보았다.

firebase 에서 전달해주는 이벤트에 대한 처리만 하면 되기 때문에, 활용방법이 매우 광범위하고, 쉽게 설계가 가능하다.


브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari