├── .gitignore ├── Cartfile ├── Cartfile.private ├── Cartfile.resolved ├── DemoPayload.md ├── ICEInformation.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── ICEInformation.xcscmblueprint │ └── xcuserdata │ │ └── lukaslaschmidt.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcshareddata │ └── xcschemes │ │ └── ICEOnboardAPI.xcscheme └── xcuserdata │ ├── lukaslaschmidt.xcuserdatad │ ├── xcdebugger │ │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ │ ├── ICEInformation.xcscheme │ │ ├── ICEOnboardWidget.xcscheme │ │ ├── NextStopNotification.xcscheme │ │ └── xcschememanagement.plist │ └── lukasschmidt.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── ICEInformation.xcscheme │ └── xcschememanagement.plist ├── ICEInformation ├── AppDelegate.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── BuyableCell.swift ├── ConnectingTrainViewController.swift ├── Info.plist ├── ServiceViewController.swift ├── StationOverviewViewController.swift ├── StationViewController.swift └── TrainStationCell.swift ├── ICEInformationiOS ├── Buyable+JSONSerialization.swift ├── Buyable.swift ├── ErrorType.swift ├── FakeICENetworkAccess.swift ├── FirstClassDeliverOffers+JSONSerialization.swift ├── FirstClassDeliverOffers.swift ├── ICEInformationiOS.h ├── ICEStatus+JSONSerialization.swift ├── ICEStatus.swift ├── ICETrip+JSONSerialization.swift ├── ICETrip.swift ├── Info.plist ├── Location+CLLocation.swift ├── Location+JSONSerialization.swift ├── Location.swift ├── Station+JSONSerialization.swift ├── Station+MKAnnotation.swift ├── Station.swift ├── StationSchedule+JSONSerialization.swift ├── Stop+JSONSerialization.swift ├── Stop.swift ├── Track+JSONSerialization.swift ├── Track.swift ├── TrainConnection+JSONSerialization.swift ├── TrainConnection.swift ├── TrainConnections+JSONSerialization.swift ├── TrainOnBoardAPI.swift └── Trip+MKAnnotations.swift ├── ICEInformationiOSTests ├── ICEInformationiOSTests.swift ├── Info.plist └── TestData.swift ├── ICEOnboardWidget ├── Base.lproj │ └── MainInterface.storyboard ├── Info.plist └── TodayViewController.swift ├── LICENSE ├── NextStopNotification ├── Base.lproj │ └── MainInterface.storyboard ├── Info.plist └── NotificationViewController.swift ├── README.md └── Screenshot.png /.gitignore: -------------------------------------------------------------------------------- 1 | Carthage/* 2 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "matthewcheok/JSONCodable" == 3.0.1 2 | github "dbsystel/DBNetworkStack" == 0.6.0 3 | -------------------------------------------------------------------------------- /Cartfile.private: -------------------------------------------------------------------------------- 1 | github "lightsprint09/Sourcing" ~> 2.0 -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "dbsystel/DBNetworkStack" "0.6.0" 2 | github "lightsprint09/Sourcing" "2.2" 3 | github "matthewcheok/JSONCodable" "3.0.1" 4 | -------------------------------------------------------------------------------- /DemoPayload.md: -------------------------------------------------------------------------------- 1 | http://ice.portal2/jetty/api/v1/tripInfo 2 | ```json 3 | "actualPosition" : 173933, 4 | "tripDate" : "2015-12-10", 5 | "trainType" : "ICE", 6 | "distanceFromLastStop" : 45828, 7 | "totalDistance" : 475988, 8 | "stops" : [ 9 | { 10 | "station" : { 11 | "evaNr" : "8500010_00", 12 | "name" : "Basel SBB", 13 | "geocoordinates" : { 14 | "longitude" : 7.589169, 15 | "latitude" : 47.547077 16 | } 17 | }, 18 | "info" : { 19 | "status" : 0, 20 | "passed" : true, 21 | "distance" : 0, 22 | "distanceFromStart" : 0 23 | }, 24 | "track" : { 25 | "scheduled" : "12", 26 | "actual" : "" 27 | }, 28 | "timetable" : { 29 | "actualDepartureTime" : 1449742380000, 30 | "departureDelay" : "", 31 | "actualArrivalTime" : null, 32 | "scheduledArrivalTime" : null, 33 | "arrivalDelay" : "", 34 | "scheduledDepartureTime" : 1449742380000 35 | } 36 | }, 37 | { 38 | "station" : { 39 | "evaNr" : "8000026_00", 40 | "name" : "Basel Bad Bf", 41 | "geocoordinates" : { 42 | "longitude" : 7.607805, 43 | "latitude" : 47.567288 44 | } 45 | }, 46 | "info" : { 47 | "status" : 0, 48 | "passed" : true, 49 | "distance" : 2648, 50 | "distanceFromStart" : 2648 51 | }, 52 | "track" : { 53 | "scheduled" : "4", 54 | "actual" : "" 55 | }, 56 | "timetable" : { 57 | "actualDepartureTime" : 1449742980000, 58 | "departureDelay" : "", 59 | "actualArrivalTime" : 1449742800000, 60 | "scheduledArrivalTime" : 1449742740000, 61 | "arrivalDelay" : "+1", 62 | "scheduledDepartureTime" : 1449742980000 63 | } 64 | }, 65 | { 66 | "station" : { 67 | "evaNr" : "8000107_00", 68 | "name" : "Freiburg(Breisgau) Hbf", 69 | "geocoordinates" : { 70 | "longitude" : 7.84117, 71 | "latitude" : 47.997697 72 | } 73 | }, 74 | "info" : { 75 | "status" : 0, 76 | "passed" : true, 77 | "distance" : 50951, 78 | "distanceFromStart" : 53599 79 | }, 80 | "track" : { 81 | "scheduled" : "1", 82 | "actual" : "" 83 | }, 84 | "timetable" : { 85 | "actualDepartureTime" : 1449745080000, 86 | "departureDelay" : "+1", 87 | "actualArrivalTime" : 1449744900000, 88 | "scheduledArrivalTime" : 1449744900000, 89 | "arrivalDelay" : "", 90 | "scheduledDepartureTime" : 1449745020000 91 | } 92 | }, 93 | { 94 | "station" : { 95 | "evaNr" : "8000290_00", 96 | "name" : "Offenburg", 97 | "geocoordinates" : { 98 | "longitude" : 7.946723, 99 | "latitude" : 48.476475 100 | } 101 | }, 102 | "info" : { 103 | "status" : 0, 104 | "passed" : true, 105 | "distance" : 53824, 106 | "distanceFromStart" : 107423 107 | }, 108 | "track" : { 109 | "scheduled" : "3", 110 | "actual" : "" 111 | }, 112 | "timetable" : { 113 | "actualDepartureTime" : 1449747060000, 114 | "departureDelay" : "+1", 115 | "actualArrivalTime" : 1449746880000, 116 | "scheduledArrivalTime" : 1449746880000, 117 | "arrivalDelay" : "", 118 | "scheduledDepartureTime" : 1449747000000 119 | } 120 | }, 121 | { 122 | "station" : { 123 | "evaNr" : "8000191_00", 124 | "name" : "Karlsruhe Hbf", 125 | "geocoordinates" : { 126 | "longitude" : 8.402181000000001, 127 | "latitude" : 48.993515 128 | } 129 | }, 130 | "info" : { 131 | "status" : 0, 132 | "passed" : true, 133 | "distance" : 66510, 134 | "distanceFromStart" : 173933 135 | }, 136 | "track" : { 137 | "scheduled" : "3", 138 | "actual" : "4" 139 | }, 140 | "timetable" : { 141 | "actualDepartureTime" : 1449748980000, 142 | "departureDelay" : "+3", 143 | "actualArrivalTime" : 1449748800000, 144 | "scheduledArrivalTime" : 1449748680000, 145 | "arrivalDelay" : "+2", 146 | "scheduledDepartureTime" : 1449748800000 147 | } 148 | }, 149 | { 150 | "station" : { 151 | "evaNr" : "8000244_00", 152 | "name" : "Mannheim Hbf", 153 | "geocoordinates" : { 154 | "longitude" : 8.468921, 155 | "latitude" : 49.479354 156 | } 157 | }, 158 | "info" : { 159 | "status" : 0, 160 | "passed" : false, 161 | "distance" : 54255, 162 | "distanceFromStart" : 228188 163 | }, 164 | "track" : { 165 | "scheduled" : "3", 166 | "actual" : "" 167 | }, 168 | "timetable" : { 169 | "actualDepartureTime" : 1449750960000, 170 | "departureDelay" : "", 171 | "actualArrivalTime" : 1449750420000, 172 | "scheduledArrivalTime" : 1449750180000, 173 | "arrivalDelay" : "+4", 174 | "scheduledDepartureTime" : 1449750960000 175 | } 176 | }, 177 | { 178 | "station" : { 179 | "evaNr" : "8070003_00", 180 | "name" : "Frankfurt(M) Flughafen Fernbf", 181 | "geocoordinates" : { 182 | "longitude" : 8.570185, 183 | "latitude" : 50.053167 184 | } 185 | }, 186 | "info" : { 187 | "status" : 0, 188 | "passed" : false, 189 | "distance" : 64236, 190 | "distanceFromStart" : 292424 191 | }, 192 | "track" : { 193 | "scheduled" : "Fern 6", 194 | "actual" : "" 195 | }, 196 | "timetable" : { 197 | "actualDepartureTime" : 1449752940000, 198 | "departureDelay" : "", 199 | "actualArrivalTime" : 1449752760000, 200 | "scheduledArrivalTime" : 1449752760000, 201 | "arrivalDelay" : "", 202 | "scheduledDepartureTime" : 1449752940000 203 | } 204 | }, 205 | { 206 | "station" : { 207 | "evaNr" : "8005556_00", 208 | "name" : "Siegburg\/Bonn", 209 | "geocoordinates" : { 210 | "longitude" : 7.203026, 211 | "latitude" : 50.793915 212 | } 213 | }, 214 | "info" : { 215 | "status" : 0, 216 | "passed" : false, 217 | "distance" : 127174, 218 | "distanceFromStart" : 419598 219 | }, 220 | "track" : { 221 | "scheduled" : "3", 222 | "actual" : "" 223 | }, 224 | "timetable" : { 225 | "actualDepartureTime" : 1449755340000, 226 | "departureDelay" : "", 227 | "actualArrivalTime" : 1449755220000, 228 | "scheduledArrivalTime" : 1449755220000, 229 | "arrivalDelay" : "", 230 | "scheduledDepartureTime" : 1449755340000 231 | } 232 | }, 233 | { 234 | "station" : { 235 | "evaNr" : "8073368_00", 236 | "name" : "Köln Messe\/Deutz Gl.11-12", 237 | "geocoordinates" : { 238 | "longitude" : 6.974067, 239 | "latitude" : 50.941721 240 | } 241 | }, 242 | "info" : { 243 | "status" : 0, 244 | "passed" : false, 245 | "distance" : 22991, 246 | "distanceFromStart" : 442589 247 | }, 248 | "track" : { 249 | "scheduled" : "12", 250 | "actual" : "" 251 | }, 252 | "timetable" : { 253 | "actualDepartureTime" : 1449756360000, 254 | "departureDelay" : "", 255 | "actualArrivalTime" : 1449756180000, 256 | "scheduledArrivalTime" : 1449756180000, 257 | "arrivalDelay" : "", 258 | "scheduledDepartureTime" : 1449756360000 259 | } 260 | }, 261 | { 262 | "station" : { 263 | "evaNr" : "8000085_00", 264 | "name" : "Düsseldorf Hbf", 265 | "geocoordinates" : { 266 | "longitude" : 6.794319, 267 | "latitude" : 51.219962 268 | } 269 | }, 270 | "info" : { 271 | "status" : 0, 272 | "passed" : false, 273 | "distance" : 33399, 274 | "distanceFromStart" : 475988 275 | }, 276 | "track" : { 277 | "scheduled" : "17", 278 | "actual" : "" 279 | }, 280 | "timetable" : { 281 | "actualDepartureTime" : null, 282 | "departureDelay" : "", 283 | "actualArrivalTime" : 1449757620000, 284 | "scheduledArrivalTime" : 1449757620000, 285 | "arrivalDelay" : "", 286 | "scheduledDepartureTime" : null 287 | } 288 | } 289 | ], 290 | "stopInfo" : { 291 | "actualLast" : "8000191_00", 292 | "actualLastStarted" : "8000244", 293 | "actualNext" : "8000244_00", 294 | "finalStationName" : "Düsseldorf Hbf", 295 | "scheduledNext" : "8000244_00", 296 | "finalStationEvaNr" : "8000085_00" 297 | }, 298 | "vzn" : "108" 299 | } 300 | ``` 301 | 302 | 303 | 304 | http://ice.portal2/jetty/api/v1/status 305 | ```json 306 | { 307 | "serverTime" : 1449749986439, 308 | "speed" : 221.8000030517578, 309 | "latitude" : 49.404305, 310 | "connection" : true, 311 | "servicelevel" : "SERVICE", 312 | "longitude" : 8.547148 313 | } 314 | ``` 315 | 316 | https://portal.imice.de/api1/rs/tripInfo/connection/8000244_00 317 | 318 | https://portal.imice.de/api1/rs/pois/map/50.105662/8.658493/3921.9763328765366 319 | 320 | https://portal.imice.de/api1/rs/pages/1259002220/775 321 | 322 | -------------------------------------------------------------------------------- /ICEInformation.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ICEInformation.xcodeproj/project.xcworkspace/xcshareddata/ICEInformation.xcscmblueprint: -------------------------------------------------------------------------------- 1 | { 2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "08E1C23069954CB7B944845CD25D0DCF5E5B7F92", 3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { 4 | 5 | }, 6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { 7 | "CA88B9C3A61C2C61F61FB87727D4B32C7135B85F" : 9223372036854775807, 8 | "08E1C23069954CB7B944845CD25D0DCF5E5B7F92" : 9223372036854775807 9 | }, 10 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "2D4E3032-EB56-4E80-BED8-759E96ABA2B2", 11 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { 12 | "CA88B9C3A61C2C61F61FB87727D4B32C7135B85F" : "DBNetworkStackSourcing\/", 13 | "08E1C23069954CB7B944845CD25D0DCF5E5B7F92" : "InTrain-ICE-API\/" 14 | }, 15 | "DVTSourceControlWorkspaceBlueprintNameKey" : "ICEInformation", 16 | "DVTSourceControlWorkspaceBlueprintVersion" : 204, 17 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "ICEInformation.xcodeproj", 18 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ 19 | { 20 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/lightsprint09\/InTrain-ICE-API.git", 21 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "08E1C23069954CB7B944845CD25D0DCF5E5B7F92" 23 | }, 24 | { 25 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/dbsystel\/DBNetworkStack-Sourcing.git", 26 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 27 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "CA88B9C3A61C2C61F61FB87727D4B32C7135B85F" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /ICEInformation.xcodeproj/project.xcworkspace/xcuserdata/lukaslaschmidt.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightsprint09/ICEOnboardAPI/05ebda2d3719e3f2d8305784d6ca0a1470494965/ICEInformation.xcodeproj/project.xcworkspace/xcuserdata/lukaslaschmidt.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ICEInformation.xcodeproj/xcshareddata/xcschemes/ICEOnboardAPI.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /ICEInformation.xcodeproj/xcuserdata/lukaslaschmidt.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 20 | 21 | 22 | 24 | 36 | 37 | 38 | 40 | 52 | 53 | 67 | 68 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /ICEInformation.xcodeproj/xcuserdata/lukaslaschmidt.xcuserdatad/xcschemes/ICEInformation.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ICEInformation.xcodeproj/xcuserdata/lukaslaschmidt.xcuserdatad/xcschemes/ICEOnboardWidget.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 6 | 9 | 10 | 16 | 22 | 23 | 24 | 30 | 36 | 37 | 38 | 39 | 40 | 45 | 46 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 70 | 72 | 78 | 79 | 80 | 81 | 82 | 83 | 90 | 92 | 98 | 99 | 100 | 101 | 103 | 104 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /ICEInformation.xcodeproj/xcuserdata/lukaslaschmidt.xcuserdatad/xcschemes/NextStopNotification.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 6 | 9 | 10 | 16 | 22 | 23 | 24 | 30 | 36 | 37 | 38 | 39 | 40 | 45 | 46 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 70 | 72 | 78 | 79 | 80 | 81 | 82 | 83 | 90 | 92 | 98 | 99 | 100 | 101 | 103 | 104 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /ICEInformation.xcodeproj/xcuserdata/lukaslaschmidt.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ICEInformation.xcscheme 8 | 9 | orderHint 10 | 1 11 | 12 | ICEOnboardAPI.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | ICEOnboardWidget.xcscheme 18 | 19 | orderHint 20 | 2 21 | 22 | NextStopNotification.xcscheme 23 | 24 | orderHint 25 | 3 26 | 27 | 28 | SuppressBuildableAutocreation 29 | 30 | 9431F9B81C19935000860884 31 | 32 | primary 33 | 34 | 35 | 943FF82A1C2D63AF009E7C5F 36 | 37 | primary 38 | 39 | 40 | 943FF8331C2D63AF009E7C5F 41 | 42 | primary 43 | 44 | 45 | C6A2A4D91E9792FD00B55A0F 46 | 47 | primary 48 | 49 | 50 | C6CF23631EADFFBD00580862 51 | 52 | primary 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /ICEInformation.xcodeproj/xcuserdata/lukasschmidt.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /ICEInformation.xcodeproj/xcuserdata/lukasschmidt.xcuserdatad/xcschemes/ICEInformation.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 53 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 71 | 72 | 73 | 74 | 84 | 86 | 92 | 93 | 94 | 95 | 96 | 97 | 103 | 105 | 111 | 112 | 113 | 114 | 116 | 117 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /ICEInformation.xcodeproj/xcuserdata/lukasschmidt.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | ICEInformation.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | ICEInformationiOS.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 1 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | 9431F9B81C19935000860884 21 | 22 | primary 23 | 24 | 25 | 9431F9CC1C19935000860884 26 | 27 | primary 28 | 29 | 30 | 9431F9D71C19935000860884 31 | 32 | primary 33 | 34 | 35 | 943FF82A1C2D63AF009E7C5F 36 | 37 | primary 38 | 39 | 40 | 943FF8331C2D63AF009E7C5F 41 | 42 | primary 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /ICEInformation/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ICEInformation 4 | // 5 | // Created by Lukas Schmidt on 10.12.15. 6 | // Copyright © 2015 Lukas Schmidt. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | 19 | if let localNotificationInfo = launchOptions?[.remoteNotification] as? UILocalNotification { 20 | if let something = localNotificationInfo.userInfo!["evaId"] as? [String: Any] { 21 | print(something) 22 | } 23 | 24 | 25 | } 26 | 27 | return true 28 | } 29 | 30 | func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) { 31 | if let something = userInfo["evaId"] as? [String: Any] { 32 | print(something) 33 | } 34 | } 35 | 36 | func application(_ application: UIApplication, didReceive notification: UILocalNotification) { 37 | print(notification.userInfo) 38 | if let something = notification.userInfo?["evaId"] as? [String: Any] { 39 | print(something) 40 | } 41 | } 42 | 43 | } 44 | 45 | -------------------------------------------------------------------------------- /ICEInformation/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /ICEInformation/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /ICEInformation/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 124 | 130 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 203 | 209 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 290 | 291 | 292 | 293 | 299 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 321 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 341 | 342 | 343 | 344 | 350 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 423 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | -------------------------------------------------------------------------------- /ICEInformation/BuyableCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 Lukas Schmidt. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | // DEALINGS IN THE SOFTWARE. 21 | // 22 | 23 | import Foundation 24 | import Sourcing 25 | import ICEOnboardAPI 26 | 27 | class BuyableCell: UITableViewCell { 28 | @IBOutlet weak var titleLabel: UILabel! 29 | @IBOutlet weak var priceLabel: UILabel! 30 | @IBOutlet weak var descriptionLabel: UILabel! 31 | 32 | } 33 | 34 | extension BuyableCell: CellIdentifierProviding {} 35 | extension BuyableCell: ConfigurableCell { 36 | func configure(with buyable: Buyable) { 37 | titleLabel.text = buyable.title 38 | priceLabel.text = buyable.price 39 | descriptionLabel.text = buyable.description 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ICEInformation/ConnectingTrainViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 Lukas Schmidt. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | // DEALINGS IN THE SOFTWARE. 21 | // 22 | // 23 | // ConnectingTrainViewController.swift 24 | // ICEInformation 25 | // 26 | // Created by Lukas Schmidt on 05.04.17. 27 | // 28 | 29 | import UIKit 30 | import DBNetworkStack 31 | import ICEOnboardAPI 32 | 33 | let dateFormatter: DateFormatter = { 34 | let formatter = DateFormatter() 35 | formatter.dateFormat = "HH:mm" 36 | 37 | return formatter 38 | }() 39 | 40 | 41 | class ConnectingTrainViewController: UITableViewController { 42 | var station: Station! 43 | var connections: TrainConnections? 44 | let networkService = NetworkService(networkAccess: URLSession(configuration: .default)) 45 | let trainOnBoardAPI = TrainOnBoardAPI() 46 | 47 | override func viewDidLoad() { 48 | networkService.request(trainOnBoardAPI.connectionTrains(at: station), onCompletion: { connections in 49 | self.connections = connections 50 | self.tableView.reloadData() 51 | }, onError: { error in 52 | 53 | }) 54 | } 55 | 56 | override func numberOfSections(in tableView: UITableView) -> Int { 57 | return 1 58 | } 59 | 60 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 61 | return connections?.connections.count ?? 0 62 | } 63 | 64 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 65 | let cell = tableView.dequeueReusableCell(withIdentifier: "TrainConnectionCell", for: indexPath) 66 | if let connection = connections?.connections[indexPath.row] { 67 | cell.textLabel?.text = "\(dateFormatter.string(from: connection.schedule.departureTime)) \(connection.trainType) \(connection.trainNumber) nach \(connection.destination.name)" 68 | cell.detailTextLabel?.text = connection.track?.actualTrack ?? connection.track?.scheduledTrack 69 | } 70 | 71 | return cell 72 | } 73 | 74 | override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 75 | return "Anschlusszüge" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /ICEInformation/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | NSAppTransportSecurity 47 | 48 | NSAllowsArbitraryLoads 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /ICEInformation/ServiceViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 Lukas Schmidt. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | // DEALINGS IN THE SOFTWARE. 21 | // 22 | 23 | import UIKit 24 | import DBNetworkStack 25 | import ICEOnboardAPI 26 | import Sourcing 27 | 28 | 29 | class ServiceViewController: UITableViewController { 30 | 31 | let networkService = NetworkService(networkAccess: URLSession(configuration: .default)) 32 | var dataSource: TableViewDataSource! 33 | var dataProvider: ArrayDataProvider! 34 | let trainOnBoardAPI = TrainOnBoardAPI() 35 | 36 | override func viewDidLoad() { 37 | tableView.estimatedRowHeight = 55 38 | tableView.rowHeight = UITableViewAutomaticDimension 39 | let firstClassOffers = trainOnBoardAPI.firstClassOffers() 40 | let cell = CellConfiguration() 41 | dataProvider = ArrayDataProvider(rows: []) 42 | dataSource = TableViewDataSource(tableView: tableView, dataProvider: dataProvider, cell: cell) 43 | networkService.request(firstClassOffers, onCompletion: {[weak self] offers in 44 | self?.dataProvider.reconfigure(with: offers.food) 45 | }, onError: { err in 46 | print(err) 47 | }) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ICEInformation/StationOverviewViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StationOverviewViewController.swift 3 | // ICEInformation 4 | // 5 | // Created by Lukas Schmidt on 15.01.16. 6 | // Copyright © 2016 Lukas Schmidt. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import MapKit 11 | import ICEOnboardAPI 12 | import DBNetworkStack 13 | import Sourcing 14 | import NotificationCenter 15 | import UserNotifications 16 | 17 | 18 | class StationOverviewViewController: UIViewController { 19 | @IBOutlet weak var speedLabel: UILabel! 20 | @IBOutlet weak var mapView: MKMapView! 21 | @IBOutlet weak var tableView: UITableView! 22 | var trip: ICETrip? 23 | 24 | var dataProvider: ArrayDataProvider! 25 | var dataSource: TableViewDataSource! 26 | let trainOnBoardAPI = TrainOnBoardAPI() 27 | let networkService = NetworkService(networkAccess: URLSession(configuration: .default)) 28 | 29 | override func viewDidLoad() { 30 | setupDataSource() 31 | fetchData() 32 | fetchStatus() 33 | NCWidgetController().setHasContent(true, forWidgetWithBundleIdentifier: "de.freiraum.ICEInformation.ICEOnboardWidget") 34 | Timer.scheduledTimer(timeInterval: 30, target: self, selector: #selector(StationOverviewViewController.fetchData), userInfo: nil, repeats: true) 35 | Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(StationOverviewViewController.fetchStatus), userInfo: nil, repeats: true) 36 | } 37 | 38 | func setupDataSource() { 39 | dataProvider = ArrayDataProvider(rows: []) 40 | dataSource = TableViewDataSource(tableView: tableView, dataProvider: dataProvider, cell: CellConfiguration()) 41 | } 42 | 43 | func fetchStatus() { 44 | networkService.request(trainOnBoardAPI.status(), onCompletion: didSuceedStatusLoad, onError: { error in 45 | print(error) 46 | }) 47 | } 48 | 49 | func fetchData() { 50 | networkService.request(trainOnBoardAPI.trip(), onCompletion: didSuceedTripLoad, onError: { error in 51 | print(error) 52 | }) 53 | } 54 | 55 | func didSuceedStatusLoad(_ iceStatus: ICEStatus) { 56 | speedLabel.text = "\(iceStatus.speed)km/h" 57 | } 58 | 59 | func didSuceedTripLoad(_ iceTrip: ICETrip) { 60 | title = iceTrip.trainName 61 | mapView.addAnnotations(iceTrip.mapAnnotations) 62 | trip = iceTrip 63 | dataProvider.headerTitles = ["Vergangene Halte", "Kommende Halte"] 64 | dataProvider.reconfigure(with: [iceTrip.passedStops, iceTrip.commingStops]) 65 | guard let nextStop = iceTrip.nextStop, let indexPath = dataProvider.indexPath(for: nextStop) else { 66 | return 67 | } 68 | tableView.scrollToRow(at: indexPath, at: .top, animated: true) 69 | if #available(iOS 10.0, *) { 70 | scheduleNotification(at: nextStop) 71 | } else { 72 | // Fallback on earlier versions 73 | } 74 | } 75 | 76 | func registerNotifications() { 77 | 78 | } 79 | var registerdNextStop: Stop? 80 | @available(iOS 10.0, *) 81 | func scheduleNotification(at nextStop: Stop) { 82 | guard registerdNextStop != nextStop else { 83 | return 84 | } 85 | registerdNextStop = nextStop 86 | UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) {(accepted, error) in 87 | if !accepted { 88 | print("Notification access denied.") 89 | } 90 | } 91 | let category = UNNotificationCategory(identifier: "NextStop", actions: [], intentIdentifiers: [], options: .customDismissAction) 92 | UNUserNotificationCenter.current().setNotificationCategories([category]) 93 | let calendar = Calendar(identifier: .gregorian) 94 | let date = Date().addingTimeInterval(60) 95 | let components = calendar.dateComponents(in: .current, from: date) 96 | let newComponents = DateComponents(calendar: calendar, timeZone: .current, month: components.month, day: components.day, hour: components.hour, minute: components.minute, second: components.second) 97 | let trigger = UNCalendarNotificationTrigger(dateMatching: newComponents, repeats: false) 98 | let content = UNMutableNotificationContent() 99 | content.title = "Nächster Halt" 100 | content.body = "Um \(dateFormatter.string(from: nextStop.schduledTimes.arrivalTime.addingTimeInterval(nextStop.schduledTimes.arrivalDelay ?? 0))) erreichen wir \(nextStop.station.name)." 101 | 102 | content.categoryIdentifier = "NextStop" 103 | 104 | content.sound = UNNotificationSound.default() 105 | let request = UNNotificationRequest(identifier: "textNotification", content: content, trigger: trigger) 106 | networkService.request(trainOnBoardAPI.connectionTrains(at: nextStop.station), onCompletion: { connections in 107 | content.userInfo = ["evaId": try! nextStop.station.toJSON(), "payload": try! connections.toJSON()] 108 | UNUserNotificationCenter.current().removeAllPendingNotificationRequests() 109 | UNUserNotificationCenter.current().add(request) {(error) in 110 | if let error = error { 111 | print("Uh oh! We had an error: \(error)") 112 | } 113 | } 114 | }, onError: {_ in }) 115 | 116 | } 117 | 118 | 119 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 120 | if let stationViewController = segue.destination as? StationViewController { 121 | stationViewController.stop = dataSource.selectedObject 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /ICEInformation/StationViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StationViewController.swift 3 | // ICEInformation 4 | // 5 | // Created by Lukas Schmidt on 15.01.16. 6 | // Copyright © 2016 Lukas Schmidt. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ICEOnboardAPI 11 | import MapKit 12 | 13 | extension UIColor { 14 | static let delayedRed = UIColor(red: 178 / 255, green: 42 / 255, blue: 34 / 255, alpha: 1) 15 | 16 | static let inTimeGreen = UIColor(red: 93 / 255, green: 179 / 255, blue: 113 / 255, alpha: 1) 17 | } 18 | 19 | class StationViewController: UIViewController { 20 | var stop: Stop! 21 | 22 | @IBOutlet weak var mapView: MKMapView! 23 | @IBOutlet weak var trackLabel: UILabel! 24 | @IBOutlet weak var arrivalLabel: UILabel! 25 | @IBOutlet weak var arrivalDelayLabel: UILabel! 26 | @IBOutlet weak var depatureLabel: UILabel! 27 | @IBOutlet weak var departureDelayLabel: UILabel! 28 | 29 | override func viewDidLoad() { 30 | let dateFormatter = DateFormatter() 31 | dateFormatter.dateFormat = "HH:mm" 32 | if let mapAnotation = stop.station.mapAnnotation, let location = stop.station.location { 33 | mapView.addAnnotation(mapAnotation) 34 | let span = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta: 0.1) 35 | let region = MKCoordinateRegion(center: location.locationCoordinate, span: span) 36 | mapView.setRegion(region, animated: true) 37 | } 38 | 39 | title = stop.station.name 40 | 41 | trackLabel.text = stop.track?.scheduledTrack 42 | arrivalLabel.text = dateFormatter.string(from: stop.schduledTimes.arrivalTime) 43 | depatureLabel.text = dateFormatter.string(from: stop.schduledTimes.departureTime) 44 | setDelayTimeAt(lable: arrivalDelayLabel, delay: stop.schduledTimes.arrivalDelay) 45 | setDelayTimeAt(lable: departureDelayLabel, delay: stop.schduledTimes.depatureDelay) 46 | } 47 | 48 | func setDelayTimeAt(lable : UILabel, delay: TimeInterval?) { 49 | let delay = stop.schduledTimes.arrivalDelay ?? 0 50 | lable.text = "+\(Int(delay / 60))" 51 | lable.textColor = delay >= 5 ? .delayedRed : .inTimeGreen 52 | } 53 | 54 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 55 | if let connectionTrainsViewController = segue.destination as? ConnectingTrainViewController { 56 | connectionTrainsViewController.station = stop.station 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ICEInformation/TrainStationCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TrainStationCell.swift 3 | // ICEInformation 4 | // 5 | // Created by Lukas Schmidt on 15.01.16. 6 | // Copyright © 2016 Lukas Schmidt. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ICEOnboardAPI 11 | import Sourcing 12 | 13 | class TrainStationCell: UITableViewCell, ConfigurableCell { 14 | @IBOutlet weak var stationNameLabel: UILabel! 15 | @IBOutlet weak var delayLabel: UILabel! 16 | @IBOutlet weak var arrivalTimeLabel: UILabel! 17 | 18 | static let dateFormatter: DateFormatter = { 19 | let dateFormatter = DateFormatter() 20 | dateFormatter.dateFormat = "HH:mm" 21 | 22 | return dateFormatter 23 | }() 24 | 25 | func configure(with stop: Stop) { 26 | stationNameLabel.text = stop.station.name 27 | let delay = stop.schduledTimes.arrivalDelay ?? 0 28 | delayLabel.text = "+\(Int(delay / 60))" 29 | delayLabel.textColor = delay >= 5 ? .delayedRed : .inTimeGreen 30 | arrivalTimeLabel.text = TrainStationCell.dateFormatter.string(from: stop.schduledTimes.arrivalTime) 31 | } 32 | } 33 | 34 | extension TrainStationCell: CellIdentifierProviding {} 35 | -------------------------------------------------------------------------------- /ICEInformationiOS/Buyable+JSONSerialization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 Lukas Schmidt. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | // DEALINGS IN THE SOFTWARE. 21 | // 22 | 23 | import Foundation 24 | import JSONCodable 25 | 26 | extension Buyable: JSONDecodable { 27 | public init(object: JSONObject) throws { 28 | let decoder = JSONDecoder(object: object) 29 | price = try decoder.decode("priceEUR") 30 | title = try decoder.decode("title") 31 | let text: String? = try decoder.decode("text") 32 | description = text?.replacingOccurrences(of: "<[^>]+>", with: "", options: .regularExpression, range: nil) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ICEInformationiOS/Buyable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 Lukas Schmidt. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | // DEALINGS IN THE SOFTWARE. 21 | // 22 | 23 | import Foundation 24 | 25 | public struct Buyable { 26 | public let title: String 27 | public let description: String? 28 | public let price: String 29 | } 30 | 31 | -------------------------------------------------------------------------------- /ICEInformationiOS/ErrorType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorType.swift 3 | // ICEInformation 4 | // 5 | // Created by Lukas Schmidt on 28.12.15. 6 | // Copyright © 2015 Lukas Schmidt. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum ICEErrorType: Error { 12 | case wrongWifi 13 | case parse 14 | case network 15 | } 16 | -------------------------------------------------------------------------------- /ICEInformationiOS/FakeICENetworkAccess.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 Lukas Schmidt. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | // DEALINGS IN THE SOFTWARE. 21 | // 22 | // 23 | // FakeICENetworkAccess.swift 24 | // ICEInformation 25 | // 26 | // Created by Lukas Schmidt on 07.04.17. 27 | // 28 | 29 | import Foundation 30 | import DBNetworkStack 31 | 32 | let statusPayload = "{\"serverTime\":1449749986439,\"speed\":221.8000030517578,\"latitude\":49.404305,\"connection\":true,\"servicelevel\":\"SERVICE\",\"longitude\":8.547148}".data(using: .utf8) 33 | 34 | let tripInfoPayload = "{\"serverTime\":1449749986439,\"speed\":221.8000030517578,\"latitude\":49.404305,\"connection\":true,\"servicelevel\":\"SERVICE\",\"longitude\":8.547148}".data(using: .utf8) 35 | 36 | public class FakeICENetworkAccess: NetworkAccessProviding { 37 | 38 | public init() {} 39 | 40 | public func load(request: URLRequest, callback: @escaping (Data?, HTTPURLResponse?, Error?) -> Void) -> NetworkTaskRepresenting { 41 | guard let urlString = request.url?.absoluteString else { 42 | callback(nil, nil, nil) 43 | return NetworkTaskMock() 44 | } 45 | if urlString.contains("status") { 46 | callback(statusPayload, nil, nil) 47 | } else if urlString.contains("tripInfo") && !urlString.contains("connections") { 48 | 49 | } 50 | else { 51 | callback(nil, nil, nil) 52 | } 53 | return NetworkTaskMock() 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /ICEInformationiOS/FirstClassDeliverOffers+JSONSerialization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 Lukas Schmidt. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | // DEALINGS IN THE SOFTWARE. 21 | // 22 | 23 | import Foundation 24 | import JSONCodable 25 | 26 | extension FirstClassDeliverOffers: JSONDecodable { 27 | public init(object: JSONObject) throws { 28 | let decoder = JSONDecoder(object: object) 29 | food = try decoder.decode("articlesNested[0].items") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ICEInformationiOS/FirstClassDeliverOffers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 Lukas Schmidt. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | // DEALINGS IN THE SOFTWARE. 21 | // 22 | 23 | import Foundation 24 | 25 | public struct FirstClassDeliverOffers { 26 | public let food: [Buyable] 27 | } 28 | 29 | -------------------------------------------------------------------------------- /ICEInformationiOS/ICEInformationiOS.h: -------------------------------------------------------------------------------- 1 | // 2 | // ICEInformationiOS.h 3 | // ICEInformationiOS 4 | // 5 | // Created by Lukas Schmidt on 25.12.15. 6 | // Copyright © 2015 Lukas Schmidt. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for ICEInformationiOS. 12 | FOUNDATION_EXPORT double ICEInformationiOSVersionNumber; 13 | 14 | //! Project version string for ICEInformationiOS. 15 | FOUNDATION_EXPORT const unsigned char ICEInformationiOSVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /ICEInformationiOS/ICEStatus+JSONSerialization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 Lukas Schmidt. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | // DEALINGS IN THE SOFTWARE. 21 | // 22 | // 23 | // ICEStatus+JSONSerialization.swift 24 | // ICEInformation 25 | // 26 | // Created by Lukas Schmidt on 05.04.17. 27 | // 28 | 29 | import Foundation 30 | import JSONCodable 31 | 32 | extension ICEStatus: JSONDecodable { 33 | public init(object: JSONObject) throws { 34 | let decoder = JSONDecoder(object: object) 35 | speed = try decoder.decode("speed") 36 | location = try Location(object: object) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ICEInformationiOS/ICEStatus.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ICEStatus.swift 3 | // ICEInformation 4 | // 5 | // Created by Lukas Schmidt on 25.12.15. 6 | // Copyright © 2015 Lukas Schmidt. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct ICEStatus { 12 | public let location: Location 13 | public let speed: Float 14 | } -------------------------------------------------------------------------------- /ICEInformationiOS/ICETrip+JSONSerialization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 Lukas Schmidt. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | // DEALINGS IN THE SOFTWARE. 21 | // 22 | // 23 | // ICETrip+JSONSerialization.swift 24 | // ICEInformation 25 | // 26 | // Created by Lukas Schmidt on 05.04.17. 27 | // 28 | 29 | import Foundation 30 | import JSONCodable 31 | 32 | extension ICETrip: JSONDecodable { 33 | public init(object: JSONObject) throws { 34 | let decoder = JSONDecoder(object: object) 35 | self.trainType = try decoder.decode("trainType") 36 | self.trainNumber = try decoder.decode("vzn") 37 | self.stops = try decoder.decode("stops") 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ICEInformationiOS/ICETrip.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ICETripInformation.swift 3 | // ICEInformation 4 | // 5 | // Created by Lukas Schmidt on 25.12.15. 6 | // Copyright © 2015 Lukas Schmidt. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct ICETrip { 12 | public let trainType: String 13 | public let trainNumber: String 14 | public let stops: Array 15 | 16 | public var from: Stop { 17 | return stops.first! 18 | } 19 | 20 | public var to: Stop { 21 | return stops.last! 22 | } 23 | 24 | public var trainName: String { 25 | return trainType + " " + trainNumber 26 | } 27 | 28 | public var passedStops: [Stop] { 29 | return stops.filter { $0.passed } 30 | } 31 | 32 | public var commingStops: [Stop] { 33 | return stops.filter { !$0.passed } 34 | } 35 | 36 | public var nextStop: Stop? { 37 | return commingStops.first 38 | } 39 | 40 | public init(trainNumber: String, stops: [Stop], trainType: String) { 41 | if stops.count < 2 { 42 | fatalError("A trip must have at least 2 stops") 43 | } 44 | self.trainNumber = trainNumber 45 | self.stops = stops 46 | self.trainType = trainType 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ICEInformationiOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ICEInformationiOS/Location+CLLocation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Location+CLLocation.swift 3 | // ICEInformation 4 | // 5 | // Created by Lukas Schmidt on 26.12.15. 6 | // Copyright © 2015 Lukas Schmidt. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreLocation 11 | 12 | public extension Location { 13 | public var locationCoordinate: CLLocationCoordinate2D { 14 | return CLLocationCoordinate2D(latitude: latitude, longitude: longitude) 15 | } 16 | } -------------------------------------------------------------------------------- /ICEInformationiOS/Location+JSONSerialization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 Lukas Schmidt. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | // DEALINGS IN THE SOFTWARE. 21 | // 22 | // 23 | // Location+JSONSerialization.swift 24 | // ICEInformation 25 | // 26 | // Created by Lukas Schmidt on 05.04.17. 27 | // 28 | 29 | import Foundation 30 | import JSONCodable 31 | 32 | extension Location: JSONDecodable, JSONEncodable { 33 | public init(object: JSONObject) throws { 34 | let decoder = JSONDecoder(object: object) 35 | latitude = try decoder.decode("latitude") 36 | longitude = try decoder.decode("longitude") 37 | } 38 | 39 | public func toJSON() throws -> Any { 40 | return try JSONEncoder.create({ (encoder) -> Void in 41 | try encoder.encode(latitude, key: "latitude") 42 | try encoder.encode(longitude, key: "longitude") 43 | }) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ICEInformationiOS/Location.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Location.swift 3 | // ICEInformation 4 | // 5 | // Created by Lukas Schmidt on 08.01.16. 6 | // Copyright © 2016 Lukas Schmidt. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct Location { 12 | public let latitude: Double 13 | public let longitude: Double 14 | } 15 | -------------------------------------------------------------------------------- /ICEInformationiOS/Station+JSONSerialization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 Lukas Schmidt. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | // DEALINGS IN THE SOFTWARE. 21 | // 22 | // 23 | // Station+JSONSerialization.swift 24 | // ICEInformation 25 | // 26 | // Created by Lukas Schmidt on 05.04.17. 27 | // 28 | 29 | import Foundation 30 | import JSONCodable 31 | 32 | extension Station: JSONDecodable, JSONEncodable { 33 | public init(object: JSONObject) throws { 34 | let decoder = JSONDecoder(object: object) 35 | location = try decoder.decode("geocoordinates") 36 | evaId = try decoder.decode("evaNr") 37 | name = try decoder.decode("name") 38 | } 39 | 40 | public func toJSON() throws -> Any { 41 | return try JSONEncoder.create({ (encoder) -> Void in 42 | try encoder.encode(location, key: "geocoordinates") 43 | try encoder.encode(evaId, key: "evaNr") 44 | try encoder.encode(name, key: "name") 45 | }) 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /ICEInformationiOS/Station+MKAnnotation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Station+MKAnotation.swift 3 | // ICEInformation 4 | // 5 | // Created by Lukas Schmidt on 15.01.16. 6 | // Copyright © 2016 Lukas Schmidt. All rights reserved. 7 | // 8 | 9 | import MapKit 10 | 11 | extension Station { 12 | public var mapAnnotation: MKPointAnnotation? { 13 | guard let locationCoordinate = location?.locationCoordinate else { 14 | return nil 15 | } 16 | let dropPin = MKPointAnnotation() 17 | dropPin.coordinate = locationCoordinate 18 | dropPin.title = name 19 | 20 | return dropPin 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ICEInformationiOS/Station.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Station.swift 3 | // ICEInformation 4 | // 5 | // Created by Lukas Schmidt on 26.12.15. 6 | // Copyright © 2015 Lukas Schmidt. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct StationSchedule{ 12 | public let arrivalTime: Date 13 | public let departureTime: Date 14 | 15 | public let arrivalDelay: TimeInterval? 16 | public let depatureDelay: TimeInterval? 17 | } 18 | 19 | public struct Station { 20 | public let evaId: String 21 | public let name: String 22 | public let location: Location? 23 | } 24 | -------------------------------------------------------------------------------- /ICEInformationiOS/StationSchedule+JSONSerialization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 Lukas Schmidt. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | // DEALINGS IN THE SOFTWARE. 21 | // 22 | // 23 | // StationSchedule+JSONSerialization.swift 24 | // ICEInformation 25 | // 26 | // Created by Lukas Schmidt on 05.04.17. 27 | // 28 | 29 | import Foundation 30 | import JSONCodable 31 | 32 | extension StationSchedule: JSONDecodable, JSONEncodable { 33 | public init(object: JSONObject) throws { 34 | let decoder = JSONDecoder(object: object) 35 | let scheduledArrivalTime: Double? = try decoder.decode("scheduledArrivalTime") 36 | let scheduledDepartureTime: Double? = try decoder.decode("scheduledDepartureTime") 37 | 38 | arrivalTime = Date(timeIntervalSince1970: (scheduledArrivalTime ?? scheduledDepartureTime!) * 0.001) 39 | departureTime = Date(timeIntervalSince1970: (scheduledDepartureTime ?? scheduledArrivalTime!) * 0.001) 40 | 41 | arrivalDelay = extractDelay(try decoder.decode("arrivalDelay")) 42 | depatureDelay = extractDelay(try decoder.decode("departureDelay")) 43 | } 44 | 45 | public func toJSON() throws -> Any { 46 | return try JSONEncoder.create({ (encoder) -> Void in 47 | try encoder.encode(arrivalTime.timeIntervalSinceReferenceDate, key: "scheduledArrivalTime") 48 | try encoder.encode(departureTime.timeIntervalSinceReferenceDate, key: "scheduledArrivalTime") 49 | try encoder.encode(arrivalDelay, key: "arrivalDelay") 50 | try encoder.encode(depatureDelay, key: "depatureDelay") 51 | }) 52 | } 53 | } 54 | 55 | 56 | func extractDelay(_ delay:String?) -> TimeInterval? { 57 | guard let delay = delay, 58 | let delayTime = Double(delay.replacingOccurrences(of: "+", with: "")) else { return nil } 59 | return delayTime * 60 60 | } 61 | -------------------------------------------------------------------------------- /ICEInformationiOS/Stop+JSONSerialization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 Lukas Schmidt. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | // DEALINGS IN THE SOFTWARE. 21 | // 22 | // 23 | // Stop+JSONSerialization.swift 24 | // ICEInformation 25 | // 26 | // Created by Lukas Schmidt on 05.04.17. 27 | // 28 | 29 | import Foundation 30 | import JSONCodable 31 | 32 | extension Stop: JSONDecodable { 33 | public init(object: JSONObject) throws { 34 | let decoder = JSONDecoder(object: object) 35 | station = try decoder.decode("station") 36 | schduledTimes = try decoder.decode("timetable") 37 | track = try decoder.decode("track") 38 | passed = try decoder.decode("info.passed") 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ICEInformationiOS/Stop.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 Lukas Schmidt. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | // DEALINGS IN THE SOFTWARE. 21 | // 22 | // 23 | // Stop.swift 24 | // ICEInformation 25 | // 26 | // Created by Lukas Schmidt on 05.04.17. 27 | // 28 | 29 | import Foundation 30 | 31 | public struct Stop { 32 | public let station: Station 33 | public let passed: Bool 34 | public let schduledTimes: StationSchedule 35 | public let track: Track? 36 | } 37 | 38 | extension Stop: Equatable { } 39 | 40 | public func ==(lhs: Stop, rhs: Stop) -> Bool { 41 | return lhs.station.evaId == rhs.station.evaId 42 | } 43 | 44 | -------------------------------------------------------------------------------- /ICEInformationiOS/Track+JSONSerialization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 Lukas Schmidt. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | // DEALINGS IN THE SOFTWARE. 21 | // 22 | // 23 | // Track+JSONSerialization.swift 24 | // ICEInformation 25 | // 26 | // Created by Lukas Schmidt on 05.04.17. 27 | // 28 | 29 | import Foundation 30 | import JSONCodable 31 | 32 | extension Track: JSONDecodable, JSONEncodable { 33 | public init(object: JSONObject) throws { 34 | let decoder = JSONDecoder(object: object) 35 | scheduledTrack = try decoder.decode("scheduled") 36 | actualTrack = try decoder.decode("actual") 37 | } 38 | 39 | public func toJSON() throws -> Any { 40 | return try JSONEncoder.create({ (encoder) -> Void in 41 | try encoder.encode(scheduledTrack, key: "scheduled") 42 | try encoder.encode(actualTrack, key: "actual") 43 | }) 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /ICEInformationiOS/Track.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 Lukas Schmidt. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | // DEALINGS IN THE SOFTWARE. 21 | // 22 | // 23 | // Track.swift 24 | // ICEInformation 25 | // 26 | // Created by Lukas Schmidt on 05.04.17. 27 | // 28 | 29 | import Foundation 30 | 31 | public struct Track { 32 | public let actualTrack: String? 33 | public let scheduledTrack: String? 34 | } 35 | -------------------------------------------------------------------------------- /ICEInformationiOS/TrainConnection+JSONSerialization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 Lukas Schmidt. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | // DEALINGS IN THE SOFTWARE. 21 | // 22 | // 23 | // TrainConnection+JSONSerialization.swift 24 | // ICEInformation 25 | // 26 | // Created by Lukas Schmidt on 05.04.17. 27 | // 28 | 29 | import Foundation 30 | import JSONCodable 31 | 32 | extension TrainConnection: JSONDecodable, JSONEncodable { 33 | public init(object: JSONObject) throws { 34 | let decoder = JSONDecoder(object: object) 35 | trainType = try decoder.decode("trainType") 36 | vzn = try decoder.decode("vzn") 37 | let lineName: String? = try decoder.decode("lineName") 38 | trainNumber = (try decoder.decode("trainNumber") as String?) ?? lineName ?? "" 39 | schedule = try decoder.decode("timetable") 40 | track = try decoder.decode("track") 41 | destination = try decoder.decode("station") 42 | } 43 | 44 | public func toJSON() throws -> Any { 45 | return try JSONEncoder.create({ (encoder) -> Void in 46 | try encoder.encode(trainType, key: "trainType") 47 | try encoder.encode(vzn, key: "vzn") 48 | try encoder.encode(trainNumber, key: "trainNumber") 49 | try encoder.encode(schedule, key: "timetable") 50 | try encoder.encode(track, key: "track") 51 | try encoder.encode(destination, key: "destination") 52 | }) 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /ICEInformationiOS/TrainConnection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 Lukas Schmidt. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | // DEALINGS IN THE SOFTWARE. 21 | // 22 | // 23 | // TrainConnection.swift 24 | // ICEInformation 25 | // 26 | // Created by Lukas Schmidt on 05.04.17. 27 | // 28 | 29 | import Foundation 30 | 31 | public struct TrainConnections { 32 | public let connections: [TrainConnection] 33 | } 34 | 35 | 36 | 37 | public struct TrainConnection { 38 | public let trainType: String 39 | public let vzn: String 40 | public let trainNumber: String 41 | public let schedule: StationSchedule 42 | public let track: Track? 43 | public let destination: Station 44 | } 45 | -------------------------------------------------------------------------------- /ICEInformationiOS/TrainConnections+JSONSerialization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2017 Lukas Schmidt. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the "Software"), 6 | // to deal in the Software without restriction, including without limitation 7 | // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | // and/or sell copies of the Software, and to permit persons to whom the 9 | // Software is furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | // DEALINGS IN THE SOFTWARE. 21 | // 22 | // 23 | // TrainConnections+JSONSerialization.swift 24 | // ICEInformation 25 | // 26 | // Created by Lukas Schmidt on 05.04.17. 27 | // 28 | 29 | import Foundation 30 | import JSONCodable 31 | 32 | extension TrainConnections: JSONDecodable, JSONEncodable { 33 | public init(object: JSONObject) throws { 34 | let decoder = JSONDecoder(object: object) 35 | connections = try decoder.decode("connections") 36 | } 37 | 38 | public func toJSON() throws -> Any { 39 | return try JSONEncoder.create({ (encoder) -> Void in 40 | try encoder.encode(connections, key: "connections") 41 | }) 42 | 43 | } 44 | } 45 | 46 | 47 | -------------------------------------------------------------------------------- /ICEInformationiOS/TrainOnBoardAPI.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ICEStatusURLRessources.swift 3 | // ICEInformation 4 | // 5 | // Created by Lukas Schmidt on 26.12.15. 6 | // Copyright © 2015 Lukas Schmidt. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import DBNetworkStack 11 | 12 | let baseURLPortal = URL(string: "https://portal.imice.de/api1/rs/")! 13 | 14 | extension ICEStatus: JSONMappable { } 15 | extension ICETrip: JSONMappable { } 16 | extension TrainConnections: JSONMappable { } 17 | extension FirstClassDeliverOffers: JSONMappable { } 18 | 19 | public final class TrainOnBoardAPI { 20 | 21 | private let baseURL: URL 22 | 23 | public init(baseURL: URL = baseURLPortal) { 24 | self.baseURL = baseURL 25 | } 26 | 27 | public func status() -> Resource { 28 | let request = URLRequest(path: "status", baseURL: baseURL) 29 | 30 | return Resource(resource: JSONResource(request: request)) 31 | } 32 | 33 | public func trip() -> Resource { 34 | let request = URLRequest(path: "tripInfo", baseURL: baseURL) 35 | 36 | return Resource(resource: JSONResource(request: request)) 37 | } 38 | 39 | public func connectionTrains(at station: Station) -> Resource { 40 | return connectionTrains(for: station.evaId) 41 | } 42 | 43 | public func connectionTrains(for evaId: String) -> Resource { 44 | let request = URLRequest(path: "tripInfo/connection/\(evaId)", baseURL: baseURL) 45 | 46 | return Resource(resource: JSONResource(request: request)) 47 | } 48 | 49 | public func firstClassOffers() -> Resource { 50 | let request = URLRequest(path: "filterTop/1259026820/0", baseURL: baseURL) 51 | 52 | return Resource(resource: JSONResource(request: request)) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /ICEInformationiOS/Trip+MKAnnotations.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Trip+MKAnnotations.swift 3 | // ICEInformation 4 | // 5 | // Created by Lukas Schmidt on 15.01.16. 6 | // Copyright © 2016 Lukas Schmidt. All rights reserved. 7 | // 8 | 9 | import MapKit 10 | 11 | extension ICETrip { 12 | public var mapAnnotations: [MKPointAnnotation] { 13 | return stops.map { $0.station.mapAnnotation }.flatMap { $0 } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ICEInformationiOSTests/ICEInformationiOSTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ICEInformationiOSTests.swift 3 | // ICEInformationiOSTests 4 | // 5 | // Created by Lukas Schmidt on 25.12.15. 6 | // Copyright © 2015 Lukas Schmidt. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import ICEOnboardAPI 11 | 12 | class ICEInformationiOSTests: XCTestCase { 13 | 14 | func testParseICEStatus() { 15 | let data = statusDataString.data(using: String.Encoding.utf8)! 16 | guard let json = try! JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] else { 17 | XCTFail() 18 | return 19 | } 20 | 21 | let iceStatus = try! ICEStatus(object: json) 22 | XCTAssertEqual(iceStatus.location.latitude, 49.404305) 23 | XCTAssertEqual(iceStatus.location.longitude, 8.547148) 24 | XCTAssertEqual(iceStatus.speed, 221.8000030517578) 25 | } 26 | 27 | func testParseICETripInformation() { 28 | let data = tripInforationDataString.data(using: .utf8)! 29 | guard let json = try! JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] else { 30 | XCTFail() 31 | return 32 | } 33 | let tripInfo = try! ICETrip(object: json) 34 | XCTAssertEqual(tripInfo.trainNumber, "108") 35 | XCTAssertEqual(tripInfo.trainType, "ICE") 36 | XCTAssertEqual(tripInfo.stops.count, 10) 37 | if let firstStop = tripInfo.stops.first { 38 | XCTAssertEqual(firstStop.track, "12") 39 | XCTAssertEqual(firstStop.name, "Basel SBB") 40 | XCTAssertEqual(firstStop.evaNr, "8500010_00") 41 | XCTAssertEqual(firstStop.location.longitude, 7.589169) 42 | XCTAssertEqual(firstStop.location.latitude, 47.547077) 43 | XCTAssertEqual(firstStop.schduledTimes.departureTime, Date(timeIntervalSince1970: 1449742380000 * 0.001)) 44 | } 45 | let freiburg = tripInfo.stops[2] 46 | XCTAssertEqual(freiburg.schduledTimes.depatureDelay, 60) 47 | XCTAssertTrue(freiburg.passed) 48 | XCTAssertEqual(freiburg.track, "1") 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /ICEInformationiOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ICEInformationiOSTests/TestData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestData.swift 3 | // ICEInformation 4 | // 5 | // Created by Lukas Schmidt on 26.12.15. 6 | // Copyright © 2015 Lukas Schmidt. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | let statusDataString = "{\"connection\":true,\"servicelevel\":\"SERVICE\",\"speed\":221.8000030517578,\"longitude\":8.547148,\"latitude\":49.404305,\"serverTime\":1449749986439}" 12 | 13 | let tripInforationDataString = "{\"tripDate\":\"2015-12-10\",\"trainType\":\"ICE\",\"vzn\":\"108\",\"actualPosition\":173933,\"distanceFromLastStop\":45828,\"totalDistance\":475988,\"stopInfo\":{\"scheduledNext\":\"8000244_00\",\"actualNext\":\"8000244_00\",\"actualLast\":\"8000191_00\",\"actualLastStarted\":\"8000244\",\"finalStationName\":\"Düsseldorf Hbf\",\"finalStationEvaNr\":\"8000085_00\"},\"stops\":[{\"station\":{\"evaNr\":\"8500010_00\",\"name\":\"Basel SBB\",\"geocoordinates\":{\"latitude\":47.547077,\"longitude\":7.589169}},\"timetable\":{\"scheduledArrivalTime\":null,\"actualArrivalTime\":null,\"arrivalDelay\":\"\",\"scheduledDepartureTime\":1449742380000,\"actualDepartureTime\":1449742380000,\"departureDelay\":\"\"},\"track\":{\"scheduled\":\"12\",\"actual\":\"\"},\"info\":{\"status\":0,\"passed\":true,\"distance\":0,\"distanceFromStart\":0}},{\"station\":{\"evaNr\":\"8000026_00\",\"name\":\"Basel Bad Bf\",\"geocoordinates\":{\"latitude\":47.567288,\"longitude\":7.607805}},\"timetable\":{\"scheduledArrivalTime\":1449742740000,\"actualArrivalTime\":1449742800000,\"arrivalDelay\":\"+1\",\"scheduledDepartureTime\":1449742980000,\"actualDepartureTime\":1449742980000,\"departureDelay\":\"\"},\"track\":{\"scheduled\":\"4\",\"actual\":\"\"},\"info\":{\"status\":0,\"passed\":true,\"distance\":2648,\"distanceFromStart\":2648}},{\"station\":{\"evaNr\":\"8000107_00\",\"name\":\"Freiburg(Breisgau) Hbf\",\"geocoordinates\":{\"latitude\":47.997697,\"longitude\":7.84117}},\"timetable\":{\"scheduledArrivalTime\":1449744900000,\"actualArrivalTime\":1449744900000,\"arrivalDelay\":\"\",\"scheduledDepartureTime\":1449745020000,\"actualDepartureTime\":1449745080000,\"departureDelay\":\"+1\"},\"track\":{\"scheduled\":\"1\",\"actual\":\"\"},\"info\":{\"status\":0,\"passed\":true,\"distance\":50951,\"distanceFromStart\":53599}},{\"station\":{\"evaNr\":\"8000290_00\",\"name\":\"Offenburg\",\"geocoordinates\":{\"latitude\":48.476475,\"longitude\":7.946723}},\"timetable\":{\"scheduledArrivalTime\":1449746880000,\"actualArrivalTime\":1449746880000,\"arrivalDelay\":\"\",\"scheduledDepartureTime\":1449747000000,\"actualDepartureTime\":1449747060000,\"departureDelay\":\"+1\"},\"track\":{\"scheduled\":\"3\",\"actual\":\"\"},\"info\":{\"status\":0,\"passed\":true,\"distance\":53824,\"distanceFromStart\":107423}},{\"station\":{\"evaNr\":\"8000191_00\",\"name\":\"Karlsruhe Hbf\",\"geocoordinates\":{\"latitude\":48.993515,\"longitude\":8.402181}},\"timetable\":{\"scheduledArrivalTime\":1449748680000,\"actualArrivalTime\":1449748800000,\"arrivalDelay\":\"+2\",\"scheduledDepartureTime\":1449748800000,\"actualDepartureTime\":1449748980000,\"departureDelay\":\"+3\"},\"track\":{\"scheduled\":\"3\",\"actual\":\"4\"},\"info\":{\"status\":0,\"passed\":true,\"distance\":66510,\"distanceFromStart\":173933}},{\"station\":{\"evaNr\":\"8000244_00\",\"name\":\"Mannheim Hbf\",\"geocoordinates\":{\"latitude\":49.479354,\"longitude\":8.468921}},\"timetable\":{\"scheduledArrivalTime\":1449750180000,\"actualArrivalTime\":1449750420000,\"arrivalDelay\":\"+4\",\"scheduledDepartureTime\":1449750960000,\"actualDepartureTime\":1449750960000,\"departureDelay\":\"\"},\"track\":{\"scheduled\":\"3\",\"actual\":\"\"},\"info\":{\"status\":0,\"passed\":false,\"distance\":54255,\"distanceFromStart\":228188}},{\"station\":{\"evaNr\":\"8070003_00\",\"name\":\"Frankfurt(M) Flughafen Fernbf\",\"geocoordinates\":{\"latitude\":50.053167,\"longitude\":8.570185}},\"timetable\":{\"scheduledArrivalTime\":1449752760000,\"actualArrivalTime\":1449752760000,\"arrivalDelay\":\"\",\"scheduledDepartureTime\":1449752940000,\"actualDepartureTime\":1449752940000,\"departureDelay\":\"\"},\"track\":{\"scheduled\":\"Fern 6\",\"actual\":\"\"},\"info\":{\"status\":0,\"passed\":false,\"distance\":64236,\"distanceFromStart\":292424}},{\"station\":{\"evaNr\":\"8005556_00\",\"name\":\"Siegburg/Bonn\",\"geocoordinates\":{\"latitude\":50.793915,\"longitude\":7.203026}},\"timetable\":{\"scheduledArrivalTime\":1449755220000,\"actualArrivalTime\":1449755220000,\"arrivalDelay\":\"\",\"scheduledDepartureTime\":1449755340000,\"actualDepartureTime\":1449755340000,\"departureDelay\":\"\"},\"track\":{\"scheduled\":\"3\",\"actual\":\"\"},\"info\":{\"status\":0,\"passed\":false,\"distance\":127174,\"distanceFromStart\":419598}},{\"station\":{\"evaNr\":\"8073368_00\",\"name\":\"Köln Messe/Deutz Gl.11-12\",\"geocoordinates\":{\"latitude\":50.941721,\"longitude\":6.974067}},\"timetable\":{\"scheduledArrivalTime\":1449756180000,\"actualArrivalTime\":1449756180000,\"arrivalDelay\":\"\",\"scheduledDepartureTime\":1449756360000,\"actualDepartureTime\":1449756360000,\"departureDelay\":\"\"},\"track\":{\"scheduled\":\"12\",\"actual\":\"\"},\"info\":{\"status\":0,\"passed\":false,\"distance\":22991,\"distanceFromStart\":442589}},{\"station\":{\"evaNr\":\"8000085_00\",\"name\":\"Düsseldorf Hbf\",\"geocoordinates\":{\"latitude\":51.219962,\"longitude\":6.794319}},\"timetable\":{\"scheduledArrivalTime\":1449757620000,\"actualArrivalTime\":1449757620000,\"arrivalDelay\":\"\",\"scheduledDepartureTime\":null,\"actualDepartureTime\":null,\"departureDelay\":\"\"},\"track\":{\"scheduled\":\"17\",\"actual\":\"\"},\"info\":{\"status\":0,\"passed\":false,\"distance\":33399,\"distanceFromStart\":475988}}]}" -------------------------------------------------------------------------------- /ICEOnboardWidget/Base.lproj/MainInterface.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 34 | 35 | 36 | 37 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 55 | 61 | 62 | 63 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /ICEOnboardWidget/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ICEOnboardWidget 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | NSExtension 24 | 25 | NSExtensionMainStoryboard 26 | MainInterface 27 | NSExtensionPointIdentifier 28 | com.apple.widget-extension 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /ICEOnboardWidget/TodayViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TodayViewController.swift 3 | // ICEOnboardWidget 4 | // 5 | // Created by Lukas Schmidt on 07.04.17. 6 | // Copyright © 2017 Lukas Schmidt. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import NotificationCenter 11 | import DBNetworkStack 12 | import ICEOnboardAPI 13 | 14 | 15 | class TodayViewController: UIViewController, NCWidgetProviding { 16 | @IBOutlet weak var speedLabel: UILabel! 17 | @IBOutlet weak var destinationLabel: UILabel! 18 | @IBOutlet weak var nextStopArrivalLabel: UILabel! 19 | @IBOutlet weak var nextStopStationNameLabel: UILabel! 20 | @IBOutlet weak var nextStopPlatformLabel: UILabel! 21 | 22 | let widgetController: NCWidgetController? = nil 23 | let widgetBundleIDentifier = "de.freiraum.ICEInformation.ICEOnboardWidget" 24 | let trainOnBoardAPI = TrainOnBoardAPI() 25 | 26 | let networkService: NetworkServiceProviding = NetworkService(networkAccess: URLSession(configuration: .default)) 27 | var timer: Timer? 28 | 29 | static let dateFormatter: DateFormatter = { 30 | let dateFormatter = DateFormatter() 31 | dateFormatter.dateFormat = "HH:mm" 32 | 33 | return dateFormatter 34 | }() 35 | 36 | override func viewDidLoad() { 37 | super.viewDidLoad() 38 | 39 | networkService.request(trainOnBoardAPI.trip(), onCompletion: { trip in 40 | self.setICETripUI(trip: trip) 41 | 42 | self.widgetController?.setHasContent(true, forWidgetWithBundleIdentifier: self.widgetBundleIDentifier) 43 | 44 | }, onError: { err in 45 | self.widgetController?.setHasContent(false, forWidgetWithBundleIdentifier: self.widgetBundleIDentifier) 46 | print(err) 47 | 48 | }) 49 | } 50 | 51 | func setICETripUI(trip: ICETrip) { 52 | destinationLabel.text = "\(trip.trainName) nach \(trip.to.station.name)" 53 | let nextStop = trip.commingStops.first 54 | let arrivaleDate = nextStop?.schduledTimes.arrivalTime 55 | nextStopArrivalLabel.text = arrivaleDate.map { TodayViewController.dateFormatter.string(from: $0)}.map { $0 + (nextStop?.schduledTimes.depatureDelay.map {" +\(Int($0 / 60))" } ?? "") } 56 | nextStopStationNameLabel.text = nextStop?.station.name 57 | nextStopPlatformLabel.text = (nextStop?.track?.actualTrack ?? nextStop?.track?.scheduledTrack).map { "Gleis " + $0 } 58 | } 59 | 60 | func reloadInformation(onCompletion doneCallback: @escaping (Bool) -> Void) { 61 | networkService.request(trainOnBoardAPI.status(), onCompletion: { status in 62 | self.speedLabel.text = "\(Int(status.speed)) km/h" 63 | doneCallback(true) 64 | }, onError: { err in 65 | print(err) 66 | doneCallback(false) 67 | }) 68 | 69 | networkService.request(trainOnBoardAPI.trip(), onCompletion: { trip in 70 | self.setICETripUI(trip: trip) 71 | 72 | self.widgetController?.setHasContent(true, forWidgetWithBundleIdentifier: self.widgetBundleIDentifier) 73 | doneCallback(true) 74 | 75 | }, onError: { err in 76 | self.widgetController?.setHasContent(false, forWidgetWithBundleIdentifier: self.widgetBundleIDentifier) 77 | print(err) 78 | doneCallback(false) 79 | }) 80 | } 81 | 82 | @IBAction func didTapNextStation(_ sender: Any) { 83 | //extensionContext?.open(<#T##URL: URL##URL#>, completionHandler: nil) 84 | } 85 | 86 | func widgetPerformUpdate(completionHandler: (@escaping (NCUpdateResult) -> Void)) { 87 | print("widget perform update") 88 | 89 | timer = Timer(timeInterval: 5, repeats: true, block: { timer in 90 | self.reloadInformation(onCompletion: { success in 91 | if success { 92 | completionHandler(.newData) 93 | } else { 94 | completionHandler(.failed) 95 | } 96 | }) 97 | }) 98 | RunLoop.main.add(timer!, forMode: .commonModes) 99 | } 100 | 101 | override func viewDidAppear(_ animated: Bool) { 102 | 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Lukas Schmidt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /NextStopNotification/Base.lproj/MainInterface.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 60 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 89 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /NextStopNotification/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | NextStopNotification 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | NSExtension 24 | 25 | NSExtensionAttributes 26 | 27 | UNNotificationExtensionCategory 28 | NextStop 29 | UNNotificationExtensionInitialContentSizeRatio 30 | 1 31 | 32 | NSExtensionMainStoryboard 33 | MainInterface 34 | NSExtensionPointIdentifier 35 | com.apple.usernotifications.content-extension 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /NextStopNotification/NotificationViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationViewController.swift 3 | // NextStopNotification 4 | // 5 | // Created by Lukas Schmidt on 24.04.17. 6 | // Copyright © 2017 Lukas Schmidt. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import UserNotifications 11 | import UserNotificationsUI 12 | import DBNetworkStack 13 | import ICEOnboardAPI 14 | import JSONCodable 15 | 16 | class NotificationViewController: UIViewController, UNNotificationContentExtension { 17 | 18 | @IBOutlet var label: UILabel? 19 | let networkService: NetworkServiceProviding = NetworkService(networkAccess: URLSession(configuration: .default)) 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | // Do any required interface initialization here. 24 | } 25 | 26 | func didReceive(_ notification: UNNotification) { 27 | print("hallo") 28 | guard let object = notification.request.content.userInfo["payload"] as? [String: Any] else { return } 29 | let connection = try! TrainConnections(object: object) 30 | label?.text = connection.connections.first?.destination.name 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unoffical InTrain ICE API 2 | 3 | 4 | This is the reverse engineered API for the ICE-Portal running in most of the ICE trains in Germany. This is only a Demo. Do not use it in production. This API is not offical! 5 | 6 | ## Usage 7 | Use the unfinisehd iOS framework or run the demo app. Carthage may work as well. The API is only available on InTrain-WiFi. 8 | 9 | ## REST-API 10 | If you are only interestst in the REST-API, look at [this document](https://github.com/lightsprint09/InTrain-ICE-API/blob/master/DemoPayload.md) to find URLs and example payloads. 11 | 12 | ##Contibution 13 | If you want to improve this even further make a Pull Request. It would be nice to write tests as well. 14 | -------------------------------------------------------------------------------- /Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightsprint09/ICEOnboardAPI/05ebda2d3719e3f2d8305784d6ca0a1470494965/Screenshot.png --------------------------------------------------------------------------------