Compare commits

...

4 Commits
4.0.0 ... 4.0.2

Author SHA1 Message Date
John Rommel Estropia
6de397958a fix demo app warnings 2017-06-02 02:06:47 +09:00
John Rommel Estropia
55292a84dc made mapping modell providers public 2017-06-02 02:03:41 +09:00
John Rommel Estropia
1ddbe20c86 Updated README, fixed demo app, and bumped to 4.0.1 2017-05-28 11:37:40 +09:00
John Rommel Estropia
da9e8c1550 disallow "empty" default values on some ImportableAttributeTypes 2017-05-28 10:50:25 +09:00
13 changed files with 312 additions and 181 deletions

View File

@@ -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"

View File

@@ -8,7 +8,6 @@
import UIKit
import CoreStore
// MARK: - AppDelegate

View File

@@ -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>

View File

@@ -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"]
)
)

View File

@@ -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",

View File

@@ -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:

View File

@@ -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.

View File

@@ -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")
}
}

View File

@@ -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? {

View File

@@ -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

View File

@@ -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>

View File

@@ -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

View File

@@ -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.