728x90
화면에 데이터를 보여주기 위해서 HTTP 요청을 위한 코드를 작성합니다.
<통신 코드>
import Moya
import SwiftUI
enum API {
case queryDatabase(_ campus: Campus)
}
extension API: TargetType {
var baseURL: URL {
let url = "<https://api.notion.com/v1>"
guard let baseURL = URL(string: url) else { fatalError() }
return baseURL
}
var path: String {
switch self {
case .queryDatabase(let campus):
var databaseID: String {
switch campus {
case .부산:
"///"
case .밀양:
"///"
case .양산:
"///"
}
}
return "/databases/\\(databaseID)/query"
}
}
var method: Moya.Method {
switch self {
case .queryDatabase:
return .post
}
}
var task: Moya.Task {
switch self {
case .queryDatabase:
return .requestPlain
}
}
var headers: [String: String]? {
switch self {
default:
return ["Content-Type": "application/json", "Notion-Version": "2022-02-22", "Authorization": "Bearer ///"]
}
}
}
Moya를 사용했고, 캠퍼스 별 데이터베이스를 다르게 해서 데이터를 요청합니다.
데이터베이스와 토큰 값은 임시 주석처리 했습니다.
<Response Model>
struct QueryDatabase: Codable {
var results: [QueryProperties]
struct QueryProperties: Codable {
var properties: [String: QueryProperty]
struct QueryProperty: Codable {
var type: String
var rich_text: [RichText]?
var select: [String: String]?
struct RichText: Codable {
var plain_text: String
}
}
}
}
노션 API 데이터는 JSON 형식이고, 데이터베이스는 선택, 리치텍스트 속성만 사용할 것이기 때문에 그에 맞게 구조체를 선언해줍니다.
속성 별 설명은 노션 공식 문서의 API Reference를 참조해주세요.
<enum>
class MainViewModel: ObservableObject {
@Published var menu: [Week: [Restaurant: Meal]] = [:]
// ...
}
enum Category: String, CaseIterable, Hashable {
case 조식, 중식, 석식
}
enum Restaurant: String, CaseIterable, Hashable {
case 금정회관학생 = "금정회관 학생"
case 금정회관교직원 = "금정회관 교직원"
case 샛벌회관
case 학생회관학생 = "학생회관 학생"
case 진리관
case 웅비관
case 자유관
// 학생회관 학생
case 학생회관교직원 = "학생회관 교직원"
case 비마관
case 편의동
case 행림관
}
struct Meal: Hashable {
var foodByCategory: [Category: String] = [:]
}
식단은 요일, 식당, 식사 분류를 통해 구분할 것이므로 그에 관한 열거형을 선언하고, 데이터에 관한 작업을 위해 뷰 모델을 작성합니다.
CaseIterable을 사용해서 순회를 가능하게 했으며, 순서는 부산대학교 홈페이지에 나온 순서대로 매핑했습니다.
<데이터 연결>
struct MainView: View {
// ...
private var menu: some View {
ScrollView {
if let selectedWeekday = Week(rawValue: selectedDay) {
ForEach(Campus(rawValue: selectedCampus)!.restaurant, id: \\.rawValue) {
if let restaurant = vm.menu[selectedWeekday]![$0] {
MenuView(restaurant: $0.rawValue, meal: restaurant)
}
}
}
}
}
private struct MenuView: View {
@State private var isFavorite = false
let restaurant: String
let meal: Meal
var body: some View {
VStack {
title
card
}
.padding(.horizontal)
.padding(.bottom)
}
private var title: some View {
HStack {
Text(restaurant)
.font(.headline())
.foregroundColor(.black100)
Spacer()
Button {
isFavorite = true
} label: {
Image(systemName: "star.fill")
.font(.headline())
.foregroundColor(isFavorite ? .yellow100 : .black20)
}
}
}
private var card: some View {
VStack(alignment: .leading) {
ForEach(Category.allCases, id: \\.self) {
if let food = meal.foodByCategory[$0] {
Text("\\($0.rawValue)")
.font(.subhead())
.foregroundColor(.black100)
.padding(.bottom, UIScreen.getHeight(2))
Text("\\(food)")
.font(.body())
.foregroundColor(.black100)
.padding(.bottom, UIScreen.getHeight(18))
}
}
}
.padding()
.frame(width: UIScreen.getWidth(350), alignment: .leading)
.background {
RoundedRectangle(cornerRadius: 12)
.foregroundColor(.white100)
.shadow(radius: 2)
}
}
}
}
뷰 모델의 데이터를 뷰에 연동해줍니다.
<화면 스크린샷>
시뮬레이터에 임시 식단들이 정상적으로 나타나는 걸 확인할 수 있습니다.
728x90
'Project > 뿌대식: 부산대학교 학식 알리미' 카테고리의 다른 글
뿌대식: 개발 - 시트 (1) | 2024.01.10 |
---|---|
뿌대식: 개발 - MVVM (1) | 2024.01.10 |
뿌대식: 개발 - 메인화면 (2) | 2024.01.04 |
뿌대식: 개발 - 프로젝트 설정 (0) | 2024.01.03 |
뿌대식: 디자인 - 뷰 트리 (0) | 2024.01.02 |