티스토리 뷰
[SwiftUI] 날씨 어플 만들기 04 (ViewModel 구성, Custom Color설정, Date & DateFormatter 사용하기 )
말차프라푸치노 2021. 9. 5. 23:36
지난 글에서 API를 통해 Data를 받아와 Weather Model을 만들었습니다.
이번 글에서는 Model 에 있는 Data를 View에 넣어주기 위해 ViewModel을 만들겠습니다 😄
[SwiftUI] 날씨 어플 만들기 03 (Model 만들기, @escaping)
지난 글에서 MVVM 패턴에서 View를 구성했습니다. 이번 글에서는 Model을 만들어보겠습니다 😄 날씨 어플 만들기 01에서 구현했던 함수를 실제 앱에서 사용할 수 있게 살짝 수정해서 구현하도록 하
malchafrappuccino.tistory.com
WeatherViewModel.swift 라는 새로운 파일을 만들어줍니다
import SwiftUI
import Foundation
private let defaultIcon = "questionmark"
private let defaultBackgroundColor = Gradient(colors: [.blue, Color("LightBlue")])
private let iconMap = [
"Clear" : "sun.max.fill",
"Clouds" : "cloud.fill",
"Drizzle" : "cloud.drizzle.fill",
"Rain" : "cloud.rain.fill",
"Snow" : "snowflake",
"Thunderstorm" : "cloud.bolt.rain.fill"
]
private let backgroundColor = [
"Clear" : Gradient(colors: [.blue, Color("LightBlue")]) ,
"Clouds" : Gradient(colors: [.gray, Color("LightGray")]) ,
"Drizzle" : Gradient(colors: [.gray, Color("MiddleBlue")]) ,
"Rain" : Gradient(colors: [.gray, Color("DarkBlue")]) ,
"Snow" : Gradient(colors: [Color("LightMagenta")]) ,
"Thunderstorm" : Gradient(colors: [.gray, Color("LightGray"), .yellow])
]
private var getToday: String {
let dateformatter = DateFormatter()
dateformatter.dateFormat = "MM / dd"
let todayDay = dateformatter.string(from: Date())
return todayDay
}
public class WeatherViewModel: ObservableObject {
@Published var cityName: String = "City"
@Published var temperature: String = "--"
@Published var tempMin: String = "--"
@Published var tempMax: String = "--"
@Published var weatherDescription: String = "--"
@Published var weatherIcon: String = defaultIcon
@Published var weatherBackground: Gradient = Gradient(colors: [.blue, Color("LightBlue")])
@Published var todayDay: String = "-- / --"
public let weatherService: WeatherService
public init(weatherService: WeatherService) {
self.weatherService = weatherService
}
public func refresh() {
weatherService.getData { weather in
DispatchQueue.main.async {
self.cityName = weather.city
self.temperature = "\(weather.temperature)°C"
self.tempMin = "\(weather.temp_min)"
self.tempMax = "\(weather.temp_max)"
self.weatherDescription = weather.description.capitalized
self.weatherIcon = iconMap[weather.icon] ?? defaultIcon
self.weatherBackground = backgroundColor[weather.icon] ?? defaultBackgroundColor
self.todayDay = getToday
}
}
}
}
코드 한 줄 한 줄 살펴보기
public class WeatherViewModel: ObservableObject {
@Published var cityName: String = "City"
@Published var temperature: String = "--"
@Published var tempMin: String = "--"
@Published var tempMax: String = "--"
@Published var weatherDescription: String = "--"
@Published var weatherIcon: String = defaultIcon
@Published var weatherBackground: Gradient = Gradient(colors: [.blue, Color("LightBlue")])
@Published var todayDay: String = "-- / --"
public let weatherService: WeatherService
public init(weatherService: WeatherService) {
self.weatherService = weatherService
}
public func refresh() {
weatherService.getData { weather in
DispatchQueue.main.async {
self.cityName = weather.city
self.temperature = "\(weather.temperature)°C"
self.tempMin = "\(weather.temp_min)"
self.tempMax = "\(weather.temp_max)"
self.weatherDescription = weather.description.capitalized
self.weatherIcon = iconMap[weather.icon] ?? defaultIcon
self.weatherBackground = backgroundColor[weather.icon] ?? defaultBackgroundColor
self.todayDay = getToday
}
}
}
}
WeatherViewModel 이라는 새로운 클래스를 정의합니다. ObserableObject 라는 프로토콜을 사용해서 ViewModel 안의 인스턴스 들을 @Published로 선언합니다. 이를 통해 ViewModel에서 변경이 감지되면 View 에서 이를 감지하고 바뀔 수 있도록 합니다.
public let weatherService: WeatherService
public init(weatherService: WeatherService) {
self.weatherService = weatherService
}
weatherService를 선언해 WeatherService를 사용할 것 입니다. init으로 weatherService를 initialize 합니다.
public func refresh() {
weatherService.getData { weather in
DispatchQueue.main.async {
self.cityName = weather.city
self.temperature = "\(weather.temperature)°C"
self.tempMin = "\(weather.temp_min)"
self.tempMax = "\(weather.temp_max)"
self.weatherDescription = weather.description.capitalized
self.weatherIcon = iconMap[weather.icon] ?? defaultIcon
self.weatherBackground = backgroundColor[weather.icon] ?? defaultBackgroundColor
self.todayDay = getToday
}
}
}
refresh 라는 이름의 함수를 새로 선언하고 weatherService의 getData 함수를 호출합니다.
이 때 getData 함수는 Weather를 던지기 때문에 이 weather를 받아 ViewModel의 인스턴스에 넣어줍니다.
지난 글에서 사용했던 그림을 다시 보면
@escaping closure를 사용해 Weather를 던져줍니다.
DispatchQueue.main.async 를 사용해 네트워클 통신을 메인 쓰레드를 통해 실행합니다 (최우선 순위로)
icon 과 background 는 상황에 따라 정해준 Symbol과 색을 사용할 것 이기 때문에 새로운 배열을 선언합니다.
private let defaultIcon = "questionmark"
private let iconMap = [
"Clear" : "sun.max.fill",
"Clouds" : "cloud.fill",
"Drizzle" : "cloud.drizzle.fill",
"Rain" : "cloud.rain.fill",
"Snow" : "snowflake",
"Thunderstorm" : "cloud.bolt.rain.fill"
]
weather.icon 이 Clear 이면 self.weatherIcon (viewModel의 인스턴스) 는 sun.max.fill, Clouds면 cloud.fill ... 이렇게 6가지 경우에 따라 weaherIcon 이 결정됩니다. 이 배열을 통해 상황에 따라 알맞는 SFSymbols를 사용할 수 있게 됩니다. 6가지 경우에 해당 되지 않을 경우를 위해 default 값을 ? 로 정해주었습니다.
똑같이 배경화면도 바꿔주기 위한 새로운 배열을 선언해줍니다.
private let defaultBackgroundColor = Gradient(colors: [.blue, Color("LightBlue")])
private let backgroundColor = [
"Clear" : Gradient(colors: [.blue, Color("LightBlue")]) ,
"Clouds" : Gradient(colors: [.gray, Color("LightGray")]) ,
"Drizzle" : Gradient(colors: [.gray, Color("MiddleBlue")]) ,
"Rain" : Gradient(colors: [.gray, Color("DarkBlue")]) ,
"Snow" : Gradient(colors: [Color("LightMagenta")]) ,
"Thunderstorm" : Gradient(colors: [.gray, Color("LightGray"), .yellow])
]
Color(" ")를 처음 봤을 수도 있을 것입니다. (제가 그랬습니다 ㅎㅎ)
xcode에서는 assets 에서 사용자가 원하는 Custom color를 지정할 수 있습니다. 미리 저장해놓으면 매번 RGB 값을 입력할 필요 없이 변수의 이름을 사용하면 됩니다. 만약 CSS를 접해보신 분이라면 CSS 최상단에 root { } 하고 색깔을 미리 선언해서 사용했을텐데 그것과 동일하다고 생각하면 됩니다.
왼쪽 파일 탐색기에서 Assets.xcassets 을 누릅니다
이미지와 헷갈리지 않게 Colors라는 폴더를 새로 만들어 줍니다
왼쪽 하단에 + 버튼을 눌러주고 ColorSet을 눌러줍니다
이름은 자신이 사용하는 걸로 지어주면 됩니다. 저는 LightBlue라고 했습니다
1. Any Appearance 를 누르고 2. 우측 상단에 메뉴바를 누른뒤 3. 4번째 메뉴를 선택한다음 4. 색깔을 지정해줍니다
Dark Appearance를 통해 Dark Mode 색깔도 정해줄 수 있는데 저는 일단 Any만 했습니다.
이렇게 설정을 하면 Color("LightBlue") 를 사용해 내가 설정한 색을 쉽고 빠르게 사용할 수 있습니다.
보통은 프로젝트를 할 때 디자인에서 색을 다 정리해서 보내주기 때문에 색을 저장해놓고 사용하면 매우 편리하다고 생각합니다.
이렇게 5가지 색깔을 정해주었습니다. 이제 날씨에 알맞는 gradient를 보여줄 수 있게 되었습니다
DateFormatter()
마지막으로 제가 만들려고 하는 앱은 매일의 날짜를 보여줍니다. 매일 날짜를 고쳐주면 되겠지만 매우 귀찮겠죠??
Date 라는 struct를 사용하면 시간을 알 수 있습니다. 이 datevalue 값을 바로 View에 보여줄 수 없기 때문에 DateFormatter 라는 클래스를 사용해 String으로 바꿔주겠습니다.
private var getToday: String {
let dateformatter = DateFormatter()
dateformatter.dateFormat = "MM / dd"
let todayDay = dateformatter.string(from: Date())
return todayDay
}
Date 는 2021-09-05 14:16:43 +0000 이런 형식으로 값을 반환해줍니다
제가 지금 작성하고 있는 시간이 23시 16분인데 14시 16분으로 나옵니다. GMT 기준 시간으로 나오기 때문에 9시간 빠른 런던 시간이 나옵니다.
헉 그러면 9시간을 더해줘야 되나요??
그건 아닙니다. 핸드폰에서 어플이 실행되면 그 핸드폰 시간대에 맞춰서 보여주기 때문에 그런 걱정은 안해도 됩니다.
DateFormatter 의 string이라는 함수를 사용하면 String으로 뽑아낼 수 있습니다.
2021-09-05 14:16:43+0000 에서 09 (월) 와 05 (일) 만 사용하고 싶습니다.
월은 영어로 Month 이기 때문에 M 일은 day 이기 때문에 d 를 사용해서 뽑아줍니다.
dateformatter.dateFormat = "MM / dd"
만약 연도를 사용하고 싶으면 y , 요일은 E, 오전 오후는 a, 시간은 h, 분은 m, 초는 s, 밀리초는 S를 통해 뽑아낼 수 있습니다.
처음 날짜를 뽑아내려면 어려울 수 도 있는데요. 이럴 때 아래 사이트를 사용하면 내가 원하는 시간이나 날짜를 편하게 뽑아낼 수 있습니다.
NSDateFormatter.com - Easy Skeezy Date Formatting for Swift and Objective-C
NSDateFormatter.com Easy Skeezy Date Formatting for Swift and Objective-C ... Sunday, Sep 5, 2021 EEEE, MMM d, yyyy 09/05/2021 MM/dd/yyyy 09-05-2021 14:23 MM-dd-yyyy HH:mm Sep 5, 2:23 PM MMM d, h:mm a September 2021 MMMM yyyy Sep 5, 2021 MMM d, yyyy Sun, 5
nsdateformatter.com
MM dd 를 사용하면 09 05가 나오는 것을 볼 수 있습니다.
이렇게 Format String 설정을 바꿔가며 내가 먼저 원하는 값을 뽑아낼 수 있습니다.
Reference를 보면 사용법이 자세히 나와있습니다 😉
이렇게 Date와 DateFormatter을 사용해 getToday라는 변수에 오늘의 달과 일을 넣어주었습니다.
refresh함수에서 viewModel의 today에 getToday를 넣어줍니다.
이렇게 해서 View에서 보여줄 데이터들을 다 ViewModel에 담았습니다.
다음 글에서는 View 에 ViewModel을 담아서 화면에 데이터들을 보여주겠습니다.
긴 글 읽어주셔서 감사합니다 ☺️
'SwiftUI > SwiftUI 앱 만들기' 카테고리의 다른 글
[SwiftUI] Bouncing-DVD-Logo 앱 (1) | 2022.03.10 |
---|---|
[SwiftUI] 날씨 어플 만들기 05 완성 (View 에 ViewModel 적용) (0) | 2021.09.07 |
[SwiftUI] 날씨 어플 만들기 03 (Model 만들기, @escaping) (0) | 2021.09.02 |
[SwiftUI] 날씨 어플 만들기 02 (View 구성하기, SF Symbol3 사용하기) (0) | 2021.09.02 |
[SwiftUI] 날씨 어플 만들기 01 (OpenWeather API 사용하는 법 + CodingKey, URLSession) (0) | 2021.09.02 |
- Total
- Today
- Yesterday
- 코딩 테스트
- swiftUI 기초
- Swift 서버
- 부스트캠프
- Swift문법
- 부스트캠프7기
- 코딩
- 디자인 패턴
- todo앱
- Swift DocC
- 프로그래머스
- 책후기
- 앱개발
- Combine
- Swift공식문서
- 코딩테스트
- 개발
- vapor
- Swift
- 부스트캠프iOS
- 책리뷰
- 날씨어플
- 필독서
- SwiftUI
- ios
- 책
- UX
- TODO
- 애플
- Swift 디자인 패턴
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |