Firestore에서 데이터 가져오기

DangDu Lee·2021년 1월 19일
0

웹에서 입력한 정보가 Firestore에 들어가고, iOS 앱에서 그 데이터를 꺼내는 비즈니스 로직이다. 웹 개발을 마치고, 스위프트로 Decoding할 모델 만들고, 정보 불러오는 함수를 만들어봤다.

익숙한 JSONDecoder()로 만들기 때문에, 모델에 Decodable을 채택하여 짰다.

struct RoadInfoModel : Decodable {
    var roadName: String
    var roadEnv: String
    var entireLength: String
    var addressName: String
    var aroundSubway: [AroundSubway]
    var voiceInduction: Int
    var sideWalkBlock: Int
    var brailleNotice:Int
    var safetyFence: Double
    var pavement:String
    var stairFeature:String
    var warning: String
    var toilet:String
    var benchAndRest:String
    var walkingPeople:String
    var feature:String
    var aroundEnvList: [AroundEnvList]

    enum CodingKeys:String, CodingKey {
        case roadName = "road_name"
        case roadEnv = "road_env"
        case entireLength = "entire_length"
        case addressName = "address_name"
        case aroundSubway = "around_subway"
        case voiceInduction = "voice_induction"
        case sideWalkBlock = "side_walk_block"
        case brailleNotice = "braille_notice"
        case safetyFence = "safety_fence"
        case pavement = "pavement"
        case stairFeature = "stair_feature"
        case warning = "warning"
        case toilet = "toilet"
        case benchAndRest = "bench_and_rest"
        case walkingPeople = "walking_people"
        case feature = "feature"
        case aroundEnvList = "around_env_list"
    }
}

먼저, 이 모델로 디코딩이 가능한지 테스트하기 위해 모든 문서를 불러와서, 디코딩을 시도했다. 코드는 아래와 같다.
(Firebase 공식 가이드를 많이 참고했다.)

class GetDataFromFirestore {
    static let share = GetDataFromFirestore()
    let db = Firestore.firestore()
    
    
    func gettingAllDocs(completionHandler: @escaping ([RoadInfoModel]) -> Void) {
        db.collection("WalkRoad").getDocuments() { (querySnapshot, err) in
            var roadInfos:[RoadInfoModel] = []
            
            if let err = err {
                print("Error getting documents: \(err)")
            } else {
                guard let documents = querySnapshot?.documents else {return}
                let decoder =  JSONDecoder()
                
                for document in documents {
                    do {
                        let data = document.data()
                        let jsonData = try JSONSerialization.data(withJSONObject:data)
                        let roadInfo = try decoder.decode(RoadInfoModel.self, from: jsonData)
                        roadInfos.append(roadInfo)
                    } catch let err {
                        print("err: \(err)")
                    }
                }
                completionHandler(roadInfos)
            }
        }
    }
}

completionHandler 함수를 만들어, 외부에서도

GetDataFromFirebaseStore.share.gettingAllDocs { roadInfos in
	print(roadInfos)
}

와 같이 접근할 수 있게 했다.


앱 흐름:

  1. 행정구역을 선택
  2. 해당 행정구역에 있는 산책로 정보들 조회
    *웹에서 정보를 저장할 때, 산책로가 속한 행정구역을 배열로 따로 저장한다.

2번 과정을 수행하기 위해 배열 안에 행정구역이 있는지 쿼링을 해줘야 하는데, firestore에서 제공하는 whereField(_:arrayContains)로 가능하다.
Firestore에서 제공하는 다양한 쿼리들


이 쿼리를 이용해 함수를 작성했고, 코드는 아래와 같다.

func gettingSpecificLocationDocs(location: String, completionHandler: @escaping ([RoadInfoModel]) -> Void) {
	db.collection("WalkRoad").whereField("address_area", arrayContains: "\(location)").getDocuments() { (querySnapshot, err) in
            var roadInfos:[RoadInfoModel] = []
            
            if let err = err {
                print("Error getting documents: \(err)")
            } else {
            	guard let documents = querySnapshot?.documents else {return}
                let decoder =  JSONDecoder()

                for document in documents {
                    do {
                        let data = document.data()
                        let jsonData = try JSONSerialization.data(withJSONObject:data)
                        let roadInfo = try decoder.decode(RoadInfoModel.self, from: jsonData)
                        roadInfos.append(roadInfo)
                        print(roadInfo)
                    } catch let err {
                        print("err: \(err)")
                    }
                }
                completionHandler(roadInfos)
            }
        }
    }

만났던 문제들:

  1. 디코딩을 시도할 때, RoadInfoModel을 scope에서 찾을 수 없다는 에러가 떴다.
    빌드 버튼을 눌렀을 때 원인을 알 수 있었는데, RoadInfoModel이 Codable을 준수하고 있지 않았었고, 이 때문에 에러가 떴었다.
    (어설프게 스택오버플로 따라하다 Codable에 init()을 사용했다.)

  2. 디코딩을 할 때, 형변환이 까다로웠었다.
    let data = document.data()에서 data는[String:Any]기 때문에, data를 JSONSerialization (Data 타입으로 형변환)해준 뒤, 디코딩을 진행해줘야 한다.
    (초반에는 let jsonData = try JSONSerialization.data(withJSONObject:data) 문장 없이 바로 data를 JSONDecoder()에 넣었는데, Data 타입으로 만들어줘야 디코딩이 가능하다.

0개의 댓글