mirror of
https://github.com/caoer/CodableFirebase.git
synced 2026-06-11 23:44:30 +08:00
Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
095d37e1c4 | ||
|
|
2f2d50a065 | ||
|
|
81007b2998 | ||
|
|
c907950c82 | ||
|
|
1319fdb902 | ||
|
|
6d6ad92bee | ||
|
|
49299dd857 | ||
|
|
df13646d4d | ||
|
|
b83a25bf60 | ||
|
|
4b967b2c91 | ||
|
|
c999e5375d | ||
|
|
957809a715 | ||
|
|
f445059fdb | ||
|
|
25867e12c9 | ||
|
|
0d4a779a85 | ||
|
|
c17c0603b2 | ||
|
|
f1d465b936 | ||
|
|
505f140e29 | ||
|
|
7ac3efc8b8 | ||
|
|
55b8bd92c1 | ||
|
|
9885bf2381 | ||
|
|
5e9f86496e | ||
|
|
9b65f4e473 | ||
|
|
cef13572db | ||
|
|
03459ec82a | ||
|
|
8ff6771f42 | ||
|
|
3767b9a771 | ||
|
|
c722389e5c | ||
|
|
6b0b78e597 | ||
|
|
1fbf4295b3 | ||
|
|
8ed17c7aec | ||
|
|
3cae6e90d9 | ||
|
|
222b080693 | ||
|
|
5b8627dbbe | ||
|
|
1384814716 | ||
|
|
b0e86866a0 | ||
|
|
0c60f70d91 | ||
|
|
df9dfe1dda | ||
|
|
2afa8c101c | ||
|
|
ec22c1649e | ||
|
|
8279c70901 | ||
|
|
c4ed0ec8eb | ||
|
|
fcfb7b8b1e | ||
|
|
747d540d77 | ||
|
|
e6947deb6b | ||
|
|
eaf809498a | ||
|
|
3a08c9e650 | ||
|
|
4cfd13702b | ||
|
|
3c61d54654 | ||
|
|
2528453f9b | ||
|
|
587db265cd | ||
|
|
04facd2f6d | ||
|
|
f3eed29a7d | ||
|
|
7aff81a267 | ||
|
|
9fa47dd610 | ||
|
|
31ecb57f14 | ||
|
|
6a6fcc1d07 | ||
|
|
02cd074714 | ||
|
|
69d0a835be | ||
|
|
e0b61e57e5 | ||
|
|
e32bd8f4d6 |
@@ -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'
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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>
|
||||||
@@ -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"
|
||||||
|
|||||||
77
CodableFirebase/DecodeStrategy.swift
Normal file
77
CodableFirebase/DecodeStrategy.swift
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
|||||||
89
CodableFirebase/EncodeStrategy.swift
Normal file
89
CodableFirebase/EncodeStrategy.swift
Normal 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
try value.encode(to: self)
|
do {
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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: [],
|
||||||
|
|||||||
@@ -8,14 +8,33 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
public protocol FirestoreDecodable: Decodable {}
|
||||||
|
public protocol FirestoreEncodable: Encodable {}
|
||||||
|
|
||||||
|
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 {
|
open class FirestoreDecoder {
|
||||||
public init() {}
|
public init(userInfo: [CodingUserInfoKey: Any] = [.skipFirestoreTypes: true]) {
|
||||||
|
self.userInfo = userInfo
|
||||||
|
}
|
||||||
|
|
||||||
open var userInfo: [CodingUserInfoKey : Any] = [:]
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,10 +9,12 @@
|
|||||||
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)
|
||||||
switch topLevel {
|
switch topLevel {
|
||||||
@@ -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: [],
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -95,6 +95,11 @@ class TestCodableFirebase: XCTestCase {
|
|||||||
expectedValue: expected,
|
expectedValue: expected,
|
||||||
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,
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
4
Gemfile
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
|
gem 'fastlane'
|
||||||
|
gem 'cocoapods', '1.5.0'
|
||||||
205
Gemfile.lock
Normal file
205
Gemfile.lock
Normal 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
|
||||||
69
README.md
69
README.md
@@ -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)
|
||||||
|
|
||||||
|
[](https://github.com/alickbass/CodableFirebase)
|
||||||
[](https://github.com/Carthage/Carthage)
|
[](https://github.com/Carthage/Carthage)
|
||||||
[](https://travis-ci.org/alickbass/CodableFirebase)
|
[](https://travis-ci.org/alickbass/CodableFirebase)
|
||||||
[](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
40
fastlane/Fastfile
Normal 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
39
fastlane/README.md
Normal 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).
|
||||||
Reference in New Issue
Block a user