웹에서 입력한 정보가 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)
}
와 같이 접근할 수 있게 했다.
앱 흐름:
- 행정구역을 선택
- 해당 행정구역에 있는 산책로 정보들 조회
*웹에서 정보를 저장할 때, 산책로가 속한 행정구역을 배열로 따로 저장한다.
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)
}
}
}
만났던 문제들:
- 디코딩을 시도할 때, RoadInfoModel을 scope에서 찾을 수 없다는 에러가 떴다.
빌드 버튼을 눌렀을 때 원인을 알 수 있었는데, RoadInfoModel이 Codable을 준수하고 있지 않았었고, 이 때문에 에러가 떴었다.
(어설프게 스택오버플로 따라하다 Codable에 init()을 사용했다.)
- 디코딩을 할 때, 형변환이 까다로웠었다.
let data = document.data()
에서 data는[String:Any]기 때문에, data를 JSONSerialization (Data 타입으로 형변환)해준 뒤, 디코딩을 진행해줘야 한다.
(초반에는let jsonData = try JSONSerialization.data(withJSONObject:data)
문장 없이 바로 data를 JSONDecoder()에 넣었는데, Data 타입으로 만들어줘야 디코딩이 가능하다.