61 Commits

Author SHA1 Message Date
Zitao Xiong
095d37e1c4 remove unused date extension 2018-11-03 17:08:17 +08:00
Zitao Xiong
2f2d50a065 add deferred to timestamp type, other types WIP 2018-11-03 17:01:52 +08:00
Zitao Xiong
81007b2998 refactor phase2, ready to change! 2018-11-03 15:46:44 +08:00
Zitao Xiong
c907950c82 refactor strategy 2018-11-03 13:10:56 +08:00
Oleksii Dykan
1319fdb902 0.2.1 release 2018-09-25 23:09:28 +01:00
Oleksii Dykan
6d6ad92bee Force to use libraries when using cocoapods 2018-09-25 23:07:06 +01:00
Oleksii Dykan
49299dd857 Merge pull request #54 from serjooo/master
Updated to Swift 4.2
2018-09-20 10:20:54 +01:00
Serj
df13646d4d Updated travis.yml to latest xcode version
Updated travis.yml to latest xcode version
2018-09-20 11:25:55 +03:00
Serj
b83a25bf60 Updated to Swift 4.2
Changed the Swift version in build settings. No source code changes was needed.
2018-09-20 10:15:19 +03:00
Oleksii Dykan
4b967b2c91 Merge pull request #45 from natan/readme-update-cloudfirestore
README.md updates: update product name, small consistency tweaks
2018-05-31 22:03:10 +01:00
nathan spindel
c999e5375d README.md updates: update product name, small consistency tweaks 2018-05-31 13:08:12 -07:00
Oleksii Dykan
957809a715 0.2.0 release 2018-05-19 22:29:52 +02:00
Oleksii Dykan
f445059fdb 0.1.0 release 2018-05-19 22:22:49 +02:00
Oleksii Dykan
25867e12c9 Merge pull request #43 from alickbass/support-firtimestamp
Add support for FirTimestamp
2018-05-19 21:19:19 +01:00
Oleksii Dykan
0d4a779a85 Update README 2018-05-19 20:01:28 +02:00
Oleksii Dykan
c17c0603b2 Add support for FirTimestamp 2018-05-19 19:50:58 +02:00
Marcos Griselli
f1d465b936 0.0.12 release 2018-05-14 10:45:12 -03:00
Oleksii Dykan
505f140e29 0.0.11 release 2018-05-13 21:40:47 +01:00
Oleksii Dykan
7ac3efc8b8 0.0.10 release 2018-05-13 21:22:43 +01:00
Oleksii Dykan
55b8bd92c1 0.0.9 release 2018-05-13 21:12:05 +01:00
Oleksii Dykan
9885bf2381 Merge pull request #39 from alickbass/fastlane_releases
Fastlane releases
2018-04-28 14:26:08 +01:00
Marcos Griselli
5e9f86496e Removed Appfile 2018-04-26 19:13:44 -03:00
Marcos Griselli
9b65f4e473 revert podspec version 2018-04-25 10:52:01 -03:00
Marcos Griselli
cef13572db fastlane version number 2018-04-25 10:37:18 -03:00
Marcos Griselli
03459ec82a 0.0.9 release 2018-04-25 10:24:41 -03:00
Marcos Griselli
8ff6771f42 Initial fastlane release configuration 2018-04-25 10:23:20 -03:00
Oleksii Dykan
3767b9a771 Merge pull request #38 from marcosgriselli/master
Prepare 0.0.8 release for Swift 4.1
2018-04-25 08:21:49 +01:00
Marcos Griselli
c722389e5c Updated info plist, podspec and xcodeproj to new release 0.0.8 and Swift 4.1 2018-04-23 06:32:36 -03:00
Oleksii Dykan
6b0b78e597 Merge pull request #33 from marcosgriselli/master
Changes for Swift 4.1 and Xcode9.3
2018-04-21 09:19:10 +01:00
Marcos Griselli
1fbf4295b3 updated travis to Xcode9.3 2018-04-20 20:29:25 -03:00
Marcos Griselli
8ed17c7aec updates flatMap to compactMap 2018-04-03 09:52:36 -03:00
Oleksii Dykan
3cae6e90d9 Bump version number 2018-02-20 23:13:02 +01:00
Oleksii Dykan
222b080693 Merge pull request #24 from alickbass/update-encoder
Update Encoder
2018-02-20 23:11:31 +01:00
Oleksii Dykan
5b8627dbbe If the value pushed a container before throwing, pop it back off to restore state 2018-02-20 23:00:29 +01:00
Oleksii Dykan
1384814716 Bump version number 2018-02-11 11:44:17 +01:00
Oleksii Dykan
b0e86866a0 Merge pull request #21 from alickbass/fix-decimal-encoding
Fix handling decimal value
2018-02-11 11:41:33 +01:00
Oleksii Dykan
0c60f70d91 Fix handling decimal value 2018-02-11 11:29:42 +01:00
Oleksii Dykan
df9dfe1dda Fix cocoapods link 2018-01-30 10:56:48 +01:00
Oleksii Dykan
2afa8c101c Bump version number 2018-01-29 11:59:22 +01:00
Oleksii Dykan
ec22c1649e Merge pull request #17 from alickbass/field-type-support
Field type support
2018-01-29 11:52:38 +01:00
Oleksii Dykan
8279c70901 Update README 2018-01-29 11:32:46 +01:00
Oleksii Dykan
c4ed0ec8eb Refactor to FirestoreDecodable and FirestoreEncodable protocols 2018-01-29 11:25:25 +01:00
Oleksii Dykan
fcfb7b8b1e Fix setting option for skipping values 2018-01-29 11:19:33 +01:00
Oleksii Dykan
747d540d77 Rename bool option to skip firestore types 2018-01-29 11:18:27 +01:00
Oleksii Dykan
e6947deb6b Bump version number 2018-01-26 12:53:08 +01:00
Oleksii Dykan
eaf809498a Merge pull request #14 from alickbass/geopoint-and-reference-encoding
Geopoint and reference encoding
2018-01-26 12:39:06 +01:00
Oleksii Dykan
3a08c9e650 Update README 2018-01-26 11:42:35 +01:00
Oleksii Dykan
4cfd13702b Cover firestore types with tests 2018-01-26 11:12:33 +01:00
Oleksii Dykan
3c61d54654 Fix checking for protocol conformance 2018-01-26 10:55:02 +01:00
Oleksii Dykan
2528453f9b Cover firebase database geopoint and reference with tests 2018-01-26 10:20:19 +01:00
Oleksii Dykan
587db265cd Make protocols public 2018-01-26 10:20:03 +01:00
Oleksii Dykan
04facd2f6d Fix skipping encoding 2018-01-26 10:19:44 +01:00
Oleksii Dykan
f3eed29a7d Add encoding the geo point and document reference 2018-01-25 17:18:29 +01:00
Oleksii Dykan
7aff81a267 Add geopointtype and documentreference protocols 2018-01-25 17:14:11 +01:00
Oleksii Dykan
9fa47dd610 Add skip geo and reference flag 2018-01-25 16:33:32 +01:00
Oleksii Dykan
31ecb57f14 Add enum example to README 2018-01-18 14:23:43 +01:00
Oleksii Dykan
6a6fcc1d07 Update README.md 2018-01-16 16:41:55 +01:00
Oleksii Dykan
02cd074714 Update README.md 2018-01-10 17:20:02 +01:00
Oleksii Dykan
69d0a835be Update README.md 2017-12-29 14:55:57 +01:00
Oleksii Dykan
e0b61e57e5 Update README.md 2017-12-28 12:16:28 +01:00
Oleksii Dykan
e32bd8f4d6 Update README.md 2017-12-28 12:12:36 +01:00
22 changed files with 852 additions and 177 deletions

View File

@@ -1,5 +1,5 @@
language: objective-c language: objective-c
osx_image: xcode9.2 osx_image: xcode10.0
env: env:
- ACTION=test PLATFORM=Mac DESTINATION='platform=OS X' - ACTION=test PLATFORM=Mac DESTINATION='platform=OS X'

View File

@@ -1,6 +1,6 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = "CodableFirebase" s.name = "CodableFirebase"
s.version = "0.0.3" s.version = "0.2.1"
s.summary = "Use Codable with Firebase" s.summary = "Use Codable with Firebase"
s.description = "This library helps you use your custom models that conform to Codable protocol with Firebase Realtime Database and Firestore" s.description = "This library helps you use your custom models that conform to Codable protocol with Firebase Realtime Database and Firestore"
s.homepage = "https://github.com/alickbass/CodableFirebase" s.homepage = "https://github.com/alickbass/CodableFirebase"
@@ -15,6 +15,5 @@ Pod::Spec.new do |s|
s.source = { :git => "https://github.com/alickbass/CodableFirebase.git", :tag => s.version, :branch => 'master'} s.source = { :git => "https://github.com/alickbass/CodableFirebase.git", :tag => s.version, :branch => 'master'}
s.source_files = "CodableFirebase/*.swift" s.source_files = "CodableFirebase/*.swift"
s.pod_target_xcconfig = { 'SWIFT_VERSION' => '4' } s.swift_version = '4.1'
end end

View File

@@ -7,10 +7,12 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
09D19A4B218D64F900A862A3 /* DecodeStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09D19A4A218D64F900A862A3 /* DecodeStrategy.swift */; };
09D19A4D218D650000A862A3 /* EncodeStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 09D19A4C218D650000A862A3 /* EncodeStrategy.swift */; };
09D19A4E218D874000A862A3 /* FirestoreDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7DD3821F9D04AE000225C5 /* FirestoreDecoder.swift */; };
09D19A4F218D88A800A862A3 /* FirestoreEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7DD3811F9D04AE000225C5 /* FirestoreEncoder.swift */; };
CE7DD3711F9CFA81000225C5 /* CodableFirebase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE7DD3671F9CFA81000225C5 /* CodableFirebase.framework */; }; CE7DD3711F9CFA81000225C5 /* CodableFirebase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE7DD3671F9CFA81000225C5 /* CodableFirebase.framework */; };
CE7DD3781F9CFA81000225C5 /* CodableFirebase.h in Headers */ = {isa = PBXBuildFile; fileRef = CE7DD36A1F9CFA81000225C5 /* CodableFirebase.h */; settings = {ATTRIBUTES = (Public, ); }; }; CE7DD3781F9CFA81000225C5 /* CodableFirebase.h in Headers */ = {isa = PBXBuildFile; fileRef = CE7DD36A1F9CFA81000225C5 /* CodableFirebase.h */; settings = {ATTRIBUTES = (Public, ); }; };
CE7DD3831F9D04AE000225C5 /* FirestoreEncoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7DD3811F9D04AE000225C5 /* FirestoreEncoder.swift */; };
CE7DD3841F9D04AE000225C5 /* FirestoreDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7DD3821F9D04AE000225C5 /* FirestoreDecoder.swift */; };
CE7DD3861F9DE4F7000225C5 /* TestCodableFirestore.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7DD3851F9DE4F7000225C5 /* TestCodableFirestore.swift */; }; CE7DD3861F9DE4F7000225C5 /* TestCodableFirestore.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE7DD3851F9DE4F7000225C5 /* TestCodableFirestore.swift */; };
CEFDBF821FF3B35B00745EBE /* Encoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFDBF811FF3B35B00745EBE /* Encoder.swift */; }; CEFDBF821FF3B35B00745EBE /* Encoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFDBF811FF3B35B00745EBE /* Encoder.swift */; };
CEFDBF861FF3B56200745EBE /* Decoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFDBF851FF3B56200745EBE /* Decoder.swift */; }; CEFDBF861FF3B56200745EBE /* Decoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEFDBF851FF3B56200745EBE /* Decoder.swift */; };
@@ -31,6 +33,8 @@
/* End PBXContainerItemProxy section */ /* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
09D19A4A218D64F900A862A3 /* DecodeStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecodeStrategy.swift; sourceTree = "<group>"; };
09D19A4C218D650000A862A3 /* EncodeStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncodeStrategy.swift; sourceTree = "<group>"; };
CE7DD3671F9CFA81000225C5 /* CodableFirebase.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CodableFirebase.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CE7DD3671F9CFA81000225C5 /* CodableFirebase.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CodableFirebase.framework; sourceTree = BUILT_PRODUCTS_DIR; };
CE7DD36A1F9CFA81000225C5 /* CodableFirebase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CodableFirebase.h; sourceTree = "<group>"; }; CE7DD36A1F9CFA81000225C5 /* CodableFirebase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CodableFirebase.h; sourceTree = "<group>"; };
CE7DD36B1F9CFA81000225C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; CE7DD36B1F9CFA81000225C5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -92,6 +96,8 @@
CE7DD3811F9D04AE000225C5 /* FirestoreEncoder.swift */, CE7DD3811F9D04AE000225C5 /* FirestoreEncoder.swift */,
CEFDBF891FF3E24200745EBE /* FirebaseDecoder.swift */, CEFDBF891FF3E24200745EBE /* FirebaseDecoder.swift */,
CEFDBF8B1FF3E3CB00745EBE /* FirebaseEncoder.swift */, CEFDBF8B1FF3E3CB00745EBE /* FirebaseEncoder.swift */,
09D19A4A218D64F900A862A3 /* DecodeStrategy.swift */,
09D19A4C218D650000A862A3 /* EncodeStrategy.swift */,
CEFDBF851FF3B56200745EBE /* Decoder.swift */, CEFDBF851FF3B56200745EBE /* Decoder.swift */,
CEFDBF811FF3B35B00745EBE /* Encoder.swift */, CEFDBF811FF3B35B00745EBE /* Encoder.swift */,
CE7DD36B1F9CFA81000225C5 /* Info.plist */, CE7DD36B1F9CFA81000225C5 /* Info.plist */,
@@ -167,7 +173,7 @@
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastSwiftUpdateCheck = 0900; LastSwiftUpdateCheck = 0900;
LastUpgradeCheck = 0900; LastUpgradeCheck = 0930;
ORGANIZATIONNAME = ViolentOctopus; ORGANIZATIONNAME = ViolentOctopus;
TargetAttributes = { TargetAttributes = {
CE7DD3661F9CFA81000225C5 = { CE7DD3661F9CFA81000225C5 = {
@@ -177,6 +183,7 @@
}; };
CE7DD36F1F9CFA81000225C5 = { CE7DD36F1F9CFA81000225C5 = {
CreatedOnToolsVersion = 9.0.1; CreatedOnToolsVersion = 9.0.1;
LastSwiftMigration = 1000;
ProvisioningStyle = Automatic; ProvisioningStyle = Automatic;
}; };
}; };
@@ -221,11 +228,13 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
CE7DD3831F9D04AE000225C5 /* FirestoreEncoder.swift in Sources */, 09D19A4D218D650000A862A3 /* EncodeStrategy.swift in Sources */,
CEFDBF8C1FF3E3CB00745EBE /* FirebaseEncoder.swift in Sources */, CEFDBF8C1FF3E3CB00745EBE /* FirebaseEncoder.swift in Sources */,
09D19A4B218D64F900A862A3 /* DecodeStrategy.swift in Sources */,
09D19A4E218D874000A862A3 /* FirestoreDecoder.swift in Sources */,
09D19A4F218D88A800A862A3 /* FirestoreEncoder.swift in Sources */,
CEFDBF821FF3B35B00745EBE /* Encoder.swift in Sources */, CEFDBF821FF3B35B00745EBE /* Encoder.swift in Sources */,
CEFDBF861FF3B56200745EBE /* Decoder.swift in Sources */, CEFDBF861FF3B56200745EBE /* Decoder.swift in Sources */,
CE7DD3841F9D04AE000225C5 /* FirestoreDecoder.swift in Sources */,
CEFDBF8A1FF3E24200745EBE /* FirebaseDecoder.swift in Sources */, CEFDBF8A1FF3E24200745EBE /* FirebaseDecoder.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@@ -265,6 +274,7 @@
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES; CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
@@ -272,6 +282,7 @@
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
@@ -324,6 +335,7 @@
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES; CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
@@ -331,6 +343,7 @@
CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
@@ -385,7 +398,7 @@
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator macosx"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator macosx";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0; SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = "1,2,3,4"; TARGETED_DEVICE_FAMILY = "1,2,3,4";
TVOS_DEPLOYMENT_TARGET = 9.0; TVOS_DEPLOYMENT_TARGET = 9.0;
WATCHOS_DEPLOYMENT_TARGET = 2.0; WATCHOS_DEPLOYMENT_TARGET = 2.0;
@@ -414,7 +427,7 @@
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES; SKIP_INSTALL = YES;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator macosx"; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator macosx";
SWIFT_VERSION = 4.0; SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = "1,2,3,4"; TARGETED_DEVICE_FAMILY = "1,2,3,4";
TVOS_DEPLOYMENT_TARGET = 9.0; TVOS_DEPLOYMENT_TARGET = 9.0;
WATCHOS_DEPLOYMENT_TARGET = 2.0; WATCHOS_DEPLOYMENT_TARGET = 2.0;
@@ -437,7 +450,7 @@
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvos watchos macosx appletvsimulator iphonesimulator watchsimulator"; SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvos watchos macosx appletvsimulator iphonesimulator watchsimulator";
SWIFT_VERSION = 4.0; SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = "1,2,3,4"; TARGETED_DEVICE_FAMILY = "1,2,3,4";
TVOS_DEPLOYMENT_TARGET = 9.0; TVOS_DEPLOYMENT_TARGET = 9.0;
WATCHOS_DEPLOYMENT_TARGET = 2.0; WATCHOS_DEPLOYMENT_TARGET = 2.0;
@@ -460,7 +473,7 @@
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvos watchos macosx appletvsimulator iphonesimulator watchsimulator"; SUPPORTED_PLATFORMS = "iphonesimulator iphoneos appletvos watchos macosx appletvsimulator iphonesimulator watchsimulator";
SWIFT_VERSION = 4.0; SWIFT_VERSION = 4.2;
TARGETED_DEVICE_FAMILY = "1,2,3,4"; TARGETED_DEVICE_FAMILY = "1,2,3,4";
TVOS_DEPLOYMENT_TARGET = 9.0; TVOS_DEPLOYMENT_TARGET = 9.0;
WATCHOS_DEPLOYMENT_TARGET = 2.0; WATCHOS_DEPLOYMENT_TARGET = 2.0;

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0900" LastUpgradeVersion = "0930"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
@@ -26,7 +26,6 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<Testables> <Testables>
<TestableReference <TestableReference
@@ -56,7 +55,6 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"

View File

@@ -0,0 +1,77 @@
//
// DecodeStrategy.swift
// CodableFirebase
//
// Created by Zitao Xiong on 11/3/18.
// Copyright © 2018 ViolentOctopus. All rights reserved.
//
import Foundation
/// The strategy to use for decoding `Date` values.
public enum DateDecodingStrategy {
/// Defer to `Date` for decoding. This is the default strategy.
case deferredToDate
case deferredToTimestamp
/// Decode the `Date` as a UNIX timestamp from a JSON number.
case secondsSince1970
/// Decode the `Date` as UNIX millisecond timestamp from a JSON number.
case millisecondsSince1970
/// Decode the `Date` as an ISO-8601-formatted string (in RFC 3339 format).
@available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
case iso8601
/// Decode the `Date` as a string parsed by the given formatter.
case formatted(DateFormatter)
/// Decode the `Date` as a custom value decoded by the given closure.
case custom((_ decoder: Decoder) throws -> Date)
}
/// The strategy to use for decoding `Data` values.
public enum DataDecodingStrategy {
/// Defer to `Data` for decoding.
case deferredToData
/// Decode the `Data` from a Base64-encoded string. This is the default strategy.
case base64
/// Decode the `Data` as a custom value decoded by the given closure.
case custom((_ decoder: Decoder) throws -> Data)
}
public enum FirestoreTypeDecodingStrategy {
case deferredToPtotocol
case custom((_ value: Any) throws -> Any)
}
extension CodingUserInfoKey {
public static let dateDecodingStrategy: CodingUserInfoKey = CodingUserInfoKey(rawValue: "dateDecodingStrategy")!
public static let dataDecodingStrategy: CodingUserInfoKey = CodingUserInfoKey(rawValue: "dataDecodingStrategy")!
public static let firestoreTypeDecodingStrategy: CodingUserInfoKey = CodingUserInfoKey(rawValue: "firestoreTypeDecodingStrategy")!
}
extension Dictionary where Key == CodingUserInfoKey, Value == Any {
var dateDecodingStrategy: DateDecodingStrategy? {
return self[.dateDecodingStrategy] as? DateDecodingStrategy
}
var dataDecodingStrategy: DataDecodingStrategy? {
return self[.dataDecodingStrategy] as? DataDecodingStrategy
}
var firestoreTypeDecodingStrategy: FirestoreTypeDecodingStrategy {
if let strategy = self[.firestoreTypeDecodingStrategy] as? FirestoreTypeDecodingStrategy {
return strategy
}
return FirestoreTypeDecodingStrategy.deferredToPtotocol
}
}

View File

@@ -9,34 +9,22 @@
import Foundation import Foundation
class _FirebaseDecoder : Decoder { class _FirebaseDecoder : Decoder {
/// Options set on the top-level encoder to pass down the decoding hierarchy.
struct _Options {
let dateDecodingStrategy: FirebaseDecoder.DateDecodingStrategy?
let dataDecodingStrategy: FirebaseDecoder.DataDecodingStrategy?
let userInfo: [CodingUserInfoKey : Any]
}
// MARK: Properties // MARK: Properties
/// The decoder's storage. /// The decoder's storage.
fileprivate var storage: _FirebaseDecodingStorage fileprivate var storage: _FirebaseDecodingStorage
fileprivate let options: _Options
/// The path to the current point in encoding. /// The path to the current point in encoding.
fileprivate(set) public var codingPath: [CodingKey] fileprivate(set) public var codingPath: [CodingKey]
/// Contextual user-provided information for use during encoding. let userInfo: [CodingUserInfoKey : Any]
public var userInfo: [CodingUserInfoKey : Any] {
return options.userInfo
}
// MARK: - Initialization // MARK: - Initialization
/// Initializes `self` with the given top-level container and options. /// Initializes `self` with the given top-level container and options.
init(referencing container: Any, at codingPath: [CodingKey] = [], options: _Options) { init(referencing container: Any, at codingPath: [CodingKey] = [], userInfo: [CodingUserInfoKey: Any]) {
self.storage = _FirebaseDecodingStorage() self.storage = _FirebaseDecodingStorage()
self.storage.push(container: container) self.storage.push(container: container)
self.codingPath = codingPath self.codingPath = codingPath
self.options = options self.userInfo = userInfo
} }
// MARK: - Decoder Methods // MARK: - Decoder Methods
@@ -129,7 +117,7 @@ fileprivate struct _FirebaseKeyedDecodingContainer<K : CodingKey> : KeyedDecodin
// MARK: - KeyedDecodingContainerProtocol Methods // MARK: - KeyedDecodingContainerProtocol Methods
public var allKeys: [Key] { public var allKeys: [Key] {
return container.keys.flatMap { Key(stringValue: $0) } return container.keys.compactMap { Key(stringValue: $0) }
} }
public func contains(_ key: Key) -> Bool { public func contains(_ key: Key) -> Bool {
@@ -409,7 +397,7 @@ fileprivate struct _FirebaseKeyedDecodingContainer<K : CodingKey> : KeyedDecodin
defer { self.decoder.codingPath.removeLast() } defer { self.decoder.codingPath.removeLast() }
let value: Any = container[key.stringValue] ?? NSNull() let value: Any = container[key.stringValue] ?? NSNull()
return _FirebaseDecoder(referencing: value, at: self.decoder.codingPath, options: decoder.options) return _FirebaseDecoder(referencing: value, at: self.decoder.codingPath, userInfo: decoder.userInfo)
} }
public func superDecoder() throws -> Decoder { public func superDecoder() throws -> Decoder {
@@ -770,7 +758,7 @@ fileprivate struct _FirebaseUnkeyedDecodingContainer : UnkeyedDecodingContainer
let value = self.container[self.currentIndex] let value = self.container[self.currentIndex]
self.currentIndex += 1 self.currentIndex += 1
return _FirebaseDecoder(referencing: value, at: decoder.codingPath, options: decoder.options) return _FirebaseDecoder(referencing: value, at: decoder.codingPath, userInfo: decoder.userInfo)
} }
} }
@@ -1108,7 +1096,7 @@ extension _FirebaseDecoder {
func unbox(_ value: Any, as type: Date.Type) throws -> Date? { func unbox(_ value: Any, as type: Date.Type) throws -> Date? {
guard !(value is NSNull) else { return nil } guard !(value is NSNull) else { return nil }
guard let options = options.dateDecodingStrategy else { guard let options = userInfo.dateDecodingStrategy else {
guard let date = value as? Date else { guard let date = value as? Date else {
throw DecodingError._typeMismatch(at: codingPath, expectation: type, reality: value) throw DecodingError._typeMismatch(at: codingPath, expectation: type, reality: value)
} }
@@ -1116,6 +1104,9 @@ extension _FirebaseDecoder {
} }
switch options { switch options {
case .deferredToTimestamp:
let timestamp = value as! TimestampType
return timestamp.dateValue()
case .deferredToDate: case .deferredToDate:
self.storage.push(container: value) self.storage.push(container: value)
let date = try Date(from: self) let date = try Date(from: self)
@@ -1161,7 +1152,7 @@ extension _FirebaseDecoder {
func unbox(_ value: Any, as type: Data.Type) throws -> Data? { func unbox(_ value: Any, as type: Data.Type) throws -> Data? {
guard !(value is NSNull) else { return nil } guard !(value is NSNull) else { return nil }
guard let options = options.dataDecodingStrategy else { guard let options = userInfo.dataDecodingStrategy else {
guard let data = value as? Data else { guard let data = value as? Data else {
throw DecodingError._typeMismatch(at: codingPath, expectation: type, reality: value) throw DecodingError._typeMismatch(at: codingPath, expectation: type, reality: value)
} }
@@ -1229,7 +1220,17 @@ extension _FirebaseDecoder {
} else if T.self == Decimal.self || T.self == NSDecimalNumber.self { } else if T.self == Decimal.self || T.self == NSDecimalNumber.self {
guard let decimal = try self.unbox(value, as: Decimal.self) else { return nil } guard let decimal = try self.unbox(value, as: Decimal.self) else { return nil }
decoded = decimal as! T decoded = decimal as! T
} else { }
else if userInfo.skipFirestoreTypes && (T.self is FirestoreDecodable.Type) {
let strategy = userInfo.firestoreTypeDecodingStrategy
switch strategy {
case .deferredToPtotocol:
decoded = value as! T
case .custom(let decodeFunc):
decoded = try decodeFunc(value) as! T
}
}
else {
self.storage.push(container: value) self.storage.push(container: value)
decoded = try T(from: self) decoded = try T(from: self)
self.storage.popContainer() self.storage.popContainer()

View File

@@ -0,0 +1,89 @@
//
// EncodeStrategy.swift
// CodableFirebase
//
// Created by Zitao Xiong on 11/3/18.
// Copyright © 2018 ViolentOctopus. All rights reserved.
//
import Foundation
/// The strategy to use for encoding `Date` values.
public enum DateEncodingStrategy {
/// Defer to `Date` for choosing an encoding. This is the default strategy.
case deferredToDate
case deferredToTimestamp((Date) -> TimestampType)
/// Encode the `Date` as a UNIX timestamp (as a JSON number).
case secondsSince1970
/// Encode the `Date` as UNIX millisecond timestamp (as a JSON number).
case millisecondsSince1970
/// Encode the `Date` as an ISO-8601-formatted string (in RFC 3339 format).
@available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
case iso8601
/// Encode the `Date` as a string formatted by the given formatter.
case formatted(DateFormatter)
/// Encode the `Date` as a custom value encoded by the given closure.
///
/// If the closure fails to encode a value into the given encoder, the encoder will encode an empty automatic container in its place.
case custom((Date, Encoder) throws -> Void)
}
/// The strategy to use for encoding `Data` values.
public enum DataEncodingStrategy {
/// Defer to `Data` for choosing an encoding.
case deferredToData
/// Encoded the `Data` as a Base64-encoded string. This is the default strategy.
case base64
/// Encode the `Data` as a custom value encoded by the given closure.
///
/// If the closure fails to encode a value into the given encoder, the encoder will encode an empty automatic container in its place.
case custom((Data, Encoder) throws -> Void)
}
public enum FirestoreTypeEncodingStrategy {
case deferredToPtotocol
case custom((_ value: Any) throws -> Any)
}
extension CodingUserInfoKey {
public static let dateEncodingStrategy: CodingUserInfoKey = CodingUserInfoKey(rawValue: "dateEncodingStrategy")!
public static let dataEncodingStrategy: CodingUserInfoKey = CodingUserInfoKey(rawValue: "dataEncodingStrategy")!
public static let skipFirestoreTypes: CodingUserInfoKey = CodingUserInfoKey(rawValue: "skipFirestoreTypes")!
public static let firestoreTypeEncodingStrategy: CodingUserInfoKey = CodingUserInfoKey(rawValue: "firestoreTypeEncodingStrategy")!
}
extension Dictionary where Key == CodingUserInfoKey, Value == Any {
var dateEncodingStrategy: DateEncodingStrategy? {
return self[.dateEncodingStrategy] as? DateEncodingStrategy
}
var dataEncodingStrategy: DataEncodingStrategy? {
return self[.dataEncodingStrategy] as? DataEncodingStrategy
}
var skipFirestoreTypes: Bool {
if let skip = self[.skipFirestoreTypes] as? Bool {
return skip
}
return false
}
var firestoreTypeEncodingStrategy: FirestoreTypeEncodingStrategy {
if let strategy = self[.firestoreTypeEncodingStrategy] as? FirestoreTypeEncodingStrategy {
return strategy
}
return FirestoreTypeEncodingStrategy.deferredToPtotocol
}
}

View File

@@ -10,24 +10,16 @@ import Foundation
class _FirebaseEncoder : Encoder { class _FirebaseEncoder : Encoder {
/// Options set on the top-level encoder to pass down the encoding hierarchy. /// Options set on the top-level encoder to pass down the encoding hierarchy.
struct _Options {
let dateEncodingStrategy: FirebaseEncoder.DateEncodingStrategy?
let dataEncodingStrategy: FirebaseEncoder.DataEncodingStrategy?
let userInfo: [CodingUserInfoKey : Any]
}
fileprivate var storage: _FirebaseEncodingStorage fileprivate var storage: _FirebaseEncodingStorage
fileprivate let options: _Options
fileprivate(set) public var codingPath: [CodingKey] fileprivate(set) public var codingPath: [CodingKey]
public var userInfo: [CodingUserInfoKey : Any] { let userInfo: [CodingUserInfoKey: Any]
return options.userInfo
}
init(options: _Options, codingPath: [CodingKey] = []) { init(userInfo: [CodingUserInfoKey: Any], codingPath: [CodingKey] = []) {
self.storage = _FirebaseEncodingStorage() self.storage = _FirebaseEncodingStorage()
self.codingPath = codingPath self.codingPath = codingPath
self.options = options self.userInfo = userInfo
} }
/// Returns whether a new element can be encoded at this coding path. /// Returns whether a new element can be encoded at this coding path.
@@ -311,9 +303,11 @@ extension _FirebaseEncoder {
} }
fileprivate func box(_ date: Date) throws -> NSObject { fileprivate func box(_ date: Date) throws -> NSObject {
guard let options = options.dateEncodingStrategy else { return date as NSDate } guard let options = userInfo.dateEncodingStrategy else { return date as NSDate }
switch options { switch options {
case .deferredToTimestamp(let converter):
return converter(date) as! NSObject
case .deferredToDate: case .deferredToDate:
// Must be called with a surrounding with(pushedKey:) call. // Must be called with a surrounding with(pushedKey:) call.
try date.encode(to: self) try date.encode(to: self)
@@ -350,7 +344,7 @@ extension _FirebaseEncoder {
} }
fileprivate func box(_ data: Data) throws -> NSObject { fileprivate func box(_ data: Data) throws -> NSObject {
guard let options = options.dataEncodingStrategy else { return data as NSData } guard let options = userInfo.dataEncodingStrategy else { return data as NSData }
switch options { switch options {
case .deferredToData: case .deferredToData:
@@ -382,14 +376,38 @@ extension _FirebaseEncoder {
return try self.box((value as! Data)) return try self.box((value as! Data))
} else if T.self == URL.self || T.self == NSURL.self { } else if T.self == URL.self || T.self == NSURL.self {
return self.box((value as! URL).absoluteString) return self.box((value as! URL).absoluteString)
} else if T.self == Decimal.self || T.self == NSDecimalNumber.self {
return (value as! NSDecimalNumber)
} else if userInfo.skipFirestoreTypes && (value is FirestoreEncodable) {
let target: Any
switch userInfo.firestoreTypeDecodingStrategy {
case .deferredToPtotocol:
target = value
case .custom(let encodeFunc):
target = try encodeFunc(value)
}
guard let result = target as? NSObject else {
throw DocumentReferenceError.typeIsNotNSObject
}
return result
} }
// The value should request a container from the _FirebaseEncoder. // The value should request a container from the _FirebaseEncoder.
let depth = storage.count let depth = self.storage.count
do {
try value.encode(to: self) try value.encode(to: self)
} catch {
// If the value pushed a container before throwing, pop it back off to restore state.
if self.storage.count > depth {
let _ = self.storage.popContainer()
}
throw error
}
// The top container should be a new container. // The top container should be a new container.
guard storage.count > depth else { guard self.storage.count > depth else {
return nil return nil
} }
@@ -507,7 +525,7 @@ fileprivate class _FirebaseReferencingEncoder : _FirebaseEncoder {
fileprivate init(referencing encoder: _FirebaseEncoder, at index: Int, wrapping array: NSMutableArray) { fileprivate init(referencing encoder: _FirebaseEncoder, at index: Int, wrapping array: NSMutableArray) {
self.encoder = encoder self.encoder = encoder
self.reference = .array(array, index) self.reference = .array(array, index)
super.init(options: encoder.options, codingPath: encoder.codingPath) super.init(userInfo: encoder.userInfo, codingPath: encoder.codingPath)
self.codingPath.append(_FirebaseKey(index: index)) self.codingPath.append(_FirebaseKey(index: index))
} }
@@ -516,7 +534,7 @@ fileprivate class _FirebaseReferencingEncoder : _FirebaseEncoder {
fileprivate init(referencing encoder: _FirebaseEncoder, at key: CodingKey, wrapping dictionary: NSMutableDictionary) { fileprivate init(referencing encoder: _FirebaseEncoder, at key: CodingKey, wrapping dictionary: NSMutableDictionary) {
self.encoder = encoder self.encoder = encoder
reference = .dictionary(dictionary, key.stringValue) reference = .dictionary(dictionary, key.stringValue)
super.init(options: encoder.options, codingPath: encoder.codingPath) super.init(userInfo: encoder.userInfo, codingPath: encoder.codingPath)
codingPath.append(key) codingPath.append(key)
} }

View File

@@ -9,53 +9,36 @@
import Foundation import Foundation
open class FirebaseDecoder { open class FirebaseDecoder {
/// The strategy to use for decoding `Date` values.
public enum DateDecodingStrategy {
/// Defer to `Date` for decoding. This is the default strategy.
case deferredToDate
/// Decode the `Date` as a UNIX timestamp from a JSON number.
case secondsSince1970
/// Decode the `Date` as UNIX millisecond timestamp from a JSON number.
case millisecondsSince1970
/// Decode the `Date` as an ISO-8601-formatted string (in RFC 3339 format).
@available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
case iso8601
/// Decode the `Date` as a string parsed by the given formatter.
case formatted(DateFormatter)
/// Decode the `Date` as a custom value decoded by the given closure.
case custom((_ decoder: Decoder) throws -> Date)
}
/// The strategy to use for decoding `Data` values.
public enum DataDecodingStrategy {
/// Defer to `Data` for decoding.
case deferredToData
/// Decode the `Data` from a Base64-encoded string. This is the default strategy.
case base64
/// Decode the `Data` as a custom value decoded by the given closure.
case custom((_ decoder: Decoder) throws -> Data)
}
public init() {} public init() {}
open var userInfo: [CodingUserInfoKey: Any] = [:] open var userInfo: [CodingUserInfoKey: Any] = [:]
open var dateDecodingStrategy: DateDecodingStrategy = .deferredToDate
open var dataDecodingStrategy: DataDecodingStrategy = .deferredToData public var dateDecodingStrategy: DateDecodingStrategy {
set {
userInfo[.dateDecodingStrategy] = newValue
}
get {
if let strategy = userInfo[.dateDecodingStrategy] as? DateDecodingStrategy {
return strategy
}
return .deferredToDate
}
}
public var dataDecodingStrategy: DataDecodingStrategy {
set {
userInfo[.dataDecodingStrategy] = newValue
}
get {
if let strategy = userInfo[.dataDecodingStrategy] as? DataDecodingStrategy {
return strategy
}
return .deferredToData
}
}
open func decode<T : Decodable>(_ type: T.Type, from container: Any) throws -> T { open func decode<T : Decodable>(_ type: T.Type, from container: Any) throws -> T {
let options = _FirebaseDecoder._Options( let decoder = _FirebaseDecoder(referencing: container, userInfo: userInfo)
dateDecodingStrategy: dateDecodingStrategy,
dataDecodingStrategy: dataDecodingStrategy,
userInfo: userInfo
)
let decoder = _FirebaseDecoder(referencing: container, options: options)
guard let value = try decoder.unbox(container, as: T.self) else { guard let value = try decoder.unbox(container, as: T.self) else {
throw DecodingError.valueNotFound(T.self, DecodingError.Context(codingPath: [], debugDescription: "The given dictionary was invalid")) throw DecodingError.valueNotFound(T.self, DecodingError.Context(codingPath: [], debugDescription: "The given dictionary was invalid"))
} }

View File

@@ -9,57 +9,12 @@
import Foundation import Foundation
open class FirebaseEncoder { open class FirebaseEncoder {
/// The strategy to use for encoding `Date` values.
public enum DateEncodingStrategy {
/// Defer to `Date` for choosing an encoding. This is the default strategy.
case deferredToDate
/// Encode the `Date` as a UNIX timestamp (as a JSON number).
case secondsSince1970
/// Encode the `Date` as UNIX millisecond timestamp (as a JSON number).
case millisecondsSince1970
/// Encode the `Date` as an ISO-8601-formatted string (in RFC 3339 format).
@available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
case iso8601
/// Encode the `Date` as a string formatted by the given formatter.
case formatted(DateFormatter)
/// Encode the `Date` as a custom value encoded by the given closure.
///
/// If the closure fails to encode a value into the given encoder, the encoder will encode an empty automatic container in its place.
case custom((Date, Encoder) throws -> Void)
}
/// The strategy to use for encoding `Data` values.
public enum DataEncodingStrategy {
/// Defer to `Data` for choosing an encoding.
case deferredToData
/// Encoded the `Data` as a Base64-encoded string. This is the default strategy.
case base64
/// Encode the `Data` as a custom value encoded by the given closure.
///
/// If the closure fails to encode a value into the given encoder, the encoder will encode an empty automatic container in its place.
case custom((Data, Encoder) throws -> Void)
}
public init() {} public init() {}
open var userInfo: [CodingUserInfoKey : Any] = [:] open var userInfo: [CodingUserInfoKey : Any] = [:]
open var dateEncodingStrategy: DateEncodingStrategy = .deferredToDate
open var dataEncodingStrategy: DataEncodingStrategy = .deferredToData
open func encode<Value : Encodable>(_ value: Value) throws -> Any { open func encode<Value : Encodable>(_ value: Value) throws -> Any {
let options = _FirebaseEncoder._Options( let encoder = _FirebaseEncoder(userInfo: userInfo)
dateEncodingStrategy: dateEncodingStrategy,
dataEncodingStrategy: dataEncodingStrategy,
userInfo: userInfo
)
let encoder = _FirebaseEncoder(options: options)
guard let topLevel = try encoder.box_(value) else { guard let topLevel = try encoder.box_(value) else {
throw EncodingError.invalidValue(value, throw EncodingError.invalidValue(value,
EncodingError.Context(codingPath: [], EncodingError.Context(codingPath: [],

View File

@@ -8,14 +8,33 @@
import Foundation import Foundation
open class FirestoreDecoder { public protocol FirestoreDecodable: Decodable {}
public init() {} public protocol FirestoreEncodable: Encodable {}
open var userInfo: [CodingUserInfoKey : Any] = [:] public typealias DocumentReferenceType = FirestoreDecodable & FirestoreEncodable
public typealias FieldValueType = FirestoreEncodable
public protocol GeoPointType: FirestoreDecodable, FirestoreEncodable {
var latitude: Double { get }
var longitude: Double { get }
init(latitude: Double, longitude: Double)
}
public protocol TimestampType: FirestoreDecodable, FirestoreEncodable {
init(date: Date)
func dateValue() -> Date
}
open class FirestoreDecoder {
public init(userInfo: [CodingUserInfoKey: Any] = [.skipFirestoreTypes: true]) {
self.userInfo = userInfo
}
public let userInfo: [CodingUserInfoKey: Any]
open func decode<T : Decodable>(_ type: T.Type, from container: [String: Any]) throws -> T { open func decode<T : Decodable>(_ type: T.Type, from container: [String: Any]) throws -> T {
let options = _FirebaseDecoder._Options(dateDecodingStrategy: nil, dataDecodingStrategy: nil, userInfo: userInfo) let decoder = _FirebaseDecoder(referencing: container,
let decoder = _FirebaseDecoder(referencing: container, options: options) userInfo: userInfo)
guard let value = try decoder.unbox(container, as: T.self) else { guard let value = try decoder.unbox(container, as: T.self) else {
throw DecodingError.valueNotFound(T.self, DecodingError.Context(codingPath: [], debugDescription: "The given dictionary was invalid")) throw DecodingError.valueNotFound(T.self, DecodingError.Context(codingPath: [], debugDescription: "The given dictionary was invalid"))
} }
@@ -23,3 +42,51 @@ open class FirestoreDecoder {
return value return value
} }
} }
enum GeoPointKeys: CodingKey {
case latitude, longitude
}
extension GeoPointType {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: GeoPointKeys.self)
let latitude = try container.decode(Double.self, forKey: .latitude)
let longitude = try container.decode(Double.self, forKey: .longitude)
self.init(latitude: latitude, longitude: longitude)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: GeoPointKeys.self)
try container.encode(latitude, forKey: .latitude)
try container.encode(longitude, forKey: .longitude)
}
}
enum DocumentReferenceError: Error {
case typeIsNotSupported
case typeIsNotNSObject
}
extension FirestoreDecodable {
public init(from decoder: Decoder) throws {
throw DocumentReferenceError.typeIsNotSupported
}
}
extension FirestoreEncodable {
public func encode(to encoder: Encoder) throws {
throw DocumentReferenceError.typeIsNotSupported
}
}
extension TimestampType {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
self.init(date: try container.decode(Date.self))
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(self.dateValue())
}
}

View File

@@ -9,9 +9,11 @@
import Foundation import Foundation
open class FirestoreEncoder { open class FirestoreEncoder {
public init() {} public init(userInfo: [CodingUserInfoKey: Any] = [.skipFirestoreTypes: true]) {
self.userInfo = userInfo
}
open var userInfo: [CodingUserInfoKey : Any] = [:] public let userInfo: [CodingUserInfoKey: Any]
open func encode<Value : Encodable>(_ value: Value) throws -> [String: Any] { open func encode<Value : Encodable>(_ value: Value) throws -> [String: Any] {
let topLevel = try encodeToTopLevelContainer(value) let topLevel = try encodeToTopLevelContainer(value)
@@ -26,8 +28,7 @@ open class FirestoreEncoder {
} }
internal func encodeToTopLevelContainer<Value : Encodable>(_ value: Value) throws -> Any { internal func encodeToTopLevelContainer<Value : Encodable>(_ value: Value) throws -> Any {
let options = _FirebaseEncoder._Options(dateEncodingStrategy: nil, dataEncodingStrategy: nil, userInfo: userInfo) let encoder = _FirebaseEncoder(userInfo: userInfo)
let encoder = _FirebaseEncoder(options: options)
guard let topLevel = try encoder.box_(value) else { guard let topLevel = try encoder.box_(value) else {
throw EncodingError.invalidValue(value, throw EncodingError.invalidValue(value,
EncodingError.Context(codingPath: [], EncodingError.Context(codingPath: [],

View File

@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>FMWK</string> <string>FMWK</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>0.0.3</string> <string>0.2.1</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string> <string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSPrincipalClass</key> <key>NSPrincipalClass</key>

View File

@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>BNDL</string> <string>BNDL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.0</string> <string>0.2.1</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>1</string>
</dict> </dict>

View File

@@ -96,6 +96,11 @@ class TestCodableFirebase: XCTestCase {
dateEncodingStrategy: .secondsSince1970, dateEncodingStrategy: .secondsSince1970,
dateDecodingStrategy: .secondsSince1970) dateDecodingStrategy: .secondsSince1970)
_testRoundTrip(of: TopLevelWrapper(FirTimestamp(date: Date(timeIntervalSince1970: seconds))),
expectedValue: expected,
dateEncodingStrategy: .secondsSince1970,
dateDecodingStrategy: .secondsSince1970)
_testRoundTrip(of: OptionalTopLevelWrapper(Date(timeIntervalSince1970: seconds)), _testRoundTrip(of: OptionalTopLevelWrapper(Date(timeIntervalSince1970: seconds)),
expectedValue: expected, expectedValue: expected,
dateEncodingStrategy: .secondsSince1970, dateEncodingStrategy: .secondsSince1970,
@@ -369,20 +374,33 @@ class TestCodableFirebase: XCTestCase {
_testRoundTrip(of: 3 as Double) _testRoundTrip(of: 3 as Double)
} }
// MARK: - GeoPoint
func testEncodingGeoPoint() {
let point = Point(latitude: 2, longitude: 2)
XCTAssertEqual((try? FirebaseEncoder().encode(point)) as? NSDictionary, ["latitude": 2, "longitude": 2])
XCTAssertEqual(try? FirebaseDecoder().decode(Point.self, from: ["latitude": 2, "longitude": 2]), point)
}
// MARK: - Document Reference
func testEncodingDocumentReference() {
XCTAssertThrowsError(try FirebaseEncoder().encode(DocumentReference()))
XCTAssertThrowsError(try FirebaseDecoder().decode(DocumentReference.self, from: []))
}
// MARK: - Helper Functions // MARK: - Helper Functions
private var _emptyDictionary: [String: Any] = [:] private var _emptyDictionary: [String: Any] = [:]
private func _testRoundTrip<T>(of value: T, private func _testRoundTrip<T>(of value: T,
expectedValue json: Any? = nil, expectedValue json: Any? = nil,
dateEncodingStrategy: FirebaseEncoder.DateEncodingStrategy = .deferredToDate, dateEncodingStrategy: DateEncodingStrategy = .deferredToDate,
dateDecodingStrategy: FirebaseDecoder.DateDecodingStrategy = .deferredToDate, dateDecodingStrategy: DateDecodingStrategy = .deferredToDate,
dataEncodingStrategy: FirebaseEncoder.DataEncodingStrategy = .base64, dataEncodingStrategy: DataEncodingStrategy = .base64,
dataDecodingStrategy: FirebaseDecoder.DataDecodingStrategy = .base64) where T : Codable, T : Equatable { dataDecodingStrategy: DataDecodingStrategy = .base64) where T : Codable, T : Equatable {
var payload: Any! = nil var payload: Any! = nil
do { do {
let encoder = FirebaseEncoder() let encoder = FirebaseEncoder()
encoder.dateEncodingStrategy = dateEncodingStrategy encoder.userInfo[.dateEncodingStrategy] = dateEncodingStrategy
encoder.dataEncodingStrategy = dataEncodingStrategy encoder.userInfo[.dataEncodingStrategy] = dataEncodingStrategy
payload = try encoder.encode(value) payload = try encoder.encode(value)
} catch { } catch {
XCTFail("Failed to encode \(T.self) to val: \(error)") XCTFail("Failed to encode \(T.self) to val: \(error)")
@@ -415,6 +433,19 @@ class TestCodableFirebase: XCTestCase {
// MARK: - Test Types // MARK: - Test Types
/* FIXME: Import from %S/Inputs/Coding/SharedTypes.swift somehow. */ /* FIXME: Import from %S/Inputs/Coding/SharedTypes.swift somehow. */
// MARK: - GeoPoint
struct Point: GeoPointType, Equatable {
let latitude: Double
let longitude: Double
static func == (lhs: Point, rhs: Point) -> Bool {
return lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude
}
}
// MARK: - ReferenceType
fileprivate struct DocumentReference: DocumentReferenceType {}
// MARK: - Empty Types // MARK: - Empty Types
fileprivate struct EmptyStruct : Codable, Equatable { fileprivate struct EmptyStruct : Codable, Equatable {
static func ==(_ lhs: EmptyStruct, _ rhs: EmptyStruct) -> Bool { static func ==(_ lhs: EmptyStruct, _ rhs: EmptyStruct) -> Bool {
@@ -474,6 +505,22 @@ fileprivate struct Timestamp : Codable, Equatable {
} }
} }
fileprivate struct FirTimestamp : TimestampType, Equatable {
let date: Date
init(date: Date) {
self.date = date
}
func dateValue() -> Date {
return date
}
static func == (_ lhs: FirTimestamp, _ rhs: FirTimestamp) -> Bool {
return lhs.date == rhs.date
}
}
/// A simple referential counter type that encodes as a single Int value. /// A simple referential counter type that encodes as a single Int value.
fileprivate final class Counter : Codable, Equatable { fileprivate final class Counter : Codable, Equatable {
var count: Int = 0 var count: Int = 0

View File

@@ -109,6 +109,55 @@ class TestCodableFirestore: XCTestCase {
_testRoundTrip(of: TopLevelWrapper(date), expected: ["value": date]) _testRoundTrip(of: TopLevelWrapper(date), expected: ["value": date])
} }
func testDecimalValue() {
let value = Decimal(2)
_testRoundTrip(of: TopLevelWrapper(value), expected: ["value": value])
}
// MARK: - GeoPoint & Document Reference
func testEncodingGeoPoint() {
let point = GeoPoint(latitude: 2, longitude: 2)
let wrapper = TopLevelWrapper(point)
XCTAssertEqual((try? FirestoreEncoder().encode(wrapper)) as NSDictionary?, ["value": point])
XCTAssertEqual(try? FirestoreDecoder().decode(TopLevelWrapper<GeoPoint>.self, from: ["value": point]), wrapper)
XCTAssertThrowsError(try FirestoreEncoder().encode(TopLevelWrapper(Point(latitude: 2, longitude: 2))))
}
func testEncodingDocumentReference() {
let val = TopLevelWrapper(DocumentReference())
XCTAssertEqual((try? FirestoreEncoder().encode(val)) as NSDictionary?, ["value": val.value])
XCTAssertEqual(try? FirestoreDecoder().decode(TopLevelWrapper<DocumentReference>.self, from: ["value": val.value]), val)
}
func testEncodingTimestamp() {
let timestamp = Timestamp(date: Date())
let wrapper = TopLevelWrapper(timestamp)
XCTAssertEqual((try? FirestoreEncoder().encode(wrapper)) as NSDictionary?, ["value": timestamp])
XCTAssertEqual(try? FirestoreDecoder().decode(TopLevelWrapper<Timestamp>.self, from: ["value": timestamp]), wrapper)
}
func testCustomEncodingTimestamp() {
let date = Date()
let timestamp = Timestamp(date: date)
// encode date to Timestamp
let encodeWrapper = TopLevelWrapper(date)
let encodeResult = (try? FirestoreEncoder(userInfo: [CodingUserInfoKey.dateEncodingStrategy: DateEncodingStrategy.deferredToTimestamp({ date in
return Timestamp(date: date)
})])
.encode(encodeWrapper)) as NSDictionary?
XCTAssertEqual(encodeResult, ["value": timestamp])
// decode timestamp to date
let decodeWrapper = TopLevelWrapper(timestamp)
let decoder = FirestoreDecoder(userInfo: [
CodingUserInfoKey.dateDecodingStrategy: DateDecodingStrategy.deferredToTimestamp
])
let decodeResult = (try? decoder.decode(TopLevelWrapper<Timestamp>.self, from: ["value": timestamp]))
XCTAssertEqual(decodeResult, decodeWrapper)
}
private func _testEncodeFailure<T : Encodable>(of value: T) { private func _testEncodeFailure<T : Encodable>(of value: T) {
do { do {
let _ = try FirestoreEncoder().encode(value) let _ = try FirestoreEncoder().encode(value)
@@ -164,3 +213,40 @@ func expectEqualPaths(_ lhs: [CodingKey], _ rhs: [CodingKey], _ prefix: String)
XCTAssertEqual(key1.stringValue, key2.stringValue, "\(prefix) CodingKey.stringValue mismatch: \(type(of: key1))('\(key1.stringValue)') != \(type(of: key2))('\(key2.stringValue)')") XCTAssertEqual(key1.stringValue, key2.stringValue, "\(prefix) CodingKey.stringValue mismatch: \(type(of: key1))('\(key1.stringValue)') != \(type(of: key2))('\(key2.stringValue)')")
} }
} }
// MARK: - GeioPoint
fileprivate class GeoPoint: NSObject, GeoPointType {
let latitude: Double
let longitude: Double
required init(latitude: Double, longitude: Double) {
self.latitude = latitude
self.longitude = longitude
}
override func isEqual(_ object: Any?) -> Bool {
guard let other = object.flatMap({ $0 as? GeoPoint }) else { return false }
return latitude == other.latitude && longitude == other.longitude
}
}
// MARK: - ReferenceType
fileprivate class DocumentReference: NSObject, DocumentReferenceType {}
// MARK: - Timestamp
fileprivate class Timestamp: NSObject, TimestampType {
let date: Date
required init(date: Date) {
self.date = date
}
func dateValue() -> Date {
return date
}
override func isEqual(_ object: Any?) -> Bool {
guard let other = object.flatMap({ $0 as? Timestamp }) else { return false }
return date == other.date
}
}

4
Gemfile Normal file
View File

@@ -0,0 +1,4 @@
source 'https://rubygems.org'
gem 'fastlane'
gem 'cocoapods', '1.5.0'

205
Gemfile.lock Normal file
View File

@@ -0,0 +1,205 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.0)
activesupport (4.2.10)
i18n (~> 0.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
atomos (0.1.2)
babosa (1.0.2)
claide (1.0.2)
cocoapods (1.5.0)
activesupport (>= 4.0.2, < 5)
claide (>= 1.0.2, < 2.0)
cocoapods-core (= 1.5.0)
cocoapods-deintegrate (>= 1.0.2, < 2.0)
cocoapods-downloader (>= 1.2.0, < 2.0)
cocoapods-plugins (>= 1.0.0, < 2.0)
cocoapods-search (>= 1.0.0, < 2.0)
cocoapods-stats (>= 1.0.0, < 2.0)
cocoapods-trunk (>= 1.3.0, < 2.0)
cocoapods-try (>= 1.1.0, < 2.0)
colored2 (~> 3.1)
escape (~> 0.0.4)
fourflusher (~> 2.0.1)
gh_inspector (~> 1.0)
molinillo (~> 0.6.5)
nap (~> 1.0)
ruby-macho (~> 1.1)
xcodeproj (>= 1.5.7, < 2.0)
cocoapods-core (1.5.0)
activesupport (>= 4.0.2, < 6)
fuzzy_match (~> 2.0.4)
nap (~> 1.0)
cocoapods-deintegrate (1.0.2)
cocoapods-downloader (1.2.0)
cocoapods-plugins (1.0.0)
nap
cocoapods-search (1.0.0)
cocoapods-stats (1.0.0)
cocoapods-trunk (1.3.0)
nap (>= 0.8, < 2.0)
netrc (~> 0.11)
cocoapods-try (1.1.0)
colored (1.2)
colored2 (3.1.2)
commander-fastlane (4.4.6)
highline (~> 1.7.2)
concurrent-ruby (1.0.5)
declarative (0.0.10)
declarative-option (0.1.0)
domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0)
dotenv (2.4.0)
emoji_regex (0.1.1)
escape (0.0.4)
excon (0.62.0)
faraday (0.15.0)
multipart-post (>= 1.2, < 3)
faraday-cookie_jar (0.0.6)
faraday (>= 0.7.4)
http-cookie (~> 1.0.0)
faraday_middleware (0.12.2)
faraday (>= 0.7.4, < 1.0)
fastimage (2.1.1)
fastlane (2.93.1)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.3, < 3.0.0)
babosa (>= 1.0.2, < 2.0.0)
bundler (>= 1.12.0, < 2.0.0)
colored
commander-fastlane (>= 4.4.6, < 5.0.0)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (~> 0.1)
excon (>= 0.45.0, < 1.0.0)
faraday (~> 0.9)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 0.9)
fastimage (>= 2.1.0, < 3.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-api-client (>= 0.13.1, < 0.14.0)
highline (>= 1.7.2, < 2.0.0)
json (< 3.0.0)
mini_magick (~> 4.5.1)
multi_json
multi_xml (~> 0.5)
multipart-post (~> 2.0.0)
plist (>= 3.1.0, < 4.0.0)
public_suffix (~> 2.0.0)
rubyzip (>= 1.1.0, < 2.0.0)
security (= 0.1.3)
simctl (~> 1.6.3)
slack-notifier (>= 2.0.0, < 3.0.0)
terminal-notifier (>= 1.6.2, < 2.0.0)
terminal-table (>= 1.4.5, < 2.0.0)
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.5.7, < 2.0.0)
xcpretty (>= 0.2.4, < 1.0.0)
xcpretty-travis-formatter (>= 0.0.3)
fourflusher (2.0.1)
fuzzy_match (2.0.4)
gh_inspector (1.1.3)
google-api-client (0.13.6)
addressable (~> 2.5, >= 2.5.1)
googleauth (~> 0.5)
httpclient (>= 2.8.1, < 3.0)
mime-types (~> 3.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
googleauth (0.6.2)
faraday (~> 0.12)
jwt (>= 1.4, < 3.0)
logging (~> 2.0)
memoist (~> 0.12)
multi_json (~> 1.11)
os (~> 0.9)
signet (~> 0.7)
highline (1.7.10)
http-cookie (1.0.3)
domain_name (~> 0.5)
httpclient (2.8.3)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
json (2.1.0)
jwt (2.1.0)
little-plugger (1.1.4)
logging (2.2.2)
little-plugger (~> 1.1)
multi_json (~> 1.10)
memoist (0.16.0)
mime-types (3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
mini_magick (4.5.1)
minitest (5.11.3)
molinillo (0.6.5)
multi_json (1.13.1)
multi_xml (0.6.0)
multipart-post (2.0.0)
nanaimo (0.2.5)
nap (1.1.0)
naturally (2.1.0)
netrc (0.11.0)
os (0.9.6)
plist (3.4.0)
public_suffix (2.0.5)
representable (3.0.4)
declarative (< 0.1.0)
declarative-option (< 0.2.0)
uber (< 0.2.0)
retriable (3.1.1)
rouge (2.0.7)
ruby-macho (1.1.0)
rubyzip (1.2.1)
security (0.1.3)
signet (0.8.1)
addressable (~> 2.3)
faraday (~> 0.9)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
simctl (1.6.4)
CFPropertyList
naturally
slack-notifier (2.3.2)
terminal-notifier (1.8.0)
terminal-table (1.8.0)
unicode-display_width (~> 1.1, >= 1.1.1)
thread_safe (0.3.6)
tty-cursor (0.5.0)
tty-screen (0.6.4)
tty-spinner (0.8.0)
tty-cursor (>= 0.5.0)
tzinfo (1.2.5)
thread_safe (~> 0.1)
uber (0.1.0)
unf (0.1.4)
unf_ext
unf_ext (0.0.7.5)
unicode-display_width (1.3.2)
word_wrap (1.0.0)
xcodeproj (1.5.7)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.2)
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.2.4)
xcpretty (0.2.8)
rouge (~> 2.0.7)
xcpretty-travis-formatter (1.0.0)
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS
ruby
DEPENDENCIES
cocoapods (= 1.5.0)
fastlane
BUNDLED WITH
1.16.1

View File

@@ -1,27 +1,32 @@
# CodableFirebase # CodableFirebase
Use [Codable](https://developer.apple.com/documentation/swift/codable) with [Firebase](https://firebase.google.com) Use [Codable](https://developer.apple.com/documentation/swift/codable) with [Firebase](https://firebase.google.com)
[![CocoaPods](https://img.shields.io/cocoapods/p/CodableFirebase.svg)](https://github.com/alickbass/CodableFirebase)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![Build Status](https://travis-ci.org/alickbass/CodableFirebase.svg?branch=master)](https://travis-ci.org/alickbass/CodableFirebase) [![Build Status](https://travis-ci.org/alickbass/CodableFirebase.svg?branch=master)](https://travis-ci.org/alickbass/CodableFirebase)
[![codecov](https://codecov.io/gh/alickbass/CodableFirebase/branch/master/graph/badge.svg)](https://codecov.io/gh/alickbass/CodableFirebase)
## Overview ## Overview
This library helps you to use your custom type that conform to `Codable` protocol with Firebase. Here's an example of model: This library helps you to use your custom types that conform to `Codable` protocol with Firebase. Here's an example of a custom model:
```swift ```swift
struct Model: Codable { struct Model: Codable {
enum MyEnum: Int, Codable {
case one, two, three
}
let stringExample: String let stringExample: String
let booleanExample: Bool let booleanExample: Bool
let numberExample: Double let numberExample: Double
let dateExample: Date let dateExample: Date
let arrayExample: [String] let arrayExample: [String]
let nullExample: Int? let optionalExample: Int?
let objectExample: [String: String] let objectExample: [String: String]
let myEnumExample: MyEnum
} }
``` ```
### Firebase Database usage ### Firebase Realtime Database usage
This is how you would use the library with [Firebase Realtime Database](https://firebase.google.com/products/realtime-database/): This is how you would use the library with [Firebase Realtime Database](https://firebase.google.com/products/realtime-database/):
@@ -38,7 +43,7 @@ Database.database().reference().child("model").setValue(data)
And here is how you would read the same value from [Firebase Realtime Database](https://firebase.google.com/products/realtime-database/): And here is how you would read the same value from [Firebase Realtime Database](https://firebase.google.com/products/realtime-database/):
```swift ```swift
Database.database().reference().child("model").observeSingleEvent(of: .value, with: { (snapshot) in Database.database().reference().child("model").observeSingleEvent(of: .value, with: { snapshot in
guard let value = snapshot.value else { return } guard let value = snapshot.value else { return }
do { do {
let model = try FirebaseDecoder().decode(Model.self, from: value) let model = try FirebaseDecoder().decode(Model.self, from: value)
@@ -49,9 +54,9 @@ Database.database().reference().child("model").observeSingleEvent(of: .value, wi
}) })
``` ```
### Firestore usage ### Firebase Cloud Firestore usage
And this is how you would encode it with [Firebase Firestore](https://firebase.google.com/products/firestore/): This is how you would encode a model with [Firebase Cloud Firestore](https://firebase.google.com/products/firestore/):
```swift ```swift
import Firebase import Firebase
@@ -59,19 +64,19 @@ import CodableFirebase
let model: Model // here you will create an instance of Model let model: Model // here you will create an instance of Model
let docData = try! FirestoreEncoder().encode(model) let docData = try! FirestoreEncoder().encode(model)
Firestore.firestore().collection("data").document("one").setData(docData) { err in Firestore.firestore().collection("data").document("one").setData(docData) { error in
if let err = err { if let error = error {
print("Error writing document: \(err)") print("Error writing document: \(error)")
} else { } else {
print("Document successfully written!") print("Document successfully written!")
} }
} }
``` ```
And this is how you would decode the same model with [Firebase Firestore](https://firebase.google.com/products/firestore/): And this is how you would decode the same model with [Firebase Cloud Firestore](https://firebase.google.com/products/firestore/):
```swift ```swift
Firestore.firestore().collection("data").document("one").getDocument { (document, error) in Firestore.firestore().collection("data").document("one").getDocument { document, error in
if let document = document { if let document = document {
let model = try! FirestoreDecoder().decode(Model.self, from: document.data()) let model = try! FirestoreDecoder().decode(Model.self, from: document.data())
print("Model: \(model)") print("Model: \(model)")
@@ -80,3 +85,43 @@ Firestore.firestore().collection("data").document("one").getDocument { (document
} }
} }
``` ```
#### How to use `GeoPoint`, `DocumentRefence`, `FieldValue`, `Timestamp` in Cloud Firestore
In order to use these types with Cloud Firestore, you need to add the following code somewhere in your app:
```swift
extension DocumentReference: DocumentReferenceType {}
extension GeoPoint: GeoPointType {}
extension FieldValue: FieldValueType {}
extension Timestamp: TimestampType {}
```
and now they become `Codable` and can be used properly with `FirestoreEncoder` and `FirestoreDecoder`.
***PLEASE NOTE*** that as `FieldValue` is only used to [`setData()` and `updateData()`](https://firebase.google.com/docs/reference/swift/firebasefirestore/api/reference/Classes/FieldValue), it only adopts the `Encodable` protocol.
## Integration
### CocoaPods (iOS 9+)
You can use CocoaPods to install CodableFirebase by adding it to your Podfile:
```swift
platform :ios, '9.0'
use_frameworks!
target 'MyApp' do
pod 'CodableFirebase'
end
```
Note that this requires CocoaPods version 36, and your iOS deployment target to be at least 9.0:
### Carthage (iOS 9+)
You can use Carthage to install CodableFirebase by adding it to your Cartfile:
```swift
github "alickbass/CodableFirebase"
```

40
fastlane/Fastfile Normal file
View File

@@ -0,0 +1,40 @@
default_platform(:ios)
platform :ios do
desc "Release a new version with a patch bump_type"
lane :patch do
release("patch")
end
desc "Release a new version with a minor bump_type"
lane :minor do
release("minor")
end
desc "Release a new version with a major bump_type"
lane :major do
release("major")
end
def release(type)
pod_lib_lint(
allow_warnings: true,
use_libraries: true,
)
podspec_name = "CodableFirebase.podspec"
version = version_bump_podspec(path: podspec_name,
bump_type: type)
increment_version_number(version_number: version)
#cocoapods
git_add
git_commit(path: ".",
message: "#{version} release")
add_git_tag(tag: "#{version}")
push_to_git_remote
pod_push(
allow_warnings: true,
use_libraries: true,
)
end
end

39
fastlane/README.md Normal file
View File

@@ -0,0 +1,39 @@
fastlane documentation
================
# Installation
Make sure you have the latest version of the Xcode command line tools installed:
```
xcode-select --install
```
Install _fastlane_ using
```
[sudo] gem install fastlane -NV
```
or alternatively using `brew cask install fastlane`
# Available Actions
## iOS
### ios patch
```
fastlane ios patch
```
Release a new version with a patch bump_type
### ios minor
```
fastlane ios minor
```
Release a new version with a minor bump_type
### ios major
```
fastlane ios major
```
Release a new version with a major bump_type
----
This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run.
More information about fastlane can be found on [fastlane.tools](https://fastlane.tools).
The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).