Skip to content
This repository was archived by the owner on Sep 15, 2025. It is now read-only.

Commit 90addf3

Browse files
Merge pull request #80 from wordpress-mobile/issue/72-locale-handling
Modified locale handling to inspect request parameters
2 parents e01e695 + fafdb59 commit 90addf3

File tree

6 files changed

+264
-84
lines changed

6 files changed

+264
-84
lines changed

Podfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ PODS:
2727
- OHHTTPStubs/Swift (6.1.0):
2828
- OHHTTPStubs/Default
2929
- UIDeviceIdentifier (1.1.4)
30-
- WordPressKit (1.8.0-beta.1):
30+
- WordPressKit (2.0.0-beta.1):
3131
- Alamofire (~> 4.7.3)
3232
- CocoaLumberjack (= 3.4.2)
3333
- NSObject-SafeExpectations (= 0.0.3)
@@ -70,7 +70,7 @@ SPEC CHECKSUMS:
7070
OCMock: 43565190abc78977ad44a61c0d20d7f0784d35ab
7171
OHHTTPStubs: 1e21c7d2c084b8153fc53d48400d8919d2d432d0
7272
UIDeviceIdentifier: 8f8a24b257a4d978c8d40ad1e7355b944ffbfa8c
73-
WordPressKit: f2edbc8f99f7c698306193cfe216fd6e5b74fa54
73+
WordPressKit: f54ffbba343061975b72e64e5d49615c28156147
7474
WordPressShared: a2fc2db66c210a05d317ae9678b5823dd6a4d708
7575
wpxmlrpc: 6ba55c773cfa27083ae4a2173e69b19f46da98e2
7676

WordPressKit.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = "WordPressKit"
3-
s.version = "1.8.0"
3+
s.version = "2.0.0-beta.1"
44
s.summary = "WordPressKit offers a clean and simple WordPress.com and WordPress.org API."
55

66
s.description = <<-DESC

WordPressKit.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
73A2F38A21E7F81E00388609 /* WordPressComServiceRemote+SiteVerticalsPrompt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A2F38921E7F81E00388609 /* WordPressComServiceRemote+SiteVerticalsPrompt.swift */; };
5151
73A2F38D21E7FC8200388609 /* WordPressComServiceRemoteTests+SiteVerticalsPrompt.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73A2F38C21E7FC8200388609 /* WordPressComServiceRemoteTests+SiteVerticalsPrompt.swift */; };
5252
73A2F38E21E7FD9B00388609 /* site-verticals-prompt.json in Resources */ = {isa = PBXBuildFile; fileRef = 73A2F38B21E7FC2A00388609 /* site-verticals-prompt.json */; };
53+
73B3DAD621FBB20D00B2CF18 /* WordPressComRestApiTests+Locale.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73B3DAD521FBB20D00B2CF18 /* WordPressComRestApiTests+Locale.swift */; };
5354
73D592FB21E550D300E4CF84 /* site-verticals-multiple.json in Resources */ = {isa = PBXBuildFile; fileRef = 73D592F821E550D200E4CF84 /* site-verticals-multiple.json */; };
5455
73D592FC21E550D300E4CF84 /* site-verticals-single.json in Resources */ = {isa = PBXBuildFile; fileRef = 73D592F921E550D300E4CF84 /* site-verticals-single.json */; };
5556
73D592FD21E550D300E4CF84 /* site-verticals-empty.json in Resources */ = {isa = PBXBuildFile; fileRef = 73D592FA21E550D300E4CF84 /* site-verticals-empty.json */; };
@@ -510,6 +511,7 @@
510511
73A2F38921E7F81E00388609 /* WordPressComServiceRemote+SiteVerticalsPrompt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WordPressComServiceRemote+SiteVerticalsPrompt.swift"; sourceTree = "<group>"; };
511512
73A2F38B21E7FC2A00388609 /* site-verticals-prompt.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "site-verticals-prompt.json"; sourceTree = "<group>"; };
512513
73A2F38C21E7FC8200388609 /* WordPressComServiceRemoteTests+SiteVerticalsPrompt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WordPressComServiceRemoteTests+SiteVerticalsPrompt.swift"; sourceTree = "<group>"; };
514+
73B3DAD521FBB20D00B2CF18 /* WordPressComRestApiTests+Locale.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WordPressComRestApiTests+Locale.swift"; sourceTree = "<group>"; };
513515
73D592F821E550D200E4CF84 /* site-verticals-multiple.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "site-verticals-multiple.json"; sourceTree = "<group>"; };
514516
73D592F921E550D300E4CF84 /* site-verticals-single.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "site-verticals-single.json"; sourceTree = "<group>"; };
515517
73D592FA21E550D300E4CF84 /* site-verticals-empty.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "site-verticals-empty.json"; sourceTree = "<group>"; };
@@ -1132,6 +1134,7 @@
11321134
740B23D51F17F7C100067A2A /* XMLRPCTestable.swift */,
11331135
FFE247A620C891D1002DF3A2 /* WordPressComOAuthTests.swift */,
11341136
74B335D91F06F3D60053A184 /* WordPressComRestApiTests.swift */,
1137+
73B3DAD521FBB20D00B2CF18 /* WordPressComRestApiTests+Locale.swift */,
11351138
74B335DB1F06F4180053A184 /* WordPressOrgXMLRPCApiTests.swift */,
11361139
93BD273E1EE732CC002BB00B /* Accounts */,
11371140
826016FE1F9FD59400533B6C /* Activity */,
@@ -2304,6 +2307,7 @@
23042307
74B335D81F06F1CA0053A184 /* MockWordPressComRestApi.swift in Sources */,
23052308
74B5F0DE1EF82A9600B411E7 /* BlogServiceRemoteRESTTests.m in Sources */,
23062309
74E2294B1F1E73340085F7F2 /* SharingServiceRemoteTests.m in Sources */,
2310+
73B3DAD621FBB20D00B2CF18 /* WordPressComRestApiTests+Locale.swift in Sources */,
23072311
9F3E0BAC20873785009CB5BA /* ServiceRequestTest.swift in Sources */,
23082312
93AC8EDE1ED32FD000900F5A /* StatsItemTests.m in Sources */,
23092313
740B23D31F17F6BB00067A2A /* PostServiceRemoteXMLRPCTests.swift in Sources */,

WordPressKit/WordPressComRestApi.swift

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ open class WordPressComRestApi: NSObject {
4343

4444
fileprivate let backgroundUploads: Bool
4545

46-
fileprivate static let localeKey = "locale"
46+
static let localeKey = "locale"
4747

4848
fileprivate let oAuthToken: String?
4949
fileprivate let userAgent: String?
@@ -145,7 +145,7 @@ open class WordPressComRestApi: NSObject {
145145
success: @escaping SuccessResponseBlock,
146146
failure: @escaping FailureReponseBlock) -> Progress? {
147147

148-
guard let URLString = buildRequestURLFor(path: urlString) else {
148+
guard let URLString = buildRequestURLFor(path: urlString, parameters: parameters) else {
149149
let error = NSError(domain: String(describing: WordPressComRestApiError.self),
150150
code: WordPressComRestApiError.requestSerializationFailed.rawValue,
151151
userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("Failed to serialize request to the REST API.", comment: "Error message to show when wrong URL format is used to access the REST API")])
@@ -241,7 +241,7 @@ open class WordPressComRestApi: NSObject {
241241
success: @escaping SuccessResponseBlock,
242242
failure: @escaping FailureReponseBlock) -> Progress? {
243243

244-
guard let URLString = buildRequestURLFor(path: URLString) else {
244+
guard let URLString = buildRequestURLFor(path: URLString, parameters: parameters) else {
245245
let error = NSError(domain: String(describing: WordPressComRestApiError.self),
246246
code: WordPressComRestApiError.requestSerializationFailed.rawValue,
247247
userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("Failed to serialize request to the REST API.", comment: "Error message to show when wrong URL format is used to access the REST API")])
@@ -299,18 +299,52 @@ open class WordPressComRestApi: NSObject {
299299
return "\(String(describing: oAuthToken)),\(String(describing: userAgent))".hashValue
300300
}
301301

302-
fileprivate func buildRequestURLFor(path: String) -> String? {
303-
let pathWithLocale = appendLocaleIfNeeded(path)
302+
/// This method assembles a valid request URL for the specified path & parameters.
303+
/// The framework relies on a field (`appendsPreferredLanguageLocale`) to influence whether or not locale should be
304+
/// added to the path of requests. This approach did not consider request parameters.
305+
///
306+
/// This method now considers both the path and specified request parameters when performing the substitution.
307+
/// It only accounts for the locale parameter. AlamoFire encodes other parameters via `SessionManager.request(_:method:parameters:encoding:headers:)`
308+
///
309+
/// - Parameters:
310+
/// - path: the path for the request, which might include `locale`
311+
/// - parameters: the request parameters, which could conceivably include `locale`
312+
/// - localeKey: the locale key to search for (`locale` in v1 endpoints, `_locale` for v2)
313+
/// - Returns: a request URL if successful, `nil` otherwise.
314+
///
315+
func buildRequestURLFor(path: String, parameters: [String: AnyObject]? = [:], localeKey: String = WordPressComRestApi.localeKey) -> String? {
316+
304317
let baseURL = URL(string: WordPressComRestApi.apiBaseURLString)
305-
let requestURLString = URL(string: pathWithLocale, relativeTo: baseURL)?.absoluteString
306-
return requestURLString
318+
319+
guard let requestURLString = URL(string: path, relativeTo: baseURL)?.absoluteString,
320+
let urlComponents = URLComponents(string: requestURLString) else {
321+
322+
return nil
323+
}
324+
325+
let urlComponentsWithLocale = applyLocaleIfNeeded(urlComponents: urlComponents, parameters: parameters, localeKey: localeKey)
326+
327+
return urlComponentsWithLocale?.url?.absoluteString
307328
}
308329

309-
fileprivate func appendLocaleIfNeeded(_ path: String) -> String {
330+
private func applyLocaleIfNeeded(urlComponents: URLComponents, parameters: [String: AnyObject]? = [:], localeKey: String) -> URLComponents? {
310331
guard appendsPreferredLanguageLocale else {
311-
return path
332+
return urlComponents
312333
}
313-
return WordPressComRestApi.pathByAppendingPreferredLanguageLocale(path)
334+
335+
var componentsWithLocale = urlComponents
336+
var existingQueryItems = componentsWithLocale.queryItems ?? []
337+
338+
let existingLocaleQueryItems = existingQueryItems.filter { $0.name == localeKey }
339+
if let parameters = parameters, parameters[localeKey] == nil, existingLocaleQueryItems.isEmpty {
340+
let preferredLanguageIdentifier = WordPressComLanguageDatabase().deviceLanguage.slug
341+
let localeQueryItem = URLQueryItem(name: localeKey, value: preferredLanguageIdentifier)
342+
343+
existingQueryItems.append(localeQueryItem)
344+
}
345+
componentsWithLocale.queryItems = existingQueryItems
346+
347+
return componentsWithLocale
314348
}
315349

316350
@objc public func temporaryFileURL(withExtension fileExtension: String) -> URL {
@@ -412,7 +446,6 @@ extension WordPressComRestApi {
412446
let errorWithLocalizedMessage = NSError(domain: nsError.domain, code: nsError.code, userInfo:userInfo)
413447
return errorWithLocalizedMessage
414448
}
415-
416449
}
417450

418451
extension WordPressComRestApi {
@@ -421,25 +454,6 @@ extension WordPressComRestApi {
421454
@objc class public func anonymousApi(userAgent: String) -> WordPressComRestApi {
422455
return WordPressComRestApi(oAuthToken: nil, userAgent: userAgent)
423456
}
424-
425-
/// Append the user's preferred device locale as a query param to the URL path.
426-
/// If the locale already exists the original path is returned.
427-
///
428-
/// - Parameters:
429-
/// - path: A URL string. Can be an absolute or relative URL string.
430-
///
431-
/// - Returns: The path with the locale appended, or the original path if it already had a locale param.
432-
///
433-
@objc class public func pathByAppendingPreferredLanguageLocale(_ path: String) -> String {
434-
let localeKey = WordPressComRestApi.localeKey
435-
if path.isEmpty || path.contains("\(localeKey)=") {
436-
return path
437-
}
438-
let preferredLanguageIdentifier = WordPressComLanguageDatabase().deviceLanguage.slug
439-
let separator = path.contains("?") ? "&" : "?"
440-
return "\(path)\(separator)\(localeKey)=\(preferredLanguageIdentifier)"
441-
}
442-
443457
}
444458

445459
@objc extension Progress {
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import Foundation
2+
import XCTest
3+
4+
import WordPressShared
5+
@testable import WordPressKit
6+
7+
extension WordPressComRestApiTests {
8+
9+
func testThatAppendingLocaleWorks() {
10+
// Given
11+
let path = "/path/path"
12+
let preferredLanguageIdentifier = WordPressComLanguageDatabase().deviceLanguage.slug
13+
14+
// When
15+
let localeAppendedPath = WordPressComRestApi().buildRequestURLFor(path: path)
16+
17+
// Then
18+
XCTAssertNotNil(localeAppendedPath)
19+
let actualURL = URL(string: localeAppendedPath!, relativeTo: URL(string: WordPressComRestApi.apiBaseURLString))
20+
XCTAssertNotNil(actualURL)
21+
22+
let actualURLComponents = URLComponents(url: actualURL!, resolvingAgainstBaseURL: false)
23+
XCTAssertNotNil(actualURLComponents)
24+
25+
let expectedPath = path
26+
let actualPath = actualURLComponents!.path
27+
XCTAssertEqual(expectedPath, actualPath)
28+
29+
let actualQueryItems = actualURLComponents!.queryItems
30+
XCTAssertNotNil(actualQueryItems)
31+
32+
let expectedQueryItemCount = 1
33+
let actualQueryItemCount = actualQueryItems!.count
34+
XCTAssertEqual(expectedQueryItemCount, actualQueryItemCount)
35+
36+
let actualQueryItem = actualQueryItems!.first
37+
XCTAssertNotNil(actualQueryItem!)
38+
39+
let actualQueryItemKey = actualQueryItem!.name
40+
let expectedQueryItemKey = WordPressComRestApi.localeKey
41+
XCTAssertEqual(expectedQueryItemKey, actualQueryItemKey)
42+
43+
let actualQueryItemValue = actualQueryItem!.value
44+
XCTAssertNotNil(actualQueryItemValue)
45+
46+
let expectedQueryItemValue = preferredLanguageIdentifier
47+
XCTAssertEqual(expectedQueryItemValue, actualQueryItemValue!)
48+
}
49+
50+
func testThatAppendingLocaleWorksWithExistingParams() {
51+
// Given
52+
let path = "/path/path"
53+
let params: [String: AnyObject] = [
54+
"someKey": "value" as AnyObject
55+
]
56+
57+
// When
58+
let localeAppendedPath = WordPressComRestApi().buildRequestURLFor(path: path, parameters: params)
59+
60+
// Then
61+
XCTAssertNotNil(localeAppendedPath)
62+
let actualURL = URL(string: localeAppendedPath!, relativeTo: URL(string: WordPressComRestApi.apiBaseURLString))
63+
XCTAssertNotNil(actualURL)
64+
65+
let actualURLComponents = URLComponents(url: actualURL!, resolvingAgainstBaseURL: false)
66+
XCTAssertNotNil(actualURLComponents)
67+
68+
let expectedPath = "/path/path"
69+
let actualPath = actualURLComponents!.path
70+
XCTAssertEqual(expectedPath, actualPath)
71+
72+
let actualQueryItems = actualURLComponents!.queryItems
73+
XCTAssertNotNil(actualQueryItems)
74+
75+
let expectedQueryItemCount = 1
76+
let actualQueryItemCount = actualQueryItems!.count
77+
XCTAssertEqual(expectedQueryItemCount, actualQueryItemCount)
78+
79+
let actualQueryString = actualURLComponents?.query
80+
XCTAssertNotNil(actualQueryString)
81+
82+
let queryStringIncludesLocale = actualQueryString!.contains(WordPressComRestApi.localeKey)
83+
XCTAssertTrue(queryStringIncludesLocale)
84+
}
85+
86+
func testThatLocaleIsNotAppendedIfAlreadyIncludedInPath() {
87+
// Given
88+
let preferredLanguageIdentifier = "foo"
89+
let path = "/path/path?locale=\(preferredLanguageIdentifier)"
90+
91+
// When
92+
let localeAppendedPath = WordPressComRestApi().buildRequestURLFor(path: path)
93+
94+
// Then
95+
XCTAssertNotNil(localeAppendedPath)
96+
let actualURL = URL(string: localeAppendedPath!, relativeTo: URL(string: WordPressComRestApi.apiBaseURLString))
97+
XCTAssertNotNil(actualURL)
98+
99+
let actualURLComponents = URLComponents(url: actualURL!, resolvingAgainstBaseURL: false)
100+
XCTAssertNotNil(actualURLComponents)
101+
102+
let expectedPath = "/path/path"
103+
let actualPath = actualURLComponents!.path
104+
XCTAssertEqual(expectedPath, actualPath)
105+
106+
let actualQueryItems = actualURLComponents!.queryItems
107+
XCTAssertNotNil(actualQueryItems)
108+
109+
let expectedQueryItemCount = 1
110+
let actualQueryItemCount = actualQueryItems!.count
111+
XCTAssertEqual(expectedQueryItemCount, actualQueryItemCount)
112+
113+
let actualQueryItem = actualQueryItems!.first
114+
XCTAssertNotNil(actualQueryItem!)
115+
116+
let actualQueryItemKey = actualQueryItem!.name
117+
let expectedQueryItemKey = WordPressComRestApi.localeKey
118+
XCTAssertEqual(expectedQueryItemKey, actualQueryItemKey)
119+
120+
let actualQueryItemValue = actualQueryItem!.value
121+
XCTAssertNotNil(actualQueryItemValue)
122+
123+
let expectedQueryItemValue = preferredLanguageIdentifier
124+
XCTAssertEqual(expectedQueryItemValue, actualQueryItemValue!)
125+
}
126+
127+
func testThatAppendingLocaleIgnoresIfAlreadyIncludedInRequestParameters() {
128+
// Given
129+
let inputPath = "/path/path"
130+
let expectedLocaleValue = "foo"
131+
let params: [String : AnyObject] = [
132+
WordPressComRestApi.localeKey: expectedLocaleValue as AnyObject
133+
]
134+
135+
// When
136+
let requestURLString = WordPressComRestApi().buildRequestURLFor(path: inputPath, parameters: params)
137+
138+
// Then
139+
let preferredLanguageIdentifier = WordPressComLanguageDatabase().deviceLanguage.slug
140+
XCTAssertFalse(requestURLString!.contains(preferredLanguageIdentifier))
141+
}
142+
143+
func testThatLocaleIsNotAppendedWhenDisabled() {
144+
// Given
145+
let path = "/path/path"
146+
147+
// When
148+
let api = WordPressComRestApi()
149+
api.appendsPreferredLanguageLocale = false
150+
let localeAppendedPath = api.buildRequestURLFor(path: path)
151+
152+
// Then
153+
XCTAssertNotNil(localeAppendedPath)
154+
let actualURL = URL(string: localeAppendedPath!, relativeTo: URL(string: WordPressComRestApi.apiBaseURLString))
155+
XCTAssertNotNil(actualURL)
156+
157+
let actualURLComponents = URLComponents(url: actualURL!, resolvingAgainstBaseURL: false)
158+
XCTAssertNotNil(actualURLComponents)
159+
160+
let expectedPath = path
161+
let actualPath = actualURLComponents!.path
162+
XCTAssertEqual(expectedPath, actualPath)
163+
164+
let actualQueryItems = actualURLComponents!.queryItems
165+
XCTAssertNil(actualQueryItems)
166+
}
167+
}

0 commit comments

Comments
 (0)