Skip to content

Commit

Permalink
feat(app): widgets WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
krystxf committed May 25, 2024
1 parent 7b52a8a commit 4346dfb
Show file tree
Hide file tree
Showing 19 changed files with 566 additions and 49 deletions.
24 changes: 20 additions & 4 deletions app/Common/Models/LocationModel.swift
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
//
// metro-now
//
// Created by Kryštof Krátký on 15.05.2024.
// Author: Kryštof Krátký
//

import Foundation
import MapKit

final class LocationModel: NSObject, ObservableObject, CLLocationManagerDelegate {
@Published var location: CLLocation?
@Published var location: CLLocation? {
didSet {
saveLocationToUserDefaults(location)
}
}

var locationManager: CLLocationManager?

override init() {
super.init()
checkLocationServicesEnabled()
}

private func saveLocationToUserDefaults(_ location: CLLocation?) {
let userDefaults = UserDefaults(suiteName: "group.com.yourapp.group")
if let location {
userDefaults?.set(location.coordinate.latitude, forKey: "latitude")
userDefaults?.set(location.coordinate.longitude, forKey: "longitude")
}
}

func checkLocationServicesEnabled() {
let isEnabled = CLLocationManager.locationServicesEnabled()

Expand Down
23 changes: 23 additions & 0 deletions app/Common/Types/departuresResponseTypes.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// Author: Kryštof Krátký
//

import Foundation

struct ApiDeparture: Codable {
let departureTimestamp: DepartureTimestamp
let route: Route
let trip: Trip
}

struct DepartureTimestamp: Codable {
let predicted: Date
}

struct Route: Codable {
let shortName: String
}

struct Trip: Codable {
let headsign: String
}
9 changes: 0 additions & 9 deletions app/Common/Utils/const.swift

This file was deleted.

10 changes: 10 additions & 0 deletions app/Common/Utils/networkUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@
import Foundation
import SwiftUI

let METRO_NOW_API = "https://api.metronow.dev"

enum FetchError:
Error
{
case InvalidURL
case InvalidResponse
case InvalidaData
}

typealias DeparturesByGtfsIDs = [String: [ApiDeparture]]

func getDeparturesByGtfsID(gtfsIDs: [String]) async throws -> DeparturesByGtfsIDs {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"colors": [
{
"idiom": "universal"
}
],
"info": {
"author": "xcode",
"version": 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"images": [
{
"idiom": "universal",
"platform": "ios",
"size": "1024x1024"
}
],
"info": {
"author": "xcode",
"version": 1
}
}
6 changes: 6 additions & 0 deletions app/metro-now-widgets/Assets.xcassets/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info": {
"author": "xcode",
"version": 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"colors": [
{
"idiom": "universal"
}
],
"info": {
"author": "xcode",
"version": 1
}
}
74 changes: 74 additions & 0 deletions app/metro-now-widgets/Core/Provider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//
// Author: Kryštof Krátký
//

import SwiftUI
import WidgetKit

import CoreLocation

class LocationManager: NSObject, ObservableObject, CLLocationManagerDelegate {
private let locationManager = CLLocationManager()
@Published var location: CLLocation?

override init() {
super.init()
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
}

func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
self.location = location
locationManager.stopUpdatingLocation()
}
}

func locationManager(_: CLLocationManager, didFailWithError error: Error) {
print("Failed to find user's location: \(error.localizedDescription)")
}
}

struct Provider: TimelineProvider {
let locationManager = LocationManager()

func placeholder(in _: Context) -> WidgetEntry {
WidgetEntry(date: Date(), stationName: "Loading...", departures: [])
}

func getSnapshot(in _: Context, completion: @escaping (WidgetEntry) -> Void) {
let entry = WidgetEntry(date: Date(), stationName: "Kacerov", departures: [])
completion(entry)
}

func getTimeline(in _: Context, completion: @escaping (Timeline<Entry>) -> Void) {
Task {
let departures = []
}

var entries: [WidgetEntry] = []

// Generate a timeline consisting of five entries an hour apart, starting from the current date.
let currentDate = Date()
for hourOffset in 0 ..< 5 {
let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)!
let entry = WidgetEntry(date: Date(), stationName: "Kacerov", departures: [])
entries.append(entry)
}

let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}

private func fetchNearestMetroStation(completion: @escaping (String) -> Void) {
guard let location = locationManager.location else {
completion("Location not available")
return
}

// Replace with actual API call to fetch nearest metro station using location.coordinate
let nearestStation = "Mock Metro Station"
completion(nearestStation)
}
}
18 changes: 18 additions & 0 deletions app/metro-now-widgets/Core/Types/SimpleEntryType.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// Author: Kryštof Krátký
//

import WidgetKit

struct WidgetEntryDeparture {
var departureDate: Date
var direction: String
var metroLine: String
}

struct WidgetEntry: TimelineEntry {
var date: Date

let stationName: String
let departures: [WidgetEntryDeparture]
}
29 changes: 29 additions & 0 deletions app/metro-now-widgets/Core/Views/Departure.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// Author: Kryštof Krátký
//

import SwiftUI

struct Departure: View {
let direction: String
let departureDate: Date
let metroLine: String

var body: some View {
HStack {
Text(direction)
.frame(alignment: .leading)
.font(.caption)
.fontWeight(.bold)
Spacer()
Text(departureDate.formatted(.dateTime.hour().minute()))
.frame(alignment: .leading)
.font(.caption)
.fontWeight(.bold)
}
.padding(.vertical, 10)
.padding(.horizontal, 5)
.background(getMetroLineColor(metroLine))
.clipShape(.rect(cornerRadius: 10))
}
}
13 changes: 13 additions & 0 deletions app/metro-now-widgets/Core/Views/PlaceholderView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// Author: Kryštof Krátký
//

import SwiftUI

struct PlaceholderView: View {
var entry: Provider.Entry

var body: some View {
Text("Widget is waiting for data to be fetched")
}
}
25 changes: 25 additions & 0 deletions app/metro-now-widgets/Core/Views/SmallWidgetView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// Author: Kryštof Krátký
//

import SwiftUI

struct SmallWidgetView: View {
var entry: Provider.Entry

var body: some View {
VStack {
WidgetHeading(stationName: "Kacerov")
Departure(
direction: "Haje",
departureDate: Date(),
metroLine: "C"
)
Departure(
direction: "Letnany",
departureDate: Date(),
metroLine: "C"
)
}
}
}
18 changes: 18 additions & 0 deletions app/metro-now-widgets/Core/Views/WidgetHeading.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// Author: Kryštof Krátký
//

import SwiftUI

struct WidgetHeading: View {
let stationName: String

var body: some View {
HStack {
Text(stationName)
.font(.headline)
.frame(alignment: .topLeading)
Spacer()
}
}
}
17 changes: 17 additions & 0 deletions app/metro-now-widgets/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSWidgetWantsLocation</key>
<true/>
<key>NSLocationUsageDescription</key>
<string>Location is needed to determine nearest metro station</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Location is needed to determine nearest metro station</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.widgetkit-extension</string>
</dict>
</dict>
</plist>
27 changes: 27 additions & 0 deletions app/metro-now-widgets/metro_now_widgets.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// Author: Kryštof Krátký
//

import MapKit
import SwiftUI
import WidgetKit

struct metro_now_widgets: Widget {
let kind: String = "metro_now_widgets"

var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) {
entry in
SmallWidgetView(entry: entry)
.containerBackground(.fill.tertiary, for: .widget)
}
.configurationDisplayName("Departures")
.description("Metro departures from nearest station")
}
}

#Preview(as: .systemSmall) {
metro_now_widgets()
} timeline: {
WidgetEntry(date: .now, stationName: "Dejvicka", departures: [])
}
13 changes: 13 additions & 0 deletions app/metro-now-widgets/metro_now_widgetsBundle.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// Author: Kryštof Krátký
//

import SwiftUI
import WidgetKit

@main
struct metro_now_widgetsBundle: WidgetBundle {
var body: some Widget {
metro_now_widgets()
}
}
Loading

0 comments on commit 4346dfb

Please sign in to comment.