├── .gitattributes
├── .github
├── FUNDING.yml
└── workflows
│ ├── ci.yml
│ └── release.yml
├── .gitignore
├── AnyCodable-FlightSchool.podspec
├── AnyCodable.playground
├── Contents.swift
└── contents.xcplayground
├── AnyCodable.xcconfig
├── AnyCodable.xcodeproj
├── AnyCodableTests_Info.plist
├── AnyCodable_Info.plist
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── WorkspaceSettings.xcsettings
└── xcshareddata
│ └── xcschemes
│ └── AnyCodable-Package.xcscheme
├── AnyCodable.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ └── IDEWorkspaceChecks.plist
├── LICENSE.md
├── Package.swift
├── README.md
├── Sources
└── AnyCodable
│ ├── AnyCodable.swift
│ ├── AnyDecodable.swift
│ └── AnyEncodable.swift
└── Tests
└── AnyCodableTests
├── AnyCodableTests.swift
├── AnyDecodableTests.swift
└── AnyEncodableTests.swift
/.gitattributes:
--------------------------------------------------------------------------------
1 | Brewfile linguist-detectable=false
2 | Makefile linguist-detectable=false
3 | *.podspec linguist-detectable=false
4 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [mattt]
2 | custom: https://flight.school/books/codable
3 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: [master]
6 | pull_request:
7 | branches: [master]
8 |
9 | jobs:
10 | macos:
11 | runs-on: macos-latest
12 |
13 | steps:
14 | - name: Checkout
15 | uses: actions/checkout@v1
16 | - name: Build and Test
17 | run: swift test
18 |
19 | linux:
20 | runs-on: ubuntu-latest
21 |
22 | strategy:
23 | matrix:
24 | swift:
25 | - "5.1"
26 | - "5.2"
27 | - "5.3"
28 | - latest
29 |
30 | container:
31 | image: swift:${{ matrix.swift }}
32 |
33 | steps:
34 | - name: Checkout
35 | uses: actions/checkout@v1
36 | - name: Build and Test
37 | run: swift test --enable-test-discovery
38 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - "*.*.*"
7 |
8 | jobs:
9 | release:
10 | name: Push release to CocoaPods Trunk
11 |
12 | runs-on: macos-latest
13 |
14 | steps:
15 | - name: Checkout
16 | uses: actions/checkout@v2
17 |
18 | - name: Install Cocoapods
19 | run: gem install cocoapods
20 |
21 | - name: Deploy to Cocoapods
22 | run: |
23 | set -eo pipefail
24 | pod lib lint --allow-warnings
25 | pod trunk push --allow-warnings
26 | env:
27 | COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | /.build
3 | /Packages
4 | xcuserdata/
5 | DeriveredData/
6 | *.xcodeproj/*
7 | !*.xcodeproj/project.pbxproj
8 | !*.xcodeproj/xcshareddata/
9 | !*.xcworkspace/contents.xcworkspacedata
10 | /*.gcno
11 | **/xcshareddata/WorkspaceSettings.xcsettings
12 | Carthage/Build/
13 | /.swiftpm
14 |
--------------------------------------------------------------------------------
/AnyCodable-FlightSchool.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'AnyCodable-FlightSchool'
3 | s.module_name = 'AnyCodable'
4 | s.version = '0.6.7'
5 | s.summary = 'Type-erased wrappers for Encodable, Decodable, and Codable values.'
6 |
7 | s.description = <<-DESC
8 | This functionality is discussed in Chapter 3 of Flight School Guide to Swift Codable.
9 | DESC
10 |
11 | s.homepage = 'https://flight.school/books/codable/'
12 |
13 | s.license = { type: 'MIT', file: 'LICENSE.md' }
14 |
15 | s.author = { 'Mattt' => 'mattt@flight.school' }
16 |
17 | s.social_media_url = 'https://twitter.com/mattt'
18 |
19 | s.framework = 'Foundation'
20 |
21 | s.ios.deployment_target = '9.0'
22 | s.osx.deployment_target = '10.10'
23 | s.watchos.deployment_target = '2.0'
24 | s.tvos.deployment_target = '9.0'
25 |
26 | s.source = { git: 'https://github.com/Flight-School/AnyCodable.git', tag: s.version.to_s }
27 |
28 | s.source_files = 'Sources/**/*.swift'
29 |
30 | s.swift_version = '5.1'
31 | end
32 |
--------------------------------------------------------------------------------
/AnyCodable.playground/Contents.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | import AnyCodable
3 |
4 | let dictionary: [String: AnyEncodable] = [
5 | "boolean": true,
6 | "integer": 1,
7 | "double": 3.141592653589793,
8 | "string": "string",
9 | "array": [1, 2, 3],
10 | "nested": [
11 | "a": "alpha",
12 | "b": "bravo",
13 | "c": "charlie"
14 | ],
15 | "null": nil
16 | ]
17 |
18 | let encoder = JSONEncoder()
19 | let data = try encoder.encode(dictionary)
20 |
21 | let string = String(data: data, encoding: .utf8)
22 |
23 | let decoder = JSONDecoder()
24 | let object = try decoder.decode([String: AnyDecodable].self, from: data)
25 |
--------------------------------------------------------------------------------
/AnyCodable.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/AnyCodable.xcconfig:
--------------------------------------------------------------------------------
1 | IPHONEOS_DEPLOYMENT_TARGET = 8.0
2 | MACOSX_DEPLOYMENT_TARGET = 10.10
3 | TVOS_DEPLOYMENT_TARGET = 9.0
4 | WATCHOS_DEPLOYMENT_TARGET = 2.0
5 | MACH_O_TYPE = staticlib
6 |
--------------------------------------------------------------------------------
/AnyCodable.xcodeproj/AnyCodableTests_Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | CFBundleDevelopmentRegion
5 | en
6 | CFBundleExecutable
7 | $(EXECUTABLE_NAME)
8 | CFBundleIdentifier
9 | $(PRODUCT_BUNDLE_IDENTIFIER)
10 | CFBundleInfoDictionaryVersion
11 | 6.0
12 | CFBundleName
13 | $(PRODUCT_NAME)
14 | CFBundlePackageType
15 | BNDL
16 | CFBundleShortVersionString
17 | 1.0
18 | CFBundleSignature
19 | ????
20 | CFBundleVersion
21 | $(CURRENT_PROJECT_VERSION)
22 | NSPrincipalClass
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/AnyCodable.xcodeproj/AnyCodable_Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | CFBundleDevelopmentRegion
5 | en
6 | CFBundleExecutable
7 | $(EXECUTABLE_NAME)
8 | CFBundleIdentifier
9 | $(PRODUCT_BUNDLE_IDENTIFIER)
10 | CFBundleInfoDictionaryVersion
11 | 6.0
12 | CFBundleName
13 | $(PRODUCT_NAME)
14 | CFBundlePackageType
15 | FMWK
16 | CFBundleShortVersionString
17 | 1.0
18 | CFBundleSignature
19 | ????
20 | CFBundleVersion
21 | 1
22 | NSPrincipalClass
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/AnyCodable.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXAggregateTarget section */
10 | "AnyCodable::AnyCodablePackageTests::ProductTarget" /* AnyCodablePackageTests */ = {
11 | isa = PBXAggregateTarget;
12 | buildConfigurationList = OBJ_41 /* Build configuration list for PBXAggregateTarget "AnyCodablePackageTests" */;
13 | buildPhases = (
14 | );
15 | dependencies = (
16 | OBJ_44 /* PBXTargetDependency */,
17 | );
18 | name = AnyCodablePackageTests;
19 | productName = AnyCodablePackageTests;
20 | };
21 | /* End PBXAggregateTarget section */
22 |
23 | /* Begin PBXBuildFile section */
24 | OBJ_30 /* AnyCodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* AnyCodable.swift */; };
25 | OBJ_31 /* AnyDecodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* AnyDecodable.swift */; };
26 | OBJ_32 /* AnyEncodable.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* AnyEncodable.swift */; };
27 | OBJ_39 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; };
28 | OBJ_50 /* AnyCodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_14 /* AnyCodableTests.swift */; };
29 | OBJ_51 /* AnyDecodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_15 /* AnyDecodableTests.swift */; };
30 | OBJ_52 /* AnyEncodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_16 /* AnyEncodableTests.swift */; };
31 | OBJ_54 /* AnyCodable.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "AnyCodable::AnyCodable::Product" /* AnyCodable.framework */; };
32 | /* End PBXBuildFile section */
33 |
34 | /* Begin PBXContainerItemProxy section */
35 | F8558960228E1831001D1245 /* PBXContainerItemProxy */ = {
36 | isa = PBXContainerItemProxy;
37 | containerPortal = OBJ_1 /* Project object */;
38 | proxyType = 1;
39 | remoteGlobalIDString = "AnyCodable::AnyCodable";
40 | remoteInfo = AnyCodable;
41 | };
42 | F8558961228E1831001D1245 /* PBXContainerItemProxy */ = {
43 | isa = PBXContainerItemProxy;
44 | containerPortal = OBJ_1 /* Project object */;
45 | proxyType = 1;
46 | remoteGlobalIDString = "AnyCodable::AnyCodableTests";
47 | remoteInfo = AnyCodableTests;
48 | };
49 | /* End PBXContainerItemProxy section */
50 |
51 | /* Begin PBXFileReference section */
52 | "AnyCodable::AnyCodable::Product" /* AnyCodable.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = AnyCodable.framework; sourceTree = BUILT_PRODUCTS_DIR; };
53 | "AnyCodable::AnyCodableTests::Product" /* AnyCodableTests.xctest */ = {isa = PBXFileReference; lastKnownFileType = file; path = AnyCodableTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
54 | OBJ_10 /* AnyDecodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyDecodable.swift; sourceTree = ""; };
55 | OBJ_11 /* AnyEncodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyEncodable.swift; sourceTree = ""; };
56 | OBJ_14 /* AnyCodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyCodableTests.swift; sourceTree = ""; };
57 | OBJ_15 /* AnyDecodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyDecodableTests.swift; sourceTree = ""; };
58 | OBJ_16 /* AnyEncodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyEncodableTests.swift; sourceTree = ""; };
59 | OBJ_20 /* AnyCodable.xcworkspace */ = {isa = PBXFileReference; lastKnownFileType = wrapper.workspace; path = AnyCodable.xcworkspace; sourceTree = SOURCE_ROOT; };
60 | OBJ_21 /* LICENSE.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = ""; };
61 | OBJ_22 /* AnyCodable-FlightSchool.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = "AnyCodable-FlightSchool.podspec"; sourceTree = ""; };
62 | OBJ_23 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; };
63 | OBJ_24 /* AnyCodable.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AnyCodable.xcconfig; sourceTree = ""; };
64 | OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; };
65 | OBJ_9 /* AnyCodable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyCodable.swift; sourceTree = ""; };
66 | /* End PBXFileReference section */
67 |
68 | /* Begin PBXFrameworksBuildPhase section */
69 | OBJ_33 /* Frameworks */ = {
70 | isa = PBXFrameworksBuildPhase;
71 | buildActionMask = 0;
72 | files = (
73 | );
74 | runOnlyForDeploymentPostprocessing = 0;
75 | };
76 | OBJ_53 /* Frameworks */ = {
77 | isa = PBXFrameworksBuildPhase;
78 | buildActionMask = 0;
79 | files = (
80 | OBJ_54 /* AnyCodable.framework in Frameworks */,
81 | );
82 | runOnlyForDeploymentPostprocessing = 0;
83 | };
84 | /* End PBXFrameworksBuildPhase section */
85 |
86 | /* Begin PBXGroup section */
87 | OBJ_12 /* Tests */ = {
88 | isa = PBXGroup;
89 | children = (
90 | OBJ_13 /* AnyCodableTests */,
91 | );
92 | name = Tests;
93 | sourceTree = SOURCE_ROOT;
94 | };
95 | OBJ_13 /* AnyCodableTests */ = {
96 | isa = PBXGroup;
97 | children = (
98 | OBJ_14 /* AnyCodableTests.swift */,
99 | OBJ_15 /* AnyDecodableTests.swift */,
100 | OBJ_16 /* AnyEncodableTests.swift */,
101 | );
102 | name = AnyCodableTests;
103 | path = Tests/AnyCodableTests;
104 | sourceTree = SOURCE_ROOT;
105 | };
106 | OBJ_17 /* Products */ = {
107 | isa = PBXGroup;
108 | children = (
109 | "AnyCodable::AnyCodableTests::Product" /* AnyCodableTests.xctest */,
110 | "AnyCodable::AnyCodable::Product" /* AnyCodable.framework */,
111 | );
112 | name = Products;
113 | sourceTree = BUILT_PRODUCTS_DIR;
114 | };
115 | OBJ_5 = {
116 | isa = PBXGroup;
117 | children = (
118 | OBJ_6 /* Package.swift */,
119 | OBJ_7 /* Sources */,
120 | OBJ_12 /* Tests */,
121 | OBJ_17 /* Products */,
122 | OBJ_20 /* AnyCodable.xcworkspace */,
123 | OBJ_21 /* LICENSE.md */,
124 | OBJ_22 /* AnyCodable-FlightSchool.podspec */,
125 | OBJ_23 /* README.md */,
126 | OBJ_24 /* AnyCodable.xcconfig */,
127 | );
128 | sourceTree = "";
129 | };
130 | OBJ_7 /* Sources */ = {
131 | isa = PBXGroup;
132 | children = (
133 | OBJ_8 /* AnyCodable */,
134 | );
135 | name = Sources;
136 | sourceTree = SOURCE_ROOT;
137 | };
138 | OBJ_8 /* AnyCodable */ = {
139 | isa = PBXGroup;
140 | children = (
141 | OBJ_9 /* AnyCodable.swift */,
142 | OBJ_10 /* AnyDecodable.swift */,
143 | OBJ_11 /* AnyEncodable.swift */,
144 | );
145 | name = AnyCodable;
146 | path = Sources/AnyCodable;
147 | sourceTree = SOURCE_ROOT;
148 | };
149 | /* End PBXGroup section */
150 |
151 | /* Begin PBXNativeTarget section */
152 | "AnyCodable::AnyCodable" /* AnyCodable */ = {
153 | isa = PBXNativeTarget;
154 | buildConfigurationList = OBJ_26 /* Build configuration list for PBXNativeTarget "AnyCodable" */;
155 | buildPhases = (
156 | OBJ_29 /* Sources */,
157 | OBJ_33 /* Frameworks */,
158 | );
159 | buildRules = (
160 | );
161 | dependencies = (
162 | );
163 | name = AnyCodable;
164 | productName = AnyCodable;
165 | productReference = "AnyCodable::AnyCodable::Product" /* AnyCodable.framework */;
166 | productType = "com.apple.product-type.framework";
167 | };
168 | "AnyCodable::AnyCodableTests" /* AnyCodableTests */ = {
169 | isa = PBXNativeTarget;
170 | buildConfigurationList = OBJ_46 /* Build configuration list for PBXNativeTarget "AnyCodableTests" */;
171 | buildPhases = (
172 | OBJ_49 /* Sources */,
173 | OBJ_53 /* Frameworks */,
174 | );
175 | buildRules = (
176 | );
177 | dependencies = (
178 | OBJ_55 /* PBXTargetDependency */,
179 | );
180 | name = AnyCodableTests;
181 | productName = AnyCodableTests;
182 | productReference = "AnyCodable::AnyCodableTests::Product" /* AnyCodableTests.xctest */;
183 | productType = "com.apple.product-type.bundle.unit-test";
184 | };
185 | "AnyCodable::SwiftPMPackageDescription" /* AnyCodablePackageDescription */ = {
186 | isa = PBXNativeTarget;
187 | buildConfigurationList = OBJ_35 /* Build configuration list for PBXNativeTarget "AnyCodablePackageDescription" */;
188 | buildPhases = (
189 | OBJ_38 /* Sources */,
190 | );
191 | buildRules = (
192 | );
193 | dependencies = (
194 | );
195 | name = AnyCodablePackageDescription;
196 | productName = AnyCodablePackageDescription;
197 | productType = "com.apple.product-type.framework";
198 | };
199 | /* End PBXNativeTarget section */
200 |
201 | /* Begin PBXProject section */
202 | OBJ_1 /* Project object */ = {
203 | isa = PBXProject;
204 | attributes = {
205 | LastSwiftMigration = 9999;
206 | LastUpgradeCheck = 9999;
207 | };
208 | buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "AnyCodable" */;
209 | compatibilityVersion = "Xcode 3.2";
210 | developmentRegion = English;
211 | hasScannedForEncodings = 0;
212 | knownRegions = (
213 | English,
214 | en,
215 | );
216 | mainGroup = OBJ_5;
217 | productRefGroup = OBJ_17 /* Products */;
218 | projectDirPath = "";
219 | projectRoot = "";
220 | targets = (
221 | "AnyCodable::AnyCodable" /* AnyCodable */,
222 | "AnyCodable::SwiftPMPackageDescription" /* AnyCodablePackageDescription */,
223 | "AnyCodable::AnyCodablePackageTests::ProductTarget" /* AnyCodablePackageTests */,
224 | "AnyCodable::AnyCodableTests" /* AnyCodableTests */,
225 | );
226 | };
227 | /* End PBXProject section */
228 |
229 | /* Begin PBXSourcesBuildPhase section */
230 | OBJ_29 /* Sources */ = {
231 | isa = PBXSourcesBuildPhase;
232 | buildActionMask = 0;
233 | files = (
234 | OBJ_30 /* AnyCodable.swift in Sources */,
235 | OBJ_31 /* AnyDecodable.swift in Sources */,
236 | OBJ_32 /* AnyEncodable.swift in Sources */,
237 | );
238 | runOnlyForDeploymentPostprocessing = 0;
239 | };
240 | OBJ_38 /* Sources */ = {
241 | isa = PBXSourcesBuildPhase;
242 | buildActionMask = 0;
243 | files = (
244 | OBJ_39 /* Package.swift in Sources */,
245 | );
246 | runOnlyForDeploymentPostprocessing = 0;
247 | };
248 | OBJ_49 /* Sources */ = {
249 | isa = PBXSourcesBuildPhase;
250 | buildActionMask = 0;
251 | files = (
252 | OBJ_50 /* AnyCodableTests.swift in Sources */,
253 | OBJ_51 /* AnyDecodableTests.swift in Sources */,
254 | OBJ_52 /* AnyEncodableTests.swift in Sources */,
255 | );
256 | runOnlyForDeploymentPostprocessing = 0;
257 | };
258 | /* End PBXSourcesBuildPhase section */
259 |
260 | /* Begin PBXTargetDependency section */
261 | OBJ_44 /* PBXTargetDependency */ = {
262 | isa = PBXTargetDependency;
263 | target = "AnyCodable::AnyCodableTests" /* AnyCodableTests */;
264 | targetProxy = F8558961228E1831001D1245 /* PBXContainerItemProxy */;
265 | };
266 | OBJ_55 /* PBXTargetDependency */ = {
267 | isa = PBXTargetDependency;
268 | target = "AnyCodable::AnyCodable" /* AnyCodable */;
269 | targetProxy = F8558960228E1831001D1245 /* PBXContainerItemProxy */;
270 | };
271 | /* End PBXTargetDependency section */
272 |
273 | /* Begin XCBuildConfiguration section */
274 | OBJ_27 /* Debug */ = {
275 | isa = XCBuildConfiguration;
276 | buildSettings = {
277 | BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
278 | DYLIB_COMPATIBILITY_VERSION = 1;
279 | DYLIB_CURRENT_VERSION = 1;
280 | ENABLE_TESTABILITY = YES;
281 | FRAMEWORK_SEARCH_PATHS = (
282 | "$(inherited)",
283 | "$(PLATFORM_DIR)/Developer/Library/Frameworks",
284 | );
285 | HEADER_SEARCH_PATHS = "$(inherited)";
286 | INFOPLIST_FILE = AnyCodable.xcodeproj/AnyCodable_Info.plist;
287 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
288 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx";
289 | MACOSX_DEPLOYMENT_TARGET = 10.10;
290 | OTHER_CFLAGS = "$(inherited)";
291 | OTHER_LDFLAGS = "$(inherited)";
292 | OTHER_SWIFT_FLAGS = "$(inherited)";
293 | PRODUCT_BUNDLE_IDENTIFIER = AnyCodable;
294 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)";
295 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
296 | SKIP_INSTALL = YES;
297 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)";
298 | SWIFT_VERSION = 5.0;
299 | TARGET_NAME = AnyCodable;
300 | TVOS_DEPLOYMENT_TARGET = 9.0;
301 | WATCHOS_DEPLOYMENT_TARGET = 2.0;
302 | };
303 | name = Debug;
304 | };
305 | OBJ_28 /* Release */ = {
306 | isa = XCBuildConfiguration;
307 | buildSettings = {
308 | BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
309 | DYLIB_COMPATIBILITY_VERSION = 1;
310 | DYLIB_CURRENT_VERSION = 1;
311 | ENABLE_TESTABILITY = YES;
312 | FRAMEWORK_SEARCH_PATHS = (
313 | "$(inherited)",
314 | "$(PLATFORM_DIR)/Developer/Library/Frameworks",
315 | );
316 | HEADER_SEARCH_PATHS = "$(inherited)";
317 | INFOPLIST_FILE = AnyCodable.xcodeproj/AnyCodable_Info.plist;
318 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
319 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) $(TOOLCHAIN_DIR)/usr/lib/swift/macosx";
320 | MACOSX_DEPLOYMENT_TARGET = 10.10;
321 | OTHER_CFLAGS = "$(inherited)";
322 | OTHER_LDFLAGS = "$(inherited)";
323 | OTHER_SWIFT_FLAGS = "$(inherited)";
324 | PRODUCT_BUNDLE_IDENTIFIER = AnyCodable;
325 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)";
326 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
327 | SKIP_INSTALL = YES;
328 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)";
329 | SWIFT_VERSION = 5.0;
330 | TARGET_NAME = AnyCodable;
331 | TVOS_DEPLOYMENT_TARGET = 9.0;
332 | WATCHOS_DEPLOYMENT_TARGET = 2.0;
333 | };
334 | name = Release;
335 | };
336 | OBJ_3 /* Debug */ = {
337 | isa = XCBuildConfiguration;
338 | buildSettings = {
339 | CLANG_ENABLE_OBJC_ARC = YES;
340 | COMBINE_HIDPI_IMAGES = YES;
341 | COPY_PHASE_STRIP = NO;
342 | DEBUG_INFORMATION_FORMAT = dwarf;
343 | DYLIB_INSTALL_NAME_BASE = "@rpath";
344 | ENABLE_NS_ASSERTIONS = YES;
345 | GCC_OPTIMIZATION_LEVEL = 0;
346 | GCC_PREPROCESSOR_DEFINITIONS = (
347 | "$(inherited)",
348 | "SWIFT_PACKAGE=1",
349 | "DEBUG=1",
350 | );
351 | MACOSX_DEPLOYMENT_TARGET = 10.10;
352 | ONLY_ACTIVE_ARCH = YES;
353 | OTHER_SWIFT_FLAGS = "-DXcode";
354 | PRODUCT_NAME = "$(TARGET_NAME)";
355 | SDKROOT = macosx;
356 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator";
357 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) SWIFT_PACKAGE DEBUG";
358 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
359 | USE_HEADERMAP = NO;
360 | };
361 | name = Debug;
362 | };
363 | OBJ_36 /* Debug */ = {
364 | isa = XCBuildConfiguration;
365 | buildSettings = {
366 | BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
367 | LD = /usr/bin/true;
368 | OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk";
369 | SWIFT_VERSION = 5.0;
370 | };
371 | name = Debug;
372 | };
373 | OBJ_37 /* Release */ = {
374 | isa = XCBuildConfiguration;
375 | buildSettings = {
376 | BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
377 | LD = /usr/bin/true;
378 | OTHER_SWIFT_FLAGS = "-swift-version 5 -I $(TOOLCHAIN_DIR)/usr/lib/swift/pm/4_2 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.sdk";
379 | SWIFT_VERSION = 5.0;
380 | };
381 | name = Release;
382 | };
383 | OBJ_4 /* Release */ = {
384 | isa = XCBuildConfiguration;
385 | buildSettings = {
386 | CLANG_ENABLE_OBJC_ARC = YES;
387 | COMBINE_HIDPI_IMAGES = YES;
388 | COPY_PHASE_STRIP = YES;
389 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
390 | DYLIB_INSTALL_NAME_BASE = "@rpath";
391 | GCC_OPTIMIZATION_LEVEL = s;
392 | GCC_PREPROCESSOR_DEFINITIONS = (
393 | "$(inherited)",
394 | "SWIFT_PACKAGE=1",
395 | );
396 | MACOSX_DEPLOYMENT_TARGET = 10.10;
397 | OTHER_SWIFT_FLAGS = "-DXcode";
398 | PRODUCT_NAME = "$(TARGET_NAME)";
399 | SDKROOT = macosx;
400 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator";
401 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) SWIFT_PACKAGE";
402 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
403 | USE_HEADERMAP = NO;
404 | };
405 | name = Release;
406 | };
407 | OBJ_42 /* Debug */ = {
408 | isa = XCBuildConfiguration;
409 | buildSettings = {
410 | BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
411 | };
412 | name = Debug;
413 | };
414 | OBJ_43 /* Release */ = {
415 | isa = XCBuildConfiguration;
416 | buildSettings = {
417 | BUILD_LIBRARY_FOR_DISTRIBUTION = YES;
418 | };
419 | name = Release;
420 | };
421 | OBJ_47 /* Debug */ = {
422 | isa = XCBuildConfiguration;
423 | buildSettings = {
424 | CLANG_ENABLE_MODULES = YES;
425 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
426 | FRAMEWORK_SEARCH_PATHS = (
427 | "$(inherited)",
428 | "$(PLATFORM_DIR)/Developer/Library/Frameworks",
429 | );
430 | HEADER_SEARCH_PATHS = "$(inherited)";
431 | INFOPLIST_FILE = AnyCodable.xcodeproj/AnyCodableTests_Info.plist;
432 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
433 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks";
434 | MACOSX_DEPLOYMENT_TARGET = 10.10;
435 | OTHER_CFLAGS = "$(inherited)";
436 | OTHER_LDFLAGS = "$(inherited)";
437 | OTHER_SWIFT_FLAGS = "$(inherited)";
438 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)";
439 | SWIFT_VERSION = 5.0;
440 | TARGET_NAME = AnyCodableTests;
441 | TVOS_DEPLOYMENT_TARGET = 9.0;
442 | WATCHOS_DEPLOYMENT_TARGET = 2.0;
443 | };
444 | name = Debug;
445 | };
446 | OBJ_48 /* Release */ = {
447 | isa = XCBuildConfiguration;
448 | buildSettings = {
449 | CLANG_ENABLE_MODULES = YES;
450 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
451 | FRAMEWORK_SEARCH_PATHS = (
452 | "$(inherited)",
453 | "$(PLATFORM_DIR)/Developer/Library/Frameworks",
454 | );
455 | HEADER_SEARCH_PATHS = "$(inherited)";
456 | INFOPLIST_FILE = AnyCodable.xcodeproj/AnyCodableTests_Info.plist;
457 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
458 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @loader_path/../Frameworks @loader_path/Frameworks";
459 | MACOSX_DEPLOYMENT_TARGET = 10.10;
460 | OTHER_CFLAGS = "$(inherited)";
461 | OTHER_LDFLAGS = "$(inherited)";
462 | OTHER_SWIFT_FLAGS = "$(inherited)";
463 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)";
464 | SWIFT_VERSION = 5.0;
465 | TARGET_NAME = AnyCodableTests;
466 | TVOS_DEPLOYMENT_TARGET = 9.0;
467 | WATCHOS_DEPLOYMENT_TARGET = 2.0;
468 | };
469 | name = Release;
470 | };
471 | /* End XCBuildConfiguration section */
472 |
473 | /* Begin XCConfigurationList section */
474 | OBJ_2 /* Build configuration list for PBXProject "AnyCodable" */ = {
475 | isa = XCConfigurationList;
476 | buildConfigurations = (
477 | OBJ_3 /* Debug */,
478 | OBJ_4 /* Release */,
479 | );
480 | defaultConfigurationIsVisible = 0;
481 | defaultConfigurationName = Release;
482 | };
483 | OBJ_26 /* Build configuration list for PBXNativeTarget "AnyCodable" */ = {
484 | isa = XCConfigurationList;
485 | buildConfigurations = (
486 | OBJ_27 /* Debug */,
487 | OBJ_28 /* Release */,
488 | );
489 | defaultConfigurationIsVisible = 0;
490 | defaultConfigurationName = Release;
491 | };
492 | OBJ_35 /* Build configuration list for PBXNativeTarget "AnyCodablePackageDescription" */ = {
493 | isa = XCConfigurationList;
494 | buildConfigurations = (
495 | OBJ_36 /* Debug */,
496 | OBJ_37 /* Release */,
497 | );
498 | defaultConfigurationIsVisible = 0;
499 | defaultConfigurationName = Release;
500 | };
501 | OBJ_41 /* Build configuration list for PBXAggregateTarget "AnyCodablePackageTests" */ = {
502 | isa = XCConfigurationList;
503 | buildConfigurations = (
504 | OBJ_42 /* Debug */,
505 | OBJ_43 /* Release */,
506 | );
507 | defaultConfigurationIsVisible = 0;
508 | defaultConfigurationName = Release;
509 | };
510 | OBJ_46 /* Build configuration list for PBXNativeTarget "AnyCodableTests" */ = {
511 | isa = XCConfigurationList;
512 | buildConfigurations = (
513 | OBJ_47 /* Debug */,
514 | OBJ_48 /* Release */,
515 | );
516 | defaultConfigurationIsVisible = 0;
517 | defaultConfigurationName = Release;
518 | };
519 | /* End XCConfigurationList section */
520 | };
521 | rootObject = OBJ_1 /* Project object */;
522 | }
523 |
--------------------------------------------------------------------------------
/AnyCodable.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
--------------------------------------------------------------------------------
/AnyCodable.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded
6 |
7 |
8 |
--------------------------------------------------------------------------------
/AnyCodable.xcodeproj/xcshareddata/xcschemes/AnyCodable-Package.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
55 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
74 |
76 |
77 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/AnyCodable.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/AnyCodable.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright 2018 Read Evaluate Press, LLC
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a
4 | copy of this software and associated documentation files (the "Software"),
5 | to deal in the Software without restriction, including without limitation
6 | the rights to use, copy, modify, merge, publish, distribute, sublicense,
7 | and/or sell copies of the Software, and to permit persons to whom the
8 | Software is furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19 | DEALINGS IN THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.1
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "AnyCodable",
8 | platforms: [
9 | .iOS(.v9),
10 | .macOS(.v10_10),
11 | .tvOS(.v9),
12 | .watchOS(.v2),
13 | ],
14 | products: [
15 | // Products define the executables and libraries produced by a package, and make them visible to other packages.
16 | .library(
17 | name: "AnyCodable",
18 | targets: ["AnyCodable"]
19 | ),
20 | ],
21 | dependencies: [
22 | // Dependencies declare other packages that this package depends on.
23 | // .package(url: /* package url */, from: "1.0.0"),
24 | ],
25 | targets: [
26 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
27 | // Targets can depend on other targets in this package, and on products in packages which this package depends on.
28 | .target(
29 | name: "AnyCodable",
30 | dependencies: []
31 | ),
32 | .testTarget(
33 | name: "AnyCodableTests",
34 | dependencies: ["AnyCodable"]
35 | ),
36 | ]
37 | )
38 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AnyCodable
2 |
3 | [![Build Status][build status badge]][build status]
4 | [![License][license badge]][license]
5 | [![Swift Version][swift version badge]][swift version]
6 | ![Cocoapods platforms][cocoapods platforms badge]
7 | [![Cocoapods compatible][cocoapods badge]][cocoapods]
8 | [![Carthage compatible][carthage badge]][carthage]
9 |
10 | Type-erased wrappers for `Encodable`, `Decodable`, and `Codable` values.
11 |
12 | This functionality is discussed in Chapter 3 of
13 | [Flight School Guide to Swift Codable](https://flight.school/books/codable).
14 |
15 | ## Installation
16 |
17 | ### Swift Package Manager
18 |
19 | Add the AnyCodable package to your target dependencies in `Package.swift`:
20 |
21 | ```swift
22 | import PackageDescription
23 |
24 | let package = Package(
25 | name: "YourProject",
26 | dependencies: [
27 | .package(
28 | url: "https://github.com/Flight-School/AnyCodable",
29 | from: "0.6.0"
30 | ),
31 | ]
32 | )
33 | ```
34 |
35 | Then run the `swift build` command to build your project.
36 |
37 | ### CocoaPods
38 |
39 | You can install `AnyCodable` via CocoaPods
40 | by adding the following line to your `Podfile`:
41 |
42 | ```ruby
43 | pod 'AnyCodable-FlightSchool', '~> 0.6.0'
44 | ```
45 |
46 | Run the `pod install` command to download the library
47 | and integrate it into your Xcode project.
48 |
49 | > **Note**
50 | > The module name for this library is "AnyCodable" ---
51 | > that is, to use it, you add `import AnyCodable` to the top of your Swift code
52 | > just as you would by any other installation method.
53 | > The pod is called "AnyCodable-FlightSchool"
54 | > because there's an existing pod with the name "AnyCodable".
55 |
56 | ### Carthage
57 |
58 | To use `AnyCodable` in your Xcode project using Carthage,
59 | specify it in `Cartfile`:
60 |
61 | ```
62 | github "Flight-School/AnyCodable" ~> 0.6.0
63 | ```
64 |
65 | Then run the `carthage update` command to build the framework,
66 | and drag the built AnyCodable.framework into your Xcode project.
67 |
68 | ## Usage
69 |
70 | ### AnyEncodable
71 |
72 | ```swift
73 | import AnyCodable
74 |
75 | let dictionary: [String: AnyEncodable] = [
76 | "boolean": true,
77 | "integer": 1,
78 | "double": 3.141592653589793,
79 | "string": "string",
80 | "array": [1, 2, 3],
81 | "nested": [
82 | "a": "alpha",
83 | "b": "bravo",
84 | "c": "charlie"
85 | ],
86 | "null": nil
87 | ]
88 |
89 | let encoder = JSONEncoder()
90 | let json = try! encoder.encode(dictionary)
91 | ```
92 |
93 | ### AnyDecodable
94 |
95 | ```swift
96 | let json = """
97 | {
98 | "boolean": true,
99 | "integer": 1,
100 | "double": 3.141592653589793,
101 | "string": "string",
102 | "array": [1, 2, 3],
103 | "nested": {
104 | "a": "alpha",
105 | "b": "bravo",
106 | "c": "charlie"
107 | },
108 | "null": null
109 | }
110 | """.data(using: .utf8)!
111 |
112 | let decoder = JSONDecoder()
113 | let dictionary = try! decoder.decode([String: AnyDecodable].self, from: json)
114 | ```
115 |
116 | ### AnyCodable
117 |
118 | `AnyCodable` can be used to wrap values for encoding and decoding.
119 |
120 | ## License
121 |
122 | MIT
123 |
124 | ## Contact
125 |
126 | Mattt ([@mattt](https://twitter.com/mattt))
127 |
128 | [build status]: https://github.com/Flight-School/AnyCodable/actions?query=workflow%3ACI
129 | [build status badge]: https://github.com/Flight-School/AnyCodable/workflows/CI/badge.svg
130 | [license]: https://opensource.org/licenses/MIT
131 | [license badge]: https://img.shields.io/cocoapods/l/AnyCodable-FlightSchool.svg
132 | [swift version]: https://swift.org/download/
133 | [swift version badge]: https://img.shields.io/badge/swift%20version-5.1+-orange.svg
134 | [cocoapods platforms badge]: https://img.shields.io/cocoapods/p/AnyCodable-FlightSchool.svg
135 | [cocoapods]: https://cocoapods.org/pods/AnyCodable-FlightSchool
136 | [cocoapods badge]: https://img.shields.io/cocoapods/v/AnyCodable-FlightSchool.svg
137 | [carthage]: https://github.com/Carthage/Carthage
138 | [carthage badge]: https://img.shields.io/badge/Carthage-compatible-4BC51D.svg
139 |
--------------------------------------------------------------------------------
/Sources/AnyCodable/AnyCodable.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | /**
3 | A type-erased `Codable` value.
4 |
5 | The `AnyCodable` type forwards encoding and decoding responsibilities
6 | to an underlying value, hiding its specific underlying type.
7 |
8 | You can encode or decode mixed-type values in dictionaries
9 | and other collections that require `Encodable` or `Decodable` conformance
10 | by declaring their contained type to be `AnyCodable`.
11 |
12 | - SeeAlso: `AnyEncodable`
13 | - SeeAlso: `AnyDecodable`
14 | */
15 | @frozen public struct AnyCodable: Codable {
16 | public let value: Any
17 |
18 | public init(_ value: T?) {
19 | self.value = value ?? ()
20 | }
21 | }
22 |
23 | extension AnyCodable: _AnyEncodable, _AnyDecodable {}
24 |
25 | extension AnyCodable: Equatable {
26 | public static func == (lhs: AnyCodable, rhs: AnyCodable) -> Bool {
27 | switch (lhs.value, rhs.value) {
28 | case is (Void, Void):
29 | return true
30 | case let (lhs as Bool, rhs as Bool):
31 | return lhs == rhs
32 | case let (lhs as Int, rhs as Int):
33 | return lhs == rhs
34 | case let (lhs as Int8, rhs as Int8):
35 | return lhs == rhs
36 | case let (lhs as Int16, rhs as Int16):
37 | return lhs == rhs
38 | case let (lhs as Int32, rhs as Int32):
39 | return lhs == rhs
40 | case let (lhs as Int64, rhs as Int64):
41 | return lhs == rhs
42 | case let (lhs as UInt, rhs as UInt):
43 | return lhs == rhs
44 | case let (lhs as UInt8, rhs as UInt8):
45 | return lhs == rhs
46 | case let (lhs as UInt16, rhs as UInt16):
47 | return lhs == rhs
48 | case let (lhs as UInt32, rhs as UInt32):
49 | return lhs == rhs
50 | case let (lhs as UInt64, rhs as UInt64):
51 | return lhs == rhs
52 | case let (lhs as Float, rhs as Float):
53 | return lhs == rhs
54 | case let (lhs as Double, rhs as Double):
55 | return lhs == rhs
56 | case let (lhs as String, rhs as String):
57 | return lhs == rhs
58 | case let (lhs as [String: AnyCodable], rhs as [String: AnyCodable]):
59 | return lhs == rhs
60 | case let (lhs as [AnyCodable], rhs as [AnyCodable]):
61 | return lhs == rhs
62 | case let (lhs as [String: Any], rhs as [String: Any]):
63 | return NSDictionary(dictionary: lhs) == NSDictionary(dictionary: rhs)
64 | case let (lhs as [Any], rhs as [Any]):
65 | return NSArray(array: lhs) == NSArray(array: rhs)
66 | case is (NSNull, NSNull):
67 | return true
68 | default:
69 | return false
70 | }
71 | }
72 | }
73 |
74 | extension AnyCodable: CustomStringConvertible {
75 | public var description: String {
76 | switch value {
77 | case is Void:
78 | return String(describing: nil as Any?)
79 | case let value as CustomStringConvertible:
80 | return value.description
81 | default:
82 | return String(describing: value)
83 | }
84 | }
85 | }
86 |
87 | extension AnyCodable: CustomDebugStringConvertible {
88 | public var debugDescription: String {
89 | switch value {
90 | case let value as CustomDebugStringConvertible:
91 | return "AnyCodable(\(value.debugDescription))"
92 | default:
93 | return "AnyCodable(\(description))"
94 | }
95 | }
96 | }
97 |
98 | extension AnyCodable: ExpressibleByNilLiteral {}
99 | extension AnyCodable: ExpressibleByBooleanLiteral {}
100 | extension AnyCodable: ExpressibleByIntegerLiteral {}
101 | extension AnyCodable: ExpressibleByFloatLiteral {}
102 | extension AnyCodable: ExpressibleByStringLiteral {}
103 | extension AnyCodable: ExpressibleByStringInterpolation {}
104 | extension AnyCodable: ExpressibleByArrayLiteral {}
105 | extension AnyCodable: ExpressibleByDictionaryLiteral {}
106 |
107 |
108 | extension AnyCodable: Hashable {
109 | public func hash(into hasher: inout Hasher) {
110 | switch value {
111 | case let value as Bool:
112 | hasher.combine(value)
113 | case let value as Int:
114 | hasher.combine(value)
115 | case let value as Int8:
116 | hasher.combine(value)
117 | case let value as Int16:
118 | hasher.combine(value)
119 | case let value as Int32:
120 | hasher.combine(value)
121 | case let value as Int64:
122 | hasher.combine(value)
123 | case let value as UInt:
124 | hasher.combine(value)
125 | case let value as UInt8:
126 | hasher.combine(value)
127 | case let value as UInt16:
128 | hasher.combine(value)
129 | case let value as UInt32:
130 | hasher.combine(value)
131 | case let value as UInt64:
132 | hasher.combine(value)
133 | case let value as Float:
134 | hasher.combine(value)
135 | case let value as Double:
136 | hasher.combine(value)
137 | case let value as String:
138 | hasher.combine(value)
139 | case let value as [String: AnyCodable]:
140 | hasher.combine(value)
141 | case let value as [AnyCodable]:
142 | hasher.combine(value)
143 | default:
144 | break
145 | }
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/Sources/AnyCodable/AnyDecodable.swift:
--------------------------------------------------------------------------------
1 | #if canImport(Foundation)
2 | import Foundation
3 | #endif
4 |
5 | /**
6 | A type-erased `Decodable` value.
7 |
8 | The `AnyDecodable` type forwards decoding responsibilities
9 | to an underlying value, hiding its specific underlying type.
10 |
11 | You can decode mixed-type values in dictionaries
12 | and other collections that require `Decodable` conformance
13 | by declaring their contained type to be `AnyDecodable`:
14 |
15 | let json = """
16 | {
17 | "boolean": true,
18 | "integer": 42,
19 | "double": 3.141592653589793,
20 | "string": "string",
21 | "array": [1, 2, 3],
22 | "nested": {
23 | "a": "alpha",
24 | "b": "bravo",
25 | "c": "charlie"
26 | },
27 | "null": null
28 | }
29 | """.data(using: .utf8)!
30 |
31 | let decoder = JSONDecoder()
32 | let dictionary = try! decoder.decode([String: AnyDecodable].self, from: json)
33 | */
34 | @frozen public struct AnyDecodable: Decodable {
35 | public let value: Any
36 |
37 | public init(_ value: T?) {
38 | self.value = value ?? ()
39 | }
40 | }
41 |
42 | @usableFromInline
43 | protocol _AnyDecodable {
44 | var value: Any { get }
45 | init(_ value: T?)
46 | }
47 |
48 | extension AnyDecodable: _AnyDecodable {}
49 |
50 | extension _AnyDecodable {
51 | public init(from decoder: Decoder) throws {
52 | let container = try decoder.singleValueContainer()
53 |
54 | if container.decodeNil() {
55 | #if canImport(Foundation)
56 | self.init(NSNull())
57 | #else
58 | self.init(Optional.none)
59 | #endif
60 | } else if let bool = try? container.decode(Bool.self) {
61 | self.init(bool)
62 | } else if let int = try? container.decode(Int.self) {
63 | self.init(int)
64 | } else if let uint = try? container.decode(UInt.self) {
65 | self.init(uint)
66 | } else if let double = try? container.decode(Double.self) {
67 | self.init(double)
68 | } else if let string = try? container.decode(String.self) {
69 | self.init(string)
70 | } else if let array = try? container.decode([AnyDecodable].self) {
71 | self.init(array.map { $0.value })
72 | } else if let dictionary = try? container.decode([String: AnyDecodable].self) {
73 | self.init(dictionary.mapValues { $0.value })
74 | } else {
75 | throw DecodingError.dataCorruptedError(in: container, debugDescription: "AnyDecodable value cannot be decoded")
76 | }
77 | }
78 | }
79 |
80 | extension AnyDecodable: Equatable {
81 | public static func == (lhs: AnyDecodable, rhs: AnyDecodable) -> Bool {
82 | switch (lhs.value, rhs.value) {
83 | #if canImport(Foundation)
84 | case is (NSNull, NSNull), is (Void, Void):
85 | return true
86 | #endif
87 | case let (lhs as Bool, rhs as Bool):
88 | return lhs == rhs
89 | case let (lhs as Int, rhs as Int):
90 | return lhs == rhs
91 | case let (lhs as Int8, rhs as Int8):
92 | return lhs == rhs
93 | case let (lhs as Int16, rhs as Int16):
94 | return lhs == rhs
95 | case let (lhs as Int32, rhs as Int32):
96 | return lhs == rhs
97 | case let (lhs as Int64, rhs as Int64):
98 | return lhs == rhs
99 | case let (lhs as UInt, rhs as UInt):
100 | return lhs == rhs
101 | case let (lhs as UInt8, rhs as UInt8):
102 | return lhs == rhs
103 | case let (lhs as UInt16, rhs as UInt16):
104 | return lhs == rhs
105 | case let (lhs as UInt32, rhs as UInt32):
106 | return lhs == rhs
107 | case let (lhs as UInt64, rhs as UInt64):
108 | return lhs == rhs
109 | case let (lhs as Float, rhs as Float):
110 | return lhs == rhs
111 | case let (lhs as Double, rhs as Double):
112 | return lhs == rhs
113 | case let (lhs as String, rhs as String):
114 | return lhs == rhs
115 | case let (lhs as [String: AnyDecodable], rhs as [String: AnyDecodable]):
116 | return lhs == rhs
117 | case let (lhs as [AnyDecodable], rhs as [AnyDecodable]):
118 | return lhs == rhs
119 | default:
120 | return false
121 | }
122 | }
123 | }
124 |
125 | extension AnyDecodable: CustomStringConvertible {
126 | public var description: String {
127 | switch value {
128 | case is Void:
129 | return String(describing: nil as Any?)
130 | case let value as CustomStringConvertible:
131 | return value.description
132 | default:
133 | return String(describing: value)
134 | }
135 | }
136 | }
137 |
138 | extension AnyDecodable: CustomDebugStringConvertible {
139 | public var debugDescription: String {
140 | switch value {
141 | case let value as CustomDebugStringConvertible:
142 | return "AnyDecodable(\(value.debugDescription))"
143 | default:
144 | return "AnyDecodable(\(description))"
145 | }
146 | }
147 | }
148 |
149 | extension AnyDecodable: Hashable {
150 | public func hash(into hasher: inout Hasher) {
151 | switch value {
152 | case let value as Bool:
153 | hasher.combine(value)
154 | case let value as Int:
155 | hasher.combine(value)
156 | case let value as Int8:
157 | hasher.combine(value)
158 | case let value as Int16:
159 | hasher.combine(value)
160 | case let value as Int32:
161 | hasher.combine(value)
162 | case let value as Int64:
163 | hasher.combine(value)
164 | case let value as UInt:
165 | hasher.combine(value)
166 | case let value as UInt8:
167 | hasher.combine(value)
168 | case let value as UInt16:
169 | hasher.combine(value)
170 | case let value as UInt32:
171 | hasher.combine(value)
172 | case let value as UInt64:
173 | hasher.combine(value)
174 | case let value as Float:
175 | hasher.combine(value)
176 | case let value as Double:
177 | hasher.combine(value)
178 | case let value as String:
179 | hasher.combine(value)
180 | case let value as [String: AnyDecodable]:
181 | hasher.combine(value)
182 | case let value as [AnyDecodable]:
183 | hasher.combine(value)
184 | default:
185 | break
186 | }
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/Sources/AnyCodable/AnyEncodable.swift:
--------------------------------------------------------------------------------
1 | #if canImport(Foundation)
2 | import Foundation
3 | #endif
4 |
5 | /**
6 | A type-erased `Encodable` value.
7 |
8 | The `AnyEncodable` type forwards encoding responsibilities
9 | to an underlying value, hiding its specific underlying type.
10 |
11 | You can encode mixed-type values in dictionaries
12 | and other collections that require `Encodable` conformance
13 | by declaring their contained type to be `AnyEncodable`:
14 |
15 | let dictionary: [String: AnyEncodable] = [
16 | "boolean": true,
17 | "integer": 42,
18 | "double": 3.141592653589793,
19 | "string": "string",
20 | "array": [1, 2, 3],
21 | "nested": [
22 | "a": "alpha",
23 | "b": "bravo",
24 | "c": "charlie"
25 | ],
26 | "null": nil
27 | ]
28 |
29 | let encoder = JSONEncoder()
30 | let json = try! encoder.encode(dictionary)
31 | */
32 | @frozen public struct AnyEncodable: Encodable {
33 | public let value: Any
34 |
35 | public init(_ value: T?) {
36 | self.value = value ?? ()
37 | }
38 | }
39 |
40 | @usableFromInline
41 | protocol _AnyEncodable {
42 | var value: Any { get }
43 | init(_ value: T?)
44 | }
45 |
46 | extension AnyEncodable: _AnyEncodable {}
47 |
48 | // MARK: - Encodable
49 |
50 | extension _AnyEncodable {
51 | public func encode(to encoder: Encoder) throws {
52 | var container = encoder.singleValueContainer()
53 |
54 | switch value {
55 | #if canImport(Foundation)
56 | case is NSNull:
57 | try container.encodeNil()
58 | #endif
59 | case is Void:
60 | try container.encodeNil()
61 | case let bool as Bool:
62 | try container.encode(bool)
63 | case let int as Int:
64 | try container.encode(int)
65 | case let int8 as Int8:
66 | try container.encode(int8)
67 | case let int16 as Int16:
68 | try container.encode(int16)
69 | case let int32 as Int32:
70 | try container.encode(int32)
71 | case let int64 as Int64:
72 | try container.encode(int64)
73 | case let uint as UInt:
74 | try container.encode(uint)
75 | case let uint8 as UInt8:
76 | try container.encode(uint8)
77 | case let uint16 as UInt16:
78 | try container.encode(uint16)
79 | case let uint32 as UInt32:
80 | try container.encode(uint32)
81 | case let uint64 as UInt64:
82 | try container.encode(uint64)
83 | case let float as Float:
84 | try container.encode(float)
85 | case let double as Double:
86 | try container.encode(double)
87 | case let string as String:
88 | try container.encode(string)
89 | #if canImport(Foundation)
90 | case let number as NSNumber:
91 | try encode(nsnumber: number, into: &container)
92 | case let date as Date:
93 | try container.encode(date)
94 | case let url as URL:
95 | try container.encode(url)
96 | #endif
97 | case let array as [Any?]:
98 | try container.encode(array.map { AnyEncodable($0) })
99 | case let dictionary as [String: Any?]:
100 | try container.encode(dictionary.mapValues { AnyEncodable($0) })
101 | case let encodable as Encodable:
102 | try encodable.encode(to: encoder)
103 | default:
104 | let context = EncodingError.Context(codingPath: container.codingPath, debugDescription: "AnyEncodable value cannot be encoded")
105 | throw EncodingError.invalidValue(value, context)
106 | }
107 | }
108 |
109 | #if canImport(Foundation)
110 | private func encode(nsnumber: NSNumber, into container: inout SingleValueEncodingContainer) throws {
111 | switch Character(Unicode.Scalar(UInt8(nsnumber.objCType.pointee))) {
112 | case "B":
113 | try container.encode(nsnumber.boolValue)
114 | case "c":
115 | try container.encode(nsnumber.int8Value)
116 | case "s":
117 | try container.encode(nsnumber.int16Value)
118 | case "i", "l":
119 | try container.encode(nsnumber.int32Value)
120 | case "q":
121 | try container.encode(nsnumber.int64Value)
122 | case "C":
123 | try container.encode(nsnumber.uint8Value)
124 | case "S":
125 | try container.encode(nsnumber.uint16Value)
126 | case "I", "L":
127 | try container.encode(nsnumber.uint32Value)
128 | case "Q":
129 | try container.encode(nsnumber.uint64Value)
130 | case "f":
131 | try container.encode(nsnumber.floatValue)
132 | case "d":
133 | try container.encode(nsnumber.doubleValue)
134 | default:
135 | let context = EncodingError.Context(codingPath: container.codingPath, debugDescription: "NSNumber cannot be encoded because its type is not handled")
136 | throw EncodingError.invalidValue(nsnumber, context)
137 | }
138 | }
139 | #endif
140 | }
141 |
142 | extension AnyEncodable: Equatable {
143 | public static func == (lhs: AnyEncodable, rhs: AnyEncodable) -> Bool {
144 | switch (lhs.value, rhs.value) {
145 | case is (Void, Void):
146 | return true
147 | case let (lhs as Bool, rhs as Bool):
148 | return lhs == rhs
149 | case let (lhs as Int, rhs as Int):
150 | return lhs == rhs
151 | case let (lhs as Int8, rhs as Int8):
152 | return lhs == rhs
153 | case let (lhs as Int16, rhs as Int16):
154 | return lhs == rhs
155 | case let (lhs as Int32, rhs as Int32):
156 | return lhs == rhs
157 | case let (lhs as Int64, rhs as Int64):
158 | return lhs == rhs
159 | case let (lhs as UInt, rhs as UInt):
160 | return lhs == rhs
161 | case let (lhs as UInt8, rhs as UInt8):
162 | return lhs == rhs
163 | case let (lhs as UInt16, rhs as UInt16):
164 | return lhs == rhs
165 | case let (lhs as UInt32, rhs as UInt32):
166 | return lhs == rhs
167 | case let (lhs as UInt64, rhs as UInt64):
168 | return lhs == rhs
169 | case let (lhs as Float, rhs as Float):
170 | return lhs == rhs
171 | case let (lhs as Double, rhs as Double):
172 | return lhs == rhs
173 | case let (lhs as String, rhs as String):
174 | return lhs == rhs
175 | case let (lhs as [String: AnyEncodable], rhs as [String: AnyEncodable]):
176 | return lhs == rhs
177 | case let (lhs as [AnyEncodable], rhs as [AnyEncodable]):
178 | return lhs == rhs
179 | default:
180 | return false
181 | }
182 | }
183 | }
184 |
185 | extension AnyEncodable: CustomStringConvertible {
186 | public var description: String {
187 | switch value {
188 | case is Void:
189 | return String(describing: nil as Any?)
190 | case let value as CustomStringConvertible:
191 | return value.description
192 | default:
193 | return String(describing: value)
194 | }
195 | }
196 | }
197 |
198 | extension AnyEncodable: CustomDebugStringConvertible {
199 | public var debugDescription: String {
200 | switch value {
201 | case let value as CustomDebugStringConvertible:
202 | return "AnyEncodable(\(value.debugDescription))"
203 | default:
204 | return "AnyEncodable(\(description))"
205 | }
206 | }
207 | }
208 |
209 | extension AnyEncodable: ExpressibleByNilLiteral {}
210 | extension AnyEncodable: ExpressibleByBooleanLiteral {}
211 | extension AnyEncodable: ExpressibleByIntegerLiteral {}
212 | extension AnyEncodable: ExpressibleByFloatLiteral {}
213 | extension AnyEncodable: ExpressibleByStringLiteral {}
214 | extension AnyEncodable: ExpressibleByStringInterpolation {}
215 | extension AnyEncodable: ExpressibleByArrayLiteral {}
216 | extension AnyEncodable: ExpressibleByDictionaryLiteral {}
217 |
218 | extension _AnyEncodable {
219 | public init(nilLiteral _: ()) {
220 | self.init(nil as Any?)
221 | }
222 |
223 | public init(booleanLiteral value: Bool) {
224 | self.init(value)
225 | }
226 |
227 | public init(integerLiteral value: Int) {
228 | self.init(value)
229 | }
230 |
231 | public init(floatLiteral value: Double) {
232 | self.init(value)
233 | }
234 |
235 | public init(extendedGraphemeClusterLiteral value: String) {
236 | self.init(value)
237 | }
238 |
239 | public init(stringLiteral value: String) {
240 | self.init(value)
241 | }
242 |
243 | public init(arrayLiteral elements: Any...) {
244 | self.init(elements)
245 | }
246 |
247 | public init(dictionaryLiteral elements: (AnyHashable, Any)...) {
248 | self.init([AnyHashable: Any](elements, uniquingKeysWith: { first, _ in first }))
249 | }
250 | }
251 |
252 | extension AnyEncodable: Hashable {
253 | public func hash(into hasher: inout Hasher) {
254 | switch value {
255 | case let value as Bool:
256 | hasher.combine(value)
257 | case let value as Int:
258 | hasher.combine(value)
259 | case let value as Int8:
260 | hasher.combine(value)
261 | case let value as Int16:
262 | hasher.combine(value)
263 | case let value as Int32:
264 | hasher.combine(value)
265 | case let value as Int64:
266 | hasher.combine(value)
267 | case let value as UInt:
268 | hasher.combine(value)
269 | case let value as UInt8:
270 | hasher.combine(value)
271 | case let value as UInt16:
272 | hasher.combine(value)
273 | case let value as UInt32:
274 | hasher.combine(value)
275 | case let value as UInt64:
276 | hasher.combine(value)
277 | case let value as Float:
278 | hasher.combine(value)
279 | case let value as Double:
280 | hasher.combine(value)
281 | case let value as String:
282 | hasher.combine(value)
283 | case let value as [String: AnyEncodable]:
284 | hasher.combine(value)
285 | case let value as [AnyEncodable]:
286 | hasher.combine(value)
287 | default:
288 | break
289 | }
290 | }
291 | }
292 |
--------------------------------------------------------------------------------
/Tests/AnyCodableTests/AnyCodableTests.swift:
--------------------------------------------------------------------------------
1 | @testable import AnyCodable
2 | import XCTest
3 |
4 |
5 |
6 | class AnyCodableTests: XCTestCase {
7 |
8 | struct SomeCodable: Codable {
9 | var string: String
10 | var int: Int
11 | var bool: Bool
12 | var hasUnderscore: String
13 |
14 | enum CodingKeys: String,CodingKey {
15 | case string
16 | case int
17 | case bool
18 | case hasUnderscore = "has_underscore"
19 | }
20 | }
21 |
22 | func testJSONDecoding() throws {
23 | let json = """
24 | {
25 | "boolean": true,
26 | "integer": 42,
27 | "double": 3.141592653589793,
28 | "string": "string",
29 | "array": [1, 2, 3],
30 | "nested": {
31 | "a": "alpha",
32 | "b": "bravo",
33 | "c": "charlie"
34 | },
35 | "null": null
36 | }
37 | """.data(using: .utf8)!
38 |
39 | let decoder = JSONDecoder()
40 | let dictionary = try decoder.decode([String: AnyCodable].self, from: json)
41 |
42 | XCTAssertEqual(dictionary["boolean"]?.value as! Bool, true)
43 | XCTAssertEqual(dictionary["integer"]?.value as! Int, 42)
44 | XCTAssertEqual(dictionary["double"]?.value as! Double, 3.141592653589793, accuracy: 0.001)
45 | XCTAssertEqual(dictionary["string"]?.value as! String, "string")
46 | XCTAssertEqual(dictionary["array"]?.value as! [Int], [1, 2, 3])
47 | XCTAssertEqual(dictionary["nested"]?.value as! [String: String], ["a": "alpha", "b": "bravo", "c": "charlie"])
48 | XCTAssertEqual(dictionary["null"]?.value as! NSNull, NSNull())
49 | }
50 |
51 | func testJSONDecodingEquatable() throws {
52 | let json = """
53 | {
54 | "boolean": true,
55 | "integer": 42,
56 | "double": 3.141592653589793,
57 | "string": "string",
58 | "array": [1, 2, 3],
59 | "nested": {
60 | "a": "alpha",
61 | "b": "bravo",
62 | "c": "charlie"
63 | },
64 | "null": null
65 | }
66 | """.data(using: .utf8)!
67 |
68 | let decoder = JSONDecoder()
69 | let dictionary1 = try decoder.decode([String: AnyCodable].self, from: json)
70 | let dictionary2 = try decoder.decode([String: AnyCodable].self, from: json)
71 |
72 | XCTAssertEqual(dictionary1["boolean"], dictionary2["boolean"])
73 | XCTAssertEqual(dictionary1["integer"], dictionary2["integer"])
74 | XCTAssertEqual(dictionary1["double"], dictionary2["double"])
75 | XCTAssertEqual(dictionary1["string"], dictionary2["string"])
76 | XCTAssertEqual(dictionary1["array"], dictionary2["array"])
77 | XCTAssertEqual(dictionary1["nested"], dictionary2["nested"])
78 | XCTAssertEqual(dictionary1["null"], dictionary2["null"])
79 | }
80 |
81 | func testJSONEncoding() throws {
82 |
83 | let someCodable = AnyCodable(SomeCodable(string: "String", int: 100, bool: true, hasUnderscore: "another string"))
84 |
85 | let injectedValue = 1234
86 | let dictionary: [String: AnyCodable] = [
87 | "boolean": true,
88 | "integer": 42,
89 | "double": 3.141592653589793,
90 | "string": "string",
91 | "stringInterpolation": "string \(injectedValue)",
92 | "array": [1, 2, 3],
93 | "nested": [
94 | "a": "alpha",
95 | "b": "bravo",
96 | "c": "charlie",
97 | ],
98 | "someCodable": someCodable,
99 | "null": nil
100 | ]
101 |
102 | let encoder = JSONEncoder()
103 |
104 | let json = try encoder.encode(dictionary)
105 | let encodedJSONObject = try JSONSerialization.jsonObject(with: json, options: []) as! NSDictionary
106 |
107 | let expected = """
108 | {
109 | "boolean": true,
110 | "integer": 42,
111 | "double": 3.141592653589793,
112 | "string": "string",
113 | "stringInterpolation": "string 1234",
114 | "array": [1, 2, 3],
115 | "nested": {
116 | "a": "alpha",
117 | "b": "bravo",
118 | "c": "charlie"
119 | },
120 | "someCodable": {
121 | "string":"String",
122 | "int":100,
123 | "bool": true,
124 | "has_underscore":"another string"
125 | },
126 | "null": null
127 | }
128 | """.data(using: .utf8)!
129 | let expectedJSONObject = try JSONSerialization.jsonObject(with: expected, options: []) as! NSDictionary
130 |
131 | XCTAssertEqual(encodedJSONObject, expectedJSONObject)
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/Tests/AnyCodableTests/AnyDecodableTests.swift:
--------------------------------------------------------------------------------
1 | @testable import AnyCodable
2 | import XCTest
3 |
4 | class AnyDecodableTests: XCTestCase {
5 | func testJSONDecoding() throws {
6 | let json = """
7 | {
8 | "boolean": true,
9 | "integer": 42,
10 | "double": 3.141592653589793,
11 | "string": "string",
12 | "array": [1, 2, 3],
13 | "nested": {
14 | "a": "alpha",
15 | "b": "bravo",
16 | "c": "charlie"
17 | },
18 | "null": null
19 | }
20 | """.data(using: .utf8)!
21 |
22 | let decoder = JSONDecoder()
23 | let dictionary = try decoder.decode([String: AnyDecodable].self, from: json)
24 |
25 | XCTAssertEqual(dictionary["boolean"]?.value as! Bool, true)
26 | XCTAssertEqual(dictionary["integer"]?.value as! Int, 42)
27 | XCTAssertEqual(dictionary["double"]?.value as! Double, 3.141592653589793, accuracy: 0.001)
28 | XCTAssertEqual(dictionary["string"]?.value as! String, "string")
29 | XCTAssertEqual(dictionary["array"]?.value as! [Int], [1, 2, 3])
30 | XCTAssertEqual(dictionary["nested"]?.value as! [String: String], ["a": "alpha", "b": "bravo", "c": "charlie"])
31 | XCTAssertEqual(dictionary["null"]?.value as! NSNull, NSNull())
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Tests/AnyCodableTests/AnyEncodableTests.swift:
--------------------------------------------------------------------------------
1 | @testable import AnyCodable
2 | import XCTest
3 |
4 | class AnyEncodableTests: XCTestCase {
5 |
6 | struct SomeEncodable: Encodable {
7 | var string: String
8 | var int: Int
9 | var bool: Bool
10 | var hasUnderscore: String
11 |
12 | enum CodingKeys: String,CodingKey {
13 | case string
14 | case int
15 | case bool
16 | case hasUnderscore = "has_underscore"
17 | }
18 | }
19 |
20 | func testJSONEncoding() throws {
21 |
22 | let someEncodable = AnyEncodable(SomeEncodable(string: "String", int: 100, bool: true, hasUnderscore: "another string"))
23 |
24 | let dictionary: [String: AnyEncodable] = [
25 | "boolean": true,
26 | "integer": 42,
27 | "double": 3.141592653589793,
28 | "string": "string",
29 | "array": [1, 2, 3],
30 | "nested": [
31 | "a": "alpha",
32 | "b": "bravo",
33 | "c": "charlie",
34 | ],
35 | "someCodable": someEncodable,
36 | "null": nil
37 | ]
38 |
39 | let encoder = JSONEncoder()
40 |
41 | let json = try encoder.encode(dictionary)
42 | let encodedJSONObject = try JSONSerialization.jsonObject(with: json, options: []) as! NSDictionary
43 |
44 | let expected = """
45 | {
46 | "boolean": true,
47 | "integer": 42,
48 | "double": 3.141592653589793,
49 | "string": "string",
50 | "array": [1, 2, 3],
51 | "nested": {
52 | "a": "alpha",
53 | "b": "bravo",
54 | "c": "charlie"
55 | },
56 | "someCodable": {
57 | "string":"String",
58 | "int":100,
59 | "bool": true,
60 | "has_underscore":"another string"
61 | },
62 | "null": null
63 | }
64 | """.data(using: .utf8)!
65 | let expectedJSONObject = try JSONSerialization.jsonObject(with: expected, options: []) as! NSDictionary
66 |
67 | XCTAssertEqual(encodedJSONObject, expectedJSONObject)
68 | }
69 |
70 | func testEncodeNSNumber() throws {
71 | let dictionary: [String: NSNumber] = [
72 | "boolean": true,
73 | "char": -127,
74 | "int": -32767,
75 | "short": -32767,
76 | "long": -2147483647,
77 | "longlong": -9223372036854775807,
78 | "uchar": 255,
79 | "uint": 65535,
80 | "ushort": 65535,
81 | "ulong": 4294967295,
82 | "ulonglong": 18446744073709615,
83 | "double": 3.141592653589793,
84 | ]
85 |
86 | let encoder = JSONEncoder()
87 |
88 | let json = try encoder.encode(AnyEncodable(dictionary))
89 | let encodedJSONObject = try JSONSerialization.jsonObject(with: json, options: []) as! NSDictionary
90 |
91 | let expected = """
92 | {
93 | "boolean": true,
94 | "char": -127,
95 | "int": -32767,
96 | "short": -32767,
97 | "long": -2147483647,
98 | "longlong": -9223372036854775807,
99 | "uchar": 255,
100 | "uint": 65535,
101 | "ushort": 65535,
102 | "ulong": 4294967295,
103 | "ulonglong": 18446744073709615,
104 | "double": 3.141592653589793,
105 | }
106 | """.data(using: .utf8)!
107 | let expectedJSONObject = try JSONSerialization.jsonObject(with: expected, options: []) as! NSDictionary
108 |
109 | XCTAssertEqual(encodedJSONObject, expectedJSONObject)
110 | XCTAssert(encodedJSONObject["boolean"] is Bool)
111 |
112 | XCTAssert(encodedJSONObject["char"] is Int8)
113 | XCTAssert(encodedJSONObject["int"] is Int16)
114 | XCTAssert(encodedJSONObject["short"] is Int32)
115 | XCTAssert(encodedJSONObject["long"] is Int32)
116 | XCTAssert(encodedJSONObject["longlong"] is Int64)
117 |
118 | XCTAssert(encodedJSONObject["uchar"] is UInt8)
119 | XCTAssert(encodedJSONObject["uint"] is UInt16)
120 | XCTAssert(encodedJSONObject["ushort"] is UInt32)
121 | XCTAssert(encodedJSONObject["ulong"] is UInt32)
122 | XCTAssert(encodedJSONObject["ulonglong"] is UInt64)
123 |
124 | XCTAssert(encodedJSONObject["double"] is Double)
125 | }
126 |
127 | func testStringInterpolationEncoding() throws {
128 | let dictionary: [String: AnyEncodable] = [
129 | "boolean": "\(true)",
130 | "integer": "\(42)",
131 | "double": "\(3.141592653589793)",
132 | "string": "\("string")",
133 | "array": "\([1, 2, 3])",
134 | ]
135 |
136 | let encoder = JSONEncoder()
137 |
138 | let json = try encoder.encode(dictionary)
139 | let encodedJSONObject = try JSONSerialization.jsonObject(with: json, options: []) as! NSDictionary
140 |
141 | let expected = """
142 | {
143 | "boolean": "true",
144 | "integer": "42",
145 | "double": "3.141592653589793",
146 | "string": "string",
147 | "array": "[1, 2, 3]",
148 | }
149 | """.data(using: .utf8)!
150 | let expectedJSONObject = try JSONSerialization.jsonObject(with: expected, options: []) as! NSDictionary
151 |
152 | XCTAssertEqual(encodedJSONObject, expectedJSONObject)
153 | }
154 | }
155 |
--------------------------------------------------------------------------------