Skip to content

Commit 95ead2e

Browse files
authored
Merge pull request #30 from nodes-ios/develop
Coding Keys to fix parsing of Language object snake case
2 parents d3e010f + de9b69f commit 95ead2e

File tree

12 files changed

+312
-58
lines changed

12 files changed

+312
-58
lines changed

TranslationsGenerator/Classes/Downloader/Downloader.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ struct Downloader {
5555

5656
self.semaphore.signal()
5757
}
58-
session.startDataTask(with: request, completionHandler: completion)
58+
session.startDataTask(with: request, convertFromSnakeCase: settings.convertFromSnakeCase, completionHandler: completion)
5959

6060
_ = semaphore.wait(timeout: DispatchTime.distantFuture)
6161

TranslationsGenerator/Classes/Downloader/DownloaderSettings.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ struct DownloaderSettings {
1515
var flatTranslations: Bool
1616
var authorization: String?
1717
var extraHeaders: [String]?
18+
var convertFromSnakeCase: Bool
1819
}
1920

2021
extension DownloaderSettings {
@@ -26,6 +27,7 @@ extension DownloaderSettings {
2627
fileprivate static let plistAppIDKey = "APPLICATION_ID"
2728
fileprivate static let plistAppKeyKey = "REST_API_KEY"
2829
fileprivate static let plistFlatKey = "FLAT"
30+
fileprivate static let plistConvertSnakeCaseKey = "CONVERTSNAKECASE"
2931
fileprivate static let authorizationKey = "AUTHORIZATION"
3032
fileprivate static let extraHeadersKey = "EXTRAHEADERS"
3133

@@ -59,6 +61,7 @@ extension DownloaderSettings {
5961
}
6062

6163
let flat = dictionary[plistFlatKey] as? Bool
64+
let snakeCase = dictionary[plistConvertSnakeCaseKey] as? Bool
6265

6366
if let headers = dictionary[extraHeadersKey] as? [String] {
6467
extraHeaders = headers
@@ -70,15 +73,17 @@ extension DownloaderSettings {
7073
appKey: appKey,
7174
flatTranslations: flat ?? false,
7275
authorization: auth,
73-
extraHeaders: extraHeaders)
76+
extraHeaders: extraHeaders,
77+
convertFromSnakeCase: snakeCase ?? false)
7478
}
7579

76-
init(appID: String?, appKey: String?, flatTranslations: Bool, authorization: String?, extraHeaders: [String]?) {
80+
init(appID: String?, appKey: String?, flatTranslations: Bool, authorization: String?, convertFromSnakeCase: Bool, extraHeaders: [String]?) {
7781
self.localizationsURL = DownloaderSettings.localizationsURL
7882
self.appID = appID
7983
self.appKey = appKey
8084
self.flatTranslations = flatTranslations
8185
self.authorization = authorization
86+
self.convertFromSnakeCase = convertFromSnakeCase
8287
self.extraHeaders = extraHeaders
8388
}
8489
}

TranslationsGenerator/Classes/Generator/GeneratorSettings.swift

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public struct GeneratorSettings {
1313
public var keys: (appID: String, appKey: String)?
1414
public var outputPath: String?
1515
public var flatTranslations: Bool
16+
public var convertFromSnakeCase: Bool
1617
public var availableFromObjC: Bool
1718
public var standalone: Bool
1819
public var authorization: String?
@@ -24,6 +25,7 @@ public struct GeneratorSettings {
2425
keys: (appID: String, appKey: String)?,
2526
outputPath: String?,
2627
flatTranslations: Bool,
28+
convertFromSnakeCase: Bool,
2729
availableFromObjC: Bool,
2830
standalone: Bool,
2931
authorization: String?,
@@ -34,6 +36,7 @@ public struct GeneratorSettings {
3436
self.keys = keys
3537
self.outputPath = outputPath
3638
self.flatTranslations = flatTranslations
39+
self.convertFromSnakeCase = convertFromSnakeCase
3740
self.availableFromObjC = availableFromObjC
3841
self.standalone = standalone
3942
self.authorization = authorization
@@ -68,6 +71,7 @@ extension GeneratorSettings {
6871
var keys: (appID: String, appKey: String)?
6972
var authorization: String?
7073
var flatTranslations = false
74+
var convertFromSnakeCase = false
7175
var availableFromObjC = false
7276
var standalone = false
7377
var extraHeaders: [String]?
@@ -99,6 +103,10 @@ extension GeneratorSettings {
99103
if let flat = parsedArguments["-flat"] , flat.count == 1 && flat[0] == "1" {
100104
flatTranslations = true
101105
}
106+
107+
if let convertSnakeCase = parsedArguments["-convertFromSnakeCase"] , convertSnakeCase.count == 1 && convertSnakeCase[0] == "1" {
108+
convertFromSnakeCase = true
109+
}
102110

103111
if let _ = parsedArguments["-use-objc"] {
104112
availableFromObjC = true
@@ -127,16 +135,20 @@ extension GeneratorSettings {
127135
}
128136

129137
return GeneratorSettings(plistPath: plistPath, keys: keys, outputPath: outputPath,
130-
flatTranslations: flatTranslations, availableFromObjC: availableFromObjC,
138+
flatTranslations: flatTranslations,
139+
convertFromSnakeCase: convertFromSnakeCase,
140+
availableFromObjC: availableFromObjC,
131141
standalone: standalone, authorization: authorization, extraHeaders: extraHeaders,
132142
jsonPath: jsonPath, jsonLocaleIdentifier: jsonLocale)
133143
}
134144

135145
func downloaderSettings() throws -> DownloaderSettings {
136146
if let keys = keys {
137147
return DownloaderSettings(appID: keys.appID, appKey: keys.appKey,
138-
flatTranslations: self.flatTranslations,
139-
authorization: authorization, extraHeaders: extraHeaders)
148+
flatTranslations: flatTranslations,
149+
authorization: authorization,
150+
convertFromSnakeCase: convertFromSnakeCase,
151+
extraHeaders: extraHeaders)
140152
} else if let plistPath = plistPath {
141153
var settings = try DownloaderSettings.settingsFromConfigurationFile(plistPath: plistPath)
142154
settings.authorization = authorization

TranslationsGenerator/Classes/Models/Language.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,11 @@ public struct Language: Codable {
1414
public let direction: String
1515
public let isDefault: Bool
1616
public let isBestFit: Bool
17+
18+
19+
enum CodingKeys: String, CodingKey {
20+
case name, locale, direction
21+
case isBestFit = "is_best_fit"
22+
case isDefault = "is_default"
23+
}
1724
}

TranslationsGenerator/Classes/Other/URLSession+Request.swift

Lines changed: 23 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -43,79 +43,55 @@ extension URLSession {
4343
request.allHTTPHeaderFields = headers
4444
return request
4545
}
46-
47-
func dataTask<T>(with request: URLRequest,
48-
completionHandler: @escaping (Result<T>) -> Void) -> URLSessionDataTask {
49-
let handler = dataHandler(completionHandler)
50-
let task = dataTask(with: request, completionHandler: handler)
51-
return task
52-
}
53-
54-
@discardableResult
55-
func startDataTask<T>(with request: URLRequest,
56-
completionHandler: @escaping (Result<T>) -> Void) -> URLSessionDataTask {
57-
let task = dataTask(with: request, completionHandler: completionHandler)
58-
task.resume()
59-
return task
60-
}
61-
46+
6247
func dataTask<T: Codable>(with request: URLRequest,
48+
convertFromSnakeCase: Bool,
6349
completionHandler: @escaping (Result<T>) -> Void) -> URLSessionDataTask {
64-
let handler = dataHandler(completionHandler)
50+
let handler = dataHandler(convertFromSnakeCase: convertFromSnakeCase, handler: completionHandler)
6551
let task = dataTask(with: request, completionHandler: handler)
6652
return task
6753
}
6854

6955
@discardableResult
7056
func startDataTask<T: Codable>(with request: URLRequest,
57+
convertFromSnakeCase: Bool,
7158
completionHandler: @escaping (Result<T>) -> Void) -> URLSessionDataTask {
72-
let task = dataTask(with: request, completionHandler: completionHandler)
59+
let task = dataTask(with: request, convertFromSnakeCase: convertFromSnakeCase, completionHandler: completionHandler)
7360
task.resume()
7461
return task
7562
}
7663

7764
func dataTask<T: WrapperModelType>(with request: URLRequest,
7865
wrapperType: T.Type,
66+
convertFromSnakeCase: Bool,
7967
completionHandler: @escaping (Result<T.ModelType>) -> Void) -> URLSessionDataTask {
80-
let handler = dataHandler(completionHandler, wrapperType: wrapperType)
68+
let handler = dataHandler(convertFromSnakeCase: convertFromSnakeCase, handler: completionHandler, wrapperType: wrapperType)
8169
let task = dataTask(with: request, completionHandler: handler)
8270
return task
8371
}
8472

8573
@discardableResult
8674
func startDataTask<T: WrapperModelType>(with request: URLRequest,
8775
wrapperType: T.Type,
76+
convertFromSnakeCase: Bool,
8877
completionHandler: @escaping (Result<T.ModelType>) -> Void) -> URLSessionDataTask {
89-
let task = dataTask(with: request, wrapperType: wrapperType, completionHandler: completionHandler)
78+
let task = dataTask(with: request,
79+
wrapperType: wrapperType,
80+
convertFromSnakeCase: convertFromSnakeCase,
81+
completionHandler: completionHandler)
9082
task.resume()
9183
return task
9284
}
93-
94-
private func dataHandler<T>(_ handler: @escaping (Result<T>) -> Void) -> ((Data?, URLResponse?, Error?) -> Void) {
95-
return { data, response, error in
96-
do {
97-
let data = try self.validate(data, response, error)
98-
let decoded = try JSONSerialization.jsonObject(with: data, options: [])
99-
100-
guard let model = decoded as? T else {
101-
// FIXME: Fix this
102-
throw NSError(domain: "", code: 10, userInfo: nil)
103-
}
104-
handler(Result.success(model))
105-
106-
} catch {
107-
handler(.failure(error))
108-
}
109-
}
110-
}
111-
112-
private func dataHandler<T: Codable>(_ handler: @escaping (Result<T>) -> Void) -> ((Data?, URLResponse?, Error?) -> Void) {
85+
86+
private func dataHandler<T: Codable>( convertFromSnakeCase: Bool, handler: @escaping (Result<T>) -> Void) -> ((Data?, URLResponse?, Error?) -> Void) {
11387
return { data, response, error in
11488
do {
11589
let data = try self.validate(data, response, error)
11690
let decoder = JSONDecoder()
117-
decoder.keyDecodingStrategy = .convertFromSnakeCase
118-
91+
if convertFromSnakeCase {
92+
decoder.keyDecodingStrategy = .convertFromSnakeCase
93+
}
94+
11995
let decoded = try decoder.decode(T.self, from: data)
12096
handler(Result.success(decoded))
12197
} catch {
@@ -124,13 +100,16 @@ extension URLSession {
124100
}
125101
}
126102

127-
private func dataHandler<T: WrapperModelType>(_ handler: @escaping (Result<T.ModelType>) -> Void,
103+
private func dataHandler<T: WrapperModelType>( convertFromSnakeCase: Bool, handler: @escaping (Result<T.ModelType>) -> Void,
128104
wrapperType: T.Type) -> ((Data?, URLResponse?, Error?) -> Void) {
129105
return { data, response, error in
130106
do {
131107
let data = try self.validate(data, response, error)
132108
let decoder = JSONDecoder()
133-
decoder.keyDecodingStrategy = .convertFromSnakeCase
109+
if convertFromSnakeCase {
110+
decoder.keyDecodingStrategy = .convertFromSnakeCase
111+
}
112+
134113

135114
let parentData = try decoder.decode(wrapperType, from: data)
136115
handler(Result.success(parentData.model))

TranslationsGenerator/Supporting Files/Generator-Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
<key>CFBundlePackageType</key>
1616
<string>FMWK</string>
1717
<key>CFBundleShortVersionString</key>
18-
<string>3.0.1-beta.1</string>
18+
<string>3.0.1</string>
1919
<key>CFBundleSignature</key>
2020
<string>????</string>
2121
<key>CFBundleVersion</key>

TranslationsGeneratorTests/SKTranslations.swift

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,130 @@
2424
// ----------------------------------------------------------------------
2525

2626
import Foundation
27+
import NStackSDK
28+
import TranslationManager
29+
30+
public var skt: SKTranslations {
31+
return SKTranslations()
32+
}
33+
34+
public final class SKTranslations: LocalizableModel {
35+
public var defaultSection = DefaultSection()
36+
public var oneMoreSection = OneMoreSection()
37+
public var otherSection = OtherSection()
38+
39+
enum CodingKeys: String, CodingKey {
40+
case defaultSection = "default"
41+
case oneMoreSection
42+
case otherSection
43+
}
44+
45+
public override init() { super.init() }
46+
47+
public required init(from decoder: Decoder) throws {
48+
super.init()
49+
let container = try decoder.container(keyedBy: CodingKeys.self)
50+
defaultSection = try container.decodeIfPresent(DefaultSection.self, forKey: .defaultSection) ?? defaultSection
51+
oneMoreSection = try container.decodeIfPresent(OneMoreSection.self, forKey: .oneMoreSection) ?? oneMoreSection
52+
otherSection = try container.decodeIfPresent(OtherSection.self, forKey: .otherSection) ?? otherSection
53+
}
54+
55+
public override subscript(key: String) -> LocalizableSection? {
56+
switch key {
57+
case CodingKeys.defaultSection.stringValue: return defaultSection
58+
case CodingKeys.oneMoreSection.stringValue: return oneMoreSection
59+
case CodingKeys.otherSection.stringValue: return otherSection
60+
default: return nil
61+
}
62+
}
63+
64+
public final class DefaultSection: LocalizableSection {
65+
public var keyys = ""
66+
public var successKey = ""
67+
public var emptyKey = ""
68+
69+
enum CodingKeys: String, CodingKey {
70+
case keyys
71+
case successKey
72+
case emptyKey
73+
}
74+
75+
public override init() {
76+
super.init()
77+
keyys = "\(classNameLowerCased()).keyys"
78+
successKey = "\(classNameLowerCased()).successKey"
79+
emptyKey = "\(classNameLowerCased()).emptyKey"
80+
}
81+
82+
public required init(from decoder: Decoder) throws {
83+
super.init()
84+
let container = try decoder.container(keyedBy: CodingKeys.self)
85+
keyys = try container.decodeIfPresent(String.self, forKey: .keyys) ?? "__keyys"
86+
successKey = try container.decodeIfPresent(String.self, forKey: .successKey) ?? "__successKey"
87+
emptyKey = try container.decodeIfPresent(String.self, forKey: .emptyKey) ?? "__emptyKey"
88+
}
89+
90+
public override subscript(key: String) -> String? {
91+
return ""
92+
}
93+
}
94+
95+
public final class OneMoreSection: LocalizableSection {
96+
public var test1 = ""
97+
public var soManyKeys = ""
98+
public var testURL = ""
99+
public var test2 = ""
100+
101+
enum CodingKeys: String, CodingKey {
102+
case test1
103+
case soManyKeys
104+
case testURL
105+
case test2
106+
}
107+
108+
public override init() {
109+
super.init()
110+
test1 = "\(classNameLowerCased()).test1"
111+
soManyKeys = "\(classNameLowerCased()).soManyKeys"
112+
testURL = "\(classNameLowerCased()).testURL"
113+
test2 = "\(classNameLowerCased()).test2"
114+
}
115+
116+
public required init(from decoder: Decoder) throws {
117+
super.init()
118+
let container = try decoder.container(keyedBy: CodingKeys.self)
119+
test1 = try container.decodeIfPresent(String.self, forKey: .test1) ?? "__test1"
120+
soManyKeys = try container.decodeIfPresent(String.self, forKey: .soManyKeys) ?? "__soManyKeys"
121+
testURL = try container.decodeIfPresent(String.self, forKey: .testURL) ?? "__testURL"
122+
test2 = try container.decodeIfPresent(String.self, forKey: .test2) ?? "__test2"
123+
}
124+
125+
public override subscript(key: String) -> String? {
126+
return ""
127+
}
128+
}
129+
130+
public final class OtherSection: LocalizableSection {
131+
public var otherString = ""
132+
133+
enum CodingKeys: String, CodingKey {
134+
case otherString
135+
}
136+
137+
public override init() {
138+
super.init()
139+
otherString = "\(classNameLowerCased()).otherString"
140+
}
141+
142+
public required init(from decoder: Decoder) throws {
143+
super.init()
144+
let container = try decoder.container(keyedBy: CodingKeys.self)
145+
otherString = try container.decodeIfPresent(String.self, forKey: .otherString) ?? "__otherString"
146+
}
147+
148+
public override subscript(key: String) -> String? {
149+
return ""
150+
}
151+
}
152+
}
153+

0 commit comments

Comments
 (0)