I am testing out some code, just to play. Just to learn SwiftUI.
The idea: I want to see on a map, my location and I want to see the sunrise and sunset times.
I found on GitHub a package dependency: SunKit
And I have a question.
In Sunlit the standard location (if no location is found) is Apple HQ.
I want to change that. I want to change my code so that It takes the location of user and that the SunKit dependency makes the sunrise and sunset times based on the users location.
Your help s appreciated
How can I change the code?
import SwiftUI
import SunKit
import CoreLocation
struct ContentView: View {
let timeFormatter: DateFormatter = {
let tf = DateFormatter()
tf.dateFormat = "HH:mm"
return tf
}()
#State var location: CLLocation?
let timeZone = TimeZone.current
public init() {
self.location = CLLocationManager().location
}
var body: some View {
let sun: Sun
let timeZoneOffset = Double(timeZone.secondsFromGMT()) / 3600.0
let locationManager = CLLocationManager()
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
if let userLocation = locationManager.location {
sun = Sun(location: userLocation, timeZone: timeZoneOffset)
} else {
sun = Sun(location: CLLocation(latitude: 37.7749, longitude: -122.4194), timeZone: 0)
}
return VStack {
Image(systemName: "globe")
.imageScale(.large)
.foregroundColor(.accentColor)
Text("Hello, world!")
Text("Sunrise time: \(timeFormatter.string(from: sun.sunrise))")
Text("Azimuth: \(sun.sunriseAzimuth)°")
Text("Sunrise: \(sun.sunrise)")
Text("Location: \(sun.location)")
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I tried the code and I was expecting that the location of the user would give the correct sunrise and sunset time.
Related
I would like to add some historical weather data to an app. I am able to use the new
WeatherKit to get current weather but cannot find ANY information to tell me how to
access historical data. One of the WWDC videos made reference to adding a start and end
date to the WeatherService call but I cannot find any info on this.
Also, I am struggling with the attribution requirements. I can make it work but only in
light mode. When the device is in dark mode, the Apple Weather Logo is just a white
box in the dark background (I assume the logo is there but in white - but can't prove it).
This is a simplified version - fetching current weather only:
struct ContentView: View {
#Environment(\.colorScheme) var colorScheme
#State private var weather: Weather?
#State private var attLogo: URL?
#State private var attributionURL: URL?
#State private var logoImage: Image?
let weatherService = WeatherService.shared
var body: some View {
VStack {
if let weather {
VStack {
Text("San Francisco")
.font(.largeTitle)
Text("\(weather.currentWeather.temperature.formatted()) | \(weather.currentWeather.condition.description)")
}
}//if let
Spacer()
//white letters on white box if device in dark mode
AsyncImage(url: attLogo)
Group{
if let attributionURL {
Link("Weather Attribution", destination: attributionURL)
}
}//att group
}//outer v
.padding()
.task {
do {
let location = CLLocation(latitude: 37.77, longitude: -122.41)
self.weather = try await weatherService.weather(for: location)
} catch {
print(error)
}//do catch
}//task 1
.task {
do {
let attribution = try await weatherService.attribution
let attributionLink = attribution.legalPageURL
self.attributionURL = attributionLink
let attributionLogo = colorScheme == .light ? attribution.combinedMarkDarkURL : attribution.combinedMarkLightURL
self.attLogo = attributionLogo
} catch {
print("failed to load attribution")
}
}//task for logo and link
}//body
}//struct
Any guidance would be appreciated. Xcode 14.0 Beta, iOS 16.0 (20A5283p) in Simulator
As of 10 July both logo marks are unavailable at the provided links. I have created a placeholder in the AsyncImage for now, I don't know if it would ever pass Apple's check but it seems viable for a Beta/offline solution.
if let arributionLogo = arributionLogo{
AsyncImage(url: arributionLogo) { image in
image.scaledToFit()
} placeholder: {
Label("Apple Weather", systemImage: "cloud.sun.fill")
}
}else{
ProgressView()
}
if let arributionLink = arributionLink{
Link("Other data sources", destination: arributionLink)
}else{
ProgressView()
}
Historical Weather is now available with
let forecast = try await weatherService.weather(for: location, including:.hourly(startDate: startDate, endDate: endDate))
And
let forecast = try await weatherService.weather(for: location, including: .daily(startDate: startDate, endDate: endDate))
I've been stuck trying to debug this for a while so i thought i would ask for some advice as i've got to the point where i've really confused myself. I'm trying to get data from a local api that i've created and use mapkit to display the events i get from my api on the map.
In my apps entry point i have this error "Type CommunityEventsApp does not conform to protocol 'App', it appears on the same line as struct CommunityEventsApp: App. I've trying adding an initialiser above my body in this file as this is what the error suggested as the fix however this didn't resolve the error. I have no other errors in my app. This is the code:
struct CommunityEventsApp: App {
#StateObject var viewModel: ContentViewModel
var event: Event
var body: some Scene {
WindowGroup {
TabView {
//rest of the tab view code
}
.environmentObject(viewModel)
}
}
}
I'm trying to get data from a local api i've made here in my ContentViewModel:
var eventsList: [Event]
var primary: Event
init() {
self.eventsList = []
self.primary = eventsList[0]
}
func getEvents() async throws {
guard let url = URL(string: "http://localhost:5172/events") else {fatalError("Missing URL")}
let urlRequest = URLRequest(url:url)
let (data, response) = try await URLSession.shared.data(for: urlRequest)
guard (response as? HTTPURLResponse)?.statusCode == 200 else { fatalError("Error while fetching data")}
eventsList = try JSONDecoder().decode([Event].self, from:data)
print("Async decodedEvent", eventsList)
}
}
This is my Event struct
struct Event: Decodable, Identifiable {
let id: String
let title: String
let date: String
let time: String
let location: String
let latitude: Double
let longitude: Double
let price: Double
let description: String
let link: String?
let imageUrl: String
init(id: String,
title: String,
date: String,
time: String,
location: String,
latitude: Double,
longitude: Double,
price: Double,
description: String,
link: String,
imageUrl: String) {
self.id = id
self.title = title
self.date = date
self.time = time
self.location = location
self.latitude = latitude
self.longitude = longitude
self.price = price
self.description = description
self.link = link
self.imageUrl = imageUrl
}
}
I call getEvents inside the onAppear in the ContentView:
let event: Event
let viewModel: ContentViewModel
var body: some View {
// UI formatting here
.onAppear {
Task {
do {
try await viewModel.getEvents()
} catch {
print("Error", error)
}
}
}
}
.navigationTitle("Event Information")
}
}
}
MapView where i use my api data to display events on the map:
#EnvironmentObject var events: ContentViewModel
#State var region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 53.483959, longitude: -2.244644),
span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2)
)
var body: some View {
Map(coordinateRegion: $region,
annotationItems: events.eventsList) {
event in
MapAnnotation(coordinate: CLLocationCoordinate2D(latitude: event.latitude, longitude: event.longitude)) {
NavigationLink(destination: ContentView(event: event, viewModel: ContentViewModel())) {
Image(systemName: "pin.fill")
.onHover { hover in
print(event.title)
}
}
}
}
.navigationTitle("Events Near You")
}
}
I don't have anyone else that i can ask for help so any help or information would be greatly appreciated! I'm still a beginner with Swift development and don't really feel comfortable with it yet. I'm using xcode version 13 and swift version 5
Your properties need default values.
The App struct is the entry point in your application. So every property needs to be properly initialized.
Especially your #StateObject var.
I've been trying to create a mapmarker on my screen via mapkit for 1 week. These mapmarkers come from the api I have as latitude and longitude. However, I learned swift 1 month ago and I'm having a hard time. Is there someone to help me? Code and screenshot attached.
Obviously what I want is to know where to write the codes and to know which codes to use. (My interface is swift, there is usually a storyboard interface on the internet)
import SwiftUI
import MapKit
struct depremHaritasi: View {
#State var quakes: [EarthQuake] = []
#State var coordinateRegion = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 38.9520281, longitude: 35.6980142),
span: MKCoordinateSpan(latitudeDelta: 30, longitudeDelta: 10))
var body: some View {
Map(coordinateRegion: $coordinateRegion)
.edgesIgnoringSafeArea(.all)
.onAppear {
Api().getEarthQuake { (quakes) in
self.quakes = quakes
}
}
}
}
struct depremHaritasi_Previews: PreviewProvider {
static var previews: some View {
depremHaritasi()
}
}
Earthquake Map Screenshot
I am setting up a MapView with custom coordinates. Here is my code thus far
var coordModel: AirportModel?
struct MapView: View {
#Binding private var lat: Double
#Binding private var lon: Double
private let initialLatitudinalMetres: Double = coordModel?.airportLat ?? 0
private let initialLongitudinalMetres: Double = coordModel?.airportLong ?? 0
#State private var span: MKCoordinateSpan?
init(lat: Binding<Double>, lon: Binding<Double>) {
_lat = lat
_lon = lon
}
private var region: Binding<MKCoordinateRegion> {
Binding {
let centre = CLLocationCoordinate2D(latitude: lat, longitude: lon)
if let span = span {
return MKCoordinateRegion(center: centre, span: span)
} else {
return MKCoordinateRegion(center: centre, latitudinalMeters: initialLatitudinalMetres, longitudinalMeters: initialLongitudinalMetres)
}
} set: { region in
lat = region.center.latitude
lon = region.center.longitude
span = region.span
}
}
var body: some View {
Map(coordinateRegion: region)
}
}
In the AirportModel, there is a piece of data decoded in Double form called "latitude" and "longitude." I am simply trying to pass these data into the view after they are decoded. Here is my view body:
var body: some View {
ScrollView{
MapView(lat: <#Binding<Double>#>, lon: <#Binding<Double>#>)
.frame(height: 250)
.edgesIgnoringSafeArea(/*#START_MENU_TOKEN#*/.all/*#END_MENU_TOKEN#*/)
}
I have tried putting coordModel?.latitude and coordModel?.longitude in for lat and lon, as well as setting variables above the call. I'm not quite sure where to go now.
Note: I am decoding a bunch of JSON data and every user query results in a different lat/lon pull (over 10000 potential responses). Thanks again!
Use a model that is an ObservableObject:
import Combine
import MapKit
class MapModel: ObservableObject {
var region: MKCoordinateRegion = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: 48.687330584, longitude: 9.219832454),
latitudinalMeters: 1000000,
longitudinalMeters: 1000000
) {
willSet {
self.objectWillChange.send()
}
}
// MARK: - test code change center evey 10 seconds
init() {
self.timer = Timer.scheduledTimer(withTimeInterval: 10, repeats: true) { timer in
self.testIndex += 1
self.testIndex %= self.testCoordinates.count
let newCenter = self.testCoordinates[self.testIndex]
let oldSpan = self.region.span
DispatchQueue.main.async {
self.region = MKCoordinateRegion(center: newCenter, span: oldSpan)
}
}
}
let testCoordinates = [
CLLocationCoordinate2D(latitude: 48.687330584, longitude: 9.219832454), // STR
CLLocationCoordinate2D(latitude: 41.297445, longitude: 2.0832941), // BCN
]
var testIndex: Int = 0
var timer: Timer? = nil
}
then create a View that observes your model
import SwiftUI
import MapKit
struct ContentView: View {
#ObservedObject private var mapModel = MapModel()
var body: some View {
Map(coordinateRegion: $mapModel.region)
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
For test reasons I used a timer that simulates updated data from a server.
If you need Annotations, add them to the model and don't forget self.objectWillChange.send() if you change them.
How to get startDateOfMonth and endDateOfMonth based on selected date in SwiftUI?
I have found some answers for Swift (DateComponents), but couldn't make it work with SwiftUI.
Why I need this: I am going to use dynamic filters using predicate to filter all the data in the currently selected month (using custom control to switch months). But first I need to get the start and end dates per selected month.
EXAMPLE code:
ContentView.swift
import SwiftUI
struct ContentView: View {
#State var currentDate = Date()
// How to make startDateOfMonth and endDateOfMonth dependent on selected month?
#State private var startDateOfMonth = "1st January"
#State private var endDateOfMonth = "31st January"
var body: some View {
VStack {
DateView(date: $currentDate)
Text("\(currentDate)")
Text(startDateOfMonth)
Text(endDateOfMonth)
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
DateView.swift
import SwiftUI
struct DateView: View {
static let dateFormat: DateFormatter = {
let formatter = DateFormatter()
formatter.setLocalizedDateFormatFromTemplate("yyyy MMMM")
return formatter
}()
#Binding var date : Date
var body: some View {
HStack {
Image(systemName: "chevron.left")
.padding()
.onTapGesture {
print("Month -1")
self.changeDateBy(-1)
}
Spacer()
Text("\(date, formatter: Self.dateFormat)")
Spacer()
Image(systemName: "chevron.right")
.padding()
.onTapGesture {
print("Month +1")
self.changeDateBy(1)
}
}
.padding(EdgeInsets(top: 5, leading: 10, bottom: 5, trailing: 10))
.background(Color.yellow)
}
func changeDateBy(_ months: Int) {
if let date = Calendar.current.date(byAdding: .month, value: months, to: date) {
self.date = date
}
}
}
struct DateView_Previews: PreviewProvider {
struct BindingTestHolder: View {
#State var testItem: Date = Date()
var body: some View {
DateView(date: $testItem)
}
}
static var previews: some View {
BindingTestHolder()
}
}
I managed to solve it by the following implementation of ContentView
#State var currentDate = Date()
private var startDateOfMonth: String {
let components = Calendar.current.dateComponents([.year, .month], from: currentDate)
let startOfMonth = Calendar.current.date(from: components)!
return format(date: startOfMonth)
}
private var endDateOfMonth: String {
var components = Calendar.current.dateComponents([.year, .month], from: currentDate)
components.month = (components.month ?? 0) + 1
components.hour = (components.hour ?? 0) - 1
let endOfMonth = Calendar.current.date(from: components)!
return format(date: endOfMonth)
}
var body: some View {
VStack {
DateView(date: $currentDate)
Text("\(currentDate)")
Text(startDateOfMonth)
Text(endDateOfMonth)
}
}
private func format(date: Date) -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .medium
return dateFormatter.string(from: date)
}
Because currentDate is changed by DateView through Binding the body computed property will be invoked thus startDateOfMonth and endDateOfMonth computed properties will return the updated values.