mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-01-12 12:20:30 +01:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6de397958a | ||
|
|
55292a84dc | ||
|
|
1ddbe20c86 | ||
|
|
da9e8c1550 |
@@ -1,6 +1,6 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = "CoreStore"
|
||||
s.version = "4.0.0"
|
||||
s.version = "4.0.2"
|
||||
s.license = "MIT"
|
||||
s.summary = "Unleashing the real power of Core Data with the elegance and safety of Swift"
|
||||
s.homepage = "https://github.com/JohnEstropia/CoreStore"
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
import CoreStore
|
||||
|
||||
// MARK: - AppDelegate
|
||||
|
||||
|
||||
@@ -17,11 +17,11 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.1</string>
|
||||
<string>4.0.2</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<string>4</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
|
||||
@@ -46,7 +46,7 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD
|
||||
self.present(alert, animated: true, completion: nil)
|
||||
|
||||
|
||||
let modelMetadata = withExtendedLifetime(DataStack(modelName: "MigrationDemo")) {
|
||||
let modelMetadata = withExtendedLifetime(DataStack(xcodeModelName: "MigrationDemo")) {
|
||||
(dataStack: DataStack) -> ModelMetadata in
|
||||
|
||||
let models = self.models
|
||||
@@ -91,6 +91,11 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD
|
||||
}
|
||||
}
|
||||
|
||||
func listMonitorDidRefetch(_ monitor: ListMonitor<NSManagedObject>) {
|
||||
|
||||
self.listMonitorDidChange(monitor)
|
||||
}
|
||||
|
||||
// MARK: UITableViewDataSource
|
||||
|
||||
@objc dynamic func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
@@ -148,7 +153,10 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD
|
||||
label: "Model V1",
|
||||
entityType: OrganismV1.self,
|
||||
schemaHistory: SchemaHistory(
|
||||
modelName: "MigrationDemo",
|
||||
XcodeDataModelSchema.from(
|
||||
modelName: "MigrationDemo",
|
||||
migrationChain: ["MigrationDemoV3", "MigrationDemoV2", "MigrationDemo"]
|
||||
),
|
||||
migrationChain: ["MigrationDemoV3", "MigrationDemoV2", "MigrationDemo"]
|
||||
)
|
||||
),
|
||||
@@ -156,7 +164,13 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD
|
||||
label: "Model V2",
|
||||
entityType: OrganismV2.self,
|
||||
schemaHistory: SchemaHistory(
|
||||
modelName: "MigrationDemo",
|
||||
XcodeDataModelSchema.from(
|
||||
modelName: "MigrationDemo",
|
||||
migrationChain: [
|
||||
"MigrationDemo": "MigrationDemoV2",
|
||||
"MigrationDemoV3": "MigrationDemoV2"
|
||||
]
|
||||
),
|
||||
migrationChain: [
|
||||
"MigrationDemo": "MigrationDemoV2",
|
||||
"MigrationDemoV3": "MigrationDemoV2"
|
||||
@@ -167,7 +181,10 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD
|
||||
label: "Model V3",
|
||||
entityType: OrganismV3.self,
|
||||
schemaHistory: SchemaHistory(
|
||||
modelName: "MigrationDemo",
|
||||
XcodeDataModelSchema.from(
|
||||
modelName: "MigrationDemo",
|
||||
migrationChain: ["MigrationDemo", "MigrationDemoV2", "MigrationDemoV3"]
|
||||
),
|
||||
migrationChain: ["MigrationDemo", "MigrationDemoV2", "MigrationDemoV3"]
|
||||
)
|
||||
)
|
||||
|
||||
@@ -17,7 +17,7 @@ private struct Static {
|
||||
|
||||
static let facebookStack: DataStack = {
|
||||
|
||||
let dataStack = DataStack(modelName: "StackSetupDemo")
|
||||
let dataStack = DataStack(xcodeModelName: "StackSetupDemo")
|
||||
try! dataStack.addStorageAndWait(
|
||||
SQLiteStore(
|
||||
fileName: "AccountsDemo_FB_Male.sqlite",
|
||||
@@ -55,7 +55,7 @@ private struct Static {
|
||||
|
||||
static let twitterStack: DataStack = {
|
||||
|
||||
let dataStack = DataStack(modelName: "StackSetupDemo")
|
||||
let dataStack = DataStack(xcodeModelName: "StackSetupDemo")
|
||||
try! dataStack.addStorageAndWait(
|
||||
SQLiteStore(
|
||||
fileName: "AccountsDemo_TW_Male.sqlite",
|
||||
|
||||
36
README.md
36
README.md
@@ -22,6 +22,8 @@ Unleashing the real power of Core Data with the elegance and safety of Swift
|
||||
|
||||
Upgrading from CoreStore 3.x to 4.x? Check out the [new features](#features) and make sure to read the [Migration guide](#upgrading-from-3xx-to-4xx).
|
||||
|
||||
CoreStore is now part of the [Swift Source Compatibility projects](https://swift.org/source-compatibility/#current-list-of-projects).
|
||||
|
||||
|
||||
## Why use CoreStore?
|
||||
CoreStore is the answer to the [challenges](http://inessential.com/2010/02/26/on_switching_away_from_core_data) [of](http://bsktapp.com/blog/why-is-realm-great-and-why-are-we-not-using-it/) [using](https://www.quora.com/Why-would-you-use-Realm-over-Core-Data) [Core](http://sebastiandobrincu.com/blog/5-reasons-why-you-should-choose-realm-over-coredata) [Data](https://medium.com/the-way-north/ditching-core-data-865c1bb5564c#.a5h8ou6ri).
|
||||
@@ -88,6 +90,7 @@ CoreStore was (and is) heavily shaped by real-world needs of developing data-dep
|
||||
- [Observe a list of objects](#observe-a-list-of-objects)
|
||||
- [Objective-C support](#objective-c-support)
|
||||
- [Type-safe `CoreStoreObject`s](#type-safe-corestoreobjects)
|
||||
- [`VersionLock`s](#versionlocks)
|
||||
- [Roadmap](#roadmap)
|
||||
- [Installation](#installation)
|
||||
- [Changesets](#changesets)
|
||||
@@ -1696,6 +1699,39 @@ let puppies = CoreStore.fetchAll(
|
||||
|
||||
All CoreStore APIs that are usable with `NSManagedObject`s are also available for `CoreStoreObject`s. These include `ListMonitor`s, `ImportableObject`s, fetching, etc.
|
||||
|
||||
### `VersionLock`s
|
||||
|
||||
While it is convenient to be able to declare entities only in code, it is worrying that we might accidentally change the `CoreStoreObject`'s properties and break our users' model version history. For this, the `CoreStoreSchema` allows us to "lock" our properties to a particular configuration. Any changes to that `VersionLock` will raise an assertion failure during the `CoreStoreSchema` initialization, so you can then look for the commit which changed the `VersionLock` hash.
|
||||
|
||||
To use `VersionLock`s, create the `CoreStoreSchema`, run the app, and look for this particular log message that is automatically printed to the console:
|
||||
|
||||
<img width="700" alt="VersionLock" src="https://cloud.githubusercontent.com/assets/3029684/26525632/757f1bd0-4398-11e7-9795-4132a2df0538.png" />
|
||||
|
||||
Copy this dictionary value and use it as the `versionLock:` argument of the `CoreStoreSchema` initializer:
|
||||
```swift
|
||||
CoreStoreSchema(
|
||||
modelVersion: "V1",
|
||||
entities: [
|
||||
Entity<Animal>("Animal", isAbstract: true),
|
||||
Entity<Dog>("Dog"),
|
||||
Entity<Person>("Person"),
|
||||
],
|
||||
versionLock: [
|
||||
"Animal": [0x1b59d511019695cf, 0xdeb97e86c5eff179, 0x1cfd80745646cb3, 0x4ff99416175b5b9a],
|
||||
"Dog": [0xe3f0afeb109b283a, 0x29998d292938eb61, 0x6aab788333cfc2a3, 0x492ff1d295910ea7],
|
||||
"Person": [0x66d8bbfd8b21561f, 0xcecec69ecae3570f, 0xc4b73d71256214ef, 0x89b99bfe3e013e8b]
|
||||
]
|
||||
)
|
||||
```
|
||||
You can also get this hash after the `DataStack` has been fully set up by printing to the console:
|
||||
```swift
|
||||
print(CoreStore.defaultStack.modelSchema.printCoreStoreSchema())
|
||||
```
|
||||
|
||||
Once the version lock is set, any changes in the properties or to the model will trigger an assertion failure similar to this:
|
||||
|
||||
<img width="700" alt="VersionLock failure" src="https://cloud.githubusercontent.com/assets/3029684/26525666/92f46f0c-4399-11e7-9395-4379f6f20876.png" />
|
||||
|
||||
|
||||
# Installation
|
||||
- Requires:
|
||||
|
||||
@@ -32,7 +32,7 @@ import Foundation
|
||||
/**
|
||||
A `SchemaMappingProvider` that accepts custom mappings for some entities. Mappings of entities with no `CustomMapping` provided will be automatically calculated if possible.
|
||||
*/
|
||||
open class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
||||
public class CustomSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
||||
|
||||
/**
|
||||
The source model version for the mapping.
|
||||
|
||||
@@ -32,7 +32,7 @@ import Foundation
|
||||
public extension DynamicSchema {
|
||||
|
||||
/**
|
||||
Prints the `DynamicSchema` as their corresponding `CoreStoreObject` Swift declarations. This is useful for converting current `XcodeDataModelSchema`-based models into the new `CoreStoreSchema` framework. Additional adjustments may need to be done to the generated source code for "Transformable" attributes.
|
||||
Prints the `DynamicSchema` as their corresponding `CoreStoreObject` Swift declarations. This is useful for converting current `XcodeDataModelSchema`-based models into the new `CoreStoreSchema` framework. Additional adjustments may need to be done to the generated source code; for example: `Transformable` concrete types need to be provided, as well as `default` values.
|
||||
|
||||
- Important: After transitioning to the new `CoreStoreSchema` framework, it is recommended to add the new schema as a new version that the existing versions' `XcodeDataModelSchema` can migrate to. It is discouraged to load existing SQLite files created with `XcodeDataModelSchema` directly into a `CoreStoreSchema`.
|
||||
- returns: a string that represents the source code for the `DynamicSchema` as their corresponding `CoreStoreObject` Swift declarations.
|
||||
@@ -153,8 +153,7 @@ public extension DynamicSchema {
|
||||
}
|
||||
case .dateAttributeType:
|
||||
valueType = Date.self
|
||||
if let defaultValue = (attribute.defaultValue as! Date.ImportableNativeType?).flatMap(Date.cs_fromImportableNativeType),
|
||||
defaultValue != Date.cs_emptyValue() {
|
||||
if let defaultValue = (attribute.defaultValue as! Date.ImportableNativeType?).flatMap(Date.cs_fromImportableNativeType) {
|
||||
|
||||
defaultString = ", default: Date(timeIntervalSinceReferenceDate: \(defaultValue.timeIntervalSinceReferenceDate))"
|
||||
}
|
||||
@@ -194,9 +193,11 @@ public extension DynamicSchema {
|
||||
let indexedString = attribute.isIndexed ? ", isIndexed: true" : ""
|
||||
let transientString = attribute.isTransient ? ", isTransient: true" : ""
|
||||
// TODO: escape strings
|
||||
let versionHashModifierString = attribute.versionHashModifier.flatMap({ ", versionHashModifier: \"\($0)\"" }) ?? ""
|
||||
let versionHashModifierString = attribute.versionHashModifier
|
||||
.flatMap({ ", versionHashModifier: \"\($0)\"" }) ?? ""
|
||||
// TODO: escape strings
|
||||
let renamingIdentifierString = attribute.renamingIdentifier.flatMap({ ", renamingIdentifier: \"\($0)\"" }) ?? ""
|
||||
let renamingIdentifierString = attribute.renamingIdentifier
|
||||
.flatMap({ ($0 == attributeName ? "" : ", renamingIdentifier: \"\($0)\"") as String }) ?? ""
|
||||
output.append(" let \(attributeName) = \(containerType)<\(String(describing: valueType))>(\"\(attributeName)\"\(indexedString)\(defaultString)\(transientString)\(versionHashModifierString)\(renamingIdentifierString))\n")
|
||||
}
|
||||
}
|
||||
@@ -261,8 +262,10 @@ public extension DynamicSchema {
|
||||
fatalError("Unsupported delete rule \((relationship.deleteRule)) for relationship \"\(relationshipQualifier)\"")
|
||||
}
|
||||
}
|
||||
let versionHashModifierString = relationship.versionHashModifier.flatMap({ ", versionHashModifier: \"\($0)\"" }) ?? ""
|
||||
let renamingIdentifierString = relationship.renamingIdentifier.flatMap({ ", renamingIdentifier: \"\($0)\"" }) ?? ""
|
||||
let versionHashModifierString = relationship.versionHashModifier
|
||||
.flatMap({ ", versionHashModifier: \"\($0)\"" }) ?? ""
|
||||
let renamingIdentifierString = relationship.renamingIdentifier
|
||||
.flatMap({ ($0 == relationshipName ? "" : ", renamingIdentifier: \"\($0)\"") as String }) ?? ""
|
||||
output.append(" let \(relationshipName) = \(containerType)<\(relationship.destinationEntity!.name!)>(\"\(relationshipName)\"\(inverseString)\(deleteRuleString)\(minCountString)\(maxCountString)\(versionHashModifierString)\(renamingIdentifierString))\n")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,12 +64,6 @@ public protocol ImportableAttributeType: QueryableAttributeType {
|
||||
*/
|
||||
associatedtype ImportableNativeType: QueryableNativeType
|
||||
|
||||
/**
|
||||
Returns the default "empty" value for this type.
|
||||
*/
|
||||
@inline(__always)
|
||||
static func cs_emptyValue() -> Self
|
||||
|
||||
/**
|
||||
Creates an instance of this type from its `ImportableNativeType` value.
|
||||
*/
|
||||
@@ -84,18 +78,31 @@ public protocol ImportableAttributeType: QueryableAttributeType {
|
||||
}
|
||||
|
||||
|
||||
// MARK: - EmptyableAttributeType
|
||||
|
||||
/**
|
||||
`ImportableAttributeType`s that have a natural "empty" value. Example: `0` for `Int`, `""` for `String`.
|
||||
|
||||
- Discussion: Not all `ImportableAttributeType`s can have empty values. `URL`s and `Date`s for example have no obvious empty values.
|
||||
*/
|
||||
public protocol EmptyableAttributeType: ImportableAttributeType {
|
||||
|
||||
/**
|
||||
Returns the default "empty" value for this type.
|
||||
*/
|
||||
@inline(__always)
|
||||
static func cs_emptyValue() -> Self
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Bool
|
||||
|
||||
extension Bool: ImportableAttributeType {
|
||||
extension Bool: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
public typealias ImportableNativeType = NSNumber
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Bool {
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Bool? {
|
||||
|
||||
@@ -107,21 +114,26 @@ extension Bool: ImportableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Bool {
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - CGFloat
|
||||
|
||||
extension CGFloat: ImportableAttributeType {
|
||||
extension CGFloat: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
public typealias ImportableNativeType = NSNumber
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> CGFloat {
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> CGFloat? {
|
||||
|
||||
@@ -133,21 +145,26 @@ extension CGFloat: ImportableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> CGFloat {
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Data
|
||||
|
||||
extension Data: ImportableAttributeType {
|
||||
extension Data: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
public typealias ImportableNativeType = NSData
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Data {
|
||||
|
||||
return Data()
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Data? {
|
||||
|
||||
@@ -159,6 +176,15 @@ extension Data: ImportableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Data {
|
||||
|
||||
return Data()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -166,13 +192,9 @@ extension Data: ImportableAttributeType {
|
||||
|
||||
extension Date: ImportableAttributeType {
|
||||
|
||||
public typealias ImportableNativeType = NSDate
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Date {
|
||||
|
||||
return Date(timeIntervalSinceReferenceDate: 0)
|
||||
}
|
||||
public typealias ImportableNativeType = NSDate
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Date? {
|
||||
@@ -190,16 +212,12 @@ extension Date: ImportableAttributeType {
|
||||
|
||||
// MARK: - Double
|
||||
|
||||
extension Double: ImportableAttributeType {
|
||||
extension Double: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
public typealias ImportableNativeType = NSNumber
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Double {
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Double? {
|
||||
|
||||
@@ -211,21 +229,26 @@ extension Double: ImportableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Double {
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Float
|
||||
|
||||
extension Float: ImportableAttributeType {
|
||||
extension Float: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
public typealias ImportableNativeType = NSNumber
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Float {
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Float? {
|
||||
|
||||
@@ -237,21 +260,26 @@ extension Float: ImportableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Float {
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Int
|
||||
|
||||
extension Int: ImportableAttributeType {
|
||||
extension Int: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
public typealias ImportableNativeType = NSNumber
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Int {
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Int? {
|
||||
|
||||
@@ -263,21 +291,26 @@ extension Int: ImportableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Int {
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Int8
|
||||
|
||||
extension Int8: ImportableAttributeType {
|
||||
extension Int8: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
public typealias ImportableNativeType = NSNumber
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Int8 {
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Int8? {
|
||||
|
||||
@@ -289,21 +322,26 @@ extension Int8: ImportableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Int8 {
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Int16
|
||||
|
||||
extension Int16: ImportableAttributeType {
|
||||
extension Int16: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
public typealias ImportableNativeType = NSNumber
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Int16 {
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Int16? {
|
||||
|
||||
@@ -315,21 +353,26 @@ extension Int16: ImportableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Int16 {
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Int32
|
||||
|
||||
extension Int32: ImportableAttributeType {
|
||||
extension Int32: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
public typealias ImportableNativeType = NSNumber
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Int32 {
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Int32? {
|
||||
|
||||
@@ -341,21 +384,26 @@ extension Int32: ImportableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Int32 {
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Int64
|
||||
|
||||
extension Int64: ImportableAttributeType {
|
||||
extension Int64: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
public typealias ImportableNativeType = NSNumber
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Int64 {
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Int64? {
|
||||
|
||||
@@ -367,21 +415,26 @@ extension Int64: ImportableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> Int64 {
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - NSData
|
||||
|
||||
extension NSData: ImportableAttributeType {
|
||||
extension NSData: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
public typealias ImportableNativeType = NSData
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
public class func cs_emptyValue() -> Self {
|
||||
|
||||
return self.init()
|
||||
}
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? {
|
||||
|
||||
@@ -393,6 +446,15 @@ extension NSData: ImportableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
public class func cs_emptyValue() -> Self {
|
||||
|
||||
return self.init()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -400,13 +462,9 @@ extension NSData: ImportableAttributeType {
|
||||
|
||||
extension NSDate: ImportableAttributeType {
|
||||
|
||||
public typealias ImportableNativeType = NSDate
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
public class func cs_emptyValue() -> Self {
|
||||
|
||||
return self.init(timeIntervalSinceReferenceDate: 0)
|
||||
}
|
||||
public typealias ImportableNativeType = NSDate
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? {
|
||||
@@ -424,16 +482,12 @@ extension NSDate: ImportableAttributeType {
|
||||
|
||||
// MARK: - NSNumber
|
||||
|
||||
extension NSNumber: ImportableAttributeType {
|
||||
extension NSNumber: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
public typealias ImportableNativeType = NSNumber
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
public class func cs_emptyValue() -> Self {
|
||||
|
||||
return self.init()
|
||||
}
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? {
|
||||
|
||||
@@ -445,21 +499,26 @@ extension NSNumber: ImportableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
public class func cs_emptyValue() -> Self {
|
||||
|
||||
return self.init()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - NSString
|
||||
|
||||
extension NSString: ImportableAttributeType {
|
||||
extension NSString: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
public typealias ImportableNativeType = NSString
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
public class func cs_emptyValue() -> Self {
|
||||
|
||||
return self.init()
|
||||
}
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? {
|
||||
|
||||
@@ -471,6 +530,15 @@ extension NSString: ImportableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
public class func cs_emptyValue() -> Self {
|
||||
|
||||
return self.init()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -478,13 +546,9 @@ extension NSString: ImportableAttributeType {
|
||||
|
||||
extension NSURL: ImportableAttributeType {
|
||||
|
||||
public typealias ImportableNativeType = NSString
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
public class func cs_emptyValue() -> Self {
|
||||
|
||||
return self.init(string: "")!
|
||||
}
|
||||
public typealias ImportableNativeType = NSString
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? {
|
||||
@@ -504,16 +568,9 @@ extension NSURL: ImportableAttributeType {
|
||||
|
||||
extension NSUUID: ImportableAttributeType {
|
||||
|
||||
public typealias ImportableNativeType = NSString
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
public class func cs_emptyValue() -> Self {
|
||||
|
||||
enum Static {
|
||||
|
||||
static var zero = Array<UInt8>(repeating: 0, count: 16)
|
||||
}
|
||||
return self.init(uuidBytes: &Static.zero)
|
||||
}
|
||||
public typealias ImportableNativeType = NSString
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? {
|
||||
@@ -531,16 +588,12 @@ extension NSUUID: ImportableAttributeType {
|
||||
|
||||
// MARK: - String
|
||||
|
||||
extension String: ImportableAttributeType {
|
||||
extension String: ImportableAttributeType, EmptyableAttributeType {
|
||||
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
public typealias ImportableNativeType = NSString
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> String {
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> String? {
|
||||
|
||||
@@ -552,6 +605,15 @@ extension String: ImportableAttributeType {
|
||||
|
||||
return self.cs_toQueryableNativeType()
|
||||
}
|
||||
|
||||
|
||||
// MARK: EmptyableAttributeType
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_emptyValue() -> String {
|
||||
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -559,16 +621,9 @@ extension String: ImportableAttributeType {
|
||||
|
||||
extension URL: ImportableAttributeType {
|
||||
|
||||
public typealias ImportableNativeType = NSString
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
public static func cs_emptyValue() -> URL {
|
||||
|
||||
enum Static {
|
||||
|
||||
static let empty = URL(string: "")!
|
||||
}
|
||||
return Static.empty
|
||||
}
|
||||
public typealias ImportableNativeType = NSString
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> URL? {
|
||||
@@ -588,20 +643,9 @@ extension URL: ImportableAttributeType {
|
||||
|
||||
extension UUID: ImportableAttributeType {
|
||||
|
||||
public typealias ImportableNativeType = NSString
|
||||
// MARK: ImportableAttributeType
|
||||
|
||||
public static func cs_emptyValue() -> UUID {
|
||||
|
||||
enum Static {
|
||||
|
||||
static let empty: UUID = cs_lazy {
|
||||
|
||||
var zero = Array<UInt8>(repeating: 0, count: 16)
|
||||
return NSUUID(uuidBytes: &zero) as UUID
|
||||
}
|
||||
}
|
||||
return Static.empty
|
||||
}
|
||||
public typealias ImportableNativeType = NSString
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> UUID? {
|
||||
@@ -623,11 +667,6 @@ extension RawRepresentable where RawValue: ImportableAttributeType {
|
||||
|
||||
public typealias ImportableNativeType = RawValue.ImportableNativeType
|
||||
|
||||
public static func cs_emptyValue() -> Self {
|
||||
|
||||
return self.init(rawValue: RawValue.cs_emptyValue())!
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? {
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ import Foundation
|
||||
/**
|
||||
A `SchemaMappingProvider` that tries to infer model migration between two `DynamicSchema` versions by searching all `xcmappingmodel`s from `Bundle.allBundles` or by relying on lightweight migration if possible. Throws an error if lightweight migration is impossible for the two `DynamicSchema`. This mapping is automatically used as a fallback mapping provider, even if no mapping providers are explicitly declared in the `StorageInterface`.
|
||||
*/
|
||||
final class InferredSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
||||
public final class InferredSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
||||
|
||||
// MARK: Equatable
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.0.0</string>
|
||||
<string>4.0.2</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
|
||||
@@ -102,7 +102,7 @@ public enum ValueContainer<O: CoreStoreObject> {
|
||||
}
|
||||
```
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter default: the initial value for the property when the object is first created. Defaults to the `ImportableAttributeType`'s empty value if not specified.
|
||||
- parameter default: the initial value for the property when the object is first created. For types that implement `EmptyableAttributeType`s, this argument may be omitted and the type's "empty" value will be used instead (e.g. `false` for `Bool`, `0` for `Int`, `""` for `String`, etc.)
|
||||
- parameter isIndexed: `true` if the property should be indexed for searching, otherwise `false`. Defaults to `false` if not specified.
|
||||
- parameter isTransient: `true` if the property is transient, otherwise `false`. Defaults to `false` if not specified. The transient flag specifies whether or not a property's value is ignored when an object is saved to a persistent store. Transient properties are not saved to the persistent store, but are still managed for undo, redo, validation, and so on.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
@@ -115,7 +115,7 @@ public enum ValueContainer<O: CoreStoreObject> {
|
||||
- parameter finalNewValue: the transformed new value
|
||||
- parameter originalNewValue: the original new value
|
||||
*/
|
||||
public init(_ keyPath: KeyPath, `default`: V = V.cs_emptyValue(), isIndexed: Bool = false, isTransient: Bool = false, versionHashModifier: String? = nil, renamingIdentifier: String? = nil, customGetter: @escaping (_ `self`: O, _ getValue: () -> V) -> V = { $1() }, customSetter: @escaping (_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void = { $1($2) }) {
|
||||
public init(_ keyPath: KeyPath, `default`: V, isIndexed: Bool = false, isTransient: Bool = false, versionHashModifier: String? = nil, renamingIdentifier: String? = nil, customGetter: @escaping (_ `self`: O, _ getValue: () -> V) -> V = { $1() }, customSetter: @escaping (_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void = { $1($2) }) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.isIndexed = isIndexed
|
||||
@@ -338,6 +338,43 @@ public enum ValueContainer<O: CoreStoreObject> {
|
||||
}
|
||||
}
|
||||
|
||||
public extension ValueContainer.Required where V: EmptyableAttributeType {
|
||||
|
||||
/**
|
||||
Initializes the metadata for the property. This convenience initializer uses the `EmptyableAttributeType`'s "empty" value as the initial value for the property when the object is first created (e.g. `false` for `Bool`, `0` for `Int`, `""` for `String`, etc.)
|
||||
```
|
||||
class Person: CoreStoreObject {
|
||||
let title = Value.Required<String>("title") // initial value defaults to empty string
|
||||
}
|
||||
```
|
||||
- parameter keyPath: the permanent attribute name for this property.
|
||||
- parameter isIndexed: `true` if the property should be indexed for searching, otherwise `false`. Defaults to `false` if not specified.
|
||||
- parameter isTransient: `true` if the property is transient, otherwise `false`. Defaults to `false` if not specified. The transient flag specifies whether or not a property's value is ignored when an object is saved to a persistent store. Transient properties are not saved to the persistent store, but are still managed for undo, redo, validation, and so on.
|
||||
- parameter versionHashModifier: used to mark or denote a property as being a different "version" than another even if all of the values which affect persistence are equal. (Such a difference is important in cases where the properties are unchanged but the format or content of its data are changed.)
|
||||
- parameter renamingIdentifier: used to resolve naming conflicts between models. When creating an entity mapping between entities in two managed object models, a source entity property and a destination entity property that share the same identifier indicate that a property mapping should be configured to migrate from the source to the destination. If unset, the identifier will be the property's name.
|
||||
- parameter customGetter: use this closure to make final transformations to the property's value before returning from the getter.
|
||||
- parameter self: the `CoreStoreObject`
|
||||
- parameter getValue: the original getter for the property
|
||||
- parameter customSetter: use this closure to make final transformations to the new value before assigning to the property.
|
||||
- parameter setValue: the original setter for the property
|
||||
- parameter finalNewValue: the transformed new value
|
||||
- parameter originalNewValue: the original new value
|
||||
*/
|
||||
public convenience init(_ keyPath: KeyPath, isIndexed: Bool = false, isTransient: Bool = false, versionHashModifier: String? = nil, renamingIdentifier: String? = nil, customGetter: @escaping (_ `self`: O, _ getValue: () -> V) -> V = { $1() }, customSetter: @escaping (_ `self`: O, _ setValue: (_ finalNewValue: V) -> Void, _ originalNewValue: V) -> Void = { $1($2) }) {
|
||||
|
||||
self.init(
|
||||
keyPath,
|
||||
default: V.cs_emptyValue(),
|
||||
isIndexed: isIndexed,
|
||||
isTransient: isTransient,
|
||||
versionHashModifier: versionHashModifier,
|
||||
renamingIdentifier: renamingIdentifier,
|
||||
customGetter: customGetter,
|
||||
customSetter: customSetter
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - TransformableContainer
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ import Foundation
|
||||
/**
|
||||
A `SchemaMappingProvider` that tries to infer model migration between two `DynamicSchema` versions by loading an xcmappingmodel file from the specified `Bundle`. Throws `CoreStoreError.mappingModelNotFound` if the xcmappingmodel file cannot be found, or if the xcmappingmodel doesn't resolve the source and destination `DynamicSchema`.
|
||||
*/
|
||||
final class XcodeSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
||||
public final class XcodeSchemaMappingProvider: Hashable, SchemaMappingProvider {
|
||||
|
||||
/**
|
||||
The source model version for the mapping.
|
||||
|
||||
Reference in New Issue
Block a user