Last active
December 1, 2024 21:09
-
-
Save jordansinger/f0111835e54f8c2db488283f52097815 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import PlaygroundSupport | |
// NOTE: this example currently only works with US-based coordinates | |
// constants | |
// New York | |
let latitude = 40.709335 | |
let longitude = -73.956558 | |
// San Francisco | |
// let latitude = 37.7749 | |
// let longitude = -122.4194 | |
// lil weather api | |
let apiURL = "https://api.lil.software/weather" | |
struct Weather: Codable { | |
var forecast: [Day] | |
} | |
struct Day: Codable, Hashable { | |
var name: String | |
var temperature: Int | |
var description: String | |
} | |
struct ForecastView: View { | |
@State var forecast: [Day] = [] | |
@State var loading = true | |
var body: some View { | |
VStack { | |
if self.loading { | |
Text("Loading...") | |
} else { | |
ScrollView(.horizontal, showsIndicators: false) { | |
HStack(spacing: 28) { | |
ForEach(forecast, id: \.self) { day in | |
DayView(day: day) | |
} | |
} | |
.padding() | |
} | |
} | |
} | |
.onAppear(perform: { | |
self.loadForecast() | |
}) | |
} | |
func loadForecast() { | |
// create the API url (ex: https://api.lil.software/weather?latitude=40.709335&longitude=-73.956558) | |
let request = URLRequest(url: URL(string: "\(apiURL)?latitude=\(latitude)&longitude=\(longitude)")!) | |
// initiate the API request | |
URLSession.shared.dataTask(with: request) { data, response, error in | |
if let data = data { | |
// decode the response into our Weather struct | |
if let decodedResponse = try? JSONDecoder().decode(Weather.self, from: data) { | |
DispatchQueue.main.async { | |
// set the forecast based on the API response | |
self.forecast = decodedResponse.forecast | |
// we're no longer "loading" | |
self.loading = false | |
} | |
return | |
} | |
} | |
}.resume() | |
} | |
} | |
struct DayView: View { | |
@State var day: Day | |
var body: some View { | |
VStack(spacing: 0) { | |
Text(day.name) | |
.font(.subheadline) | |
.lineLimit(1) | |
.foregroundColor(Color(UIColor.secondaryLabel)) | |
Spacer() | |
Icon(day: day) | |
Spacer() | |
Text("\(day.temperature)°") | |
.font(.title) | |
} | |
.padding(.vertical, 28) | |
.padding(.horizontal) | |
.frame(width: 192, height: 212) | |
.background(Color(UIColor.secondarySystemBackground)) | |
.cornerRadius(24) | |
} | |
} | |
struct Icon: View { | |
@State var day: Day | |
@State var imageName = "sun.max.fill" // ☀️ sunny by default | |
@State var color = Color(UIColor.systemOrange) | |
var body: some View { | |
Image(systemName: imageName) | |
.font(.system(size: 48, weight: .medium)) | |
.frame(width: 48) | |
.foregroundColor(color) | |
.onAppear(perform: { | |
self.setIcon() | |
}) | |
} | |
func setIcon() { | |
// sets the icon of the day based on the weather description | |
let forecastDescription = self.day.description.lowercased() | |
if self.day.name.lowercased().contains("night") { | |
// 🌙 if name contains "night" | |
self.imageName = "moon.fill" | |
color = Color(UIColor.systemGray) | |
} else if forecastDescription.contains("showers") || forecastDescription.contains("rain") { | |
// 🌧 if description contains "showers" or "rain" | |
self.imageName = "cloud.rain.fill" | |
color = Color(UIColor.systemTeal) | |
} | |
} | |
} | |
PlaygroundPage.current.setLiveView(ForecastView()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment