From 7e5043991d95f345d21eab58511de01454bb6dc2 Mon Sep 17 00:00:00 2001 From: Farwa Naqi Date: Fri, 15 Jan 2016 17:47:19 -0800 Subject: [PATCH] Add China text localization and fixes #6. --- UberRides.podspec | 8 +- source/UberRides.xcodeproj/project.pbxproj | 10 +- source/UberRides/RequestButton.swift | 9 +- source/UberRides/RequestDeeplink.swift | 124 ++++++++---- .../Badge.imageset/Contents.json | 23 +++ .../Badge.imageset/uber_badge_24.png | Bin 0 -> 785 bytes .../Badge.imageset/uber_badge_24@2x.png | Bin 0 -> 1522 bytes .../Badge.imageset/uber_badge_24@3x.png | Bin 0 -> 2147 bytes .../Media.xcassets/Contents.json | 6 + .../en.lproj/Localizable.strings | 2 + .../zh-Hans.lproj/Localizable.strings | 2 + .../zh-Hant.lproj/Localizable.strings | 2 + .../UberRidesTests/RequestDeeplinkTests.swift | 188 +++++++++++++++--- 13 files changed, 291 insertions(+), 83 deletions(-) create mode 100644 source/UberRides/UberRidesResources.bundle/Media.xcassets/Badge.imageset/Contents.json create mode 100644 source/UberRides/UberRidesResources.bundle/Media.xcassets/Badge.imageset/uber_badge_24.png create mode 100644 source/UberRides/UberRidesResources.bundle/Media.xcassets/Badge.imageset/uber_badge_24@2x.png create mode 100644 source/UberRides/UberRidesResources.bundle/Media.xcassets/Badge.imageset/uber_badge_24@3x.png create mode 100644 source/UberRides/UberRidesResources.bundle/Media.xcassets/Contents.json create mode 100644 source/UberRides/UberRidesResources.bundle/en.lproj/Localizable.strings create mode 100644 source/UberRides/UberRidesResources.bundle/zh-Hans.lproj/Localizable.strings create mode 100644 source/UberRides/UberRidesResources.bundle/zh-Hant.lproj/Localizable.strings 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 0000000000000000000000000000000000000000..35465297e3d8848fed8f1faa72f08e8dff62fc0e GIT binary patch literal 785 zcmV+s1Md8ZP)xx!nTOFX3! zrBfkSQJUi{3^Ab*H8)mws_%+Ai6+3&sizBg|-1?)OH`fjuC8EZL8DacDa#*fvq*s;sTl|tdB zo$|rwYnwZ;ySoQxXTR8D0_cbm3Iu!Jlnr~L+qhjk=ku4LR4PKb{1BbJLI=MYc&=94 zjsTq(T&<_@xIDp}JaW@W3r7Aw!O`~N<0qslmqA0AmVtq5khLxeP!P6O>o5RE8n8*H z*g9SZj*m|Qhwalg*CQhYGQk%XUV;=`3cIwl0^2+9xqY;F2PP*UMAppBJ!ejwU`KfQ z4iqgsa@KddU2vQqAm=^L3!-(L6KFQS`D1{=!RugI7H=kv%mLmE&WZ4h;ERi|V>#d7 z{|bAbzXUdW{sjyV-!j2{oHTCD5EN*T(@6yd{~fA<3;HNJfJnB87m|U?XYmd(g!gv< zkyKRCBm<|u(8fDHPCA${JX28O{~MeL0JT1_6QzN1h;%*^fEzSw;30~_>dS2>04FLb zbZ{9or^txJr^m`>6M|Fd>$5_diTS9=nrD7^n3G=MmX&2|Z-9PasY#I;WwY60r?$(t z{d8tZIr$6t?CcB&>U2CfIA|EbYPHA|bZhG!E434Fw=~LHkr%NJD;1!nAJU>nzOk_x zBY03M%xtN?SqH*wveL=c(A@v?3NATt46*H4asMv)Ctyb4yb@eR_4c(4SMcKl% zNN_1~26@8Te%k&SD`>sZFb-if730DC`>zJI$i;7HuWUIFI6jY!-9uVkJO>s0K;t(w ze)%W^4FwOYh$jC9s?~hGOg@_{d1X{v*HubhRQTDl_nu P00000NkvXXu0mjf0H|TF literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c0c0e076b9e95677e4c6d0633618df2bc7feaece GIT binary patch literal 1522 zcmV&m=x$ zjDhP&l9(sWo~~xe22J~ie;)GmE4A9;j(9jh;2ZpOO@NOXJWa=fT;zpl$^xYDE8>g$ z`~SkTjZOaiO?-`+#XnASU}pwc7j5 ziPUUfh??Kn_!aKmU*+KYGL!fm1AzLB6pV>$<*Z(3c|rPp8+`H`d7T!vR4PHE@fpmD@k^ z!t4tRJROPJN1Govv(e%4y2o2$ZSAQrqsd6T06Xls!;ACw%N^;NBJrZc+l)Uv{$YO* z-l#zRt`ELgEW*^(ds;#0?-G!mo!3tIBh!biE>q+w?%SOX^m~0PJVbP;6P_uesi4@u%iCx*jqpFM{(N9>)8@9_ zZg;HS=F^2UX#uF-vcao35p8y-t8|)KSXZf-krSRNCK!=+^!j-P8b$#RMjx=jYt@-` zV%p>FL5e9-J#>)D0wF1h4PI*n&5hdO^|6Jt&wvru@&*q)m~PZ3518)u7z-{Bn4$?fm7}Vf@fgVz71YH zQ{n&>PlF6bMh_2UaHYX(Efvwg;gbin50p!#pzwH|OdFE-xZpKiX3;{-G4Q5CzNw(_ zcvKHn%vfNB2Qw1q$%AAXBl{(eS#hp16g=j2s9IIPdcB?l-e`e*d4MA+oX!)qg3XN+ zts&s?eD-XE=UG-FO>H#ZZ{;8YH9x%Oi3;jCH$O;WA-1<)!otECZa%Zhvd=K^ z*kRG;(QI;SULTuh1z^gwU0#stcK6_i8I`xTMYK_w6`{43m6!3U`{`@%_{2oqhZS9hUZtm-!tj9~4xEeTaHIMML_Hm&* z-fF-L(cs^bTMbYW;8G^82KHq^{O(ad8|ww0GZ?diSsH?)%_BIK9k|tCW~SDZn?1I+ zUdzoM<{l5`*pOjgT^T0bv#&&Vo$+Q5gjaT}q=l&B@;%*dGe+?7b{qb=!r|q1o3{cC Y07NByaWr}RNB{r;07*qoM6N<$f>5FK9smFU literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..e7d8f7ebf8a89e0c6e056a4d49ec272e9f1268e0 GIT binary patch literal 2147 zcmV-p2%PtcP)E`moq6x~X5O2PJL&Eulby`J-S^q|e!t)M_ujlV&@2lJ zPu>tOqhj4B)||f|5y51sorV_Y#-FdgJ=SxsE7oao-c)z@EBCp3v@u`^#zX~-^oC6Z zM?QC2tS1F1R|`nM85A$q#rk}U!SR=W#5y7%6%;@fI5)YQluBjz{f|H4@xrgQw+h@j zxKDo^L!Wc`FxHMq<-oxC82+OiXU|tkkJVdjdTk@*y1F2f$#BT%&ro;w#zGZPrCHsR zYficRFI>C!HQc-Zz}xoT#4i}CP-?Y|IWMrURu*|5hTsMFRksuEL*+}%W}bt-d>@RB zz6058M*ga)CiMtNt`F$CY;*1lC|9n038ivbU+DiS9RGvSXC^q~$B#mPzE`eF(*n>) zTg6-5li^(YLm2{_sRCD$EmpTP_v?%(@JpY_2R|Lo~@P4*Qg^m zEwVYy-mGLe%Z{i7pI`Yxw(a|zU=ToK}V7M@kPL&V>P?6wtLY9AF}Fhl?az6%I1;q+NFWws1N-U#x`e zC>1~j<{vK$XW%~l%?QNWju)V#V=Z$$=x#71M`|{u3`O^;IUH{vh9ph9PQjVLIdWta zcJ9~~D`*i_!FcP|k1#p;S!^xYwQC2w{nox%fvXd!Hw(AC7{?5bdLJh#9I81<;m|TL zupN5)dW@P?QaH2>?izqxuFDJ#^En;)wx%{GsDy3d_{X|BUwWo+{9{@1S&iVJdcDJc zcte9+2OKF^p>1k^EiTptr&f3qIO-STs7BhtTRmeq^jj$W0CRKmiQ$CPP0?_$Qoi8z z?ZKNOfQ2jP0bITMRmDR^CA5~e7Z?9Fa^+{wo`X%BUe$Ule^;)n6UN34vUE(~a7Ak8 z+P6`hgM95EIFBC9uXvpE%iN>*I7d@;OyRIbI+R03iy%xpL4~L1Q z(L#743kNGdsR>W6plAk%E0@FF%S4rH`{EGNRpk*KhOuy@ilm98%~BSu-w1sZY3Fm0 zZDLVyfN7?VhL2G`hja6*4i46$;5azQ7BNex~qXgwHWbPUvBI0!m7Zv!VXVWvuj z$OOA4m5~}8)FxPr;jlF*5gZ1RG!&=daCB`eWhueof<#mXV8z3c zO^%ZYjz-+_M!G?8*n}USQ8b0aB<=0;Ic&8|4GvZv2dBr(;Doi9Dm5&1RHP_tI2_P= zv0yukf`hS1duttqz((RJz(I`{QlUh^@r6G|^ov>PD9#q81cy(Q+rf#9gM!`CruiIw z7fc#(A_ifA864DUvCxG!$u2gviK)QB2#F>yl0b4*f2VcSdc;p%I+8 z!3)M7Op|}<%5|m!hnDs0b9ygk1Sdv|*?2J2B&RyocBBG_w%4ueSOGW~t;I}@bh%Vw zlT-P8Z*}BHBjIE-S=h2A&%#++DjC7ih}C5I92&Xh54;ZEnC!s*{UZ&9L%#=*By zW#K$|QZ$5Ppv7$RId|^-%!c*D!+T(GaA(8d?A|>Hd-o2r&Fc2;8Dluy^$y#oWtg(~ z_Pg)3-7Lq)kHWiS2fe3io@KK-e0U5_oH(wHfo|OR#uyF;fAHKqFW4N!K46(nG&EcAr|Nc~asCk*gQ3Kz0vuo7%XV1j%OeULLBp7RX0 z4ni7mPM`ijye?V6(P}X^U1}jbsi~bg^HJz=qO{?7o+}X?jF1|LiIZLA_Q%KH^XyGp zaLDEyI&>^C9B#~I>_BoG;khJg(E0Npd*hOa-`oTJ`8@YL=!(LjIETQHx3V?J`h6^* zl}nQrT^BB1m=yaaVZDHrAxK(pCFP2x+)SttQYO1*Ns>_goQ=pB|6kW5k^8!+MimoW3sO_JRIp1uyFSIx} zPSkRGoITl_qRWS)#6gv5sd{A3)*^7GE6u79+}WnP$%6;;>@LOuWh002ovPDHLkV1lRK43+=@ literal 0 HcmV?d00001 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) } }