mirror of
https://github.com/caoer/CodableFirebase.git
synced 2026-06-16 18:19:51 +08:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1319fdb902 | ||
|
|
6d6ad92bee | ||
|
|
49299dd857 | ||
|
|
df13646d4d | ||
|
|
b83a25bf60 | ||
|
|
4b967b2c91 | ||
|
|
c999e5375d | ||
|
|
957809a715 | ||
|
|
f445059fdb | ||
|
|
25867e12c9 | ||
|
|
0d4a779a85 | ||
|
|
c17c0603b2 | ||
|
|
f1d465b936 |
@@ -1,5 +1,5 @@
|
|||||||
language: objective-c
|
language: objective-c
|
||||||
osx_image: xcode9.3
|
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.11"
|
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"
|
||||||
|
|||||||
@@ -177,6 +177,7 @@
|
|||||||
};
|
};
|
||||||
CE7DD36F1F9CFA81000225C5 = {
|
CE7DD36F1F9CFA81000225C5 = {
|
||||||
CreatedOnToolsVersion = 9.0.1;
|
CreatedOnToolsVersion = 9.0.1;
|
||||||
|
LastSwiftMigration = 1000;
|
||||||
ProvisioningStyle = Automatic;
|
ProvisioningStyle = Automatic;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -389,7 +390,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;
|
||||||
@@ -418,7 +419,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;
|
||||||
@@ -441,7 +442,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;
|
||||||
@@ -464,7 +465,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;
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ public protocol GeoPointType: FirestoreDecodable, FirestoreEncodable {
|
|||||||
init(latitude: Double, longitude: Double)
|
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() {}
|
||||||
|
|
||||||
@@ -76,3 +81,15 @@ extension FirestoreEncodable {
|
|||||||
throw DocumentReferenceError.typeIsNotSupported
|
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())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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.11</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>0.0.11</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,
|
||||||
@@ -500,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
|
||||||
|
|||||||
@@ -128,7 +128,14 @@ class TestCodableFirestore: XCTestCase {
|
|||||||
XCTAssertEqual((try? FirestoreEncoder().encode(val)) as NSDictionary?, ["value": val.value])
|
XCTAssertEqual((try? FirestoreEncoder().encode(val)) as NSDictionary?, ["value": val.value])
|
||||||
XCTAssertEqual(try? FirestoreDecoder().decode(TopLevelWrapper<DocumentReference>.self, from: ["value": val.value]), val)
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
@@ -195,10 +202,29 @@ fileprivate class GeoPoint: NSObject, GeoPointType {
|
|||||||
self.longitude = longitude
|
self.longitude = longitude
|
||||||
}
|
}
|
||||||
|
|
||||||
static func == (lhs: Point, rhs: Point) -> Bool {
|
override func isEqual(_ object: Any?) -> Bool {
|
||||||
return lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude
|
guard let other = object.flatMap({ $0 as? GeoPoint }) else { return false }
|
||||||
|
return latitude == other.latitude && longitude == other.longitude
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - ReferenceType
|
// MARK: - ReferenceType
|
||||||
fileprivate class DocumentReference: NSObject, DocumentReferenceType {}
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
29
README.md
29
README.md
@@ -20,13 +20,13 @@ struct Model: Codable {
|
|||||||
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 myEnum: MyEnum
|
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/):
|
||||||
|
|
||||||
@@ -43,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)
|
||||||
@@ -54,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
|
||||||
@@ -64,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)")
|
||||||
@@ -86,14 +86,15 @@ Firestore.firestore().collection("data").document("one").getDocument { (document
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### How to use `GeoPoint`, `DocumentRefence`, `FieldValue` in Firestore
|
#### How to use `GeoPoint`, `DocumentRefence`, `FieldValue`, `Timestamp` in Cloud Firestore
|
||||||
|
|
||||||
In order to use these 2 types with `Firestore`, you need to add the following code somewhere in your app:
|
In order to use these types with Cloud Firestore, you need to add the following code somewhere in your app:
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
extension DocumentReference: DocumentReferenceType {}
|
extension DocumentReference: DocumentReferenceType {}
|
||||||
extension GeoPoint: GeoPointType {}
|
extension GeoPoint: GeoPointType {}
|
||||||
extension FieldValue: FieldValueType {}
|
extension FieldValue: FieldValueType {}
|
||||||
|
extension Timestamp: TimestampType {}
|
||||||
```
|
```
|
||||||
|
|
||||||
and now they become `Codable` and can be used properly with `FirestoreEncoder` and `FirestoreDecoder`.
|
and now they become `Codable` and can be used properly with `FirestoreEncoder` and `FirestoreDecoder`.
|
||||||
@@ -111,7 +112,7 @@ platform :ios, '9.0'
|
|||||||
use_frameworks!
|
use_frameworks!
|
||||||
|
|
||||||
target 'MyApp' do
|
target 'MyApp' do
|
||||||
pod 'CodableFirebase'
|
pod 'CodableFirebase'
|
||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ platform :ios do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def release(type)
|
def release(type)
|
||||||
pod_lib_lint
|
pod_lib_lint(
|
||||||
|
allow_warnings: true,
|
||||||
|
use_libraries: true,
|
||||||
|
)
|
||||||
podspec_name = "CodableFirebase.podspec"
|
podspec_name = "CodableFirebase.podspec"
|
||||||
version = version_bump_podspec(path: podspec_name,
|
version = version_bump_podspec(path: podspec_name,
|
||||||
bump_type: type)
|
bump_type: type)
|
||||||
@@ -29,6 +32,9 @@ platform :ios do
|
|||||||
message: "#{version} release")
|
message: "#{version} release")
|
||||||
add_git_tag(tag: "#{version}")
|
add_git_tag(tag: "#{version}")
|
||||||
push_to_git_remote
|
push_to_git_remote
|
||||||
pod_push
|
pod_push(
|
||||||
|
allow_warnings: true,
|
||||||
|
use_libraries: true,
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user