diff --git a/.swift-version b/.swift-version new file mode 100644 index 0000000..819e07a --- /dev/null +++ b/.swift-version @@ -0,0 +1 @@ +5.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index e5794ec..aca7d22 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1 +1 @@ -github "Quick/Nimble" "v7.3.1" +github "Quick/Nimble" "v8.0.1" diff --git a/Spot.xcodeproj/project.pbxproj b/Spot.xcodeproj/project.pbxproj index 42f441e..7066b6f 100644 --- a/Spot.xcodeproj/project.pbxproj +++ b/Spot.xcodeproj/project.pbxproj @@ -12,7 +12,6 @@ 077B35401F7BA13D0000282F /* LocationHandlerPermissionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FFC2D451E3A24F000080DD9 /* LocationHandlerPermissionTests.swift */; }; 077B35411F7BA9DD0000282F /* LocationHandlerRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FA74A291E3752F90014BD82 /* LocationHandlerRequestTests.swift */; }; 077B35421F7BAF2B0000282F /* LocationServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F44F4E71E379C3E000BBC4E /* LocationServiceTests.swift */; }; - 4F3399B51E3A0AD5002634E5 /* LocationResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F44F4DC1E376D31000BBC4E /* LocationResult.swift */; }; 4F3399B91E3A0C05002634E5 /* LocationAccess.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F44F4DE1E37770C000BBC4E /* LocationAccess.swift */; }; 4F44F4D21E376C2F000BBC4E /* MockLocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F44F4D11E376C2F000BBC4E /* MockLocationManager.swift */; }; 4F44F4E11E37775D000BBC4E /* LocationAccessTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F44F4E01E37775D000BBC4E /* LocationAccessTests.swift */; }; @@ -41,7 +40,6 @@ 077B353E1F7B955B0000282F /* LocationHandlerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationHandlerTests.swift; sourceTree = ""; }; 4F44F4D11E376C2F000BBC4E /* MockLocationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockLocationManager.swift; sourceTree = ""; }; 4F44F4DA1E376CB8000BBC4E /* CLLocationCoordinate2D+Equatable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CLLocationCoordinate2D+Equatable.swift"; sourceTree = ""; }; - 4F44F4DC1E376D31000BBC4E /* LocationResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationResult.swift; sourceTree = ""; }; 4F44F4DE1E37770C000BBC4E /* LocationAccess.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationAccess.swift; sourceTree = ""; }; 4F44F4E01E37775D000BBC4E /* LocationAccessTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocationAccessTests.swift; sourceTree = ""; }; 4F44F4E21E377DF6000BBC4E /* LocationManagerType+Mocks.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "LocationManagerType+Mocks.swift"; sourceTree = ""; }; @@ -122,7 +120,6 @@ 4F9DE3D81E365F49009013B4 /* Info.plist */, 4F9DE3D71E365F49009013B4 /* Spot.h */, 4F44F4DE1E37770C000BBC4E /* LocationAccess.swift */, - 4F44F4DC1E376D31000BBC4E /* LocationResult.swift */, 4F44F4E51E3797C5000BBC4E /* LocationService.swift */, 077B353C1F7B90FC0000282F /* LocationHandler.swift */, ); @@ -212,27 +209,28 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0820; - LastUpgradeCheck = 0930; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = "David Hardiman"; TargetAttributes = { 4F9DE3D31E365F49009013B4 = { CreatedOnToolsVersion = 8.2.1; - LastSwiftMigration = 1000; + LastSwiftMigration = 1020; ProvisioningStyle = Automatic; }; 4F9DE3DC1E365F49009013B4 = { CreatedOnToolsVersion = 8.2.1; - LastSwiftMigration = 1000; + LastSwiftMigration = 1020; ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 4F9DE3CE1E365F49009013B4 /* Build configuration list for PBXProject "Spot" */; compatibilityVersion = "Xcode 9.3"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, + Base, ); mainGroup = 4F9DE3CA1E365F49009013B4; productRefGroup = 4F9DE3D51E365F49009013B4 /* Products */; @@ -304,7 +302,6 @@ files = ( 077B353D1F7B90FC0000282F /* LocationHandler.swift in Sources */, 4F3399B91E3A0C05002634E5 /* LocationAccess.swift in Sources */, - 4F3399B51E3A0AD5002634E5 /* LocationResult.swift in Sources */, 4F44F4E61E3797C5000BBC4E /* LocationService.swift in Sources */, 4F44F4E41E378D2D000BBC4E /* CLLocationCoordinate2D+Equatable.swift in Sources */, ); @@ -341,6 +338,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -421,7 +419,7 @@ SKIP_INSTALL = YES; SWIFT_COMPILATION_MODE = singlefile; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Calabash; }; @@ -439,7 +437,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = me.davidhardiman.SpotTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Calabash; }; @@ -447,6 +445,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -509,6 +508,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -583,7 +583,7 @@ SKIP_INSTALL = YES; SWIFT_COMPILATION_MODE = singlefile; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -607,7 +607,7 @@ PRODUCT_BUNDLE_IDENTIFIER = me.davidhardiman.SPOT; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -625,7 +625,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = me.davidhardiman.SpotTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -643,7 +643,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = me.davidhardiman.SpotTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/Spot.xcodeproj/xcshareddata/xcschemes/Spot.xcscheme b/Spot.xcodeproj/xcshareddata/xcschemes/Spot.xcscheme index c7fa6ef..9a003eb 100644 --- a/Spot.xcodeproj/xcshareddata/xcschemes/Spot.xcscheme +++ b/Spot.xcodeproj/xcshareddata/xcschemes/Spot.xcscheme @@ -1,6 +1,6 @@ Bool static func authorizationStatus() -> CLAuthorizationStatus } @@ -59,6 +59,8 @@ open class LocationAccess { return false case .authorizedAlways, .authorizedWhenInUse: return true + @unknown default: + return false } } } diff --git a/Spot/LocationHandler.swift b/Spot/LocationHandler.swift index c3a83ae..9674c88 100644 --- a/Spot/LocationHandler.swift +++ b/Spot/LocationHandler.swift @@ -9,12 +9,12 @@ import CoreLocation import Foundation -public typealias LocationCompletion = ((LocationResult) -> Void) +public typealias LocationCompletion = ((Result) -> Void) -public protocol LocationHandling: class { +public protocol LocationHandling: AnyObject { func requestLocation(_ completion: @escaping LocationCompletion) func cancelCurrentRequest() - var didChangeAuthorizationStatus: ((LocationResult) -> Void)? { get set } + var didChangeAuthorizationStatus: ((Result) -> Void)? { get set } } // MARK: - CLLocationManagerDelegate @@ -26,7 +26,7 @@ class LocationHandler: NSObject, LocationHandling, CLLocationManagerDelegate { let locationAccess: LocationAccess /// Closure called when authorization status changes - var didChangeAuthorizationStatus: ((LocationResult) -> Void)? + var didChangeAuthorizationStatus: ((Result) -> Void)? init(locationManager: LocationManaging, locationAccess: LocationAccess = LocationAccess()) { self.locationManager = locationManager @@ -104,6 +104,8 @@ class LocationHandler: NSObject, LocationHandling, CLLocationManagerDelegate { case .notDetermined: // Not determined is the initial state and is not used for observing changes to the authorization status break + @unknown default: + break } } } diff --git a/Spot/LocationResult.swift b/Spot/LocationResult.swift deleted file mode 100644 index 605be83..0000000 --- a/Spot/LocationResult.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// Result+Additions.swift -// Spot -// -// Created by David Hardiman on 26/09/2018. -// Copyright © 2018 David Hardiman. All rights reserved. -// - -import Foundation - -/// Encapsulates the result of task that can succeed or fail -/// -/// - success: Success result with object -/// - failure: Failure result with error -public enum LocationResult { - case success(T) - case failure(E) -} - -extension LocationResult { - /// Returns the success result if it exists, otherwise nil - func successResult() -> T? { - switch self { - case let .success(successResult): - return successResult - case .failure: - return nil - } - } - - /// Returns the error if it exists, otherwise nil - func error() -> E? { - switch self { - case .success: - return nil - case let .failure(error): - return error - } - } -} diff --git a/Spot/LocationService.swift b/Spot/LocationService.swift index 40827c0..d627323 100644 --- a/Spot/LocationService.swift +++ b/Spot/LocationService.swift @@ -46,7 +46,7 @@ public protocol LocationReporting { /// Method to request a users location /// /// - Parameter completion: Completion including the result of the request - func requestLocation(completion: @escaping (LocationResult) -> Void) + func requestLocation(completion: @escaping (Result) -> Void) /// Cancel all requests for location func cancelRequestingLocation() @@ -54,7 +54,7 @@ public protocol LocationReporting { /// Register to observe authorisation changes /// /// - Parameter changesHandler: Closure to call when changes happen - func observeAuthorizationChanges(changesHandler: @escaping (LocationResult) -> Void) + func observeAuthorizationChanges(changesHandler: @escaping (Result) -> Void) } /// The operation context to use get a user location @@ -74,7 +74,7 @@ public class LocationService: LocationReporting { self.locationHandler = locationHandler } - public func requestLocation(completion: @escaping (LocationResult) -> Void) { + public func requestLocation(completion: @escaping (Result) -> Void) { locationHandler.requestLocation(completion) } @@ -82,7 +82,7 @@ public class LocationService: LocationReporting { locationHandler.cancelCurrentRequest() } - public func observeAuthorizationChanges(changesHandler: @escaping (LocationResult) -> Void) { + public func observeAuthorizationChanges(changesHandler: @escaping (Result) -> Void) { locationHandler.didChangeAuthorizationStatus = changesHandler } } diff --git a/SpotTests/LocationHandlerAuthorisationTests.swift b/SpotTests/LocationHandlerAuthorisationTests.swift index ddfaa33..cd6eb79 100644 --- a/SpotTests/LocationHandlerAuthorisationTests.swift +++ b/SpotTests/LocationHandlerAuthorisationTests.swift @@ -14,7 +14,7 @@ extension LocationHandlerTests { func givenSuccessExpectation() { let closureExpectation = expectation(description: #function) handler.didChangeAuthorizationStatus = { result in - expect(result.successResult()).toNot(beNil()) + expect(try? result.get()).toNot(beNil()) closureExpectation.fulfill() } } @@ -34,7 +34,7 @@ extension LocationHandlerTests { func givenExpectationForFailure(with error: LocationPermissionError) { let closureExpectation = expectation(description: #function) handler.didChangeAuthorizationStatus = { result in - expect(result.error()).to(equal(error)) + expect(result.error).to(equal(error)) closureExpectation.fulfill() } } diff --git a/SpotTests/LocationHandlerPermissionTests.swift b/SpotTests/LocationHandlerPermissionTests.swift index 3a0c1b2..9019690 100644 --- a/SpotTests/LocationHandlerPermissionTests.swift +++ b/SpotTests/LocationHandlerPermissionTests.swift @@ -46,11 +46,11 @@ extension LocationHandlerTests { func testLocationRequestReturnsDisabledErrorWhenLocationServicesAreDisabled() { MockLocationManager.isLocationServicesEnabled = false - var receivedResult: LocationResult? + var receivedResult: Result? handler.requestLocation { result in receivedResult = result } - expect(receivedResult!.error()).to(equal(LocationRequestError.locationDisabled)) + expect(receivedResult!.error).to(equal(LocationRequestError.locationDisabled)) } func testLocationRequestIsInitiatedWhenAuthorizationStatusIsAuthorizedAlways() { @@ -69,22 +69,22 @@ extension LocationHandlerTests { func testLocationRequestReportsErrorWhenAuthorizationStatusIsDenied() { givenLocationPermissionHasNotBeenRequested() - var receivedResult: LocationResult? + var receivedResult: Result? handler.requestLocation { result in receivedResult = result } whenLocationPermissionIsReceived(withStatus: .denied) - expect(receivedResult!.error()).to(equal(LocationRequestError.locationDenied)) + expect(receivedResult!.error).to(equal(LocationRequestError.locationDenied)) } func testLocationRequestReportsErrorWhenAuthorizationStatusIsRestricted() { givenLocationPermissionHasNotBeenRequested() - var receivedResult: LocationResult? + var receivedResult: Result? handler.requestLocation { result in receivedResult = result } whenLocationPermissionIsReceived(withStatus: .restricted) - expect(receivedResult!.error()).to(equal(LocationRequestError.locationDenied)) + expect(receivedResult!.error).to(equal(LocationRequestError.locationDenied)) } } diff --git a/SpotTests/LocationHandlerRequestTests.swift b/SpotTests/LocationHandlerRequestTests.swift index 44f9701..e34ba2e 100644 --- a/SpotTests/LocationHandlerRequestTests.swift +++ b/SpotTests/LocationHandlerRequestTests.swift @@ -26,7 +26,7 @@ extension LocationHandlerTests { } func testLocationRequestDoesNotCallBackIfCancelled() { - var receivedResult: LocationResult? + var receivedResult: Result? handler.requestLocation { result in receivedResult = result } @@ -37,22 +37,22 @@ extension LocationHandlerTests { func testLocationRequestDoesNotCallRequestWhenLocationServicesDisabled() { MockLocationManager.isLocationServicesEnabled = false - var receivedResult: LocationResult? + var receivedResult: Result? handler.requestLocation { result in receivedResult = result } - expect(receivedResult!.error()).to(equal(LocationRequestError.locationDisabled)) + expect(receivedResult!.error).to(equal(LocationRequestError.locationDisabled)) expect(self.mockLocationManager.requestLocationCallCount).to(equal(0)) } func testLocationRequestDoesNotCallRequestLocationWhenLocationServicesDenied() { MockLocationManager.currentAuthorizationStatus = .denied - var receivedResult: LocationResult? + var receivedResult: Result? handler.requestLocation { result in receivedResult = result } whenLocationPermissionIsReceived(withStatus: .denied) - expect(receivedResult!.error()).to(equal(LocationRequestError.locationDenied)) + expect(receivedResult!.error).to(equal(LocationRequestError.locationDenied)) expect(self.mockLocationManager.requestLocationCallCount).to(equal(0)) } @@ -62,31 +62,40 @@ extension LocationHandlerTests { } func testLocationManagerDidUpdateLocationCallsBackWithErrorWhenNoLocationsAreReturned() { - var receivedResult: LocationResult? + var receivedResult: Result? handler.requestLocation { result in receivedResult = result } handler.locationManager(actualLocationManager, didUpdateLocations: []) - expect(receivedResult!.error()).to(equal(LocationRequestError.locationUnknown)) + expect(receivedResult!.error).to(equal(LocationRequestError.locationUnknown)) } func testLocationManagerDidUpdateLocationCallsBackWithLocationOnSuccess() { - var receivedResult: LocationResult? + var receivedResult: Result? handler.requestLocation { result in receivedResult = result } let location = whenALocationIsReturned() - expect(receivedResult!.successResult()).to(equal(location.coordinate)) + expect(try? receivedResult!.get()).to(equal(location.coordinate)) } func testLocationManagerDidFailWithErrorCallsBackForwardingError() { let mockedError = LocationRequestError.locationUnknown - var receivedResult: LocationResult? + var receivedResult: Result? handler.requestLocation { result in receivedResult = result } handler.locationManager(actualLocationManager, didFailWithError: mockedError) - expect(receivedResult!.error()).to(equal(mockedError)) + expect(receivedResult!.error).to(equal(mockedError)) + } +} + +extension Result { + var error: Failure? { + if case let .failure(error) = self { + return error + } + return nil } } diff --git a/SpotTests/LocationServiceTests.swift b/SpotTests/LocationServiceTests.swift index f03ff0e..6774d6c 100644 --- a/SpotTests/LocationServiceTests.swift +++ b/SpotTests/LocationServiceTests.swift @@ -22,7 +22,7 @@ class MockLocationHandler: LocationHandling { receivedCancel = true } - var didChangeAuthorizationStatus: ((LocationResult) -> Void)? + var didChangeAuthorizationStatus: ((Result) -> Void)? } class LocationServiceTests: XCTestCase {