Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#10] 버스 검색 기능 구현 #16

Merged
merged 3 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions TMT/TMT.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
D86739712CA933CE00FFE8ED /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D86739702CA933CE00FFE8ED /* Assets.xcassets */; };
D86739742CA933CE00FFE8ED /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = D86739732CA933CE00FFE8ED /* Preview Assets.xcassets */; };
D8D377E62CBE549F0043D103 /* BusStopData.csv in Resources */ = {isa = PBXBuildFile; fileRef = D8D377E52CBE549F0043D103 /* BusStopData.csv */; };
D8D377E92CBE95C30043D103 /* BusSearchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D377E82CBE95C30043D103 /* BusSearchModel.swift */; };
D8D377EB2CBE95CA0043D103 /* BusSearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D377EA2CBE95CA0043D103 /* BusSearchViewModel.swift */; };
D8D377ED2CBE95D10043D103 /* BusSearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8D377EC2CBE95D10043D103 /* BusSearchView.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand All @@ -21,6 +24,9 @@
D86739702CA933CE00FFE8ED /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
D86739732CA933CE00FFE8ED /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
D8D377E52CBE549F0043D103 /* BusStopData.csv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = BusStopData.csv; sourceTree = "<group>"; };
D8D377E82CBE95C30043D103 /* BusSearchModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BusSearchModel.swift; sourceTree = "<group>"; };
D8D377EA2CBE95CA0043D103 /* BusSearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BusSearchViewModel.swift; sourceTree = "<group>"; };
D8D377EC2CBE95D10043D103 /* BusSearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BusSearchView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -55,6 +61,7 @@
children = (
D867396C2CA933CD00FFE8ED /* TMTApp.swift */,
D867396E2CA933CD00FFE8ED /* ContentView.swift */,
D8D377E72CBE95B80043D103 /* BusSearch */,
D8D377E42CBE548F0043D103 /* Resource */,
D86739702CA933CE00FFE8ED /* Assets.xcassets */,
D86739722CA933CE00FFE8ED /* Preview Content */,
Expand All @@ -78,6 +85,16 @@
path = Resource;
sourceTree = "<group>";
};
D8D377E72CBE95B80043D103 /* BusSearch */ = {
isa = PBXGroup;
children = (
D8D377E82CBE95C30043D103 /* BusSearchModel.swift */,
D8D377EA2CBE95CA0043D103 /* BusSearchViewModel.swift */,
D8D377EC2CBE95D10043D103 /* BusSearchView.swift */,
);
path = BusSearch;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -149,8 +166,11 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D8D377E92CBE95C30043D103 /* BusSearchModel.swift in Sources */,
D867396F2CA933CD00FFE8ED /* ContentView.swift in Sources */,
D867396D2CA933CD00FFE8ED /* TMTApp.swift in Sources */,
D8D377EB2CBE95CA0043D103 /* BusSearchViewModel.swift in Sources */,
D8D377ED2CBE95D10043D103 /* BusSearchView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
16 changes: 16 additions & 0 deletions TMT/TMT/BusSearch/BusSearchModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// BusSearchModel.swift
// TMT
//
// Created by 김유빈 on 10/15/24.
//

struct BusStopInfo: Codable {
var busNumber: String? // 노선명 (버스번호)
var busType: Int? // 버스 타입
var stopOrder: Int? // 순번
var stopName: String? // 정류소명 (한글)
var romanizedStopName: String? // 정류소명 (로마자 표기)
var xCoordinate: String? // x좌표
var yCoordinate: String? // y좌표
}
18 changes: 18 additions & 0 deletions TMT/TMT/BusSearch/BusSearchView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// BusSearchView.swift
// TMT
//
// Created by 김유빈 on 10/15/24.
//

import SwiftUI

struct BusSearchView: View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}

#Preview {
BusSearchView()
}
75 changes: 75 additions & 0 deletions TMT/TMT/BusSearch/BusSearchViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// BusSearchViewModel.swift
// TMT
//
// Created by 김유빈 on 10/15/24.
//

import Foundation

final class BusStopSearchViewModel: ObservableObject {
@Published var busStops: [BusStopInfo] = []
@Published var filteredBusStops: [BusStopInfo] = []
@Published var busNumbers = [Int]()

init() {
loadCSV()
}

private func loadCSV() {
Task {
guard let filepath = Bundle.main.path(forResource: "BusStopData", ofType: "csv") else {
print("Error \(#function) in \(#file) :: CSV file not found")
return
}
do {
let content = try String(contentsOfFile: filepath)

let response = content.components(separatedBy: "\n")

let searchResponse = response.map { $0.components(separatedBy: ",")}

await self.apply(searchResponse)

} catch {
print("Error \(#function) in \(#file)")
}
}
}

@MainActor
private func apply(_ searchResponse: [[String]]) {
for response in searchResponse {
self.busStops.append(BusStopInfo(busNumber: response[0].isEmpty ? nil : response[0],
busType: response[1].isEmpty ? nil : Int(response[1]),
stopOrder: response[2].isEmpty ? nil : Int(response[2]),
stopName: response[3].isEmpty ? nil : response[3],
romanizedStopName: response[4].isEmpty ? nil : response[4],
xCoordinate: response[5].isEmpty ? nil : response[5],
yCoordinate: response[6].isEmpty ? nil : String(response[6].dropLast(1))))
}
}

func searchBusStops(by number: String) {
filteredBusStops = busStops.filter { busStop in
if let busNumber = busStop.busNumber {
return busNumber.hasPrefix(number)
}
return false
}

fetchBusNumbersList()
}

/// 검색 결과 배열에는 노선 전체 데이터가 담겨 있어 동일한 버스 번호를 가진 데이터가 많습니다.
/// 사용자에게 보여줄 때는 중복된 데이터를 제외하고, 버스 번호만 추출해서 보여줘야 합니다.
/// 사용자가 검색한 버스 번호를 추출하여 결과로 보여주기 위해 버스 번호만 추출하기 위해 만든 함수입니다.
private func fetchBusNumbersList() {
busNumbers = Array(Set(filteredBusStops.compactMap { busStop in
if let busNumberString = busStop.busNumber, let busNumber = Int(busNumberString) {
return busNumber
}
return nil
}))
}
}