Skip to content

Commit

Permalink
Merge pull request #6 from krystxf/feat/ios-app
Browse files Browse the repository at this point in the history
feat: native app
  • Loading branch information
krystxf authored Apr 21, 2024
2 parents cc90033 + fe48853 commit 187a3a0
Show file tree
Hide file tree
Showing 26 changed files with 2,609 additions and 0 deletions.
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

0 comments on commit 187a3a0

Please sign in to comment.