diff --git a/UberRides.podspec b/UberRides.podspec index ba222e47..6d20dc4f 100644 --- a/UberRides.podspec +++ b/UberRides.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "UberRides" - s.version = "0.1.1" + s.version = "0.2.0" s.summary = "The Official Uber Rides iOS SDK." s.description = <<-DESC This Swift library allows you to integrate Uber into your iOS app. It is designed to make it quick and easy to add a 'Request a Ride' button in your application, seamlessly connecting your users with Uber. @@ -9,12 +9,12 @@ Pod::Spec.new do |s| s.homepage = "https://github.com/uber/rides-ios-sdk" s.screenshots = "https://raw.githubusercontent.com/uber/rides-ios-sdk/master/img/example_app.png" s.license = { :type => "MIT", :file => "LICENSE" } - s.author = { "Christine Kim" => "christinek@uber.com" } + s.authors = { "Christine Kim" => "christinek@uber.com", "Farwa Naqi" => "farwa@uber.com" } s.platform = :ios, "8.0" - s.source = { :git => "https://github.com/uber/rides-ios-sdk.git", :tag => 'v' + s.version.to_s } + s.source = { :git => "https://github.com/uber/rides-ios-sdk.git", :tag => s.version } s.source_files = "source/UberRides/*.swift" - s.resources = "source/UberRides/Media.xcassets" + s.resource = "source/UberRides/UberRidesResources.bundle/*" s.requires_arc = true end diff --git a/source/UberRides.xcodeproj/project.pbxproj b/source/UberRides.xcodeproj/project.pbxproj index 7594118f..8b4dbbe4 100644 --- a/source/UberRides.xcodeproj/project.pbxproj +++ b/source/UberRides.xcodeproj/project.pbxproj @@ -14,8 +14,8 @@ AC0404921BFACD7C00AC1501 /* RequestButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC0404911BFACD7C00AC1501 /* RequestButton.swift */; }; AC0404941BFACDAF00AC1501 /* RequestDeeplink.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC0404931BFACDAF00AC1501 /* RequestDeeplink.swift */; }; AC0404961BFACDC900AC1501 /* RidesClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC0404951BFACDC900AC1501 /* RidesClient.swift */; }; - AC0404981BFACE0600AC1501 /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AC0404971BFACE0600AC1501 /* Media.xcassets */; }; AC04049A1BFACE5400AC1501 /* RequestDeeplinkTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC0404991BFACE5400AC1501 /* RequestDeeplinkTests.swift */; }; + D8F92D9A1C4486E100D65712 /* UberRidesResources.bundle in Resources */ = {isa = PBXBuildFile; fileRef = D8F92D991C4486E100D65712 /* UberRidesResources.bundle */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -39,8 +39,8 @@ AC0404911BFACD7C00AC1501 /* RequestButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestButton.swift; sourceTree = ""; }; AC0404931BFACDAF00AC1501 /* RequestDeeplink.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestDeeplink.swift; sourceTree = ""; }; AC0404951BFACDC900AC1501 /* RidesClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RidesClient.swift; sourceTree = ""; }; - AC0404971BFACE0600AC1501 /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = ""; }; AC0404991BFACE5400AC1501 /* RequestDeeplinkTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestDeeplinkTests.swift; sourceTree = ""; }; + D8F92D991C4486E100D65712 /* UberRidesResources.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = UberRidesResources.bundle; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -89,7 +89,7 @@ AC0404911BFACD7C00AC1501 /* RequestButton.swift */, AC0404931BFACDAF00AC1501 /* RequestDeeplink.swift */, AC0404951BFACDC900AC1501 /* RidesClient.swift */, - AC0404971BFACE0600AC1501 /* Media.xcassets */, + D8F92D991C4486E100D65712 /* UberRidesResources.bundle */, ); path = UberRides; sourceTree = ""; @@ -179,6 +179,8 @@ hasScannedForEncodings = 0; knownRegions = ( en, + "zh-Hans", + "zh-Hant", ); mainGroup = AC04046B1BFACD1D00AC1501; productRefGroup = AC0404761BFACD1D00AC1501 /* Products */; @@ -196,7 +198,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - AC0404981BFACE0600AC1501 /* Media.xcassets in Resources */, + D8F92D9A1C4486E100D65712 /* UberRidesResources.bundle in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/source/UberRides/RequestButton.swift b/source/UberRides/RequestButton.swift index a785aa50..bfb00004 100644 --- a/source/UberRides/RequestButton.swift +++ b/source/UberRides/RequestButton.swift @@ -82,7 +82,6 @@ public class RequestButton: UIButton { let clientID = RidesClient.sharedInstance.clientID deeplink = RequestDeeplink(withClientID: clientID!, fromSource: .Button) - deeplink!.build() } /** @@ -91,7 +90,6 @@ public class RequestButton: UIButton { public func setPickupLocationToCurrentLocation() { if RidesClient.sharedInstance.hasClientID() { deeplink!.setPickupLocationToCurrentLocation() - deeplink!.build() } } @@ -106,7 +104,6 @@ public class RequestButton: UIButton { public func setPickupLocation(latitude lat: String, longitude: String, nickname: String? = nil, address: String? = nil) { if RidesClient.sharedInstance.hasClientID() { deeplink!.setPickupLocation(latitude: lat, longitude: longitude, nickname: nickname, address: address) - deeplink!.build() } } @@ -121,7 +118,6 @@ public class RequestButton: UIButton { public func setDropoffLocation(latitude lat: String, longitude: String, nickname: String? = nil, address: String? = nil) { if RidesClient.sharedInstance.hasClientID() { deeplink!.setDropoffLocation(latitude: lat, longitude: longitude, nickname: nickname, address: address) - deeplink!.build() } } @@ -134,14 +130,14 @@ public class RequestButton: UIButton { public func setProductID(productID: String) { if RidesClient.sharedInstance.hasClientID() { deeplink!.setProductID(productID) - deeplink!.build() } } // add title, image, and sizing configuration private func setContent() { // add title label - uberTitleLabel.text = "Ride there with Uber" + let bundle = NSBundle(forClass: RequestButton.self) + uberTitleLabel.text = NSLocalizedString("RequestButton.TitleText", bundle: bundle, comment: "Request button description") uberTitleLabel.font = UIFont.systemFontOfSize(17) uberTitleLabel.numberOfLines = 1; @@ -238,6 +234,7 @@ public class RequestButton: UIButton { // initiate deeplink when button is tapped public func uberButtonTapped(sender: UIButton) { if RidesClient.sharedInstance.hasClientID() { + deeplink!.build() deeplink!.execute() } } diff --git a/source/UberRides/RequestDeeplink.swift b/source/UberRides/RequestDeeplink.swift index a0ffaa29..baba53fb 100644 --- a/source/UberRides/RequestDeeplink.swift +++ b/source/UberRides/RequestDeeplink.swift @@ -28,15 +28,16 @@ import UIKit // RequestDeeplink builds and executes a deeplink to the native Uber app. public class RequestDeeplink: NSObject { - private var parameters: [QueryParameter] + private var parameters: QueryParameters private var clientID: String private var deeplinkURI: String? private var source: RequestDeeplink.SourceParameter public init(withClientID: String, fromSource: SourceParameter = .Deeplink) { + parameters = QueryParameters() clientID = withClientID source = fromSource - parameters = [QueryParameter(parameterName: .ClientID, parameterValue: clientID)] + parameters.setParameter(.ClientID, parameterValue: clientID) } /** @@ -47,12 +48,18 @@ public class RequestDeeplink: NSObject { setPickupLocationToCurrentLocation() } - let deeplink = "uber://?" - var parameterStrings: [String] = [] - for parameter in parameters { - parameterStrings.append(parameter.toString()) + if !parameters.pendingChanges { + return deeplinkURI! } - deeplinkURI = deeplink + parameterStrings.joinWithSeparator("&") + + let components = NSURLComponents() + components.scheme = "uber" + components.host = "" + components.queryItems = parameters.getQueryItems() + + parameters.pendingChanges = false; + + deeplinkURI = components.string?.stringByRemovingPercentEncoding return deeplinkURI! } @@ -74,38 +81,42 @@ public class RequestDeeplink: NSObject { Set the user's current location as a default pickup location. */ public func setPickupLocationToCurrentLocation() { - parameters.append(QueryParameter(parameterName: .Action, parameterValue: "setPickup")) - parameters.append(QueryParameter(parameterName: .PickupDefault, parameterValue: "my_location")) + parameters.setParameter(.Action, parameterValue: "setPickup") + parameters.setParameter(.PickupDefault, parameterValue: "my_location") + parameters.deleteParameters([.PickupLatitude, .PickupLongitude, .PickupAddress, .PickupNickname]) } /** Set deeplink pickup location information. */ public func setPickupLocation(latitude lat: String, longitude: String, nickname: String? = nil, address: String? = nil) { - parameters.append(QueryParameter(parameterName: .Action, parameterValue: "setPickup")) - parameters.append(QueryParameter(parameterName: .PickupLatitude, parameterValue: lat)) - parameters.append(QueryParameter(parameterName: .PickupLongitude, parameterValue: longitude)) + parameters.deleteParameters([.PickupNickname, .PickupAddress]) + parameters.setParameter(.Action, parameterValue: "setPickup") + parameters.setParameter(.PickupLatitude, parameterValue: lat) + parameters.setParameter(.PickupLongitude, parameterValue: longitude) if nickname != nil { - parameters.append(QueryParameter(parameterName: .PickupNickname, parameterValue: nickname!)) + parameters.setParameter(.PickupNickname, parameterValue: nickname!) } if address != nil { - parameters.append(QueryParameter(parameterName: .PickupAddress, parameterValue: address!)) + parameters.setParameter(.PickupAddress, parameterValue: address!) } + + parameters.deleteParameters([.PickupDefault]) } /** Set deeplink dropoff location information. */ public func setDropoffLocation(latitude lat: String, longitude: String, nickname: String? = nil, address: String? = nil) { - parameters.append(QueryParameter(parameterName: .DropoffLatitude, parameterValue: lat)) - parameters.append(QueryParameter(parameterName: .DropoffLongitude, parameterValue: longitude)) + parameters.setParameter(.DropoffLatitude, parameterValue: lat) + parameters.setParameter(.DropoffLongitude, parameterValue: longitude) if nickname != nil { - parameters.append(QueryParameter(parameterName: .DropoffNickname, parameterValue: nickname!)) + parameters.setParameter(.DropoffNickname, parameterValue: nickname!) } if address != nil { - parameters.append(QueryParameter(parameterName: .DropoffAddress, parameterValue: address!)) + parameters.setParameter(.DropoffAddress, parameterValue: address!) } } @@ -114,25 +125,14 @@ public class RequestDeeplink: NSObject { location with the Rides API `GET /v1/products` endpoint. */ public func setProductID(productID: String) { - parameters.append(QueryParameter(parameterName: .ProductID, parameterValue: productID)) + parameters.setParameter(.ProductID, parameterValue: productID) } /** Return true if deeplink has set pickup latitude and longitude, false otherwise. */ internal func pickupLocationSet() -> Bool { - var hasLatitude = false - var hasLongitude = false - - for parameter in parameters { - if parameter.name == .PickupLatitude { - hasLatitude = true - } else if parameter.name == .PickupLongitude { - hasLongitude = true - } - } - - return (hasLatitude && hasLongitude) + return (parameters.doesParameterExist(.PickupLatitude) && parameters.doesParameterExist(.PickupLongitude)) || parameters.doesParameterExist(.PickupDefault) } /** @@ -158,8 +158,14 @@ public class RequestDeeplink: NSObject { } -// Store information about the name and value of a query parameter. -private class QueryParameter: NSObject { +// Store mapping of parameter names to values +private class QueryParameters: NSObject { + private var params = [String: String]() + private var pendingChanges: Bool + + private override init() { + pendingChanges = false; + } /** QueryParameterName is a set of query parameters than can be sent @@ -183,22 +189,47 @@ private class QueryParameter: NSObject { case DropoffAddress } - private let name: QueryParameterName - private let value: String + /** + Adds a query parameter. If parameterName has already been assigned a value, + its overwritten with parameterValue. + */ + private func setParameter(parameterName: QueryParameterName, parameterValue: String) { + params[stringFromParameterName(parameterName)] = stringFromParameterValue(parameterValue) + pendingChanges = true + } - private init(parameterName: QueryParameterName, parameterValue: String) { - name = parameterName - value = parameterValue - super.init() + /** + Removes key-value pair of all query parameters in array of parameter names. + */ + private func deleteParameters(parameters: Array) { + for name in parameters { + params.removeValueForKey(stringFromParameterName(name)) + } + pendingChanges = true } - private func toString() -> String { - let customAllowedChars = NSCharacterSet(charactersInString: " =\"#%/<>?@\\^`{|}!$&'()*+,:;[]%").invertedSet - let stringFromParameterValue = value.stringByAddingPercentEncodingWithAllowedCharacters(customAllowedChars)! - return "\(stringFromParameterName())=\(stringFromParameterValue)" + /** + - returns: An array containing an NSURLQueryItem for every parameter + */ + private func getQueryItems() -> Array { + var queryItems = [NSURLQueryItem]() + + for (parameterName, parameterValue) in params { + let queryItem = NSURLQueryItem(name: parameterName, value: parameterValue) + queryItems.append(queryItem) + } + + return queryItems } - private func stringFromParameterName() -> String { + /** + - returns: true if given query parameter has been set; false otherwise. + */ + private func doesParameterExist(parameterName: QueryParameterName) -> Bool { + return params[stringFromParameterName(parameterName)] != nil + } + + private func stringFromParameterName(name: QueryParameterName) -> String { switch name { case .Action: return "action" @@ -226,4 +257,9 @@ private class QueryParameter: NSObject { return "dropoff[formatted_address]" } } + + private func stringFromParameterValue(value: String) -> String { + let customAllowedChars = NSCharacterSet(charactersInString: " =\"#%/<>?@\\^`{|}!$&'()*+,:;[]%").invertedSet + return value.stringByAddingPercentEncodingWithAllowedCharacters(customAllowedChars)! + } } diff --git a/source/UberRides/UberRidesResources.bundle/Media.xcassets/Badge.imageset/Contents.json b/source/UberRides/UberRidesResources.bundle/Media.xcassets/Badge.imageset/Contents.json new file mode 100644 index 00000000..c883a615 --- /dev/null +++ b/source/UberRides/UberRidesResources.bundle/Media.xcassets/Badge.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "uber_badge_24.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "uber_badge_24@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "uber_badge_24@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/source/UberRides/UberRidesResources.bundle/Media.xcassets/Badge.imageset/uber_badge_24.png b/source/UberRides/UberRidesResources.bundle/Media.xcassets/Badge.imageset/uber_badge_24.png new file mode 100644 index 00000000..35465297 Binary files /dev/null and b/source/UberRides/UberRidesResources.bundle/Media.xcassets/Badge.imageset/uber_badge_24.png differ diff --git a/source/UberRides/UberRidesResources.bundle/Media.xcassets/Badge.imageset/uber_badge_24@2x.png b/source/UberRides/UberRidesResources.bundle/Media.xcassets/Badge.imageset/uber_badge_24@2x.png new file mode 100644 index 00000000..c0c0e076 Binary files /dev/null and b/source/UberRides/UberRidesResources.bundle/Media.xcassets/Badge.imageset/uber_badge_24@2x.png differ diff --git a/source/UberRides/UberRidesResources.bundle/Media.xcassets/Badge.imageset/uber_badge_24@3x.png b/source/UberRides/UberRidesResources.bundle/Media.xcassets/Badge.imageset/uber_badge_24@3x.png new file mode 100644 index 00000000..e7d8f7eb Binary files /dev/null and b/source/UberRides/UberRidesResources.bundle/Media.xcassets/Badge.imageset/uber_badge_24@3x.png differ diff --git a/source/UberRides/UberRidesResources.bundle/Media.xcassets/Contents.json b/source/UberRides/UberRidesResources.bundle/Media.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/source/UberRides/UberRidesResources.bundle/Media.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/source/UberRides/UberRidesResources.bundle/en.lproj/Localizable.strings b/source/UberRides/UberRidesResources.bundle/en.lproj/Localizable.strings new file mode 100644 index 00000000..8242700f --- /dev/null +++ b/source/UberRides/UberRidesResources.bundle/en.lproj/Localizable.strings @@ -0,0 +1,2 @@ +/* The title of the request button that deep links to the Uber app */ +"RequestButton.TitleText" = "Ride there with Uber"; \ No newline at end of file diff --git a/source/UberRides/UberRidesResources.bundle/zh-Hans.lproj/Localizable.strings b/source/UberRides/UberRidesResources.bundle/zh-Hans.lproj/Localizable.strings new file mode 100644 index 00000000..8b99922c --- /dev/null +++ b/source/UberRides/UberRidesResources.bundle/zh-Hans.lproj/Localizable.strings @@ -0,0 +1,2 @@ +/* The title of the request button that deep links to the Uber app */ +"RequestButton.TitleText" = "搭乘优步前往"; \ No newline at end of file diff --git a/source/UberRides/UberRidesResources.bundle/zh-Hant.lproj/Localizable.strings b/source/UberRides/UberRidesResources.bundle/zh-Hant.lproj/Localizable.strings new file mode 100644 index 00000000..8b99922c --- /dev/null +++ b/source/UberRides/UberRidesResources.bundle/zh-Hant.lproj/Localizable.strings @@ -0,0 +1,2 @@ +/* The title of the request button that deep links to the Uber app */ +"RequestButton.TitleText" = "搭乘优步前往"; \ No newline at end of file diff --git a/source/UberRidesTests/RequestDeeplinkTests.swift b/source/UberRidesTests/RequestDeeplinkTests.swift index 9a44e63d..8a5f9179 100644 --- a/source/UberRidesTests/RequestDeeplinkTests.swift +++ b/source/UberRidesTests/RequestDeeplinkTests.swift @@ -26,12 +26,31 @@ import XCTest @testable import UberRides +let clientID = "clientID1234" +let productID = "productID1234" +let pickupLat = "37.770" +let pickupLong = "-122.466" +let dropoffLat = "37.791" +let dropoffLong = "-122.405" +let pickupNickname = "California Academy of Science" +let pickupAddress = "55 Music Concourse Drive, San Francisco" +let dropoffNickname = "Pier 39" +let dropoffAddress = "Beach Street & The Embarcadero, San Francisco" + struct ExpectedDeeplink { - static let defaultParameters = "uber://?client_id=clientID1234&action=setPickup&pickup=my_location" - static let pickupLatLng = "uber://?client_id=clientID1234&action=setPickup&pickup[latitude]=37.770&pickup[longitude]=-122.466" - static let allPickupParameters = "uber://?client_id=clientID1234&action=setPickup&pickup[latitude]=37.770&pickup[longitude]=-122.466&pickup[nickname]=California%20Academy%20of%20Science&pickup[formatted_address]=55%20Music%20Concourse%20Drive%2C%20San%20Francisco" - static let dropoffLatLng = "uber://?client_id=clientID1234&dropoff[latitude]=37.791&dropoff[longitude]=-122.405&action=setPickup&pickup=my_location" - static let allParameters = "uber://?client_id=clientID1234&product_id=productID1234&action=setPickup&pickup[latitude]=37.770&pickup[longitude]=-122.466&pickup[nickname]=California%20Academy%20of%20Science&pickup[formatted_address]=55%20Music%20Concourse%20Drive%2C%20San%20Francisco&dropoff[latitude]=37.791&dropoff[longitude]=-122.405&dropoff[nickname]=Pier%2039&dropoff[formatted_address]=Beach%20Street%20%26%20The%20Embarcadero%2C%20San%20Francisco" + static let uberScheme = "uber://?" + static let clientIDQuery = "client_id=\(clientID)" + static let productIDQuery = "product_id=\(productID)" + static let setPickupAction = "action=setPickup" + static let defaultPickupQuery = "pickup=my_location" + static let pickupLatQuery = "pickup[latitude]=\(pickupLat)" + static let pickupLongQuery = "pickup[longitude]=\(pickupLong)" + static let pickupNicknameQuery = "pickup[nickname]=\(pickupNickname)" + static let pickupAddressQuery = "pickup[formatted_address]=\(pickupAddress)" + static let dropoffLatQuery = "dropoff[latitude]=\(dropoffLat)" + static let dropoffLongQuery = "dropoff[longitude]=\(dropoffLong)" + static let dropoffNicknameQuery = "dropoff[nickname]=\(dropoffNickname)" + static let dropoffAddressQuery = "dropoff[formatted_address]=\(dropoffAddress)" } class UberRidesDeeplinkTests: XCTestCase { @@ -48,7 +67,7 @@ class UberRidesDeeplinkTests: XCTestCase { Test that PickupLocationSet check returns False if no Pickup Parameters were added */ func testPickupLocationSetIsFalseWithNoPickupParameters() { - let deeplink = RequestDeeplink(withClientID: "clientID1234") + let deeplink = RequestDeeplink(withClientID: clientID) XCTAssertFalse(deeplink.pickupLocationSet()) } @@ -56,8 +75,8 @@ class UberRidesDeeplinkTests: XCTestCase { Test that PickupLocationSet check returns True if Pickup Parameters are added */ func testPickupLocationSetIsTrueWithPickupParameters() { - let deeplink = RequestDeeplink(withClientID: "clientID1234") - deeplink.setPickupLocation(latitude: "37.770", longitude: "-122.466") + let deeplink = RequestDeeplink(withClientID: clientID) + deeplink.setPickupLocation(latitude: pickupLat, longitude: pickupLong) XCTAssertTrue(deeplink.pickupLocationSet()) } @@ -65,45 +84,164 @@ class UberRidesDeeplinkTests: XCTestCase { Test to build an UberDeeplink with no PickupLatLng and assign user's current location as default */ func testBuildDeeplinkWithClientIDHasDefaultParameters() { - let deeplink = RequestDeeplink(withClientID: "clientID1234") - XCTAssertEqual(ExpectedDeeplink.defaultParameters, deeplink.build()) + let deeplink = RequestDeeplink(withClientID: clientID) + let uri = deeplink.build() + XCTAssertTrue(uri.containsString(ExpectedDeeplink.uberScheme)) + + let components = NSURLComponents(string: uri) + XCTAssertEqual(components?.queryItems?.count, 3) + + let query = components?.query + XCTAssertTrue(query!.containsString(ExpectedDeeplink.clientIDQuery)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.setPickupAction)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.defaultPickupQuery)) } /** Test to build an UberDeeplink with a Pickup Latitude and Longitude. */ func testBuildDeeplinkWithPickupLatLng() { - let deeplink = RequestDeeplink(withClientID: "clientID1234") - deeplink.setPickupLocation(latitude: "37.770", longitude: "-122.466") - XCTAssertEqual(ExpectedDeeplink.pickupLatLng, deeplink.build()) + let deeplink = RequestDeeplink(withClientID: clientID) + deeplink.setPickupLocation(latitude: pickupLat, longitude: pickupLong) + + let components = NSURLComponents(URL: NSURL(string: deeplink.build())!, resolvingAgainstBaseURL: false) + XCTAssertEqual(components?.queryItems?.count, 4) + + let query = components?.query + XCTAssertTrue(query!.containsString(ExpectedDeeplink.clientIDQuery)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.setPickupAction)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.pickupLatQuery)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.pickupLongQuery)) } /** Test to build an UberDeeplink with all optional Pickup Parameters. */ - func testBuildDeeplinkWithAllPickupParamerets() { - let deeplink = RequestDeeplink(withClientID: "clientID1234") - deeplink.setPickupLocation(latitude: "37.770", longitude: "-122.466", nickname: "California Academy of Science", address: "55 Music Concourse Drive, San Francisco") - XCTAssertEqual(ExpectedDeeplink.allPickupParameters, deeplink.build()) + func testBuildDeeplinkWithAllPickupParameters() { + let deeplink = RequestDeeplink(withClientID: clientID) + deeplink.setPickupLocation(latitude: pickupLat, longitude: pickupLong, nickname: pickupNickname, address: pickupAddress) + + let components = NSURLComponents(URL: NSURL(string: deeplink.build())!, resolvingAgainstBaseURL: false) + XCTAssertEqual(components?.queryItems?.count, 6) + + let query = components?.query + XCTAssertTrue(query!.containsString(ExpectedDeeplink.clientIDQuery)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.setPickupAction)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.pickupLatQuery)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.pickupLongQuery)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.pickupNicknameQuery)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.pickupAddressQuery)) } /** Test to build an UberDeeplink with only Dropoff Parameters (set default Pickup Parameters). */ func testBuildDeeplinkWithoutPickupParameters() { - let deeplink = RequestDeeplink(withClientID: "clientID1234") - deeplink.setDropoffLocation(latitude: "37.791", longitude: "-122.405") - XCTAssertEqual(ExpectedDeeplink.dropoffLatLng, deeplink.build()) + let deeplink = RequestDeeplink(withClientID: clientID) + deeplink.setDropoffLocation(latitude: dropoffLat, longitude: dropoffLong) + + let components = NSURLComponents(URL: NSURL(string: deeplink.build())!, resolvingAgainstBaseURL: false) + XCTAssertEqual(components?.queryItems?.count, 5) + + let query = components?.query + XCTAssertTrue(query!.containsString(ExpectedDeeplink.clientIDQuery)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.setPickupAction)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.defaultPickupQuery)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.dropoffLatQuery)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.dropoffLongQuery)) } /** Test to build an UberDeeplink with all possible query parameters. */ func testBuildDeeplinkWithAllParameters() { - let deeplink = RequestDeeplink(withClientID: "clientID1234") - deeplink.setProductID("productID1234") - deeplink.setPickupLocation(latitude: "37.770", longitude: "-122.466", nickname: "California Academy of Science", address: "55 Music Concourse Drive, San Francisco") - deeplink.setDropoffLocation(latitude: "37.791", longitude: "-122.405", nickname: "Pier 39", address: "Beach Street & The Embarcadero, San Francisco") - XCTAssertEqual(ExpectedDeeplink.allParameters, deeplink.build()) + let deeplink = RequestDeeplink(withClientID: clientID) + deeplink.setProductID(productID) + deeplink.setPickupLocation(latitude: pickupLat, longitude: pickupLong, nickname: pickupNickname, address: pickupAddress) + deeplink.setDropoffLocation(latitude: dropoffLat, longitude: dropoffLong, nickname: dropoffNickname, address: dropoffAddress) + + let components = NSURLComponents(URL: NSURL(string: deeplink.build())!, resolvingAgainstBaseURL: false) + XCTAssertEqual(components?.queryItems?.count, 11) + + let query = components?.query + XCTAssertTrue(query!.containsString(ExpectedDeeplink.clientIDQuery)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.productIDQuery)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.setPickupAction)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.pickupLatQuery)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.pickupLongQuery)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.pickupNicknameQuery)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.pickupAddressQuery)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.dropoffLatQuery)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.dropoffLongQuery)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.dropoffNicknameQuery)) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.dropoffAddressQuery)) + } + + /** + Test to set deeplink to default location, then override with another location. + Test ensures deeplink removes original default location parameter. + */ + func testOverrideDefaultPickupWithPickupLocation() { + let deeplink = RequestDeeplink(withClientID: clientID) + deeplink.setPickupLocationToCurrentLocation() + + var components = NSURLComponents(URL: NSURL(string: deeplink.build())!, resolvingAgainstBaseURL: false) + XCTAssertEqual(components?.queryItems?.count, 3) + + var query = components?.query + XCTAssertTrue(query!.containsString(ExpectedDeeplink.defaultPickupQuery)) + + deeplink.setPickupLocation(latitude: pickupLat, longitude: pickupLong) + components = NSURLComponents(URL: NSURL(string: deeplink.build())!, resolvingAgainstBaseURL: false) + query = components?.query + + XCTAssertEqual(components?.queryItems?.count, 4) + XCTAssertFalse(query!.containsString(ExpectedDeeplink.defaultPickupQuery)) + } + + /** + Test to set deeplink to pickup location, then override with current location. + Test ensures deeplink removes original pickup location parameters. + */ + func testOverridePickupLocationWithDefault() { + let deeplink = RequestDeeplink(withClientID: clientID) + deeplink.setPickupLocation(latitude: pickupLat, longitude: pickupLong, nickname: pickupNickname, address: pickupAddress) + + var components = NSURLComponents(URL: NSURL(string: deeplink.build())!, resolvingAgainstBaseURL: false) + XCTAssertEqual(components?.queryItems?.count, 6) + + deeplink.setPickupLocationToCurrentLocation() + components = NSURLComponents(URL: NSURL(string: deeplink.build())!, resolvingAgainstBaseURL: false) + let query = components?.query + + XCTAssertEqual(components?.queryItems?.count, 3) + XCTAssertTrue(query!.containsString(ExpectedDeeplink.defaultPickupQuery)) + } + + /** + Test to rebuild deep link without making changes and verify that the same string is returned. + */ + func testRebuildingDeeplinkWithoutChanges() { + let deeplink = RequestDeeplink(withClientID: clientID) + deeplink.setPickupLocation(latitude: pickupLat, longitude: pickupLong, nickname: pickupNickname, address: pickupAddress) + + let originalAddress = unsafeAddressOf(deeplink.build()) + let rebuiltAddress = unsafeAddressOf(deeplink.build()) + + XCTAssertEqual(originalAddress, rebuiltAddress) + } + + /** + Test to rebuild deep link after making changes and verify that the deep link has been built again. + */ + func testRebuildingDeeplinWithChanges() { + let deeplink = RequestDeeplink(withClientID: clientID) + deeplink.setPickupLocation(latitude: pickupLat, longitude: pickupLong, nickname: pickupNickname, address: pickupAddress) + + let originalAddress = unsafeAddressOf(deeplink.build()) + deeplink.setProductID(productID) + let rebuiltAddress = unsafeAddressOf(deeplink.build()) + + XCTAssertNotEqual(originalAddress, rebuiltAddress) } }