Skip to content

Commit 1d21d70

Browse files
author
Mccc
committed
优化代码
1 parent 1bab7c6 commit 1d21d70

8 files changed

Lines changed: 736 additions & 706 deletions

File tree

Example/Pods/Pods.xcodeproj/project.pbxproj

Lines changed: 489 additions & 485 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

SmartCodable/Classes/JSONDecoder/Decoder/Impl/JSONDecoderImpl+KeyedContainer.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -296,8 +296,7 @@ extension JSONDecoderImpl.KeyedContainer {
296296

297297
// 检查 SmartPublished 包装器类型
298298
if let publishedType = T.self as? any SmartPublishedProtocol.Type,
299-
let wrappedValue = decoded as? Any,
300-
let publishedValue = publishedType.createInstance(with: wrappedValue) as? T {
299+
let publishedValue = publishedType.createInstance(with: decoded) as? T {
301300
return publishedValue
302301
}
303302
}

SmartCodable/Classes/JSONDecoder/Patcher/Patcher+Transformer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ extension Bool: TypeTransformable {
3131
case .string(let string):
3232
if ["1","YES","Yes","yes","TRUE","True","true"].contains(string) { return true }
3333
if ["0","NO","No","no","FALSE","False","false"].contains(string) { return false }
34-
case .number(let number):
34+
case .number(_):
3535
if let int = try? impl.unwrapFixedWidthInteger(from: value, as: Int.self) {
3636
if int == 1 {
3737
return true

SmartCodable/Classes/JSONEncoder/Impl/_SpecialTreatmentEncoder.swift

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ extension _SpecialTreatmentEncoder {
8484
return value
8585
}
8686

87-
8887
switch self.options.dateEncodingStrategy {
8988
case .deferredToDate:
9089
let encoder = self.getEncoder(for: additionalKey)
@@ -112,6 +111,10 @@ extension _SpecialTreatmentEncoder {
112111
try closure(date, encoder)
113112
// The closure didn't encode anything. Return the default keyed container.
114113
return encoder.value ?? .object([:])
114+
@unknown default:
115+
let encoder = self.getEncoder(for: additionalKey)
116+
try date.encode(to: encoder)
117+
return encoder.value ?? .null
115118
}
116119
}
117120

@@ -131,6 +134,10 @@ extension _SpecialTreatmentEncoder {
131134
try closure(data, encoder)
132135
// The closure didn't encode anything. Return the default keyed container.
133136
return encoder.value ?? .object([:])
137+
@unknown default:
138+
let encoder = self.getEncoder(for: additionalKey)
139+
try data.encode(to: encoder)
140+
return encoder.value ?? .null
134141
}
135142
}
136143

@@ -167,6 +174,8 @@ extension _SpecialTreatmentEncoder {
167174
extension _SpecialTreatmentEncoder {
168175
internal func _converted(_ key: CodingKey) -> CodingKey {
169176

177+
var newKey = key
178+
170179
var useMappedKeys = false
171180
if let key = CodingUserInfoKey.useMappedKeys {
172181
useMappedKeys = impl.userInfo[key] as? Bool ?? false
@@ -175,25 +184,29 @@ extension _SpecialTreatmentEncoder {
175184
if let objectType = impl.cache.cacheType {
176185
if let mappings = objectType.mappingForKey() {
177186
for mapping in mappings {
178-
if mapping.to.stringValue == key.stringValue {
187+
if mapping.to.stringValue == newKey.stringValue {
179188
if useMappedKeys, let first = mapping.from.first {
180-
return _JSONKey.init(stringValue: first, intValue: nil)
189+
newKey = _JSONKey.init(stringValue: first, intValue: nil)
181190
} else {
182-
return mapping.to
191+
newKey = mapping.to
183192
}
184193
}
185194
}
186195
}
187196
}
188-
197+
189198
switch self.options.keyEncodingStrategy {
199+
case .toSnakeCase:
200+
let newKeyString = SmartJSONEncoder.SmartKeyEncodingStrategy._convertToSnakeCase(newKey.stringValue)
201+
return _JSONKey(stringValue: newKeyString, intValue: newKey.intValue)
202+
case .firstLetterLower:
203+
let newKeyString = SmartJSONEncoder.SmartKeyEncodingStrategy._convertFirstLetterToLowercase(newKey.stringValue)
204+
return _JSONKey(stringValue: newKeyString, intValue: newKey.intValue)
205+
case .firstLetterUpper:
206+
let newKeyString = SmartJSONEncoder.SmartKeyEncodingStrategy._convertFirstLetterToUppercase(newKey.stringValue)
207+
return _JSONKey(stringValue: newKeyString, intValue: newKey.intValue)
190208
case .useDefaultKeys:
191-
return key
192-
case .convertToSnakeCase:
193-
let newKeyString = SmartJSONEncoder.KeyEncodingStrategy._convertToSnakeCase(key.stringValue)
194-
return _JSONKey(stringValue: newKeyString, intValue: key.intValue)
195-
case .custom(let converter):
196-
return converter(codingPath + [key])
209+
return newKey
197210
}
198211
}
199212
}

SmartCodable/Classes/JSONEncoder/SmartJSONEncoder.swift

Lines changed: 7 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -7,180 +7,18 @@ import Foundation
77
//===----------------------------------------------------------------------===//
88

99
/// `JSONEncoder` facilitates the encoding of `Encodable` values into JSON.
10-
open class SmartJSONEncoder {
11-
// MARK: Options
10+
open class SmartJSONEncoder: JSONEncoder {
1211

13-
/// The formatting of the output JSON data.
14-
public struct OutputFormatting: OptionSet {
15-
/// The format's default value.
16-
public let rawValue: UInt
12+
open var smartKeyEncodingStrategy: SmartKeyEncodingStrategy = .useDefaultKeys
1713

18-
/// Creates an OutputFormatting value with the given raw value.
19-
public init(rawValue: UInt) {
20-
self.rawValue = rawValue
21-
}
22-
23-
/// Produce human-readable JSON with indented output.
24-
public static let prettyPrinted = OutputFormatting(rawValue: 1 << 0)
25-
26-
/// Produce JSON with dictionary keys sorted in lexicographic order.
27-
@available(macOS 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *)
28-
public static let sortedKeys = OutputFormatting(rawValue: 1 << 1)
29-
30-
/// By default slashes get escaped ("/" → "\/", "http://apple.com/" → "http:\/\/apple.com\/")
31-
/// for security reasons, allowing outputted JSON to be safely embedded within HTML/XML.
32-
/// In contexts where this escaping is unnecessary, the JSON is known to not be embedded,
33-
/// or is intended only for display, this option avoids this escaping.
34-
public static let withoutEscapingSlashes = OutputFormatting(rawValue: 1 << 3)
35-
}
36-
37-
/// The strategy to use for encoding `Date` values.
38-
public enum DateEncodingStrategy {
39-
/// Defer to `Date` for choosing an encoding. This is the default strategy.
40-
case deferredToDate
41-
42-
/// Encode the `Date` as a UNIX timestamp (as a JSON number).
43-
case secondsSince1970
44-
45-
/// Encode the `Date` as UNIX millisecond timestamp (as a JSON number).
46-
case millisecondsSince1970
47-
48-
/// Encode the `Date` as an ISO-8601-formatted string (in RFC 3339 format).
49-
@available(macOS 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
50-
case iso8601
51-
52-
/// Encode the `Date` as a string formatted by the given formatter.
53-
case formatted(DateFormatter)
54-
55-
/// Encode the `Date` as a custom value encoded by the given closure.
56-
///
57-
/// If the closure fails to encode a value into the given encoder, the encoder will encode an empty automatic container in its place.
58-
case custom((Date, Encoder) throws -> Void)
59-
}
60-
61-
/// The strategy to use for encoding `Data` values.
62-
public enum DataEncodingStrategy {
63-
/// Defer to `Data` for choosing an encoding.
64-
case deferredToData
65-
66-
/// Encoded the `Data` as a Base64-encoded string. This is the default strategy.
67-
case base64
68-
69-
/// Encode the `Data` as a custom value encoded by the given closure.
70-
///
71-
/// If the closure fails to encode a value into the given encoder, the encoder will encode an empty automatic container in its place.
72-
case custom((Data, Encoder) throws -> Void)
73-
}
74-
75-
/// The strategy to use for non-JSON-conforming floating-point values (IEEE 754 infinity and NaN).
76-
public enum NonConformingFloatEncodingStrategy {
77-
/// Throw upon encountering non-conforming values. This is the default strategy.
78-
case `throw`
79-
80-
/// Encode the values using the given representation strings.
81-
case convertToString(positiveInfinity: String, negativeInfinity: String, nan: String)
82-
}
83-
84-
/// The strategy to use for automatically changing the value of keys before encoding.
85-
public enum KeyEncodingStrategy {
86-
/// Use the keys specified by each type. This is the default strategy.
87-
case useDefaultKeys
88-
89-
/// Convert from "camelCaseKeys" to "snake_case_keys" before writing a key to JSON payload.
90-
///
91-
/// Capital characters are determined by testing membership in `CharacterSet.uppercaseLetters` and `CharacterSet.lowercaseLetters` (Unicode General Categories Lu and Lt).
92-
/// The conversion to lower case uses `Locale.system`, also known as the ICU "root" locale. This means the result is consistent regardless of the current user's locale and language preferences.
93-
///
94-
/// Converting from camel case to snake case:
95-
/// 1. Splits words at the boundary of lower-case to upper-case
96-
/// 2. Inserts `_` between words
97-
/// 3. Lowercases the entire string
98-
/// 4. Preserves starting and ending `_`.
99-
///
100-
/// For example, `oneTwoThree` becomes `one_two_three`. `_oneTwoThree_` becomes `_one_two_three_`.
101-
///
102-
/// - Note: Using a key encoding strategy has a nominal performance cost, as each string key has to be converted.
103-
case convertToSnakeCase
104-
105-
/// Provide a custom conversion to the key in the encoded JSON from the keys specified by the encoded types.
106-
/// The full path to the current encoding position is provided for context (in case you need to locate this key within the payload). The returned key is used in place of the last component in the coding path before encoding.
107-
/// If the result of the conversion is a duplicate key, then only one value will be present in the result.
108-
case custom((_ codingPath: [CodingKey]) -> CodingKey)
109-
110-
static func _convertToSnakeCase(_ stringKey: String) -> String {
111-
guard !stringKey.isEmpty else { return stringKey }
112-
113-
var words: [Range<String.Index>] = []
114-
// The general idea of this algorithm is to split words on transition from lower to upper case, then on transition of >1 upper case characters to lowercase
115-
//
116-
// myProperty -> my_property
117-
// myURLProperty -> my_url_property
118-
//
119-
// We assume, per Swift naming conventions, that the first character of the key is lowercase.
120-
var wordStart = stringKey.startIndex
121-
var searchRange = stringKey.index(after: wordStart)..<stringKey.endIndex
122-
123-
// Find next uppercase character
124-
while let upperCaseRange = stringKey.rangeOfCharacter(from: CharacterSet.uppercaseLetters, options: [], range: searchRange) {
125-
let untilUpperCase = wordStart..<upperCaseRange.lowerBound
126-
words.append(untilUpperCase)
127-
128-
// Find next lowercase character
129-
searchRange = upperCaseRange.lowerBound..<searchRange.upperBound
130-
guard let lowerCaseRange = stringKey.rangeOfCharacter(from: CharacterSet.lowercaseLetters, options: [], range: searchRange) else {
131-
// There are no more lower case letters. Just end here.
132-
wordStart = searchRange.lowerBound
133-
break
134-
}
135-
136-
// Is the next lowercase letter more than 1 after the uppercase? If so, we encountered a group of uppercase letters that we should treat as its own word
137-
let nextCharacterAfterCapital = stringKey.index(after: upperCaseRange.lowerBound)
138-
if lowerCaseRange.lowerBound == nextCharacterAfterCapital {
139-
// The next character after capital is a lower case character and therefore not a word boundary.
140-
// Continue searching for the next upper case for the boundary.
141-
wordStart = upperCaseRange.lowerBound
142-
} else {
143-
// There was a range of >1 capital letters. Turn those into a word, stopping at the capital before the lower case character.
144-
let beforeLowerIndex = stringKey.index(before: lowerCaseRange.lowerBound)
145-
words.append(upperCaseRange.lowerBound..<beforeLowerIndex)
146-
147-
// Next word starts at the capital before the lowercase we just found
148-
wordStart = beforeLowerIndex
149-
}
150-
searchRange = lowerCaseRange.upperBound..<searchRange.upperBound
151-
}
152-
words.append(wordStart..<searchRange.upperBound)
153-
let result = words.map({ (range) in
154-
return stringKey[range].lowercased()
155-
}).joined(separator: "_")
156-
return result
157-
}
158-
}
159-
160-
/// The output format to produce. Defaults to `[]`.
161-
open var outputFormatting: OutputFormatting = []
162-
163-
/// The strategy to use in encoding dates. Defaults to `.deferredToDate`.
164-
open var dateEncodingStrategy: DateEncodingStrategy = .deferredToDate
165-
166-
/// The strategy to use in encoding binary data. Defaults to `.base64`.
167-
open var dataEncodingStrategy: DataEncodingStrategy = .base64
168-
169-
/// The strategy to use in encoding non-conforming numbers. Defaults to `.throw`.
170-
open var nonConformingFloatEncodingStrategy: NonConformingFloatEncodingStrategy = .throw
171-
172-
/// The strategy to use for encoding keys. Defaults to `.useDefaultKeys`.
173-
open var keyEncodingStrategy: KeyEncodingStrategy = .useDefaultKeys
174-
175-
/// Contextual user-provided information for use during encoding.
176-
open var userInfo: [CodingUserInfoKey: Any] = [:]
14+
17715

17816
/// Options set on the top-level encoder to pass down the encoding hierarchy.
17917
struct _Options {
18018
let dateEncodingStrategy: DateEncodingStrategy
18119
let dataEncodingStrategy: DataEncodingStrategy
18220
let nonConformingFloatEncodingStrategy: NonConformingFloatEncodingStrategy
183-
let keyEncodingStrategy: KeyEncodingStrategy
21+
let keyEncodingStrategy: SmartKeyEncodingStrategy
18422
let userInfo: [CodingUserInfoKey: Any]
18523
}
18624

@@ -189,14 +27,10 @@ open class SmartJSONEncoder {
18927
return _Options(dateEncodingStrategy: dateEncodingStrategy,
19028
dataEncodingStrategy: dataEncodingStrategy,
19129
nonConformingFloatEncodingStrategy: nonConformingFloatEncodingStrategy,
192-
keyEncodingStrategy: keyEncodingStrategy,
30+
keyEncodingStrategy: smartKeyEncodingStrategy,
19331
userInfo: userInfo)
19432
}
19533

196-
// MARK: - Constructing a JSON Encoder
197-
198-
/// Initializes `self` with default strategies.
199-
public init() {}
20034

20135
// MARK: - Encoding Values
20236

@@ -206,7 +40,7 @@ open class SmartJSONEncoder {
20640
/// - returns: A new `Data` value containing the encoded JSON data.
20741
/// - throws: `EncodingError.invalidValue` if a non-conforming floating-point value is encountered during encoding, and the encoding strategy is `.throw`.
20842
/// - throws: An error if any value throws an error during encoding.
209-
open func encode<T: Encodable>(_ value: T) throws -> Data {
43+
open override func encode<T: Encodable>(_ value: T) throws -> Data {
21044
let value: JSONValue = try encodeAsJSONValue(value)
21145
let writer = JSONValue.Writer(options: self.outputFormatting)
21246
let bytes = writer.writeValue(value)
@@ -254,3 +88,4 @@ extension CodingUserInfoKey {
25488
/// 是否使用映射之后的key
25589
static var useMappedKeys = CodingUserInfoKey.init(rawValue: "Stamrt.useMappedKeys")
25690
}
91+

0 commit comments

Comments
 (0)