├── MyTravelHelper
├── Assets.xcassets
│ ├── Contents.json
│ ├── train.imageset
│ │ ├── train@1X.png
│ │ ├── train@2X.png
│ │ ├── train@3X.png
│ │ └── Contents.json
│ ├── transitArrow.imageset
│ │ ├── arrow@2x.jpg
│ │ ├── arrow@3x.jpg
│ │ ├── arrow@2x-1.jpg
│ │ └── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── App-utils
│ ├── AppConstants.swift
│ ├── AppUtils.swift
│ ├── ProgressIndicator.swift
│ ├── Extension.swift
│ └── Reachability.swift
├── AppDelegate.swift
├── Modules
│ └── SearchTrains
│ │ ├── Router
│ │ └── SearchTrainRouter.swift
│ │ ├── View
│ │ ├── TrainInfoCell.swift
│ │ └── SearchTrainViewController.swift
│ │ ├── Protocols
│ │ └── SearchTrainProtocols.swift
│ │ ├── Presenter
│ │ └── SearchTrainPresenter.swift
│ │ ├── Entity
│ │ ├── Stations.swift
│ │ ├── TrainMovements.swift
│ │ └── StationInfo.swift
│ │ ├── Repository
│ │ └── Client.swift
│ │ └── Interactor
│ │ └── SearchTrainInteractor.swift
├── Info.plist
└── App-Ui
│ └── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── MyTravelHelper.xcworkspace
├── xcuserdata
│ ├── satish.xcuserdatad
│ │ ├── xcdebugger
│ │ │ └── Breakpoints_v2.xcbkptlist
│ │ └── UserInterfaceState.xcuserstate
│ ├── navroz.xcuserdatad
│ │ ├── UserInterfaceState.xcuserstate
│ │ └── xcdebugger
│ │ │ └── Breakpoints_v2.xcbkptlist
│ ├── jmfi0001.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
│ ├── navrozsingh.xcuserdatad
│ │ ├── UserInterfaceState.xcuserstate
│ │ └── xcdebugger
│ │ │ └── Breakpoints_v2.xcbkptlist
│ └── vijay.ghooli.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
├── contents.xcworkspacedata
└── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── MyTravelHelper.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcuserdata
│ │ └── satish.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcuserdata
│ ├── jmfi0001.xcuserdatad
│ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ ├── navroz.xcuserdatad
│ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ ├── satish.xcuserdatad
│ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ ├── navrozsingh.xcuserdatad
│ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ └── vijay.ghooli.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── project.pbxproj
├── Podfile
├── MyTravelHelperTests
├── Info.plist
├── SearchTrainInteractorsTest.swift
├── SearchTrainPresenterTests.swift
└── MockResponses.swift
├── Podfile.lock
└── readme.MD
/MyTravelHelper/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/MyTravelHelper/Assets.xcassets/train.imageset/train@1X.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/navrozSingh/TechTest/HEAD/MyTravelHelper/Assets.xcassets/train.imageset/train@1X.png
--------------------------------------------------------------------------------
/MyTravelHelper/Assets.xcassets/train.imageset/train@2X.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/navrozSingh/TechTest/HEAD/MyTravelHelper/Assets.xcassets/train.imageset/train@2X.png
--------------------------------------------------------------------------------
/MyTravelHelper/Assets.xcassets/train.imageset/train@3X.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/navrozSingh/TechTest/HEAD/MyTravelHelper/Assets.xcassets/train.imageset/train@3X.png
--------------------------------------------------------------------------------
/MyTravelHelper/Assets.xcassets/transitArrow.imageset/arrow@2x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/navrozSingh/TechTest/HEAD/MyTravelHelper/Assets.xcassets/transitArrow.imageset/arrow@2x.jpg
--------------------------------------------------------------------------------
/MyTravelHelper/Assets.xcassets/transitArrow.imageset/arrow@3x.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/navrozSingh/TechTest/HEAD/MyTravelHelper/Assets.xcassets/transitArrow.imageset/arrow@3x.jpg
--------------------------------------------------------------------------------
/MyTravelHelper/Assets.xcassets/transitArrow.imageset/arrow@2x-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/navrozSingh/TechTest/HEAD/MyTravelHelper/Assets.xcassets/transitArrow.imageset/arrow@2x-1.jpg
--------------------------------------------------------------------------------
/MyTravelHelper.xcworkspace/xcuserdata/satish.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/MyTravelHelper.xcworkspace/xcuserdata/navroz.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/navrozSingh/TechTest/HEAD/MyTravelHelper.xcworkspace/xcuserdata/navroz.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/MyTravelHelper.xcworkspace/xcuserdata/satish.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/navrozSingh/TechTest/HEAD/MyTravelHelper.xcworkspace/xcuserdata/satish.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/MyTravelHelper.xcworkspace/xcuserdata/jmfi0001.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/navrozSingh/TechTest/HEAD/MyTravelHelper.xcworkspace/xcuserdata/jmfi0001.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/MyTravelHelper.xcworkspace/xcuserdata/navrozsingh.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/navrozSingh/TechTest/HEAD/MyTravelHelper.xcworkspace/xcuserdata/navrozsingh.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/MyTravelHelper.xcworkspace/xcuserdata/vijay.ghooli.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/navrozSingh/TechTest/HEAD/MyTravelHelper.xcworkspace/xcuserdata/vijay.ghooli.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/MyTravelHelper.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/MyTravelHelper.xcodeproj/project.xcworkspace/xcuserdata/satish.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/navrozSingh/TechTest/HEAD/MyTravelHelper.xcodeproj/project.xcworkspace/xcuserdata/satish.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/MyTravelHelper/App-utils/AppConstants.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppConstants.swift
3 | // MyTravelHelper
4 | //
5 | // Created by Satish on 11/03/19.
6 | // Copyright © 2019 Sample. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | let PROGRESS_INDICATOR_VIEW_TAG: Int = 10
12 |
--------------------------------------------------------------------------------
/MyTravelHelper.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/MyTravelHelper.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/MyTravelHelper.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/MyTravelHelper.xcodeproj/xcuserdata/jmfi0001.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | MyTravelHelper.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 6
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/MyTravelHelper.xcodeproj/xcuserdata/navroz.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | MyTravelHelper.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 5
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/MyTravelHelper.xcodeproj/xcuserdata/satish.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | MyTravelHelper.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 6
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/MyTravelHelper.xcodeproj/xcuserdata/navrozsingh.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | MyTravelHelper.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 1
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/MyTravelHelper.xcodeproj/xcuserdata/vijay.ghooli.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | MyTravelHelper.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/MyTravelHelper/Assets.xcassets/train.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "train@1X.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "train@2X.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "train@3X.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/MyTravelHelper/Assets.xcassets/transitArrow.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "arrow@2x-1.jpg",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "arrow@2x.jpg",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "arrow@3x.jpg",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment the next line to define a global platform for your project
2 | # platform :ios, '9.0'
3 |
4 | target 'MyTravelHelper' do
5 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
6 | use_frameworks!
7 |
8 | # Pods for MyTravelHelper
9 | pod 'XMLParsing', :git => 'https://github.com/ShawnMoore/XMLParsing.git'
10 | #pod 'Alamofire', '~> 4.3'
11 | pod 'DropDown'
12 | pod 'SwiftSpinner'
13 |
14 | target 'MyTravelHelperTests' do
15 | inherit! :search_paths
16 | # Pods for testing
17 | end
18 |
19 | end
20 |
--------------------------------------------------------------------------------
/MyTravelHelper/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // MyTravelHelper
4 | //
5 | // Created by Satish on 11/03/19.
6 | // Copyright © 2019 Sample. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 | var window: UIWindow?
14 |
15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
16 | let notice = SearchTrainRouter.createModule()
17 | window = UIWindow(frame: UIScreen.main.bounds)
18 | window?.rootViewController = UINavigationController(rootViewController: notice)
19 | window?.makeKeyAndVisible()
20 | return false
21 | }
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/MyTravelHelperTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/MyTravelHelper/App-utils/AppUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppUtils.swift
3 | // MyTravelHelper
4 | //
5 | // Created by Satish on 11/03/19.
6 | // Copyright © 2019 Sample. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UIKit
11 |
12 | /* Show Progress Indicator */
13 | func showProgressIndicator(view:UIView){
14 | view.isUserInteractionEnabled = false
15 | let progressIndicator = ProgressIndicator(text: "Please wait..")
16 | progressIndicator.tag = PROGRESS_INDICATOR_VIEW_TAG
17 | view.addSubview(progressIndicator)
18 | }
19 |
20 | /* Hide progress Indicator */
21 | func hideProgressIndicator(view:UIView){
22 | view.isUserInteractionEnabled = true
23 |
24 | if let viewWithTag = view.viewWithTag(PROGRESS_INDICATOR_VIEW_TAG) {
25 | viewWithTag.removeFromSuperview()
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - DropDown (2.3.12)
3 | - SwiftSpinner (1.6.2)
4 | - XMLParsing (0.0.3)
5 |
6 | DEPENDENCIES:
7 | - DropDown
8 | - SwiftSpinner
9 | - XMLParsing (from `https://github.com/ShawnMoore/XMLParsing.git`)
10 |
11 | SPEC REPOS:
12 | https://github.com/CocoaPods/Specs.git:
13 | - DropDown
14 | - SwiftSpinner
15 |
16 | EXTERNAL SOURCES:
17 | XMLParsing:
18 | :git: https://github.com/ShawnMoore/XMLParsing.git
19 |
20 | CHECKOUT OPTIONS:
21 | XMLParsing:
22 | :commit: 594272e4df40798c53ae1735ee65f3f387291670
23 | :git: https://github.com/ShawnMoore/XMLParsing.git
24 |
25 | SPEC CHECKSUMS:
26 | DropDown: 0bcc278799ec11a3d34ce1aac9367fbbaf5423d9
27 | SwiftSpinner: 3a4c6dea425e5a31b908a8b1dd5dcd1ad4d96340
28 | XMLParsing: 085edb6fa22c71d6b53a79299f990a91e7414d19
29 |
30 | PODFILE CHECKSUM: 42811fdc070b391d438395b04a81798f692ba318
31 |
32 | COCOAPODS: 1.9.1
33 |
--------------------------------------------------------------------------------
/MyTravelHelper/Modules/SearchTrains/Router/SearchTrainRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchTrainRouter.swift
3 | // MyTravelHelper
4 | //
5 | // Created by Satish on 11/03/19.
6 | // Copyright © 2019 Sample. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | class SearchTrainRouter: PresenterToRouterProtocol {
11 | static func createModule() -> SearchTrainViewController {
12 | let view = UIStoryboard.mainstoryboard.instantiateViewController(withIdentifier: "searchTrain") as! SearchTrainViewController
13 | let presenter: ViewToPresenterProtocol & InteractorToPresenterProtocol = SearchTrainPresenter()
14 | let interactor: PresenterToInteractorProtocol = SearchTrainInteractor()
15 | let router:PresenterToRouterProtocol = SearchTrainRouter()
16 | let networkClient: Client = TrainClient()
17 |
18 | view.presenter = presenter
19 | presenter.view = view
20 | presenter.router = router
21 | presenter.interactor = interactor
22 | interactor.presenter = presenter
23 | interactor.networkClient = networkClient
24 |
25 | return view
26 | }
27 |
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/readme.MD:
--------------------------------------------------------------------------------
1 | ## Below are the changes I have made to project in order to achieve exercise GOALS
2 |
3 | ## In Appdelegate I have following changes
4 | - [x] added Navigationbar
5 | - [x] Setup ‘window’ properly
6 |
7 | ## Then I have made few changes Entities.
8 | - [x] Added a static method to get URLREQUEST
9 | - [x] function to save ‘StationTrain’ to defaults
10 |
11 | ## Then I moved to ‘SearchTrainInteractor’ and made the following changes
12 | - [x] removed amlofire.
13 | - [x] Implemented ‘URLSession’.
14 | - [x] Managed multiple requests in ‘proceesTrainListforDestinationCheck’.
15 | - [x] Segregated code
16 | - [x] Added favourite functionality.
17 |
18 | ## In Presenter, Protocols, Router .
19 | - [x] only removed boilerplate code to with generic functions.
20 |
21 | ## After that I code the view part in ‘SearchTrainViewController.swift’
22 |
23 | - [x] Changed to cell handling.
24 | - [x] handled Favourite functionality via closure
25 | - [x] Changed view component to run on main thread.
26 |
27 | ## Lastly in ‘SearchTrainPresenterTests’ added few test
28 |
29 | ###### Newly implemented tasks
30 | - [x] Implemented the network adapter layer.
31 | - [x] Wrote test cases for interactor
32 |
--------------------------------------------------------------------------------
/MyTravelHelper/Modules/SearchTrains/View/TrainInfoCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TrainInfoswift
3 | // MyTravelHelper
4 | //
5 | // Created by Satish on 11/03/19.
6 | // Copyright © 2019 Sample. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class TrainInfoCell: UITableViewCell {
12 | @IBOutlet weak var destinationTimeLabel: UILabel!
13 | @IBOutlet weak var sourceTimeLabel: UILabel!
14 | @IBOutlet weak var destinationInfoLabel: UILabel!
15 | @IBOutlet weak var souceInfoLabel: UILabel!
16 | @IBOutlet weak var trainCode: UILabel!
17 | var favStationTrain: ((StationTrain)->())?
18 | var train: StationTrain? {
19 | didSet {
20 | trainCode.text = train?.trainCode
21 | souceInfoLabel.text = train?.stationFullName
22 | sourceTimeLabel.text = train?.expDeparture
23 | if let _destinationDetails = train?.destinationDetails {
24 | destinationInfoLabel.text = _destinationDetails.locationFullName
25 | destinationTimeLabel.text = _destinationDetails.expDeparture
26 | }
27 | }
28 | }
29 | @IBAction func saveStationTrain() {
30 | guard let favStationTrain = favStationTrain,
31 | let train = train
32 | else {
33 | return
34 | }
35 | favStationTrain(train)
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/MyTravelHelper/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSAppTransportSecurity
6 |
7 | NSAllowsArbitraryLoads
8 |
9 |
10 | CFBundleDevelopmentRegion
11 | $(DEVELOPMENT_LANGUAGE)
12 | CFBundleExecutable
13 | $(EXECUTABLE_NAME)
14 | CFBundleIdentifier
15 | $(PRODUCT_BUNDLE_IDENTIFIER)
16 | CFBundleInfoDictionaryVersion
17 | 6.0
18 | CFBundleName
19 | $(PRODUCT_NAME)
20 | CFBundlePackageType
21 | APPL
22 | CFBundleShortVersionString
23 | 1.0
24 | CFBundleVersion
25 | 1
26 | LSRequiresIPhoneOS
27 |
28 | UILaunchStoryboardName
29 | LaunchScreen
30 | UIRequiredDeviceCapabilities
31 |
32 | armv7
33 |
34 | UISupportedInterfaceOrientations
35 |
36 | UIInterfaceOrientationPortrait
37 | UIInterfaceOrientationLandscapeLeft
38 | UIInterfaceOrientationLandscapeRight
39 |
40 | UISupportedInterfaceOrientations~ipad
41 |
42 | UIInterfaceOrientationPortrait
43 | UIInterfaceOrientationPortraitUpsideDown
44 | UIInterfaceOrientationLandscapeLeft
45 | UIInterfaceOrientationLandscapeRight
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/MyTravelHelper/App-Ui/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 |
--------------------------------------------------------------------------------
/MyTravelHelper/Modules/SearchTrains/Protocols/SearchTrainProtocols.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchTrainProtocols.swift
3 | // MyTravelHelper
4 | //
5 | // Created by Satish on 11/03/19.
6 | // Copyright © 2019 Sample. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | enum ErrorMessages: Error {
12 | case noInternet
13 | case NoTrainsFound
14 | case NoTrainAvailbilityFromSource
15 | case InvalidSourceAndDestination
16 | }
17 |
18 | protocol ViewToPresenterProtocol: class{
19 | var view: PresenterToViewProtocol? {get set}
20 | var interactor: PresenterToInteractorProtocol? {get set}
21 | var router: PresenterToRouterProtocol? {get set}
22 | func fetchallStations()
23 | func searchTapped(source:String,destination:String)
24 | func saveStationToFav(_ station: StationTrain)
25 | func getFavouriteStation()->StationTrain?
26 | }
27 |
28 | protocol PresenterToViewProtocol: class{
29 | func saveFetchedStations(stations:[Station]?)
30 | func updateLatestTrainList(trainsList: [StationTrain])
31 | func showErrorMessage(for Error: ErrorMessages)
32 | }
33 |
34 | protocol PresenterToRouterProtocol: class {
35 | static func createModule()-> SearchTrainViewController
36 | }
37 |
38 | protocol PresenterToInteractorProtocol: class {
39 | var presenter:InteractorToPresenterProtocol? {get set}
40 | var networkClient: Client?{get set}
41 | func fetchallStations()
42 | func fetchTrainsFromSource(sourceCode:String,destinationCode:String)
43 | func saveStationToFav(_ station: StationTrain)
44 | func getFavouriteStation()->StationTrain?
45 | }
46 |
47 | protocol InteractorToPresenterProtocol: class {
48 | func stationListFetched(list:[Station])
49 | func fetchedTrainsList(trainsList:[StationTrain]?)
50 | func showErrorMessage(for Error: ErrorMessages)
51 | func getFavouriteStation()->StationTrain?
52 | }
53 |
--------------------------------------------------------------------------------
/MyTravelHelper/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/MyTravelHelper/Modules/SearchTrains/Presenter/SearchTrainPresenter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchTrainPresenter.swift
3 | // MyTravelHelper
4 | //
5 | // Created by Satish on 11/03/19.
6 | // Copyright © 2019 Sample. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class SearchTrainPresenter:ViewToPresenterProtocol {
12 | private var stationsList:[Station] = [Station]()
13 | var interactor: PresenterToInteractorProtocol?
14 | var router: PresenterToRouterProtocol?
15 | var view:PresenterToViewProtocol?
16 |
17 | func searchTapped(source: String, destination: String) {
18 | let sourceStationCode = getStationCode(stationName: source)
19 | let destinationStationCode = getStationCode(stationName: destination)
20 | interactor?.fetchTrainsFromSource(sourceCode: sourceStationCode,
21 | destinationCode: destinationStationCode)
22 | }
23 | func fetchallStations() {
24 | interactor?.fetchallStations()
25 | }
26 | func getFavouriteStation() -> StationTrain? {
27 | interactor?.getFavouriteStation()
28 | }
29 | func saveStationToFav(_ station: StationTrain) {
30 | interactor?.saveStationToFav(station)
31 | }
32 | private func getStationCode(stationName:String)->String {
33 | let stationCode = stationsList.filter{$0.stationDesc == stationName}.first
34 | return stationCode?.stationCode?.lowercased() ?? ""
35 | }
36 | }
37 |
38 | extension SearchTrainPresenter: InteractorToPresenterProtocol {
39 | func showErrorMessage(for Error: ErrorMessages) {
40 | self.view?.showErrorMessage(for: Error)
41 | }
42 | func fetchedTrainsList(trainsList: [StationTrain]?) {
43 | if let _trainsList = trainsList {
44 | self.view?.updateLatestTrainList(trainsList: _trainsList)
45 | }
46 | //TODO: Check if this required
47 | /*
48 | else {
49 | self.view?.showNoTrainsFoundAlert()
50 | }*/
51 | }
52 | func stationListFetched(list: [Station]) {
53 | self.stationsList = list
54 | self.view?.saveFetchedStations(stations: list)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/MyTravelHelper/Modules/SearchTrains/Entity/Stations.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Stations.swift
3 | // MyTravelHelper
4 | //
5 | // Created by Satish on 11/03/19.
6 | // Copyright © 2019 Sample. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct Stations: Codable {
12 | var stationsList: [Station]
13 |
14 | enum CodingKeys: String, CodingKey {
15 | case stationsList = "objStation"
16 | }
17 | }
18 |
19 | struct Station: Codable {
20 | let stationDesc: String?
21 | let stationLatitude: Double?
22 | let stationLongitude: Double?
23 | let stationCode: String?
24 | let stationId: Int?
25 |
26 | enum CodingKeys: String, CodingKey {
27 | case stationDesc = "StationDesc"
28 | case stationLatitude = "StationLatitude"
29 | case stationLongitude = "StationLongitude"
30 | case stationCode = "StationCode"
31 | case stationId = "StationId"
32 | }
33 | init(desc: String, latitude: Double, longitude: Double, code: String, stationId: Int) {
34 | self.stationDesc = desc
35 | self.stationLatitude = latitude
36 | self.stationLongitude = longitude
37 | self.stationCode = code
38 | self.stationId = stationId
39 | }
40 |
41 | init(from decoder: Decoder) throws {
42 | let values = try decoder.container(keyedBy: CodingKeys.self)
43 | let stationDesc = try values.decode(String.self, forKey: .stationDesc)
44 | let stationLatitude = try values.decode(Double.self, forKey: .stationLatitude)
45 | let stationLongitude = try values.decode(Double.self, forKey: .stationLongitude)
46 | let stationCode = try values.decode(String.self, forKey: .stationCode)
47 | let stationId = try values.decode(Int.self, forKey: .stationId)
48 | self.init(desc: stationDesc, latitude: stationLatitude, longitude: stationLongitude, code: stationCode, stationId: stationId)
49 |
50 | }
51 | }
52 | extension Station {
53 | static func request() -> URLRequest {
54 | var allStation = URL.baseUrl
55 | allStation.addPath("/realtime/realtime.asmx/getAllStationsXML")
56 | return URLRequest(url: allStation)
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/MyTravelHelper/Modules/SearchTrains/Entity/TrainMovements.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TrainMovements.swift
3 | // MyTravelHelper
4 | //
5 | // Created by Satish on 11/03/19.
6 | // Copyright © 2019 Sample. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct TrainMovementsData: Codable {
12 | var trainMovements: [TrainMovement]
13 |
14 | enum CodingKeys: String, CodingKey {
15 | case trainMovements = "objTrainMovements"
16 | }
17 | }
18 |
19 | struct TrainMovement: Codable {
20 | let trainCode: String?
21 | let locationCode: String?
22 | let locationFullName: String?
23 | let expDeparture:String?
24 |
25 | enum CodingKeys: String, CodingKey {
26 | case trainCode = "TrainCode"
27 | case locationCode = "LocationCode"
28 | case locationFullName = "LocationFullName"
29 | case expDeparture = "ExpectedDeparture"
30 | }
31 | init(trainCode: String, locationCode: String, locationFullName: String,expDeparture:String) {
32 | self.trainCode = trainCode
33 | self.locationCode = locationCode
34 | self.locationFullName = locationFullName
35 | self.expDeparture = expDeparture
36 | }
37 | init(from decoder: Decoder) throws {
38 | let values = try decoder.container(keyedBy: CodingKeys.self)
39 | let trainCode = try values.decode(String.self, forKey: .trainCode)
40 | let locationCode = try values.decode(String.self, forKey: .locationCode)
41 | let locationFullName = try values.decode(String.self, forKey: .locationFullName)
42 | let expDeparture = try values.decode(String.self, forKey: .expDeparture)
43 | self.init(trainCode: trainCode,
44 | locationCode: locationCode,
45 | locationFullName: locationFullName,
46 | expDeparture: expDeparture)
47 | }
48 | }
49 | extension TrainMovementsData {
50 | static func request(for code: String, date trainDate: String) -> URLRequest {
51 | var getTrainMovementsXML = URL.baseUrl
52 | getTrainMovementsXML.addPath("/realtime/realtime.asmx/getTrainMovementsXML")
53 | getTrainMovementsXML.appendQueryItem("TrainId", value: code)
54 | getTrainMovementsXML.appendQueryItem("TrainDate", value: trainDate)
55 | return URLRequest(url: getTrainMovementsXML)
56 | }
57 | }
58 |
59 |
--------------------------------------------------------------------------------
/MyTravelHelper/Modules/SearchTrains/Repository/Client.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Client.swift
3 | // MyTravelHelper
4 | //
5 | // Created by Navroz Singh on 08/01/21.
6 | // Copyright © 2021 Sample. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | protocol Client {
11 | func fetchallStations(resultHandler: @escaping (Result) -> Void)
12 | func fetchTrainsFromSource(_ sourceRequest: URLRequest,
13 | resultHandler: @escaping (Result<[StationTrain], ErrorMessages>) -> Void)
14 | func proceesTrainListforDestinationCheck(_ sourceRequest: URLRequest,
15 | resultHandler: @escaping (Result) -> Void)
16 | }
17 | class TrainClient: Client {
18 | func fetchallStations(resultHandler: @escaping (Result) -> Void) {
19 |
20 | URLSession.shared.perform(Station.request(),
21 | decode: Stations.self) { (result) in
22 | switch result {
23 | case .success(let stations):
24 | resultHandler(.success(stations))
25 | case .failure(_):
26 | //MARK: Fix me Cover all Cases
27 | resultHandler(.failure(.noInternet))
28 | }
29 | }
30 | }
31 | func fetchTrainsFromSource(_ sourceRequest: URLRequest,
32 | resultHandler: @escaping (Result<[StationTrain], ErrorMessages>) -> Void) {
33 | URLSession.shared.perform(sourceRequest, decode: StationData.self) { (result) in
34 | switch result {
35 | case .failure(let error):
36 | let error = error as NSError
37 | switch error.code {
38 | case -1009:
39 | resultHandler(.failure(.noInternet))
40 | break
41 | case 400...404:
42 | resultHandler(.failure(.NoTrainsFound))
43 | break
44 | default: break
45 | }
46 | case .success(let stationData):
47 | if stationData.trainsList.count > 0 {
48 | resultHandler(.success(stationData.trainsList))
49 | } else {
50 | resultHandler(.failure(.NoTrainsFound))
51 | }
52 | }
53 | }
54 | }
55 | func proceesTrainListforDestinationCheck(_ sourceRequest: URLRequest,
56 | resultHandler: @escaping (Result) -> Void) {
57 | URLSession.shared.perform(sourceRequest,
58 | decode: TrainMovementsData.self) { (result) in
59 | switch result {
60 | //MARK: Fix me Cover all Cases
61 | case .failure(_):
62 | resultHandler(.failure(.noInternet))
63 | break
64 | case .success(let trainMovements):
65 | resultHandler(.success(trainMovements))
66 | }
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/MyTravelHelper/App-utils/ProgressIndicator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProgressIndicator.swift
3 | // MyTravelHelper
4 | //
5 | // Created by Satish on 11/03/19.
6 | // Copyright © 2019 Sample. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | import Foundation
12 | import UIKit
13 |
14 | class ProgressIndicator: UIVisualEffectView {
15 | var text: String? {
16 | didSet {
17 | label.text = text
18 | label.textColor = UIColor.gray
19 | }
20 | }
21 |
22 | let activityIndictor: UIActivityIndicatorView = UIActivityIndicatorView(style: UIActivityIndicatorView.Style.whiteLarge)
23 | let label: UILabel = UILabel()
24 | let blurEffect = UIBlurEffect(style: .dark)
25 | let vibrancyView: UIVisualEffectView
26 |
27 | init(text: String) {
28 | self.text = text
29 | self.vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: blurEffect))
30 | super.init(effect: blurEffect)
31 | self.setup()
32 | }
33 |
34 | required init?(coder aDecoder: NSCoder) {
35 | self.text = ""
36 | self.vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: blurEffect))
37 | super.init(coder: aDecoder)
38 | self.setup()
39 | }
40 |
41 | func setup() {
42 | contentView.addSubview(vibrancyView)
43 | contentView.addSubview(activityIndictor)
44 | contentView.addSubview(label)
45 | activityIndictor.startAnimating()
46 | }
47 |
48 | override func didMoveToSuperview() {
49 | super.didMoveToSuperview()
50 |
51 | if let superview = self.superview {
52 | let width = superview.frame.size.width / 1.8
53 | let height: CGFloat = 70.0
54 | self.frame = CGRect(x: superview.frame.size.width / 2 - width / 2,
55 | y: superview.frame.height / 2 - height / 2,
56 | width: width,
57 | height: height)
58 | vibrancyView.frame = self.bounds
59 |
60 | let activityIndicatorSize: CGFloat = 40
61 | activityIndictor.frame = CGRect(x: 15,
62 | y: height / 2 - activityIndicatorSize / 2,
63 | width: activityIndicatorSize,
64 | height: activityIndicatorSize)
65 |
66 | layer.cornerRadius = 8.0
67 | layer.masksToBounds = true
68 | label.text = text
69 | label.textAlignment = NSTextAlignment.center
70 | label.frame = CGRect(x: activityIndicatorSize + 5,
71 | y: 0,
72 | width: width - activityIndicatorSize - 15,
73 | height: height)
74 | label.textColor = UIColor.white
75 | label.font = UIFont.boldSystemFont(ofSize: 17)
76 | }
77 | }
78 |
79 | func show() {
80 | self.isHidden = false
81 | }
82 |
83 | func hide() {
84 | self.isHidden = true
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/MyTravelHelper/App-utils/Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // URL+Extension.swift
3 | // MyTravelHelper
4 | //
5 | // Created by Navroz on 04/01/21.
6 | // Copyright © 2021 Sample. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import XMLParsing
11 |
12 | extension URL {
13 | static var baseUrl: URL {
14 | guard let url = URL(string: "http://api.irishrail.ie/") else {
15 | fatalError("Invalid base URL")
16 | }
17 | return url
18 | }
19 | mutating func addPath(_ path: String) {
20 | guard var urlComponents = URLComponents(string: absoluteString) else { return }
21 | urlComponents.path = path
22 | self = urlComponents.url ?? self
23 | }
24 | mutating func appendQueryItem(_ name: String, value: String?) {
25 | guard var urlComponents = URLComponents(string: absoluteString) else { return }
26 | var queryItems: [URLQueryItem] = urlComponents.queryItems ?? []
27 | let queryItem = URLQueryItem(name: name, value: value)
28 | queryItems.append(queryItem)
29 | urlComponents.queryItems = queryItems
30 | self = urlComponents.url ?? self
31 | }
32 | }
33 | extension UIStoryboard {
34 | static var mainstoryboard: UIStoryboard {
35 | return UIStoryboard(name:"Main",bundle: Bundle.main)
36 | }
37 | }
38 | extension Date {
39 | static var TrainDate: String {
40 | let today = Date()
41 | let formatter = DateFormatter()
42 | formatter.dateFormat = "dd/MM/yyyy"
43 | let dateString = formatter.string(from: today)
44 | return dateString
45 | }
46 | }
47 | extension URLSession {
48 | func perform(_ request: URLRequest,
49 | decode decodable: T.Type,
50 | result: @escaping (Result) -> Void) {
51 |
52 | URLSession.shared.dataTask(with: request) { (data, response, error) in
53 | guard let data = data
54 | else {
55 | result(.failure(NSError.nilData))
56 | return
57 | }
58 | guard let object = try? XMLDecoder().decode(decodable.self, from: data)
59 | else {
60 | debugPrint(String(decoding: data, as: UTF8.self))
61 | result(.failure(NSError.badResponse))
62 | return
63 | }
64 | debugPrint(String(decoding: data, as: UTF8.self))
65 | result(.success(object))
66 | }.resume()
67 | }
68 | }
69 |
70 | extension NSError {
71 | static let nilData = NSError(domain: "com.example.com.MyTravelHelper",
72 | code: 404,
73 | userInfo: [NSLocalizedDescriptionKey : "Data is nil"])
74 | static let badResponse = NSError(domain: "com.example.com.MyTravelHelper",
75 | code: 400,
76 | userInfo: [NSLocalizedDescriptionKey : "Unable to decode response"])
77 | static let noNetwork = NSError(domain: "com.example.com.MyTravelHelper",
78 | code: -1009,
79 | userInfo: [NSLocalizedDescriptionKey : "The Internet connection appears to be offline."])
80 | }
81 |
--------------------------------------------------------------------------------
/MyTravelHelperTests/SearchTrainInteractorsTest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchTrainInteractorsTest.swift
3 | // MyTravelHelperTests
4 | //
5 | // Created by Navroz on 10/01/21.
6 | // Copyright © 2021 Sample. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import MyTravelHelper
11 | class InteractorTest: XCTestCase {
12 | var view: ViewToPresenterProtocol?
13 | var Interactor: PresenterToInteractorProtocol?
14 | var presenter = MockPresenter()
15 | var network = MockNetwork()
16 | override func setUp() {
17 | Interactor = SearchTrainInteractor()
18 | Interactor?.networkClient = network
19 | Interactor?.presenter = presenter
20 | }
21 | func testFetchallStationsFailure() {
22 | presenter.currentError = .NoTrainsFound
23 | network.currentError = .NoTrainsFound
24 | Interactor?.fetchallStations()
25 | }
26 | func testFetchallStationsSuccess() {
27 | presenter.currentError = nil
28 | network.currentError = nil
29 | Interactor?.fetchallStations()
30 | }
31 | func testEmptyFetchTrainList() {
32 | presenter.currentError = .InvalidSourceAndDestination
33 | Interactor?.fetchTrainsFromSource(sourceCode: "", destinationCode: "")
34 | }
35 | func testFetchTrainList() {
36 | let unlockExpectation = expectation(description: "delegate is called twice")
37 | presenter.expectation = unlockExpectation
38 | Interactor?.fetchTrainsFromSource(sourceCode: "Dublin Belfast", destinationCode: "DUNMR")
39 | wait(for: [unlockExpectation], timeout: 60)
40 | }
41 |
42 | func testFavourite() {
43 | let station = Responses.TrainList()[0]
44 | Interactor?.saveStationToFav(station)
45 | let favStation = presenter.getFavouriteStation()
46 | XCTAssertEqual(station.trainCode, favStation?.trainCode)
47 | }
48 |
49 |
50 | }
51 | class MockPresenter: InteractorToPresenterProtocol {
52 | var expectation: XCTestExpectation?
53 |
54 | var currentError: ErrorMessages?
55 | func stationListFetched(list: [Station]) {
56 | let staticList = Responses.AllStation().stationsList
57 | XCTAssertEqual(staticList.count, list.count)
58 | }
59 |
60 | func fetchedTrainsList(trainsList: [StationTrain]?) {
61 | XCTAssertEqual(trainsList!.count, 1)
62 | expectation?.fulfill()
63 | }
64 |
65 | func showErrorMessage(for Error: ErrorMessages) {
66 | XCTAssertEqual(currentError, Error)
67 | }
68 |
69 | func getFavouriteStation() -> StationTrain? {
70 | StationTrain.getFavouriteStation()
71 | }
72 | }
73 | class MockNetwork: Client {
74 | var currentError: ErrorMessages?
75 | func fetchallStations(resultHandler: @escaping (Result) -> Void) {
76 | if let currentError = currentError {
77 | resultHandler(.failure(currentError))
78 | } else {
79 | resultHandler(.success(Responses.AllStation()))
80 | }
81 | }
82 |
83 | func fetchTrainsFromSource(_ sourceRequest: URLRequest, resultHandler: @escaping (Result<[StationTrain], ErrorMessages>) -> Void) {
84 | if let currentError = currentError {
85 | resultHandler(.failure(currentError))
86 | } else {
87 | resultHandler(.success(Responses.TrainList()))
88 | }
89 | }
90 | func proceesTrainListforDestinationCheck(_ sourceRequest: URLRequest,
91 | resultHandler: @escaping (Result) -> Void) {
92 | if let currentError = currentError {
93 | resultHandler(.failure(currentError))
94 | } else {
95 | resultHandler(.success(Responses.TrainMovements()))
96 | }
97 | }
98 | }
99 |
100 |
--------------------------------------------------------------------------------
/MyTravelHelper/Modules/SearchTrains/Entity/StationInfo.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StationInfo.swift
3 | // MyTravelHelper
4 | //
5 | // Created by Satish on 11/03/19.
6 | // Copyright © 2019 Sample. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct StationData: Codable {
12 | var trainsList: [StationTrain]
13 |
14 | enum CodingKeys: String, CodingKey {
15 | case trainsList = "objStationData"
16 | }
17 | }
18 |
19 | struct StationTrain: Codable {
20 | let trainCode: String?
21 | let stationFullName: String?
22 | let stationCode: String?
23 | let trainDate: String?
24 | let dueIn: Int?
25 | let lateBy:Int?
26 | let expArrival:String?
27 | let expDeparture:String?
28 | var destinationDetails:TrainMovement?
29 |
30 | enum CodingKeys: String, CodingKey {
31 | case trainCode = "Traincode"
32 | case stationFullName = "Stationfullname"
33 | case stationCode = "Stationcode"
34 | case trainDate = "Traindate"
35 | case dueIn = "Duein"
36 | case lateBy = "Late"
37 | case expArrival = "Exparrival"
38 | case expDeparture = "Expdepart"
39 | case destinationDetails = "destinationDetails"
40 | }
41 | init(trainCode: String,
42 | fullName: String,
43 | stationCode: String,
44 | trainDate: String,
45 | dueIn: Int,
46 | lateBy:Int,
47 | expArrival:String,
48 | expDeparture:String,
49 | destinationDetails: TrainMovement?) {
50 | self.trainCode = trainCode
51 | self.stationFullName = fullName
52 | self.stationCode = stationCode
53 | self.trainDate = trainDate
54 | self.dueIn = dueIn
55 | self.lateBy = lateBy
56 | self.expArrival = expArrival
57 | self.expDeparture = expDeparture
58 | self.destinationDetails = destinationDetails
59 | }
60 | init(from decoder: Decoder) throws {
61 | let values = try decoder.container(keyedBy: CodingKeys.self)
62 | let trainCode = try values.decode(String.self, forKey: .trainCode)
63 | let stationFullName = try values.decode(String.self, forKey: .stationFullName)
64 | let stationCode = try values.decode(String.self, forKey: .stationCode)
65 | let trainDate = try values.decode(String.self, forKey: .trainDate)
66 | let dueIn = try values.decode(Int.self, forKey: .dueIn)
67 | let lateBy = try values.decode(Int.self, forKey: .lateBy)
68 | let expArrival = try values.decode(String.self, forKey: .expArrival)
69 | let expDeparture = try values.decode(String.self, forKey: .expDeparture)
70 | let destinationDetails = try? values.decode(TrainMovement.self, forKey: .destinationDetails)
71 |
72 | self.init(trainCode: trainCode,
73 | fullName: stationFullName,
74 | stationCode: stationCode,
75 | trainDate: trainDate,
76 | dueIn: dueIn,
77 | lateBy: lateBy,
78 | expArrival: expArrival,
79 | expDeparture: expDeparture, destinationDetails: destinationDetails)
80 | }
81 | }
82 | extension StationData {
83 | static func request(for sourceCode: String) -> URLRequest {
84 | var getStationDataByCodeXML = URL.baseUrl
85 | getStationDataByCodeXML.addPath("/realtime/realtime.asmx/getStationDataByCodeXML")
86 | getStationDataByCodeXML.appendQueryItem("StationCode", value: sourceCode)
87 | return URLRequest(url: getStationDataByCodeXML)
88 | }
89 | }
90 | //MARK: Saving logic
91 | extension StationTrain {
92 | static func saveStationAsFavourite(_ station: StationTrain) {
93 | UserDefaults.standard.set(try? PropertyListEncoder().encode(station), forKey:"FavStationTrain")
94 | }
95 | static func getFavouriteStation() -> StationTrain? {
96 | if let data = UserDefaults.standard.value(forKey:"FavStationTrain") as? Data {
97 | let station = try? PropertyListDecoder().decode(StationTrain.self, from: data)
98 | return station
99 | }
100 | return nil
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/MyTravelHelperTests/SearchTrainPresenterTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchTrainPresenterTests.swift
3 | // MyTravelHelperTests
4 | //
5 | // Created by Satish on 11/03/19.
6 | // Copyright © 2019 Sample. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import MyTravelHelper
11 |
12 | class SearchTrainPresenterTests: XCTestCase {
13 | var presenter: SearchTrainPresenter!
14 | var view = SearchTrainMockView()
15 | var interactor = SearchTrainInteractorMock()
16 |
17 | override func setUp() {
18 | presenter = SearchTrainPresenter()
19 | presenter.view = view
20 | presenter.interactor = interactor
21 | interactor.presenter = presenter
22 | }
23 |
24 | func testfetchallStations() {
25 | presenter.fetchallStations()
26 | if !view.isSaveFetchedStatinsCalled {
27 | XCTFail("saveFetchedStations func not called")
28 | }
29 | }
30 | func testErrorMessages() {
31 | let error: ErrorMessages = .noInternet
32 | presenter.showErrorMessage(for: error)
33 | XCTAssertEqual(view.error, error)
34 | }
35 | func testTrainList() {
36 | let sourceName = "dubin"
37 | let destinationName = "foundhound"
38 |
39 | interactor.fetchTrainsFromSource(sourceCode: sourceName, destinationCode: destinationName)
40 | guard view.trainsList?.count == 1,
41 | let stationFullName = view.trainsList?[0].stationFullName ,
42 | let locationFullName = view.trainsList?[0].destinationDetails?.locationFullName else {
43 | XCTFail("invalid station")
44 | return
45 | }
46 | XCTAssertEqual(stationFullName, sourceName)
47 | XCTAssertEqual(locationFullName, destinationName)
48 | }
49 |
50 | func testFav() {
51 | let stationTrain: StationTrain = StationTrain.init(trainCode: "A123", fullName: "sourceStation", stationCode: "456", trainDate: "5/01/2021", dueIn: 2, lateBy: 3, expArrival: "16:00", expDeparture: "18:00", destinationDetails: TrainMovement.init(trainCode: "A345", locationCode: "798", locationFullName: "destinationStation", expDeparture: "20:00"))
52 | presenter.saveStationToFav(stationTrain)
53 | let favStation = presenter.getFavouriteStation()
54 | XCTAssertEqual(stationTrain.stationFullName, favStation?.stationFullName)
55 | XCTAssertEqual(stationTrain.stationCode, favStation?.stationCode)
56 | XCTAssertEqual(stationTrain.expArrival, favStation?.expArrival)
57 | XCTAssertEqual(stationTrain.destinationDetails?.locationFullName, favStation?.destinationDetails?.locationFullName)
58 | }
59 |
60 | override func tearDown() {
61 | presenter = nil
62 | }
63 | }
64 |
65 |
66 | class SearchTrainMockView:PresenterToViewProtocol {
67 | var error: ErrorMessages?
68 | func showErrorMessage(for Error: ErrorMessages) {
69 | error = Error
70 | }
71 | var isSaveFetchedStatinsCalled = false
72 | func saveFetchedStations(stations: [Station]?) {
73 | isSaveFetchedStatinsCalled = true
74 | }
75 | var trainsList:[StationTrain]?
76 | func updateLatestTrainList(trainsList: [StationTrain]) {
77 | self.trainsList = trainsList
78 | }
79 | }
80 |
81 | class SearchTrainInteractorMock: PresenterToInteractorProtocol {
82 | var networkClient: Client?
83 | var presenter: InteractorToPresenterProtocol?
84 |
85 | func fetchallStations() {
86 | let station = Station(desc: "Belfast Central",
87 | latitude: 54.6123,
88 | longitude: -5.91744,
89 | code: "BFSTC",
90 | stationId: 228)
91 | presenter?.stationListFetched(list: [station])
92 | }
93 |
94 | func fetchTrainsFromSource(sourceCode: String, destinationCode: String) {
95 | let stationTrain: StationTrain = StationTrain.init(trainCode: "A123", fullName: sourceCode, stationCode: "456", trainDate: "5/01/2021", dueIn: 2, lateBy: 3, expArrival: "16:00", expDeparture: "18:00", destinationDetails: TrainMovement.init(trainCode: "A345", locationCode: "798", locationFullName: destinationCode, expDeparture: "20:00"))
96 |
97 | presenter?.fetchedTrainsList(trainsList: [stationTrain])
98 | }
99 | var favStation: StationTrain?
100 | func saveStationToFav(_ station: StationTrain) {
101 | self.favStation = station
102 | }
103 | func getFavouriteStation() -> StationTrain? {
104 | return favStation
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/MyTravelHelper/App-utils/Reachability.swift:
--------------------------------------------------------------------------------
1 | // The MIT License (MIT)
2 | //
3 | // Copyright (c) 2015 Isuru Nanayakkara
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 | import Foundation
24 | import SystemConfiguration
25 |
26 | let reachabilityStatusChangedNotification = "reachabilityStatusChangedNotification"
27 |
28 | enum ReachabilityType: CustomStringConvertible {
29 | case wwan
30 | case wiFi
31 |
32 | var description: String {
33 | switch self {
34 | case .wwan: return "WWAN"
35 | case .wiFi: return "WiFi"
36 | }
37 | }
38 | }
39 |
40 | enum ReachabilityStatus: CustomStringConvertible {
41 | case offline
42 | case online(ReachabilityType)
43 | case unknown
44 |
45 | var description: String {
46 | switch self {
47 | case .offline: return "Offline"
48 | case .online(let type): return "Online (\(type))"
49 | case .unknown: return "Unknown"
50 | }
51 | }
52 | }
53 |
54 | open class Reach {
55 | func connectionStatus() -> ReachabilityStatus {
56 | var zeroAddress = sockaddr_in()
57 | zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
58 | zeroAddress.sin_family = sa_family_t(AF_INET)
59 |
60 | guard let defaultRouteReachability = withUnsafePointer(to: &zeroAddress, {
61 | $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
62 | SCNetworkReachabilityCreateWithAddress(nil, UnsafePointer($0))
63 | }
64 | }) else {
65 | return .unknown
66 | }
67 |
68 | var flags: SCNetworkReachabilityFlags = []
69 | if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
70 | return .unknown
71 | }
72 |
73 | return ReachabilityStatus(reachabilityFlags: flags)
74 | }
75 |
76 | func monitorReachabilityChanges() {
77 | let host = "google.com"
78 | var context = SCNetworkReachabilityContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil)
79 | let reachability = SCNetworkReachabilityCreateWithName(nil, host)!
80 |
81 | SCNetworkReachabilitySetCallback(reachability, { (_, flags, _) in
82 | let status = ReachabilityStatus(reachabilityFlags: flags)
83 |
84 | NotificationCenter.default.post(name: Notification.Name(rawValue: reachabilityStatusChangedNotification),
85 | object: nil,
86 | userInfo: ["Status": status.description])
87 |
88 | }, &context)
89 |
90 | SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetMain(), CFRunLoopMode.commonModes.rawValue)
91 | }
92 |
93 | func isNetworkReachable() -> Bool {
94 |
95 | let status = connectionStatus()
96 |
97 | switch status {
98 | case .unknown, .offline:
99 | return false
100 | case .online(.wwan):
101 | return true
102 | case .online(.wiFi):
103 | return true
104 | }
105 | }
106 | }
107 |
108 | extension ReachabilityStatus {
109 | fileprivate init(reachabilityFlags flags: SCNetworkReachabilityFlags) {
110 | let connectionRequired = flags.contains(.connectionRequired)
111 | let isReachable = flags.contains(.reachable)
112 | let isWWAN = flags.contains(.isWWAN)
113 |
114 | if !connectionRequired && isReachable {
115 | if isWWAN {
116 | self = .online(.wwan)
117 | } else {
118 | self = .online(.wiFi)
119 | }
120 | } else {
121 | self = .offline
122 | }
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/MyTravelHelper/Modules/SearchTrains/Interactor/SearchTrainInteractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchTrainInteractor.swift
3 | // MyTravelHelper
4 | //
5 | // Created by Satish on 11/03/19.
6 | // Copyright © 2019 Sample. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import XMLParsing
11 | //import Alamofire
12 |
13 | class SearchTrainInteractor: PresenterToInteractorProtocol {
14 | var networkClient: Client?
15 | var _sourceStationCode = String()
16 | var _destinationStationCode = String()
17 | var presenter: InteractorToPresenterProtocol?
18 | func fetchallStations() {
19 | networkClient?.fetchallStations(resultHandler: { (result) in
20 | switch result {
21 | case .success(let station):
22 | self.presenter?.stationListFetched(list: station.stationsList)
23 | break
24 | case .failure(let error):
25 | self.presenter?.showErrorMessage(for: error)
26 | break
27 | }
28 | })
29 | }
30 | func fetchTrainsFromSource(sourceCode: String, destinationCode: String) {
31 | guard validSourceAndDestination(sourceCode,destinationCode)
32 | else {
33 | self.presenter?.showErrorMessage(for: .InvalidSourceAndDestination)
34 | return
35 | }
36 | _sourceStationCode = sourceCode
37 | _destinationStationCode = destinationCode
38 | let request = StationData.request(for: sourceCode)
39 | networkClient?.fetchTrainsFromSource(request, resultHandler: { (result) in
40 | switch result {
41 | case .success(let trainsList):
42 | self.proceesTrainListforDestinationCheck(trainsList: trainsList)
43 | break
44 | case .failure(let error):
45 | self.presenter?.showErrorMessage(for: error)
46 | break
47 | }
48 | })
49 | }
50 | private func proceesTrainListforDestinationCheck(trainsList: [StationTrain]) {
51 | var _trainsList = trainsList
52 | DispatchQueue.global(qos: .background).async {
53 | let group = DispatchGroup()
54 | for index in 0...trainsList.count-1 {
55 | guard let code = trainsList[index].trainCode else {
56 | continue
57 | }
58 | group.enter()
59 | let request = TrainMovementsData.request(for: code, date: Date.TrainDate)
60 | self.networkClient?.proceesTrainListforDestinationCheck(request, resultHandler: { (result) in
61 | group.leave()
62 | switch result {
63 | case .success(let trainMovements):
64 | if let firstStationMoment = self.destinationTrains(for: trainMovements) {
65 | _trainsList[index].destinationDetails = firstStationMoment
66 | }
67 | break
68 | case .failure(let error):
69 | self.presenter?.showErrorMessage(for: error)
70 | break
71 | }
72 | })
73 | group.wait()
74 | }
75 | group.notify(queue: DispatchQueue.main) {
76 | let sourceToDestinationTrains = _trainsList.filter{$0.destinationDetails != nil}
77 | self.presenter?.fetchedTrainsList(trainsList: sourceToDestinationTrains)
78 | if sourceToDestinationTrains.count == 0 {
79 | self.presenter?.showErrorMessage(for: .NoTrainAvailbilityFromSource)
80 | }
81 | }
82 | }
83 | }
84 | //MARK: Favourite Logic
85 | func saveStationToFav(_ station: StationTrain) {
86 | StationTrain.saveStationAsFavourite(station)
87 | }
88 | func getFavouriteStation() -> StationTrain? {
89 | return StationTrain.getFavouriteStation()
90 | }
91 | }
92 | //MARK: Validation & Destination Logic
93 | extension SearchTrainInteractor {
94 | private func destinationTrains(for trainMovements: TrainMovementsData) -> TrainMovement? {
95 | let _movements = trainMovements.trainMovements
96 | let sourceIndex = _movements.firstIndex(where: {$0.locationCode?.caseInsensitiveCompare(self._sourceStationCode) == .orderedSame})
97 | let destinationIndex = _movements.firstIndex(where: {$0.locationCode?.caseInsensitiveCompare(self._destinationStationCode) == .orderedSame})
98 | let desiredStationMoment = _movements.filter{$0.locationCode?.caseInsensitiveCompare(self._destinationStationCode) == .orderedSame}
99 | let isDestinationAvailable = desiredStationMoment.count == 1
100 |
101 | if isDestinationAvailable && sourceIndex! < destinationIndex! {
102 | return desiredStationMoment.first
103 | }
104 | return nil
105 | }
106 |
107 | private func validSourceAndDestination(_ sourceCode: String, _ destinationCode: String) -> Bool {
108 | guard sourceCode != destinationCode,
109 | !sourceCode.isEmpty,
110 | !destinationCode.isEmpty else {
111 | return false
112 | }
113 | return true
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/MyTravelHelper.xcworkspace/xcuserdata/navroz.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
21 |
22 |
23 |
25 |
37 |
38 |
39 |
41 |
53 |
54 |
55 |
57 |
69 |
70 |
71 |
73 |
85 |
86 |
87 |
89 |
101 |
102 |
103 |
105 |
115 |
116 |
117 |
119 |
131 |
132 |
133 |
134 |
135 |
--------------------------------------------------------------------------------
/MyTravelHelper/Modules/SearchTrains/View/SearchTrainViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SearchTrainViewController.swift
3 | // MyTravelHelper
4 | //
5 | // Created by Satish on 11/03/19.
6 | // Copyright © 2019 Sample. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SwiftSpinner
11 | import DropDown
12 |
13 | class SearchTrainViewController: UIViewController {
14 | @IBOutlet weak var destinationTextField: UITextField!
15 | @IBOutlet weak var sourceTxtField: UITextField!
16 | @IBOutlet weak var trainsListTable: UITableView!
17 |
18 | var stationsList:[Station] = [Station]()
19 | var trains:[StationTrain] = [StationTrain]()
20 | var presenter:ViewToPresenterProtocol?
21 | var dropDown = DropDown()
22 | var transitPoints:(source:String,destination:String) = ("","")
23 | var favStations: StationTrain?
24 |
25 | override func viewDidLoad() {
26 | super.viewDidLoad()
27 | trainsListTable.isHidden = true
28 | setupFavButton()
29 | }
30 |
31 | override func viewWillAppear(_ animated: Bool) {
32 | if stationsList.count == 0 {
33 | SwiftSpinner.useContainerView(view)
34 | SwiftSpinner.show("Please wait loading station list ....")
35 | presenter?.fetchallStations()
36 | }
37 | }
38 | @IBAction func searchTrainsTapped(_ sender: Any) {
39 | view.endEditing(true)
40 | showProgressIndicator(view: self.view)
41 | presenter?.searchTapped(source: transitPoints.source,
42 | destination: transitPoints.destination)
43 | }
44 | }
45 | extension SearchTrainViewController {
46 | private func setupFavButton() {
47 | navigationItem.rightBarButtonItem = nil
48 | guard let favStations = presenter?.getFavouriteStation() else {
49 | return
50 | }
51 | self.favStations = favStations
52 | navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Favourite Stations",
53 | style: .plain,
54 | target: self,
55 | action: #selector(setFavStations))
56 | }
57 | @objc func setFavStations() {
58 | transitPoints.source = favStations?.stationFullName ?? ""
59 | transitPoints.destination = favStations?.destinationDetails?.locationFullName ?? ""
60 | self.sourceTxtField.text = transitPoints.source
61 | self.destinationTextField.text = transitPoints.destination
62 | }
63 | }
64 |
65 | extension SearchTrainViewController:PresenterToViewProtocol {
66 | func showErrorMessage(for Error: ErrorMessages) {
67 | DispatchQueue.main.async {
68 | switch Error {
69 | case .noInternet:
70 | self.showNoInterNetAvailabilityMessage()
71 | break
72 | case .NoTrainsFound:
73 | self.showNoTrainsFoundAlert()
74 | break
75 | case .NoTrainAvailbilityFromSource:
76 | self.showNoTrainAvailbilityFromSource()
77 | break
78 | case .InvalidSourceAndDestination:
79 | self.showInvalidSourceOrDestinationAlert()
80 | break
81 | }
82 | }
83 | }
84 |
85 | func showNoInterNetAvailabilityMessage() {
86 | trainsListTable.isHidden = true
87 | hideProgressIndicator(view: self.view)
88 | showAlert(title: "No Internet", message: "Please Check you internet connection and try again", actionTitle: "Okay")
89 | }
90 | func showInvalidSourceAndDestination() {
91 | trainsListTable.isHidden = true
92 | hideProgressIndicator(view: self.view)
93 | showAlert(title: "Invalid Source/destination", message: "Please select valid Source and destination", actionTitle: "Okay")
94 | }
95 | func showNoTrainAvailbilityFromSource() {
96 | trainsListTable.isHidden = true
97 | hideProgressIndicator(view: self.view)
98 | showAlert(title: "No Trains", message: "Sorry No trains arriving source station in another 90 mins", actionTitle: "Okay")
99 | }
100 |
101 | func updateLatestTrainList(trainsList: [StationTrain]) {
102 | DispatchQueue.main.async {
103 | hideProgressIndicator(view: self.view)
104 | self.trains = trainsList
105 | self.trainsListTable.isHidden = false
106 | self.trainsListTable.reloadData()
107 | }
108 | }
109 |
110 | func showNoTrainsFoundAlert() {
111 | trainsListTable.isHidden = true
112 | hideProgressIndicator(view: self.view)
113 | trainsListTable.isHidden = true
114 | showAlert(title: "No Trains", message: "Sorry No trains Found from source to destination in another 90 mins", actionTitle: "Okay")
115 | }
116 |
117 | func showAlert(title:String,message:String,actionTitle:String) {
118 | let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.alert)
119 | alert.addAction(UIAlertAction(title: actionTitle, style: UIAlertAction.Style.default, handler: nil))
120 | self.present(alert, animated: true, completion: nil)
121 | }
122 |
123 | func showInvalidSourceOrDestinationAlert() {
124 | trainsListTable.isHidden = true
125 | hideProgressIndicator(view: self.view)
126 | showAlert(title: "Invalid Source/Destination", message: "Invalid Source or Destination Station names Please Check", actionTitle: "Okay")
127 | }
128 |
129 | func saveFetchedStations(stations: [Station]?) {
130 | DispatchQueue.main.async {
131 | if let _stations = stations {
132 | self.stationsList = _stations
133 | }
134 | SwiftSpinner.hide()
135 | }
136 | }
137 | }
138 |
139 | extension SearchTrainViewController:UITextFieldDelegate {
140 | func textFieldDidBeginEditing(_ textField: UITextField) {
141 | dropDown = DropDown()
142 | dropDown.anchorView = textField
143 | dropDown.direction = .bottom
144 | dropDown.bottomOffset = CGPoint(x: 0, y:(dropDown.anchorView?.plainView.bounds.height)!)
145 | dropDown.dataSource = stationsList.compactMap{$0.stationDesc}
146 | dropDown.selectionAction = { (index: Int, item: String) in
147 | if textField == self.sourceTxtField {
148 | self.transitPoints.source = item
149 | }else {
150 | self.transitPoints.destination = item
151 | }
152 | textField.text = item
153 | }
154 | dropDown.show()
155 | }
156 |
157 | func textFieldShouldReturn(_ textField: UITextField) -> Bool {
158 | dropDown.hide()
159 | return textField.resignFirstResponder()
160 | }
161 |
162 | func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
163 | if let inputedText = textField.text {
164 | var desiredSearchText = inputedText
165 | if string != "\n" && !string.isEmpty{
166 | desiredSearchText = desiredSearchText + string
167 | }else {
168 | desiredSearchText = String(desiredSearchText.dropLast())
169 | }
170 |
171 | //dropDown.dataSource = stationsList
172 | dropDown.show()
173 | dropDown.reloadAllComponents()
174 | }
175 | return true
176 | }
177 | }
178 |
179 | extension SearchTrainViewController:UITableViewDataSource {
180 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
181 | return trains.count
182 | }
183 |
184 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
185 | guard let cell = tableView.dequeueReusableCell(withIdentifier: "train",
186 | for: indexPath) as? TrainInfoCell
187 | else {
188 | fatalError("cell misconfiguration")
189 | }
190 | cell.train = trains[indexPath.row]
191 | cell.favStationTrain = { station in
192 | self.presenter?.saveStationToFav(station)
193 | self.setupFavButton()
194 | }
195 | return cell
196 | }
197 |
198 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
199 | return 140
200 | }
201 | }
202 |
203 |
204 |
--------------------------------------------------------------------------------
/MyTravelHelper.xcworkspace/xcuserdata/navrozsingh.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
21 |
22 |
23 |
25 |
37 |
38 |
52 |
53 |
67 |
68 |
69 |
70 |
71 |
73 |
85 |
86 |
87 |
89 |
101 |
102 |
103 |
105 |
117 |
118 |
132 |
133 |
147 |
148 |
149 |
150 |
151 |
153 |
165 |
166 |
167 |
169 |
181 |
182 |
183 |
185 |
197 |
198 |
199 |
200 |
201 |
--------------------------------------------------------------------------------
/MyTravelHelper/App-Ui/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 |
30 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
103 |
109 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
129 |
135 |
141 |
142 |
143 |
144 |
145 |
146 |
156 |
157 |
158 |
159 |
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 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
--------------------------------------------------------------------------------
/MyTravelHelper.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 51;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 0301C30125A3875100A5E630 /* Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0301C30025A3875100A5E630 /* Extension.swift */; };
11 | 03399B7C25A4E6D000A8A343 /* readme.MD in Resources */ = {isa = PBXBuildFile; fileRef = 03399B7B25A4E6D000A8A343 /* readme.MD */; };
12 | 1654AF9B5118F79C1ACB2509 /* Pods_MyTravelHelperTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BD4A9E0F8C97F8AA92AAA053 /* Pods_MyTravelHelperTests.framework */; };
13 | 753C8FA422361EC9005FF13B /* TrainMovements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753C8FA122361EC9005FF13B /* TrainMovements.swift */; };
14 | 753C8FA522361EC9005FF13B /* StationInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753C8FA222361EC9005FF13B /* StationInfo.swift */; };
15 | 753C8FA622361EC9005FF13B /* Stations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753C8FA322361EC9005FF13B /* Stations.swift */; };
16 | 753C8FA822361FC0005FF13B /* SearchTrainProtocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753C8FA722361FC0005FF13B /* SearchTrainProtocols.swift */; };
17 | 753C8FAA2236228C005FF13B /* SearchTrainPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753C8FA92236228C005FF13B /* SearchTrainPresenter.swift */; };
18 | 753C8FAC223623C2005FF13B /* SearchTrainInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753C8FAB223623C2005FF13B /* SearchTrainInteractor.swift */; };
19 | 753C8FAE22362463005FF13B /* SearchTrainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753C8FAD22362463005FF13B /* SearchTrainViewController.swift */; };
20 | 753C8FB022365E9A005FF13B /* SearchTrainRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753C8FAF22365E9A005FF13B /* SearchTrainRouter.swift */; };
21 | 753C8FB522367D1C005FF13B /* AppUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753C8FB422367D1C005FF13B /* AppUtils.swift */; };
22 | 753C8FB722367DF4005FF13B /* ProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753C8FB622367DF4005FF13B /* ProgressIndicator.swift */; };
23 | 753C8FBA2236BD80005FF13B /* TrainInfoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753C8FB92236BD80005FF13B /* TrainInfoCell.swift */; };
24 | 753C8FBC223731E3005FF13B /* Reachability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753C8FBB223731E3005FF13B /* Reachability.swift */; };
25 | 753C8FBE2237367E005FF13B /* SearchTrainPresenterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753C8FBD2237367E005FF13B /* SearchTrainPresenterTests.swift */; };
26 | 753F0847223618AB00175107 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753F0846223618AB00175107 /* AppDelegate.swift */; };
27 | 753F084C223618AB00175107 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 753F084A223618AB00175107 /* Main.storyboard */; };
28 | 753F084E223618AD00175107 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 753F084D223618AD00175107 /* Assets.xcassets */; };
29 | 753F0851223618AD00175107 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 753F084F223618AD00175107 /* LaunchScreen.storyboard */; };
30 | 753F087122361A6500175107 /* AppConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 753F087022361A6500175107 /* AppConstants.swift */; };
31 | 8CBBCE0D40B8FE266A1617DE /* Pods_MyTravelHelper.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF635B96516F13D67ADE01AF /* Pods_MyTravelHelper.framework */; };
32 | /* End PBXBuildFile section */
33 |
34 | /* Begin PBXContainerItemProxy section */
35 | 753F0858223618AE00175107 /* PBXContainerItemProxy */ = {
36 | isa = PBXContainerItemProxy;
37 | containerPortal = 753F083B223618AB00175107 /* Project object */;
38 | proxyType = 1;
39 | remoteGlobalIDString = 753F0842223618AB00175107;
40 | remoteInfo = MyTravelHelper;
41 | };
42 | /* End PBXContainerItemProxy section */
43 |
44 | /* Begin PBXFileReference section */
45 | 0301C30025A3875100A5E630 /* Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extension.swift; sourceTree = ""; };
46 | 03399B7B25A4E6D000A8A343 /* readme.MD */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = readme.MD; sourceTree = ""; };
47 | 11F481535B44216B54872EF2 /* Pods-MyTravelHelperTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MyTravelHelperTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MyTravelHelperTests/Pods-MyTravelHelperTests.debug.xcconfig"; sourceTree = ""; };
48 | 2F515F7A370B032163D445BC /* Pods-MyTravelHelper.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MyTravelHelper.release.xcconfig"; path = "Pods/Target Support Files/Pods-MyTravelHelper/Pods-MyTravelHelper.release.xcconfig"; sourceTree = ""; };
49 | 4A3D05BF450D92647BC863F2 /* Pods-MyTravelHelper.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MyTravelHelper.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MyTravelHelper/Pods-MyTravelHelper.debug.xcconfig"; sourceTree = ""; };
50 | 753C8FA122361EC9005FF13B /* TrainMovements.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TrainMovements.swift; sourceTree = ""; };
51 | 753C8FA222361EC9005FF13B /* StationInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StationInfo.swift; sourceTree = ""; };
52 | 753C8FA322361EC9005FF13B /* Stations.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stations.swift; sourceTree = ""; };
53 | 753C8FA722361FC0005FF13B /* SearchTrainProtocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTrainProtocols.swift; sourceTree = ""; };
54 | 753C8FA92236228C005FF13B /* SearchTrainPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTrainPresenter.swift; sourceTree = ""; };
55 | 753C8FAB223623C2005FF13B /* SearchTrainInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTrainInteractor.swift; sourceTree = ""; };
56 | 753C8FAD22362463005FF13B /* SearchTrainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTrainViewController.swift; sourceTree = ""; };
57 | 753C8FAF22365E9A005FF13B /* SearchTrainRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTrainRouter.swift; sourceTree = ""; };
58 | 753C8FB422367D1C005FF13B /* AppUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppUtils.swift; sourceTree = ""; };
59 | 753C8FB622367DF4005FF13B /* ProgressIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressIndicator.swift; sourceTree = ""; };
60 | 753C8FB92236BD80005FF13B /* TrainInfoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrainInfoCell.swift; sourceTree = ""; };
61 | 753C8FBB223731E3005FF13B /* Reachability.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reachability.swift; sourceTree = ""; };
62 | 753C8FBD2237367E005FF13B /* SearchTrainPresenterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTrainPresenterTests.swift; sourceTree = ""; };
63 | 753F0843223618AB00175107 /* MyTravelHelper.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MyTravelHelper.app; sourceTree = BUILT_PRODUCTS_DIR; };
64 | 753F0846223618AB00175107 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
65 | 753F084B223618AB00175107 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
66 | 753F084D223618AD00175107 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
67 | 753F0850223618AD00175107 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
68 | 753F0852223618AD00175107 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
69 | 753F0857223618AE00175107 /* MyTravelHelperTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MyTravelHelperTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
70 | 753F085D223618AE00175107 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
71 | 753F087022361A6500175107 /* AppConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConstants.swift; sourceTree = ""; };
72 | 849A0A29A57A433EA5AB15FB /* Pods-MyTravelHelperTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MyTravelHelperTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-MyTravelHelperTests/Pods-MyTravelHelperTests.release.xcconfig"; sourceTree = ""; };
73 | AF635B96516F13D67ADE01AF /* Pods_MyTravelHelper.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MyTravelHelper.framework; sourceTree = BUILT_PRODUCTS_DIR; };
74 | BD4A9E0F8C97F8AA92AAA053 /* Pods_MyTravelHelperTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MyTravelHelperTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
75 | /* End PBXFileReference section */
76 |
77 | /* Begin PBXFrameworksBuildPhase section */
78 | 753F0840223618AB00175107 /* Frameworks */ = {
79 | isa = PBXFrameworksBuildPhase;
80 | buildActionMask = 2147483647;
81 | files = (
82 | 8CBBCE0D40B8FE266A1617DE /* Pods_MyTravelHelper.framework in Frameworks */,
83 | );
84 | runOnlyForDeploymentPostprocessing = 0;
85 | };
86 | 753F0854223618AE00175107 /* Frameworks */ = {
87 | isa = PBXFrameworksBuildPhase;
88 | buildActionMask = 2147483647;
89 | files = (
90 | 1654AF9B5118F79C1ACB2509 /* Pods_MyTravelHelperTests.framework in Frameworks */,
91 | );
92 | runOnlyForDeploymentPostprocessing = 0;
93 | };
94 | /* End PBXFrameworksBuildPhase section */
95 |
96 | /* Begin PBXGroup section */
97 | 2BABC0CEC890350419CA1D40 /* Pods */ = {
98 | isa = PBXGroup;
99 | children = (
100 | 4A3D05BF450D92647BC863F2 /* Pods-MyTravelHelper.debug.xcconfig */,
101 | 2F515F7A370B032163D445BC /* Pods-MyTravelHelper.release.xcconfig */,
102 | 11F481535B44216B54872EF2 /* Pods-MyTravelHelperTests.debug.xcconfig */,
103 | 849A0A29A57A433EA5AB15FB /* Pods-MyTravelHelperTests.release.xcconfig */,
104 | );
105 | name = Pods;
106 | sourceTree = "";
107 | };
108 | 5DCA91A6E1C4EF2A4B4FE637 /* Frameworks */ = {
109 | isa = PBXGroup;
110 | children = (
111 | AF635B96516F13D67ADE01AF /* Pods_MyTravelHelper.framework */,
112 | BD4A9E0F8C97F8AA92AAA053 /* Pods_MyTravelHelperTests.framework */,
113 | );
114 | name = Frameworks;
115 | sourceTree = "";
116 | };
117 | 753C8FB822367E64005FF13B /* App-Ui */ = {
118 | isa = PBXGroup;
119 | children = (
120 | 753F084F223618AD00175107 /* LaunchScreen.storyboard */,
121 | 753F084A223618AB00175107 /* Main.storyboard */,
122 | );
123 | path = "App-Ui";
124 | sourceTree = "";
125 | };
126 | 753F083A223618AB00175107 = {
127 | isa = PBXGroup;
128 | children = (
129 | 03399B7B25A4E6D000A8A343 /* readme.MD */,
130 | 753F0845223618AB00175107 /* MyTravelHelper */,
131 | 753F085A223618AE00175107 /* MyTravelHelperTests */,
132 | 753F0844223618AB00175107 /* Products */,
133 | 2BABC0CEC890350419CA1D40 /* Pods */,
134 | 5DCA91A6E1C4EF2A4B4FE637 /* Frameworks */,
135 | );
136 | sourceTree = "";
137 | };
138 | 753F0844223618AB00175107 /* Products */ = {
139 | isa = PBXGroup;
140 | children = (
141 | 753F0843223618AB00175107 /* MyTravelHelper.app */,
142 | 753F0857223618AE00175107 /* MyTravelHelperTests.xctest */,
143 | );
144 | name = Products;
145 | sourceTree = "";
146 | };
147 | 753F0845223618AB00175107 /* MyTravelHelper */ = {
148 | isa = PBXGroup;
149 | children = (
150 | 753C8FB822367E64005FF13B /* App-Ui */,
151 | 753F086F22361A5100175107 /* App-utils */,
152 | 753F08672236190700175107 /* Modules */,
153 | 753F0846223618AB00175107 /* AppDelegate.swift */,
154 | 753F084D223618AD00175107 /* Assets.xcassets */,
155 | 753F0852223618AD00175107 /* Info.plist */,
156 | );
157 | path = MyTravelHelper;
158 | sourceTree = "";
159 | };
160 | 753F085A223618AE00175107 /* MyTravelHelperTests */ = {
161 | isa = PBXGroup;
162 | children = (
163 | 753C8FBD2237367E005FF13B /* SearchTrainPresenterTests.swift */,
164 | 753F085D223618AE00175107 /* Info.plist */,
165 | );
166 | path = MyTravelHelperTests;
167 | sourceTree = "";
168 | };
169 | 753F08672236190700175107 /* Modules */ = {
170 | isa = PBXGroup;
171 | children = (
172 | 753F08682236191900175107 /* SearchTrains */,
173 | );
174 | path = Modules;
175 | sourceTree = "";
176 | };
177 | 753F08682236191900175107 /* SearchTrains */ = {
178 | isa = PBXGroup;
179 | children = (
180 | 753F086A2236196400175107 /* Entity */,
181 | 753F08692236196400175107 /* Interactor */,
182 | 753F086D2236196500175107 /* Presenter */,
183 | 753F086B2236196500175107 /* Protocols */,
184 | 753F086C2236196500175107 /* Router */,
185 | 753F086E2236196500175107 /* View */,
186 | );
187 | path = SearchTrains;
188 | sourceTree = "";
189 | };
190 | 753F08692236196400175107 /* Interactor */ = {
191 | isa = PBXGroup;
192 | children = (
193 | 753C8FAB223623C2005FF13B /* SearchTrainInteractor.swift */,
194 | );
195 | path = Interactor;
196 | sourceTree = "";
197 | };
198 | 753F086A2236196400175107 /* Entity */ = {
199 | isa = PBXGroup;
200 | children = (
201 | 753C8FA222361EC9005FF13B /* StationInfo.swift */,
202 | 753C8FA322361EC9005FF13B /* Stations.swift */,
203 | 753C8FA122361EC9005FF13B /* TrainMovements.swift */,
204 | );
205 | path = Entity;
206 | sourceTree = "";
207 | };
208 | 753F086B2236196500175107 /* Protocols */ = {
209 | isa = PBXGroup;
210 | children = (
211 | 753C8FA722361FC0005FF13B /* SearchTrainProtocols.swift */,
212 | );
213 | path = Protocols;
214 | sourceTree = "";
215 | };
216 | 753F086C2236196500175107 /* Router */ = {
217 | isa = PBXGroup;
218 | children = (
219 | 753C8FAF22365E9A005FF13B /* SearchTrainRouter.swift */,
220 | );
221 | path = Router;
222 | sourceTree = "";
223 | };
224 | 753F086D2236196500175107 /* Presenter */ = {
225 | isa = PBXGroup;
226 | children = (
227 | 753C8FA92236228C005FF13B /* SearchTrainPresenter.swift */,
228 | );
229 | path = Presenter;
230 | sourceTree = "";
231 | };
232 | 753F086E2236196500175107 /* View */ = {
233 | isa = PBXGroup;
234 | children = (
235 | 753C8FAD22362463005FF13B /* SearchTrainViewController.swift */,
236 | 753C8FB92236BD80005FF13B /* TrainInfoCell.swift */,
237 | );
238 | path = View;
239 | sourceTree = "";
240 | };
241 | 753F086F22361A5100175107 /* App-utils */ = {
242 | isa = PBXGroup;
243 | children = (
244 | 753C8FBB223731E3005FF13B /* Reachability.swift */,
245 | 753F087022361A6500175107 /* AppConstants.swift */,
246 | 753C8FB422367D1C005FF13B /* AppUtils.swift */,
247 | 753C8FB622367DF4005FF13B /* ProgressIndicator.swift */,
248 | 0301C30025A3875100A5E630 /* Extension.swift */,
249 | );
250 | path = "App-utils";
251 | sourceTree = "";
252 | };
253 | /* End PBXGroup section */
254 |
255 | /* Begin PBXNativeTarget section */
256 | 753F0842223618AB00175107 /* MyTravelHelper */ = {
257 | isa = PBXNativeTarget;
258 | buildConfigurationList = 753F0860223618AE00175107 /* Build configuration list for PBXNativeTarget "MyTravelHelper" */;
259 | buildPhases = (
260 | B959427FDE4AD37906C260C9 /* [CP] Check Pods Manifest.lock */,
261 | 753F083F223618AB00175107 /* Sources */,
262 | 753F0840223618AB00175107 /* Frameworks */,
263 | 753F0841223618AB00175107 /* Resources */,
264 | CEB0D538B5BF80D48BFD0091 /* [CP] Embed Pods Frameworks */,
265 | );
266 | buildRules = (
267 | );
268 | dependencies = (
269 | );
270 | name = MyTravelHelper;
271 | productName = MyTravelHelper;
272 | productReference = 753F0843223618AB00175107 /* MyTravelHelper.app */;
273 | productType = "com.apple.product-type.application";
274 | };
275 | 753F0856223618AE00175107 /* MyTravelHelperTests */ = {
276 | isa = PBXNativeTarget;
277 | buildConfigurationList = 753F0863223618AE00175107 /* Build configuration list for PBXNativeTarget "MyTravelHelperTests" */;
278 | buildPhases = (
279 | 4F7E6086A5A283A9BEEF924E /* [CP] Check Pods Manifest.lock */,
280 | 753F0853223618AE00175107 /* Sources */,
281 | 753F0854223618AE00175107 /* Frameworks */,
282 | 753F0855223618AE00175107 /* Resources */,
283 | );
284 | buildRules = (
285 | );
286 | dependencies = (
287 | 753F0859223618AE00175107 /* PBXTargetDependency */,
288 | );
289 | name = MyTravelHelperTests;
290 | productName = MyTravelHelperTests;
291 | productReference = 753F0857223618AE00175107 /* MyTravelHelperTests.xctest */;
292 | productType = "com.apple.product-type.bundle.unit-test";
293 | };
294 | /* End PBXNativeTarget section */
295 |
296 | /* Begin PBXProject section */
297 | 753F083B223618AB00175107 /* Project object */ = {
298 | isa = PBXProject;
299 | attributes = {
300 | LastSwiftUpdateCheck = 1010;
301 | LastUpgradeCheck = 1010;
302 | ORGANIZATIONNAME = Sample;
303 | TargetAttributes = {
304 | 753F0842223618AB00175107 = {
305 | CreatedOnToolsVersion = 10.1;
306 | LastSwiftMigration = 1020;
307 | };
308 | 753F0856223618AE00175107 = {
309 | CreatedOnToolsVersion = 10.1;
310 | LastSwiftMigration = 1020;
311 | TestTargetID = 753F0842223618AB00175107;
312 | };
313 | };
314 | };
315 | buildConfigurationList = 753F083E223618AB00175107 /* Build configuration list for PBXProject "MyTravelHelper" */;
316 | compatibilityVersion = "Xcode 9.3";
317 | developmentRegion = en;
318 | hasScannedForEncodings = 0;
319 | knownRegions = (
320 | en,
321 | Base,
322 | );
323 | mainGroup = 753F083A223618AB00175107;
324 | productRefGroup = 753F0844223618AB00175107 /* Products */;
325 | projectDirPath = "";
326 | projectRoot = "";
327 | targets = (
328 | 753F0842223618AB00175107 /* MyTravelHelper */,
329 | 753F0856223618AE00175107 /* MyTravelHelperTests */,
330 | );
331 | };
332 | /* End PBXProject section */
333 |
334 | /* Begin PBXResourcesBuildPhase section */
335 | 753F0841223618AB00175107 /* Resources */ = {
336 | isa = PBXResourcesBuildPhase;
337 | buildActionMask = 2147483647;
338 | files = (
339 | 753F0851223618AD00175107 /* LaunchScreen.storyboard in Resources */,
340 | 753F084E223618AD00175107 /* Assets.xcassets in Resources */,
341 | 03399B7C25A4E6D000A8A343 /* readme.MD in Resources */,
342 | 753F084C223618AB00175107 /* Main.storyboard in Resources */,
343 | );
344 | runOnlyForDeploymentPostprocessing = 0;
345 | };
346 | 753F0855223618AE00175107 /* Resources */ = {
347 | isa = PBXResourcesBuildPhase;
348 | buildActionMask = 2147483647;
349 | files = (
350 | );
351 | runOnlyForDeploymentPostprocessing = 0;
352 | };
353 | /* End PBXResourcesBuildPhase section */
354 |
355 | /* Begin PBXShellScriptBuildPhase section */
356 | 4F7E6086A5A283A9BEEF924E /* [CP] Check Pods Manifest.lock */ = {
357 | isa = PBXShellScriptBuildPhase;
358 | buildActionMask = 2147483647;
359 | files = (
360 | );
361 | inputPaths = (
362 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
363 | "${PODS_ROOT}/Manifest.lock",
364 | );
365 | name = "[CP] Check Pods Manifest.lock";
366 | outputPaths = (
367 | "$(DERIVED_FILE_DIR)/Pods-MyTravelHelperTests-checkManifestLockResult.txt",
368 | );
369 | runOnlyForDeploymentPostprocessing = 0;
370 | shellPath = /bin/sh;
371 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
372 | showEnvVarsInLog = 0;
373 | };
374 | B959427FDE4AD37906C260C9 /* [CP] Check Pods Manifest.lock */ = {
375 | isa = PBXShellScriptBuildPhase;
376 | buildActionMask = 2147483647;
377 | files = (
378 | );
379 | inputPaths = (
380 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
381 | "${PODS_ROOT}/Manifest.lock",
382 | );
383 | name = "[CP] Check Pods Manifest.lock";
384 | outputPaths = (
385 | "$(DERIVED_FILE_DIR)/Pods-MyTravelHelper-checkManifestLockResult.txt",
386 | );
387 | runOnlyForDeploymentPostprocessing = 0;
388 | shellPath = /bin/sh;
389 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
390 | showEnvVarsInLog = 0;
391 | };
392 | CEB0D538B5BF80D48BFD0091 /* [CP] Embed Pods Frameworks */ = {
393 | isa = PBXShellScriptBuildPhase;
394 | buildActionMask = 2147483647;
395 | files = (
396 | );
397 | inputFileListPaths = (
398 | "${PODS_ROOT}/Target Support Files/Pods-MyTravelHelper/Pods-MyTravelHelper-frameworks-${CONFIGURATION}-input-files.xcfilelist",
399 | );
400 | name = "[CP] Embed Pods Frameworks";
401 | outputFileListPaths = (
402 | "${PODS_ROOT}/Target Support Files/Pods-MyTravelHelper/Pods-MyTravelHelper-frameworks-${CONFIGURATION}-output-files.xcfilelist",
403 | );
404 | runOnlyForDeploymentPostprocessing = 0;
405 | shellPath = /bin/sh;
406 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-MyTravelHelper/Pods-MyTravelHelper-frameworks.sh\"\n";
407 | showEnvVarsInLog = 0;
408 | };
409 | /* End PBXShellScriptBuildPhase section */
410 |
411 | /* Begin PBXSourcesBuildPhase section */
412 | 753F083F223618AB00175107 /* Sources */ = {
413 | isa = PBXSourcesBuildPhase;
414 | buildActionMask = 2147483647;
415 | files = (
416 | 753C8FBA2236BD80005FF13B /* TrainInfoCell.swift in Sources */,
417 | 753C8FA822361FC0005FF13B /* SearchTrainProtocols.swift in Sources */,
418 | 753C8FAA2236228C005FF13B /* SearchTrainPresenter.swift in Sources */,
419 | 753C8FAE22362463005FF13B /* SearchTrainViewController.swift in Sources */,
420 | 753C8FB722367DF4005FF13B /* ProgressIndicator.swift in Sources */,
421 | 753C8FB522367D1C005FF13B /* AppUtils.swift in Sources */,
422 | 753C8FA522361EC9005FF13B /* StationInfo.swift in Sources */,
423 | 753C8FBC223731E3005FF13B /* Reachability.swift in Sources */,
424 | 0301C30125A3875100A5E630 /* Extension.swift in Sources */,
425 | 753C8FA622361EC9005FF13B /* Stations.swift in Sources */,
426 | 753C8FAC223623C2005FF13B /* SearchTrainInteractor.swift in Sources */,
427 | 753F087122361A6500175107 /* AppConstants.swift in Sources */,
428 | 753F0847223618AB00175107 /* AppDelegate.swift in Sources */,
429 | 753C8FA422361EC9005FF13B /* TrainMovements.swift in Sources */,
430 | 753C8FB022365E9A005FF13B /* SearchTrainRouter.swift in Sources */,
431 | );
432 | runOnlyForDeploymentPostprocessing = 0;
433 | };
434 | 753F0853223618AE00175107 /* Sources */ = {
435 | isa = PBXSourcesBuildPhase;
436 | buildActionMask = 2147483647;
437 | files = (
438 | 753C8FBE2237367E005FF13B /* SearchTrainPresenterTests.swift in Sources */,
439 | );
440 | runOnlyForDeploymentPostprocessing = 0;
441 | };
442 | /* End PBXSourcesBuildPhase section */
443 |
444 | /* Begin PBXTargetDependency section */
445 | 753F0859223618AE00175107 /* PBXTargetDependency */ = {
446 | isa = PBXTargetDependency;
447 | target = 753F0842223618AB00175107 /* MyTravelHelper */;
448 | targetProxy = 753F0858223618AE00175107 /* PBXContainerItemProxy */;
449 | };
450 | /* End PBXTargetDependency section */
451 |
452 | /* Begin PBXVariantGroup section */
453 | 753F084A223618AB00175107 /* Main.storyboard */ = {
454 | isa = PBXVariantGroup;
455 | children = (
456 | 753F084B223618AB00175107 /* Base */,
457 | );
458 | name = Main.storyboard;
459 | sourceTree = "";
460 | };
461 | 753F084F223618AD00175107 /* LaunchScreen.storyboard */ = {
462 | isa = PBXVariantGroup;
463 | children = (
464 | 753F0850223618AD00175107 /* Base */,
465 | );
466 | name = LaunchScreen.storyboard;
467 | sourceTree = "";
468 | };
469 | /* End PBXVariantGroup section */
470 |
471 | /* Begin XCBuildConfiguration section */
472 | 753F085E223618AE00175107 /* Debug */ = {
473 | isa = XCBuildConfiguration;
474 | buildSettings = {
475 | ALWAYS_SEARCH_USER_PATHS = NO;
476 | CLANG_ANALYZER_NONNULL = YES;
477 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
478 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
479 | CLANG_CXX_LIBRARY = "libc++";
480 | CLANG_ENABLE_MODULES = YES;
481 | CLANG_ENABLE_OBJC_ARC = YES;
482 | CLANG_ENABLE_OBJC_WEAK = YES;
483 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
484 | CLANG_WARN_BOOL_CONVERSION = YES;
485 | CLANG_WARN_COMMA = YES;
486 | CLANG_WARN_CONSTANT_CONVERSION = YES;
487 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
488 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
489 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
490 | CLANG_WARN_EMPTY_BODY = YES;
491 | CLANG_WARN_ENUM_CONVERSION = YES;
492 | CLANG_WARN_INFINITE_RECURSION = YES;
493 | CLANG_WARN_INT_CONVERSION = YES;
494 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
495 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
496 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
497 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
498 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
499 | CLANG_WARN_STRICT_PROTOTYPES = YES;
500 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
501 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
502 | CLANG_WARN_UNREACHABLE_CODE = YES;
503 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
504 | CODE_SIGN_IDENTITY = "iPhone Developer";
505 | COPY_PHASE_STRIP = NO;
506 | DEBUG_INFORMATION_FORMAT = dwarf;
507 | ENABLE_STRICT_OBJC_MSGSEND = YES;
508 | ENABLE_TESTABILITY = YES;
509 | GCC_C_LANGUAGE_STANDARD = gnu11;
510 | GCC_DYNAMIC_NO_PIC = NO;
511 | GCC_NO_COMMON_BLOCKS = YES;
512 | GCC_OPTIMIZATION_LEVEL = 0;
513 | GCC_PREPROCESSOR_DEFINITIONS = (
514 | "DEBUG=1",
515 | "$(inherited)",
516 | );
517 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
518 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
519 | GCC_WARN_UNDECLARED_SELECTOR = YES;
520 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
521 | GCC_WARN_UNUSED_FUNCTION = YES;
522 | GCC_WARN_UNUSED_VARIABLE = YES;
523 | IPHONEOS_DEPLOYMENT_TARGET = 12.1;
524 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
525 | MTL_FAST_MATH = YES;
526 | ONLY_ACTIVE_ARCH = YES;
527 | SDKROOT = iphoneos;
528 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
529 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
530 | };
531 | name = Debug;
532 | };
533 | 753F085F223618AE00175107 /* Release */ = {
534 | isa = XCBuildConfiguration;
535 | buildSettings = {
536 | ALWAYS_SEARCH_USER_PATHS = NO;
537 | CLANG_ANALYZER_NONNULL = YES;
538 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
539 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
540 | CLANG_CXX_LIBRARY = "libc++";
541 | CLANG_ENABLE_MODULES = YES;
542 | CLANG_ENABLE_OBJC_ARC = YES;
543 | CLANG_ENABLE_OBJC_WEAK = YES;
544 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
545 | CLANG_WARN_BOOL_CONVERSION = YES;
546 | CLANG_WARN_COMMA = YES;
547 | CLANG_WARN_CONSTANT_CONVERSION = YES;
548 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
549 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
550 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
551 | CLANG_WARN_EMPTY_BODY = YES;
552 | CLANG_WARN_ENUM_CONVERSION = YES;
553 | CLANG_WARN_INFINITE_RECURSION = YES;
554 | CLANG_WARN_INT_CONVERSION = YES;
555 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
556 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
557 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
558 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
559 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
560 | CLANG_WARN_STRICT_PROTOTYPES = YES;
561 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
562 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
563 | CLANG_WARN_UNREACHABLE_CODE = YES;
564 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
565 | CODE_SIGN_IDENTITY = "iPhone Developer";
566 | COPY_PHASE_STRIP = NO;
567 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
568 | ENABLE_NS_ASSERTIONS = NO;
569 | ENABLE_STRICT_OBJC_MSGSEND = YES;
570 | GCC_C_LANGUAGE_STANDARD = gnu11;
571 | GCC_NO_COMMON_BLOCKS = YES;
572 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
573 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
574 | GCC_WARN_UNDECLARED_SELECTOR = YES;
575 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
576 | GCC_WARN_UNUSED_FUNCTION = YES;
577 | GCC_WARN_UNUSED_VARIABLE = YES;
578 | IPHONEOS_DEPLOYMENT_TARGET = 12.1;
579 | MTL_ENABLE_DEBUG_INFO = NO;
580 | MTL_FAST_MATH = YES;
581 | SDKROOT = iphoneos;
582 | SWIFT_COMPILATION_MODE = wholemodule;
583 | SWIFT_OPTIMIZATION_LEVEL = "-O";
584 | VALIDATE_PRODUCT = YES;
585 | };
586 | name = Release;
587 | };
588 | 753F0861223618AE00175107 /* Debug */ = {
589 | isa = XCBuildConfiguration;
590 | baseConfigurationReference = 4A3D05BF450D92647BC863F2 /* Pods-MyTravelHelper.debug.xcconfig */;
591 | buildSettings = {
592 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
593 | CODE_SIGN_STYLE = Automatic;
594 | INFOPLIST_FILE = MyTravelHelper/Info.plist;
595 | LD_RUNPATH_SEARCH_PATHS = (
596 | "$(inherited)",
597 | "@executable_path/Frameworks",
598 | );
599 | PRODUCT_BUNDLE_IDENTIFIER = com.example.com.MyTravelHelper;
600 | PRODUCT_NAME = "$(TARGET_NAME)";
601 | SWIFT_VERSION = 5.0;
602 | TARGETED_DEVICE_FAMILY = "1,2";
603 | };
604 | name = Debug;
605 | };
606 | 753F0862223618AE00175107 /* Release */ = {
607 | isa = XCBuildConfiguration;
608 | baseConfigurationReference = 2F515F7A370B032163D445BC /* Pods-MyTravelHelper.release.xcconfig */;
609 | buildSettings = {
610 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
611 | CODE_SIGN_STYLE = Automatic;
612 | INFOPLIST_FILE = MyTravelHelper/Info.plist;
613 | LD_RUNPATH_SEARCH_PATHS = (
614 | "$(inherited)",
615 | "@executable_path/Frameworks",
616 | );
617 | PRODUCT_BUNDLE_IDENTIFIER = com.example.com.MyTravelHelper;
618 | PRODUCT_NAME = "$(TARGET_NAME)";
619 | SWIFT_VERSION = 5.0;
620 | TARGETED_DEVICE_FAMILY = "1,2";
621 | };
622 | name = Release;
623 | };
624 | 753F0864223618AE00175107 /* Debug */ = {
625 | isa = XCBuildConfiguration;
626 | baseConfigurationReference = 11F481535B44216B54872EF2 /* Pods-MyTravelHelperTests.debug.xcconfig */;
627 | buildSettings = {
628 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
629 | BUNDLE_LOADER = "$(TEST_HOST)";
630 | CODE_SIGN_STYLE = Automatic;
631 | INFOPLIST_FILE = MyTravelHelperTests/Info.plist;
632 | LD_RUNPATH_SEARCH_PATHS = (
633 | "$(inherited)",
634 | "@executable_path/Frameworks",
635 | "@loader_path/Frameworks",
636 | );
637 | PRODUCT_BUNDLE_IDENTIFIER = com.example.com.MyTravelHelperTests;
638 | PRODUCT_NAME = "$(TARGET_NAME)";
639 | SWIFT_VERSION = 5.0;
640 | TARGETED_DEVICE_FAMILY = "1,2";
641 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MyTravelHelper.app/MyTravelHelper";
642 | };
643 | name = Debug;
644 | };
645 | 753F0865223618AE00175107 /* Release */ = {
646 | isa = XCBuildConfiguration;
647 | baseConfigurationReference = 849A0A29A57A433EA5AB15FB /* Pods-MyTravelHelperTests.release.xcconfig */;
648 | buildSettings = {
649 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
650 | BUNDLE_LOADER = "$(TEST_HOST)";
651 | CODE_SIGN_STYLE = Automatic;
652 | INFOPLIST_FILE = MyTravelHelperTests/Info.plist;
653 | LD_RUNPATH_SEARCH_PATHS = (
654 | "$(inherited)",
655 | "@executable_path/Frameworks",
656 | "@loader_path/Frameworks",
657 | );
658 | PRODUCT_BUNDLE_IDENTIFIER = com.example.com.MyTravelHelperTests;
659 | PRODUCT_NAME = "$(TARGET_NAME)";
660 | SWIFT_VERSION = 5.0;
661 | TARGETED_DEVICE_FAMILY = "1,2";
662 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/MyTravelHelper.app/MyTravelHelper";
663 | };
664 | name = Release;
665 | };
666 | /* End XCBuildConfiguration section */
667 |
668 | /* Begin XCConfigurationList section */
669 | 753F083E223618AB00175107 /* Build configuration list for PBXProject "MyTravelHelper" */ = {
670 | isa = XCConfigurationList;
671 | buildConfigurations = (
672 | 753F085E223618AE00175107 /* Debug */,
673 | 753F085F223618AE00175107 /* Release */,
674 | );
675 | defaultConfigurationIsVisible = 0;
676 | defaultConfigurationName = Release;
677 | };
678 | 753F0860223618AE00175107 /* Build configuration list for PBXNativeTarget "MyTravelHelper" */ = {
679 | isa = XCConfigurationList;
680 | buildConfigurations = (
681 | 753F0861223618AE00175107 /* Debug */,
682 | 753F0862223618AE00175107 /* Release */,
683 | );
684 | defaultConfigurationIsVisible = 0;
685 | defaultConfigurationName = Release;
686 | };
687 | 753F0863223618AE00175107 /* Build configuration list for PBXNativeTarget "MyTravelHelperTests" */ = {
688 | isa = XCConfigurationList;
689 | buildConfigurations = (
690 | 753F0864223618AE00175107 /* Debug */,
691 | 753F0865223618AE00175107 /* Release */,
692 | );
693 | defaultConfigurationIsVisible = 0;
694 | defaultConfigurationName = Release;
695 | };
696 | /* End XCConfigurationList section */
697 | };
698 | rootObject = 753F083B223618AB00175107 /* Project object */;
699 | }
700 |
--------------------------------------------------------------------------------
/MyTravelHelperTests/MockResponses.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MockResponses.swift
3 | // MyTravelHelperTests
4 | //
5 | // Created by Navroz on 10/01/21.
6 | // Copyright © 2021 Sample. All rights reserved.
7 | //
8 |
9 | import XMLParsing
10 | @testable import MyTravelHelper
11 | //TODO: Construct modals with init
12 | struct Responses {
13 | //MARK: TrainMovements
14 | static let trainMovements = "\r\n\r\n \r\n A867 \r\n 10 Jan 2021\r\n BALNA\r\n Ballina\r\n 1\r\n O\r\n Ballina\r\n Manulla Junction\r\n 00:00:00\r\n 15:35:00\r\n 00:00:00\r\n 15:35:00\r\n \r\n \r\n \r\n \r\n C\r\n \r\n \r\n A867 \r\n 10 Jan 2021\r\n FXFRD\r\n Foxford\r\n 2\r\n S\r\n Ballina\r\n Manulla Junction\r\n 15:46:30\r\n 15:47:00\r\n 15:46:30\r\n 15:47:00\r\n \r\n \r\n \r\n \r\n N\r\n \r\n \r\n A867 \r\n 10 Jan 2021\r\n MNLAJ\r\n Manulla Junction\r\n 3\r\n D\r\n Ballina\r\n Manulla Junction\r\n 16:03:00\r\n 00:00:00\r\n 16:03:00\r\n 00:00:00\r\n \r\n \r\n \r\n \r\n -\r\n \r\n"
15 | static func TrainMovements() -> TrainMovementsData {
16 | guard let object = try? XMLDecoder().decode(TrainMovementsData.self, from: Data(Responses.trainMovements.utf8))
17 | else {
18 | fatalError("Unable to decode static responses")
19 | }
20 |
21 | return object
22 | }
23 |
24 |
25 | //MARK: TrainList
26 | static let trainList = "\r\n\r\n \r\n 2021-01-10T14:07:28.81\r\n A864 \r\n Ballina\r\n BALNA\r\n 14:07:28\r\n 10 Jan 2021\r\n Manulla Junction\r\n Ballina\r\n 13:43\r\n 14:11\r\n No Information\r\n \r\n 4\r\n 0\r\n 14:11\r\n 00:00\r\n 14:11\r\n 00:00\r\n To Ballina\r\n DMU\r\n D\r\n \r\n \r\n 2021-01-10T14:07:28.81\r\n A867 \r\n Ballina\r\n BALNA\r\n 14:07:28\r\n 10 Jan 2021\r\n Ballina\r\n Manulla Junction\r\n 15:35\r\n 16:03\r\n No Information\r\n \r\n 88\r\n 0\r\n 00:00\r\n 15:35\r\n 00:00\r\n 15:35\r\n To Manulla Junction\r\n DMU\r\n O\r\n \r\n"
27 |
28 | static func TrainList() -> [StationTrain] {
29 | guard var object = try? XMLDecoder().decode(StationData.self, from: Data(Responses.trainList.utf8))
30 | else {
31 | fatalError("Unable to decode static responses")
32 | }
33 | object.trainsList[0].destinationDetails = self.TrainMovements().trainMovements[0]
34 | return object.trainsList
35 | }
36 |
37 | //MARK: ALL Station
38 | static let allStation =
39 | "\r\n\r\n \r\n Belfast\r\n \r\n 54.6123\r\n -5.91744\r\n BFSTC\r\n 228\r\n \r\n \r\n Lisburn\r\n \r\n 54.514\r\n -6.04327\r\n LBURN\r\n 238\r\n \r\n \r\n Lurgan\r\n \r\n 54.4672\r\n -6.33547\r\n LURGN\r\n 241\r\n \r\n \r\n Portadown\r\n \r\n 54.4295\r\n -6.43868\r\n PDOWN\r\n 242\r\n \r\n \r\n Sligo\r\n \r\n 54.2723\r\n -8.48249\r\n SLIGO\r\n 180\r\n \r\n \r\n Newry\r\n \r\n 54.1911\r\n -6.36225\r\n NEWRY\r\n 260\r\n \r\n \r\n Collooney\r\n \r\n 54.1871\r\n -8.49453\r\n COLNY\r\n 177\r\n \r\n \r\n Ballina\r\n \r\n 54.1085\r\n -9.16146\r\n BALNA\r\n 167\r\n \r\n \r\n Ballymote\r\n \r\n 54.0887\r\n -8.52088\r\n BMOTE\r\n 176\r\n \r\n \r\n Dundalk\r\n \r\n 54.0007\r\n -6.41291\r\n DDALK\r\n 123\r\n \r\n \r\n Foxford\r\n \r\n 53.983\r\n -9.1364\r\n FXFRD\r\n 193\r\n \r\n \r\n Boyle\r\n \r\n 53.9676\r\n -8.30438\r\n BOYLE\r\n 175\r\n \r\n \r\n Carrick on Shannon\r\n \r\n 53.9383\r\n -8.10657\r\n CKOSH\r\n 174\r\n \r\n \r\n Dromod\r\n \r\n 53.8591\r\n -7.9164\r\n DRMOD\r\n 173\r\n \r\n \r\n Castlebar\r\n \r\n 53.8471\r\n -9.2873\r\n CLBAR\r\n 168\r\n \r\n \r\n Manulla Junction\r\n \r\n 53.828\r\n -9.19296\r\n MNLAJ\r\n 194\r\n \r\n \r\n Westport\r\n \r\n 53.7955\r\n -9.50885\r\n WPORT\r\n 169\r\n \r\n \r\n Ballyhaunis\r\n \r\n 53.7616\r\n -8.7584\r\n BYHNS\r\n 165\r\n \r\n \r\n Castlerea\r\n \r\n 53.7612\r\n -8.48448\r\n CSREA\r\n 164\r\n \r\n \r\n Longford\r\n \r\n 53.7243\r\n -7.79574\r\n LFORD\r\n 172\r\n \r\n \r\n Claremorris\r\n \r\n 53.7204\r\n -9.00222\r\n CLMRS\r\n 166\r\n \r\n \r\n Drogheda\r\n \r\n 53.712\r\n -6.33538\r\n DGHDA\r\n 120\r\n \r\n \r\n Edgeworthstown\r\n \r\n 53.6888\r\n -7.60299\r\n ETOWN\r\n 171\r\n \r\n \r\n Laytown\r\n \r\n 53.6794\r\n -6.24253\r\n LTOWN\r\n 119\r\n \r\n \r\n Gormanston\r\n \r\n 53.638\r\n -6.21705\r\n GSTON\r\n 117\r\n \r\n \r\n Roscommon\r\n \r\n 53.6243\r\n -8.19631\r\n RSCMN\r\n 163\r\n \r\n \r\n Balbriggan\r\n \r\n 53.6118\r\n -6.18226\r\n BBRGN\r\n 116\r\n \r\n \r\n Skerries\r\n \r\n 53.5741\r\n -6.11933\r\n SKRES\r\n 115\r\n \r\n \r\n Mullingar\r\n \r\n 53.523\r\n -7.34608\r\n MLGAR\r\n 153\r\n \r\n \r\n Rush and Lusk\r\n \r\n 53.5201\r\n -6.1439\r\n RLUSK\r\n 114\r\n \r\n \r\n Donabate\r\n \r\n 53.4855\r\n -6.15134\r\n DBATE\r\n 113\r\n \r\n \r\n Malahide\r\n \r\n 53.4509\r\n -6.15649\r\n MHIDE\r\n 112\r\n \r\n \r\n M3 Parkway\r\n \r\n 53.4349\r\n -6.46898\r\n M3WAY\r\n 86\r\n \r\n \r\n Athlone\r\n \r\n 53.4273\r\n -7.93683\r\n ATLNE\r\n 156\r\n \r\n \r\n Dunboyne\r\n \r\n 53.4175\r\n -6.46483\r\n DBYNE\r\n 85\r\n \r\n \r\n Portmarnock\r\n \r\n 53.4169\r\n -6.1512\r\n PMNCK\r\n 111\r\n \r\n \r\n Enfield\r\n \r\n 53.4157\r\n -6.83395\r\n ENFLD\r\n 83\r\n \r\n \r\n Kilcock\r\n \r\n 53.4043\r\n -6.67892\r\n KCOCK\r\n 90\r\n \r\n \r\n Clongriffin\r\n \r\n 53.4032\r\n -6.14839\r\n GRGRD\r\n 187\r\n \r\n \r\n Sutton\r\n \r\n 53.392\r\n -6.11448\r\n SUTTN\r\n 107\r\n \r\n \r\n Bayside\r\n \r\n 53.3917\r\n -6.13678\r\n BYSDE\r\n 106\r\n \r\n \r\n Howth Junction\r\n Donaghmede ( Howth Junction )\r\n 53.3909\r\n -6.15672\r\n HWTHJ\r\n 105\r\n \r\n \r\n Howth\r\n \r\n 53.3891\r\n -6.07401\r\n HOWTH\r\n 108\r\n \r\n \r\n Kilbarrack\r\n \r\n 53.387\r\n -6.16163\r\n KBRCK\r\n 104\r\n \r\n \r\n Hansfield\r\n \r\n 53.3853\r\n -6.44205\r\n HAFLD\r\n 87\r\n \r\n \r\n Clonsilla\r\n \r\n 53.3831\r\n -6.4242\r\n CLSLA\r\n 94\r\n \r\n \r\n Castleknock\r\n \r\n 53.3816\r\n -6.37149\r\n CNOCK\r\n 96\r\n \r\n \r\n Raheny\r\n \r\n 53.3815\r\n -6.17699\r\n RAHNY\r\n 103\r\n \r\n \r\n Harmonstown\r\n \r\n 53.3786\r\n -6.19131\r\n HTOWN\r\n 102\r\n \r\n \r\n Maynooth\r\n \r\n 53.378\r\n -6.58993\r\n MYNTH\r\n 91\r\n \r\n \r\n Navan Road Parkway\r\n Phoenix Park\r\n 53.3777\r\n -6.34591\r\n PHNPK\r\n 89\r\n \r\n \r\n Coolmine\r\n \r\n 53.3776\r\n -6.39072\r\n CMINE\r\n 95\r\n \r\n \r\n Ashtown\r\n \r\n 53.3755\r\n -6.33135\r\n ASHTN\r\n 97\r\n \r\n \r\n Leixlip (Confey)\r\n \r\n 53.3743\r\n -6.48624\r\n LXCON\r\n 93\r\n \r\n \r\n Killester\r\n \r\n 53.373\r\n -6.20442\r\n KLSTR\r\n 101\r\n \r\n \r\n Broombridge\r\n \r\n 53.3725\r\n -6.29869\r\n BBRDG\r\n 98\r\n \r\n \r\n Leixlip (Louisa Bridge)\r\n \r\n 53.3704\r\n -6.50598\r\n LXLSA\r\n 92\r\n \r\n \r\n Drumcondra\r\n \r\n 53.3632\r\n -6.25908\r\n DCDRA\r\n 99\r\n \r\n \r\n Clontarf Road\r\n \r\n 53.3629\r\n -6.22753\r\n CTARF\r\n 109\r\n \r\n \r\n Dublin Connolly\r\n Connolly\r\n 53.3531\r\n -6.24591\r\n CNLLY\r\n 100\r\n \r\n \r\n Docklands\r\n \r\n 53.3509\r\n -6.23929\r\n DCKLS\r\n 84\r\n \r\n \r\n Tara Street\r\n \r\n 53.347\r\n -6.25425\r\n TARA \r\n 124\r\n \r\n \r\n Dublin Heuston\r\n Heuston\r\n 53.3464\r\n -6.29461\r\n HSTON\r\n 1\r\n \r\n \r\n Dublin Pearse\r\n Pearse\r\n 53.3433\r\n -6.24829\r\n PERSE\r\n 150\r\n \r\n \r\n Woodlawn\r\n \r\n 53.3432\r\n -8.47231\r\n WLAWN\r\n 158\r\n \r\n \r\n Grand Canal Dock\r\n \r\n 53.3397\r\n -6.23773\r\n GCDK \r\n 110\r\n \r\n \r\n Clara\r\n \r\n 53.3395\r\n -7.61596\r\n CLARA\r\n 73\r\n \r\n \r\n Ballinasloe\r\n \r\n 53.3363\r\n -8.24081\r\n BSLOE\r\n 157\r\n \r\n \r\n Adamstown\r\n \r\n 53.3353\r\n -7\r\n ADAMS\r\n 1075\r\n \r\n \r\n Adamstown\r\n \r\n 53.3353\r\n -6.45233\r\n ADAMF\r\n 975\r\n \r\n \r\n Adamstown\r\n \r\n 53.3353\r\n -6.45233\r\n ADMTN\r\n 75\r\n \r\n \r\n Lansdowne Road\r\n \r\n 53.3347\r\n -6.22979\r\n LDWNE\r\n 125\r\n \r\n \r\n Park West and Cherry Orchard\r\n Park West (Cherry Orchard )\r\n 53.334\r\n -6.37868\r\n CHORC\r\n 76\r\n \r\n \r\n Park West and Cherry Orchard\r\n Park West (Cherry Orchard )\r\n 53.334\r\n -6.37868\r\n PWESF\r\n 976\r\n \r\n \r\n PARK WEST\r\n Park West (Cherry Orchard )\r\n 53.334\r\n -6.37868\r\n PWESS\r\n 1076\r\n \r\n \r\n Clondalkin\r\n Fonthill ( Clondalkin )\r\n 53.3334\r\n -6.40628\r\n CLONS\r\n 1077\r\n \r\n \r\n Clondalkin\r\n Fonthill ( Clondalkin )\r\n 53.3334\r\n -6.40628\r\n CLONF\r\n 977\r\n \r\n \r\n Clondalkin\r\n Fonthill ( Clondalkin )\r\n 53.3334\r\n -6.40628\r\n CLDKN\r\n 77\r\n \r\n \r\n Sandymount\r\n \r\n 53.3281\r\n -6.22116\r\n SMONT\r\n 188\r\n \r\n \r\n Hazelhatch\r\n Celbridge (Hazelhatch ) \r\n 53.3223\r\n -6.52356\r\n HZLCH\r\n 78\r\n \r\n \r\n Hazelhatch\r\n Celbridge (Hazelhatch ) \r\n 53.3223\r\n -6.52356\r\n HAZEF\r\n 978\r\n \r\n \r\n Hazelhatch\r\n Celbridge (Hazelhatch ) \r\n 53.3223\r\n -6.52356\r\n HAZES\r\n 1078\r\n \r\n \r\n Attymon\r\n \r\n 53.3212\r\n -8.60608\r\n ATMON\r\n 159\r\n \r\n \r\n Sydney Parade\r\n \r\n 53.3206\r\n -6.21112\r\n SIDNY\r\n 126\r\n \r\n \r\n Booterstown\r\n \r\n 53.3099\r\n -6.19498\r\n BTSTN\r\n 127\r\n \r\n \r\n Blackrock\r\n \r\n 53.3027\r\n -6.17833\r\n BROCK\r\n 128\r\n \r\n \r\n Athenry\r\n \r\n 53.3015\r\n -8.74855\r\n ATHRY\r\n 162\r\n \r\n \r\n Seapoint\r\n \r\n 53.2991\r\n -6.16512\r\n SEAPT\r\n 129\r\n \r\n \r\n Salthill and Monkstown\r\n Monkstown ( Salthill )\r\n 53.2954\r\n -6.15206\r\n SHILL\r\n 130\r\n \r\n \r\n Dun Laoghaire\r\n \r\n 53.2951\r\n -6.13498\r\n DLERY\r\n 131\r\n \r\n \r\n Sandycove\r\n Glasthule (Sandycove ) \r\n 53.2878\r\n -6.12712\r\n SCOVE\r\n 132\r\n \r\n \r\n Glenageary\r\n \r\n 53.2812\r\n -6.12289\r\n GLGRY\r\n 133\r\n \r\n \r\n Dalkey\r\n \r\n 53.2756\r\n -6.10333\r\n DLKEY\r\n 134\r\n \r\n \r\n Oranmore\r\n \r\n 53.2751\r\n -8.94792\r\n ORNMR\r\n 161\r\n \r\n \r\n Galway\r\n \r\n 53.2736\r\n -9.04696\r\n GALWY\r\n 170\r\n \r\n \r\n Tullamore\r\n \r\n 53.2704\r\n -7.49985\r\n TMORE\r\n 72\r\n \r\n \r\n Killiney\r\n \r\n 53.2557\r\n -6.11317\r\n KILNY\r\n 135\r\n \r\n \r\n Sallins\r\n \r\n 53.2469\r\n -6.66386\r\n SALNS\r\n 79\r\n \r\n \r\n Shankill\r\n \r\n 53.2364\r\n -6.11691\r\n SKILL\r\n 136\r\n \r\n \r\n Craughwell\r\n \r\n 53.2252\r\n -8.7359\r\n CRGHW\r\n 184\r\n \r\n \r\n Woodbrook\r\n \r\n 53.22\r\n -6.1101\r\n WBROK\r\n 801\r\n \r\n \r\n Bray\r\n \r\n 53.2043\r\n -6.10046\r\n BRAY \r\n 140\r\n \r\n \r\n Newbridge\r\n \r\n 53.1855\r\n -6.80807\r\n NBRGE\r\n 4\r\n \r\n \r\n Curragh\r\n \r\n 53.1725\r\n -6.86245\r\n CURAH\r\n 5\r\n \r\n \r\n Kildare\r\n \r\n 53.163\r\n -6.90802\r\n KDARE\r\n 6\r\n \r\n \r\n Ardrahan\r\n \r\n 53.1572\r\n -8.81483\r\n ARHAN\r\n 183\r\n \r\n \r\n Portarlington\r\n \r\n 53.146\r\n -7.18055\r\n PTRTN\r\n 8\r\n \r\n \r\n Monasterevin\r\n \r\n 53.1454\r\n -7.06361\r\n MONVN\r\n 7\r\n \r\n \r\n Greystones\r\n \r\n 53.1442\r\n -6.06085\r\n GSTNS\r\n 141\r\n \r\n \r\n Kilcoole\r\n \r\n 53.107\r\n -6.04112\r\n KCOOL\r\n 139\r\n \r\n \r\n Gort\r\n \r\n 53.0653\r\n -8.81595\r\n GORT\r\n 182\r\n \r\n \r\n Portlaoise\r\n \r\n 53.0371\r\n -7.30086\r\n PTLSE\r\n 9\r\n \r\n \r\n Athy\r\n \r\n 52.992\r\n -6.9762\r\n ATHY \r\n 45\r\n \r\n \r\n Wicklow\r\n \r\n 52.9882\r\n -6.05338\r\n WLOW \r\n 142\r\n \r\n \r\n Roscrea\r\n \r\n 52.9607\r\n -7.7941\r\n RCREA\r\n 31\r\n \r\n \r\n Cloughjordan\r\n \r\n 52.9363\r\n -8.0246\r\n CJRDN\r\n 32\r\n \r\n \r\n Rathdrum\r\n \r\n 52.9295\r\n -6.22641\r\n RDRUM\r\n 143\r\n \r\n \r\n Ballybrophy\r\n \r\n 52.8999\r\n -7.60259\r\n BBRHY\r\n 11\r\n \r\n \r\n Nenagh\r\n \r\n 52.8605\r\n -8.19471\r\n NNAGH\r\n 33\r\n \r\n \r\n Carlow\r\n \r\n 52.8407\r\n -6.92217\r\n CRLOW\r\n 46\r\n \r\n \r\n Ennis\r\n \r\n 52.8386\r\n -8.97491\r\n ENNIS\r\n 181\r\n \r\n \r\n Arklow\r\n \r\n 52.7932\r\n -6.15994\r\n ARKLW\r\n 144\r\n \r\n \r\n Templemore\r\n \r\n 52.7878\r\n -7.82293\r\n TPMOR\r\n 12\r\n \r\n \r\n Birdhill\r\n \r\n 52.7656\r\n -8.44247\r\n BHILL\r\n 34\r\n \r\n \r\n Sixmilebridge\r\n \r\n 52.7376\r\n -8.78427\r\n SXMBR\r\n 185\r\n \r\n \r\n Castleconnell\r\n \r\n 52.7128\r\n -8.49794\r\n CCONL\r\n 35\r\n \r\n \r\n Muine Bheag\r\n Bagenalstown\r\n 52.699\r\n -6.95213\r\n MNEBG\r\n 47\r\n \r\n \r\n Thurles\r\n \r\n 52.6766\r\n -7.82189\r\n THRLS\r\n 13\r\n \r\n \r\n Gorey\r\n \r\n 52.6712\r\n -6.29195\r\n GOREY\r\n 145\r\n \r\n \r\n Limerick\r\n \r\n 52.6587\r\n -8.62397\r\n LMRCK\r\n 40\r\n \r\n \r\n Kilkenny\r\n \r\n 52.655\r\n -7.24498\r\n KKNNY\r\n 48\r\n \r\n \r\n Thomastown\r\n \r\n 52.523\r\n -7.14891\r\n THTWN\r\n 49\r\n \r\n \r\n Enniscorthy\r\n \r\n 52.5046\r\n -6.56627\r\n ECRTY\r\n 147\r\n \r\n \r\n Limerick Junction\r\n \r\n 52.5009\r\n -8.20003\r\n LMRKJ\r\n 16\r\n \r\n \r\n Tipperary\r\n \r\n 52.4701\r\n -8.1625\r\n TIPRY\r\n 41\r\n \r\n \r\n Cahir\r\n \r\n 52.3777\r\n -7.92181\r\n CAHIR\r\n 42\r\n \r\n \r\n Clonmel\r\n \r\n 52.3611\r\n -7.69936\r\n CLMEL\r\n 43\r\n \r\n \r\n Carrick on Suir\r\n \r\n 52.3487\r\n -7.40354\r\n CKOSR\r\n 44\r\n \r\n \r\n Charleville\r\n \r\n 52.3468\r\n -8.65362\r\n CVILL\r\n 19\r\n \r\n \r\n Wexford\r\n \r\n 52.3434\r\n -6.4636\r\n WXFRD\r\n 148\r\n \r\n \r\n Campile\r\n \r\n 52.2855\r\n -6.93896\r\n CPILE\r\n 52\r\n \r\n \r\n Ballycullane\r\n \r\n 52.2834\r\n -6.83958\r\n BCLAN\r\n 53\r\n \r\n \r\n Rosslare Strand\r\n \r\n 52.2726\r\n -6.39254\r\n RLSTD\r\n 58\r\n \r\n \r\n Tralee\r\n \r\n 52.271\r\n -9.69846\r\n TRLEE\r\n 28\r\n \r\n \r\n Wellingtonbridge\r\n \r\n 52.2678\r\n -6.75392\r\n WBDGE\r\n 54\r\n \r\n \r\n Waterford\r\n \r\n 52.2667\r\n -7.1183\r\n WFORD\r\n 50\r\n \r\n \r\n Rosslare Europort\r\n Rosslare Harbour\r\n 52.2531\r\n -6.33493\r\n RLEPT\r\n 60\r\n \r\n \r\n Bridgetown\r\n \r\n 52.2312\r\n -6.54918\r\n BRGTN\r\n 56\r\n \r\n \r\n Farranfore\r\n \r\n 52.1733\r\n -9.55278\r\n FFORE\r\n 27\r\n \r\n \r\n Mallow\r\n \r\n 52.1396\r\n -8.65521\r\n MLLOW\r\n 21\r\n \r\n \r\n Banteer\r\n \r\n 52.1287\r\n -8.89793\r\n BTEER\r\n 23\r\n \r\n \r\n Rathmore\r\n \r\n 52.0854\r\n -9.21756\r\n RMORE\r\n 25\r\n \r\n \r\n Millstreet\r\n \r\n 52.0776\r\n -9.06973\r\n MLSRT\r\n 24\r\n \r\n \r\n Killarney\r\n \r\n 52.0595\r\n -9.50198\r\n KLRNY\r\n 26\r\n \r\n \r\n Midleton\r\n \r\n 51.9212\r\n -8.17579\r\n MDLTN\r\n 68\r\n \r\n \r\n Carrigtwohill\r\n \r\n 51.9163\r\n -8.26323\r\n CGTWL\r\n 67\r\n \r\n \r\n Glounthaune\r\n \r\n 51.9112\r\n -8.3254\r\n GHANE\r\n 380\r\n \r\n \r\n LittleIsland\r\n Little Island\r\n 51.9078\r\n -8.35466\r\n LSLND\r\n 61\r\n \r\n \r\n Cork\r\n \r\n 51.9018\r\n -8.4582\r\n CORK \r\n 30\r\n \r\n \r\n Fota\r\n \r\n 51.896\r\n -8.3183\r\n FOTA \r\n 63\r\n \r\n \r\n Carrigaloe\r\n \r\n 51.8688\r\n -8.32417\r\n CGLOE\r\n 64\r\n \r\n \r\n Rushbrooke\r\n \r\n 51.8496\r\n -8.32252\r\n RBROK\r\n 65\r\n \r\n \r\n Cobh\r\n \r\n 51.8491\r\n -8.29956\r\n COBH \r\n 66\r\n \r\n \r\n CITY JUNCTION\r\n Dublin Belfast\r\n 0\r\n 0\r\n CITYJ\r\n 1516\r\n \r\n \r\n CENTRAL JUNCTION\r\n Dublin Belfast\r\n 0\r\n 0\r\n CENTJ\r\n 1517\r\n \r\n \r\n DUNMURRAY\r\n Dublin Belfast\r\n 0\r\n 0\r\n DUNMR\r\n 1518\r\n \r\n \r\n MOIRA\r\n Dublin Belfast\r\n 0\r\n 0\r\n MOIRA\r\n 1519\r\n \r\n"
40 |
41 | static func AllStation() -> Stations {
42 | guard let object = try? XMLDecoder().decode(Stations.self, from: Data(Responses.allStation.utf8))
43 | else {
44 | fatalError("Unable to decode static responses")
45 | }
46 | return object
47 | }
48 | }
49 |
--------------------------------------------------------------------------------