Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: native app #6

Merged
merged 1 commit into from
Apr 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore

## User settings
xcuserdata/

## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
*.xcscmblueprint
*.xccheckout

## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
build/
DerivedData/
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3

## Obj-C/Swift specific
*.hmap

## App packaging
*.ipa
*.dSYM.zip
*.dSYM

## Playgrounds
timeline.xctimeline
playground.xcworkspace

# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
# Package.resolved
# *.xcodeproj
#
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
# hence it is not needed unless you have added a package configuration file to your project
# .swiftpm

.build/

# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
#
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace

# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts

Carthage/Build/

# Accio dependency management
Dependencies/
.accio/

# fastlane
#
# It is recommended to not store the screenshots in the git repo.
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control

fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output

# Code Injection
#
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode

iOSInjectionProject/

env.swift
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,14 @@
{
"images" : [
{
"filename" : "icon.png",
"idiom" : "universal",
"platform" : "watchos",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions app/MetroMate Watch App/Assets.xcassets/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
17 changes: 17 additions & 0 deletions app/MetroMate Watch App/MetroMateApp.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// MetroMateApp.swift
// MetroMate Watch App
//
// Created by Kryštof Krátký on 31.03.2024.
//

import SwiftUI

@main
struct MetroMate_Watch_AppApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
48 changes: 48 additions & 0 deletions app/MetroMate Watch App/departures/departure.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// departure.swift
// MetroMate
//
// Created by Kryštof Krátký on 01.04.2024.
//

import Foundation

struct Departure: Codable, Identifiable, Hashable {
let departureTimestamp: DepartureTimestamp
let trip: DepartureTrip
let delay: DepartureDelay?
let stop: DepartureStop
let route: DepartureRoute

var id: String { return trip.id }

func hash(into hasher: inout Hasher) {
hasher.combine(trip.id)
}

static func == (lhs: Departure, rhs: Departure) -> Bool {
return lhs.trip.id == rhs.trip.id
}
}

struct DepartureStop: Codable {
let id: String
}

struct DepartureRoute: Codable {
let shortName: String
}

struct DepartureTimestamp: Codable {
let predicted: String
let scheduled: String
}

struct DepartureTrip: Codable {
let headsign: String
let id: String
}

struct DepartureDelay: Codable {
let minutes: Int?
}
94 changes: 94 additions & 0 deletions app/MetroMate Watch App/departures/fetch-departures.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//
// fetch-departures.swift
// MetroMate
//
// Created by Kryštof Krátký on 01.04.2024.
//

import Foundation

let ENDPOINT_URL = "https://api.golemio.cz/v2/pid/departureboards"
let REQUEST_PARAMETERS = [
URLQueryItem(name: "includeMetroTrains", value: "true"),
URLQueryItem(name: "preferredTimezone", value: "Europe_Prague"),
URLQueryItem(name: "mode", value: "departures"),
URLQueryItem(name: "order", value: "real"),
URLQueryItem(name: "filter", value: "none"),
URLQueryItem(name: "minutesBefore", value: String(2)),
URLQueryItem(name: "minutesAfter", value: String(360))

]

struct DepartureBoardResponse: Codable {
let departures: [Departure]
}

func fetchDepartureBoardData(
platformIDs: [String], // gtfsIDs
completion: @escaping (Result<[Departure], Error>) -> Void
) {
guard let baseURL = URL(string: ENDPOINT_URL) else {
print("Invalid base URL")
return
}

var components = URLComponents(
url: baseURL,
resolvingAgainstBaseURL: false
)
components?.queryItems = REQUEST_PARAMETERS + platformIDs.map { platformID in
URLQueryItem(name: "ids[]", value: platformID)
}

guard let url = components?.url else {
print("Failed to construct URL")
return
}

var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue(
GOLEMIO_API_KEY,
forHTTPHeaderField: "X-Access-Token"
)

let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
completion(.failure(error))
return
}

guard let httpResponse = response as? HTTPURLResponse,
(200 ... 299).contains(httpResponse.statusCode)
else {
completion(
.failure(
NSError(
domain: "InvalidResponse",
code: 0,
userInfo: nil
)
)
)
return
}

if let data = data {
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

let decodedResponse = try decoder.decode(DepartureBoardResponse.self, from: data)

completion(
.success(decodedResponse.departures)
)
} catch {
completion(
.failure(error)
)
}
}
}
task.resume()
}
22 changes: 22 additions & 0 deletions app/MetroMate Watch App/lines.utils.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// lines.utils.swift
// MetroMate
//
// Created by Kryštof Krátký on 31.03.2024.
//

import Foundation
import SwiftUI

func getLineColor(line: String) -> Color {
switch line {
case "A":
return .green
case "B":
return .yellow
case "C":
return .red
default:
return .white
}
}
50 changes: 50 additions & 0 deletions app/MetroMate Watch App/location/location.manager.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// location.manager.swift
// MetroMate
//
// Created by Kryštof Krátký on 31.03.2024.
//

import CoreLocation
import Foundation

class LocationManager: NSObject, ObservableObject {
private let manager = CLLocationManager()
@Published var userLocation: CLLocation?
static let shared = LocationManager()

override init() {
super.init()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyHundredMeters
manager.startUpdatingLocation()
}

func requestLocation() {
manager.requestWhenInUseAuthorization()
}
}

extension LocationManager: CLLocationManagerDelegate {
func locationManager(_: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
print("location manager: notDetermined")
case .restricted:
print("location manager: restricted")
case .denied:
print("location manager: denied")
case .authorizedAlways:
print("location manager: authorizedAlways")
case .authorizedWhenInUse:
print("location manager: authorizedWhenInUse")
@unknown default:
break
}
}

func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let location = locations.last else { return }
userLocation = location
}
}
Loading