Repository: samkhawse/OpenStreetAmenities
Branch: master
Commit: 9a83b3115c78
Files: 40
Total size: 100.4 KB
Directory structure:
gitextract_axgr4pdw/
├── .gitignore
├── .travis.yml
├── LICENSE.md
├── LooLocator/
│ ├── AppDelegate.swift
│ ├── Assets.xcassets/
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ ├── Image.imageset/
│ │ │ └── Contents.json
│ │ └── location.imageset/
│ │ └── Contents.json
│ ├── Info.plist
│ ├── LaunchScreen.storyboard
│ ├── Main.storyboard
│ ├── Models/
│ │ └── Location.swift
│ ├── Protocols/
│ │ ├── ApiResourceProviding.swift
│ │ ├── Extensions/
│ │ │ ├── CLLocationManagerExtension.swift
│ │ │ └── NetworkReqeustProvidingExtension.swift
│ │ ├── Implementations/
│ │ │ ├── AmenityRequest.swift
│ │ │ ├── AmentityResource.swift
│ │ │ └── LocationProvider.swift
│ │ ├── LocationManagerConfigurable.swift
│ │ ├── LocationProvidable.swift
│ │ └── NetworkRequestProviding.swift
│ ├── ViewControllers/
│ │ └── ViewController.swift
│ └── ViewModels/
│ └── AmenityViewModel.swift
├── LooLocator.xcodeproj/
│ ├── project.pbxproj
│ ├── project.xcworkspace/
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata/
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── swiftpm/
│ │ └── Package.resolved
│ └── xcshareddata/
│ └── xcschemes/
│ └── LooLocator.xcscheme
├── LooLocatorTests/
│ ├── AmenityMocks.swift
│ ├── AmenityRequestTests.swift
│ ├── ApiClientTests.swift
│ ├── Info.plist
│ ├── LocationMocks.swift
│ ├── LocationProviderTests.swift
│ ├── MapViewModelTests.swift
│ ├── MockViewController.swift
│ ├── OSMModelTests.swift
│ └── stubbedRepsonse.json
├── README.md
└── berlin.gpx
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Created by https://www.gitignore.io/api/swift
### Swift ###
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## Build generated
build/
DerivedData/
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM
## Playgrounds
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
.build/
# CocoaPods - Refactored to standalone file
# Carthage - Refactored to standalone file
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
# End of https://www.gitignore.io/api/swift
android/
Carthage/
================================================
FILE: .travis.yml
================================================
language: swift
os: osx
================================================
FILE: LICENSE.md
================================================
MIT License
Copyright (c) 2018 Sam Khawase
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: LooLocator/AppDelegate.swift
================================================
//
// AppDelegate.swift
// LooLocator
//
// Created by Sam Khawase on 14.02.20.
// Copyright © 2020 LooLocator. All rights reserved.
//
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(_ application: UIApplication) {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
================================================
FILE: LooLocator/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"filename" : "Icon-App-20x20@2x.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"filename" : "Icon-App-20x20@3x.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"filename" : "Icon-App-29x29@1x.png",
"idiom" : "iphone",
"scale" : "1x",
"size" : "29x29"
},
{
"filename" : "Icon-App-29x29@2x.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"filename" : "Icon-App-29x29@3x.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"filename" : "Icon-App-40x40@2x.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"filename" : "Icon-App-40x40@3x.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"filename" : "Icon-App-57x57@1x.png",
"idiom" : "iphone",
"scale" : "1x",
"size" : "57x57"
},
{
"filename" : "Icon-App-57x57@2x.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "57x57"
},
{
"filename" : "Icon-App-60x60@2x.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"filename" : "Icon-App-60x60@3x.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"filename" : "Icon-App-20x20@1x.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"filename" : "Icon-App-20x20@2x.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"filename" : "Icon-App-29x29@1x.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"filename" : "Icon-App-29x29@2x.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"filename" : "Icon-App-40x40@1x.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"filename" : "Icon-App-40x40@2x.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"filename" : "Icon-Small-50x50@1x.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "50x50"
},
{
"filename" : "Icon-Small-50x50@2x.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "50x50"
},
{
"filename" : "Icon-App-72x72@1x.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "72x72"
},
{
"filename" : "Icon-App-72x72@2x.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "72x72"
},
{
"filename" : "Icon-App-76x76@1x.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"filename" : "Icon-App-76x76@2x.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"filename" : "Icon-App-83.5x83.5@2x.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"filename" : "ItunesArtwork@2x.png",
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
================================================
FILE: LooLocator/Assets.xcassets/Contents.json
================================================
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: LooLocator/Assets.xcassets/Image.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "Amenity_toilets.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: LooLocator/Assets.xcassets/location.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "icons8-near-me-50.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "icons8-near-me-100.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "icons8-near-me-500.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: LooLocator/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We need your current location to find the Amenities near you</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>We need your current location to find the Amenities near you</string>
</dict>
</plist>
================================================
FILE: LooLocator/LaunchScreen.storyboard
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" systemVersion="17A277" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
================================================
FILE: LooLocator/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="LooLocator" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<mapView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" mapType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="LUD-2e-Zm3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
</mapView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="J3Y-ve-Woo">
<rect key="frame" x="309" y="20" width="50" height="50"/>
<state key="normal" image="location">
<color key="titleColor" cocoaTouchSystemColor="darkTextColor"/>
</state>
<connections>
<action selector="resetLocation:" destination="BYZ-38-t0r" eventType="touchUpInside" id="QAV-RY-6ts"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="LUD-2e-Zm3" secondAttribute="trailing" id="1eP-z4-Ezv"/>
<constraint firstItem="LUD-2e-Zm3" firstAttribute="top" secondItem="8bC-Xf-vdC" secondAttribute="top" id="HKr-6I-Ciq"/>
<constraint firstItem="J3Y-ve-Woo" firstAttribute="leading" secondItem="LUD-2e-Zm3" secondAttribute="trailing" constant="-66" id="HlH-Gl-1Im"/>
<constraint firstItem="LUD-2e-Zm3" firstAttribute="top" secondItem="J3Y-ve-Woo" secondAttribute="bottom" constant="-70" id="UMb-do-wV1"/>
<constraint firstItem="LUD-2e-Zm3" firstAttribute="bottom" secondItem="6Tk-OE-BBY" secondAttribute="bottom" id="anx-VV-IpK"/>
<constraint firstItem="LUD-2e-Zm3" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leading" id="pvL-je-9Se"/>
</constraints>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
<connections>
<outlet property="mapView" destination="LUD-2e-Zm3" id="cVc-DX-XNR"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="136.80000000000001" y="132.68365817091455"/>
</scene>
</scenes>
<resources>
<image name="location" width="50" height="50"/>
</resources>
</document>
================================================
FILE: LooLocator/Models/Location.swift
================================================
//
// Location.swift
// LooLocator
//
// Created by Sam Khawase on 15.02.20.
// Copyright © 2020 LooLocator. All rights reserved.
//
import Contacts
import MapKit
class OSMData: Codable {
var elements: [Location]?
// MARK: - Codable methods
enum CodingKeys: String, CodingKey {
case elements
case id, lat, lon, tags
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.elements = try container.decode([Location].self, forKey: .elements)
}
func encode(to encoder: Encoder) throws {
}
}
class Location: NSObject, Codable {
// MARK: - variable declarations
let id: Int
let title: String?
let locationDescription: String?
let coordinates: (Double, Double)
var amenity: String?
var fee: Bool?
var feeAmount: String?
let isAccessible: Bool
// MARK: - Convenience initializers
init(id: Int, title:String, locationDescription: String, coordintes: (Double, Double), isAccessible: Bool ) {
self.id = id
self.title = title
self.locationDescription = locationDescription
self.coordinates = coordintes
self.isAccessible = isAccessible
}
// MARK: - Codable methods
enum CodingKeys: String, CodingKey {
case id, lat, lon, tags
// tags container
case name, wheelchair, fee, amenity
case englishName = "name:en"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(Int.self, forKey: .id)
if let lat = try? container.decode(Double.self, forKey: .lat),
let lon = try? container.decode(Double.self, forKey: .lon) {
self.coordinates = (lat, lon)
} else {
self.coordinates = (0.0, 0.0)
}
let tagsContainer = try? container.nestedContainer(keyedBy: CodingKeys.self, forKey: .tags)
self.locationDescription = try? tagsContainer?.decode(String.self, forKey: .name)
if let _name = try? tagsContainer?.decode(String.self, forKey: .englishName) {
self.title = _name
} else {
self.title = self.locationDescription
}
self.isAccessible = ((try? tagsContainer?.decode(String.self, forKey: .wheelchair)) != nil)
}
func encode(to encoder: Encoder) throws {
}
}
// MARK: - MKAnnotation support
extension Location: MKAnnotation {
var coordinate: CLLocationCoordinate2D {
return CLLocationCoordinate2D(latitude: CLLocationDegrees(coordinates.0), longitude: CLLocationDegrees(coordinates.1))
}
var subtitle: String? {
return "\(isAccessible ? "♿︎" : "")"
}
// Annotation right callout accessory opens this mapItem in Maps app
func mapItem() -> MKMapItem {
let addressDict = [CNPostalAddressStreetKey: title!]
let mapCoordinates = CLLocationCoordinate2D(latitude: CLLocationDegrees(coordinates.0), longitude: CLLocationDegrees(coordinates.1))
let placemark = MKPlacemark(coordinate: mapCoordinates, addressDictionary: addressDict)
let mapItem = MKMapItem(placemark: placemark)
mapItem.name = title
return mapItem
}
}
================================================
FILE: LooLocator/Protocols/ApiResourceProviding.swift
================================================
//
// ApiResourceProviding.swift
// LooLocator
//
// Created by Sam Khawase on 15.02.20.
// Copyright © 2020 LooLocator. All rights reserved.
//
import Foundation
protocol ApiResourceProviding {
var data: Data { get }
var headers:Dictionary<String, String> { get }
}
================================================
FILE: LooLocator/Protocols/Extensions/CLLocationManagerExtension.swift
================================================
//
// CLLocationManagerExtension.swift
// LooLocator
//
// Created by Sam Khawase on 15.02.20.
// Copyright © 2020 LooLocator. All rights reserved.
//
import CoreLocation
// CoreLocation extenstion for protocol conformance
extension CLLocationManager: LocationManagerConfigurable {
func setDelegate(to instance: CLLocationManagerDelegate?) {
guard let delegate = instance else {
return
}
self.delegate = delegate
}
// Changed this because CLLocationAccuracy is just a typealias for Double
func setDesiredAccuracy(to accuracy: CLLocationAccuracy) {
self.desiredAccuracy = accuracy
}
}
================================================
FILE: LooLocator/Protocols/Extensions/NetworkReqeustProvidingExtension.swift
================================================
//
// NetworkReqeustProvidingExtension.swift
// LooLocator
//
// Created by Sam Khawase on 15.02.20.
// Copyright © 2020 LooLocator. All rights reserved.
//
import Foundation
extension NetworkRequestProviding {
internal var baseUrl: String {
let _baseUrl = "https://overpass-api.de/api/interpreter"
return _baseUrl
}
func dataTask(request: NSMutableURLRequest,
completion: @escaping (Result<SerializedType, Error>) -> Void) {
let session = URLSession(configuration: URLSessionConfiguration.default)
session.dataTask(with: request as URLRequest) { (data, response, error) -> Void in
let decoder = JSONDecoder()
if let data = data,
let serverResponse = try? decoder.decode(SerializedType.self, from: data),
let response = response as? HTTPURLResponse, 200...299 ~= response.statusCode {
completion(.success(serverResponse))
} else {
completion(.failure(NSError(domain: "in.b3rl", code: 999, userInfo: nil)))
}
}.resume()
}
}
================================================
FILE: LooLocator/Protocols/Implementations/AmenityRequest.swift
================================================
//
// AmenityRequest.swift
// LooLocator
//
// Created by Sam Khawase on 15.02.20.
// Copyright © 2020 LooLocator. All rights reserved.
//
import Foundation
class AmenityRequest: NetworkRequestProviding {
func getAmeneties(of type: AmenityType,
latitude: Double,
longitude: Double,
radius: Double,
completionBlock: @escaping (Result<SerializedType, Error>) -> Void) {
let amenityResource = AmenityResource(latitude: String(latitude),
longitude: String(longitude),
amenity: type.rawValue,
radius: String(radius))
guard let amenityUrlRequest = createURLRequest(from: amenityResource) else {
return
}
post(request: amenityUrlRequest, completion: completionBlock)
// post(request: amenityUrlRequest) { (success, result) in
// if success {
// guard let result = result as? OSMData,
// let elements = result.elements else {
// completionBlock(false, nil)
// return
// }
// var jsonElements: [Location] = []
// for element in elements {
// jsonElements.append(Location(jsonElement: element))
// }
// completionBlock(success, elements as AnyObject)
// }
// }
}
func get(request: NSMutableURLRequest, completion: @escaping (Result<SerializedType, Error>) -> Void) { }
func post(request: NSMutableURLRequest, completion: @escaping (Result<SerializedType, Error>) -> Void) {
dataTask(request: request, completion: completion)
}
func put(request: NSMutableURLRequest, completion: @escaping (Result<SerializedType, Error>) -> Void) { }
func delete(request: NSMutableURLRequest, completion: @escaping (Result<SerializedType, Error>) -> Void) { }
func createURLRequest<T>(from resource: T) -> NSMutableURLRequest? where T : ApiResourceProviding {
guard let baseUrl = URL(string:self.baseUrl) else {
return nil
}
let locationRequest = NSMutableURLRequest(url:baseUrl,
cachePolicy: .reloadIgnoringCacheData,
timeoutInterval: 1.0)
locationRequest.httpMethod = "POST"
locationRequest.httpBody = resource.data
resource.headers.forEach { (arg) in
let (key, value) = arg
locationRequest.addValue(value, forHTTPHeaderField: key)
}
return locationRequest
}
typealias SerializedType = OSMData
}
================================================
FILE: LooLocator/Protocols/Implementations/AmentityResource.swift
================================================
//
// AmentityResource.swift
// LooLocator
//
// Created by Sam Khawase on 15.02.20.
// Copyright © 2020 LooLocator. All rights reserved.
//
import Foundation
class AmenityResource: ApiResourceProviding {
var latitude: String
var longitude: String
var amenityType: String
var radius: String
init(latitude: String, longitude: String, amenity: String, radius: String) {
self.longitude = longitude
self.latitude = latitude
self.amenityType = amenity
self.radius = radius
}
// OSM Needs the data in XML format
var data: Data {
if let _data = """
<osm-script output="json">
<query into="_" type="node">
<has-kv k="amenity" modv="" v="\(amenityType)"/>
<around from="_" into="_" lat="\(latitude)" lon="\(longitude)" radius="\(radius)"/>
</query>
<print e="" from="_" geometry="skeleton" limit="" mode="body" n="" order="id" s="" w=""/>
</osm-script>
""".data(using: String.Encoding.utf8) {
return _data
} else {
return Data()
}
}
var headers: Dictionary<String, String> {
var _headers = Dictionary<String, String>()
_headers["Content-Type"] = "application/xml"
_headers["Access-Control-Allow-Origin"] = "*"
_headers["Access-Control-Allow-Origin"] = "*/*"
return _headers
}
}
================================================
FILE: LooLocator/Protocols/Implementations/LocationProvider.swift
================================================
//
// LocationProvider.swift
// LooLocator
//
// Created by Sam Khawase on 14.02.20.
// Copyright © 2020 LooLocator. All rights reserved.
//
import CoreLocation
class LocationProvider: NSObject, LocationProvidable, CLLocationManagerDelegate {
var listener: LocationObservable?
func setListener(listener: LocationObservable) {
self.listener = listener
}
fileprivate var locationManager: LocationManagerConfigurable
fileprivate var currentLocation: CLLocation?
//fileprivate var observer: LocationObservable?
// inject
init(locationManager:LocationManagerConfigurable){
self.locationManager = locationManager
}
func startLocationUpdates() {
if (CLLocationManager.locationServicesEnabled())
{
locationManager.setDelegate(to: self)
locationManager.setDesiredAccuracy(to: kCLLocationAccuracyBest)
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
}
func getCurrentLocation() -> (Double, Double) {
guard let currentLocation = currentLocation else {
return (0,0)
}
return (currentLocation.coordinate.latitude, currentLocation.coordinate.longitude)
}
// CLLocationManager delegate methods
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
guard let lastLocation = locations.last,
let listener = self.listener else {
return
}
if currentLocation != nil && Double((currentLocation?.distance(from: lastLocation))!) < 100.0 {
//print("current Location \(String(describing: currentLocation?.coordinate)) is same as last Location: \(String(describing: lastLocation.coordinate))")
return
}
currentLocation = lastLocation
listener.setCurrentLocation(latitude: lastLocation.coordinate.latitude, longitude: lastLocation.coordinate.longitude)
}
}
================================================
FILE: LooLocator/Protocols/LocationManagerConfigurable.swift
================================================
//
// LocationManagerConfigurable.swift
// LooLocator
//
// Created by Sam Khawase on 15.02.20.
// Copyright © 2020 LooLocator. All rights reserved.
//
import CoreLocation
protocol LocationManagerConfigurable {
// wrap var delegate and desiredAccuracy to keep it platform agnostic
func setDelegate(to instance: CLLocationManagerDelegate?)
func setDesiredAccuracy(to accuracy: CLLocationAccuracy)
func requestAlwaysAuthorization()
func startUpdatingLocation()
}
================================================
FILE: LooLocator/Protocols/LocationProvidable.swift
================================================
//
// LocationProvidable.swift
// LooLocator
//
// Created by Sam Khawase on 15.02.20.
// Copyright © 2020 LooLocator. All rights reserved.
//
protocol LocationProvidable {
var listener: LocationObservable? { get set }
func setListener(listener: LocationObservable)
func startLocationUpdates()
func getCurrentLocation() -> (Double, Double)
}
// to be implemented by the VM
protocol LocationObservable {
func setCurrentLocation(latitude: Double, longitude: Double)
}
================================================
FILE: LooLocator/Protocols/NetworkRequestProviding.swift
================================================
//
// NetworkRequest.swift
// LooLocator
//
// Created by Sam Khawase on 15.02.20.
// Copyright © 2020 LooLocator. All rights reserved.
//
import Foundation
//typealias CompletionBlock = (_ success: Bool, _ object: AnyObject?) -> ()
protocol NetworkRequestProviding {
// The model that the request deals with
associatedtype SerializedType : Codable
// CRUD interface
func get(request: NSMutableURLRequest, completion: @escaping (Result<SerializedType, Error>) -> Void )
func post(request: NSMutableURLRequest, completion: @escaping (Result<SerializedType, Error>) -> Void)
func put(request: NSMutableURLRequest, completion: @escaping (Result<SerializedType, Error>) -> Void)
func delete(request: NSMutableURLRequest, completion: @escaping (Result<SerializedType, Error>) -> Void)
// Internal workhorse function: implemented in default extension
func dataTask(request: NSMutableURLRequest, completion: @escaping (Result<SerializedType, Error>) -> Void)
// The implementor needs to implement this to provide the ApiResource that the request needs
func createURLRequest<T: ApiResourceProviding>(from resource: T) -> NSMutableURLRequest?
}
================================================
FILE: LooLocator/ViewControllers/ViewController.swift
================================================
//
// ViewController.swift
// LooLocator
//
// Created by Sam Khawase on 14.02.20.
// Copyright © 2020 LooLocator. All rights reserved.
//
import UIKit
import MapKit
class ViewController: UIViewController {
@IBOutlet weak var mapView: MKMapView!
private(set) lazy var viewModel: MapViewModel<ViewController> = {
let _viewModel = MapViewModel(locationProvider: amenityLocationProvider, amenityRequest: amenityRequest, listener: self)
return _viewModel
}()
private(set) lazy var amenityLocationProvider: LocationProvidable = {
let _locationProvider = LocationProvider(locationManager: locationManager)
return _locationProvider
}()
private(set) lazy var amenityRequest: AmenityRequest = {
let _amenityRequest = AmenityRequest()
return _amenityRequest
}()
private(set) lazy var locationManager: LocationManagerConfigurable = {
let _clLocationManager = CLLocationManager()
return _clLocationManager
}()
let regionRadius: CLLocationDistance = 500
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
_ = viewModel.getCurrentLocation()
}
fileprivate func getAmenities() {
viewModel.getAmenities(in: 1000, type: .Toilets) { [weak self] result in
switch result {
case .success(let locations):
for location in locations {
print("Location.coordinate: \(location.coordinate.latitude) : \(location.coordinate.longitude)")
DispatchQueue.main.async { [weak self] in
self?.mapView.addAnnotation(location)
}
}
break
case .failure(let error):
self?.showAlert(error.localizedDescription)
break
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
mapView.showsUserLocation = true
}
@IBAction func resetLocation(_ sender: Any) {
viewModel.centerMapToCurrentLocationAction()
}
func centerMapOnLocation(location: CLLocation) {
let coordinateRegion = MKCoordinateRegion.init(center: location.coordinate,
latitudinalMeters: regionRadius,
longitudinalMeters: regionRadius)
DispatchQueue.main.async { [weak self] in
self?.mapView.setRegion(coordinateRegion, animated: true)
}
}
fileprivate func showAlert(_ message: String){
let alertController = UIAlertController(title: "Loolocator Alert", message: message, preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil)
alertController.addAction(cancelAction)
let OKAction = UIAlertAction(title: "OK", style: .default, handler: nil)
alertController.addAction(OKAction)
DispatchQueue.main.async { [weak self] in
self?.present(alertController, animated: true, completion: nil)
}
}
}
extension ViewController: MapViewModelObservable{
typealias Amenity = Location
func addAmenityToMap(amenity: Location) { }
func setCurrentLocation(latitude: Double, longitude: Double) {
print("current latitude: \(latitude), longitude: \(longitude)")
let currentLocation = CLLocation(latitude: CLLocationDegrees(latitude), longitude: CLLocationDegrees(longitude))
centerMapOnLocation(location: currentLocation)
getAmenities()
}
func centerMapToCurrentLocation(latitude: Double, longitude: Double) {
let currentLocation = CLLocation(latitude: CLLocationDegrees(latitude), longitude: CLLocationDegrees(longitude))
centerMapOnLocation(location: currentLocation)
}
}
extension ViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
guard let annotation = annotation as? Location else {
return nil
}
let identifier = "marker"
var view: MKMarkerAnnotationView
if let dequeuedView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier)
as? MKMarkerAnnotationView {
dequeuedView.annotation = annotation
view = dequeuedView
} else {
view = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.calloutOffset = CGPoint(x: -5, y: 5)
view.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
}
return view
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView,
calloutAccessoryControlTapped control: UIControl) {
guard let location = view.annotation as? Location else {
return
}
let launchOptions = [MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeWalking]
location.mapItem().openInMaps(launchOptions: launchOptions)
}
}
================================================
FILE: LooLocator/ViewModels/AmenityViewModel.swift
================================================
//
// AmenityViewModel.swift
// LooLocator
//
// Created by Sam Khawase on 14.02.20.
// Copyright © 2020 LooLocator. All rights reserved.
//
import CoreLocation
protocol MapViewModelConfirming {
func getCurrentLocation() -> (Double, Double)
func getAmenities(in range: Int, type: AmenityType, completion: @escaping (Result<[Location], Error>) -> Void)
func centerMapToCurrentLocationAction()
}
protocol MapViewModelObservable {
// This needs to be supplied by the VM Observer
associatedtype Amenity
func setCurrentLocation(latitude: Double, longitude: Double)
func addAmenityToMap(amenity:Amenity)
func centerMapToCurrentLocation(latitude: Double, longitude: Double)
}
enum AmenityType: String {
case Toilets = "toilets"
}
// MapviewModel: Protocol implementation
class MapViewModel<S:MapViewModelObservable>: MapViewModelConfirming, LocationObservable {
var locationProvider: LocationProvidable
var amenityRequest: AmenityRequest
var listenerView: S
// inject the dependencies in ctor
init(locationProvider: LocationProvidable, amenityRequest: AmenityRequest, listener: S) {
self.locationProvider = locationProvider
self.locationProvider.startLocationUpdates()
self.amenityRequest = amenityRequest
self.listenerView = listener
defer {
self.locationProvider.setListener(listener: self)
}
}
func getCurrentLocation() -> (Double, Double) {
let (lat, lon) = locationProvider.getCurrentLocation()
return (lat, lon)
}
func getAmenities(in range: Int, type: AmenityType, completion: @escaping (Result<[Location], Error>) -> Void) {
let (lat, lon) = getCurrentLocation()
print("latitude: \(lat) longitude: \(lon)")
if lat == 0 && lon == 0 {
completion(.failure(NSError(domain: "in.b3rl.loolocator", code: 666, userInfo: nil)))
}
amenityRequest.getAmeneties(of: AmenityType.Toilets,
latitude: lat,
longitude: lon,
radius: Double(range)) { result in
switch result {
case .success(let osmElement):
if let locations = osmElement.elements {
completion(.success(locations))
}
case .failure(let error):
completion(.failure(error))
}
}
}
// This is a message from the location provider
func setCurrentLocation(latitude: Double, longitude: Double) {
listenerView.setCurrentLocation(latitude: latitude, longitude: longitude)
}
func centerMapToCurrentLocationAction() {
let currentLocation = getCurrentLocation()
listenerView.centerMapToCurrentLocation(latitude: currentLocation.0, longitude: currentLocation.1)
}
}
================================================
FILE: LooLocator.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 52;
objects = {
/* Begin PBXBuildFile section */
22801B3A2035B0F100D4C2D2 /* LocationMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B392035B0F100D4C2D2 /* LocationMocks.swift */; };
22801B3C2035B1E000D4C2D2 /* LocationProvidable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B3B2035B1E000D4C2D2 /* LocationProvidable.swift */; };
22801B3E2035B20100D4C2D2 /* LocationManagerConfigurable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B3D2035B20100D4C2D2 /* LocationManagerConfigurable.swift */; };
22801B412035B25500D4C2D2 /* CLLocationManagerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B402035B25500D4C2D2 /* CLLocationManagerExtension.swift */; };
22801B552035BEC400D4C2D2 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 22801B532035BEC400D4C2D2 /* LaunchScreen.storyboard */; };
22801B562035BEC400D4C2D2 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 22801B542035BEC400D4C2D2 /* Main.storyboard */; };
22801B5A2035E07800D4C2D2 /* ApiResourceProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B592035E07800D4C2D2 /* ApiResourceProviding.swift */; };
22801B5C2035E10C00D4C2D2 /* NetworkRequestProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B5B2035E10B00D4C2D2 /* NetworkRequestProviding.swift */; };
22801B5E2035E40A00D4C2D2 /* AmentityResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B5D2035E40A00D4C2D2 /* AmentityResource.swift */; };
22801B602035E48200D4C2D2 /* AmenityRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B5F2035E48200D4C2D2 /* AmenityRequest.swift */; };
22801B652035E59300D4C2D2 /* NetworkReqeustProvidingExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B642035E59300D4C2D2 /* NetworkReqeustProvidingExtension.swift */; };
22801B672036209C00D4C2D2 /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B662036209C00D4C2D2 /* Location.swift */; };
22801B692036D9D700D4C2D2 /* AmenityMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B682036D9D700D4C2D2 /* AmenityMocks.swift */; };
229B3291203455CA0098E456 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 229B3290203455CA0098E456 /* AppDelegate.swift */; };
229B3293203455CA0098E456 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 229B3292203455CA0098E456 /* ViewController.swift */; };
229B3298203455CA0098E456 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 229B3297203455CA0098E456 /* Assets.xcassets */; };
229B32BA203459660098E456 /* AmenityViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 229B32B9203459660098E456 /* AmenityViewModel.swift */; };
229B32BC203460AE0098E456 /* LocationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 229B32BB203460AE0098E456 /* LocationProvider.swift */; };
22D1387D20386A2A0077E457 /* ApiClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22D1387C20386A2A0077E457 /* ApiClientTests.swift */; };
22D1387F20386D3D0077E457 /* stubbedRepsonse.json in Resources */ = {isa = PBXBuildFile; fileRef = 22D1387E20386D3D0077E457 /* stubbedRepsonse.json */; };
22D13882203A19450077E457 /* AmenityRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B5F2035E48200D4C2D2 /* AmenityRequest.swift */; };
22D13883203A19FE0077E457 /* LocationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 229B32BB203460AE0098E456 /* LocationProvider.swift */; };
22D13884203A19FE0077E457 /* AmentityResource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B5D2035E40A00D4C2D2 /* AmentityResource.swift */; };
22D13885203A1A040077E457 /* CLLocationManagerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B402035B25500D4C2D2 /* CLLocationManagerExtension.swift */; };
22D13886203A1A040077E457 /* NetworkReqeustProvidingExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B642035E59300D4C2D2 /* NetworkReqeustProvidingExtension.swift */; };
22D13887203A1A040077E457 /* LocationProvidable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B3B2035B1E000D4C2D2 /* LocationProvidable.swift */; };
22D13888203A1A040077E457 /* LocationManagerConfigurable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B3D2035B20100D4C2D2 /* LocationManagerConfigurable.swift */; };
22D13889203A1A040077E457 /* NetworkRequestProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B5B2035E10B00D4C2D2 /* NetworkRequestProviding.swift */; };
22D1388A203A1A040077E457 /* ApiResourceProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B592035E07800D4C2D2 /* ApiResourceProviding.swift */; };
22D1388C203A1A080077E457 /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22801B662036209C00D4C2D2 /* Location.swift */; };
22D1388D203A1A2A0077E457 /* AmenityViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 229B32B9203459660098E456 /* AmenityViewModel.swift */; };
22D13890203AD0D30077E457 /* LocationProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 229B32A5203455CA0098E456 /* LocationProviderTests.swift */; };
22D13892203C94AB0077E457 /* berlin.gpx in Resources */ = {isa = PBXBuildFile; fileRef = 22D13891203C94AB0077E457 /* berlin.gpx */; };
22D13893204022E70077E457 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 229B3292203455CA0098E456 /* ViewController.swift */; };
22D13895204023640077E457 /* MockViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22D13894204023640077E457 /* MockViewController.swift */; };
22D138972040269C0077E457 /* MapViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22D138962040269C0077E457 /* MapViewModelTests.swift */; };
8A048946253F41EA00037931 /* OSMModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A048945253F41EA00037931 /* OSMModelTests.swift */; };
8A2413F9253636AD005B7E5C /* Quick in Frameworks */ = {isa = PBXBuildFile; productRef = 8A2413F8253636AD005B7E5C /* Quick */; };
8A2413FE253636E2005B7E5C /* Nimble in Frameworks */ = {isa = PBXBuildFile; productRef = 8A2413FD253636E2005B7E5C /* Nimble */; };
8A2414032536370D005B7E5C /* OHHTTPStubsSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 8A2414022536370D005B7E5C /* OHHTTPStubsSwift */; };
8A2414052536370D005B7E5C /* OHHTTPStubs in Frameworks */ = {isa = PBXBuildFile; productRef = 8A2414042536370D005B7E5C /* OHHTTPStubs */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
229B32A2203455CA0098E456 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 229B3285203455C90098E456 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 229B328C203455CA0098E456;
remoteInfo = LooLocator;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
22801B392035B0F100D4C2D2 /* LocationMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationMocks.swift; sourceTree = "<group>"; };
22801B3B2035B1E000D4C2D2 /* LocationProvidable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationProvidable.swift; sourceTree = "<group>"; };
22801B3D2035B20100D4C2D2 /* LocationManagerConfigurable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationManagerConfigurable.swift; sourceTree = "<group>"; };
22801B402035B25500D4C2D2 /* CLLocationManagerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CLLocationManagerExtension.swift; sourceTree = "<group>"; };
22801B532035BEC400D4C2D2 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
22801B542035BEC400D4C2D2 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
22801B592035E07800D4C2D2 /* ApiResourceProviding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiResourceProviding.swift; sourceTree = "<group>"; };
22801B5B2035E10B00D4C2D2 /* NetworkRequestProviding.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkRequestProviding.swift; sourceTree = "<group>"; };
22801B5D2035E40A00D4C2D2 /* AmentityResource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AmentityResource.swift; sourceTree = "<group>"; };
22801B5F2035E48200D4C2D2 /* AmenityRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AmenityRequest.swift; sourceTree = "<group>"; };
22801B642035E59300D4C2D2 /* NetworkReqeustProvidingExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkReqeustProvidingExtension.swift; sourceTree = "<group>"; };
22801B662036209C00D4C2D2 /* Location.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Location.swift; sourceTree = "<group>"; };
22801B682036D9D700D4C2D2 /* AmenityMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AmenityMocks.swift; sourceTree = "<group>"; };
22801B6A2036DF8300D4C2D2 /* RxSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxSwift.framework; path = Carthage/Build/iOS/RxSwift.framework; sourceTree = "<group>"; };
22801B6C2036DFA100D4C2D2 /* RxCocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxCocoa.framework; path = Carthage/Build/iOS/RxCocoa.framework; sourceTree = "<group>"; };
22801B712036DFC800D4C2D2 /* RxTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxTest.framework; path = Carthage/Build/iOS/RxTest.framework; sourceTree = "<group>"; };
22801B722036DFC800D4C2D2 /* RxBlocking.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = RxBlocking.framework; path = Carthage/Build/iOS/RxBlocking.framework; sourceTree = "<group>"; };
229B328D203455CA0098E456 /* LooLocator.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = LooLocator.app; sourceTree = BUILT_PRODUCTS_DIR; };
229B3290203455CA0098E456 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
229B3292203455CA0098E456 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
229B3297203455CA0098E456 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
229B329C203455CA0098E456 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
229B32A1203455CA0098E456 /* LooLocatorTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = LooLocatorTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
229B32A5203455CA0098E456 /* LocationProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationProviderTests.swift; sourceTree = "<group>"; };
229B32A7203455CA0098E456 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
229B32B1203457AC0098E456 /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Nimble.framework; path = Carthage/Build/iOS/Nimble.framework; sourceTree = "<group>"; };
229B32B2203457AC0098E456 /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quick.framework; path = Carthage/Build/iOS/Quick.framework; sourceTree = "<group>"; };
229B32B9203459660098E456 /* AmenityViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AmenityViewModel.swift; sourceTree = "<group>"; };
229B32BB203460AE0098E456 /* LocationProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationProvider.swift; sourceTree = "<group>"; };
22D13879203862940077E457 /* OHHTTPStubs.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OHHTTPStubs.framework; path = Carthage/Build/iOS/OHHTTPStubs.framework; sourceTree = "<group>"; };
22D1387C20386A2A0077E457 /* ApiClientTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiClientTests.swift; sourceTree = "<group>"; };
22D1387E20386D3D0077E457 /* stubbedRepsonse.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = stubbedRepsonse.json; sourceTree = "<group>"; };
22D13891203C94AB0077E457 /* berlin.gpx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = berlin.gpx; sourceTree = SOURCE_ROOT; };
22D13894204023640077E457 /* MockViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockViewController.swift; sourceTree = "<group>"; };
22D138962040269C0077E457 /* MapViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapViewModelTests.swift; sourceTree = "<group>"; };
8A048945253F41EA00037931 /* OSMModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSMModelTests.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
229B328A203455CA0098E456 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
229B329E203455CA0098E456 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
8A2413F9253636AD005B7E5C /* Quick in Frameworks */,
8A2414032536370D005B7E5C /* OHHTTPStubsSwift in Frameworks */,
8A2413FE253636E2005B7E5C /* Nimble in Frameworks */,
8A2414052536370D005B7E5C /* OHHTTPStubs in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
22801B382035B0E200D4C2D2 /* Mocks */ = {
isa = PBXGroup;
children = (
22801B392035B0F100D4C2D2 /* LocationMocks.swift */,
22801B682036D9D700D4C2D2 /* AmenityMocks.swift */,
22D13894204023640077E457 /* MockViewController.swift */,
);
name = Mocks;
sourceTree = "<group>";
};
22801B462035BA1800D4C2D2 /* Resources */ = {
isa = PBXGroup;
children = (
22D13891203C94AB0077E457 /* berlin.gpx */,
229B3297203455CA0098E456 /* Assets.xcassets */,
229B329C203455CA0098E456 /* Info.plist */,
);
name = Resources;
sourceTree = "<group>";
};
22801B4C2035BB9F00D4C2D2 /* Protocols */ = {
isa = PBXGroup;
children = (
22801B4E2035BBC100D4C2D2 /* Implementations */,
22801B4D2035BBB900D4C2D2 /* Extensions */,
22801B3B2035B1E000D4C2D2 /* LocationProvidable.swift */,
22801B3D2035B20100D4C2D2 /* LocationManagerConfigurable.swift */,
22801B5B2035E10B00D4C2D2 /* NetworkRequestProviding.swift */,
22801B592035E07800D4C2D2 /* ApiResourceProviding.swift */,
);
path = Protocols;
sourceTree = "<group>";
};
22801B4D2035BBB900D4C2D2 /* Extensions */ = {
isa = PBXGroup;
children = (
22801B402035B25500D4C2D2 /* CLLocationManagerExtension.swift */,
22801B642035E59300D4C2D2 /* NetworkReqeustProvidingExtension.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
22801B4E2035BBC100D4C2D2 /* Implementations */ = {
isa = PBXGroup;
children = (
229B32BB203460AE0098E456 /* LocationProvider.swift */,
22801B5D2035E40A00D4C2D2 /* AmentityResource.swift */,
22801B5F2035E48200D4C2D2 /* AmenityRequest.swift */,
);
path = Implementations;
sourceTree = "<group>";
};
22801B502035BE1800D4C2D2 /* ViewModels */ = {
isa = PBXGroup;
children = (
229B32B9203459660098E456 /* AmenityViewModel.swift */,
);
path = ViewModels;
sourceTree = "<group>";
};
22801B512035BE2800D4C2D2 /* ViewControllers */ = {
isa = PBXGroup;
children = (
229B3292203455CA0098E456 /* ViewController.swift */,
);
path = ViewControllers;
sourceTree = "<group>";
};
22801B612035E4D500D4C2D2 /* Models */ = {
isa = PBXGroup;
children = (
22801B662036209C00D4C2D2 /* Location.swift */,
);
path = Models;
sourceTree = "<group>";
};
229B3284203455C90098E456 = {
isa = PBXGroup;
children = (
229B328F203455CA0098E456 /* LooLocator */,
229B32A4203455CA0098E456 /* LooLocatorTests */,
229B328E203455CA0098E456 /* Products */,
229B32B0203457AC0098E456 /* Frameworks */,
);
sourceTree = "<group>";
};
229B328E203455CA0098E456 /* Products */ = {
isa = PBXGroup;
children = (
229B328D203455CA0098E456 /* LooLocator.app */,
229B32A1203455CA0098E456 /* LooLocatorTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
229B328F203455CA0098E456 /* LooLocator */ = {
isa = PBXGroup;
children = (
22801B612035E4D500D4C2D2 /* Models */,
22801B532035BEC400D4C2D2 /* LaunchScreen.storyboard */,
22801B542035BEC400D4C2D2 /* Main.storyboard */,
229B3290203455CA0098E456 /* AppDelegate.swift */,
22801B502035BE1800D4C2D2 /* ViewModels */,
22801B4C2035BB9F00D4C2D2 /* Protocols */,
22801B462035BA1800D4C2D2 /* Resources */,
22801B512035BE2800D4C2D2 /* ViewControllers */,
);
path = LooLocator;
sourceTree = "<group>";
};
229B32A4203455CA0098E456 /* LooLocatorTests */ = {
isa = PBXGroup;
children = (
22D1387E20386D3D0077E457 /* stubbedRepsonse.json */,
22801B382035B0E200D4C2D2 /* Mocks */,
229B32A5203455CA0098E456 /* LocationProviderTests.swift */,
229B32A7203455CA0098E456 /* Info.plist */,
22D1387C20386A2A0077E457 /* ApiClientTests.swift */,
22D138962040269C0077E457 /* MapViewModelTests.swift */,
8A048945253F41EA00037931 /* OSMModelTests.swift */,
);
path = LooLocatorTests;
sourceTree = "<group>";
};
229B32B0203457AC0098E456 /* Frameworks */ = {
isa = PBXGroup;
children = (
22D13879203862940077E457 /* OHHTTPStubs.framework */,
22801B722036DFC800D4C2D2 /* RxBlocking.framework */,
22801B712036DFC800D4C2D2 /* RxTest.framework */,
22801B6C2036DFA100D4C2D2 /* RxCocoa.framework */,
22801B6A2036DF8300D4C2D2 /* RxSwift.framework */,
229B32B1203457AC0098E456 /* Nimble.framework */,
229B32B2203457AC0098E456 /* Quick.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
229B328C203455CA0098E456 /* LooLocator */ = {
isa = PBXNativeTarget;
buildConfigurationList = 229B32AA203455CA0098E456 /* Build configuration list for PBXNativeTarget "LooLocator" */;
buildPhases = (
229B3289203455CA0098E456 /* Sources */,
229B328A203455CA0098E456 /* Frameworks */,
229B328B203455CA0098E456 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = LooLocator;
productName = LooLocator;
productReference = 229B328D203455CA0098E456 /* LooLocator.app */;
productType = "com.apple.product-type.application";
};
229B32A0203455CA0098E456 /* LooLocatorTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 229B32AD203455CA0098E456 /* Build configuration list for PBXNativeTarget "LooLocatorTests" */;
buildPhases = (
229B329D203455CA0098E456 /* Sources */,
229B329E203455CA0098E456 /* Frameworks */,
229B329F203455CA0098E456 /* Resources */,
);
buildRules = (
);
dependencies = (
229B32A3203455CA0098E456 /* PBXTargetDependency */,
);
name = LooLocatorTests;
packageProductDependencies = (
8A2413F8253636AD005B7E5C /* Quick */,
8A2413FD253636E2005B7E5C /* Nimble */,
8A2414022536370D005B7E5C /* OHHTTPStubsSwift */,
8A2414042536370D005B7E5C /* OHHTTPStubs */,
);
productName = LooLocatorTests;
productReference = 229B32A1203455CA0098E456 /* LooLocatorTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
229B3285203455C90098E456 /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0920;
LastUpgradeCheck = 1200;
ORGANIZATIONNAME = LooLocator;
TargetAttributes = {
229B328C203455CA0098E456 = {
CreatedOnToolsVersion = 9.2;
LastSwiftMigration = 1200;
ProvisioningStyle = Automatic;
};
229B32A0203455CA0098E456 = {
CreatedOnToolsVersion = 9.2;
LastSwiftMigration = 1200;
ProvisioningStyle = Automatic;
};
};
};
buildConfigurationList = 229B3288203455C90098E456 /* Build configuration list for PBXProject "LooLocator" */;
compatibilityVersion = "Xcode 8.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 229B3284203455C90098E456;
packageReferences = (
8A2413F7253636AD005B7E5C /* XCRemoteSwiftPackageReference "Quick" */,
8A2413FC253636E2005B7E5C /* XCRemoteSwiftPackageReference "Nimble" */,
8A2414012536370D005B7E5C /* XCRemoteSwiftPackageReference "OHHTTPStubs" */,
);
productRefGroup = 229B328E203455CA0098E456 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
229B328C203455CA0098E456 /* LooLocator */,
229B32A0203455CA0098E456 /* LooLocatorTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
229B328B203455CA0098E456 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
22801B562035BEC400D4C2D2 /* Main.storyboard in Resources */,
22801B552035BEC400D4C2D2 /* LaunchScreen.storyboard in Resources */,
229B3298203455CA0098E456 /* Assets.xcassets in Resources */,
22D13892203C94AB0077E457 /* berlin.gpx in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
229B329F203455CA0098E456 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
22D1387F20386D3D0077E457 /* stubbedRepsonse.json in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
229B3289203455CA0098E456 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
22801B5E2035E40A00D4C2D2 /* AmentityResource.swift in Sources */,
22801B5C2035E10C00D4C2D2 /* NetworkRequestProviding.swift in Sources */,
229B3293203455CA0098E456 /* ViewController.swift in Sources */,
22801B3E2035B20100D4C2D2 /* LocationManagerConfigurable.swift in Sources */,
229B32BC203460AE0098E456 /* LocationProvider.swift in Sources */,
22801B672036209C00D4C2D2 /* Location.swift in Sources */,
229B3291203455CA0098E456 /* AppDelegate.swift in Sources */,
22801B5A2035E07800D4C2D2 /* ApiResourceProviding.swift in Sources */,
22801B652035E59300D4C2D2 /* NetworkReqeustProvidingExtension.swift in Sources */,
229B32BA203459660098E456 /* AmenityViewModel.swift in Sources */,
22801B3C2035B1E000D4C2D2 /* LocationProvidable.swift in Sources */,
22801B602035E48200D4C2D2 /* AmenityRequest.swift in Sources */,
22801B412035B25500D4C2D2 /* CLLocationManagerExtension.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
229B329D203455CA0098E456 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
22D13890203AD0D30077E457 /* LocationProviderTests.swift in Sources */,
22801B3A2035B0F100D4C2D2 /* LocationMocks.swift in Sources */,
22D1388D203A1A2A0077E457 /* AmenityViewModel.swift in Sources */,
22D138972040269C0077E457 /* MapViewModelTests.swift in Sources */,
22D13886203A1A040077E457 /* NetworkReqeustProvidingExtension.swift in Sources */,
22D1388C203A1A080077E457 /* Location.swift in Sources */,
22D13889203A1A040077E457 /* NetworkRequestProviding.swift in Sources */,
22D13887203A1A040077E457 /* LocationProvidable.swift in Sources */,
22D1388A203A1A040077E457 /* ApiResourceProviding.swift in Sources */,
22801B692036D9D700D4C2D2 /* AmenityMocks.swift in Sources */,
22D1387D20386A2A0077E457 /* ApiClientTests.swift in Sources */,
22D13884203A19FE0077E457 /* AmentityResource.swift in Sources */,
22D13893204022E70077E457 /* ViewController.swift in Sources */,
22D13895204023640077E457 /* MockViewController.swift in Sources */,
22D13888203A1A040077E457 /* LocationManagerConfigurable.swift in Sources */,
22D13882203A19450077E457 /* AmenityRequest.swift in Sources */,
8A048946253F41EA00037931 /* OSMModelTests.swift in Sources */,
22D13883203A19FE0077E457 /* LocationProvider.swift in Sources */,
22D13885203A1A040077E457 /* CLLocationManagerExtension.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
229B32A3203455CA0098E456 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 229B328C203455CA0098E456 /* LooLocator */;
targetProxy = 229B32A2203455CA0098E456 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
229B32A8203455CA0098E456 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
229B32A9203455CA0098E456 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
229B32AB203455CA0098E456 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = CWXKC246LH;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
INFOPLIST_FILE = LooLocator/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = de.sam.LooLocator;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
229B32AC203455CA0098E456 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = CWXKC246LH;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
INFOPLIST_FILE = LooLocator/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = de.sam.LooLocator;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
229B32AE203455CA0098E456 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = CWXKC246LH;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
INFOPLIST_FILE = LooLocatorTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = de.sam.LooLocatorTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
229B32AF203455CA0098E456 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = CWXKC246LH;
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
INFOPLIST_FILE = LooLocatorTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = de.sam.LooLocatorTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
229B3288203455C90098E456 /* Build configuration list for PBXProject "LooLocator" */ = {
isa = XCConfigurationList;
buildConfigurations = (
229B32A8203455CA0098E456 /* Debug */,
229B32A9203455CA0098E456 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
229B32AA203455CA0098E456 /* Build configuration list for PBXNativeTarget "LooLocator" */ = {
isa = XCConfigurationList;
buildConfigurations = (
229B32AB203455CA0098E456 /* Debug */,
229B32AC203455CA0098E456 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
229B32AD203455CA0098E456 /* Build configuration list for PBXNativeTarget "LooLocatorTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
229B32AE203455CA0098E456 /* Debug */,
229B32AF203455CA0098E456 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
8A2413F7253636AD005B7E5C /* XCRemoteSwiftPackageReference "Quick" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/Quick/Quick.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 3.0.0;
};
};
8A2413FC253636E2005B7E5C /* XCRemoteSwiftPackageReference "Nimble" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/Quick/Nimble.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 9.0.0;
};
};
8A2414012536370D005B7E5C /* XCRemoteSwiftPackageReference "OHHTTPStubs" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/AliSoftware/OHHTTPStubs.git";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 9.0.0;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
8A2413F8253636AD005B7E5C /* Quick */ = {
isa = XCSwiftPackageProductDependency;
package = 8A2413F7253636AD005B7E5C /* XCRemoteSwiftPackageReference "Quick" */;
productName = Quick;
};
8A2413FD253636E2005B7E5C /* Nimble */ = {
isa = XCSwiftPackageProductDependency;
package = 8A2413FC253636E2005B7E5C /* XCRemoteSwiftPackageReference "Nimble" */;
productName = Nimble;
};
8A2414022536370D005B7E5C /* OHHTTPStubsSwift */ = {
isa = XCSwiftPackageProductDependency;
package = 8A2414012536370D005B7E5C /* XCRemoteSwiftPackageReference "OHHTTPStubs" */;
productName = OHHTTPStubsSwift;
};
8A2414042536370D005B7E5C /* OHHTTPStubs */ = {
isa = XCSwiftPackageProductDependency;
package = 8A2414012536370D005B7E5C /* XCRemoteSwiftPackageReference "OHHTTPStubs" */;
productName = OHHTTPStubs;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 229B3285203455C90098E456 /* Project object */;
}
================================================
FILE: LooLocator.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
================================================
FILE: LooLocator.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
================================================
FILE: LooLocator.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
================================================
{
"object": {
"pins": [
{
"package": "CwlCatchException",
"repositoryURL": "https://github.com/mattgallagher/CwlCatchException.git",
"state": {
"branch": null,
"revision": "f809deb30dc5c9d9b78c872e553261a61177721a",
"version": "2.0.0"
}
},
{
"package": "CwlPreconditionTesting",
"repositoryURL": "https://github.com/mattgallagher/CwlPreconditionTesting.git",
"state": {
"branch": null,
"revision": "02b7a39a99c4da27abe03cab2053a9034379639f",
"version": "2.0.0"
}
},
{
"package": "Nimble",
"repositoryURL": "https://github.com/Quick/Nimble.git",
"state": {
"branch": null,
"revision": "e491a6731307bb23783bf664d003be9b2fa59ab5",
"version": "9.0.0"
}
},
{
"package": "OHHTTPStubs",
"repositoryURL": "https://github.com/AliSoftware/OHHTTPStubs.git",
"state": {
"branch": null,
"revision": "e92b5a5746ef16add2a1424f1fc19529d9a75cde",
"version": "9.0.0"
}
},
{
"package": "Quick",
"repositoryURL": "https://github.com/Quick/Quick.git",
"state": {
"branch": null,
"revision": "0038bcbab4292f3b028632556507c124e5ba69f3",
"version": "3.0.0"
}
}
]
},
"version": 1
}
================================================
FILE: LooLocator.xcodeproj/xcshareddata/xcschemes/LooLocator.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "229B328C203455CA0098E456"
BuildableName = "LooLocator.app"
BlueprintName = "LooLocator"
ReferencedContainer = "container:LooLocator.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "229B328C203455CA0098E456"
BuildableName = "LooLocator.app"
BlueprintName = "LooLocator"
ReferencedContainer = "container:LooLocator.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "229B32A0203455CA0098E456"
BuildableName = "LooLocatorTests.xctest"
BlueprintName = "LooLocatorTests"
ReferencedContainer = "container:LooLocator.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "229B328C203455CA0098E456"
BuildableName = "LooLocator.app"
BlueprintName = "LooLocator"
ReferencedContainer = "container:LooLocator.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<LocationScenarioReference
identifier = "../../berlin.gpx"
referenceType = "0">
</LocationScenarioReference>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "229B328C203455CA0098E456"
BuildableName = "LooLocator.app"
BlueprintName = "LooLocator"
ReferencedContainer = "container:LooLocator.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
================================================
FILE: LooLocatorTests/AmenityMocks.swift
================================================
//
// AmenityMocks.swift
// LooLocatorTests
//
// Created by Sam Khawase on 16.02.20.
// Copyright © 2020 LooLocator. All rights reserved.
//
import Foundation
class MockAmenityRequest: AmenityRequest {
override func getAmeneties(of type: AmenityType,
latitude: Double,
longitude: Double,
radius: Double,
completionBlock: @escaping (Result<AmenityRequest.SerializedType, Error>) -> Void) {
let jsonDecoder = JSONDecoder()
if let jsonData = """
{
"elements": [
{
"type": "node",
"id": 66917214,
"lat": 52.5168607,
"lon": 13.3829509,
"tags": {
"amenity": "toilets",
"fee": "yes",
"name": "City Toilette",
"toilets:wheelchair": "yes",
"wheelchair": "yes"
}
}
]
}
""".data(using: .utf8),
let dummyOSMData = try? jsonDecoder.decode(OSMData.self, from: jsonData) {
completionBlock(.success(dummyOSMData))
}
}
}
================================================
FILE: LooLocatorTests/AmenityRequestTests.swift
================================================
//
// AmenityRequestTests.swift
// LooLocatorTests
//
// Created by Sam Khawase on 17.02.18.
// Copyright © 2018 LooLocator. All rights reserved.
//
import XCTest
import OHHTTPStubs
//@testable import LooLocator
class AmenityRequestTests: XCTestCase {
override func setUp() {
super.setUp()
let testHost = "overpass-api.de"
stub(condition: isHost(testHost), response: { _ in
guard let path = OHPathForFile("stubbedRepsonse.json", type(of: self)) else {
preconditionFailure("Could not find expected file in test bundle")
}
return fixture(filePath: path, status: 200, headers: ["Content-Type":"application/json"])
})
}
override func tearDown() {
super.tearDown()
OHHTTPStubs.removeAllStubs()
}
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
let amenityReqeust = AmenityRequest()
let expectation = self.expectation(description: "calls the callback with a resource object")
var successFlag = false
var locations: [Location] = []
let timeout = 1.0
amenityReqeust.getAmeneties(of: AmenityType.Toilets,
latitude: 52.51631,
longitude: 13.37777,
radius: 1000,
completionBlock: { (success, results) in
print("success is \(success)")
successFlag = success
if successFlag, let results = results as? [Location] {
locations = results
expectation.fulfill()
}
})
self.waitForExpectations(timeout: timeout) { err in
XCTAssertNotNil(locations, "Received data should not be nil")
}
}
}
================================================
FILE: LooLocatorTests/ApiClientTests.swift
================================================
//
// ApiClientTests.swift
// LooLocatorTests
//
// Created by Sam Khawase on 17.02.20.
// Copyright © 2020 LooLocator. All rights reserved.
//
import XCTest
import Quick
import Nimble
import CoreLocation
import OHHTTPStubs
import OHHTTPStubsSwift
class ApiClientTests: QuickSpec {
override func spec() {
describe("Amenity Request tests") {
let amenityReqeust = AmenityRequest()
beforeEach {
let testHost = "overpass-api.de"
stub(condition: isHost(testHost), response: { _ in
guard let path = OHPathForFile("stubbedRepsonse.json", type(of: self)) else {
preconditionFailure("Could not find expected file in test bundle")
}
return fixture(filePath: path, status: 200, headers: ["Content-Type":"application/json"])
})
}
afterEach {
HTTPStubs.removeAllStubs()
}
it("should fetch amenities", closure: {
// Arrange
var successFlag = false
var locations: [Location] = []
// Act
amenityReqeust.getAmeneties(of: AmenityType.Toilets,
latitude: 52.51631,
longitude: 13.37777,
radius: 1000) { result in
switch result {
case .success(let osmData):
successFlag = true
if let results = osmData.elements {
locations = results
}
break
case .failure( _):
successFlag = false
break
}
}
//Assert
expect(successFlag).toEventuallyNot(beFalse())
expect(locations).toEventuallyNot(beEmpty())
expect(locations.first?.id).toEventuallyNot(equal(0))
expect(locations.first?.coordinates.0).toEventuallyNot(equal(0))
expect(locations.first?.coordinates.1).toEventuallyNot(equal(0))
})
}
}
}
================================================
FILE: LooLocatorTests/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We need this information to locate you on the map, in order to find the amenities near you</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>We need this information to locate you on the map, in order to find the amenities near you</string>
</dict>
</plist>
================================================
FILE: LooLocatorTests/LocationMocks.swift
================================================
//
// LocationMocks.swift
// LooLocatorTests
//
// Created by Sam Khawase on 15.02.20.
// Copyright © 2020 LooLocator. All rights reserved.
//
import MapKit
class MockLocationProvider: LocationProvidable {
var listener: LocationObservable?
func setListener(listener: LocationObservable) {
self.listener = listener
}
func startLocationUpdates() {
listener?.setCurrentLocation(latitude: 52.51631, longitude: 13.37777)
}
func getCurrentLocation() -> (Double, Double) {
return (52.51631, 13.37777)
}
}
class MockLocationObservable: LocationObservable {
internal var coordinates: (Double, Double)?
func setCurrentLocation(latitude: Double, longitude: Double) {
coordinates = (latitude, longitude)
}
}
class MockLocationManager: LocationManagerConfigurable {
internal var callCount = 0
fileprivate var delegate: LocationProvider?
func setDelegate(to instance: CLLocationManagerDelegate?) {
callCount += 1
delegate = instance as? LocationProvider
}
func setDesiredAccuracy(to accuracy: Double) {
callCount += 1
}
func requestAlwaysAuthorization() {
callCount += 1
}
func startUpdatingLocation() {
callCount += 1
updateLocation()
}
func updateLocation() {
let mockLocation = CLLocation(latitude: CLLocationDegrees(52.51631), longitude: CLLocationDegrees(13.37777))
let mockLocationManager = CLLocationManager()
delegate?.locationManager(mockLocationManager, didUpdateLocations: [mockLocation])
}
}
================================================
FILE: LooLocatorTests/LocationProviderTests.swift
================================================
//
// LooLocatorTests.swift
// LooLocatorTests
//
// Created by Sam Khawase on 14.02.20.
// Copyright © 2020 LooLocator. All rights reserved.
//
import XCTest
import Quick
import Nimble
class LocationProviderTests: QuickSpec {
override func spec() {
describe("Given a LocationProvider") {
context("When it's started with LocationManager", closure: {
// Arrange
let mockLocationManager = MockLocationManager()
let mockLocationObservable = MockLocationObservable()
let locationProvider: LocationProvidable = LocationProvider(locationManager: mockLocationManager)
beforeEach {
mockLocationManager.callCount = 0
// Arrange
locationProvider.setListener(listener: mockLocationObservable)
//Act
locationProvider.startLocationUpdates()
}
it("then starts location updates", closure: {
//Assert
expect(mockLocationManager.callCount).toEventually(equal(4))
expect(mockLocationObservable.coordinates).toEventuallyNot(beNil())
expect(mockLocationObservable.coordinates?.0).toEventually(equal(52.51631))
expect(mockLocationObservable.coordinates?.1).toEventually(equal(13.37777))
})
it("then provides current location", closure: {
// Act
let (lat, lon) = locationProvider.getCurrentLocation()
//Assert
expect(lat).to(equal(52.51631))
expect(lon).to(equal(13.37777))
})
})
}
}
}
================================================
FILE: LooLocatorTests/MapViewModelTests.swift
================================================
//
// MapViewModelTests.swift
// LooLocatorTests
//
// Created by Sam Khawase on 23.02.20.
// Copyright © 2020 LooLocator. All rights reserved.
//
import XCTest
import Quick
import Nimble
import CoreLocation
import OHHTTPStubs
class MapViewModelTests: QuickSpec {
override func spec() {
describe("Given a MapViewModel") {
var viewModel: MapViewModel<MockViewController>?
beforeEach {
let mockLocationProvider = MockLocationProvider()
let mockAmenityRequest = MockAmenityRequest()
let mockViewController = MockViewController()
viewModel = MapViewModel(locationProvider: mockLocationProvider,
amenityRequest:mockAmenityRequest,
listener:mockViewController)
}
it("get current location", closure: {
if let (lat, lon) = viewModel?.getCurrentLocation() {
expect(lat).to(equal(52.51631))
expect(lon).to(equal(13.37777))
}
})
it("should get all amenities in range", closure: {
var successFlag = false
var locations: [Location] = []
viewModel?.getAmenities(in: 1000, type: AmenityType.Toilets) { result in
switch result {
case .success(let _locations):
locations = _locations
successFlag = true
case .failure(_):
successFlag = false
}
}
//Assert
expect(successFlag).toEventuallyNot(beFalse())
expect(locations).toEventuallyNot(beEmpty())
expect(locations.first?.id).toEventually(equal(66917214))
expect(locations.first?.title).toEventually(equal("City Toilette"))
expect(locations.first?.coordinates.0).toEventually(equal(52.5168607))
expect(locations.first?.coordinates.1).toEventually(equal(13.3829509))
})
}
}
}
================================================
FILE: LooLocatorTests/MockViewController.swift
================================================
//
// MockViewController.swift
// LooLocatorTests
//
// Created by Sam Khawase on 23.02.20.
// Copyright © 2020 LooLocator. All rights reserved.
//
import Foundation
class MockViewController: MapViewModelObservable {
func centerMapToCurrentLocation(latitude: Double, longitude: Double) {
}
func setCurrentLocation(latitude: Double, longitude: Double) {}
func addAmenityToMap(amenity: Location) {}
typealias Amenity = Location
}
================================================
FILE: LooLocatorTests/OSMModelTests.swift
================================================
//
// OSMModelTests.swift
// LooLocatorTests
//
// Created by Sam Khawase on 20.10.20.
// Copyright © 2020 LooLocator. All rights reserved.
//
import Foundation
import Quick
import Nimble
class OSMModelTests: QuickSpec {
override func spec() {
var jsonData: Data?
beforeEach {
let currentBundle = Bundle(for: type(of: self))
guard let fileURL = currentBundle.url(forResource: "stubbedRepsonse", withExtension: "json"),
let fileContents = try? String(contentsOf: fileURL),
let _jsonData = fileContents.data(using: .utf8)
else {
preconditionFailure("Could not find expected file in test bundle")
}
jsonData = _jsonData
}
context("Given a JSON data set") {
describe("When loading data from json") {
it("should parse the json to Location Model correctly") {
// Arrange
let decoder = JSONDecoder()
// Act
let locations = try? decoder.decode(OSMData.self, from: jsonData!)
// Assert
expect(locations).toNot(beNil())
}
}
}
}
}
================================================
FILE: LooLocatorTests/stubbedRepsonse.json
================================================
{
"version": 0.6,
"generator": "Overpass API 0.7.54.12 054bb0bb",
"osm3s": {
"timestamp_osm_base": "2018-02-18T20:06:02Z",
"copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL."
},
"elements": [
{
"type": "node",
"id": 66917214,
"lat": 52.5168607,
"lon": 13.3829509,
"tags": {
"amenity": "toilets",
"fee": "yes",
"name": "City Toilette",
"toilets:wheelchair": "yes",
"wheelchair": "yes"
}
},
{
"type": "node",
"id": 71180687,
"lat": 52.5175448,
"lon": 13.3738695,
"tags": {
"amenity": "toilets",
"fee": "yes",
"toilets:wheelchair": "yes",
"wheelchair": "yes",
"wheelchair:description": "Euro-Schlüssel notwendig"
}
},
{
"type": "node",
"id": 304575905,
"lat": 52.5163769,
"lon": 13.3755227,
"tags": {
"amenity": "toilets",
"fee": "yes",
"name": "City Toilette",
"opening_hours": "24/7",
"toilets:wheelchair": "yes",
"wheelchair": "yes"
}
},
{
"type": "node",
"id": 599913127,
"lat": 52.5081387,
"lon": 13.3744541,
"tags": {
"amenity": "toilets",
"layer": "-1",
"operator": "unknown",
"wheelchair": "yes"
}
},
{
"type": "node",
"id": 927092067,
"lat": 52.5141833,
"lon": 13.3914532,
"tags": {
"amenity": "toilets",
"centralkey": "eurokey",
"fee": "yes",
"name": "City Toilette",
"opening_hours": "24/7",
"operator": "Wall",
"toilets:wheelchair": "yes",
"wheelchair": "yes",
"wheelchair:description": "Wall City Toilette"
}
},
{
"type": "node",
"id": 1453830466,
"lat": 52.5226503,
"lon": 13.3720210,
"tags": {
"amenity": "toilets",
"wheelchair": "no"
}
},
{
"type": "node",
"id": 1453886966,
"lat": 52.5225436,
"lon": 13.3708178,
"tags": {
"amenity": "toilets",
"wheelchair": "no"
}
},
{
"type": "node",
"id": 2351691628,
"lat": 52.5165754,
"lon": 13.3811272,
"tags": {
"amenity": "toilets",
"fee": "yes",
"name": "City Toilette",
"toilets:wheelchair": "yes",
"wheelchair": "yes"
}
},
{
"type": "node",
"id": 2378807163,
"lat": 52.5207250,
"lon": 13.3875590,
"tags": {
"amenity": "toilets",
"diaper": "yes",
"fee": "yes",
"female": "yes",
"male": "yes",
"name": "Bahnhof Friedrichstraße",
"opening_hours": "24/7",
"wheelchair": "yes"
}
},
{
"type": "node",
"id": 3338458355,
"lat": 52.5106351,
"lon": 13.3830226,
"tags": {
"access": "customers",
"amenity": "toilets",
"fee": "yes",
"toilets:disposal": "flush",
"toilets:position": "seated",
"wheelchair": "yes"
}
},
{
"type": "node",
"id": 3338459657,
"lat": 52.5183901,
"lon": 13.3890855,
"tags": {
"access": "customers",
"amenity": "toilets",
"fee": "no",
"toilets:disposal": "flush",
"toilets:position": "seated",
"wheelchair": "yes"
}
},
{
"type": "node",
"id": 4003667897,
"lat": 52.5203163,
"lon": 13.3876880,
"tags": {
"access": "public",
"amenity": "toilets",
"fee": "yes",
"indoor": "yes",
"level": "-1",
"operator": "Sanifair",
"wheelchair": "yes"
}
},
{
"type": "node",
"id": 5022093422,
"lat": 52.5191343,
"lon": 13.3646383,
"tags": {
"amenity": "toilets"
}
}
]
}
================================================
FILE: README.md
================================================
# LooLocator
Find Amenities (*like toilets*) near you! The simple iOS fetches the crowd-sourced data from OpenStreetMap, and shows toilets within walking distance.
User can then use AppleMaps to find walking directions to the amenity.
## Design Rationale
The following series of bite-sized posts explain the design rationale behind creating the app:
1. [Part 1: Introduction - Writing a modular, and testable iOS App in Swift using MVVM pattern](https://samkhawase.com/blog/mvvm_swift_introduction/)
2. [Part 2: Defining the Data Model](https://samkhawase.com/blog/mvvm_swift_model/)
3. [Part 3: The Location provider](https://samkhawase.com/blog/mvvm_swift_location_provider/)
4. [Part 4: Defining the networking layer](https://samkhawase.com/blog/mvvm_swift_networking/)
5. [Part 5: The ViewModel](https://samkhawase.com/blog/mvvm_swift_view_model/)
6. [Part 6: The Final App - Putting it all together](https://samkhawase.com/blog/mvvm_swift_final_app/)
## Getting Started
Here are the steps to get started with the project on your local machine:
1. Clone the git repositiory
2. Run `carthage update --platform iOS --cache-builds --no-use-binaries` to fetch the dependencies.
3. If running on the simulator, you can edit the scheme and set the simulated location in Xcode. (*E.g. Hongkong*)
4. Run the project via Xcode.
### Prerequisites
What things you need to install the software and how to install them
1. Mac OS X
2. Xcode 9
3. [Carthage](https://github.com/Carthage/Carthage)
4. Optional: [xcpretty](https://github.com/supermarin/xcpretty)
## Running the tests
The app uses BDD style tests using Quick and Nimble. There are unit tests written to test the LocationManager, APIClient (*with Network mocks*), and ViewModel behaviors.
To run the test, enter the command on the command line.
```
xcodebuild -scheme 'LooLocator' \
-sdk iphonesimulator \
-configuration Debug \
-destination 'platform=iOS Simulator,name=iPhone 6s,OS=latest' \
test | xcpretty
```
The output will be similar to
```
Test Suite LooLocatorTests.xctest started
ApiClientTests
✓ Amenity_Request_tests__should_fetch_amenities (0.027 seconds)
LocationProviderTests
✓ Given_a_LocationProvider__When_it_s_started_with_LocationManager__then_starts_location_updates (1.547 seconds)
✓ Given_a_LocationProvider__When_it_s_started_with_LocationManager__then_provides_current_location (0.001 seconds)
MapViewModelTests
✓ Given_a_MapViewModel__get_current_location (0.002 seconds)
✓ Given_a_MapViewModel__should_get_all_amenities_in_range (0.004 seconds)
```
## License
This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
## Acknowledgments
* This project is inspired by the MapKit article from [RayWenderlich](https://www.raywenderlich.com/160517/mapkit-tutorial-getting-started)
* The [Overpass Turbo API](https://overpass-turbo.eu/)
================================================
FILE: berlin.gpx
================================================
<?xml version="1.0"?>
<gpx version="1.1" creator="gpxgenerator.com">
<wpt lat="52.516327366345344" lon="13.378034122288227">
<ele>34.65</ele>
<time>2017-08-10T13:15:24Z</time>
</wpt>
</gpx>
gitextract_axgr4pdw/ ├── .gitignore ├── .travis.yml ├── LICENSE.md ├── LooLocator/ │ ├── AppDelegate.swift │ ├── Assets.xcassets/ │ │ ├── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── Image.imageset/ │ │ │ └── Contents.json │ │ └── location.imageset/ │ │ └── Contents.json │ ├── Info.plist │ ├── LaunchScreen.storyboard │ ├── Main.storyboard │ ├── Models/ │ │ └── Location.swift │ ├── Protocols/ │ │ ├── ApiResourceProviding.swift │ │ ├── Extensions/ │ │ │ ├── CLLocationManagerExtension.swift │ │ │ └── NetworkReqeustProvidingExtension.swift │ │ ├── Implementations/ │ │ │ ├── AmenityRequest.swift │ │ │ ├── AmentityResource.swift │ │ │ └── LocationProvider.swift │ │ ├── LocationManagerConfigurable.swift │ │ ├── LocationProvidable.swift │ │ └── NetworkRequestProviding.swift │ ├── ViewControllers/ │ │ └── ViewController.swift │ └── ViewModels/ │ └── AmenityViewModel.swift ├── LooLocator.xcodeproj/ │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm/ │ │ └── Package.resolved │ └── xcshareddata/ │ └── xcschemes/ │ └── LooLocator.xcscheme ├── LooLocatorTests/ │ ├── AmenityMocks.swift │ ├── AmenityRequestTests.swift │ ├── ApiClientTests.swift │ ├── Info.plist │ ├── LocationMocks.swift │ ├── LocationProviderTests.swift │ ├── MapViewModelTests.swift │ ├── MockViewController.swift │ ├── OSMModelTests.swift │ └── stubbedRepsonse.json ├── README.md └── berlin.gpx
Condensed preview — 40 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (113K chars).
[
{
"path": ".gitignore",
"chars": 1242,
"preview": "\n# Created by https://www.gitignore.io/api/swift\n\n### Swift ###\n# Xcode\n#\n# gitignore contributors: remember to update G"
},
{
"path": ".travis.yml",
"chars": 24,
"preview": "language: swift\nos: osx\n"
},
{
"path": "LICENSE.md",
"chars": 1068,
"preview": "MIT License\n\nCopyright (c) 2018 Sam Khawase\n\nPermission is hereby granted, free of charge, to any person obtaining a cop"
},
{
"path": "LooLocator/AppDelegate.swift",
"chars": 2174,
"preview": "//\n// AppDelegate.swift\n// LooLocator\n//\n// Created by Sam Khawase on 14.02.20.\n// Copyright © 2020 LooLocator. All "
},
{
"path": "LooLocator/Assets.xcassets/AppIcon.appiconset/Contents.json",
"chars": 3278,
"preview": "{\n \"images\" : [\n {\n \"filename\" : \"Icon-App-20x20@2x.png\",\n \"idiom\" : \"iphone\",\n \"scale\" : \"2x\",\n "
},
{
"path": "LooLocator/Assets.xcassets/Contents.json",
"chars": 62,
"preview": "{\n \"info\" : {\n \"version\" : 1,\n \"author\" : \"xcode\"\n }\n}"
},
{
"path": "LooLocator/Assets.xcassets/Image.imageset/Contents.json",
"chars": 164,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"Amenity_toilets.pdf\"\n }\n ],\n \"info\" : {\n "
},
{
"path": "LooLocator/Assets.xcassets/location.imageset/Contents.json",
"chars": 404,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"icons8-near-me-50.png\",\n \"scale\" : \"1x\"\n "
},
{
"path": "LooLocator/Info.plist",
"chars": 1726,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "LooLocator/LaunchScreen.storyboard",
"chars": 1681,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard"
},
{
"path": "LooLocator/Main.storyboard",
"chars": 4315,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
},
{
"path": "LooLocator/Models/Location.swift",
"chars": 3357,
"preview": "//\n// Location.swift\n// LooLocator\n//\n// Created by Sam Khawase on 15.02.20.\n// Copyright © 2020 LooLocator. All rig"
},
{
"path": "LooLocator/Protocols/ApiResourceProviding.swift",
"chars": 281,
"preview": "//\n// ApiResourceProviding.swift\n// LooLocator\n//\n// Created by Sam Khawase on 15.02.20.\n// Copyright © 2020 LooLoca"
},
{
"path": "LooLocator/Protocols/Extensions/CLLocationManagerExtension.swift",
"chars": 651,
"preview": "//\n// CLLocationManagerExtension.swift\n// LooLocator\n//\n// Created by Sam Khawase on 15.02.20.\n// Copyright © 2020 L"
},
{
"path": "LooLocator/Protocols/Extensions/NetworkReqeustProvidingExtension.swift",
"chars": 1136,
"preview": "//\n// NetworkReqeustProvidingExtension.swift\n// LooLocator\n//\n// Created by Sam Khawase on 15.02.20.\n// Copyright © "
},
{
"path": "LooLocator/Protocols/Implementations/AmenityRequest.swift",
"chars": 2881,
"preview": "//\n// AmenityRequest.swift\n// LooLocator\n//\n// Created by Sam Khawase on 15.02.20.\n// Copyright © 2020 LooLocator. A"
},
{
"path": "LooLocator/Protocols/Implementations/AmentityResource.swift",
"chars": 1478,
"preview": "//\n// AmentityResource.swift\n// LooLocator\n//\n// Created by Sam Khawase on 15.02.20.\n// Copyright © 2020 LooLocator."
},
{
"path": "LooLocator/Protocols/Implementations/LocationProvider.swift",
"chars": 2056,
"preview": "//\n// LocationProvider.swift\n// LooLocator\n//\n// Created by Sam Khawase on 14.02.20.\n// Copyright © 2020 LooLocator."
},
{
"path": "LooLocator/Protocols/LocationManagerConfigurable.swift",
"chars": 487,
"preview": "//\n// LocationManagerConfigurable.swift\n// LooLocator\n//\n// Created by Sam Khawase on 15.02.20.\n// Copyright © 2020 "
},
{
"path": "LooLocator/Protocols/LocationProvidable.swift",
"chars": 492,
"preview": "//\n// LocationProvidable.swift\n// LooLocator\n//\n// Created by Sam Khawase on 15.02.20.\n// Copyright © 2020 LooLocato"
},
{
"path": "LooLocator/Protocols/NetworkRequestProviding.swift",
"chars": 1208,
"preview": "//\n// NetworkRequest.swift\n// LooLocator\n//\n// Created by Sam Khawase on 15.02.20.\n// Copyright © 2020 LooLocator. A"
},
{
"path": "LooLocator/ViewControllers/ViewController.swift",
"chars": 5237,
"preview": "//\n// ViewController.swift\n// LooLocator\n//\n// Created by Sam Khawase on 14.02.20.\n// Copyright © 2020 LooLocator. A"
},
{
"path": "LooLocator/ViewModels/AmenityViewModel.swift",
"chars": 2922,
"preview": "//\n// AmenityViewModel.swift\n// LooLocator\n//\n// Created by Sam Khawase on 14.02.20.\n// Copyright © 2020 LooLocator."
},
{
"path": "LooLocator.xcodeproj/project.pbxproj",
"chars": 35514,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 52;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "LooLocator.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"chars": 135,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"self:\">\n </FileRef"
},
{
"path": "LooLocator.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
"chars": 238,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "LooLocator.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved",
"chars": 1441,
"preview": "{\n \"object\": {\n \"pins\": [\n {\n \"package\": \"CwlCatchException\",\n \"repositoryURL\": \"https://github.c"
},
{
"path": "LooLocator.xcodeproj/xcshareddata/xcschemes/LooLocator.xcscheme",
"chars": 3811,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"1200\"\n version = \"1.7\">\n <BuildAction\n "
},
{
"path": "LooLocatorTests/AmenityMocks.swift",
"chars": 1501,
"preview": "//\n// AmenityMocks.swift\n// LooLocatorTests\n//\n// Created by Sam Khawase on 16.02.20.\n// Copyright © 2020 LooLocator"
},
{
"path": "LooLocatorTests/AmenityRequestTests.swift",
"chars": 2149,
"preview": "//\n// AmenityRequestTests.swift\n// LooLocatorTests\n//\n// Created by Sam Khawase on 17.02.18.\n// Copyright © 2018 Loo"
},
{
"path": "LooLocatorTests/ApiClientTests.swift",
"chars": 2368,
"preview": "//\n// ApiClientTests.swift\n// LooLocatorTests\n//\n// Created by Sam Khawase on 17.02.20.\n// Copyright © 2020 LooLocat"
},
{
"path": "LooLocatorTests/Info.plist",
"chars": 1024,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "LooLocatorTests/LocationMocks.swift",
"chars": 1615,
"preview": "//\n// LocationMocks.swift\n// LooLocatorTests\n//\n// Created by Sam Khawase on 15.02.20.\n// Copyright © 2020 LooLocato"
},
{
"path": "LooLocatorTests/LocationProviderTests.swift",
"chars": 1815,
"preview": "//\n// LooLocatorTests.swift\n// LooLocatorTests\n//\n// Created by Sam Khawase on 14.02.20.\n// Copyright © 2020 LooLoca"
},
{
"path": "LooLocatorTests/MapViewModelTests.swift",
"chars": 2213,
"preview": "//\n// MapViewModelTests.swift\n// LooLocatorTests\n//\n// Created by Sam Khawase on 23.02.20.\n// Copyright © 2020 LooLo"
},
{
"path": "LooLocatorTests/MockViewController.swift",
"chars": 468,
"preview": "//\n// MockViewController.swift\n// LooLocatorTests\n//\n// Created by Sam Khawase on 23.02.20.\n// Copyright © 2020 LooL"
},
{
"path": "LooLocatorTests/OSMModelTests.swift",
"chars": 1262,
"preview": "//\n// OSMModelTests.swift\n// LooLocatorTests\n//\n// Created by Sam Khawase on 20.10.20.\n// Copyright © 2020 LooLocato"
},
{
"path": "LooLocatorTests/stubbedRepsonse.json",
"chars": 5833,
"preview": "{\n \"version\": 0.6,\n \"generator\": \"Overpass API 0.7.54.12 054bb0bb\",\n \"osm3s\": {\n \"timestamp_osm_base\": \""
},
{
"path": "README.md",
"chars": 2912,
"preview": "\n# LooLocator\n\nFind Amenities (*like toilets*) near you! The simple iOS fetches the crowd-sourced data from OpenStreetM"
},
{
"path": "berlin.gpx",
"chars": 197,
"preview": "<?xml version=\"1.0\"?>\n<gpx version=\"1.1\" creator=\"gpxgenerator.com\">\n<wpt lat=\"52.516327366345344\" lon=\"13.3780341222882"
}
]
About this extraction
This page contains the full source code of the samkhawse/OpenStreetAmenities GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 40 files (100.4 KB), approximately 28.9k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.