├── .gitignore
├── LICENSE
├── README.md
├── codable
├── .gitignore
├── FirestoreCodableSamples.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── swiftpm
│ │ │ └── Package.resolved
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── FirestoreCodableSamples.xcscheme
├── FirestoreCodableSamples
│ ├── App
│ │ └── FirestoreCodableSamplesApp.swift
│ ├── Assets.xcassets
│ │ ├── AccentColor.colorset
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── Info.plist
│ ├── Models
│ │ ├── Article.swift
│ │ ├── Book.swift
│ │ ├── BookWithCoverImages.swift
│ │ ├── BookWithGenre.swift
│ │ ├── BookWithTags.swift
│ │ ├── ColorEntry.swift
│ │ ├── Office.swift
│ │ └── ProgrammingLanguage.swift
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ ├── Screens
│ │ ├── CustomizeMappingScreen.swift
│ │ ├── ManuallyMappingSimpleTypesScreen.swift
│ │ ├── MappingArraysScreen.swift
│ │ ├── MappingArraysWithNestedTypesScreen.swift
│ │ ├── MappingColorsScreen.swift
│ │ ├── MappingCustomTypesScreen.swift
│ │ ├── MappingEnumsScreen.swift
│ │ ├── MappingGeoPointsScreen.swift
│ │ ├── MappingSimpleTypesScreen.swift
│ │ └── MenuScreen.swift
│ └── Utilities
│ │ ├── Color+Codable.swift
│ │ ├── Color+FontColor.swift
│ │ └── SampleScreen.swift
├── data
│ ├── auth_export
│ │ ├── accounts.json
│ │ └── config.json
│ ├── firebase-export-metadata.json
│ └── firestore_export
│ │ ├── all_namespaces
│ │ └── all_kinds
│ │ │ ├── all_namespaces_all_kinds.export_metadata
│ │ │ └── output-0
│ │ └── firestore_export.overall_export_metadata
├── firebase.json
├── firestore.indexes.json
├── firestore.rules
└── start.sh
└── images
├── header.png
├── logo.png
├── swift-logo-512.png
└── swift-logo.png
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/xcode,swift,android,swiftpm,firebase,cocoapods,androidstudio,visualstudiocode,swiftpackagemanager
3 | # Edit at https://www.gitignore.io/?templates=xcode,swift,android,swiftpm,firebase,cocoapods,androidstudio,visualstudiocode,swiftpackagemanager
4 |
5 | ### Android ###
6 | # Built application files
7 | *.apk
8 | *.ap_
9 | *.aab
10 |
11 | # Files for the ART/Dalvik VM
12 | *.dex
13 |
14 | # Java class files
15 | *.class
16 |
17 | # Generated files
18 | bin/
19 | gen/
20 | out/
21 | release/
22 |
23 | # Gradle files
24 | .gradle/
25 | build/
26 |
27 | # Local configuration file (sdk path, etc)
28 | local.properties
29 |
30 | # Proguard folder generated by Eclipse
31 | proguard/
32 |
33 | # Log Files
34 | *.log
35 |
36 | # Android Studio Navigation editor temp files
37 | .navigation/
38 |
39 | # Android Studio captures folder
40 | captures/
41 |
42 | # IntelliJ
43 | *.iml
44 | .idea/workspace.xml
45 | .idea/tasks.xml
46 | .idea/gradle.xml
47 | .idea/assetWizardSettings.xml
48 | .idea/dictionaries
49 | .idea/libraries
50 | # Android Studio 3 in .gitignore file.
51 | .idea/caches
52 | .idea/modules.xml
53 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you
54 | .idea/navEditor.xml
55 |
56 | # Keystore files
57 | # Uncomment the following lines if you do not want to check your keystore files in.
58 | #*.jks
59 | #*.keystore
60 |
61 | # External native build folder generated in Android Studio 2.2 and later
62 | .externalNativeBuild
63 |
64 | # Google Services (e.g. APIs or Firebase)
65 | # google-services.json
66 |
67 | # Freeline
68 | freeline.py
69 | freeline/
70 | freeline_project_description.json
71 |
72 | # fastlane
73 | fastlane/report.xml
74 | fastlane/Preview.html
75 | fastlane/screenshots
76 | fastlane/test_output
77 | fastlane/readme.md
78 |
79 | # Version control
80 | vcs.xml
81 |
82 | # lint
83 | lint/intermediates/
84 | lint/generated/
85 | lint/outputs/
86 | lint/tmp/
87 | # lint/reports/
88 |
89 | ### Android Patch ###
90 | gen-external-apklibs
91 | output.json
92 |
93 | # Replacement of .externalNativeBuild directories introduced
94 | # with Android Studio 3.5.
95 | .cxx/
96 |
97 | ### CocoaPods ###
98 | ## CocoaPods GitIgnore Template
99 |
100 | # CocoaPods - Only use to conserve bandwidth / Save time on Pushing
101 | # - Also handy if you have a large number of dependant pods
102 | # - AS PER https://guides.cocoapods.org/using/using-cocoapods.html NEVER IGNORE THE LOCK FILE
103 | Pods/
104 |
105 | ### Firebase ###
106 | .idea
107 | **/node_modules/*
108 | **/.firebaserc
109 |
110 | ### Firebase Patch ###
111 | .runtimeconfig.json
112 | .firebase/
113 |
114 | ### Swift ###
115 | # Xcode
116 | #
117 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
118 |
119 | ## Build generated
120 | DerivedData/
121 |
122 | ## Various settings
123 | *.pbxuser
124 | !default.pbxuser
125 | *.mode1v3
126 | !default.mode1v3
127 | *.mode2v3
128 | !default.mode2v3
129 | *.perspectivev3
130 | !default.perspectivev3
131 | xcuserdata/
132 |
133 | ## Other
134 | *.moved-aside
135 | *.xccheckout
136 | *.xcscmblueprint
137 |
138 | ## Obj-C/Swift specific
139 | *.hmap
140 | *.ipa
141 | *.dSYM.zip
142 | *.dSYM
143 |
144 | ## Playgrounds
145 | timeline.xctimeline
146 | playground.xcworkspace
147 |
148 | # Swift Package Manager
149 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
150 | # Packages/
151 | # Package.pins
152 | # Package.resolved
153 | .build/
154 | # Add this line if you want to avoid checking in Xcode SPM integration.
155 | # .swiftpm/xcode
156 |
157 | # CocoaPods
158 | # We recommend against adding the Pods directory to your .gitignore. However
159 | # you should judge for yourself, the pros and cons are mentioned at:
160 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
161 | # Pods/
162 | # Add this line if you want to avoid checking in source code from the Xcode workspace
163 | # *.xcworkspace
164 |
165 | # Carthage
166 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
167 | # Carthage/Checkouts
168 |
169 | Carthage/Build
170 |
171 | # Accio dependency management
172 | Dependencies/
173 | .accio/
174 |
175 | # fastlane
176 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
177 | # screenshots whenever they are needed.
178 | # For more information about the recommended setup visit:
179 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
180 |
181 | fastlane/screenshots/**/*.png
182 |
183 | # Code Injection
184 | # After new code Injection tools there's a generated folder /iOSInjectionProject
185 | # https://github.com/johnno1962/injectionforxcode
186 |
187 | iOSInjectionProject/
188 |
189 | ### SwiftPackageManager ###
190 | Packages
191 | xcuserdata
192 |
193 |
194 | ### SwiftPM ###
195 |
196 |
197 | ### VisualStudioCode ###
198 | .vscode/*
199 | !.vscode/settings.json
200 | !.vscode/tasks.json
201 | !.vscode/launch.json
202 | !.vscode/extensions.json
203 |
204 | ### VisualStudioCode Patch ###
205 | # Ignore all local history of files
206 | .history
207 |
208 | ### Xcode ###
209 | # Xcode
210 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
211 |
212 | ## User settings
213 |
214 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
215 |
216 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
217 |
218 | ## Xcode Patch
219 | *.xcodeproj/*
220 | !*.xcodeproj/project.pbxproj
221 | !*.xcodeproj/xcshareddata/
222 | !*.xcworkspace/contents.xcworkspacedata
223 | /*.gcno
224 |
225 | ### Xcode Patch ###
226 | **/xcshareddata/WorkspaceSettings.xcsettings
227 |
228 | ### AndroidStudio ###
229 | # Covers files to be ignored for android development using Android Studio.
230 |
231 | # Built application files
232 |
233 | # Files for the ART/Dalvik VM
234 |
235 | # Java class files
236 |
237 | # Generated files
238 |
239 | # Gradle files
240 | .gradle
241 |
242 | # Signing files
243 | .signing/
244 |
245 | # Local configuration file (sdk path, etc)
246 |
247 | # Proguard folder generated by Eclipse
248 |
249 | # Log Files
250 |
251 | # Android Studio
252 | /*/build/
253 | /*/local.properties
254 | /*/out
255 | /*/*/build
256 | /*/*/production
257 | *.ipr
258 | *~
259 | *.swp
260 |
261 | # Android Patch
262 |
263 | # External native build folder generated in Android Studio 2.2 and later
264 |
265 | # NDK
266 | obj/
267 |
268 | # IntelliJ IDEA
269 | *.iws
270 | /out/
271 |
272 | # User-specific configurations
273 | .idea/caches/
274 | .idea/libraries/
275 | .idea/shelf/
276 | .idea/.name
277 | .idea/compiler.xml
278 | .idea/copyright/profiles_settings.xml
279 | .idea/encodings.xml
280 | .idea/misc.xml
281 | .idea/scopes/scope_settings.xml
282 | .idea/vcs.xml
283 | .idea/jsLibraryMappings.xml
284 | .idea/datasources.xml
285 | .idea/dataSources.ids
286 | .idea/sqlDataSources.xml
287 | .idea/dynamic.xml
288 | .idea/uiDesigner.xml
289 |
290 | # OS-specific files
291 | .DS_Store
292 | .DS_Store?
293 | ._*
294 | .Spotlight-V100
295 | .Trashes
296 | ehthumbs.db
297 | Thumbs.db
298 |
299 | # Legacy Eclipse project files
300 | .classpath
301 | .project
302 | .cproject
303 | .settings/
304 |
305 | # Mobile Tools for Java (J2ME)
306 | .mtj.tmp/
307 |
308 | # Package Files #
309 | *.war
310 | *.ear
311 |
312 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml)
313 | hs_err_pid*
314 |
315 | ## Plugin-specific files:
316 |
317 | # mpeltonen/sbt-idea plugin
318 | .idea_modules/
319 |
320 | # JIRA plugin
321 | atlassian-ide-plugin.xml
322 |
323 | # Mongo Explorer plugin
324 | .idea/mongoSettings.xml
325 |
326 | # Crashlytics plugin (for Android Studio and IntelliJ)
327 | com_crashlytics_export_strings.xml
328 | crashlytics.properties
329 | crashlytics-build.properties
330 | fabric.properties
331 |
332 | ### AndroidStudio Patch ###
333 |
334 | !/gradle/wrapper/gradle-wrapper.jar
335 |
336 | # End of https://www.gitignore.io/api/xcode,swift,android,swiftpm,firebase,cocoapods,androidstudio,visualstudiocode,swiftpackagemanager
337 | GoogleService-Info.plist
338 | /archive
339 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | [![Contributors][contributors-shield]][contributors-url]
3 | [![Forks][forks-shield]][forks-url]
4 | [![Stargazers][stars-shield]][stars-url]
5 | [![Issues][issues-shield]][issues-url]
6 | [![Discussions][discussions-shield]][discussions-url]
7 | [![Feature Requests][featurerequest-shield]][featurerequest-url]
8 | [![License][license-shield]][license-url]
9 | [![LinkedIn][linkedin-shield]][linkedin-url]
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
Swift & Firestore - The Comprehensive Guide
19 |
20 |
21 | Code for my series about Firestore and Swift
22 |
23 | Read the article »
24 |
25 |
26 | Report Bug
27 | ·
28 | Request Feature
29 |
30 |
31 |
32 |
33 |
34 |
35 | Table of Contents
36 |
37 | -
38 | About The Project
39 |
40 | -
41 | Getting Started
42 |
47 |
48 | - Contributing
49 | - License
50 |
51 |
52 |
53 |
54 | ## About
55 |
56 | This project shows how to use Swift's Codable API with Firestore.
57 |
58 |
59 |
60 | ## Getting Started
61 |
62 | ### Prerequisites
63 |
64 | To run this demo, you will need:
65 | * Xcode 12.x
66 | * Firebase CLI
67 |
68 | ### Installation
69 |
70 | 1. Install Xcode (see https://developer.apple.com/xcode/)
71 | 2. Install the Firebase CLI (see https://firebase.google.com/docs/cli)
72 |
73 | ### Running
74 |
75 | You won't have to set up a Firebase project to run this sample, as it makes use of the local emulator suite.
76 |
77 | 1. First, launch the Firebase Emulator suite:
78 | ```bash
79 | $ ./start.sh
80 | ```
81 | 1. Open the project in Xcode
82 | ```bash
83 | $ xed .
84 | ```
85 | 1. Run the iOS project on a local Simulator (so it can connect to the Firebase Emulator)
86 |
87 |
88 |
89 | ## Contributing
90 |
91 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.
92 |
93 | 1. Fork the Project
94 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
95 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
96 | 4. Push to the Branch (`git push origin feature/AmazingFeature`)
97 | 5. Open a Pull Request
98 |
99 |
100 |
101 |
102 | ## License
103 |
104 | Distributed under the Apache 2 License. See `LICENSE` for more information.
105 |
106 |
107 |
108 | [contributors-shield]: https://img.shields.io/github/contributors/peterfriese/Swift-Firestore-Guide.svg?style=flat-square
109 | [contributors-url]: https://github.com/peterfriese/Swift-Firestore-Guide/graphs/contributors
110 | [forks-shield]: https://img.shields.io/github/forks/peterfriese/Swift-Firestore-Guide.svg?style=flat-square
111 | [forks-url]: https://github.com/peterfriese/Swift-Firestore-Guide/network/members
112 | [stars-shield]: https://img.shields.io/github/stars/peterfriese/Swift-Firestore-Guide.svg?style=flat-square
113 | [stars-url]: https://github.com/peterfriese/Swift-Firestore-Guide/stargazers
114 | [issues-shield]: https://img.shields.io/github/issues/peterfriese/Swift-Firestore-Guide.svg?style=flat-square
115 | [issues-url]: https://github.com/peterfriese/Swift-Firestore-Guide/issues
116 | [license-shield]: https://img.shields.io/github/license/peterfriese/Swift-Firestore-Guide.svg?style=flat-square
117 | [license-url]: https://github.com/peterfriese/Swift-Firestore-Guide/blob/master/LICENSE.txt
118 |
119 | [linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=flat-square&logo=linkedin&colorB=555
120 | [linkedin-url]: https://www.linkedin.com/in/peterfriese
121 | [product-screenshot]: images/screenshot.png
122 |
123 | [swift-shield]: https://img.shields.io/badge/swift-5.3-FA7343?logo=swift&color=FA7343&style=flat-square
124 | [swift-url]: https://swift.org
125 |
126 | [xcode-shield]: https://img.shields.io/badge/xcode-12.5_beta-1575F9?logo=Xcode&style=flat-square
127 | [xcode-url]: https://developer.apple.com/xcode/
128 |
129 | [featurerequest-url]: https://github.com/peterfriese/Swift-Firestore-Guide/issues/new?assignees=&labels=type%3A+feature+request&template=feature_request.md
130 | [featurerequest-shield]: https://img.shields.io/github/issues/peterfriese/Swift-Firestore-Guide/feature-request?logo=github&style=flat-square
131 | [discussions-url]: https://github.com/peterfriese/Swift-Firestore-Guide/discussions
132 | [discussions-shield]: https://img.shields.io/badge/discussions-brightgreen?logo=github&style=flat-square
133 |
--------------------------------------------------------------------------------
/codable/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | firebase-debug.log*
8 | firebase-debug.*.log*
9 |
10 | # Firebase cache
11 | .firebase/
12 |
13 | # Firebase config
14 |
15 | # Uncomment this if you'd like others to create their own Firebase project.
16 | # For a team working on the same Firebase project(s), it is recommended to leave
17 | # it commented so all members can deploy to the same project(s) in .firebaserc.
18 | # .firebaserc
19 |
20 | # Runtime data
21 | pids
22 | *.pid
23 | *.seed
24 | *.pid.lock
25 |
26 | # Directory for instrumented libs generated by jscoverage/JSCover
27 | lib-cov
28 |
29 | # Coverage directory used by tools like istanbul
30 | coverage
31 |
32 | # nyc test coverage
33 | .nyc_output
34 |
35 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
36 | .grunt
37 |
38 | # Bower dependency directory (https://bower.io/)
39 | bower_components
40 |
41 | # node-waf configuration
42 | .lock-wscript
43 |
44 | # Compiled binary addons (http://nodejs.org/api/addons.html)
45 | build/Release
46 |
47 | # Dependency directories
48 | node_modules/
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Optional REPL history
57 | .node_repl_history
58 |
59 | # Output of 'npm pack'
60 | *.tgz
61 |
62 | # Yarn Integrity file
63 | .yarn-integrity
64 |
65 | # dotenv environment variables file
66 | .env
67 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 52;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 88014F1C28F40D1F0081CB49 /* FirebaseFirestoreSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 88014F1B28F40D1F0081CB49 /* FirebaseFirestoreSwift */; };
11 | 88014F1E28F412560081CB49 /* Office.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88014F1D28F412560081CB49 /* Office.swift */; };
12 | 88014F2028F412AF0081CB49 /* Article.swift in Sources */ = {isa = PBXBuildFile; fileRef = 88014F1F28F412AF0081CB49 /* Article.swift */; };
13 | 8805D39425D49A7300851C96 /* FirebaseFirestore in Frameworks */ = {isa = PBXBuildFile; productRef = 8805D39325D49A7300851C96 /* FirebaseFirestore */; };
14 | 884040DB27E3827D000B515A /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 884040DA27E3827D000B515A /* FirebaseAuth */; };
15 | 8845BD7A25C46965002AEC45 /* FirestoreCodableSamplesApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8845BD7925C46965002AEC45 /* FirestoreCodableSamplesApp.swift */; };
16 | 8845BD7E25C46967002AEC45 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8845BD7D25C46967002AEC45 /* Assets.xcassets */; };
17 | 8845BD8125C46967002AEC45 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8845BD8025C46967002AEC45 /* Preview Assets.xcassets */; };
18 | 8845C98526020142008F2FA2 /* MenuScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8845C98426020142008F2FA2 /* MenuScreen.swift */; };
19 | 8845C98726020601008F2FA2 /* SampleScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8845C98626020601008F2FA2 /* SampleScreen.swift */; };
20 | 8845C98A26020FA3008F2FA2 /* MappingSimpleTypesScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8845C98926020FA3008F2FA2 /* MappingSimpleTypesScreen.swift */; };
21 | 8845C98C260346A9008F2FA2 /* ManuallyMappingSimpleTypesScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8845C98B260346A9008F2FA2 /* ManuallyMappingSimpleTypesScreen.swift */; };
22 | 8845C98E260383C6008F2FA2 /* BookWithGenre.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8845C98D260383C6008F2FA2 /* BookWithGenre.swift */; };
23 | 8845C99026038433008F2FA2 /* MappingArraysScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8845C98F26038433008F2FA2 /* MappingArraysScreen.swift */; };
24 | 8845C9922603A15A008F2FA2 /* BookWithTags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8845C9912603A15A008F2FA2 /* BookWithTags.swift */; };
25 | 8845C9942603A286008F2FA2 /* MappingArraysWithNestedTypesScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8845C9932603A286008F2FA2 /* MappingArraysWithNestedTypesScreen.swift */; };
26 | 8845C9962603A74C008F2FA2 /* BookWithCoverImages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8845C9952603A74C008F2FA2 /* BookWithCoverImages.swift */; };
27 | 8845C9982603C853008F2FA2 /* MappingCustomTypesScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8845C9972603C853008F2FA2 /* MappingCustomTypesScreen.swift */; };
28 | 8845C99A2603E733008F2FA2 /* Color+Codable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8845C9992603E733008F2FA2 /* Color+Codable.swift */; };
29 | 8845C99F2604C064008F2FA2 /* Color+FontColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8845C99E2604C064008F2FA2 /* Color+FontColor.swift */; };
30 | 8845C9A12604C5AE008F2FA2 /* MappingColorsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8845C9A02604C5AD008F2FA2 /* MappingColorsScreen.swift */; };
31 | 8845C9A32604ECA0008F2FA2 /* ProgrammingLanguage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8845C9A22604ECA0008F2FA2 /* ProgrammingLanguage.swift */; };
32 | 8845C9A526050395008F2FA2 /* CustomizeMappingScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8845C9A426050395008F2FA2 /* CustomizeMappingScreen.swift */; };
33 | 8845C9A7260503D7008F2FA2 /* ColorEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8845C9A6260503D7008F2FA2 /* ColorEntry.swift */; };
34 | 885435F2260897CC006188D9 /* MappingGeoPointsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 885435F1260897CC006188D9 /* MappingGeoPointsScreen.swift */; };
35 | 887A4A802608EA7E009ACBB1 /* MappingEnumsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 887A4A7F2608EA7E009ACBB1 /* MappingEnumsScreen.swift */; };
36 | 887DD2B425C47A23003F7A99 /* Book.swift in Sources */ = {isa = PBXBuildFile; fileRef = 887DD2B325C47A23003F7A99 /* Book.swift */; };
37 | 88A294A02804AC0900B8F266 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 88A2949F2804AC0900B8F266 /* GoogleService-Info.plist */; };
38 | /* End PBXBuildFile section */
39 |
40 | /* Begin PBXFileReference section */
41 | 88014F1D28F412560081CB49 /* Office.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Office.swift; sourceTree = ""; };
42 | 88014F1F28F412AF0081CB49 /* Article.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Article.swift; sourceTree = ""; };
43 | 8845BD7625C46965002AEC45 /* FirestoreCodableSamples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FirestoreCodableSamples.app; sourceTree = BUILT_PRODUCTS_DIR; };
44 | 8845BD7925C46965002AEC45 /* FirestoreCodableSamplesApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirestoreCodableSamplesApp.swift; sourceTree = ""; };
45 | 8845BD7D25C46967002AEC45 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
46 | 8845BD8025C46967002AEC45 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
47 | 8845BD8225C46967002AEC45 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
48 | 8845C98426020142008F2FA2 /* MenuScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuScreen.swift; sourceTree = ""; };
49 | 8845C98626020601008F2FA2 /* SampleScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleScreen.swift; sourceTree = ""; };
50 | 8845C98926020FA3008F2FA2 /* MappingSimpleTypesScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MappingSimpleTypesScreen.swift; sourceTree = ""; };
51 | 8845C98B260346A9008F2FA2 /* ManuallyMappingSimpleTypesScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManuallyMappingSimpleTypesScreen.swift; sourceTree = ""; };
52 | 8845C98D260383C6008F2FA2 /* BookWithGenre.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookWithGenre.swift; sourceTree = ""; };
53 | 8845C98F26038433008F2FA2 /* MappingArraysScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MappingArraysScreen.swift; sourceTree = ""; };
54 | 8845C9912603A15A008F2FA2 /* BookWithTags.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookWithTags.swift; sourceTree = ""; };
55 | 8845C9932603A286008F2FA2 /* MappingArraysWithNestedTypesScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MappingArraysWithNestedTypesScreen.swift; sourceTree = ""; };
56 | 8845C9952603A74C008F2FA2 /* BookWithCoverImages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookWithCoverImages.swift; sourceTree = ""; };
57 | 8845C9972603C853008F2FA2 /* MappingCustomTypesScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MappingCustomTypesScreen.swift; sourceTree = ""; };
58 | 8845C9992603E733008F2FA2 /* Color+Codable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Codable.swift"; sourceTree = ""; };
59 | 8845C99E2604C064008F2FA2 /* Color+FontColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+FontColor.swift"; sourceTree = ""; };
60 | 8845C9A02604C5AD008F2FA2 /* MappingColorsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MappingColorsScreen.swift; sourceTree = ""; };
61 | 8845C9A22604ECA0008F2FA2 /* ProgrammingLanguage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgrammingLanguage.swift; sourceTree = ""; };
62 | 8845C9A426050395008F2FA2 /* CustomizeMappingScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomizeMappingScreen.swift; sourceTree = ""; };
63 | 8845C9A6260503D7008F2FA2 /* ColorEntry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorEntry.swift; sourceTree = ""; };
64 | 885435F1260897CC006188D9 /* MappingGeoPointsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MappingGeoPointsScreen.swift; sourceTree = ""; };
65 | 887A4A7F2608EA7E009ACBB1 /* MappingEnumsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MappingEnumsScreen.swift; sourceTree = ""; };
66 | 887DD2B325C47A23003F7A99 /* Book.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Book.swift; sourceTree = ""; };
67 | 88A2949F2804AC0900B8F266 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; };
68 | /* End PBXFileReference section */
69 |
70 | /* Begin PBXFrameworksBuildPhase section */
71 | 8845BD7325C46965002AEC45 /* Frameworks */ = {
72 | isa = PBXFrameworksBuildPhase;
73 | buildActionMask = 2147483647;
74 | files = (
75 | 8805D39425D49A7300851C96 /* FirebaseFirestore in Frameworks */,
76 | 884040DB27E3827D000B515A /* FirebaseAuth in Frameworks */,
77 | 88014F1C28F40D1F0081CB49 /* FirebaseFirestoreSwift in Frameworks */,
78 | );
79 | runOnlyForDeploymentPostprocessing = 0;
80 | };
81 | /* End PBXFrameworksBuildPhase section */
82 |
83 | /* Begin PBXGroup section */
84 | 8845BD6D25C46965002AEC45 = {
85 | isa = PBXGroup;
86 | children = (
87 | 8845BD7825C46965002AEC45 /* FirestoreCodableSamples */,
88 | 8845BD7725C46965002AEC45 /* Products */,
89 | 887DD2AD25C479FD003F7A99 /* Frameworks */,
90 | );
91 | sourceTree = "";
92 | };
93 | 8845BD7725C46965002AEC45 /* Products */ = {
94 | isa = PBXGroup;
95 | children = (
96 | 8845BD7625C46965002AEC45 /* FirestoreCodableSamples.app */,
97 | );
98 | name = Products;
99 | sourceTree = "";
100 | };
101 | 8845BD7825C46965002AEC45 /* FirestoreCodableSamples */ = {
102 | isa = PBXGroup;
103 | children = (
104 | 8845C98826020F68008F2FA2 /* Utilities */,
105 | 8845C98226020033008F2FA2 /* App */,
106 | 887DD2B125C47A14003F7A99 /* Models */,
107 | 8845C98326020127008F2FA2 /* Screens */,
108 | 8845BD7D25C46967002AEC45 /* Assets.xcassets */,
109 | 8845BD8225C46967002AEC45 /* Info.plist */,
110 | 88A2949F2804AC0900B8F266 /* GoogleService-Info.plist */,
111 | 8845BD7F25C46967002AEC45 /* Preview Content */,
112 | );
113 | path = FirestoreCodableSamples;
114 | sourceTree = "";
115 | };
116 | 8845BD7F25C46967002AEC45 /* Preview Content */ = {
117 | isa = PBXGroup;
118 | children = (
119 | 8845BD8025C46967002AEC45 /* Preview Assets.xcassets */,
120 | );
121 | path = "Preview Content";
122 | sourceTree = "";
123 | };
124 | 8845C98226020033008F2FA2 /* App */ = {
125 | isa = PBXGroup;
126 | children = (
127 | 8845BD7925C46965002AEC45 /* FirestoreCodableSamplesApp.swift */,
128 | );
129 | path = App;
130 | sourceTree = "";
131 | };
132 | 8845C98326020127008F2FA2 /* Screens */ = {
133 | isa = PBXGroup;
134 | children = (
135 | 8845C98426020142008F2FA2 /* MenuScreen.swift */,
136 | 8845C98B260346A9008F2FA2 /* ManuallyMappingSimpleTypesScreen.swift */,
137 | 8845C98926020FA3008F2FA2 /* MappingSimpleTypesScreen.swift */,
138 | 8845C9972603C853008F2FA2 /* MappingCustomTypesScreen.swift */,
139 | 8845C98F26038433008F2FA2 /* MappingArraysScreen.swift */,
140 | 8845C9932603A286008F2FA2 /* MappingArraysWithNestedTypesScreen.swift */,
141 | 8845C9A02604C5AD008F2FA2 /* MappingColorsScreen.swift */,
142 | 8845C9A426050395008F2FA2 /* CustomizeMappingScreen.swift */,
143 | 885435F1260897CC006188D9 /* MappingGeoPointsScreen.swift */,
144 | 887A4A7F2608EA7E009ACBB1 /* MappingEnumsScreen.swift */,
145 | );
146 | path = Screens;
147 | sourceTree = "";
148 | };
149 | 8845C98826020F68008F2FA2 /* Utilities */ = {
150 | isa = PBXGroup;
151 | children = (
152 | 8845C98626020601008F2FA2 /* SampleScreen.swift */,
153 | 8845C9992603E733008F2FA2 /* Color+Codable.swift */,
154 | 8845C99E2604C064008F2FA2 /* Color+FontColor.swift */,
155 | );
156 | path = Utilities;
157 | sourceTree = "";
158 | };
159 | 887DD2AD25C479FD003F7A99 /* Frameworks */ = {
160 | isa = PBXGroup;
161 | children = (
162 | );
163 | name = Frameworks;
164 | sourceTree = "";
165 | };
166 | 887DD2B125C47A14003F7A99 /* Models */ = {
167 | isa = PBXGroup;
168 | children = (
169 | 887DD2B325C47A23003F7A99 /* Book.swift */,
170 | 8845C98D260383C6008F2FA2 /* BookWithGenre.swift */,
171 | 8845C9912603A15A008F2FA2 /* BookWithTags.swift */,
172 | 8845C9952603A74C008F2FA2 /* BookWithCoverImages.swift */,
173 | 8845C9A22604ECA0008F2FA2 /* ProgrammingLanguage.swift */,
174 | 8845C9A6260503D7008F2FA2 /* ColorEntry.swift */,
175 | 88014F1D28F412560081CB49 /* Office.swift */,
176 | 88014F1F28F412AF0081CB49 /* Article.swift */,
177 | );
178 | path = Models;
179 | sourceTree = "";
180 | };
181 | /* End PBXGroup section */
182 |
183 | /* Begin PBXNativeTarget section */
184 | 8845BD7525C46965002AEC45 /* FirestoreCodableSamples */ = {
185 | isa = PBXNativeTarget;
186 | buildConfigurationList = 8845BD8525C46967002AEC45 /* Build configuration list for PBXNativeTarget "FirestoreCodableSamples" */;
187 | buildPhases = (
188 | 8845BD7225C46965002AEC45 /* Sources */,
189 | 8845BD7325C46965002AEC45 /* Frameworks */,
190 | 8845BD7425C46965002AEC45 /* Resources */,
191 | );
192 | buildRules = (
193 | );
194 | dependencies = (
195 | );
196 | name = FirestoreCodableSamples;
197 | packageProductDependencies = (
198 | 8805D39325D49A7300851C96 /* FirebaseFirestore */,
199 | 884040DA27E3827D000B515A /* FirebaseAuth */,
200 | 88014F1B28F40D1F0081CB49 /* FirebaseFirestoreSwift */,
201 | );
202 | productName = FirestoreCodableSamples;
203 | productReference = 8845BD7625C46965002AEC45 /* FirestoreCodableSamples.app */;
204 | productType = "com.apple.product-type.application";
205 | };
206 | /* End PBXNativeTarget section */
207 |
208 | /* Begin PBXProject section */
209 | 8845BD6E25C46965002AEC45 /* Project object */ = {
210 | isa = PBXProject;
211 | attributes = {
212 | LastSwiftUpdateCheck = 1240;
213 | LastUpgradeCheck = 1240;
214 | TargetAttributes = {
215 | 8845BD7525C46965002AEC45 = {
216 | CreatedOnToolsVersion = 12.4;
217 | };
218 | };
219 | };
220 | buildConfigurationList = 8845BD7125C46965002AEC45 /* Build configuration list for PBXProject "FirestoreCodableSamples" */;
221 | compatibilityVersion = "Xcode 9.3";
222 | developmentRegion = en;
223 | hasScannedForEncodings = 0;
224 | knownRegions = (
225 | en,
226 | Base,
227 | );
228 | mainGroup = 8845BD6D25C46965002AEC45;
229 | packageReferences = (
230 | 8805D39225D49A7300851C96 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */,
231 | );
232 | productRefGroup = 8845BD7725C46965002AEC45 /* Products */;
233 | projectDirPath = "";
234 | projectRoot = "";
235 | targets = (
236 | 8845BD7525C46965002AEC45 /* FirestoreCodableSamples */,
237 | );
238 | };
239 | /* End PBXProject section */
240 |
241 | /* Begin PBXResourcesBuildPhase section */
242 | 8845BD7425C46965002AEC45 /* Resources */ = {
243 | isa = PBXResourcesBuildPhase;
244 | buildActionMask = 2147483647;
245 | files = (
246 | 88A294A02804AC0900B8F266 /* GoogleService-Info.plist in Resources */,
247 | 8845BD8125C46967002AEC45 /* Preview Assets.xcassets in Resources */,
248 | 8845BD7E25C46967002AEC45 /* Assets.xcassets in Resources */,
249 | );
250 | runOnlyForDeploymentPostprocessing = 0;
251 | };
252 | /* End PBXResourcesBuildPhase section */
253 |
254 | /* Begin PBXSourcesBuildPhase section */
255 | 8845BD7225C46965002AEC45 /* Sources */ = {
256 | isa = PBXSourcesBuildPhase;
257 | buildActionMask = 2147483647;
258 | files = (
259 | 88014F2028F412AF0081CB49 /* Article.swift in Sources */,
260 | 8845C98E260383C6008F2FA2 /* BookWithGenre.swift in Sources */,
261 | 8845C9922603A15A008F2FA2 /* BookWithTags.swift in Sources */,
262 | 8845C99F2604C064008F2FA2 /* Color+FontColor.swift in Sources */,
263 | 8845C98526020142008F2FA2 /* MenuScreen.swift in Sources */,
264 | 8845C9A7260503D7008F2FA2 /* ColorEntry.swift in Sources */,
265 | 8845C9962603A74C008F2FA2 /* BookWithCoverImages.swift in Sources */,
266 | 8845C99026038433008F2FA2 /* MappingArraysScreen.swift in Sources */,
267 | 8845C98726020601008F2FA2 /* SampleScreen.swift in Sources */,
268 | 8845C9942603A286008F2FA2 /* MappingArraysWithNestedTypesScreen.swift in Sources */,
269 | 885435F2260897CC006188D9 /* MappingGeoPointsScreen.swift in Sources */,
270 | 887DD2B425C47A23003F7A99 /* Book.swift in Sources */,
271 | 8845C9A12604C5AE008F2FA2 /* MappingColorsScreen.swift in Sources */,
272 | 8845C98C260346A9008F2FA2 /* ManuallyMappingSimpleTypesScreen.swift in Sources */,
273 | 8845C9A32604ECA0008F2FA2 /* ProgrammingLanguage.swift in Sources */,
274 | 8845C9982603C853008F2FA2 /* MappingCustomTypesScreen.swift in Sources */,
275 | 88014F1E28F412560081CB49 /* Office.swift in Sources */,
276 | 8845C98A26020FA3008F2FA2 /* MappingSimpleTypesScreen.swift in Sources */,
277 | 8845C99A2603E733008F2FA2 /* Color+Codable.swift in Sources */,
278 | 8845BD7A25C46965002AEC45 /* FirestoreCodableSamplesApp.swift in Sources */,
279 | 8845C9A526050395008F2FA2 /* CustomizeMappingScreen.swift in Sources */,
280 | 887A4A802608EA7E009ACBB1 /* MappingEnumsScreen.swift in Sources */,
281 | );
282 | runOnlyForDeploymentPostprocessing = 0;
283 | };
284 | /* End PBXSourcesBuildPhase section */
285 |
286 | /* Begin XCBuildConfiguration section */
287 | 8845BD8325C46967002AEC45 /* Debug */ = {
288 | isa = XCBuildConfiguration;
289 | buildSettings = {
290 | ALWAYS_SEARCH_USER_PATHS = NO;
291 | CLANG_ANALYZER_NONNULL = YES;
292 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
293 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
294 | CLANG_CXX_LIBRARY = "libc++";
295 | CLANG_ENABLE_MODULES = YES;
296 | CLANG_ENABLE_OBJC_ARC = YES;
297 | CLANG_ENABLE_OBJC_WEAK = YES;
298 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
299 | CLANG_WARN_BOOL_CONVERSION = YES;
300 | CLANG_WARN_COMMA = YES;
301 | CLANG_WARN_CONSTANT_CONVERSION = YES;
302 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
303 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
304 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
305 | CLANG_WARN_EMPTY_BODY = YES;
306 | CLANG_WARN_ENUM_CONVERSION = YES;
307 | CLANG_WARN_INFINITE_RECURSION = YES;
308 | CLANG_WARN_INT_CONVERSION = YES;
309 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
310 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
311 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
312 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
313 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
314 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
315 | CLANG_WARN_STRICT_PROTOTYPES = YES;
316 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
317 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
318 | CLANG_WARN_UNREACHABLE_CODE = YES;
319 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
320 | COPY_PHASE_STRIP = NO;
321 | DEBUG_INFORMATION_FORMAT = dwarf;
322 | ENABLE_STRICT_OBJC_MSGSEND = YES;
323 | ENABLE_TESTABILITY = YES;
324 | GCC_C_LANGUAGE_STANDARD = gnu11;
325 | GCC_DYNAMIC_NO_PIC = NO;
326 | GCC_NO_COMMON_BLOCKS = YES;
327 | GCC_OPTIMIZATION_LEVEL = 0;
328 | GCC_PREPROCESSOR_DEFINITIONS = (
329 | "DEBUG=1",
330 | "$(inherited)",
331 | );
332 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
333 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
334 | GCC_WARN_UNDECLARED_SELECTOR = YES;
335 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
336 | GCC_WARN_UNUSED_FUNCTION = YES;
337 | GCC_WARN_UNUSED_VARIABLE = YES;
338 | IPHONEOS_DEPLOYMENT_TARGET = 16.0;
339 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
340 | MTL_FAST_MATH = YES;
341 | ONLY_ACTIVE_ARCH = YES;
342 | SDKROOT = iphoneos;
343 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
344 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
345 | };
346 | name = Debug;
347 | };
348 | 8845BD8425C46967002AEC45 /* Release */ = {
349 | isa = XCBuildConfiguration;
350 | buildSettings = {
351 | ALWAYS_SEARCH_USER_PATHS = NO;
352 | CLANG_ANALYZER_NONNULL = YES;
353 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
354 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
355 | CLANG_CXX_LIBRARY = "libc++";
356 | CLANG_ENABLE_MODULES = YES;
357 | CLANG_ENABLE_OBJC_ARC = YES;
358 | CLANG_ENABLE_OBJC_WEAK = YES;
359 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
360 | CLANG_WARN_BOOL_CONVERSION = YES;
361 | CLANG_WARN_COMMA = YES;
362 | CLANG_WARN_CONSTANT_CONVERSION = YES;
363 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
364 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
365 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
366 | CLANG_WARN_EMPTY_BODY = YES;
367 | CLANG_WARN_ENUM_CONVERSION = YES;
368 | CLANG_WARN_INFINITE_RECURSION = YES;
369 | CLANG_WARN_INT_CONVERSION = YES;
370 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
371 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
372 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
373 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
374 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
375 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
376 | CLANG_WARN_STRICT_PROTOTYPES = YES;
377 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
378 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
379 | CLANG_WARN_UNREACHABLE_CODE = YES;
380 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
381 | COPY_PHASE_STRIP = NO;
382 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
383 | ENABLE_NS_ASSERTIONS = NO;
384 | ENABLE_STRICT_OBJC_MSGSEND = YES;
385 | GCC_C_LANGUAGE_STANDARD = gnu11;
386 | GCC_NO_COMMON_BLOCKS = YES;
387 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
388 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
389 | GCC_WARN_UNDECLARED_SELECTOR = YES;
390 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
391 | GCC_WARN_UNUSED_FUNCTION = YES;
392 | GCC_WARN_UNUSED_VARIABLE = YES;
393 | IPHONEOS_DEPLOYMENT_TARGET = 16.0;
394 | MTL_ENABLE_DEBUG_INFO = NO;
395 | MTL_FAST_MATH = YES;
396 | SDKROOT = iphoneos;
397 | SWIFT_COMPILATION_MODE = wholemodule;
398 | SWIFT_OPTIMIZATION_LEVEL = "-O";
399 | VALIDATE_PRODUCT = YES;
400 | };
401 | name = Release;
402 | };
403 | 8845BD8625C46967002AEC45 /* Debug */ = {
404 | isa = XCBuildConfiguration;
405 | buildSettings = {
406 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
407 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
408 | CODE_SIGN_STYLE = Automatic;
409 | DEVELOPMENT_ASSET_PATHS = "\"FirestoreCodableSamples/Preview Content\"";
410 | DEVELOPMENT_TEAM = "";
411 | ENABLE_PREVIEWS = YES;
412 | INFOPLIST_FILE = FirestoreCodableSamples/Info.plist;
413 | IPHONEOS_DEPLOYMENT_TARGET = 16.0;
414 | LD_RUNPATH_SEARCH_PATHS = (
415 | "$(inherited)",
416 | "@executable_path/Frameworks",
417 | );
418 | OTHER_LDFLAGS = (
419 | "-Xlinker",
420 | "-interposable",
421 | );
422 | PRODUCT_BUNDLE_IDENTIFIER = dev.peterfriese.FirestoreCodableSamples;
423 | PRODUCT_NAME = "$(TARGET_NAME)";
424 | SWIFT_VERSION = 5.0;
425 | TARGETED_DEVICE_FAMILY = "1,2";
426 | };
427 | name = Debug;
428 | };
429 | 8845BD8725C46967002AEC45 /* Release */ = {
430 | isa = XCBuildConfiguration;
431 | buildSettings = {
432 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
433 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
434 | CODE_SIGN_STYLE = Automatic;
435 | DEVELOPMENT_ASSET_PATHS = "\"FirestoreCodableSamples/Preview Content\"";
436 | DEVELOPMENT_TEAM = "";
437 | ENABLE_PREVIEWS = YES;
438 | INFOPLIST_FILE = FirestoreCodableSamples/Info.plist;
439 | IPHONEOS_DEPLOYMENT_TARGET = 16.0;
440 | LD_RUNPATH_SEARCH_PATHS = (
441 | "$(inherited)",
442 | "@executable_path/Frameworks",
443 | );
444 | PRODUCT_BUNDLE_IDENTIFIER = dev.peterfriese.FirestoreCodableSamples;
445 | PRODUCT_NAME = "$(TARGET_NAME)";
446 | SWIFT_VERSION = 5.0;
447 | TARGETED_DEVICE_FAMILY = "1,2";
448 | };
449 | name = Release;
450 | };
451 | /* End XCBuildConfiguration section */
452 |
453 | /* Begin XCConfigurationList section */
454 | 8845BD7125C46965002AEC45 /* Build configuration list for PBXProject "FirestoreCodableSamples" */ = {
455 | isa = XCConfigurationList;
456 | buildConfigurations = (
457 | 8845BD8325C46967002AEC45 /* Debug */,
458 | 8845BD8425C46967002AEC45 /* Release */,
459 | );
460 | defaultConfigurationIsVisible = 0;
461 | defaultConfigurationName = Release;
462 | };
463 | 8845BD8525C46967002AEC45 /* Build configuration list for PBXNativeTarget "FirestoreCodableSamples" */ = {
464 | isa = XCConfigurationList;
465 | buildConfigurations = (
466 | 8845BD8625C46967002AEC45 /* Debug */,
467 | 8845BD8725C46967002AEC45 /* Release */,
468 | );
469 | defaultConfigurationIsVisible = 0;
470 | defaultConfigurationName = Release;
471 | };
472 | /* End XCConfigurationList section */
473 |
474 | /* Begin XCRemoteSwiftPackageReference section */
475 | 8805D39225D49A7300851C96 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = {
476 | isa = XCRemoteSwiftPackageReference;
477 | repositoryURL = "https://github.com/firebase/firebase-ios-sdk";
478 | requirement = {
479 | kind = upToNextMajorVersion;
480 | minimumVersion = 9.0.0;
481 | };
482 | };
483 | /* End XCRemoteSwiftPackageReference section */
484 |
485 | /* Begin XCSwiftPackageProductDependency section */
486 | 88014F1B28F40D1F0081CB49 /* FirebaseFirestoreSwift */ = {
487 | isa = XCSwiftPackageProductDependency;
488 | package = 8805D39225D49A7300851C96 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
489 | productName = FirebaseFirestoreSwift;
490 | };
491 | 8805D39325D49A7300851C96 /* FirebaseFirestore */ = {
492 | isa = XCSwiftPackageProductDependency;
493 | package = 8805D39225D49A7300851C96 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
494 | productName = FirebaseFirestore;
495 | };
496 | 884040DA27E3827D000B515A /* FirebaseAuth */ = {
497 | isa = XCSwiftPackageProductDependency;
498 | package = 8805D39225D49A7300851C96 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */;
499 | productName = FirebaseAuth;
500 | };
501 | /* End XCSwiftPackageProductDependency section */
502 | };
503 | rootObject = 8845BD6E25C46965002AEC45 /* Project object */;
504 | }
505 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "pins" : [
3 | {
4 | "identity" : "abseil-cpp-swiftpm",
5 | "kind" : "remoteSourceControl",
6 | "location" : "https://github.com/firebase/abseil-cpp-SwiftPM.git",
7 | "state" : {
8 | "revision" : "583de9bd60f66b40e78d08599cc92036c2e7e4e1",
9 | "version" : "0.20220203.2"
10 | }
11 | },
12 | {
13 | "identity" : "boringssl-swiftpm",
14 | "kind" : "remoteSourceControl",
15 | "location" : "https://github.com/firebase/boringssl-SwiftPM.git",
16 | "state" : {
17 | "revision" : "dd3eda2b05a3f459fc3073695ad1b28659066eab",
18 | "version" : "0.9.1"
19 | }
20 | },
21 | {
22 | "identity" : "firebase-ios-sdk",
23 | "kind" : "remoteSourceControl",
24 | "location" : "https://github.com/firebase/firebase-ios-sdk",
25 | "state" : {
26 | "revision" : "7e80c25b51c2ffa238879b07fbfc5baa54bb3050",
27 | "version" : "9.6.0"
28 | }
29 | },
30 | {
31 | "identity" : "googleappmeasurement",
32 | "kind" : "remoteSourceControl",
33 | "location" : "https://github.com/google/GoogleAppMeasurement.git",
34 | "state" : {
35 | "revision" : "c1cfde8067668027b23a42c29d11c246152fe046",
36 | "version" : "9.6.0"
37 | }
38 | },
39 | {
40 | "identity" : "googledatatransport",
41 | "kind" : "remoteSourceControl",
42 | "location" : "https://github.com/google/GoogleDataTransport.git",
43 | "state" : {
44 | "revision" : "5056b15c5acbb90cd214fe4d6138bdf5a740e5a8",
45 | "version" : "9.2.0"
46 | }
47 | },
48 | {
49 | "identity" : "googleutilities",
50 | "kind" : "remoteSourceControl",
51 | "location" : "https://github.com/google/GoogleUtilities.git",
52 | "state" : {
53 | "revision" : "68ea347bdb1a69e2d2ae2e25cd085b6ef92f64cb",
54 | "version" : "7.9.0"
55 | }
56 | },
57 | {
58 | "identity" : "grpc-ios",
59 | "kind" : "remoteSourceControl",
60 | "location" : "https://github.com/grpc/grpc-ios.git",
61 | "state" : {
62 | "revision" : "8440b914756e0d26d4f4d054a1c1581daedfc5b6",
63 | "version" : "1.44.3-grpc"
64 | }
65 | },
66 | {
67 | "identity" : "gtm-session-fetcher",
68 | "kind" : "remoteSourceControl",
69 | "location" : "https://github.com/google/gtm-session-fetcher.git",
70 | "state" : {
71 | "revision" : "d4289da23e978f37c344ea6a386e5546e2466294",
72 | "version" : "2.1.0"
73 | }
74 | },
75 | {
76 | "identity" : "leveldb",
77 | "kind" : "remoteSourceControl",
78 | "location" : "https://github.com/firebase/leveldb.git",
79 | "state" : {
80 | "revision" : "0706abcc6b0bd9cedfbb015ba840e4a780b5159b",
81 | "version" : "1.22.2"
82 | }
83 | },
84 | {
85 | "identity" : "nanopb",
86 | "kind" : "remoteSourceControl",
87 | "location" : "https://github.com/firebase/nanopb.git",
88 | "state" : {
89 | "revision" : "819d0a2173aff699fb8c364b6fb906f7cdb1a692",
90 | "version" : "2.30909.0"
91 | }
92 | },
93 | {
94 | "identity" : "promises",
95 | "kind" : "remoteSourceControl",
96 | "location" : "https://github.com/google/promises.git",
97 | "state" : {
98 | "revision" : "3e4e743631e86c8c70dbc6efdc7beaa6e90fd3bb",
99 | "version" : "2.1.1"
100 | }
101 | },
102 | {
103 | "identity" : "swift-protobuf",
104 | "kind" : "remoteSourceControl",
105 | "location" : "https://github.com/apple/swift-protobuf.git",
106 | "state" : {
107 | "revision" : "e1499bc69b9040b29184f7f2996f7bab467c1639",
108 | "version" : "1.19.0"
109 | }
110 | }
111 | ],
112 | "version" : 2
113 | }
114 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples.xcodeproj/xcshareddata/xcschemes/FirestoreCodableSamples.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
45 |
51 |
52 |
53 |
54 |
60 |
62 |
68 |
69 |
70 |
71 |
73 |
74 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/App/FirestoreCodableSamplesApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FirestoreCodableSamplesApp.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 29.01.21.
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | import SwiftUI
20 | import FirebaseCore
21 | import FirebaseFirestore
22 | import FirebaseAuth
23 |
24 | @main
25 | struct FirestoreCodableSamplesApp: App {
26 | init() {
27 | FirebaseApp.configure()
28 |
29 | Auth.auth().useEmulator(withHost:"localhost", port:9099)
30 | Auth.auth().signInAnonymously()
31 |
32 | // connect to Firestore Emulator
33 | Firestore.firestore().useEmulator(withHost: "localhost", port: 8080)
34 | let settings = Firestore.firestore().settings
35 | settings.isPersistenceEnabled = false
36 | settings.isSSLEnabled = false
37 | Firestore.firestore().settings = settings
38 | }
39 |
40 | var body: some Scene {
41 | WindowGroup {
42 | NavigationSplitView {
43 | MenuScreen()
44 | } detail: {
45 | Text("Select an item from the menu on the left.")
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 |
28 | UIApplicationSupportsIndirectInputEvents
29 |
30 | UILaunchScreen
31 |
32 | UIRequiredDeviceCapabilities
33 |
34 | armv7
35 |
36 | UISupportedInterfaceOrientations
37 |
38 | UIInterfaceOrientationPortrait
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UISupportedInterfaceOrientations~ipad
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationPortraitUpsideDown
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 | NSAppTransportSecurity
50 |
51 | NSAllowsArbitraryLoads
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Models/Article.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Article.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 10.10.22.
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | import Foundation
20 | import SwiftUI
21 | import Firebase
22 | import FirebaseFirestoreSwift
23 |
24 | enum Status: String, CaseIterable, Codable {
25 | case draft
26 | case inReview = "in review"
27 | case approved
28 | case published
29 | }
30 |
31 | extension Status {
32 | var color: Color {
33 | switch self {
34 | case .draft:
35 | return Color.red
36 | case .inReview:
37 | return Color.yellow
38 | case.approved:
39 | return Color.green
40 | case .published:
41 | return Color.blue
42 | }
43 | }
44 | }
45 |
46 | struct Article: Identifiable, Codable {
47 | @DocumentID var id: String?
48 | var title: String
49 | var status: Status
50 | }
51 |
52 | extension Article {
53 | static let empty = Article(title: "", status: .draft)
54 | static let sample = [
55 | Article(id: "codable", title: "Mapping Firestore Data in Swift - The Comprehensive Guide", status: Status.draft),
56 | Article(id: "lifecycle", title: "Firebase and the new SwiftUI 2 Application Life Cycle", status: Status.approved),
57 | Article(id: "drill-down", title: "SwiftUI Drill-down Navigation", status: .draft),
58 | Article(id: "asyncawait", title: "Using async/await in SwiftUI", status: .published),
59 | ]
60 | }
61 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Models/Book.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Person.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 29.01.21.
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | import Foundation
20 | import FirebaseFirestoreSwift
21 |
22 | public struct Book: Codable {
23 | @DocumentID var id: String?
24 | var title: String
25 | var numberOfPages: Int
26 | var author: String
27 | }
28 |
29 | extension Book {
30 | static let empty = Book(title: "", numberOfPages: 0, author: "")
31 | }
32 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Models/BookWithCoverImages.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BookWithComplexType.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 18.03.21.
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | import Foundation
20 | import FirebaseFirestoreSwift
21 | import SwiftUI
22 |
23 | struct CoverImages: Codable {
24 | var small: URL
25 | var medium: URL
26 | var large: URL
27 | }
28 |
29 | struct BookWithCoverImages: Codable {
30 | @DocumentID var id: String?
31 | var title: String
32 | var numberOfPages: Int
33 | var author: String
34 | var cover: CoverImages?
35 | var color: Color
36 | }
37 |
38 | extension BookWithCoverImages {
39 | static let empty = BookWithCoverImages(title: "", numberOfPages: 0, author: "", cover: nil, color: .black)
40 | }
41 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Models/BookWithGenre.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BookWithGenre.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 18.03.21.
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | import Foundation
20 | import FirebaseFirestoreSwift
21 |
22 | public struct BookWithGenre: Codable {
23 | @DocumentID var id: String?
24 | var title: String
25 | var numberOfPages: Int
26 | var author: String
27 | var genres: [String]
28 | }
29 |
30 | extension BookWithGenre {
31 | static let empty = BookWithGenre(title: "", numberOfPages: 0, author: "", genres: [])
32 | static let sample = BookWithGenre(title: "SwiftUI & Combine", numberOfPages: 350, author: "Peter Friese", genres: ["IT", "Programming Languages"])
33 | }
34 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Models/BookWithTags.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BookWithTags.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 18.03.21.
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | import Foundation
20 | import FirebaseFirestoreSwift
21 | import SwiftUI
22 |
23 | struct Tag: Codable, Hashable {
24 | var title: String
25 | var color: Color
26 | }
27 |
28 | struct BookWithTags: Codable {
29 | @DocumentID var id: String?
30 | var title: String
31 | var numberOfPages: Int
32 | var author: String
33 | var tags: [Tag]
34 | }
35 |
36 | extension BookWithTags {
37 | static let empty = BookWithTags(title: "", numberOfPages: 0, author: "", tags: [])
38 | static let sample = BookWithTags(title: "SwiftUI & Combine",
39 | numberOfPages: 350,
40 | author: "Peter Friese",
41 | tags: [
42 | Tag(title: "Swift", color: Color(hex: "#f05138")),
43 | Tag(title: "SwiftUI", color: Color(hex: "#012495"))
44 | ])
45 | }
46 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Models/ColorEntry.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ColorEntry.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 19.03.21.
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | import Foundation
20 | import SwiftUI
21 | import FirebaseFirestoreSwift
22 |
23 | struct ColorEntry: Identifiable, Codable {
24 | @DocumentID var id: String?
25 | var name: String
26 | var color: Color
27 | }
28 |
29 | extension ColorEntry {
30 | static var empty = ColorEntry(name: "", color: Color.red)
31 | static var sample = [
32 | ColorEntry(id: "red", name: "Red", color: .red),
33 | ColorEntry(id: "cerise", name: "Cerise", color: Color(hex: "#d52c67"))
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Models/Office.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Office.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 10.10.22.
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | import Foundation
20 | import Firebase
21 | import FirebaseFirestoreSwift
22 |
23 | struct Office: Identifiable, Codable {
24 | @DocumentID var id: String?
25 | var name: String
26 | var location: GeoPoint
27 | }
28 |
29 | extension Office {
30 | static var empty = Office(name: "", location: GeoPoint(latitude: 0, longitude: 0))
31 | static var sample = [
32 | Office(id: "6PS", name: "Google UK, 6 Pancras Square", location: GeoPoint(latitude: 51.5327652, longitude: -0.1272025)),
33 | Office(id: "googleplex", name: "Googleplex", location: GeoPoint(latitude: 37.4207294, longitude: -122.084954)),
34 | Office(id: "sfo", name: "Google San Francisco", location: GeoPoint(latitude: 37.7898829, longitude: -122.3915139)),
35 | Office(id: "ham", name: "Google Hamburg", location: GeoPoint(latitude: 53.553986, longitude: 9.9856482)),
36 | Office(id: "nyc", name: "Google New York", location: GeoPoint(latitude: 40.7414688, longitude: -74.0033873)),
37 | ]
38 | }
39 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Models/ProgrammingLanguage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProgrammingLanguage.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 19.03.21.
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | import Foundation
20 | import FirebaseFirestoreSwift
21 |
22 | struct ProgrammingLanguage: Identifiable, Codable {
23 | @DocumentID var id: String?
24 | var name: String
25 | var year: Date
26 | var reasonWhyILoveThis: String = ""
27 |
28 | enum CodingKeys: String, CodingKey {
29 | case id
30 | case name = "language_name"
31 | case year
32 | }
33 | }
34 |
35 | extension ProgrammingLanguage {
36 | func yearAsString(date: Date) -> String {
37 | let formatter = DateFormatter()
38 | formatter.dateFormat = "yyyy"
39 | return formatter.string(from: date)
40 | }
41 |
42 | var yearAsString: String {
43 | return yearAsString(date: year)
44 | }
45 | }
46 |
47 | extension ProgrammingLanguage {
48 | static let empty = ProgrammingLanguage(name: "", year: Date())
49 | static let sample = [
50 | ProgrammingLanguage(id: "plankalkuel", name: "Plankalkül", year: Date(), reasonWhyILoveThis: "It sounds fancy"),
51 | ProgrammingLanguage(id: "swift", name: "Swift", year: Date(), reasonWhyILoveThis: "Extensions & protocols")
52 | ]
53 | }
54 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Screens/CustomizeMappingScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CustomizeMappingScreen.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 19.03.21.
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | import SwiftUI
20 | import Firebase
21 | import FirebaseFirestoreSwift
22 |
23 | class CustomizeMappingViewModel: ObservableObject {
24 | @Published var programmingLanguages = ProgrammingLanguage.sample // [ProgrammingLanguage]()
25 | @Published var newLanguage = ProgrammingLanguage.empty
26 | @Published var errorMessage: String?
27 |
28 | private var db = Firestore.firestore()
29 | private var listenerRegistration: ListenerRegistration?
30 |
31 | fileprivate func unsubscribe() {
32 | if listenerRegistration != nil {
33 | listenerRegistration?.remove()
34 | listenerRegistration = nil
35 | }
36 | }
37 |
38 | fileprivate func subscribe() {
39 | if listenerRegistration == nil {
40 | listenerRegistration = db.collection("programming-languages")
41 | .addSnapshotListener { [weak self] (querySnapshot, error) in
42 | guard let documents = querySnapshot?.documents else {
43 | self?.errorMessage = "No documents in 'programming-languages' collection"
44 | return
45 | }
46 |
47 | self?.programmingLanguages = documents.compactMap { queryDocumentSnapshot in
48 | let result = Result { try queryDocumentSnapshot.data(as: ProgrammingLanguage.self) }
49 |
50 | switch result {
51 | case .success(let programmingLanguage):
52 | // A ProgrammingLanguage value was successfully initialized from the DocumentSnapshot.
53 | self?.errorMessage = nil
54 | return programmingLanguage
55 | case .failure(let error):
56 | // A Book value could not be initialized from the DocumentSnapshot.
57 | switch error {
58 | case DecodingError.typeMismatch(_, let context):
59 | self?.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
60 | case DecodingError.valueNotFound(_, let context):
61 | self?.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
62 | case DecodingError.keyNotFound(_, let context):
63 | self?.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
64 | case DecodingError.dataCorrupted(let key):
65 | self?.errorMessage = "\(error.localizedDescription): \(key)"
66 | default:
67 | self?.errorMessage = "Error decoding document: \(error.localizedDescription)"
68 | }
69 | return nil
70 | }
71 | }
72 | }
73 | }
74 | }
75 |
76 | fileprivate func addLanguage() {
77 | let collectionRef = db.collection("programming-languages")
78 | do {
79 | let newDocReference = try collectionRef.addDocument(from: newLanguage)
80 | print("ProgrammingLanguage stored with new document reference: \(newDocReference)")
81 | }
82 | catch {
83 | print(error)
84 | }
85 | }
86 | }
87 |
88 | struct CustomizeMappingScreen: View {
89 | @StateObject var viewModel = CustomizeMappingViewModel()
90 |
91 | var body: some View {
92 | SampleScreen("Customize Mapping", introduction: "We can use Codable's CodingKeys to customize Firestore's mapping") {
93 | Form {
94 | Section(header: Text("Programming Languages")) {
95 | List(viewModel.programmingLanguages) { language in
96 | HStack {
97 | Text(language.name)
98 | Spacer()
99 | Text("Released: \(language.yearAsString)")
100 | .font(.caption)
101 | }
102 | }
103 | }
104 | Section {
105 | TextField("Language name", text: $viewModel.newLanguage.name)
106 | TextField("Reason I love this", text: $viewModel.newLanguage.reasonWhyILoveThis)
107 | DatePicker("Date of release", selection: $viewModel.newLanguage.year, displayedComponents: [.date])
108 | Button(action: viewModel.addLanguage ) {
109 | Label("Add new language", systemImage: "plus")
110 | }
111 | }
112 | }
113 | }
114 | .onAppear() {
115 | viewModel.subscribe()
116 | }
117 | .onDisappear() {
118 | viewModel.unsubscribe()
119 | }
120 | }
121 | }
122 |
123 | struct CustomizeMappingScreen_Previews: PreviewProvider {
124 | static var previews: some View {
125 | Group {
126 | NavigationView {
127 | CustomizeMappingScreen()
128 | }
129 | NavigationView {
130 | CustomizeMappingScreen()
131 | }
132 | .preferredColorScheme(.dark)
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Screens/ManuallyMappingSimpleTypesScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ManuallyMappingSimpleTypesScreen.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 18.03.21.
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | import SwiftUI
20 | import Firebase
21 | import FirebaseFirestoreSwift
22 |
23 | class ManuallyMappingSimpleTypesViewModel: ObservableObject {
24 | @Published var book: Book = .empty
25 | @Published var errorMessage: String?
26 |
27 | private var db = Firestore.firestore()
28 |
29 | fileprivate func fetchAndMap() {
30 | fetchBook(documentId: "hitchhiker")
31 | }
32 |
33 | fileprivate func fetchAndMapNonExisting() {
34 | fetchBook(documentId: "does-not-exist")
35 | }
36 |
37 | fileprivate func fetchAndTryMappingInvalidData() {
38 | fetchBook(documentId: "invalid-data")
39 | }
40 |
41 | #warning("DO NOT MAP YOUR DOCUMENTS MANUALLY. USE CODABLE INSTEAD.")
42 | private func fetchBook(documentId: String) {
43 | let docRef = db.collection("books").document(documentId)
44 |
45 | docRef.getDocument { document, error in
46 | if let error = error as NSError? {
47 | self.errorMessage = "Error getting document: \(error.localizedDescription)"
48 | }
49 | else {
50 | if let document = document {
51 | let id = document.documentID
52 | let data = document.data()
53 | let title = data?["title"] as? String ?? ""
54 | let numberOfPages = data?["numberOfPages"] as? Int ?? 0
55 | let author = data?["author"] as? String ?? ""
56 | self.book = Book(id:id, title: title, numberOfPages: numberOfPages, author: author)
57 | }
58 | }
59 | }
60 | }
61 | }
62 |
63 | struct ManuallyMappingSimpleTypesScreen: View {
64 | @StateObject var viewModel = ManuallyMappingSimpleTypesViewModel()
65 |
66 | @ViewBuilder
67 | var messageView: some View {
68 | if let errorMessage = viewModel.errorMessage {
69 | Text(errorMessage).foregroundColor(.red)
70 | }
71 | else {
72 | EmptyView()
73 | }
74 | }
75 |
76 | var body: some View {
77 | SampleScreen("Mapping Simple Types", introduction: "⚠️ Mapping documents manually is possible, but not recommended.") {
78 | Form {
79 | Section(header: Text("Book"), footer: messageView) {
80 | TextField("Title", text: $viewModel.book.title)
81 | TextField("Number of pages", value: $viewModel.book.numberOfPages, formatter: NumberFormatter())
82 | TextField("Author", text: $viewModel.book.author)
83 | }
84 | Section(header: Text("Actions")) {
85 | Button("Fetch and map a book") {
86 | viewModel.fetchAndMap()
87 | }
88 | Button("Fetch and map a non-existing book") {
89 | viewModel.fetchAndMapNonExisting()
90 | }
91 | Button("Try mapping book from invalid data") {
92 | viewModel.fetchAndTryMappingInvalidData()
93 | }
94 | }
95 | }
96 | }
97 | }
98 | }
99 |
100 | struct ManuallyMappingSimpleTypesScreen_Previews: PreviewProvider {
101 | static var previews: some View {
102 | NavigationView {
103 | ManuallyMappingSimpleTypesScreen()
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Screens/MappingArraysScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MappingArraysSCreen.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 18.03.21.
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | import SwiftUI
20 | import Firebase
21 | import FirebaseFirestoreSwift
22 |
23 | class MappingArraysViewModel: ObservableObject {
24 | @Published var book: BookWithGenre = .empty
25 | @Published var errorMessage: String?
26 |
27 | private var db = Firestore.firestore()
28 |
29 | fileprivate func fetchAndMap() {
30 | fetchBook(documentId: "hitchhiker-genre")
31 | }
32 |
33 | private func fetchBook(documentId: String) {
34 | let docRef = db.collection("books").document(documentId)
35 |
36 | docRef.getDocument(as: BookWithGenre.self) { result in
37 | switch result {
38 | case .success(let book):
39 | self.book = book
40 | self.errorMessage = nil
41 | case .failure(let error):
42 | // A Book value could not be initialized from the DocumentSnapshot.
43 | switch error {
44 | case DecodingError.typeMismatch(_, let context):
45 | self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
46 | case DecodingError.valueNotFound(_, let context):
47 | self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
48 | case DecodingError.keyNotFound(_, let context):
49 | self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
50 | case DecodingError.dataCorrupted(let key):
51 | self.errorMessage = "\(error.localizedDescription): \(key)"
52 | default:
53 | self.errorMessage = "Error decoding document: \(error.localizedDescription)"
54 | }
55 | }
56 | }
57 | }
58 |
59 | fileprivate func updateBook() {
60 | if let id = book.id {
61 | let docRef = db.collection("books").document(id)
62 | do {
63 | try docRef.setData(from: book)
64 | }
65 | catch {
66 | print(error)
67 | }
68 | }
69 | }
70 |
71 | fileprivate func addBook() {
72 | let collectionRef = db.collection("books")
73 | do {
74 | let newDocReference = try collectionRef.addDocument(from: self.book)
75 | print("Book stored with new document reference: \(newDocReference)")
76 | }
77 | catch {
78 | print(error)
79 | }
80 | }
81 |
82 | func newBook() {
83 | self.book = .empty
84 | }
85 |
86 | private var genres: Set = ["Action", "Fantasy", "Fairy Tale"]
87 |
88 | func addGenre() {
89 | let unusedGenres = self.genres.subtracting(book.genres)
90 | if let genre = unusedGenres.first {
91 | self.book.genres.append(genre)
92 | }
93 | }
94 | }
95 |
96 | struct MappingArraysScreen: View {
97 | @StateObject var viewModel = MappingArraysViewModel()
98 |
99 | @ViewBuilder
100 | var messageView: some View {
101 | if let errorMessage = viewModel.errorMessage {
102 | Text(errorMessage).foregroundColor(.red)
103 | }
104 | else {
105 | EmptyView()
106 | }
107 | }
108 |
109 | var body: some View {
110 | SampleScreen("Mapping Arrays", introduction: "Firestore makes mapping arrays easy.") {
111 | Form {
112 | Section(header: Text("Book"), footer: messageView) {
113 | TextField("Title", text: $viewModel.book.title)
114 | TextField("Number of pages", value: $viewModel.book.numberOfPages, formatter: NumberFormatter())
115 | TextField("Author", text: $viewModel.book.author)
116 | }
117 | Section(header: Text("Genres")) {
118 | List(viewModel.book.genres, id: \.self) { genre in
119 | Text(genre)
120 | }
121 | Button(action: viewModel.addGenre) {
122 | Label("Add a genre", systemImage: "plus")
123 | }
124 | }
125 | Section(header: Text("Actions")) {
126 | Button(action: viewModel.fetchAndMap) {
127 | Label("Fetch and map a book", systemImage: "square.and.arrow.up")
128 | }
129 |
130 | Button(action: viewModel.updateBook) {
131 | Label("Udpate book", systemImage: "square.and.arrow.down")
132 | }
133 | Button(action: viewModel.newBook) {
134 | Label("New Book", systemImage: "sparkles")
135 | }
136 | Button(action: viewModel.addBook) {
137 | Label("Add Book", systemImage: "plus")
138 | }
139 | }
140 | }
141 | }
142 | }
143 | }
144 |
145 | struct MappingArraysScreen_Previews: PreviewProvider {
146 | static var previews: some View {
147 | NavigationView {
148 | MappingArraysScreen()
149 | }
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Screens/MappingArraysWithNestedTypesScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MappingArraysWithNestedTypes.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 18.03.21.
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | import SwiftUI
20 |
21 | import SwiftUI
22 | import Firebase
23 | import FirebaseFirestoreSwift
24 |
25 | class MappingArraysWithNestedTypesViewModel: ObservableObject {
26 | @Published var book: BookWithTags = .empty
27 | @Published var errorMessage: String?
28 |
29 | private var db = Firestore.firestore()
30 |
31 | fileprivate func fetchAndMap() {
32 | fetchBook(documentId: "hitchhiker-tags")
33 | }
34 |
35 | private func fetchBook(documentId: String) {
36 | let docRef = db.collection("books").document(documentId)
37 |
38 | docRef.getDocument(as: BookWithTags.self) { result in
39 | switch result {
40 | case .success(let book):
41 | self.book = book
42 | self.errorMessage = nil
43 | case .failure(let error):
44 | // A Book value could not be initialized from the DocumentSnapshot.
45 | switch error {
46 | case DecodingError.typeMismatch(_, let context):
47 | self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
48 | case DecodingError.valueNotFound(_, let context):
49 | self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
50 | case DecodingError.keyNotFound(_, let context):
51 | self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
52 | case DecodingError.dataCorrupted(let key):
53 | self.errorMessage = "\(error.localizedDescription): \(key)"
54 | default:
55 | self.errorMessage = "Error decoding document: \(error.localizedDescription)"
56 | }
57 | }
58 | }
59 | }
60 |
61 | fileprivate func updateBook() {
62 | if let id = book.id {
63 | let docRef = db.collection("books").document(id)
64 | do {
65 | try docRef.setData(from: book)
66 | }
67 | catch {
68 | print(error)
69 | }
70 | }
71 | }
72 |
73 | fileprivate func addBook() {
74 | let collectionRef = db.collection("books")
75 | do {
76 | let newDocReference = try collectionRef.addDocument(from: self.book)
77 | print("Book stored with new document reference: \(newDocReference)")
78 | }
79 | catch {
80 | print(error)
81 | }
82 | }
83 |
84 | fileprivate func newBook() {
85 | self.book = .empty
86 | }
87 |
88 | private var tags: Set = [
89 | Tag(title: "Space", color: Color(hex: "#131a2d")),
90 | Tag(title: "Tea", color: Color(hex: "#d8bfbf")),
91 | Tag(title: "Improbability", color: Color(hex: "#fd1d48")),
92 | ]
93 |
94 | fileprivate func addTag() {
95 | let unusedTags = self.tags.subtracting(book.tags)
96 | if let tag = unusedTags.first {
97 | self.book.tags.append(tag)
98 | }
99 | }
100 | }
101 |
102 | struct MappingArraysWithNestedTypesScreen: View {
103 | @StateObject var viewModel = MappingArraysWithNestedTypesViewModel()
104 |
105 | @ViewBuilder
106 | var messageView: some View {
107 | if let errorMessage = viewModel.errorMessage {
108 | Text(errorMessage).foregroundColor(.red)
109 | }
110 | else {
111 | EmptyView()
112 | }
113 | }
114 |
115 | var body: some View {
116 | SampleScreen("Mapping Arrays", introduction: "Firestore makes mapping arrays easy.") {
117 | Form {
118 | Section(header: Text("Book"), footer: messageView) {
119 | TextField("Title", text: $viewModel.book.title)
120 | TextField("Number of pages", value: $viewModel.book.numberOfPages, formatter: NumberFormatter())
121 | TextField("Author", text: $viewModel.book.author)
122 | }
123 | Section(header: Text("Tags")) {
124 | List(viewModel.book.tags, id: \.self) { tag in
125 | Text(tag.title)
126 | .foregroundColor(tag.color.accessibleFontColor)
127 | .padding(.horizontal, 4)
128 | .padding(.vertical, 2)
129 | .background(tag.color)
130 | .cornerRadius(3.0)
131 | }
132 | Button(action: viewModel.addTag) {
133 | Label("Add a tag", systemImage: "plus")
134 | }
135 | }
136 | Section(header: Text("Actions")) {
137 | Button(action: viewModel.fetchAndMap) {
138 | Label("Fetch and map a book", systemImage: "square.and.arrow.up")
139 | }
140 |
141 | Button(action: viewModel.updateBook) {
142 | Label("Udpate book", systemImage: "square.and.arrow.down")
143 | }
144 | Button(action: viewModel.newBook) {
145 | Label("New Book", systemImage: "sparkles")
146 | }
147 | Button(action: viewModel.addBook) {
148 | Label("Add Book", systemImage: "plus")
149 | }
150 | }
151 | }
152 | }
153 | }
154 | }
155 |
156 | struct MappingArraysWithNestedTypesScreen_Previews: PreviewProvider {
157 | static var previews: some View {
158 | NavigationView {
159 | MappingArraysWithNestedTypesScreen()
160 | }
161 | }
162 | }
163 |
164 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Screens/MappingColorsScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MappingColorsSCreen.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 19.03.21.
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | import SwiftUI
20 | import Firebase
21 | import FirebaseFirestoreSwift
22 |
23 | class MappingColorsViewModel: ObservableObject {
24 | @Published var colorEntries = [ColorEntry]()
25 | @Published var newColor = ColorEntry.empty
26 | @Published var errorMessage: String?
27 |
28 | private var db = Firestore.firestore()
29 | private var listenerRegistration: ListenerRegistration?
30 |
31 | fileprivate func unsubscribe() {
32 | if listenerRegistration != nil {
33 | listenerRegistration?.remove()
34 | listenerRegistration = nil
35 | }
36 | }
37 |
38 | fileprivate func subscribe() {
39 | if listenerRegistration == nil {
40 | listenerRegistration = db.collection("colors")
41 | .addSnapshotListener { [weak self] (querySnapshot, error) in
42 | guard let documents = querySnapshot?.documents else {
43 | self?.errorMessage = "No documents in 'colors' collection"
44 | return
45 | }
46 |
47 | self?.colorEntries = documents.compactMap { queryDocumentSnapshot in
48 | let result = Result { try queryDocumentSnapshot.data(as: ColorEntry.self) }
49 | switch result {
50 | case .success(let colorEntry):
51 | // A ColorEntry value was successfully initialized from the DocumentSnapshot.
52 | self?.errorMessage = nil
53 | return colorEntry
54 | case .failure(let error):
55 | // A ColorEntry value could not be initialized from the DocumentSnapshot.
56 | switch error {
57 | case DecodingError.typeMismatch(_, let context):
58 | self?.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
59 | case DecodingError.valueNotFound(_, let context):
60 | self?.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
61 | case DecodingError.keyNotFound(_, let context):
62 | self?.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
63 | case DecodingError.dataCorrupted(let key):
64 | self?.errorMessage = "\(error.localizedDescription): \(key)"
65 | default:
66 | self?.errorMessage = "Error decoding document: \(error.localizedDescription)"
67 | }
68 | return nil
69 | }
70 | }
71 | }
72 | }
73 | }
74 |
75 | fileprivate func addColorEntry() {
76 | let collectionRef = db.collection("colors")
77 | do {
78 | let newDocReference = try collectionRef.addDocument(from: newColor)
79 | print("ColorEntry stored with new document reference: \(newDocReference)")
80 | }
81 | catch {
82 | print(error)
83 | }
84 | }
85 | }
86 |
87 | struct MappingColorsScreen: View {
88 | @StateObject var viewModel = MappingColorsViewModel()
89 |
90 | var body: some View {
91 | SampleScreen("Mapping Colors", introduction: "Mapping colors is easy once we conform Color to Codable.") {
92 | Form {
93 | Section(header: Text("Colors")) {
94 | List(viewModel.colorEntries) { colorEntry in
95 | Text("\(colorEntry.name) (\(colorEntry.color.toHex ?? ""))")
96 | .listRowBackground(colorEntry.color)
97 | .foregroundColor(colorEntry.color.accessibleFontColor)
98 | .padding(.horizontal, 4)
99 | .padding(.vertical, 2)
100 | .background(colorEntry.color)
101 | .cornerRadius(3.0)
102 | }
103 | }
104 | Section {
105 | ColorPicker(selection: $viewModel.newColor.color) {
106 | Label("First, pick color", systemImage: "paintpalette")
107 | }
108 | Button(action: viewModel.addColorEntry ) {
109 | Label("Then add it", systemImage: "plus")
110 | }
111 | }
112 | }
113 | }
114 | .onAppear() {
115 | viewModel.subscribe()
116 | }
117 | .onDisappear() {
118 | viewModel.unsubscribe()
119 | }
120 | }
121 | }
122 |
123 | struct MappingColorsScreen_Previews: PreviewProvider {
124 | static var previews: some View {
125 | Group {
126 | NavigationView {
127 | MappingColorsScreen()
128 | }
129 | NavigationView {
130 | MappingColorsScreen()
131 | }
132 | .preferredColorScheme(.dark)
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Screens/MappingCustomTypesScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MappingCustomTypesScreen.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 18.03.21.
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | import SwiftUI
20 | import Firebase
21 | import FirebaseFirestoreSwift
22 |
23 | class MappingCustomTypesViewModel: ObservableObject {
24 | @Published var book: BookWithCoverImages = .empty
25 | @Published var errorMessage: String?
26 |
27 | private var db = Firestore.firestore()
28 |
29 | fileprivate func fetchAndMap() {
30 | fetchBook(documentId: "hitchhiker-image-urls")
31 | }
32 |
33 | private func fetchBook(documentId: String) {
34 | let docRef = db.collection("books").document(documentId)
35 |
36 | docRef.getDocument(as: BookWithCoverImages.self) { result in
37 | switch result {
38 | case .success(let book):
39 | // A Book value was successfully initialized from the DocumentSnapshot.
40 | self.book = book
41 | self.errorMessage = nil
42 | case .failure(let error):
43 | // A Book value could not be initialized from the DocumentSnapshot.
44 | switch error {
45 | case DecodingError.typeMismatch(_, let context):
46 | self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
47 | case DecodingError.valueNotFound(_, let context):
48 | self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
49 | case DecodingError.keyNotFound(_, let context):
50 | self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
51 | case DecodingError.dataCorrupted(let key):
52 | self.errorMessage = "\(error.localizedDescription): \(key)"
53 | default:
54 | self.errorMessage = "Error decoding document: \(error.localizedDescription)"
55 | }
56 | }
57 | }
58 | }
59 |
60 | fileprivate func updateBook() {
61 | if let id = book.id {
62 | let docRef = db.collection("books").document(id)
63 | do {
64 | try docRef.setData(from: book)
65 | }
66 | catch {
67 | print(error)
68 | }
69 | }
70 | }
71 |
72 | fileprivate func addBook() {
73 | let collectionRef = db.collection("books")
74 | do {
75 | let newDocReference = try collectionRef.addDocument(from: self.book)
76 | print("Book stored with new document reference: \(newDocReference)")
77 | }
78 | catch {
79 | print(error)
80 | }
81 | }
82 | }
83 |
84 | struct MappingCustomTypesScreen: View {
85 | @StateObject var viewModel = MappingCustomTypesViewModel()
86 |
87 | @ViewBuilder
88 | var messageView: some View {
89 | if let errorMessage = viewModel.errorMessage {
90 | Text(errorMessage).foregroundColor(.red)
91 | }
92 | else {
93 | EmptyView()
94 | }
95 | }
96 |
97 | var body: some View {
98 | SampleScreen("Mapping Simple Types", introduction: "Firestore makes mapping simple data types as simple as possible.") {
99 | Form {
100 | Section(header: Text("Book"), footer: messageView) {
101 | TextField("Title", text: $viewModel.book.title)
102 | TextField("Number of pages", value: $viewModel.book.numberOfPages, formatter: NumberFormatter())
103 | TextField("Author", text: $viewModel.book.author)
104 | NavigationLink(destination: CoverImagesScreen(covers: viewModel.book.cover)) {
105 | Text("Covers")
106 | }
107 | }
108 | Section(header: Text("Actions")) {
109 | Button(action: viewModel.fetchAndMap) {
110 | Label("Fetch and map a book", systemImage: "square.and.arrow.up")
111 | }
112 |
113 | Button(action: viewModel.updateBook) {
114 | Label("Udpate book", systemImage: "square.and.arrow.down")
115 | }
116 | Button(action: viewModel.addBook) {
117 | Label("Add Book", systemImage: "plus")
118 | }
119 | }
120 | }
121 | }
122 | }
123 | }
124 |
125 | struct CoverImagesScreen: View {
126 | var covers: CoverImages?
127 | var body: some View {
128 | if let covers = covers {
129 | List {
130 | Text(covers.small.absoluteString)
131 | Text(covers.medium.absoluteString)
132 | Text(covers.large.absoluteString)
133 | }
134 | }
135 | else {
136 | Text("This book has no cover images")
137 | }
138 | }
139 | }
140 |
141 | struct MappingCustomTypesScreen_Previews: PreviewProvider {
142 | static var previews: some View {
143 | NavigationView {
144 | MappingCustomTypesScreen()
145 | }
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Screens/MappingEnumsScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MappingEnumsScreen.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 22.03.21.
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | import SwiftUI
20 | import Firebase
21 | import FirebaseFirestoreSwift
22 |
23 |
24 |
25 | class MappingEnumsViewModel: ObservableObject {
26 | @Published var articles = Article.sample // [Article]()
27 | @Published var newArticle = Article.empty
28 | @Published var errorMessage: String?
29 |
30 | private var db = Firestore.firestore()
31 | private var listenerRegistration: ListenerRegistration?
32 |
33 | fileprivate func unsubscribe() {
34 | if listenerRegistration != nil {
35 | listenerRegistration?.remove()
36 | listenerRegistration = nil
37 | }
38 | }
39 |
40 | fileprivate func subscribe() {
41 | if listenerRegistration == nil {
42 | listenerRegistration = db.collection("articles")
43 | .addSnapshotListener { [weak self] (querySnapshot, error) in
44 | guard let documents = querySnapshot?.documents else {
45 | self?.errorMessage = "No documents in 'articles' collection"
46 | return
47 | }
48 |
49 | self?.articles = documents.compactMap { queryDocumentSnapshot in
50 | let result = Result { try queryDocumentSnapshot.data(as: Article.self) }
51 |
52 | switch result {
53 | case .success(let article):
54 | // An Article value was successfully initialized from the DocumentSnapshot.
55 | self?.errorMessage = nil
56 | return article
57 | case .failure(let error):
58 | // An Article value could not be initialized from the DocumentSnapshot.
59 | switch error {
60 | case DecodingError.typeMismatch(_, let context):
61 | self?.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
62 | case DecodingError.valueNotFound(_, let context):
63 | self?.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
64 | case DecodingError.keyNotFound(_, let context):
65 | self?.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
66 | case DecodingError.dataCorrupted(let key):
67 | self?.errorMessage = "\(error.localizedDescription): \(key)"
68 | default:
69 | self?.errorMessage = "Error decoding document: \(error.localizedDescription)"
70 | }
71 | return nil
72 | }
73 | }
74 | }
75 | }
76 | }
77 |
78 | fileprivate func addArticle() {
79 | let collectionRef = db.collection("articles")
80 | do {
81 | let newDocReference = try collectionRef.addDocument(from: newArticle)
82 | print("Article stored with new document reference: \(newDocReference)")
83 | }
84 | catch {
85 | print(error)
86 | }
87 | }
88 | }
89 |
90 | struct MappingEnumsScreen: View {
91 | @StateObject var viewModel = MappingEnumsViewModel()
92 |
93 | var body: some View {
94 | SampleScreen("Mapping Enums", introduction: "Enums can be mapped easily if their elements are codable") {
95 | Form {
96 | Section(header: Text("Articles")) {
97 | List(viewModel.articles) { article in
98 | VStack(alignment: .leading) {
99 | Text(article.title)
100 | Text("Status: \(article.status.rawValue)")
101 | .foregroundColor(article.status.color.accessibleFontColor)
102 | .padding(.horizontal, 4)
103 | .padding(.vertical, 2)
104 | .background(article.status.color)
105 | .cornerRadius(3.0)
106 | }
107 | }
108 | }
109 | Section {
110 | TextField("Title", text: $viewModel.newArticle.title)
111 | Picker("Status", selection: $viewModel.newArticle.status) {
112 | ForEach(Status.allCases, id: \.self) { status in
113 | Text(status.rawValue)
114 | }
115 | }
116 | Button(action: viewModel.addArticle ) {
117 | Label("Add new article", systemImage: "plus")
118 | }
119 | }
120 | }
121 | }
122 | .onAppear() {
123 | viewModel.subscribe()
124 | }
125 | .onDisappear() {
126 | viewModel.unsubscribe()
127 | }
128 | }
129 | }
130 |
131 | struct MappingEnumsScreen_Previews: PreviewProvider {
132 | static var previews: some View {
133 | NavigationView {
134 | MappingEnumsScreen()
135 | }
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Screens/MappingGeoPointsScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MappingGeoPointsScreen.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 22.03.21.
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | import SwiftUI
20 | import MapKit
21 | import Firebase
22 | import FirebaseFirestoreSwift
23 |
24 | class MappingGeoPointsViewModel: ObservableObject {
25 | @Published var offices = Office.sample // [Office]()
26 | @Published var selectedOffice = Office.sample[0]
27 | @Published var selectedOfficeCoordinate: CLLocationCoordinate2D?
28 | @Published var visibleRegion: MKCoordinateRegion!
29 | @Published var errorMessage: String?
30 |
31 | private var db = Firestore.firestore()
32 | private var listenerRegistration: ListenerRegistration?
33 |
34 | init() {
35 | $selectedOffice
36 | // .compactMap { $0 }
37 | .map { office in
38 | CLLocationCoordinate2D(latitude: office.location.latitude,
39 | longitude: office.location.longitude)
40 | }
41 | .assign(to: &$selectedOfficeCoordinate)
42 |
43 | $selectedOfficeCoordinate
44 | .compactMap { $0 }
45 | .map { officeCoordinate in
46 | MKCoordinateRegion(center: officeCoordinate,
47 | span: MKCoordinateSpan(
48 | latitudeDelta: 0.005,
49 | longitudeDelta: 0.005))
50 | }
51 | .assign(to: &$visibleRegion)
52 |
53 |
54 |
55 | }
56 |
57 | fileprivate func unsubscribe() {
58 | if listenerRegistration != nil {
59 | listenerRegistration?.remove()
60 | listenerRegistration = nil
61 | }
62 | }
63 |
64 | fileprivate func subscribe() {
65 | if listenerRegistration == nil {
66 | listenerRegistration = db.collection("locations")
67 | .addSnapshotListener { [weak self] (querySnapshot, error) in
68 | guard let documents = querySnapshot?.documents else {
69 | self?.errorMessage = "No documents in the 'locations' collection"
70 | return
71 | }
72 |
73 | self?.offices = documents.compactMap { queryDocumentSnapshot in
74 | let result = Result { try queryDocumentSnapshot.data(as: Office.self) }
75 |
76 | switch result {
77 | case .success(let office):
78 | // An Office value was successfully initialized from the DocumentSnapshot.
79 | self?.errorMessage = nil
80 | return office
81 | case .failure(let error):
82 | // An Office value could not be initialized from the DocumentSnapshot.
83 | switch error {
84 | case DecodingError.typeMismatch(_, let context):
85 | self?.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
86 | case DecodingError.valueNotFound(_, let context):
87 | self?.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
88 | case DecodingError.keyNotFound(_, let context):
89 | self?.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
90 | case DecodingError.dataCorrupted(let key):
91 | self?.errorMessage = "\(error.localizedDescription): \(key)"
92 | default:
93 | self?.errorMessage = "Error decoding document: \(error.localizedDescription)"
94 | }
95 | return nil
96 | }
97 | }
98 | }
99 | }
100 | }
101 | }
102 |
103 | struct MappingGeoPointsScreen: View {
104 | @StateObject var viewModel = MappingGeoPointsViewModel()
105 |
106 | var body: some View {
107 | SampleScreen("Mapping Geopoints", introduction: "GeoPoints are supported natively in Firestore") {
108 | Form {
109 | Map(coordinateRegion: $viewModel.visibleRegion, annotationItems: viewModel.offices) { office in
110 | MapPin(coordinate: CLLocationCoordinate2D(latitude: office.location.latitude, longitude: office.location.longitude), tint: .red)
111 | }
112 | .frame(height: 300)
113 | Section(header: Text("Google Offices")) {
114 | List(viewModel.offices) { office in
115 | VStack(alignment: .leading) {
116 | Text(office.name)
117 | Text("Lat: \(office.location.latitude), Lon: \(office.location.longitude)")
118 | }
119 | .onTapGesture {
120 | viewModel.selectedOffice = office
121 | }
122 | }
123 | }
124 | }
125 | }
126 | .onAppear() {
127 | viewModel.subscribe()
128 | }
129 | .onDisappear() {
130 | viewModel.unsubscribe()
131 | }
132 | }
133 | }
134 |
135 | struct MappingGeoPointsScreen_Previews: PreviewProvider {
136 | static var previews: some View {
137 | NavigationView {
138 | MappingGeoPointsScreen()
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Screens/MappingSimpleTypesScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MappingSimpleTypesScreen.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 17.03.21.
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | import SwiftUI
20 | import Firebase
21 | import FirebaseFirestoreSwift
22 |
23 | class MappingSimpleTypesViewModel: ObservableObject {
24 | @Published var book: Book = .empty
25 | @Published var errorMessage: String?
26 |
27 | private var db = Firestore.firestore()
28 |
29 | /// Use this to alteratively use async/await or callback-based implementations
30 | let useAsync = false
31 |
32 | fileprivate func fetchAndMap() {
33 | if useAsync {
34 | Task {
35 | await fetchBookAsync(documentId: "hitchhiker")
36 | }
37 | }
38 | else {
39 | fetchBook(documentId: "hitchhiker")
40 | }
41 | }
42 |
43 | fileprivate func fetchAndMapNonExisting() {
44 | if useAsync {
45 | Task {
46 | await fetchBookAsync(documentId: "does-not-exist")
47 | }
48 | }
49 | else {
50 | fetchBook(documentId: "does-not-exist")
51 | }
52 | }
53 |
54 | fileprivate func fetchAndTryMappingInvalidData() {
55 | if useAsync {
56 | Task {
57 | await fetchBookAsync(documentId: "invalid-data")
58 | }
59 | }
60 | else {
61 | fetchBook(documentId: "invalid-data")
62 | }
63 | }
64 |
65 | /// Alternative implementation that shows how to use async/await to call `getDocument`
66 | /// This needs to be marked as @MainActor so that we can safely access the errorMessage
67 | /// published property when encountering an error.
68 | @MainActor
69 | private func fetchBookAsync(documentId: String) async {
70 | let docRef = db.collection("books").document(documentId)
71 | do {
72 | self.book = try await docRef.getDocument(as: Book.self)
73 | }
74 | catch {
75 | switch error {
76 | case DecodingError.typeMismatch(_, let context):
77 | self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
78 | case DecodingError.valueNotFound(_, let context):
79 | self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
80 | case DecodingError.keyNotFound(_, let context):
81 | self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
82 | case DecodingError.dataCorrupted(let key):
83 | self.errorMessage = "\(error.localizedDescription): \(key)"
84 | default:
85 | self.errorMessage = "Error decoding document: \(error.localizedDescription)"
86 | }
87 | }
88 | }
89 |
90 | private func fetchBook(documentId: String) {
91 | let docRef = db.collection("books").document(documentId)
92 |
93 | docRef.getDocument(as: Book.self) { result in
94 | switch result {
95 | case .success(let book):
96 | // A Book value was successfully initialized from the DocumentSnapshot.
97 | self.book = book
98 | self.errorMessage = nil
99 | case .failure(let error):
100 | // A Book value could not be initialized from the DocumentSnapshot.
101 | switch error {
102 | case DecodingError.typeMismatch(_, let context):
103 | self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
104 | case DecodingError.valueNotFound(_, let context):
105 | self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
106 | case DecodingError.keyNotFound(_, let context):
107 | self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
108 | case DecodingError.dataCorrupted(let key):
109 | self.errorMessage = "\(error.localizedDescription): \(key)"
110 | default:
111 | self.errorMessage = "Error decoding document: \(error.localizedDescription)"
112 | }
113 | }
114 | }
115 | }
116 |
117 | private func fetchBookOptional(documentId: String) {
118 | let docRef = db.collection("books").document(documentId)
119 |
120 | // If you expect that a document might *not exist*, use an optional type (Book?.self)
121 | // and then perform an `if let book = book` dance to handle this case.
122 | docRef.getDocument(as: Book?.self) { result in
123 | switch result {
124 | case .success(let book):
125 | if let book = book {
126 | // A Book value was successfully initialized from the DocumentSnapshot.
127 | self.book = book
128 | self.errorMessage = nil
129 | }
130 | else {
131 | // A nil value was successfully initialized from the DocumentSnapshot,
132 | // or the DocumentSnapshot was nil.
133 | self.errorMessage = "Document doesn't exist."
134 | }
135 | case .failure(let error):
136 | // A Book value could not be initialized from the DocumentSnapshot.
137 | switch error {
138 | case DecodingError.typeMismatch(_, let context):
139 | self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
140 | case DecodingError.valueNotFound(_, let context):
141 | self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
142 | case DecodingError.keyNotFound(_, let context):
143 | self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
144 | case DecodingError.dataCorrupted(let key):
145 | self.errorMessage = "\(error.localizedDescription): \(key)"
146 | default:
147 | self.errorMessage = "Error decoding document: \(error.localizedDescription)"
148 | }
149 | }
150 | }
151 | }
152 |
153 | fileprivate func updateBook() {
154 | if let id = book.id {
155 | let docRef = db.collection("books").document(id)
156 | do {
157 | try docRef.setData(from: book)
158 | }
159 | catch {
160 | print(error)
161 | }
162 | }
163 | }
164 |
165 | fileprivate func addBook() {
166 | let collectionRef = db.collection("books")
167 | do {
168 | let newDocReference = try collectionRef.addDocument(from: self.book)
169 | print("Book stored with new document reference: \(newDocReference)")
170 | }
171 | catch {
172 | print(error)
173 | }
174 | }
175 | }
176 |
177 | struct MappingSimpleTypesScreen: View {
178 | @StateObject var viewModel = MappingSimpleTypesViewModel()
179 |
180 | @ViewBuilder
181 | var messageView: some View {
182 | if let errorMessage = viewModel.errorMessage {
183 | Text(errorMessage).foregroundColor(.red)
184 | }
185 | else {
186 | EmptyView()
187 | }
188 | }
189 |
190 | var body: some View {
191 | SampleScreen("Mapping Simple Types", introduction: "Firestore makes mapping simple data types as simple as possible.") {
192 | Form {
193 | Section(header: Text("Book"), footer: messageView) {
194 | TextField("Title", text: $viewModel.book.title)
195 | TextField("Number of pages", value: $viewModel.book.numberOfPages, formatter: NumberFormatter())
196 | TextField("Author", text: $viewModel.book.author)
197 | }
198 | Section(header: Text("Actions")) {
199 | Button(action: viewModel.fetchAndMap) {
200 | Label("Fetch and map a book", systemImage: "square.and.arrow.up")
201 | }
202 | Button(action: viewModel.fetchAndMapNonExisting) {
203 | Label("Fetch and map a non-existing book", systemImage: "square.and.arrow.up")
204 | }
205 | Button(action: viewModel.fetchAndTryMappingInvalidData) {
206 | Label("Try mapping book from invalid data", systemImage: "square.and.arrow.up")
207 | }
208 |
209 | Button(action: viewModel.updateBook) {
210 | Label("Udpate book", systemImage: "square.and.arrow.down")
211 | }
212 | Button(action: viewModel.addBook) {
213 | Label("Add Book", systemImage: "plus")
214 | }
215 | }
216 | }
217 | }
218 | }
219 | }
220 |
221 | struct MappingSimpleTypesScreen_Previews: PreviewProvider {
222 | static var previews: some View {
223 | NavigationView {
224 | MappingSimpleTypesScreen()
225 | }
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Screens/MenuScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MenuScreen.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 17.03.21.
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | import SwiftUI
20 |
21 | struct MenuScreen: View {
22 | var body: some View {
23 | SampleScreen("Firestore & Codable",
24 | introduction: "Learn how to map data from Firestore using Swift's Codable API. \n\n"
25 | + "To run this demo, install the Firebase Emulator Suite and run the 'start.sh' script in the root folder of this project.")
26 | {
27 | Form {
28 | Section(header: Text("Mapping manually")) {
29 | NavigationLink(destination: ManuallyMappingSimpleTypesScreen()) {
30 | Label("Manually mapping simple types", systemImage: "hand.draw")
31 | }
32 | }
33 | Section(header: Text("Mapping simple types")) {
34 | NavigationLink(destination: MappingSimpleTypesScreen()) {
35 | Label("Mapping simple types", systemImage: "list.bullet.rectangle")
36 | }
37 | }
38 | Section(header: Text("Mapping complex types")) {
39 | NavigationLink(destination: MappingCustomTypesScreen()) {
40 | Label("Mapping custom types", systemImage: "rectangle.3.offgrid")
41 | }
42 | NavigationLink(destination: MappingArraysScreen()) {
43 | Label("Mapping arrays", systemImage: "square.stack.3d.down.forward")
44 | }
45 | NavigationLink(destination: MappingArraysWithNestedTypesScreen()) {
46 | Label("Mapping arrays w/ custom types", systemImage: "square.stack.3d.down.forward")
47 | }
48 | NavigationLink(destination: CustomizeMappingScreen()) {
49 | Label("Mapping dates and times", systemImage: "calendar.badge.clock")
50 | }
51 | NavigationLink(destination: MappingGeoPointsScreen()) {
52 | Label("Mapping GeoPoints", systemImage: "globe")
53 | }
54 | NavigationLink(destination: MappingColorsScreen()) {
55 | Label("Mapping Colors", systemImage: "paintpalette")
56 | }
57 | NavigationLink(destination: MappingEnumsScreen()) {
58 | Label("Mapping Enums", systemImage: "list.number")
59 | }
60 | NavigationLink(destination: CustomizeMappingScreen()) {
61 | Label("Custom mapping", systemImage: "sparkles.square.fill.on.square")
62 | }
63 | }
64 | }
65 | }
66 | }
67 | }
68 |
69 | struct MenuScreen_Previews: PreviewProvider {
70 | static var previews: some View {
71 | NavigationView {
72 | MenuScreen()
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Utilities/Color+Codable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Color+Codable.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 18.03.21.
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | import SwiftUI
20 |
21 | // Inspired by https://cocoacasts.com/from-hex-to-uicolor-and-back-in-swift
22 | // Make Color codable. This includes support for transparency.
23 | // See https://www.digitalocean.com/community/tutorials/css-hex-code-colors-alpha-values
24 | extension Color: Codable {
25 | init(hex: String) {
26 | let rgba = hex.toRGBA()
27 |
28 | self.init(.sRGB,
29 | red: Double(rgba.r),
30 | green: Double(rgba.g),
31 | blue: Double(rgba.b),
32 | opacity: Double(rgba.alpha))
33 | }
34 |
35 | public init(from decoder: Decoder) throws {
36 | let container = try decoder.singleValueContainer()
37 | let hex = try container.decode(String.self)
38 |
39 | let rgba = hex.toRGBA()
40 |
41 | self.init(.sRGB,
42 | red: Double(rgba.r),
43 | green: Double(rgba.g),
44 | blue: Double(rgba.b),
45 | opacity: Double(rgba.alpha))
46 | }
47 |
48 | public func encode(to encoder: Encoder) throws {
49 | var container = encoder.singleValueContainer()
50 | try container.encode(toHex)
51 | }
52 |
53 | var toHex: String? {
54 | return toHex()
55 | }
56 |
57 | func toHex(alpha: Bool = false) -> String? {
58 | guard let components = cgColor?.components, components.count >= 3 else {
59 | return nil
60 | }
61 |
62 | let r = Float(components[0])
63 | let g = Float(components[1])
64 | let b = Float(components[2])
65 | var a = Float(1.0)
66 |
67 | if components.count >= 4 {
68 | a = Float(components[3])
69 | }
70 |
71 | if alpha {
72 | return String(format: "#%02lX%02lX%02lX%02lX",
73 | lroundf(r * 255),
74 | lroundf(g * 255),
75 | lroundf(b * 255),
76 | lroundf(a * 255)).lowercased()
77 | }
78 | else {
79 | return String(format: "#%02lX%02lX%02lX",
80 | lroundf(r * 255),
81 | lroundf(g * 255),
82 | lroundf(b * 255)).lowercased()
83 | }
84 | }
85 | }
86 |
87 | extension String {
88 | func toRGBA() -> (r: CGFloat, g: CGFloat, b: CGFloat, alpha: CGFloat) {
89 | var hexSanitized = self.trimmingCharacters(in: .whitespacesAndNewlines)
90 | hexSanitized = hexSanitized.replacingOccurrences(of: "#", with: "")
91 |
92 | var rgb: UInt64 = 0
93 |
94 | var r: CGFloat = 0.0
95 | var g: CGFloat = 0.0
96 | var b: CGFloat = 0.0
97 | var a: CGFloat = 1.0
98 |
99 | let length = hexSanitized.count
100 |
101 | Scanner(string: hexSanitized).scanHexInt64(&rgb)
102 |
103 | if length == 6 {
104 | r = CGFloat((rgb & 0xFF0000) >> 16) / 255.0
105 | g = CGFloat((rgb & 0x00FF00) >> 8) / 255.0
106 | b = CGFloat(rgb & 0x0000FF) / 255.0
107 | }
108 | else if length == 8 {
109 | r = CGFloat((rgb & 0xFF000000) >> 24) / 255.0
110 | g = CGFloat((rgb & 0x00FF0000) >> 16) / 255.0
111 | b = CGFloat((rgb & 0x0000FF00) >> 8) / 255.0
112 | a = CGFloat(rgb & 0x000000FF) / 255.0
113 | }
114 |
115 | return (r, g, b, a)
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Utilities/Color+FontColor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Color+FontColor.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 19.03.21.
6 | //
7 | // This code is from Apple's ScrumDinger tutorial.
8 | //
9 | // Licensed under the Apache License, Version 2.0 (the "License");
10 | // you may not use this file except in compliance with the License.
11 | // You may obtain a copy of the License at
12 | //
13 | // http://www.apache.org/licenses/LICENSE-2.0
14 | //
15 | // Unless required by applicable law or agreed to in writing, software
16 | // distributed under the License is distributed on an "AS IS" BASIS,
17 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | // See the License for the specific language governing permissions and
19 | // limitations under the License.
20 |
21 | import SwiftUI
22 |
23 | extension Color {
24 | // MARK: - font colors
25 | /// This color is either black or white, whichever is more accessible when viewed against the scrum color.
26 | var accessibleFontColor: Color {
27 | var red: CGFloat = 0
28 | var green: CGFloat = 0
29 | var blue: CGFloat = 0
30 | UIColor(self).getRed(&red, green: &green, blue: &blue, alpha: nil)
31 | return isLightColor(red: red, green: green, blue: blue) ? .black : .white
32 | }
33 |
34 | private func isLightColor(red: CGFloat, green: CGFloat, blue: CGFloat) -> Bool {
35 | let lightRed = red > 0.65
36 | let lightGreen = green > 0.65
37 | let lightBlue = blue > 0.65
38 |
39 | let lightness = [lightRed, lightGreen, lightBlue].reduce(0) { $1 ? $0 + 1 : $0 }
40 | return lightness >= 2
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/codable/FirestoreCodableSamples/Utilities/SampleScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Introduction.swift
3 | // FirestoreCodableSamples
4 | //
5 | // Created by Peter Friese on 17.03.21.
6 | //
7 | // Licensed under the Apache License, Version 2.0 (the "License");
8 | // you may not use this file except in compliance with the License.
9 | // You may obtain a copy of the License at
10 | //
11 | // http://www.apache.org/licenses/LICENSE-2.0
12 | //
13 | // Unless required by applicable law or agreed to in writing, software
14 | // distributed under the License is distributed on an "AS IS" BASIS,
15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 | // See the License for the specific language governing permissions and
17 | // limitations under the License.
18 |
19 | import SwiftUI
20 |
21 | struct SampleScreen: View {
22 | var title: String
23 | var introduction: Text
24 | var content: () -> Content
25 |
26 | init(_ title: String, introduction: String, @ViewBuilder content: @escaping () -> Content) {
27 | self.title = title
28 | self.introduction = Text(introduction)
29 | self.content = content
30 | }
31 |
32 | init(_ title: String, _ introduction: Text, @ViewBuilder content: @escaping () -> Content) {
33 | self.title = title
34 | self.introduction = introduction
35 | self.content = content
36 | }
37 |
38 | var body: some View {
39 | VStack(alignment: .leading) {
40 | introduction
41 | .font(.subheadline)
42 | .foregroundColor(Color(UIColor.secondaryLabel))
43 | .padding()
44 | content()
45 | }
46 | .navigationBarTitle(title)
47 | }
48 | }
49 |
50 | struct SampleScreen_Previews: PreviewProvider {
51 | static let multiLineIntro =
52 | """
53 | This is a multiline string
54 | Just checking out
55 | Third line.
56 | """
57 | static var previews: some View {
58 | NavigationView {
59 | SampleScreen("Demo",
60 | introduction: multiLineIntro
61 | ) {
62 | List {
63 | Text("Sample")
64 | }
65 | }
66 | }
67 | .previewLayout(.sizeThatFits)
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/codable/data/auth_export/accounts.json:
--------------------------------------------------------------------------------
1 | {"kind":"identitytoolkit#DownloadAccountResponse","users":[{"localId":"CLKg2yRKdnjbY2gcQW0xNbuHJsnp","createdAt":"1652964087522","lastLoginAt":"1652964087521","validSince":"1665389798","emailVerified":false,"disabled":false},{"localId":"WITZGC3XiSQSEr1BdTPUzPkTs9Ap","createdAt":"1652773048681","lastLoginAt":"1652773048680","validSince":"1665389798","emailVerified":false,"disabled":false},{"localId":"j8F8jxfiYaPlrrut8O71O3mvsyAy","lastLoginAt":"1665390083858","createdAt":"1665390083858","lastRefreshAt":"2022-10-10T08:21:23.859Z"}]}
--------------------------------------------------------------------------------
/codable/data/auth_export/config.json:
--------------------------------------------------------------------------------
1 | {"signIn":{"allowDuplicateEmails":false}}
--------------------------------------------------------------------------------
/codable/data/firebase-export-metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "11.11.0",
3 | "firestore": {
4 | "version": "1.14.4",
5 | "path": "firestore_export",
6 | "metadata_file": "firestore_export/firestore_export.overall_export_metadata"
7 | },
8 | "auth": {
9 | "version": "11.11.0",
10 | "path": "auth_export"
11 | }
12 | }
--------------------------------------------------------------------------------
/codable/data/firestore_export/all_namespaces/all_kinds/all_namespaces_all_kinds.export_metadata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peterfriese/Swift-Firestore-Guide/5c844b9a1665112174fd6e2839740bf04871748b/codable/data/firestore_export/all_namespaces/all_kinds/all_namespaces_all_kinds.export_metadata
--------------------------------------------------------------------------------
/codable/data/firestore_export/all_namespaces/all_kinds/output-0:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peterfriese/Swift-Firestore-Guide/5c844b9a1665112174fd6e2839740bf04871748b/codable/data/firestore_export/all_namespaces/all_kinds/output-0
--------------------------------------------------------------------------------
/codable/data/firestore_export/firestore_export.overall_export_metadata:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peterfriese/Swift-Firestore-Guide/5c844b9a1665112174fd6e2839740bf04871748b/codable/data/firestore_export/firestore_export.overall_export_metadata
--------------------------------------------------------------------------------
/codable/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "emulators": {
3 | "firestore": {
4 | "port": 8080,
5 | "rules": "firestore.rules"
6 | },
7 | "ui": {
8 | "enabled": true,
9 | "-port": 4000
10 | },
11 | "auth": {
12 | "port": 9099
13 | }
14 | },
15 | "firestore": {
16 | "rules": "firestore.rules",
17 | "indexes": "firestore.indexes.json"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/codable/firestore.indexes.json:
--------------------------------------------------------------------------------
1 | {
2 | "indexes": [],
3 | "fieldOverrides": []
4 | }
5 |
--------------------------------------------------------------------------------
/codable/firestore.rules:
--------------------------------------------------------------------------------
1 | rules_version = '2';
2 | service cloud.firestore {
3 | match /databases/{database}/documents {
4 | match /{document=**} {
5 | allow read, write: if request.auth != null;
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/codable/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | firebase emulators:start --import=./data --export-on-exit
3 |
--------------------------------------------------------------------------------
/images/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peterfriese/Swift-Firestore-Guide/5c844b9a1665112174fd6e2839740bf04871748b/images/header.png
--------------------------------------------------------------------------------
/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peterfriese/Swift-Firestore-Guide/5c844b9a1665112174fd6e2839740bf04871748b/images/logo.png
--------------------------------------------------------------------------------
/images/swift-logo-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peterfriese/Swift-Firestore-Guide/5c844b9a1665112174fd6e2839740bf04871748b/images/swift-logo-512.png
--------------------------------------------------------------------------------
/images/swift-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/peterfriese/Swift-Firestore-Guide/5c844b9a1665112174fd6e2839740bf04871748b/images/swift-logo.png
--------------------------------------------------------------------------------