Changelog History
Page 1
-
v7.3.1
November 11, 2020 -
v7.3.0 Changes
September 19, 2020🆕 New Demo app
⏪ The old CoreStoreDemo app has been renamed to LegacyDemo, and a new Demo app now showcases CoreStore features through SwiftUI:
Don't worry, standard UIKit samples are also available (thanks to
UIViewControllerRepresentable
)🆓 Feel free to suggest improvements to the Demo app!
👍 Swift 5.3 / Xcode 12 / iOS 14 Support
⏪ CoreStore now compiles using Xcode 12 and Swift 5.3!
⏪ ⚠️ There was a bug in Swift 5.3
propertyWrappers
where Segmentation Faults happen during compile time. CoreStore was able to work around this issue through runtimefatalError
s, but the result is that missing required parameters for@Field
properties may not be caught during compile-time. The runtime checks crash if there are missing parameters, so please take care to debug your models! -
v7.2.0 Changes
June 20, 20200️⃣ Default values vs. Initial values
⏪ One common mistake when assigning default values to
CoreStoreObject
properties is to assign it a value and expect it to be evaluated whenever an object is created:// ❌class Person: CoreStoreObject { @Field.Stored("identifier") var identifier: UUID = UUID() // [email protected]("createdDate") var createdDate: Date = Date() // Wrong!}
0️⃣ This default value will be evaluated only when the
DataStack
sets up the schema, and all instances will end up having the same values. This syntax for "default values" are usually used only for actual reasonable constant values, or sentinel values such as""
or0
.👍 For actual "initial values",
@Field.Stored
and@Field.Coded
now supports dynamic evaluation during object creation via thedynamicInitialValue:
argument:// ✅class Person: CoreStoreObject { @Field.Stored("identifier", dynamicInitialValue: { UUID() }) var identifier: UUID @Field.Stored("createdDate", dynamicInitialValue: { Date() }) var createdDate: Date }
0️⃣ When using this feature, a "default value" should not be assigned (i.e. no
=
expression). -
v7.1.0 Changes
March 27, 2020⚡️ Maintenance updates
- 👍 Xcode 11.4 and Swift 5.2 support
🆕 New Property Wrappers syntax
⏪ ⚠️ These changes apply only to
CoreStoreObject
subclasses, notNSManagedObject
s.🍱 ‼️ Please take note of the warnings below before migrating or else the model's hash might change.
👍 If conversion is too risky, the current
Value.Required
,Value.Optional
,Transformable.Required
,Transformable.Optional
,Relationship.ToOne
,Relationship.ToManyOrdered
, andRelationship.ToManyUnordered
will all be supported for while so you can opt to use them as is for now.🍱 ‼️ If you are confident about conversion, I cannot stress this enough, but please make sure to set your schema's
VersionLock
before converting!@Field.Stored
(replacement for non "transient"Value.Required
andValue.Optional
)class Person: CoreStoreObject { @Field.Stored("title") var title: String = "Mr."@Field.Stored("nickname") var nickname: String?}
🍱 ⚠️ Only
Value.Required
andValue.Optional
that are NOT transient values can be converted toField.Stored
.
🍱 ⚠️ When converting, make sure that all parameters, including the default values, are exactly the same or else the model's hash might change.@Field.Virtual
(replacement for "transient" versions ofValue.Required
andValue.Optional
)class Animal: CoreStoreObject { @Field.Virtual( "pluralName", customGetter: { (object, field) inreturn object.$species.value + "s" } ) var pluralName: [email protected]("species") var species: String = ""}
🍱 ⚠️ Only
Value.Required
andValue.Optional
that ARE transient values can be converted toField.Virtual
.
🍱 ⚠️ When converting, make sure that all parameters, including the default values, are exactly the same or else the model's hash might change.👍
@Field.Coded
(replacement forTransformable.Required
andTransformable.Optional
, with additional support for custom encoders such as JSON)class Person: CoreStoreObject { @Field.Coded( "bloodType", coder: { encode: { $0.toData() }, decode: { BloodType(fromData: $0) } } ) var bloodType: BloodType?}
🍱 ‼️ The current
Transformable.Required
andTransformable.Optional
mechanism have no safe conversion to@Field.Coded
. Please use@Field.Coded
only for newly added attributes.@Field.Relationship
(replacement forRelationship.ToOne
,Relationship.ToManyOrdered
, andRelationship.ToManyUnordered
)class Pet: CoreStoreObject { @Field.Relationship("master") var master: Person?}class Person: CoreStoreObject { @Field.Relationship("pets", inverse: \.$master) var pets: Set\<Pet\>}
🍱 ⚠️
Relationship.ToOne<T>
maps toT?
,Relationship.ToManyOrdered
maps toArray<T>
, andRelationship.ToManyUnordered
maps toSet<T>
🍱 ⚠️ When converting, make sure that all parameters, including the default values, are exactly the same or else the model's hash might change.Usage
Before diving into the properties themselves, note that they will effectively force you to use a different syntax for queries:
- Before:
From<Person>.where(\.title == "Mr.")
- After:
From<Person>.where(\.$title == "Mr.")
There are a several advantages to using these Property Wrappers:
- 👀 The
@propertyWrapper
versions will be magnitudes performant and efficient than their current implementations. CurrentlyMirror
reflection is used a lot to inject theNSManagedObject
reference into the properties. With@propertyWrapper
s this will be synthesized by the compiler for us. (See apple/swift#25884) - The
@propertyWrapper
versions, beingstruct
s, will give the compiler a lot more room for optimizations which were not possible before due to the need for mutable classes. - You can now add computed properties that are accessible to both
ObjectSnapshot
s andObjectPublisher
s by declaring them as@Field.Virtual
. Note that forObjectSnapshot
s, the computed values are evaluated only once during creation and are not recomputed afterwards.
The only disadvantage will be:
- ⚡️ You need to update your code by hand to migrate to the new
@propertyWrapper
s
(But the legacy ones will remain available for quite a while, so while it is recommended to migrate soon, no need to panic)
-
v7.0.4
February 07, 2020 -
v7.0.3
January 08, 2020 -
v7.0.2
December 23, 2019 -
v7.0.1
October 25, 2019 -
v7.0.0 Changes
October 22, 2019⚡️ ⚠️This update will break current code. Make sure to read the changes below:
💥 Breaking Changes
🚀 Starting version
7.0.0
, CoreStore will be using a lot of Swift 5.1 features, both internally and in its public API. You can keep using the last6.3.2
release if you still need Swift 5.0.🗄 Deprecations
⏪ The
CoreStore
-namespaced API has been deprecated in favor ofDataStack
method calls. If you are using the global utilities such asCoreStore.defaultStack
andCoreStore.logger
, a newCoreStoreDefaults
namespace has been provided:- ⏪
CoreStore.defaultStack
->CoreStoreDefaults.dataStack
- ⏪
CoreStore.logger
->CoreStoreDefaults.logger
- ⏪
CoreStore.addStorage(...)
->CoreStoreDefaults.dataStack.addStorage(...)
- ⏪
CoreStore.fetchAll(...)
->CoreStoreDefaults.dataStack.fetchAll(...)
- etc.
If you have been using your own properties to store
DataStack
references, then you should not be affected by this change.🆕 New features
Backwards-portable DiffableDataSources implementation
UITableViews
andUICollectionViews
now have a new ally:ListPublisher
s provide diffable snapshots that make reloading animations very easy and very safe. Say goodbye toUITableViews
andUICollectionViews
reload errors!🍎 DiffableDataSource.CollectionView (iOS and macOS) and DiffableDataSource.TableView (iOS)
self.dataSource = DiffableDataSource.CollectionView\<Person\>( collectionView: self.collectionView, dataStack: CoreStoreDefaults.dataStack, cellProvider: { (collectionView, indexPath, person) inlet cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PersonCell") as! PersonCell cell.setPerson(person) return cell } )
⚡️ This is now the recommended method of reloading
UITableView
s andUICollectionView
s because it uses list diffing to update your list views. This means that it is a lot less prone to cause layout errors.ListPublisher and ListSnapshot
⚡️
ListPublisher
is a more lightweight counterpart ofListMonitor
. UnlikeListMonitor
, it does not keep track of minute inserts, deletes, moves, and updates. It simply updates itssnapshot
property which is astruct
storing the list state at a specific point in time. ThisListSnapshot
is then usable with theDiffableDataSource
utilities (See section above).self.listPublisher = dataStack.listPublisher( From\<Person\>() .sectionBy(\.age") { "Age \($0)" } // sections are optional .where(\.title == "Engineer") .orderBy(.ascending(\.lastName)))self.listPublisher.addObserver(self) { [weak self] (listPublisher) in self?.dataSource?.apply( listPublisher.snapshot, animatingDifferences: true )}
ListSnapshot
s store onlyNSManagedObjectID
s and their sections.ObjectPublisher and ObjectSnapshot
ObjectPublisher
is a more lightweight counterpart ofObjectMonitor
. UnlikeObjectMonitor
, it does not keep track of per-property changes. You can create anObjectPublisher
from the object directly:let objectPublisher: ObjectPublisher\<Person\> = person.asPublisher(in: dataStack)
or by indexing a
ListPublisher
'sListSnapshot
:let objectPublisher = self.listPublisher.snapshot[indexPath]
The
ObjectPublisher
exposes asnapshot
property which returns anObjectSnapshot
, which is a lazily generatedstruct
containing fully-copied property values.objectPublisher.addObserver(self) { [weak self] (objectPublisher) inlet snapshot: ObjectSnapshot\<Person\> = objectPublisher.snapshot// handle changes}
This snapshot is completely thread-safe, and any mutations to it will not affect the actual object.
Intent-based Object representations
⏪ CoreStore is slowly moving to abstract object utilities based on usage intent.
⏪NSManageObject',
CoreStoreObject,
ObjectPublisher, and
ObjectSnapshotall conform to the
ObjectRepresentation` protocol, which allows conversion of each type to another:public protocol ObjectRepresentation { associatedtype ObjectType : CoreStore.DynamicObjectfunc objectID() -\> ObjectType.ObjectID func asPublisher(in dataStack: DataStack) -\> ObjectPublisher\<ObjectType\> func asReadOnly(in dataStack: DataStack) -\> ObjectType?func asEditable(in transaction: BaseDataTransaction) -\> ObjectType?func asSnapshot(in dataStack: DataStack) -\> ObjectSnapshot\<ObjectType\>?func asSnapshot(in transaction: BaseDataTransaction) -\> ObjectSnapshot\<ObjectType\>?}
ObjectMonitor
being excluded in this family was intentional; its initialization is complex enough to be an API of its own. - ⏪
-
v6.3.2
August 27, 2019