Skip to content

Instantly share code, notes, and snippets.

@jordansinger
Last active January 16, 2024 16:19
Show Gist options
  • Star 22 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save jordansinger/f0111835e54f8c2db488283f52097815 to your computer and use it in GitHub Desktop.
Save jordansinger/f0111835e54f8c2db488283f52097815 to your computer and use it in GitHub Desktop.
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