mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-01-11 22:30:34 +01:00
3626 lines
337 KiB
HTML
3626 lines
337 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<title>CoreStore Reference</title>
|
||
<link rel="stylesheet" type="text/css" href="css/jazzy.css" />
|
||
<link rel="stylesheet" type="text/css" href="css/highlight.css" />
|
||
<meta charset="utf-8">
|
||
<script src="js/jquery.min.js" defer></script>
|
||
<script src="js/jazzy.js" defer></script>
|
||
|
||
<script src="js/lunr.min.js" defer></script>
|
||
<script src="js/typeahead.jquery.js" defer></script>
|
||
<script src="js/jazzy.search.js" defer></script>
|
||
</head>
|
||
<body>
|
||
|
||
|
||
<a title="CoreStore Reference"></a>
|
||
|
||
<header class="header">
|
||
<p class="header-col header-col--primary">
|
||
<a class="header-link" href="index.html">
|
||
CoreStore 9.1.0 Docs
|
||
</a>
|
||
(91% documented)
|
||
</p>
|
||
|
||
<div class="header-col--secondary">
|
||
<form role="search" action="search.json">
|
||
<input type="text" placeholder="Search documentation" data-typeahead>
|
||
</form>
|
||
</div>
|
||
|
||
<p class="header-col header-col--secondary">
|
||
<a class="header-link" href="https://github.com/JohnEstropia/CoreStore">
|
||
<img class="header-icon" src="img/gh.png" alt="GitHub"/>
|
||
View on GitHub
|
||
</a>
|
||
</p>
|
||
|
||
</header>
|
||
|
||
<p class="breadcrumbs">
|
||
<a class="breadcrumb" href="index.html">CoreStore Reference</a>
|
||
<img class="carat" src="img/carat.png" alt=""/>
|
||
CoreStore Reference
|
||
</p>
|
||
|
||
<div class="content-wrapper">
|
||
<nav class="navigation">
|
||
<ul class="nav-groups">
|
||
<li class="nav-group-name">
|
||
<a class="nav-group-name-link" href="Classes.html">Classes</a>
|
||
<ul class="nav-group-tasks">
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/AsynchronousDataTransaction.html">AsynchronousDataTransaction</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/BaseDataTransaction.html">BaseDataTransaction</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/CoreStoreObject.html">CoreStoreObject</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/CoreStoreObjectObjectDiff.html">CoreStoreObjectObjectDiff</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/CoreStoreObjectOrderedDiff.html">CoreStoreObjectOrderedDiff</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/CoreStoreObjectTransformableDiff.html">CoreStoreObjectTransformableDiff</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/CoreStoreObjectUnorderedDiff.html">CoreStoreObjectUnorderedDiff</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/CoreStoreObjectValueDiff.html">CoreStoreObjectValueDiff</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/CoreStoreSchema.html">CoreStoreSchema</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/CustomSchemaMappingProvider.html">CustomSchemaMappingProvider</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/CustomSchemaMappingProvider/CustomMapping.html">– CustomMapping</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/CustomSchemaMappingProvider/UnsafeSourceObject.html">– UnsafeSourceObject</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/CustomSchemaMappingProvider/UnsafeDestinationObject.html">– UnsafeDestinationObject</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/DataStack.html">DataStack</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/DataStack/AsyncNamespace.html">– AsyncNamespace</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/DataStack/ReactiveNamespace.html">– ReactiveNamespace</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/DataStack/AddStoragePublisher.html">– AddStoragePublisher</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/DefaultLogger.html">DefaultLogger</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/DynamicEntity.html">DynamicEntity</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/Entity.html">Entity</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/InMemoryStore.html">InMemoryStore</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/InferredSchemaMappingProvider.html">InferredSchemaMappingProvider</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/ListMonitor.html">ListMonitor</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/ListPublisher.html">ListPublisher</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/ListPublisher/ReactiveNamespace.html">– ReactiveNamespace</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/ListPublisher/SnapshotPublisher.html">– SnapshotPublisher</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/ObjectMonitor.html">ObjectMonitor</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/ObjectPublisher.html">ObjectPublisher</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/ObjectPublisher/ReactiveNamespace.html">– ReactiveNamespace</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/ObjectPublisher/SnapshotPublisher.html">– SnapshotPublisher</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/SQLiteStore.html">SQLiteStore</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/SchemaHistory.html">SchemaHistory</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/SynchronousDataTransaction.html">SynchronousDataTransaction</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/UnsafeDataModelSchema.html">UnsafeDataModelSchema</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/UnsafeDataTransaction.html">UnsafeDataTransaction</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/UserInfo.html">UserInfo</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/XcodeDataModelSchema.html">XcodeDataModelSchema</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Classes/XcodeSchemaMappingProvider.html">XcodeSchemaMappingProvider</a>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li class="nav-group-name">
|
||
<a class="nav-group-name-link" href="Global%20Variables.html">Global Variables</a>
|
||
<ul class="nav-group-tasks">
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Global%20Variables.html#/s:9CoreStore0aB11ErrorDomainSSvp">CoreStoreErrorDomain</a>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li class="nav-group-name">
|
||
<a class="nav-group-name-link" href="Enums.html">Enumerations</a>
|
||
<ul class="nav-group-tasks">
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/CoreStoreDefaults.html">CoreStoreDefaults</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/CoreStoreError.html">CoreStoreError</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/CoreStoreErrorCode.html">CoreStoreErrorCode</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/DiffableDataSource.html">DiffableDataSource</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/DiffableDataSource/BaseAdapter.html">– BaseAdapter</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/DiffableDataSource/CollectionViewAdapter.html">– CollectionViewAdapter</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/DiffableDataSource/TableViewAdapter.html">– TableViewAdapter</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/FieldCoders.html">FieldCoders</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/FieldCoders/DefaultNSSecureCoding.html">– DefaultNSSecureCoding</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/FieldCoders/Json.html">– Json</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/FieldCoders/NSCoding.html">– NSCoding</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/FieldCoders/Plist.html">– Plist</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/FieldContainer.html">FieldContainer</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/FieldContainer/Stored.html">– Stored</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/FieldContainer/Coded.html">– Coded</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/FieldContainer/Relationship.html">– Relationship</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/FieldContainer/Virtual.html">– Virtual</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums.html#/s:9CoreStore8LogLevelO">LogLevel</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/MigrationProgress.html">MigrationProgress</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/MigrationType.html">MigrationType</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/RelationshipContainer.html">RelationshipContainer</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/RelationshipContainer.html#/s:9CoreStore21RelationshipContainerO10DeleteRuleO">– DeleteRule</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/RelationshipContainer/ToOne.html">– ToOne</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/RelationshipContainer/ToManyUnordered.html">– ToManyUnordered</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/RelationshipContainer/ToManyOrdered.html">– ToManyOrdered</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/SelectTerm.html">SelectTerm</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/TransformableContainer.html">TransformableContainer</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/TransformableContainer/Required.html">– Required</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/TransformableContainer/Optional.html">– Optional</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/ValueContainer.html">ValueContainer</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/ValueContainer/Required.html">– Required</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Enums/ValueContainer/Optional.html">– Optional</a>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li class="nav-group-name">
|
||
<a class="nav-group-name-link" href="Extensions.html">Extensions</a>
|
||
<ul class="nav-group-tasks">
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/Array.html">Array</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/Bool.html">Bool</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions.html#/c:objc(cs)NSBundle">Bundle</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/CGFloat.html">CGFloat</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/Data.html">Data</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/Date.html">Date</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions.html#/s:SD">Dictionary</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/DiffableDataSource.html">DiffableDataSource</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/DiffableDataSource/CollectionViewAdapter.html">– CollectionViewAdapter</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/DiffableDataSource/DefaultCollectionViewTarget.html">– DefaultCollectionViewTarget</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/Double.html">Double</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/EnvironmentValues.html">EnvironmentValues</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/Float.html">Float</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/ForEach.html">ForEach</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/Int.html">Int</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/Int16.html">Int16</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/Int32.html">Int32</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/Int64.html">Int64</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/Int8.html">Int8</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/KeyPath.html">KeyPath</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/KeyPathString.html">KeyPathString</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions.html#/c:objc(cs)NSArray">NSArray</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions.html#/c:objc(cs)NSAttributeDescription">NSAttributeDescription</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/NSAttributeType.html">NSAttributeType</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/NSData.html">NSData</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/NSDate.html">NSDate</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/NSDeleteRule.html">NSDeleteRule</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions.html#/c:objc(cs)NSDictionary">NSDictionary</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions.html#/c:objc(cs)NSEntityDescription">NSEntityDescription</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions.html#/c:objc(cs)NSError">NSError</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/NSManagedObject.html">NSManagedObject</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/NSManagedObjectContext.html">NSManagedObjectContext</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/NSManagedObjectID.html">NSManagedObjectID</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions.html#/c:objc(cs)NSManagedObjectModel">NSManagedObjectModel</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions.html#/c:objc(cs)NSMappingModel">NSMappingModel</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/NSNull.html">NSNull</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/NSNumber.html">NSNumber</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/NSOrderedSet.html">NSOrderedSet</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions.html#/c:objc(cs)NSPredicate">NSPredicate</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions.html#/c:objc(cs)NSRelationshipDescription">NSRelationshipDescription</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/NSSet.html">NSSet</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions.html#/c:objc(cs)NSSortDescriptor">NSSortDescriptor</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/NSString.html">NSString</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/NSURL.html">NSURL</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/NSUUID.html">NSUUID</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/Optional.html">Optional</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/Progress.html">Progress</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/RawRepresentable.html">RawRepresentable</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/Result.html">Result</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/Selector.html">Selector</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/Sequence.html">Sequence</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/Set.html">Set</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/String.html">String</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/URL.html">URL</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Extensions/UUID.html">UUID</a>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li class="nav-group-name">
|
||
<a class="nav-group-name-link" href="Functions.html">Functions</a>
|
||
<ul class="nav-group-tasks">
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2neoiyAA5WhereVyxGAD10ExpressionVyx_q_q0_G_q0_tAA13DynamicObjectRzAA0dE5TraitR_AA22QueryableAttributeTypeR0_r1_lF">!=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2neoiyAA5WhereVyxGAD10ExpressionVyx_q_q0_SgG_AHtAA13DynamicObjectRzAA0dE5TraitR_AA22QueryableAttributeTypeR0_r1_lF">!=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2neoiyAA5WhereVyxGAD10ExpressionVyx_q_q0_SgG_q0_tAA13DynamicObjectRzAA0dE5TraitR_AA22QueryableAttributeTypeR0_r1_lF">!=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2neoiyAA5WhereVyxGs7KeyPathCyxAA14FieldContainerO12RelationshipVyx_q_GG_21DestinationObjectTypeQy_SgtAA0abK0CRbzAA0gi5ToOneL0R_r0_lF">!=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2neoiyAA5WhereVyxGs7KeyPathCyxAA14FieldContainerO12RelationshipVyx_q_GG_q0_SgtAA0aB6ObjectCRbzAA0gI9ToOneTypeR_AA0J14RepresentationR0_0jM0Qy0_011DestinationjM0Rt_r1_lF">!=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2neoiyAA5WhereVyxGs7KeyPathCyxAA14FieldContainerO6StoredVyx_q_GG_q_tAA0aB6ObjectCRbzAA0G12StorableTypeR_r0_lF">!=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2neoiyAA5WhereVyxGs7KeyPathCyxAA14ValueContainerO8OptionalCyx_q_GG_q_SgtAA0aB6ObjectCRbzAA23ImportableAttributeTypeR_r0_lF">!=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2neoiyAA5WhereVyxGs7KeyPathCyxAA14ValueContainerO8RequiredCyx_q_GG_q_tAA0aB6ObjectCRbzAA23ImportableAttributeTypeR_r0_lF">!=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2neoiyAA5WhereVyxGs7KeyPathCyxAA21RelationshipContainerO5ToOneCyx_q_GG_q_SgtAA0aB6ObjectCRbzAPRb_r0_lF">!=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2neoiyAA5WhereVyxGs7KeyPathCyxAA21RelationshipContainerO5ToOneCyx_q_GG_q_tAA0aB6ObjectCRbzAORb_r0_lF">!=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2neoiyAA5WhereVyxGs7KeyPathCyxq_G_So17NSManagedObjectIDCtSo0gH0CRbzALRb_r0_lF">!=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2neoiyAA5WhereVyxGs7KeyPathCyxq_G_q_tSo15NSManagedObjectCRbzAA22QueryableAttributeTypeR_r0_lF">!=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2neoiyAA5WhereVyxGs7KeyPathCyxq_G_q_tSo15NSManagedObjectCRbzAJRb_r0_lF">!=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2neoiyAA5WhereVyxGs7KeyPathCyxq_G_xtAA13DynamicObjectRzAA0H14RepresentationRzSo09NSManagedH0CRb_AL0H4TypeAaJPRczr0_lF">!=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2neoiyAA5WhereVyxGs7KeyPathCyxq_SgG_AHtSo15NSManagedObjectCRbzAA22QueryableAttributeTypeR_r0_lF">!=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2neoiyAA5WhereVyxGs7KeyPathCyxq_SgG_AHtSo15NSManagedObjectCRbzAKRb_r0_lF">!=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2neoiyAA5WhereVyxGs7KeyPathCyxq_SgG_So17NSManagedObjectIDCtSo0gH0CRbzAMRb_r0_lF">!=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2neoiyAA5WhereVyxGs7KeyPathCyxq_SgG_xSgtAA13DynamicObjectRzAA0H14RepresentationRzSo09NSManagedH0CRb_AN0H4TypeAaLPRczr0_lF">!=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1loiyAA5WhereVyxGAD10ExpressionVyx_q_q0_G_q0_tAA13DynamicObjectRzAA0cD5TraitR_AA22QueryableAttributeTypeR0_SLR0_r1_lF"><(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1loiyAA5WhereVyxGAD10ExpressionVyx_q_q0_SgG_q0_tAA13DynamicObjectRzAA0cD5TraitR_AA22QueryableAttributeTypeR0_SLR0_r1_lF"><(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1loiyAA5WhereVyxGs7KeyPathCyxAA14FieldContainerO6StoredVyx_q_GG_q_tAA0aB6ObjectCRbzAA0F12OptionalTypeR_AA0f8StorableK0R_SL7WrappedAaPPRp_r0_lF"><(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1loiyAA5WhereVyxGs7KeyPathCyxAA14FieldContainerO6StoredVyx_q_GG_q_tAA0aB6ObjectCRbzAA0F12StorableTypeR_SLR_r0_lF"><(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1loiyAA5WhereVyxGs7KeyPathCyxAA14ValueContainerO8OptionalCyx_q_GG_q_SgtAA0aB6ObjectCRbzAA23ImportableAttributeTypeR_r0_lF"><(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1loiyAA5WhereVyxGs7KeyPathCyxAA14ValueContainerO8RequiredCyx_q_GG_q_tAA0aB6ObjectCRbzAA23ImportableAttributeTypeR_SLR_r0_lF"><(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1loiyAA5WhereVyxGs7KeyPathCyxq_G_q_tSo15NSManagedObjectCRbzAA22QueryableAttributeTypeR_SLR_r0_lF"><(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1loiyAA5WhereVyxGs7KeyPathCyxq_SgG_AHtSo15NSManagedObjectCRbzAA22QueryableAttributeTypeR_SLR_r0_lF"><(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2leoiyAA5WhereVyxGAD10ExpressionVyx_q_q0_G_q0_tAA13DynamicObjectRzAA0dE5TraitR_AA22QueryableAttributeTypeR0_SLR0_r1_lF"><=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2leoiyAA5WhereVyxGAD10ExpressionVyx_q_q0_SgG_AHtAA13DynamicObjectRzAA0dE5TraitR_AA22QueryableAttributeTypeR0_SLR0_r1_lF"><=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2leoiyAA5WhereVyxGs7KeyPathCyxAA14FieldContainerO6StoredVyx_q_GG_q_tAA0aB6ObjectCRbzAA0G12OptionalTypeR_AA0g8StorableL0R_SL7WrappedAaPPRp_r0_lF"><=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2leoiyAA5WhereVyxGs7KeyPathCyxAA14FieldContainerO6StoredVyx_q_GG_q_tAA0aB6ObjectCRbzAA0G12StorableTypeR_SLR_r0_lF"><=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2leoiyAA5WhereVyxGs7KeyPathCyxAA14ValueContainerO8OptionalCyx_q_GG_q_SgtAA0aB6ObjectCRbzAA23ImportableAttributeTypeR_r0_lF"><=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2leoiyAA5WhereVyxGs7KeyPathCyxAA14ValueContainerO8RequiredCyx_q_GG_q_tAA0aB6ObjectCRbzAA23ImportableAttributeTypeR_SLR_r0_lF"><=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2leoiyAA5WhereVyxGs7KeyPathCyxq_G_q_tSo15NSManagedObjectCRbzAA22QueryableAttributeTypeR_SLR_r0_lF"><=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2leoiyAA5WhereVyxGs7KeyPathCyxq_SgG_AHtSo15NSManagedObjectCRbzAA22QueryableAttributeTypeR_SLR_r0_lF"><=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2eeoiyAA5WhereVyxGAD10ExpressionVyx_q_q0_G_q0_tAA13DynamicObjectRzAA0dE5TraitR_AA22QueryableAttributeTypeR0_r1_lF">==(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2eeoiyAA5WhereVyxGAD10ExpressionVyx_q_q0_SgG_AHtAA13DynamicObjectRzAA0dE5TraitR_AA22QueryableAttributeTypeR0_r1_lF">==(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2eeoiyAA5WhereVyxGAD10ExpressionVyx_q_q0_SgG_q0_tAA13DynamicObjectRzAA0dE5TraitR_AA22QueryableAttributeTypeR0_r1_lF">==(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2eeoiyAA5WhereVyxGs7KeyPathCyxAA14FieldContainerO12RelationshipVyx_q_GG_21DestinationObjectTypeQy_SgtAA0abK0CRbzAA0gi5ToOneL0R_r0_lF">==(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2eeoiyAA5WhereVyxGs7KeyPathCyxAA14FieldContainerO12RelationshipVyx_q_GG_q0_SgtAA0aB6ObjectCRbzAA0gI9ToOneTypeR_AA0J14RepresentationR0_0jM0Qy0_011DestinationjM0Rt_r1_lF">==(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2eeoiyAA5WhereVyxGs7KeyPathCyxAA14FieldContainerO6StoredVyx_q_GG_q_tAA0aB6ObjectCRbzAA0G12StorableTypeR_r0_lF">==(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2eeoiyAA5WhereVyxGs7KeyPathCyxAA14ValueContainerO8OptionalCyx_q_GG_q_SgtAA0aB6ObjectCRbzAA23ImportableAttributeTypeR_r0_lF">==(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2eeoiyAA5WhereVyxGs7KeyPathCyxAA14ValueContainerO8RequiredCyx_q_GG_q_tAA0aB6ObjectCRbzAA23ImportableAttributeTypeR_r0_lF">==(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2eeoiyAA5WhereVyxGs7KeyPathCyxAA21RelationshipContainerO5ToOneCyx_q_GG_q_SgtAA0aB6ObjectCRbzAPRb_r0_lF">==(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2eeoiyAA5WhereVyxGs7KeyPathCyxAA21RelationshipContainerO5ToOneCyx_q_GG_q_tAA0aB6ObjectCRbzAORb_r0_lF">==(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2eeoiyAA5WhereVyxGs7KeyPathCyxq_G_So17NSManagedObjectIDCtSo0gH0CRbzALRb_r0_lF">==(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2eeoiyAA5WhereVyxGs7KeyPathCyxq_G_q_tSo15NSManagedObjectCRbzAA22QueryableAttributeTypeR_r0_lF">==(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2eeoiyAA5WhereVyxGs7KeyPathCyxq_G_q_tSo15NSManagedObjectCRbzAJRb_r0_lF">==(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2eeoiyAA5WhereVyxGs7KeyPathCyxq_G_xtAA13DynamicObjectRzAA0H14RepresentationRzSo09NSManagedH0CRb_AL0H4TypeAaJPRczr0_lF">==(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2eeoiyAA5WhereVyxGs7KeyPathCyxq_SgG_AHtSo15NSManagedObjectCRbzAA22QueryableAttributeTypeR_r0_lF">==(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2eeoiyAA5WhereVyxGs7KeyPathCyxq_SgG_AHtSo15NSManagedObjectCRbzAKRb_r0_lF">==(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2eeoiyAA5WhereVyxGs7KeyPathCyxq_SgG_So17NSManagedObjectIDCtSo0gH0CRbzAMRb_r0_lF">==(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2eeoiyAA5WhereVyxGs7KeyPathCyxq_SgG_xSgtAA13DynamicObjectRzAA0H14RepresentationRzSo09NSManagedH0CRb_AN0H4TypeAaLPRczr0_lF">==(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1goiyAA5WhereVyxGAD10ExpressionVyx_q_q0_G_q0_tAA13DynamicObjectRzAA0cD5TraitR_AA22QueryableAttributeTypeR0_SLR0_r1_lF">>(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1goiyAA5WhereVyxGAD10ExpressionVyx_q_q0_SgG_q0_tAA13DynamicObjectRzAA0cD5TraitR_AA22QueryableAttributeTypeR0_SLR0_r1_lF">>(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1goiyAA5WhereVyxGs7KeyPathCyxAA14FieldContainerO6StoredVyx_q_GG_q_tAA0aB6ObjectCRbzAA0F12OptionalTypeR_AA0f8StorableK0R_SL7WrappedAaPPRp_r0_lF">>(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1goiyAA5WhereVyxGs7KeyPathCyxAA14FieldContainerO6StoredVyx_q_GG_q_tAA0aB6ObjectCRbzAA0F12StorableTypeR_SLR_r0_lF">>(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1goiyAA5WhereVyxGs7KeyPathCyxAA14ValueContainerO8OptionalCyx_q_GG_q_SgtAA0aB6ObjectCRbzAA23ImportableAttributeTypeR_r0_lF">>(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1goiyAA5WhereVyxGs7KeyPathCyxAA14ValueContainerO8RequiredCyx_q_GG_q_tAA0aB6ObjectCRbzAA23ImportableAttributeTypeR_SLR_r0_lF">>(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1goiyAA5WhereVyxGs7KeyPathCyxq_G_q_tSo15NSManagedObjectCRbzAA22QueryableAttributeTypeR_SLR_r0_lF">>(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1goiyAA5WhereVyxGs7KeyPathCyxq_SgG_AHtSo15NSManagedObjectCRbzAA22QueryableAttributeTypeR_SLR_r0_lF">>(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2geoiyAA5WhereVyxGAD10ExpressionVyx_q_q0_G_q0_tAA13DynamicObjectRzAA0dE5TraitR_AA22QueryableAttributeTypeR0_SLR0_r1_lF">>=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2geoiyAA5WhereVyxGAD10ExpressionVyx_q_q0_SgG_AHtAA13DynamicObjectRzAA0dE5TraitR_AA22QueryableAttributeTypeR0_SLR0_r1_lF">>=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2geoiyAA5WhereVyxGs7KeyPathCyxAA14FieldContainerO6StoredVyx_q_GG_q_tAA0aB6ObjectCRbzAA0G12OptionalTypeR_AA0g8StorableL0R_SL7WrappedAaPPRp_r0_lF">>=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2geoiyAA5WhereVyxGs7KeyPathCyxAA14FieldContainerO6StoredVyx_q_GG_q_tAA0aB6ObjectCRbzAA0G12StorableTypeR_SLR_r0_lF">>=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2geoiyAA5WhereVyxGs7KeyPathCyxAA14ValueContainerO8OptionalCyx_q_GG_q_SgtAA0aB6ObjectCRbzAA23ImportableAttributeTypeR_r0_lF">>=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2geoiyAA5WhereVyxGs7KeyPathCyxAA14ValueContainerO8RequiredCyx_q_GG_q_tAA0aB6ObjectCRbzAA23ImportableAttributeTypeR_SLR_r0_lF">>=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2geoiyAA5WhereVyxGs7KeyPathCyxq_G_q_tSo15NSManagedObjectCRbzAA22QueryableAttributeTypeR_SLR_r0_lF">>=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2geoiyAA5WhereVyxGs7KeyPathCyxq_SgG_AHtSo15NSManagedObjectCRbzAA22QueryableAttributeTypeR_SLR_r0_lF">>=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1toiyAA5WhereV10ExpressionVyx_AD12SingleTargetOyx_G20DestinationValueTypeQy0_Gs7KeyPathCyxAA14FieldContainerO12RelationshipVyx_q_GG_ANy0g6ObjectI0Qy_q0_GtAA0abO0CRbzAA0ln5ToOneI0R_AA0jK17StringConvertibleR0_0oI0Qy0_AVRSr1_lF">~(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1toiyAA5WhereV10ExpressionVyx_AD12SingleTargetOyx_G20DestinationValueTypeQy0_Gs7KeyPathCyxAA21RelationshipContainerO5ToOneCyx_q_GG_ANyq_q0_GtAA0aB6ObjectCRbzAWRb_0pI0Qy0_Rs_AA0jK17StringConvertibleR0_r1_lF">~(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1toiyAA5WhereV10ExpressionVyx_AD12SingleTargetOyx_Gq0_Gs7KeyPathCyxq_G_ALyq_q0_GtSo15NSManagedObjectCRbzAPRb_AA020AllowedObjectiveCKeyH5ValueR0_r1_lF">~(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1toiyAA5WhereV10ExpressionVyx_AD12SingleTargetOyx_Gq0_Gs7KeyPathCyxq_SgG_ALyq_q0_GtSo15NSManagedObjectCRbzAQRb_AA020AllowedObjectiveCKeyH5ValueR0_r1_lF">~(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1toiyAA5WhereV10ExpressionVyx_AD16CollectionTargetOyx_G20DestinationValueTypeQy0_Gs7KeyPathCyxAA14FieldContainerO12RelationshipVyx_q_GG_ANy0g6ObjectI0Qy_q0_GtAA0abO0CRbzAA0ln5ToOneI0R_AA0p4ManynjK17StringConvertibleR0_0oI0Qy0_AVRSr1_lF">~(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1toiyAA5WhereV10ExpressionVyx_AD16CollectionTargetOyx_G20DestinationValueTypeQy0_Gs7KeyPathCyxAA21RelationshipContainerO5ToOneCyx_q_GG_ANyq_q0_GtAA0aB6ObjectCRbzAWRb_0pI0Qy0_Rs_AA0n4ManyljK17StringConvertibleR0_r1_lF">~(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1toiyAA5WhereV10ExpressionVyx_AD16CollectionTargetOyx_G20DestinationValueTypeQy1_GAFyx_q0_q_G_s7KeyPathCyq_q1_GtAA0aB6ObjectCRbz0lI0Qy1_RszARRb_AA0cD5TraitR0_AA018ToManyRelationshipjK17StringConvertibleR1_r2_lF">~(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1toiyAA5WhereV10ExpressionVyx_AD16CollectionTargetOyx_G20DestinationValueTypeQy1_GAFyx_q0_q_SgG_s7KeyPathCyq_q1_GtAA0aB6ObjectCRbz0lI0Qy1_RszASRb_AA0cD5TraitR0_AA018ToManyRelationshipjK17StringConvertibleR1_r2_lF">~(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1toiyAA5WhereV10ExpressionVyx_AD16CollectionTargetOyx_G20DestinationValueTypeQy2_GAFyx_q0_q1_G_s7KeyPathCyq_q2_GtAA0aB6ObjectCRbz0lI0Qy1_RszARRb_ASQy2_Rs_AA0cD5TraitR0_AA018ToManyRelationshipjK17StringConvertibleR1_AaWR2_r3_lF">~(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1toiyAA5WhereV10ExpressionVyx_AD16CollectionTargetOyx_Gq0_Gs7KeyPathCyxq_G_ALyq_q0_GtSo15NSManagedObjectCRbzAPRb_AA035AllowedObjectiveCToManyRelationshipgH5ValueR0_r1_lF">~(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1toiyAA5WhereV10ExpressionVyx_AD16CollectionTargetOyx_Gq0_Gs7KeyPathCyxq_SgG_ALyq_q0_GtSo15NSManagedObjectCRbzAQRb_AA035AllowedObjectiveCToManyRelationshipgH5ValueR0_r1_lF">~(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1toiyAA5WhereV10ExpressionVyx_AD16CollectionTargetOyx_Gq1_GAFyx_q0_q_G_s7KeyPathCyq_q1_GtSo15NSManagedObjectCRbzAPRb_AA0cD5TraitR0_AA035AllowedObjectiveCToManyRelationshipgH5ValueR1_r2_lF">~(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1toiyAA5WhereV10ExpressionVyx_AD16CollectionTargetOyx_Gq1_GAFyx_q0_q_SgG_s7KeyPathCyq_q1_GtSo15NSManagedObjectCRbzAQRb_AA0cD5TraitR0_AA035AllowedObjectiveCToManyRelationshipgH5ValueR1_r2_lF">~(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1toiyAA5WhereV10ExpressionVyx_AD16CollectionTargetOyx_Gq2_GAFyx_q0_q1_G_s7KeyPathCyq_q2_GtSo15NSManagedObjectCRbzAPRb_AA0cD5TraitR0_AA035AllowedObjectiveCToManyRelationshipgH5ValueR1_AA0lm4CKeyhQ0R2_r3_lF">~(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1toiyAA5WhereV10ExpressionVyx_q0_20DestinationValueTypeQy1_GAFyx_q0_q_G_s7KeyPathCyq_q1_GtAA0aB6ObjectCRbzAORb_0jG0Qy1_Rs_AA0cD5TraitR0_AA0hI17StringConvertibleR1_r2_lF">~(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1toiyAA5WhereV10ExpressionVyx_q0_20DestinationValueTypeQy1_GAFyx_q0_q_SgG_s7KeyPathCyq_q1_GtAA0aB6ObjectCRbzAPRb_0jG0Qy1_Rs_AA0cD5TraitR0_AA0hI17StringConvertibleR1_r2_lF">~(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1toiyAA5WhereV10ExpressionVyx_q0_q1_GAFyx_q0_q_G_s7KeyPathCyq_q1_GtSo15NSManagedObjectCRbzAMRb_AA0cD5TraitR0_AA020AllowedObjectiveCKeyF5ValueR1_r2_lF">~(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore1toiyAA5WhereV10ExpressionVyx_q0_q1_GAFyx_q0_q_SgG_s7KeyPathCyq_q1_GtSo15NSManagedObjectCRbzANRb_AA0cD5TraitR0_AA020AllowedObjectiveCKeyF5ValueR1_r2_lF">~(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2teoiyAA5WhereVyxGq0__s7KeyPathCyxAA14FieldContainerO12RelationshipVyx_q_GGtAA0aB6ObjectCRbzAA0gI9ToOneTypeR_STR0_7ElementQy0_011DestinationjM0Rt_r1_lF">~=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2teoiyAA5WhereVyxGq0__s7KeyPathCyxAA14FieldContainerO6StoredVyx_q_GGtAA0aB6ObjectCRbzAA0G12StorableTypeR_7ElementQy0_Rs_STR0_r1_lF">~=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2teoiyAA5WhereVyxGq0__s7KeyPathCyxAA14ValueContainerO8OptionalCyx_q_GGtAA0aB6ObjectCRbzAA23ImportableAttributeTypeR_7ElementQy0_Rs_STR0_r1_lF">~=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2teoiyAA5WhereVyxGq0__s7KeyPathCyxAA14ValueContainerO8RequiredCyx_q_GGtAA0aB6ObjectCRbzAA23ImportableAttributeTypeR_7ElementQy0_Rs_STR0_r1_lF">~=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2teoiyAA5WhereVyxGq0__s7KeyPathCyxAA21RelationshipContainerO5ToOneCyx_q_GGtAA0aB6ObjectCRbzAORb_7ElementQy0_Rs_STR0_r1_lF">~=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2teoiyAA5WhereVyxGq0__s7KeyPathCyxq_GtSo15NSManagedObjectCRbzAA22QueryableAttributeTypeR_7ElementQy0_Rs_STR0_r1_lF">~=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2teoiyAA5WhereVyxGq0__s7KeyPathCyxq_GtSo15NSManagedObjectCRbzAJRb_7ElementQy0_Rs_STR0_r1_lF">~=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2teoiyAA5WhereVyxGq0__s7KeyPathCyxq_GtSo15NSManagedObjectCRbzAJRb_STR0_So0gH2IDC7ElementRt0_r1_lF">~=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2teoiyAA5WhereVyxGq0__s7KeyPathCyxq_SgGtSo15NSManagedObjectCRbzAA22QueryableAttributeTypeR_7ElementQy0_Rs_STR0_r1_lF">~=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2teoiyAA5WhereVyxGq0__s7KeyPathCyxq_SgGtSo15NSManagedObjectCRbzAKRb_7ElementQy0_Rs_STR0_r1_lF">~=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2teoiyAA5WhereVyxGq0__s7KeyPathCyxq_SgGtSo15NSManagedObjectCRbzAKRb_STR0_So0gH2IDC7ElementRt0_r1_lF">~=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2teoiyAA5WhereVyxGq1__AD10ExpressionVyx_q_q0_GtAA13DynamicObjectRzAA0dE5TraitR_AA22QueryableAttributeTypeR0_7ElementQy1_Rs0_STR1_r2_lF">~=(_:_:)</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Functions.html#/s:9CoreStore2teoiyAA5WhereVyxGq1__AD10ExpressionVyx_q_q0_SgGtAA13DynamicObjectRzAA0dE5TraitR_AA22QueryableAttributeTypeR0_7ElementQy1_Rs0_STR1_r2_lF">~=(_:_:)</a>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li class="nav-group-name">
|
||
<a class="nav-group-name-link" href="Protocols.html">Protocols</a>
|
||
<ul class="nav-group-tasks">
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/AllowedObjectiveCAttributeKeyPathValue.html">AllowedObjectiveCAttributeKeyPathValue</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/AllowedObjectiveCKeyPathValue.html">AllowedObjectiveCKeyPathValue</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols.html#/s:9CoreStore41AllowedObjectiveCRelationshipKeyPathValueP">AllowedObjectiveCRelationshipKeyPathValue</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols.html#/s:9CoreStore47AllowedObjectiveCToManyRelationshipKeyPathValueP">AllowedObjectiveCToManyRelationshipKeyPathValue</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols.html#/s:9CoreStore37AllowedOptionalObjectiveCKeyPathValueP">AllowedOptionalObjectiveCKeyPathValue</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/AnyObjectRepresentation.html">AnyObjectRepresentation</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/AnyWhereClause.html">AnyWhereClause</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/AttributeKeyPathStringConvertible.html">AttributeKeyPathStringConvertible</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols.html#/c:@M@CoreStore@objc(pl)CoreDataNativeType">CoreDataNativeType</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/CoreStoreLogger.html">CoreStoreLogger</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/CoreStoreObjectKeyValueObservation.html">CoreStoreObjectKeyValueObservation</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols.html#/s:9CoreStore22DefaultNSSecureCodableP">DefaultNSSecureCodable</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols.html#/s:9CoreStore12DeleteClauseP">DeleteClause</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/DiffableDataSourceTarget.html">DiffableDataSourceTarget</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/DynamicObject.html">DynamicObject</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/DynamicSchema.html">DynamicSchema</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/FetchChainableBuilderType.html">FetchChainableBuilderType</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols.html#/s:9CoreStore11FetchClauseP">FetchClause</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/FetchableSource.html">FetchableSource</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/FieldCoderType.html">FieldCoderType</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/FieldOptionalType.html">FieldOptionalType</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/FieldRelationshipType.html">FieldRelationshipType</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/FieldStorableType.html">FieldStorableType</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/GroupByClause.html">GroupByClause</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols.html#/s:9CoreStore23ImportableAttributeTypeP">ImportableAttributeType</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/ImportableObject.html">ImportableObject</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/ImportableUniqueObject.html">ImportableUniqueObject</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/KeyPathStringConvertible.html">KeyPathStringConvertible</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/ListObjectObserver.html">ListObjectObserver</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/ListObserver.html">ListObserver</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/ListSectionObserver.html">ListSectionObserver</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/LocalStorage.html">LocalStorage</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/ObjectObserver.html">ObjectObserver</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/ObjectRepresentation.html">ObjectRepresentation</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/OrderByClause.html">OrderByClause</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/QueryChainableBuilderType.html">QueryChainableBuilderType</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols.html#/s:9CoreStore11QueryClauseP">QueryClause</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/QueryableAttributeType.html">QueryableAttributeType</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/QueryableSource.html">QueryableSource</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/RelationshipKeyPathStringConvertible.html">RelationshipKeyPathStringConvertible</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/SchemaMappingProvider.html">SchemaMappingProvider</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/SectionMonitorBuilderType.html">SectionMonitorBuilderType</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols.html#/s:9CoreStore26SelectAttributesResultTypeP">SelectAttributesResultType</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/SelectClause.html">SelectClause</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols.html#/s:9CoreStore16SelectResultTypeP">SelectResultType</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/StorageInterface.html">StorageInterface</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols.html#/s:9CoreStore42ToManyRelationshipKeyPathStringConvertibleP">ToManyRelationshipKeyPathStringConvertible</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols/WhereClauseType.html">WhereClauseType</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Protocols.html#/s:9CoreStore20WhereExpressionTraitP">WhereExpressionTrait</a>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li class="nav-group-name">
|
||
<a class="nav-group-name-link" href="Structs.html">Structures</a>
|
||
<ul class="nav-group-tasks">
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/FetchChainBuilder.html">FetchChainBuilder</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/From.html">From</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/GroupBy.html">GroupBy</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/Into.html">Into</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/ListReader.html">ListReader</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/ListSnapshot.html">ListSnapshot</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/ListState.html">ListState</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/LocalStorageOptions.html">LocalStorageOptions</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/MigrationChain.html">MigrationChain</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/ObjectProxy.html">ObjectProxy</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/ObjectReader.html">ObjectReader</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/ObjectSnapshot.html">ObjectSnapshot</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/ObjectState.html">ObjectState</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/OrderBy.html">OrderBy</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/OrderBy/SortKey.html">– SortKey</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/PartialObject.html">PartialObject</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/QueryChainBuilder.html">QueryChainBuilder</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/SectionBy.html">SectionBy</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/SectionMonitorChainBuilder.html">SectionMonitorChainBuilder</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/Select.html">Select</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/Tweak.html">Tweak</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/VersionLock.html">VersionLock</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/Where.html">Where</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/Where/Expression.html">– Expression</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/Where.html#/s:9CoreStore5WhereV12SingleTargetO">– SingleTarget</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Structs/Where.html#/s:9CoreStore5WhereV16CollectionTargetO">– CollectionTarget</a>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li class="nav-group-name">
|
||
<a class="nav-group-name-link" href="Typealiases.html">Type Aliases</a>
|
||
<ul class="nav-group-tasks">
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Typealiases.html#/s:9CoreStore9ClassNamea">ClassName</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Typealiases.html#/s:9CoreStore10EntityNamea">EntityName</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Typealiases.html#/s:9CoreStore13KeyPathStringa">KeyPathString</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Typealiases.html#/s:9CoreStore15MigrationResulta">MigrationResult</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Typealiases.html#/s:9CoreStore18ModelConfigurationa">ModelConfiguration</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Typealiases.html#/s:9CoreStore12ModelVersiona">ModelVersion</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Typealiases.html#/s:9CoreStore11SetupResulta">SetupResult</a>
|
||
</li>
|
||
<li class="nav-group-task">
|
||
<a class="nav-group-task-link" href="Typealiases.html#/s:9CoreStore22XcodeDataModelFileNamea">XcodeDataModelFileName</a>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</nav>
|
||
<article class="main-content">
|
||
|
||
<section class="section">
|
||
<div class="section-content top-matter">
|
||
|
||
<p align="center">
|
||
<img alt="CoreStore" src="https://github.com/JohnEstropia/CoreStore/raw/develop/CoreStore.png" width=614 />
|
||
<br />
|
||
<br />
|
||
Unleashing the real power of Core Data with the elegance and safety of Swift
|
||
<br />
|
||
<br />
|
||
<a href="https://app.bitrise.io/app/e736852157296019#/builds"><img alt="Build Status" src="https://img.shields.io/bitrise/e736852157296019/master.svg?label=build&token=vhgAmaiF3tWZoQyFLkKM7g&logo=bitrise" /></a>
|
||
<a href="https://github.com/JohnEstropia/CoreStore/commits"><img alt="Last Commit" src="https://img.shields.io/github/last-commit/johnestropia/corestore.svg?style=flat" /></a>
|
||
<a href="http://cocoadocs.org/docsets/CoreStore"><img alt="Platform" src="https://img.shields.io/cocoapods/p/CoreStore.svg?style=flat" /></a>
|
||
<a href="https://raw.githubusercontent.com/JohnEstropia/CoreStore/master/LICENSE"><img alt="License" src="https://img.shields.io/cocoapods/l/CoreStore.svg?style=flat" /></a>
|
||
<br /><br />Dependency managers<br />
|
||
<a href="https://cocoapods.org/pods/CoreStore"><img alt="Cocoapods compatible" src="https://img.shields.io/cocoapods/v/CoreStore.svg?style=flat&label=Cocoapods" /></a>
|
||
<a href="https://github.com/Carthage/Carthage"><img alt="Carthage compatible" src="https://img.shields.io/badge/Carthage-compatible-16a085.svg?style=flat" /></a>
|
||
<a href="https://swift.org/source-compatibility/#current-list-of-projects"><img alt="Swift Package Manager compatible" src="https://img.shields.io/badge/Swift_Package_Manager-compatible-orange.svg?style=flat" /></a>
|
||
<br /><br />Contact<br />
|
||
<a href="http://swift-corestore-slack.herokuapp.com/"><img alt="Join us on Slack!" src="http://swift-corestore-slack.herokuapp.com/badge.svg?logo=slack" /></a>
|
||
<a href="https://twitter.com/JohnEstropia"><img alt="Reach me on Twitter!" src="https://img.shields.io/badge/twitter-%40JohnEstropia-3498db.svg?logo=twitter" /></a>
|
||
<a href="https://github.com/sponsors/JohnEstropia"><img alt="Sponsor" src="https://img.shields.io/badge/%E2%9D%A4-Sponsor-ff69bf"></a>
|
||
<br />
|
||
</p>
|
||
|
||
<ul>
|
||
<li><strong>Swift 5.7:</strong> iOS 13+ / macOS 10.15+ / watchOS 7.4+ / tvOS 13.0+</li>
|
||
<li>Previously supported Swift versions: <a href="https://github.com/JohnEstropia/CoreStore/tree/8.1.0">Swift 5.5</a>, <a href="https://github.com/JohnEstropia/CoreStore/tree/8.0.1">Swift 5.4</a>, <a href="https://github.com/JohnEstropia/CoreStore/tree/7.3.1">Swift 5.3</a></li>
|
||
</ul>
|
||
|
||
<p>Upgrading from previous CoreStore versions? Check out the <a href="#features">🆕 features</a> and make sure to read the <a href="https://github.com/JohnEstropia/CoreStore/releases">Change logs</a>.</p>
|
||
|
||
<p>CoreStore is part of the <a href="https://swift.org/source-compatibility/#current-list-of-projects">Swift Source Compatibility projects</a>.</p>
|
||
<h2 id='contents' class='heading'>Contents</h2>
|
||
|
||
<ul>
|
||
<li><a href="#tldr-aka-sample-codes">TL;DR (a.k.a. sample codes)</a></li>
|
||
<li><a href="#why-use-corestore">Why use CoreStore?</a></li>
|
||
<li><a href="#architecture">Architecture</a></li>
|
||
<li>CoreStore Tutorials (All of these have demos in the <strong>Demo</strong> app project!)
|
||
|
||
<ul>
|
||
<li><a href="#setting-up">Setting up</a>
|
||
|
||
<ul>
|
||
<li><a href="#in-memory-store">In-memory store</a></li>
|
||
<li><a href="#local-store">Local store</a></li>
|
||
</ul></li>
|
||
<li><a href="#migrations">Migrations</a>
|
||
|
||
<ul>
|
||
<li><a href="#declaring-model-versions">Declaring model versions</a></li>
|
||
<li><a href="#starting-migrations">Starting migrations</a></li>
|
||
<li><a href="#progressive-migrations">Progressive migrations</a></li>
|
||
<li><a href="#forecasting-migrations">Forecasting migrations</a></li>
|
||
<li><a href="#custom-migrations">Custom migrations</a></li>
|
||
</ul></li>
|
||
<li><a href="#saving-and-processing-transactions">Saving and processing transactions</a>
|
||
|
||
<ul>
|
||
<li><a href="#transaction-types">Transaction types</a>
|
||
|
||
<ul>
|
||
<li><a href="#asynchronous-transactions">Asynchronous transactions</a></li>
|
||
<li><a href="#synchronous-transactions">Synchronous transactions</a></li>
|
||
<li><a href="#unsafe-transactions">Unsafe transactions</a></li>
|
||
</ul></li>
|
||
<li><a href="#creating-objects">Creating objects</a></li>
|
||
<li><a href="#updating-objects">Updating objects</a></li>
|
||
<li><a href="#deleting-objects">Deleting objects</a></li>
|
||
<li><a href="#passing-objects-safely">Passing objects safely</a></li>
|
||
</ul></li>
|
||
<li><a href="#importing-data">Importing data</a></li>
|
||
<li><a href="#fetching-and-querying">Fetching and querying</a>
|
||
|
||
<ul>
|
||
<li><a href="#from-clause"><code><a href="Structs/From.html">From</a></code> clause</a></li>
|
||
<li><a href="#fetching">Fetching</a>
|
||
|
||
<ul>
|
||
<li><a href="#where-clause"><code><a href="Structs/Where.html">Where</a></code> clause</a></li>
|
||
<li><a href="#orderby-clause"><code><a href="Structs/OrderBy.html">OrderBy</a></code> clause</a></li>
|
||
<li><a href="#tweak-clause"><code><a href="Structs/Tweak.html">Tweak</a></code> clause</a></li>
|
||
</ul></li>
|
||
<li><a href="#querying">Querying</a>
|
||
|
||
<ul>
|
||
<li><a href="#selectt-clause"><code><a href="Structs/Select.html">Select<T></a></code> clause</a></li>
|
||
<li><a href="#groupby-clause"><code><a href="Structs/GroupBy.html">GroupBy</a></code> clause</a></li>
|
||
</ul></li>
|
||
</ul></li>
|
||
<li><a href="#logging-and-error-reporting">Logging and error reporting</a></li>
|
||
<li><a href="#observing-changes-and-notifications">Observing changes and notifications</a>
|
||
|
||
<ul>
|
||
<li><a href="#observe-a-single-property">Observe a single property</a></li>
|
||
<li><a href="#observe-a-single-objects-updates">Observe a single object’s updates</a></li>
|
||
<li><a href="#observe-a-single-objects-per-property-updates">Observe a single object’s per-property updates</a></li>
|
||
<li><a href="#observe-a-diffable-list">Observe a diffable list</a></li>
|
||
<li><a href="#observe-detailed-list-changes">Observe detailed list changes</a></li>
|
||
</ul></li>
|
||
<li><a href="#type-safe-corestoreobjects">Type-safe <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code>s</a>
|
||
|
||
<ul>
|
||
<li><a href="#new-field-property-wrapper-syntax">New <code>@Field</code> Property Wrapper syntax</a>
|
||
|
||
<ul>
|
||
<li><a href="#fieldstored"><code>@Field.Stored</code> </a></li>
|
||
<li><a href="#fieldvirtual"><code>@Field.Virtual</code> </a></li>
|
||
<li><a href="#fieldcoded"><code>@Field.Coded</code> </a></li>
|
||
<li><a href="#fieldrelationship"><code>@Field.Relationship</code> </a></li>
|
||
<li><a href="#field-usage-notes"><code>@Field</code> usage notes</a></li>
|
||
</ul></li>
|
||
<li><a href="#versionlocks"><code><a href="Structs/VersionLock.html">VersionLock</a></code>s</a></li>
|
||
</ul></li>
|
||
<li><a href="#reactive-programming">Reactive Programming</a>
|
||
|
||
<ul>
|
||
<li><a href="#rxswift">RxSwift</a></li>
|
||
<li>🆕<a href="#combine">Combine</a>
|
||
|
||
<ul>
|
||
<li>🆕<a href="#datastackreactive"><code><a href="Classes/DataStack.html#/s:9CoreStore9DataStackC8reactiveAC17ReactiveNamespaceVvp">DataStack.reactive</a></code></a></li>
|
||
<li>🆕<a href="#listpublisherreactive"><code><a href="Classes/ListPublisher.html#/s:9CoreStore13ListPublisherC8reactiveAC17ReactiveNamespaceVyx_Gvp">ListPublisher.reactive</a></code></a></li>
|
||
<li>🆕<a href="#objectpublisherreactive"><code><a href="Classes/ObjectPublisher.html#/s:9CoreStore15ObjectPublisherC8reactiveAC17ReactiveNamespaceVyx_Gvp">ObjectPublisher.reactive</a></code></a></li>
|
||
</ul></li>
|
||
</ul></li>
|
||
<li>🆕<a href="#swiftui-utilities">SwiftUI Utilities</a>
|
||
|
||
<ul>
|
||
<li>🆕<a href="#swiftui-views">SwiftUI Views`</a>
|
||
|
||
<ul>
|
||
<li>🆕<a href="#listreader"><code><a href="Structs/ListReader.html">ListReader</a></code></a></li>
|
||
<li>🆕<a href="#objectreader"><code><a href="Structs/ObjectReader.html">ObjectReader</a></code></a></li>
|
||
</ul></li>
|
||
<li>🆕<a href="#swiftui-property-wrappers">SwiftUI Property Wrappers</a>
|
||
|
||
<ul>
|
||
<li>🆕<a href="#liststate"><code><a href="Structs/ListState.html">ListState</a></code></a></li>
|
||
<li>🆕<a href="#objectstate"><code><a href="Structs/ObjectState.html">ObjectState</a></code></a></li>
|
||
</ul></li>
|
||
<li>🆕<a href="#swiftui-extensions">SwiftUI Extensions</a>
|
||
|
||
<ul>
|
||
<li>🆕<a href="#foreach"><code>ForEach</code></a></li>
|
||
</ul></li>
|
||
</ul></li>
|
||
</ul></li>
|
||
<li><a href="#roadmap">Roadmap</a></li>
|
||
<li><a href="#installation">Installation</a></li>
|
||
<li><a href="#changesets">Changesets</a></li>
|
||
<li><a href="#contact">Contact</a></li>
|
||
<li><a href="#who-uses-corestore">Who uses CoreStore?</a></li>
|
||
<li><a href="#license">License</a></li>
|
||
</ul>
|
||
<h2 id='tl-dr-a-k-a-sample-codes' class='heading'>TL;DR (a.k.a. sample codes)</h2>
|
||
|
||
<p>Pure-Swift models:</p>
|
||
<pre class="highlight swift"><code><span class="kd">class</span> <span class="kt">Person</span><span class="p">:</span> <span class="kt">CoreStoreObject</span> <span class="p">{</span>
|
||
<span class="kd">@Field</span><span class="o">.</span><span class="kt">Stored</span><span class="p">(</span><span class="s">"name"</span><span class="p">)</span>
|
||
<span class="k">var</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="s">""</span>
|
||
|
||
<span class="kd">@Field</span><span class="o">.</span><span class="kt">Relationship</span><span class="p">(</span><span class="s">"pets"</span><span class="p">,</span> <span class="nv">inverse</span><span class="p">:</span> <span class="err">\</span><span class="kt">Dog</span><span class="o">.</span><span class="err">$</span><span class="n">master</span><span class="p">)</span>
|
||
<span class="k">var</span> <span class="nv">pets</span><span class="p">:</span> <span class="kt">Set</span><span class="o"><</span><span class="kt">Dog</span><span class="o">></span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>(Classic <code>NSManagedObject</code>s also supported)</p>
|
||
|
||
<p>Setting-up with progressive migration support:</p>
|
||
<pre class="highlight swift"><code><span class="n">dataStack</span> <span class="o">=</span> <span class="kt">DataStack</span><span class="p">(</span>
|
||
<span class="nv">xcodeModelName</span><span class="p">:</span> <span class="s">"MyStore"</span><span class="p">,</span>
|
||
<span class="nv">migrationChain</span><span class="p">:</span> <span class="p">[</span><span class="s">"MyStore"</span><span class="p">,</span> <span class="s">"MyStoreV2"</span><span class="p">,</span> <span class="s">"MyStoreV3"</span><span class="p">]</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>Adding a store:</p>
|
||
<pre class="highlight swift"><code><span class="n">dataStack</span><span class="o">.</span><span class="nf">addStorage</span><span class="p">(</span>
|
||
<span class="kt">SQLiteStore</span><span class="p">(</span><span class="nv">fileName</span><span class="p">:</span> <span class="s">"MyStore.sqlite"</span><span class="p">),</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">result</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>Starting transactions:</p>
|
||
<pre class="highlight swift"><code><span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="k">let</span> <span class="nv">person</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">create</span><span class="p">(</span><span class="kt">Into</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span><span class="p">())</span>
|
||
<span class="n">person</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="s">"John Smith"</span>
|
||
<span class="n">person</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="mi">42</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">result</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="k">switch</span> <span class="n">result</span> <span class="p">{</span>
|
||
<span class="k">case</span> <span class="o">.</span><span class="nv">success</span><span class="p">:</span> <span class="nf">print</span><span class="p">(</span><span class="s">"success!"</span><span class="p">)</span>
|
||
<span class="k">case</span> <span class="o">.</span><span class="nf">failure</span><span class="p">(</span><span class="k">let</span> <span class="nv">error</span><span class="p">):</span> <span class="nf">print</span><span class="p">(</span><span class="n">error</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>Fetching objects (simple):</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">people</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">fetchAll</span><span class="p">(</span><span class="kt">From</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span><span class="p">())</span>
|
||
</code></pre>
|
||
|
||
<p>Fetching objects (complex):</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">people</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">fetchAll</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span><span class="p">()</span>
|
||
<span class="o">.</span><span class="nf">where</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">age</span> <span class="o">></span> <span class="mi">30</span><span class="p">),</span>
|
||
<span class="o">.</span><span class="nf">orderBy</span><span class="p">(</span><span class="o">.</span><span class="nf">ascending</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">name</span><span class="p">),</span> <span class="o">.</span><span class="nf">descending</span><span class="p">(</span><span class="o">.</span><span class="err">\</span><span class="n">age</span><span class="p">)),</span>
|
||
<span class="o">.</span><span class="nf">tweak</span><span class="p">({</span> <span class="nv">$0</span><span class="o">.</span><span class="n">includesPendingChanges</span> <span class="o">=</span> <span class="kc">false</span> <span class="p">})</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>Querying values:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">maxAge</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">queryValue</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span><span class="p">()</span>
|
||
<span class="o">.</span><span class="nf">select</span><span class="p">(</span><span class="kt">Int</span><span class="o">.</span><span class="k">self</span><span class="p">,</span> <span class="o">.</span><span class="nf">maximum</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">age</span><span class="p">))</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>But really, there’s a reason I wrote this huge <em>README</em>. Read up on the details!</p>
|
||
|
||
<p>Check out the <strong>Demo</strong> app project for sample codes as well!</p>
|
||
<h2 id='why-use-corestore' class='heading'>Why use CoreStore?</h2>
|
||
|
||
<p>CoreStore was (and is) heavily shaped by real-world needs of developing data-dependent apps. It enforces safe and convenient Core Data usage while letting you take advantage of the industry’s encouraged best practices.</p>
|
||
<h3 id='features' class='heading'>Features</h3>
|
||
|
||
<ul>
|
||
<li><strong>🆕<a href="#swiftui-utilities">SwiftUI</a> and <a href="#combine">Combine</a> API utilities.</strong> <code><a href="Classes/ListPublisher.html">ListPublisher</a></code>s and <code><a href="Classes/ObjectPublisher.html">ObjectPublisher</a></code>s now have their <code><a href="Structs/ListState.html">@ListState</a></code> and <code><a href="Structs/ObjectState.html">@ObjectState</a></code> SwiftUI property wrappers. Combine <code>Publisher</code> s are also available through the <code><a href="Classes/ListPublisher.html#/s:9CoreStore13ListPublisherC8reactiveAC17ReactiveNamespaceVyx_Gvp">ListPublisher.reactive</a></code>, <code><a href="Classes/ObjectPublisher.html#/s:9CoreStore15ObjectPublisherC8reactiveAC17ReactiveNamespaceVyx_Gvp">ObjectPublisher.reactive</a></code>, and <code><a href="Classes/DataStack.html#/s:9CoreStore9DataStackC8reactiveAC17ReactiveNamespaceVvp">DataStack.reactive</a></code> namespaces.</li>
|
||
<li><strong>Backwards-portable <a href="#observe-a-diffable-list">DiffableDataSources implementation</a>!</strong> <code>UITableViews</code> and <code>UICollectionViews</code> now have a new ally: <code><a href="Classes/ListPublisher.html">ListPublisher</a></code>s provide diffable snapshots that make reloading animations very easy and very safe. Say goodbye to <code>UITableViews</code> and <code>UICollectionViews</code> reload errors!</li>
|
||
<li><strong>💎Tight design around Swift’s code elegance and type safety.</strong> CoreStore fully utilizes Swift’s community-driven language features.</li>
|
||
<li><strong>🚦Safer concurrency architecture.</strong> CoreStore makes it hard to fall into common concurrency mistakes. The main <code>NSManagedObjectContext</code> is strictly read-only, while all updates are done through serial <em>transactions</em>. <em>(See <a href="#saving-and-processing-transactions">Saving and processing transactions</a>)</em></li>
|
||
<li><strong>🔍Clean fetching and querying API.</strong> Fetching objects is easy, but querying for raw aggregates (<code>min</code>, <code>max</code>, etc.) and raw property values is now just as convenient. <em>(See <a href="#fetching-and-querying">Fetching and querying</a>)</em></li>
|
||
<li><strong>🔭Type-safe, easy to configure observers.</strong> You don’t have to deal with the burden of setting up <code>NSFetchedResultsController</code>s and KVO. As an added bonus, list and object observable types all support multiple observers. This means you can have multiple view controllers efficiently share a single resource! <em>(See <a href="#observing-changes-and-notifications">Observing changes and notifications</a>)</em></li>
|
||
<li><strong>📥Efficient importing utilities.</strong> Map your entities once with their corresponding import source (JSON for example), and importing from <em>transactions</em> becomes elegant. Uniquing is also done with an efficient find-and-replace algorithm. <em>(See <a href="#importing-data">Importing data</a>)</em></li>
|
||
<li><strong>🗑Say goodbye to <em>.xcdatamodeld</em> files!</strong> While CoreStore supports <code>NSManagedObject</code>s, it offers <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code> whose subclasses can declare type-safe properties all in Swift code without the need to maintain separate resource files for the models. As bonus, these special properties support custom types, and can be used to create type-safe keypaths and queries. <em>(See <a href="#type-safe-corestoreobjects">Type-safe <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code>s</a>)</em></li>
|
||
<li><strong>🔗Progressive migrations.</strong> No need to think how to migrate from all previous model versions to your latest model. Just tell the <code><a href="Classes/DataStack.html">DataStack</a></code> the sequence of version strings (<code><a href="Structs/MigrationChain.html">MigrationChain</a></code>s) and CoreStore will automatically use progressive migrations when needed. <em>(See <a href="#migrations">Migrations</a>)</em></li>
|
||
<li><strong>Easier custom migrations.</strong> Say goodbye to <em>.xcmappingmodel</em> files; CoreStore can now infer entity mappings when possible, while still allowing an easy way to write custom mappings. <em>(See <a href="#migrations">Migrations</a>)</em></li>
|
||
<li><strong>📝Plug-in your own logging framework.</strong> Although a default logger is built-in, all logging, asserting, and error reporting can be funneled to <code><a href="Protocols/CoreStoreLogger.html">CoreStoreLogger</a></code> protocol implementations. <em>(See <a href="#logging-and-error-reporting">Logging and error reporting</a>)</em></li>
|
||
<li><strong>⛓Heavy support for multiple persistent stores per data stack.</strong> CoreStore lets you manage separate stores in a single <code><a href="Classes/DataStack.html">DataStack</a></code>, just the way <em>.xcdatamodeld</em> configurations are designed to. CoreStore will also manage one stack by default, but you can create and manage as many as you need. <em>(See <a href="#setting-up">Setting up</a>)</em></li>
|
||
<li><strong>🎯Free to name entities and their class names independently.</strong> CoreStore gets around a restriction with other Core Data wrappers where the entity name should be the same as the <code>NSManagedObject</code> subclass name. CoreStore loads entity-to-class mappings from the managed object model file, so you can assign independent names for the entities and their class names.</li>
|
||
<li><strong>📙Full Documentation.</strong> No magic here; all public classes, functions, properties, etc. have detailed <em>Apple Docs</em>. This <em>README</em> also introduces a lot of concepts and explains a lot of CoreStore’s behavior.</li>
|
||
<li><strong>ℹ️Informative (and pretty) logs.</strong> All CoreStore and Core Data-related types now have very informative and pretty print outputs! <em>(See <a href="#logging-and-error-reporting">Logging and error reporting</a>)</em></li>
|
||
<li><strong>🛡More extensive Unit Tests.</strong> Extending CoreStore is safe without having to worry about breaking old behavior.</li>
|
||
</ul>
|
||
|
||
<p><em>Have ideas that may benefit other Core Data users? <a href="https://github.com/JohnEstropia/CoreStore/issues">Feature Request</a>s are welcome!</em></p>
|
||
<h2 id='architecture' class='heading'>Architecture</h2>
|
||
|
||
<p>For maximum safety and performance, CoreStore will enforce coding patterns and practices it was designed for. (Don’t worry, it’s not as scary as it sounds.) But it is advisable to understand the “magic” of CoreStore before you use it in your apps.</p>
|
||
|
||
<p>If you are already familiar with the inner workings of CoreData, here is a mapping of <code>CoreStore</code> abstractions:</p>
|
||
|
||
<table><thead>
|
||
<tr>
|
||
<th><em>Core Data</em></th>
|
||
<th><em>CoreStore</em></th>
|
||
</tr>
|
||
</thead><tbody>
|
||
<tr>
|
||
<td><code>NSPersistentContainer</code><br />(.xcdatamodeld file)</td>
|
||
<td><code><a href="Classes/DataStack.html">DataStack</a></code></td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>NSPersistentStoreDescription</code><br />(“Configuration"s in the .xcdatamodeld file)</td>
|
||
<td><code><a href="Protocols/StorageInterface.html">StorageInterface</a></code> implementations<br />(<code><a href="Classes/InMemoryStore.html">InMemoryStore</a></code>, <code><a href="Classes/SQLiteStore.html">SQLiteStore</a></code>)</td>
|
||
</tr>
|
||
<tr>
|
||
<td><code>NSManagedObjectContext</code></td>
|
||
<td><code><a href="Classes/BaseDataTransaction.html">BaseDataTransaction</a></code> subclasses<br />(<code><a href="Classes/SynchronousDataTransaction.html">SynchronousDataTransaction</a></code>, <code><a href="Classes/AsynchronousDataTransaction.html">AsynchronousDataTransaction</a></code>, <code><a href="Classes/UnsafeDataTransaction.html">UnsafeDataTransaction</a></code>)</td>
|
||
</tr>
|
||
</tbody></table>
|
||
|
||
<p>A lot of Core Data wrapper libraries set up their <code>NSManagedObjectContext</code>s this way:</p>
|
||
|
||
<p><img src="https://cloud.githubusercontent.com/assets/3029684/16707160/984ef25c-4600-11e6-869f-8db7d2c63668.png" alt="nested contexts" height=380 /></p>
|
||
|
||
<p>Nesting saves from child context to the root context ensures maximum data integrity between contexts without blocking the main queue. But <a href="http://floriankugler.com/2013/04/29/concurrent-core-data-stack-performance-shootout/">in reality</a>, merging contexts is still by far faster than saving contexts. CoreStore’s <code><a href="Classes/DataStack.html">DataStack</a></code> takes the best of both worlds by treating the main <code>NSManagedObjectContext</code> as a read-only context (or "viewContext”), and only allows changes to be made within <em>transactions</em> on the child context:</p>
|
||
|
||
<p><img src="https://cloud.githubusercontent.com/assets/3029684/16707161/9adeb962-4600-11e6-8bc8-4ec85764dba4.png" alt="nested contexts and merge hybrid" height=292 /></p>
|
||
|
||
<p>This allows for a butter-smooth main thread, while still taking advantage of safe nested contexts.</p>
|
||
<h2 id='setting-up' class='heading'>Setting up</h2>
|
||
|
||
<p>The simplest way to initialize CoreStore is to add a default store to the default stack:</p>
|
||
<pre class="highlight swift"><code><span class="k">try</span> <span class="kt">CoreStoreDefaults</span><span class="o">.</span><span class="n">dataStack</span><span class="o">.</span><span class="nf">addStorageAndWait</span><span class="p">()</span>
|
||
</code></pre>
|
||
|
||
<p>This one-liner does the following:</p>
|
||
|
||
<ul>
|
||
<li>Triggers the lazy-initialization of <code><a href="Enums/CoreStoreDefaults.html#/s:9CoreStore0aB8DefaultsO9dataStackAA04DataE0CvpZ">CoreStoreDefaults.dataStack</a></code> with a default <code><a href="Classes/DataStack.html">DataStack</a></code></li>
|
||
<li>Sets up the stack’s <code>NSPersistentStoreCoordinator</code>, the root saving <code>NSManagedObjectContext</code>, and the read-only main <code>NSManagedObjectContext</code></li>
|
||
<li>Adds an <code><a href="Classes/SQLiteStore.html">SQLiteStore</a></code> in the <em>“Application Support/<bundle id>”</em> directory (or the <em>“Caches/<bundle id>”</em> directory on tvOS) with the file name <em>“[App bundle name].sqlite”</em></li>
|
||
<li>Creates and returns the <code>NSPersistentStore</code> instance on success, or an <code>NSError</code> on failure</li>
|
||
</ul>
|
||
|
||
<p>For most cases, this configuration is enough as it is. But for more hardcore settings, refer to this extensive example:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">dataStack</span> <span class="o">=</span> <span class="kt">DataStack</span><span class="p">(</span>
|
||
<span class="nv">xcodeModelName</span><span class="p">:</span> <span class="s">"MyModel"</span><span class="p">,</span> <span class="c1">// loads from the "MyModel.xcdatamodeld" file</span>
|
||
<span class="nv">migrationChain</span><span class="p">:</span> <span class="p">[</span><span class="s">"MyStore"</span><span class="p">,</span> <span class="s">"MyStoreV2"</span><span class="p">,</span> <span class="s">"MyStoreV3"</span><span class="p">]</span> <span class="c1">// model versions for progressive migrations</span>
|
||
<span class="p">)</span>
|
||
<span class="k">let</span> <span class="nv">migrationProgress</span> <span class="o">=</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">addStorage</span><span class="p">(</span>
|
||
<span class="kt">SQLiteStore</span><span class="p">(</span>
|
||
<span class="nv">fileURL</span><span class="p">:</span> <span class="n">sqliteFileURL</span><span class="p">,</span> <span class="c1">// set the target file URL for the sqlite file</span>
|
||
<span class="nv">configuration</span><span class="p">:</span> <span class="s">"Config2"</span><span class="p">,</span> <span class="c1">// use entities from the "Config2" configuration in the .xcdatamodeld file</span>
|
||
<span class="nv">localStorageOptions</span><span class="p">:</span> <span class="o">.</span><span class="n">recreateStoreOnModelMismatch</span> <span class="c1">// if migration paths cannot be resolved, recreate the sqlite file</span>
|
||
<span class="p">),</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">result</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="k">switch</span> <span class="n">result</span> <span class="p">{</span>
|
||
<span class="k">case</span> <span class="o">.</span><span class="nf">success</span><span class="p">(</span><span class="k">let</span> <span class="nv">storage</span><span class="p">):</span>
|
||
<span class="nf">print</span><span class="p">(</span><span class="s">"Successfully added sqlite store: </span><span class="se">\(</span><span class="n">storage</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
|
||
<span class="k">case</span> <span class="o">.</span><span class="nf">failure</span><span class="p">(</span><span class="k">let</span> <span class="nv">error</span><span class="p">):</span>
|
||
<span class="nf">print</span><span class="p">(</span><span class="s">"Failed adding sqlite store with error: </span><span class="se">\(</span><span class="n">error</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
|
||
<span class="kt">CoreStoreDefaults</span><span class="o">.</span><span class="n">dataStack</span> <span class="o">=</span> <span class="n">dataStack</span> <span class="c1">// pass the dataStack to CoreStore for easier access later on</span>
|
||
</code></pre>
|
||
|
||
<blockquote>
|
||
<p>💡If you have never heard of “Configurations”, you’ll find them in your <em>.xcdatamodeld</em> file
|
||
<img src="https://cloud.githubusercontent.com/assets/3029684/8333192/e52cfaac-1acc-11e5-9902-08724f9f1324.png" alt="xcode configurations screenshot" height=212 /></p>
|
||
</blockquote>
|
||
|
||
<p>In our sample code above, note that you don’t need to do the <code>CoreStoreDefaults.dataStack = dataStack</code> line. You can just as well hold a reference to the <code><a href="Classes/DataStack.html">DataStack</a></code> like below and call all its instance methods directly:</p>
|
||
<pre class="highlight swift"><code><span class="kd">class</span> <span class="kt">MyViewController</span><span class="p">:</span> <span class="kt">UIViewController</span> <span class="p">{</span>
|
||
<span class="k">let</span> <span class="nv">dataStack</span> <span class="o">=</span> <span class="kt">DataStack</span><span class="p">(</span><span class="nv">xcodeModelName</span><span class="p">:</span> <span class="s">"MyModel"</span><span class="p">)</span> <span class="c1">// keep reference to the stack</span>
|
||
<span class="k">override</span> <span class="kd">func</span> <span class="nf">viewDidLoad</span><span class="p">()</span> <span class="p">{</span>
|
||
<span class="k">super</span><span class="o">.</span><span class="nf">viewDidLoad</span><span class="p">()</span>
|
||
<span class="k">do</span> <span class="p">{</span>
|
||
<span class="k">try</span> <span class="k">self</span><span class="o">.</span><span class="n">dataStack</span><span class="o">.</span><span class="nf">addStorageAndWait</span><span class="p">(</span><span class="kt">SQLiteStore</span><span class="o">.</span><span class="k">self</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
<span class="k">catch</span> <span class="p">{</span> <span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="kd">func</span> <span class="nf">methodToBeCalledLaterOn</span><span class="p">()</span> <span class="p">{</span>
|
||
<span class="k">let</span> <span class="nv">objects</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">dataStack</span><span class="o">.</span><span class="nf">fetchAll</span><span class="p">(</span><span class="kt">From</span><span class="o"><</span><span class="kt">MyEntity</span><span class="o">></span><span class="p">())</span>
|
||
<span class="nf">print</span><span class="p">(</span><span class="n">objects</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<blockquote>
|
||
<p>💡By default, CoreStore will initialize <code>NSManagedObject</code>s from <em>.xcdatamodeld</em> files, but you can create models completely from source code using <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code>s and <code><a href="Classes/CoreStoreSchema.html">CoreStoreSchema</a></code>. To use this feature, refer to <a href="#type-safe-corestoreobjects">Type-safe <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code>s</a>.</p>
|
||
</blockquote>
|
||
|
||
<p>Notice that in our previous examples, <code>addStorageAndWait(_:)</code> and <code>addStorage(_:completion:)</code> both accept either <code><a href="Classes/InMemoryStore.html">InMemoryStore</a></code>, or <code><a href="Classes/SQLiteStore.html">SQLiteStore</a></code>. These implement the <code><a href="Protocols/StorageInterface.html">StorageInterface</a></code> protocol.</p>
|
||
<h3 id='in-memory-store' class='heading'>In-memory store</h3>
|
||
|
||
<p>The most basic <code><a href="Protocols/StorageInterface.html">StorageInterface</a></code> concrete type is the <code><a href="Classes/InMemoryStore.html">InMemoryStore</a></code>, which just stores objects in memory. Since <code><a href="Classes/InMemoryStore.html">InMemoryStore</a></code>s always start with a fresh empty data, they do not need any migration information.</p>
|
||
<pre class="highlight swift"><code><span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">addStorageAndWait</span><span class="p">(</span>
|
||
<span class="kt">InMemoryStore</span><span class="p">(</span>
|
||
<span class="nv">configuration</span><span class="p">:</span> <span class="s">"Config2"</span> <span class="c1">// optional. Use entities from the "Config2" configuration in the .xcdatamodeld file</span>
|
||
<span class="p">)</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>Asynchronous variant:</p>
|
||
<pre class="highlight swift"><code><span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">addStorage</span><span class="p">(</span>
|
||
<span class="kt">InMemoryStore</span><span class="p">(</span>
|
||
<span class="nv">configuration</span><span class="p">:</span> <span class="s">"Config2
|
||
),
|
||
completion: { storage in
|
||
// ...
|
||
}
|
||
)
|
||
</span></code></pre>
|
||
|
||
<p>(A reactive-programming variant of this method is explained in detail in the section on <a href="#datastackreactive"><code><a href="Classes/DataStack.html">DataStack</a></code> Combine publishers</a>)</p>
|
||
<h3 id='local-store' class='heading'>Local Store</h3>
|
||
|
||
<p>The most common <code><a href="Protocols/StorageInterface.html">StorageInterface</a></code> you will probably use is the <code><a href="Classes/SQLiteStore.html">SQLiteStore</a></code>, which saves data in a local SQLite file.</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">migrationProgress</span> <span class="o">=</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">addStorage</span><span class="p">(</span>
|
||
<span class="kt">SQLiteStore</span><span class="p">(</span>
|
||
<span class="nv">fileName</span><span class="p">:</span> <span class="s">"MyStore.sqlite"</span><span class="p">,</span>
|
||
<span class="nv">configuration</span><span class="p">:</span> <span class="s">"Config2"</span><span class="p">,</span> <span class="c1">// optional. Use entities from the "Config2" configuration in the .xcdatamodeld file</span>
|
||
<span class="nv">migrationMappingProviders</span><span class="p">:</span> <span class="p">[</span><span class="kt">Bundle</span><span class="o">.</span><span class="n">main</span><span class="p">],</span> <span class="c1">// optional. The bundles that contain required .xcmappingmodel files</span>
|
||
<span class="nv">localStorageOptions</span><span class="p">:</span> <span class="o">.</span><span class="n">recreateStoreOnModelMismatch</span> <span class="c1">// optional. Provides settings that tells the DataStack how to setup the persistent store</span>
|
||
<span class="p">),</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>Refer to the <em>SQLiteStore.swift</em> source documentation for detailed explanations for each of the default values.</p>
|
||
|
||
<p>CoreStore can decide the default values for these properties, so <code><a href="Classes/SQLiteStore.html">SQLiteStore</a></code>s can be initialized with no arguments:</p>
|
||
<pre class="highlight swift"><code><span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">addStorageAndWait</span><span class="p">(</span><span class="kt">SQLiteStore</span><span class="p">())</span>
|
||
</code></pre>
|
||
|
||
<p>(The asynchronous variant of this method is explained further in the next section on <a href="#starting-migrations">Migrations</a>, and a reactive-programming variant in the section on <a href="#datastackreactive"><code><a href="Classes/DataStack.html">DataStack</a></code> Combine publishers</a>)</p>
|
||
|
||
<p>The file-related properties of <code><a href="Classes/SQLiteStore.html">SQLiteStore</a></code> are actually requirements of another protocol that it implements, the <code><a href="Protocols/LocalStorage.html">LocalStorage</a></code> protocol:</p>
|
||
<pre class="highlight swift"><code><span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">LocalStorage</span><span class="p">:</span> <span class="kt">StorageInterface</span> <span class="p">{</span>
|
||
<span class="k">var</span> <span class="nv">fileURL</span><span class="p">:</span> <span class="kt">NSURL</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
|
||
<span class="k">var</span> <span class="nv">migrationMappingProviders</span><span class="p">:</span> <span class="p">[</span><span class="kt">SchemaMappingProvider</span><span class="p">]</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
|
||
<span class="k">var</span> <span class="nv">localStorageOptions</span><span class="p">:</span> <span class="kt">LocalStorageOptions</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
|
||
<span class="kd">func</span> <span class="nf">dictionary</span><span class="p">(</span><span class="nv">forOptions</span><span class="p">:</span> <span class="kt">LocalStorageOptions</span><span class="p">)</span> <span class="o">-></span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">]?</span>
|
||
<span class="kd">func</span> <span class="nf">cs_eraseStorageAndWait</span><span class="p">(</span><span class="nv">metadata</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">],</span> <span class="nv">soureModelHint</span><span class="p">:</span> <span class="kt">NSManagedObjectModel</span><span class="p">?)</span> <span class="k">throws</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>If you have custom <code>NSIncrementalStore</code> or <code>NSAtomicStore</code> subclasses, you can implement this protocol and use it similarly to <code><a href="Classes/SQLiteStore.html">SQLiteStore</a></code>.</p>
|
||
<h2 id='migrations' class='heading'>Migrations</h2>
|
||
<h3 id='declaring-model-versions' class='heading'>Declaring model versions</h3>
|
||
|
||
<p>Model versions are now expressed as a first-class protocol, <code><a href="Protocols/DynamicSchema.html">DynamicSchema</a></code>. CoreStore currently supports the following schema classes:</p>
|
||
|
||
<ul>
|
||
<li><strong><code><a href="Classes/XcodeDataModelSchema.html">XcodeDataModelSchema</a></code></strong>: a model version with entities loaded from a <em>.xcdatamodeld</em> file.</li>
|
||
<li><strong><code><a href="Classes/CoreStoreSchema.html">CoreStoreSchema</a></code></strong>: a model version created with <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code> entities. <em>(See <a href="#type-safe-corestore-objects">Type-safe <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code>s</a>)</em></li>
|
||
<li><strong><code><a href="Classes/UnsafeDataModelSchema.html">UnsafeDataModelSchema</a></code></strong>: a model version created with an existing <code>NSManagedObjectModel</code> instance.</li>
|
||
</ul>
|
||
|
||
<p>All the <code><a href="Protocols/DynamicSchema.html">DynamicSchema</a></code> for all model versions are then collected within a single <code><a href="Classes/SchemaHistory.html">SchemaHistory</a></code> instance, which is then handed to the <code><a href="Classes/DataStack.html">DataStack</a></code>. Here are some common use cases:</p>
|
||
|
||
<p><strong>Multiple model versions grouped in a <em>.xcdatamodeld</em> file (Core Data standard method)</strong></p>
|
||
<pre class="highlight swift"><code><span class="kt">CoreStoreDefaults</span><span class="o">.</span><span class="n">dataStack</span> <span class="o">=</span> <span class="kt">DataStack</span><span class="p">(</span>
|
||
<span class="nv">xcodeModelName</span><span class="p">:</span> <span class="s">"MyModel"</span><span class="p">,</span>
|
||
<span class="nv">bundle</span><span class="p">:</span> <span class="kt">Bundle</span><span class="o">.</span><span class="n">main</span><span class="p">,</span>
|
||
<span class="nv">migrationChain</span><span class="p">:</span> <span class="p">[</span><span class="s">"MyAppModel"</span><span class="p">,</span> <span class="s">"MyAppModelV2"</span><span class="p">,</span> <span class="s">"MyAppModelV3"</span><span class="p">,</span> <span class="s">"MyAppModelV4"</span><span class="p">]</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p><strong><code><a href="Classes/CoreStoreSchema.html">CoreStoreSchema</a></code>-based model version (No <em>.xcdatamodeld</em> file needed)</strong>
|
||
<em>(For more details, see also <a href="#type-safe-corestore-objects">Type-safe <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code>s</a>)</em></p>
|
||
<pre class="highlight swift"><code><span class="kd">class</span> <span class="kt">Animal</span><span class="p">:</span> <span class="kt">CoreStoreObject</span> <span class="p">{</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="kd">class</span> <span class="kt">Dog</span><span class="p">:</span> <span class="kt">Animal</span> <span class="p">{</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="kd">class</span> <span class="kt">Person</span><span class="p">:</span> <span class="kt">CoreStoreObject</span> <span class="p">{</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kt">CoreStoreDefaults</span><span class="o">.</span><span class="n">dataStack</span> <span class="o">=</span> <span class="kt">DataStack</span><span class="p">(</span>
|
||
<span class="kt">CoreStoreSchema</span><span class="p">(</span>
|
||
<span class="nv">modelVersion</span><span class="p">:</span> <span class="s">"V1"</span><span class="p">,</span>
|
||
<span class="nv">entities</span><span class="p">:</span> <span class="p">[</span>
|
||
<span class="kt">Entity</span><span class="o"><</span><span class="kt">Animal</span><span class="o">></span><span class="p">(</span><span class="s">"Animal"</span><span class="p">,</span> <span class="nv">isAbstract</span><span class="p">:</span> <span class="kc">true</span><span class="p">),</span>
|
||
<span class="kt">Entity</span><span class="o"><</span><span class="kt">Dog</span><span class="o">></span><span class="p">(</span><span class="s">"Dog"</span><span class="p">),</span>
|
||
<span class="kt">Entity</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span><span class="p">(</span><span class="s">"Person"</span><span class="p">)</span>
|
||
<span class="p">]</span>
|
||
<span class="p">)</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p><strong>Models in a <em>.xcdatamodeld</em> file during past app versions, but migrated to the new <code><a href="Classes/CoreStoreSchema.html">CoreStoreSchema</a></code> method</strong></p>
|
||
<pre class="highlight swift"><code><span class="kd">class</span> <span class="kt">Animal</span><span class="p">:</span> <span class="kt">CoreStoreObject</span> <span class="p">{</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="kd">class</span> <span class="kt">Dog</span><span class="p">:</span> <span class="kt">Animal</span> <span class="p">{</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="kd">class</span> <span class="kt">Person</span><span class="p">:</span> <span class="kt">CoreStoreObject</span> <span class="p">{</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="k">let</span> <span class="nv">legacySchema</span> <span class="o">=</span> <span class="kt">XcodeDataModelSchema</span><span class="o">.</span><span class="nf">from</span><span class="p">(</span>
|
||
<span class="nv">modelName</span><span class="p">:</span> <span class="s">"MyModel"</span><span class="p">,</span> <span class="c1">// .xcdatamodeld name</span>
|
||
<span class="nv">bundle</span><span class="p">:</span> <span class="n">bundle</span><span class="p">,</span>
|
||
<span class="nv">migrationChain</span><span class="p">:</span> <span class="p">[</span><span class="s">"MyAppModel"</span><span class="p">,</span> <span class="s">"MyAppModelV2"</span><span class="p">,</span> <span class="s">"MyAppModelV3"</span><span class="p">,</span> <span class="s">"MyAppModelV4"</span><span class="p">]</span>
|
||
<span class="p">)</span>
|
||
<span class="k">let</span> <span class="nv">newSchema</span> <span class="o">=</span> <span class="kt">CoreStoreSchema</span><span class="p">(</span>
|
||
<span class="nv">modelVersion</span><span class="p">:</span> <span class="s">"V1"</span><span class="p">,</span>
|
||
<span class="nv">entities</span><span class="p">:</span> <span class="p">[</span>
|
||
<span class="kt">Entity</span><span class="o"><</span><span class="kt">Animal</span><span class="o">></span><span class="p">(</span><span class="s">"Animal"</span><span class="p">,</span> <span class="nv">isAbstract</span><span class="p">:</span> <span class="kc">true</span><span class="p">),</span>
|
||
<span class="kt">Entity</span><span class="o"><</span><span class="kt">Dog</span><span class="o">></span><span class="p">(</span><span class="s">"Dog"</span><span class="p">),</span>
|
||
<span class="kt">Entity</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span><span class="p">(</span><span class="s">"Person"</span><span class="p">)</span>
|
||
<span class="p">]</span>
|
||
<span class="p">)</span>
|
||
<span class="kt">CoreStoreDefaults</span><span class="o">.</span><span class="n">dataStack</span> <span class="o">=</span> <span class="kt">DataStack</span><span class="p">(</span>
|
||
<span class="nv">schemaHistory</span><span class="p">:</span> <span class="kt">SchemaHistory</span><span class="p">(</span>
|
||
<span class="n">legacySchema</span> <span class="o">+</span> <span class="p">[</span><span class="n">newSchema</span><span class="p">],</span>
|
||
<span class="nv">migrationChain</span><span class="p">:</span> <span class="p">[</span><span class="s">"MyAppModel"</span><span class="p">,</span> <span class="s">"MyAppModelV2"</span><span class="p">,</span> <span class="s">"MyAppModelV3"</span><span class="p">,</span> <span class="s">"MyAppModelV4"</span><span class="p">,</span> <span class="s">"V1"</span><span class="p">]</span>
|
||
<span class="p">)</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p><strong><code><a href="Classes/CoreStoreSchema.html">CoreStoreSchema</a></code>-based model versions with progressive migration</strong></p>
|
||
<pre class="highlight swift"><code><span class="kd">typealias</span> <span class="kt">Animal</span> <span class="o">=</span> <span class="kt">V2</span><span class="o">.</span><span class="kt">Animal</span>
|
||
<span class="kd">typealias</span> <span class="kt">Dog</span> <span class="o">=</span> <span class="kt">V2</span><span class="o">.</span><span class="kt">Dog</span>
|
||
<span class="kd">typealias</span> <span class="kt">Person</span> <span class="o">=</span> <span class="kt">V2</span><span class="o">.</span><span class="kt">Person</span>
|
||
<span class="kd">enum</span> <span class="kt">V2</span> <span class="p">{</span>
|
||
<span class="kd">class</span> <span class="kt">Animal</span><span class="p">:</span> <span class="kt">CoreStoreObject</span> <span class="p">{</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="kd">class</span> <span class="kt">Dog</span><span class="p">:</span> <span class="kt">Animal</span> <span class="p">{</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="kd">class</span> <span class="kt">Person</span><span class="p">:</span> <span class="kt">CoreStoreObject</span> <span class="p">{</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="kd">enum</span> <span class="kt">V1</span> <span class="p">{</span>
|
||
<span class="kd">class</span> <span class="kt">Animal</span><span class="p">:</span> <span class="kt">CoreStoreObject</span> <span class="p">{</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="kd">class</span> <span class="kt">Dog</span><span class="p">:</span> <span class="kt">Animal</span> <span class="p">{</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="kd">class</span> <span class="kt">Person</span><span class="p">:</span> <span class="kt">CoreStoreObject</span> <span class="p">{</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kt">CoreStoreDefaults</span><span class="o">.</span><span class="n">dataStack</span> <span class="o">=</span> <span class="kt">DataStack</span><span class="p">(</span>
|
||
<span class="kt">CoreStoreSchema</span><span class="p">(</span>
|
||
<span class="nv">modelVersion</span><span class="p">:</span> <span class="s">"V1"</span><span class="p">,</span>
|
||
<span class="nv">entities</span><span class="p">:</span> <span class="p">[</span>
|
||
<span class="kt">Entity</span><span class="o"><</span><span class="kt">V1</span><span class="o">.</span><span class="kt">Animal</span><span class="o">></span><span class="p">(</span><span class="s">"Animal"</span><span class="p">,</span> <span class="nv">isAbstract</span><span class="p">:</span> <span class="kc">true</span><span class="p">),</span>
|
||
<span class="kt">Entity</span><span class="o"><</span><span class="kt">V1</span><span class="o">.</span><span class="kt">Dog</span><span class="o">></span><span class="p">(</span><span class="s">"Dog"</span><span class="p">),</span>
|
||
<span class="kt">Entity</span><span class="o"><</span><span class="kt">V1</span><span class="o">.</span><span class="kt">Person</span><span class="o">></span><span class="p">(</span><span class="s">"Person"</span><span class="p">)</span>
|
||
<span class="p">]</span>
|
||
<span class="p">),</span>
|
||
<span class="kt">CoreStoreSchema</span><span class="p">(</span>
|
||
<span class="nv">modelVersion</span><span class="p">:</span> <span class="s">"V2"</span><span class="p">,</span>
|
||
<span class="nv">entities</span><span class="p">:</span> <span class="p">[</span>
|
||
<span class="kt">Entity</span><span class="o"><</span><span class="kt">V2</span><span class="o">.</span><span class="kt">Animal</span><span class="o">></span><span class="p">(</span><span class="s">"Animal"</span><span class="p">,</span> <span class="nv">isAbstract</span><span class="p">:</span> <span class="kc">true</span><span class="p">),</span>
|
||
<span class="kt">Entity</span><span class="o"><</span><span class="kt">V2</span><span class="o">.</span><span class="kt">Dog</span><span class="o">></span><span class="p">(</span><span class="s">"Dog"</span><span class="p">),</span>
|
||
<span class="kt">Entity</span><span class="o"><</span><span class="kt">V2</span><span class="o">.</span><span class="kt">Person</span><span class="o">></span><span class="p">(</span><span class="s">"Person"</span><span class="p">)</span>
|
||
<span class="p">]</span>
|
||
<span class="p">),</span>
|
||
<span class="nv">migrationChain</span><span class="p">:</span> <span class="p">[</span><span class="s">"V1"</span><span class="p">,</span> <span class="s">"V2"</span><span class="p">]</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
<h3 id='starting-migrations' class='heading'>Starting migrations</h3>
|
||
|
||
<p>We have seen <code>addStorageAndWait(...)</code> used to initialize our persistent store. As the method name’s <em>~AndWait</em> suffix suggests though, this method blocks so it should not do long tasks such as data migrations. In fact CoreStore will only attempt a synchronous <strong>lightweight</strong> migration if you explicitly provide the <code>.allowSynchronousLightweightMigration</code> option:</p>
|
||
<pre class="highlight swift"><code><span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">addStorageAndWait</span><span class="p">(</span>
|
||
<span class="kt">SQLiteStore</span><span class="p">(</span>
|
||
<span class="nv">fileURL</span><span class="p">:</span> <span class="n">sqliteFileURL</span><span class="p">,</span>
|
||
<span class="nv">localStorageOptions</span><span class="p">:</span> <span class="o">.</span><span class="n">allowSynchronousLightweightMigration</span>
|
||
<span class="p">)</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>if you do so, any model mismatch will be thrown as an error. </p>
|
||
|
||
<p>In general though, if migrations are expected the asynchronous variant <code>addStorage(_:completion:)</code> method is recommended instead:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">migrationProgress</span><span class="p">:</span> <span class="kt">Progress</span><span class="p">?</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">addStorage</span><span class="p">(</span>
|
||
<span class="kt">SQLiteStore</span><span class="p">(</span>
|
||
<span class="nv">fileName</span><span class="p">:</span> <span class="s">"MyStore.sqlite"</span><span class="p">,</span>
|
||
<span class="nv">configuration</span><span class="p">:</span> <span class="s">"Config2"</span>
|
||
<span class="p">),</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">result</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="k">switch</span> <span class="n">result</span> <span class="p">{</span>
|
||
<span class="k">case</span> <span class="o">.</span><span class="nf">success</span><span class="p">(</span><span class="k">let</span> <span class="nv">storage</span><span class="p">):</span>
|
||
<span class="nf">print</span><span class="p">(</span><span class="s">"Successfully added sqlite store: </span><span class="se">\(</span><span class="n">storage</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
|
||
<span class="k">case</span> <span class="o">.</span><span class="nf">failure</span><span class="p">(</span><span class="k">let</span> <span class="nv">error</span><span class="p">):</span>
|
||
<span class="nf">print</span><span class="p">(</span><span class="s">"Failed adding sqlite store with error: </span><span class="se">\(</span><span class="n">error</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>The <code>completion</code> block reports a <code><a href="Typealiases.html#/s:9CoreStore11SetupResulta">SetupResult</a></code> that indicates success or failure.</p>
|
||
|
||
<p>(A reactive-programming variant of this method is explained further in the section on <a href="#datastackreactive"><code><a href="Classes/DataStack.html">DataStack</a></code> Combine publishers</a>)</p>
|
||
|
||
<p>Notice that this method also returns an optional <code>Progress</code>. If <code>nil</code>, no migrations are needed, thus progress reporting is unnecessary as well. If not <code>nil</code>, you can use this to track migration progress by using standard KVO on the <code>"fractionCompleted"</code> key, or by using a closure-based utility exposed in <em>Progress+Convenience.swift</em>:</p>
|
||
<pre class="highlight swift"><code><span class="n">migrationProgress</span><span class="p">?</span><span class="o">.</span><span class="n">setProgressHandler</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="p">(</span><span class="n">progress</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="k">self</span><span class="p">?</span><span class="o">.</span><span class="n">progressView</span><span class="p">?</span><span class="o">.</span><span class="nf">setProgress</span><span class="p">(</span><span class="kt">Float</span><span class="p">(</span><span class="n">progress</span><span class="o">.</span><span class="n">fractionCompleted</span><span class="p">),</span> <span class="nv">animated</span><span class="p">:</span> <span class="kc">true</span><span class="p">)</span>
|
||
<span class="k">self</span><span class="p">?</span><span class="o">.</span><span class="n">percentLabel</span><span class="p">?</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="n">progress</span><span class="o">.</span><span class="n">localizedDescription</span> <span class="c1">// "50% completed"</span>
|
||
<span class="k">self</span><span class="p">?</span><span class="o">.</span><span class="n">stepLabel</span><span class="p">?</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="n">progress</span><span class="o">.</span><span class="n">localizedAdditionalDescription</span> <span class="c1">// "0 of 2"</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>This closure is executed on the main thread so UIKit and AppKit calls can be done safely.</p>
|
||
<h3 id='progressive-migrations' class='heading'>Progressive migrations</h3>
|
||
|
||
<p>By default, CoreStore uses Core Data’s default automatic migration mechanism. In other words, CoreStore will try to migrate the existing persistent store until it matches the <code><a href="Classes/SchemaHistory.html">SchemaHistory</a></code>‘s <code>currentModelVersion</code>. If no mapping model path is found from the store’s version to the data model’s version, CoreStore gives up and reports an error.</p>
|
||
|
||
<p>The <code><a href="Classes/DataStack.html">DataStack</a></code> lets you specify hints on how to break a migration into several sub-migrations using a <code><a href="Structs/MigrationChain.html">MigrationChain</a></code>. This is typically passed to the <code><a href="Classes/DataStack.html">DataStack</a></code> initializer and will be applied to all stores added to the <code><a href="Classes/DataStack.html">DataStack</a></code> with <code>addSQLiteStore(...)</code> and its variants:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">dataStack</span> <span class="o">=</span> <span class="kt">DataStack</span><span class="p">(</span><span class="nv">migrationChain</span><span class="p">:</span>
|
||
<span class="p">[</span><span class="s">"MyAppModel"</span><span class="p">,</span> <span class="s">"MyAppModelV2"</span><span class="p">,</span> <span class="s">"MyAppModelV3"</span><span class="p">,</span> <span class="s">"MyAppModelV4"</span><span class="p">])</span>
|
||
</code></pre>
|
||
|
||
<p>The most common usage is to pass in the model version (<em>.xcdatamodeld</em> version names for <code>NSManagedObject</code>s, or the <code>modelName</code> for <code><a href="Classes/CoreStoreSchema.html">CoreStoreSchema</a></code>s) in increasing order as above.</p>
|
||
|
||
<p>For more complex, non-linear migration paths, you can also pass in a version tree that maps the key-values to the source-destination versions:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">dataStack</span> <span class="o">=</span> <span class="kt">DataStack</span><span class="p">(</span><span class="nv">migrationChain</span><span class="p">:</span> <span class="p">[</span>
|
||
<span class="s">"MyAppModel"</span><span class="p">:</span> <span class="s">"MyAppModelV3"</span><span class="p">,</span>
|
||
<span class="s">"MyAppModelV2"</span><span class="p">:</span> <span class="s">"MyAppModelV4"</span><span class="p">,</span>
|
||
<span class="s">"MyAppModelV3"</span><span class="p">:</span> <span class="s">"MyAppModelV4"</span>
|
||
<span class="p">])</span>
|
||
</code></pre>
|
||
|
||
<p>This allows for different migration paths depending on the starting version. The example above resolves to the following paths:</p>
|
||
|
||
<ul>
|
||
<li>MyAppModel-MyAppModelV3-MyAppModelV4</li>
|
||
<li>MyAppModelV2-MyAppModelV4</li>
|
||
<li>MyAppModelV3-MyAppModelV4</li>
|
||
</ul>
|
||
|
||
<p>Initializing with empty values (either <code>nil</code>, <code>[]</code>, or <code>[:]</code>) instructs the <code><a href="Classes/DataStack.html">DataStack</a></code> to disable progressive migrations and revert to the default migration behavior (i.e. use the <em>.xcdatamodeld</em>’s current version as the final version):</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">dataStack</span> <span class="o">=</span> <span class="kt">DataStack</span><span class="p">(</span><span class="nv">migrationChain</span><span class="p">:</span> <span class="kc">nil</span><span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>The <code><a href="Structs/MigrationChain.html">MigrationChain</a></code> is validated when passed to the <code><a href="Classes/DataStack.html">DataStack</a></code> and unless it is empty, will raise an assertion if any of the following conditions are met:</p>
|
||
|
||
<ul>
|
||
<li>a version appears twice in an array</li>
|
||
<li>a version appears twice as a key in a dictionary literal</li>
|
||
<li>a loop is found in any of the paths</li>
|
||
</ul>
|
||
|
||
<blockquote>
|
||
<p>⚠️<strong>Important: If a <code><a href="Structs/MigrationChain.html">MigrationChain</a></code> is specified, the <em>.xcdatamodeld</em>’s “Current Version” will be bypassed</strong> and the <code><a href="Structs/MigrationChain.html">MigrationChain</a></code>’s leafmost version will be the <code><a href="Classes/DataStack.html">DataStack</a></code>’s base model version.</p>
|
||
</blockquote>
|
||
<h3 id='forecasting-migrations' class='heading'>Forecasting migrations</h3>
|
||
|
||
<p>Sometimes migrations are huge and you may want prior information so your app could display a loading screen, or to display a confirmation dialog to the user. For this, CoreStore provides a <code>requiredMigrationsForStorage(_:)</code> method you can use to inspect a persistent store before you actually call <code>addStorageAndWait(_:)</code> or <code>addStorage(_:completion:)</code>:</p>
|
||
<pre class="highlight swift"><code><span class="k">do</span> <span class="p">{</span>
|
||
<span class="k">let</span> <span class="nv">storage</span> <span class="o">=</span> <span class="kt">SQLiteStorage</span><span class="p">(</span><span class="nv">fileName</span><span class="p">:</span> <span class="s">"MyStore.sqlite"</span><span class="p">)</span>
|
||
<span class="k">let</span> <span class="nv">migrationTypes</span><span class="p">:</span> <span class="p">[</span><span class="kt">MigrationType</span><span class="p">]</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">requiredMigrationsForStorage</span><span class="p">(</span><span class="n">storage</span><span class="p">)</span>
|
||
<span class="k">if</span> <span class="n">migrationTypes</span><span class="o">.</span><span class="n">count</span> <span class="o">></span> <span class="mi">1</span>
|
||
<span class="o">||</span> <span class="p">(</span><span class="n">migrationTypes</span><span class="o">.</span><span class="n">filter</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="n">isHeavyweightMigration</span> <span class="p">}</span><span class="o">.</span><span class="n">count</span><span class="p">)</span> <span class="o">></span> <span class="mi">0</span> <span class="p">{</span>
|
||
<span class="c1">// ... will migrate more than once. Show special waiting screen</span>
|
||
<span class="p">}</span>
|
||
<span class="k">else</span> <span class="k">if</span> <span class="n">migrationTypes</span><span class="o">.</span><span class="n">count</span> <span class="o">></span> <span class="mi">0</span> <span class="p">{</span>
|
||
<span class="c1">// ... will migrate just once. Show simple activity indicator</span>
|
||
<span class="p">}</span>
|
||
<span class="k">else</span> <span class="p">{</span>
|
||
<span class="c1">// ... Do nothing</span>
|
||
<span class="p">}</span>
|
||
<span class="n">dataStack</span><span class="o">.</span><span class="nf">addStorage</span><span class="p">(</span><span class="n">storage</span><span class="p">,</span> <span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="cm">/* ... */</span> <span class="p">})</span>
|
||
<span class="p">}</span>
|
||
<span class="k">catch</span> <span class="p">{</span>
|
||
<span class="c1">// ... either inspection of the store failed, or if no mapping model was found/inferred</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p><code>requiredMigrationsForStorage(_:)</code> returns an array of <code><a href="Enums/MigrationType.html">MigrationType</a></code>s, where each item in the array may be either of the following values:</p>
|
||
<pre class="highlight swift"><code><span class="k">case</span> <span class="nf">lightweight</span><span class="p">(</span><span class="nv">sourceVersion</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">destinationVersion</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span>
|
||
<span class="k">case</span> <span class="nf">heavyweight</span><span class="p">(</span><span class="nv">sourceVersion</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">destinationVersion</span><span class="p">:</span> <span class="kt">String</span><span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>Each <code><a href="Enums/MigrationType.html">MigrationType</a></code> indicates the migration type for each step in the <code><a href="Structs/MigrationChain.html">MigrationChain</a></code>. Use these information as fit for your app.</p>
|
||
<h3 id='custom-migrations' class='heading'>Custom migrations</h3>
|
||
|
||
<p>CoreStore offers several ways to declare migration mappings:</p>
|
||
|
||
<ul>
|
||
<li><code><a href="Classes/CustomSchemaMappingProvider.html">CustomSchemaMappingProvider</a></code>: A mapping provider that infers mapping initially, but also accepts custom mappings for specified entities. This was added to support custom migrations with <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code>s as well, but may also be used with <code>NSManagedObject</code>s.</li>
|
||
<li><code><a href="Classes/XcodeSchemaMappingProvider.html">XcodeSchemaMappingProvider</a></code>: A mapping provider which loads entity mappings from <em>.xcmappingmodel</em> files in a specified <code>Bundle</code>.</li>
|
||
<li><code><a href="Classes/InferredSchemaMappingProvider.html">InferredSchemaMappingProvider</a></code>: The default mapping provider which tries to infer model migration between two <code><a href="Protocols/DynamicSchema.html">DynamicSchema</a></code> versions either by searching all <em>.xcmappingmodel</em> files from <code>Bundle.allBundles</code>, or by relying on lightweight migration if possible.</li>
|
||
</ul>
|
||
|
||
<p>These mapping providers conform to <code><a href="Protocols/SchemaMappingProvider.html">SchemaMappingProvider</a></code> and can be passed to <code><a href="Classes/SQLiteStore.html">SQLiteStore</a></code>’s initializer:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">dataStack</span> <span class="o">=</span> <span class="kt">DataStack</span><span class="p">(</span><span class="nv">migrationChain</span><span class="p">:</span> <span class="p">[</span><span class="s">"MyAppModel"</span><span class="p">,</span> <span class="s">"MyAppModelV2"</span><span class="p">,</span> <span class="s">"MyAppModelV3"</span><span class="p">,</span> <span class="s">"MyAppModelV4"</span><span class="p">])</span>
|
||
<span class="n">_</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">addStorage</span><span class="p">(</span>
|
||
<span class="kt">SQLiteStore</span><span class="p">(</span>
|
||
<span class="nv">fileName</span><span class="p">:</span> <span class="s">"MyStore.sqlite"</span><span class="p">,</span>
|
||
<span class="nv">migrationMappingProviders</span><span class="p">:</span> <span class="p">[</span>
|
||
<span class="kt">XcodeSchemaMappingProvider</span><span class="p">(</span><span class="nv">from</span><span class="p">:</span> <span class="s">"V1"</span><span class="p">,</span> <span class="nv">to</span><span class="p">:</span> <span class="s">"V2"</span><span class="p">,</span> <span class="nv">mappingModelBundle</span><span class="p">:</span> <span class="kt">Bundle</span><span class="o">.</span><span class="n">main</span><span class="p">),</span>
|
||
<span class="kt">CustomSchemaMappingProvider</span><span class="p">(</span><span class="nv">from</span><span class="p">:</span> <span class="s">"V2"</span><span class="p">,</span> <span class="nv">to</span><span class="p">:</span> <span class="s">"V3"</span><span class="p">,</span> <span class="nv">entityMappings</span><span class="p">:</span> <span class="p">[</span><span class="o">.</span><span class="nf">deleteEntity</span><span class="p">(</span><span class="s">"Person"</span><span class="p">)</span> <span class="p">])</span>
|
||
<span class="p">]</span>
|
||
<span class="p">),</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">result</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>For version migrations present in the <code><a href="Classes/DataStack.html">DataStack</a></code>’s <code><a href="Structs/MigrationChain.html">MigrationChain</a></code> but not handled by any of the <code><a href="Classes/SQLiteStore.html">SQLiteStore</a></code>’s <code>migrationMappingProviders</code> array, CoreStore will automatically try to use <code><a href="Classes/InferredSchemaMappingProvider.html">InferredSchemaMappingProvider</a></code> as fallback. Finally if the <code><a href="Classes/InferredSchemaMappingProvider.html">InferredSchemaMappingProvider</a></code> could not resolve any mapping, the migration will fail and the <code><a href="Classes/DataStack.html#/s:9CoreStore9DataStackC10addStorage_10completionyx_ys6ResultOyxAA0aB5ErrorOGctAA0F9InterfaceRzlF">DataStack.addStorage(...)</a></code> method will report the failure.</p>
|
||
|
||
<p>For <code><a href="Classes/CustomSchemaMappingProvider.html">CustomSchemaMappingProvider</a></code>, more granular updates are supported through the dynamic objects <code>UnsafeSourceObject</code> and <code>UnsafeDestinationObject</code>. The example below allows the migration to conditionally ignore some objects:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">person_v2_to_v3_mapping</span> <span class="o">=</span> <span class="kt">CustomSchemaMappingProvider</span><span class="p">(</span>
|
||
<span class="nv">from</span><span class="p">:</span> <span class="s">"V2"</span><span class="p">,</span>
|
||
<span class="nv">to</span><span class="p">:</span> <span class="s">"V3"</span><span class="p">,</span>
|
||
<span class="nv">entityMappings</span><span class="p">:</span> <span class="p">[</span>
|
||
<span class="o">.</span><span class="nf">transformEntity</span><span class="p">(</span>
|
||
<span class="nv">sourceEntity</span><span class="p">:</span> <span class="s">"Person"</span><span class="p">,</span>
|
||
<span class="nv">destinationEntity</span><span class="p">:</span> <span class="s">"Person"</span><span class="p">,</span>
|
||
<span class="nv">transformer</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="nv">sourceObject</span><span class="p">:</span> <span class="kt">UnsafeSourceObject</span><span class="p">,</span> <span class="nv">createDestinationObject</span><span class="p">:</span> <span class="p">()</span> <span class="o">-></span> <span class="kt">UnsafeDestinationObject</span><span class="p">)</span> <span class="k">in</span>
|
||
|
||
<span class="k">if</span> <span class="p">(</span><span class="n">sourceObject</span><span class="p">[</span><span class="s">"isVeryOldAccount"</span><span class="p">]</span> <span class="k">as!</span> <span class="kt">Bool</span><span class="p">?)</span> <span class="o">==</span> <span class="kc">true</span> <span class="p">{</span>
|
||
<span class="k">return</span> <span class="c1">// this account is too old, don't migrate </span>
|
||
<span class="p">}</span>
|
||
<span class="c1">// migrate the rest</span>
|
||
<span class="k">let</span> <span class="nv">destinationObject</span> <span class="o">=</span> <span class="nf">createDestinationObject</span><span class="p">()</span>
|
||
<span class="n">destinationObject</span><span class="o">.</span><span class="n">enumerateAttributes</span> <span class="p">{</span> <span class="p">(</span><span class="n">attribute</span><span class="p">,</span> <span class="n">sourceAttribute</span><span class="p">)</span> <span class="k">in</span>
|
||
|
||
<span class="k">if</span> <span class="k">let</span> <span class="nv">sourceAttribute</span> <span class="o">=</span> <span class="n">sourceAttribute</span> <span class="p">{</span>
|
||
<span class="n">destinationObject</span><span class="p">[</span><span class="n">attribute</span><span class="p">]</span> <span class="o">=</span> <span class="n">sourceObject</span><span class="p">[</span><span class="n">sourceAttribute</span><span class="p">]</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
<span class="p">]</span>
|
||
<span class="p">)</span>
|
||
<span class="kt">SQLiteStore</span><span class="p">(</span>
|
||
<span class="nv">fileName</span><span class="p">:</span> <span class="s">"MyStore.sqlite"</span><span class="p">,</span>
|
||
<span class="nv">migrationMappingProviders</span><span class="p">:</span> <span class="p">[</span><span class="n">person_v2_to_v3_mapping</span><span class="p">]</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>The <code>UnsafeSourceObject</code> is a read-only proxy for an object existing in the source model version. The <code>UnsafeDestinationObject</code> is a read-write object that is inserted (optionally) to the destination model version. Both classes’ properties are accessed through key-value-coding.</p>
|
||
<h2 id='saving-and-processing-transactions' class='heading'>Saving and processing transactions</h2>
|
||
|
||
<p>To ensure deterministic state for objects in the read-only <code>NSManagedObjectContext</code>, CoreStore does not expose API’s for updating and saving directly from the main context (or any other context for that matter.) Instead, you spawn <em>transactions</em> from <code><a href="Classes/DataStack.html">DataStack</a></code> instances:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">dataStack</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">dataStack</span>
|
||
<span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="c1">// make changes</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">result</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>Transaction closures automatically save changes once the closures completes. To cancel and rollback a transaction, throw a <code><a href="Enums/CoreStoreError.html#/s:9CoreStore0aB5ErrorO13userCancelledyA2CmF">CoreStoreError.userCancelled</a></code> from inside the closure by calling <code>try transaction.cancel()</code>:</p>
|
||
<pre class="highlight swift"><code><span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="k">if</span> <span class="n">shouldCancel</span> <span class="p">{</span>
|
||
<span class="k">try</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">cancel</span><span class="p">()</span>
|
||
<span class="p">}</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">result</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="k">if</span> <span class="k">case</span> <span class="o">.</span><span class="nf">failure</span><span class="p">(</span><span class="o">.</span><span class="n">userCancelled</span><span class="p">)</span> <span class="o">=</span> <span class="n">result</span> <span class="p">{</span>
|
||
<span class="c1">// ... cancelled</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<blockquote>
|
||
<p>⚠️<strong>Important:</strong> Never use <code>try?</code> or <code>try!</code> on a <code>transaction.cancel()</code> call. Always use <code>try</code>. Using <code>try?</code> will swallow the cancellation and the transaction will proceed to save as normal. Using <code>try!</code> will crash the app as <code>transaction.cancel()</code> will <em>always</em> throw an error.</p>
|
||
</blockquote>
|
||
|
||
<p>The examples above use <code>perform(asynchronous:...)</code>, but there are actually 3 types of transactions at your disposal: <em>asynchronous</em>, <em>synchronous</em>, and <em>unsafe</em>.</p>
|
||
<h3 id='transaction-types' class='heading'>Transaction types</h3>
|
||
<h4 id='asynchronous-transactions' class='heading'>Asynchronous transactions</h4>
|
||
|
||
<p>are spawned from <code>perform(asynchronous:...)</code>. This method returns immediately and executes its closure from a background serial queue. The return value for the closure is declared as a generic type, so any value returned from the closure can be passed to the completion result:</p>
|
||
<pre class="highlight swift"><code><span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Bool</span> <span class="k">in</span>
|
||
<span class="c1">// make changes</span>
|
||
<span class="k">return</span> <span class="n">transaction</span><span class="o">.</span><span class="n">hasChanges</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">result</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="k">switch</span> <span class="n">result</span> <span class="p">{</span>
|
||
<span class="k">case</span> <span class="o">.</span><span class="nf">success</span><span class="p">(</span><span class="k">let</span> <span class="nv">hasChanges</span><span class="p">):</span> <span class="nf">print</span><span class="p">(</span><span class="s">"success! Has changes? </span><span class="se">\(</span><span class="n">hasChanges</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
|
||
<span class="k">case</span> <span class="o">.</span><span class="nf">failure</span><span class="p">(</span><span class="k">let</span> <span class="nv">error</span><span class="p">):</span> <span class="nf">print</span><span class="p">(</span><span class="n">error</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>The success and failure can also be declared as separate handlers:</p>
|
||
<pre class="highlight swift"><code><span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Int</span> <span class="k">in</span>
|
||
<span class="c1">// make changes</span>
|
||
<span class="k">return</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">delete</span><span class="p">(</span><span class="n">objects</span><span class="p">)</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">success</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="nv">numberOfDeletedObjects</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="nf">print</span><span class="p">(</span><span class="s">"success! Deleted </span><span class="se">\(</span><span class="n">numberOfDeletedObjects</span><span class="se">)</span><span class="s"> objects"</span><span class="p">)</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">failure</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">error</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="nf">print</span><span class="p">(</span><span class="n">error</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<blockquote>
|
||
<p>⚠️Be careful when returning <code>NSManagedObject</code>s or <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code>s from the transaction closure. Those instances are for the transaction’s use only. See <a href="#passing-objects-safely">Passing objects safely</a>.</p>
|
||
</blockquote>
|
||
|
||
<p>Transactions created from <code>perform(asynchronous:...)</code> are instances of <code><a href="Classes/AsynchronousDataTransaction.html">AsynchronousDataTransaction</a></code>.</p>
|
||
<h4 id='synchronous-transactions' class='heading'>Synchronous transactions</h4>
|
||
|
||
<p>are created from <code>perform(synchronous:...)</code>. While the syntax is similar to its asynchronous counterpart, <code>perform(synchronous:...)</code> waits for its transaction block to complete before returning:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">hasChanges</span> <span class="o">=</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">synchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Bool</span> <span class="k">in</span>
|
||
<span class="c1">// make changes</span>
|
||
<span class="k">return</span> <span class="n">transaction</span><span class="o">.</span><span class="n">hasChanges</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p><code>transaction</code> above is a <code><a href="Classes/SynchronousDataTransaction.html">SynchronousDataTransaction</a></code> instance.</p>
|
||
|
||
<p>Since <code>perform(synchronous:...)</code> technically blocks two queues (the caller’s queue and the transaction’s background queue), it is considered less safe as it’s more prone to deadlock. Take special care that the closure does not block on any other external queues.</p>
|
||
|
||
<p>By default, <code>perform(synchronous:...)</code> will wait for observers such as <code><a href="Classes/ListMonitor.html">ListMonitor</a></code>s to be notified before the method returns. This may cause deadlocks, especially if you are calling this from the main thread. To reduce this risk, you may try to set the <code>waitForAllObservers:</code> parameter to <code>false</code>. Doing so tells the <code><a href="Classes/SynchronousDataTransaction.html">SynchronousDataTransaction</a></code> to block only until it completes saving. It will not wait for other context’s to receive those changes. This reduces deadlock risk but may have surprising side-effects:</p>
|
||
<pre class="highlight swift"><code><span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">synchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="k">in</span>
|
||
<span class="k">let</span> <span class="nv">person</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">create</span><span class="p">(</span><span class="kt">Into</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span><span class="p">())</span>
|
||
<span class="n">person</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="s">"John"</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">waitForAllObservers</span><span class="p">:</span> <span class="kc">false</span>
|
||
<span class="p">)</span>
|
||
<span class="k">let</span> <span class="nv">newPerson</span> <span class="o">=</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">fetchOne</span><span class="p">(</span><span class="kt">From</span><span class="o"><</span><span class="kt">Person</span><span class="o">>.</span><span class="nf">where</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s">"John"</span><span class="p">))</span>
|
||
<span class="c1">// newPerson may be nil!</span>
|
||
<span class="c1">// The DataStack may have not yet received the update notification.</span>
|
||
</code></pre>
|
||
|
||
<p>Due to this complicated nature of synchronous transactions, if your app has very heavy transaction throughput it is highly recommended to use <a href="#asynchronous-transactions">asynchronous transactions</a> instead.</p>
|
||
<h4 id='unsafe-transactions' class='heading'>Unsafe transactions</h4>
|
||
|
||
<p>are special in that they do not enclose updates within a closure:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">transaction</span> <span class="o">=</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">beginUnsafe</span><span class="p">()</span>
|
||
<span class="c1">// make changes</span>
|
||
<span class="nf">downloadJSONWithCompletion</span><span class="p">({</span> <span class="p">(</span><span class="n">json</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
|
||
<span class="c1">// make other changes</span>
|
||
<span class="n">transaction</span><span class="o">.</span><span class="nf">commit</span><span class="p">()</span>
|
||
<span class="p">})</span>
|
||
<span class="nf">downloadAnotherJSONWithCompletion</span><span class="p">({</span> <span class="p">(</span><span class="n">json</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
|
||
<span class="c1">// make some other changes</span>
|
||
<span class="n">transaction</span><span class="o">.</span><span class="nf">commit</span><span class="p">()</span>
|
||
<span class="p">})</span>
|
||
</code></pre>
|
||
|
||
<p>This allows for non-contiguous updates. Do note that this flexibility comes with a price: you are now responsible for managing concurrency for the transaction. As uncle Ben said, “with great power comes great race conditions.”</p>
|
||
|
||
<p>As the above example also shows, with unsafe transactions <code>commit()</code> can be called multiple times.</p>
|
||
|
||
<p>You’ve seen how to create transactions, but we have yet to see how to make <em>creates</em>, <em>updates</em>, and <em>deletes</em>. The 3 types of transactions above are all subclasses of <code><a href="Classes/BaseDataTransaction.html">BaseDataTransaction</a></code>, which implements the methods shown below.</p>
|
||
<h3 id='creating-objects' class='heading'>Creating objects</h3>
|
||
|
||
<p>The <code>create(...)</code> method accepts an <code><a href="Structs/Into.html">Into</a></code> clause which specifies the entity for the object you want to create:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">person</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">create</span><span class="p">(</span><span class="kt">Into</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">())</span>
|
||
</code></pre>
|
||
|
||
<p>While the syntax is straightforward, CoreStore does not just naively insert a new object. This single line does the following:</p>
|
||
|
||
<ul>
|
||
<li>Checks that the entity type exists in any of the transaction’s parent persistent store</li>
|
||
<li>If the entity belongs to only one persistent store, a new object is inserted into that store and returned from <code>create(...)</code></li>
|
||
<li>If the entity does not belong to any store, an assertion failure will be raised. <strong>This is a programmer error and should never occur in production code.</strong></li>
|
||
<li>If the entity belongs to multiple stores, an assertion failure will be raised. <strong>This is also a programmer error and should never occur in production code.</strong> Normally, with Core Data you can insert an object in this state but saving the <code>NSManagedObjectContext</code> will always fail. CoreStore checks this for you at creation time when it makes sense (not during save).</li>
|
||
</ul>
|
||
|
||
<p>If the entity exists in multiple configurations, you need to provide the configuration name for the destination persistent store:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">person</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">create</span><span class="p">(</span><span class="kt">Into</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(</span><span class="s">"Config1"</span><span class="p">))</span>
|
||
</code></pre>
|
||
|
||
<p>or if the persistent store is the auto-generated “Default” configuration, specify <code>nil</code>:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">person</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">create</span><span class="p">(</span><span class="kt">Into</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(</span><span class="kc">nil</span><span class="p">))</span>
|
||
</code></pre>
|
||
|
||
<p>Note that if you do explicitly specify the configuration name, CoreStore will only try to insert the created object to that particular store and will fail if that store is not found; it will not fall back to any other configuration that the entity belongs to. </p>
|
||
<h3 id='updating-objects' class='heading'>Updating objects</h3>
|
||
|
||
<p>After creating an object from the transaction, you can simply update its properties as normal:</p>
|
||
<pre class="highlight swift"><code><span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="k">let</span> <span class="nv">person</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">create</span><span class="p">(</span><span class="kt">Into</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">())</span>
|
||
<span class="n">person</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="s">"John Smith"</span>
|
||
<span class="n">person</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="mi">30</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>To update an existing object, fetch the object’s instance from the transaction:</p>
|
||
<pre class="highlight swift"><code><span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="k">let</span> <span class="nv">person</span> <span class="o">=</span> <span class="k">try</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">fetchOne</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">()</span>
|
||
<span class="o">.</span><span class="nf">where</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s">"Jane Smith"</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
<span class="n">person</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="n">person</span><span class="o">.</span><span class="n">age</span> <span class="o">+</span> <span class="mi">1</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p><em>(For more about fetching, see <a href="#fetching-and-querying">Fetching and querying</a>)</em></p>
|
||
|
||
<p><strong>Do not update an instance that was not created/fetched from the transaction.</strong> If you have a reference to the object already, use the transaction’s <code>edit(...)</code> method to get an editable proxy instance for that object:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">jane</span><span class="p">:</span> <span class="kt">MyPersonEntity</span> <span class="o">=</span> <span class="c1">// ...</span>
|
||
|
||
<span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="c1">// WRONG: jane.age = jane.age + 1</span>
|
||
<span class="c1">// RIGHT:</span>
|
||
<span class="k">let</span> <span class="nv">jane</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">edit</span><span class="p">(</span><span class="n">jane</span><span class="p">)</span><span class="o">!</span> <span class="c1">// using the same variable name protects us from misusing the non-transaction instance</span>
|
||
<span class="n">jane</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="n">jane</span><span class="o">.</span><span class="n">age</span> <span class="o">+</span> <span class="mi">1</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>This is also true when updating an object’s relationships. Make sure that the object assigned to the relationship is also created/fetched from the transaction:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">jane</span><span class="p">:</span> <span class="kt">MyPersonEntity</span> <span class="o">=</span> <span class="c1">// ...</span>
|
||
<span class="k">let</span> <span class="nv">john</span><span class="p">:</span> <span class="kt">MyPersonEntity</span> <span class="o">=</span> <span class="c1">// ...</span>
|
||
|
||
<span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="c1">// WRONG: jane.friends = [john]</span>
|
||
<span class="c1">// RIGHT:</span>
|
||
<span class="k">let</span> <span class="nv">jane</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">edit</span><span class="p">(</span><span class="n">jane</span><span class="p">)</span><span class="o">!</span>
|
||
<span class="k">let</span> <span class="nv">john</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">edit</span><span class="p">(</span><span class="n">john</span><span class="p">)</span><span class="o">!</span>
|
||
<span class="n">jane</span><span class="o">.</span><span class="n">friends</span> <span class="o">=</span> <span class="kt">NSSet</span><span class="p">(</span><span class="nv">array</span><span class="p">:</span> <span class="p">[</span><span class="n">john</span><span class="p">])</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
<h3 id='deleting-objects' class='heading'>Deleting objects</h3>
|
||
|
||
<p>Deleting an object is simpler because you can tell a transaction to delete an object directly without fetching an editable proxy (CoreStore does that for you):</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">john</span><span class="p">:</span> <span class="kt">MyPersonEntity</span> <span class="o">=</span> <span class="c1">// ...</span>
|
||
|
||
<span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="n">transaction</span><span class="o">.</span><span class="nf">delete</span><span class="p">(</span><span class="n">john</span><span class="p">)</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>or several objects at once:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">john</span><span class="p">:</span> <span class="kt">MyPersonEntity</span> <span class="o">=</span> <span class="c1">// ...</span>
|
||
<span class="k">let</span> <span class="nv">jane</span><span class="p">:</span> <span class="kt">MyPersonEntity</span> <span class="o">=</span> <span class="c1">// ...</span>
|
||
|
||
<span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="k">try</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">delete</span><span class="p">(</span><span class="n">john</span><span class="p">,</span> <span class="n">jane</span><span class="p">)</span>
|
||
<span class="c1">// try transaction.delete([john, jane]) is also allowed</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>If you do not have references yet to the objects to be deleted, transactions have a <code>deleteAll(...)</code> method you can pass a query to:</p>
|
||
<pre class="highlight swift"><code><span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="k">try</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">deleteAll</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">()</span>
|
||
<span class="o">.</span><span class="nf">where</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">age</span> <span class="o">></span> <span class="mi">30</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
<h3 id='passing-objects-safely' class='heading'>Passing objects safely</h3>
|
||
|
||
<p>Always remember that the <code><a href="Classes/DataStack.html">DataStack</a></code> and individual transactions manage different <code>NSManagedObjectContext</code>s so you cannot just use objects between them. That’s why transactions have an <code>edit(...)</code> method:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">jane</span><span class="p">:</span> <span class="kt">MyPersonEntity</span> <span class="o">=</span> <span class="c1">// ...</span>
|
||
|
||
<span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="k">let</span> <span class="nv">jane</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">edit</span><span class="p">(</span><span class="n">jane</span><span class="p">)</span><span class="o">!</span>
|
||
<span class="n">jane</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="n">jane</span><span class="o">.</span><span class="n">age</span> <span class="o">+</span> <span class="mi">1</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>But <code>CoreStore</code>, <code><a href="Classes/DataStack.html">DataStack</a></code> and <code><a href="Classes/BaseDataTransaction.html">BaseDataTransaction</a></code> have a very flexible <code>fetchExisting(...)</code> method that you can pass instances back and forth with:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">jane</span><span class="p">:</span> <span class="kt">MyPersonEntity</span> <span class="o">=</span> <span class="c1">// ...</span>
|
||
|
||
<span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">MyPersonEntity</span> <span class="k">in</span>
|
||
<span class="k">let</span> <span class="nv">jane</span> <span class="o">=</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">fetchExisting</span><span class="p">(</span><span class="n">jane</span><span class="p">)</span><span class="o">!</span> <span class="c1">// instance for transaction</span>
|
||
<span class="n">jane</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="n">jane</span><span class="o">.</span><span class="n">age</span> <span class="o">+</span> <span class="mi">1</span>
|
||
<span class="k">return</span> <span class="n">jane</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">success</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transactionJane</span><span class="p">)</span> <span class="k">in</span>
|
||
<span class="k">let</span> <span class="nv">jane</span> <span class="o">=</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">fetchExisting</span><span class="p">(</span><span class="n">transactionJane</span><span class="p">)</span><span class="o">!</span> <span class="c1">// instance for DataStack</span>
|
||
<span class="nf">print</span><span class="p">(</span><span class="n">jane</span><span class="o">.</span><span class="n">age</span><span class="p">)</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">failure</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">error</span><span class="p">)</span> <span class="k">in</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p><code>fetchExisting(...)</code> also works with multiple <code>NSManagedObject</code>s, <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code>s, or with <code>NSManagedObjectID</code>s:</p>
|
||
<pre class="highlight swift"><code><span class="k">var</span> <span class="nv">peopleIDs</span><span class="p">:</span> <span class="p">[</span><span class="kt">NSManagedObjectID</span><span class="p">]</span> <span class="o">=</span> <span class="c1">// ...</span>
|
||
|
||
<span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="k">let</span> <span class="nv">jane</span> <span class="o">=</span> <span class="k">try</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">fetchOne</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">()</span>
|
||
<span class="o">.</span><span class="nf">where</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s">"Jane Smith"</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
<span class="n">jane</span><span class="o">.</span><span class="n">friends</span> <span class="o">=</span> <span class="kt">NSSet</span><span class="p">(</span><span class="nv">array</span><span class="p">:</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">fetchExisting</span><span class="p">(</span><span class="n">peopleIDs</span><span class="p">)</span><span class="o">!</span><span class="p">)</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
<h2 id='importing-data' class='heading'>Importing data</h2>
|
||
|
||
<p>Some times, if not most of the time, the data that we save to Core Data comes from external sources such as web servers or external files. If you have a JSON dictionary for example, you may be extracting values as such:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">json</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">]</span> <span class="o">=</span> <span class="c1">// ...</span>
|
||
<span class="n">person</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">json</span><span class="p">[</span><span class="s">"name"</span><span class="p">]</span> <span class="k">as?</span> <span class="kt">NSString</span>
|
||
<span class="n">person</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="n">json</span><span class="p">[</span><span class="s">"age"</span><span class="p">]</span> <span class="k">as?</span> <span class="kt">NSNumber</span>
|
||
<span class="c1">// ...</span>
|
||
</code></pre>
|
||
|
||
<p>If you have many attributes, you don’t want to keep repeating this mapping everytime you want to import data. CoreStore lets you write the data mapping code just once, and all you have to do is call <code>importObject(...)</code> or <code>importUniqueObject(...)</code> through <code><a href="Classes/BaseDataTransaction.html">BaseDataTransaction</a></code> subclasses:</p>
|
||
<pre class="highlight swift"><code><span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="k">let</span> <span class="nv">json</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">]</span> <span class="o">=</span> <span class="c1">// ...</span>
|
||
<span class="k">try!</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">importObject</span><span class="p">(</span>
|
||
<span class="kt">Into</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(),</span>
|
||
<span class="nv">source</span><span class="p">:</span> <span class="n">json</span>
|
||
<span class="p">)</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>To support data import for an entity, implement either <code><a href="Protocols/ImportableObject.html">ImportableObject</a></code> or <code><a href="Protocols/ImportableUniqueObject.html">ImportableUniqueObject</a></code> on the <code>NSManagedObject</code> or <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code> subclass:</p>
|
||
|
||
<ul>
|
||
<li><code><a href="Protocols/ImportableObject.html">ImportableObject</a></code>: Use this protocol if the object have no inherent uniqueness and new objects should always be added when calling <code>importObject(...)</code>.</li>
|
||
<li><code><a href="Protocols/ImportableUniqueObject.html">ImportableUniqueObject</a></code>: Use this protocol to specify a unique ID for an object that will be used to distinguish whether a new object should be created or if an existing object should be updated when calling <code>importUniqueObject(...)</code>.</li>
|
||
</ul>
|
||
|
||
<p>Both protocols require implementers to specify an <code>ImportSource</code> which can be set to any type that the object can extract data from:</p>
|
||
<pre class="highlight swift"><code><span class="kd">typealias</span> <span class="kt">ImportSource</span> <span class="o">=</span> <span class="kt">NSDictionary</span>
|
||
</code></pre>
|
||
<pre class="highlight swift"><code><span class="kd">typealias</span> <span class="kt">ImportSource</span> <span class="o">=</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">]</span>
|
||
</code></pre>
|
||
<pre class="highlight swift"><code><span class="kd">typealias</span> <span class="kt">ImportSource</span> <span class="o">=</span> <span class="kt">NSData</span>
|
||
</code></pre>
|
||
|
||
<p>You can even use external types from popular 3rd-party JSON libraries, or just simple tuples or primitives.</p>
|
||
<h4 id='code-importableobject-code' class='heading'><code><a href="Protocols/ImportableObject.html">ImportableObject</a></code></h4>
|
||
|
||
<p><code><a href="Protocols/ImportableObject.html">ImportableObject</a></code> is a very simple protocol:</p>
|
||
<pre class="highlight swift"><code><span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">ImportableObject</span><span class="p">:</span> <span class="kt">AnyObject</span> <span class="p">{</span>
|
||
<span class="kd">typealias</span> <span class="kt">ImportSource</span>
|
||
<span class="kd">static</span> <span class="kd">func</span> <span class="nf">shouldInsert</span><span class="p">(</span><span class="n">from</span> <span class="nv">source</span><span class="p">:</span> <span class="kt">ImportSource</span><span class="p">,</span> <span class="k">in</span> <span class="nv">transaction</span><span class="p">:</span> <span class="kt">BaseDataTransaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Bool</span>
|
||
<span class="kd">func</span> <span class="nf">didInsert</span><span class="p">(</span><span class="n">from</span> <span class="nv">source</span><span class="p">:</span> <span class="kt">ImportSource</span><span class="p">,</span> <span class="k">in</span> <span class="nv">transaction</span><span class="p">:</span> <span class="kt">BaseDataTransaction</span><span class="p">)</span> <span class="k">throws</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>First, set <code>ImportSource</code> to the expected type of the data source:</p>
|
||
<pre class="highlight swift"><code><span class="kd">typealias</span> <span class="kt">ImportSource</span> <span class="o">=</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">]</span>
|
||
</code></pre>
|
||
|
||
<p>This lets us call <code>importObject(_:source:)</code> with any <code>[String: Any]</code> type as the argument to <code>source</code>:</p>
|
||
<pre class="highlight swift"><code><span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="k">let</span> <span class="nv">json</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">]</span> <span class="o">=</span> <span class="c1">// ...</span>
|
||
<span class="k">try!</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">importObject</span><span class="p">(</span>
|
||
<span class="kt">Into</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(),</span>
|
||
<span class="nv">source</span><span class="p">:</span> <span class="n">json</span>
|
||
<span class="p">)</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>The actual extraction and assignment of values should be implemented in the <code>didInsert(from:in:)</code> method of the <code><a href="Protocols/ImportableObject.html">ImportableObject</a></code> protocol:</p>
|
||
<pre class="highlight swift"><code><span class="kd">func</span> <span class="nf">didInsert</span><span class="p">(</span><span class="n">from</span> <span class="nv">source</span><span class="p">:</span> <span class="kt">ImportSource</span><span class="p">,</span> <span class="k">in</span> <span class="nv">transaction</span><span class="p">:</span> <span class="kt">BaseDataTransaction</span><span class="p">)</span> <span class="k">throws</span> <span class="p">{</span>
|
||
<span class="k">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">source</span><span class="p">[</span><span class="s">"name"</span><span class="p">]</span> <span class="k">as?</span> <span class="kt">NSString</span>
|
||
<span class="k">self</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="n">source</span><span class="p">[</span><span class="s">"age"</span><span class="p">]</span> <span class="k">as?</span> <span class="kt">NSNumber</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>Transactions also let you import multiple objects at once using the <code>importObjects(_:sourceArray:)</code> method:</p>
|
||
<pre class="highlight swift"><code><span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="k">let</span> <span class="nv">jsonArray</span><span class="p">:</span> <span class="p">[[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">]]</span> <span class="o">=</span> <span class="c1">// ...</span>
|
||
<span class="k">try!</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">importObjects</span><span class="p">(</span>
|
||
<span class="kt">Into</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(),</span>
|
||
<span class="nv">sourceArray</span><span class="p">:</span> <span class="n">jsonArray</span> <span class="c1">// make sure this is of type Array<MyPersonEntity.ImportSource></span>
|
||
<span class="p">)</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>Doing so tells the transaction to iterate through the array of import sources and calls <code>shouldInsert(from:in:)</code> on the <code><a href="Protocols/ImportableObject.html">ImportableObject</a></code> to determine which instances should be created. You can do validations and return <code>false</code> from <code>shouldInsert(from:in:)</code> if you want to skip importing from a source and continue on with the other sources in the array.</p>
|
||
|
||
<p>If on the other hand, your validation in one of the sources failed in such a manner that all other sources should also be rolled back and cancelled, you can <code>throw</code> from within <code>didInsert(from:in:)</code>:</p>
|
||
<pre class="highlight swift"><code><span class="kd">func</span> <span class="nf">didInsert</span><span class="p">(</span><span class="n">from</span> <span class="nv">source</span><span class="p">:</span> <span class="kt">ImportSource</span><span class="p">,</span> <span class="k">in</span> <span class="nv">transaction</span><span class="p">:</span> <span class="kt">BaseDataTransaction</span><span class="p">)</span> <span class="k">throws</span> <span class="p">{</span>
|
||
<span class="k">self</span><span class="o">.</span><span class="n">name</span> <span class="o">=</span> <span class="n">source</span><span class="p">[</span><span class="s">"name"</span><span class="p">]</span> <span class="k">as?</span> <span class="kt">NSString</span>
|
||
<span class="k">self</span><span class="o">.</span><span class="n">age</span> <span class="o">=</span> <span class="n">source</span><span class="p">[</span><span class="s">"age"</span><span class="p">]</span> <span class="k">as?</span> <span class="kt">NSNumber</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="k">if</span> <span class="k">self</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
|
||
<span class="k">throw</span> <span class="kt">Errors</span><span class="o">.</span><span class="kt">InvalidNameError</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>Doing so can let you abandon an invalid transaction immediately:</p>
|
||
<pre class="highlight swift"><code><span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="k">let</span> <span class="nv">jsonArray</span><span class="p">:</span> <span class="p">[[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">]]</span> <span class="o">=</span> <span class="c1">// ...</span>
|
||
|
||
<span class="k">try</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">importObjects</span><span class="p">(</span>
|
||
<span class="kt">Into</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(),</span>
|
||
<span class="nv">sourceArray</span><span class="p">:</span> <span class="n">jsonArray</span>
|
||
<span class="p">)</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">success</span><span class="p">:</span> <span class="p">{</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">failure</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">error</span><span class="p">)</span> <span class="k">in</span>
|
||
<span class="k">switch</span> <span class="n">error</span> <span class="p">{</span>
|
||
<span class="k">case</span> <span class="kt">Errors</span><span class="o">.</span><span class="kt">InvalidNameError</span><span class="p">:</span> <span class="nf">print</span><span class="p">(</span><span class="s">"Invalid name"</span><span class="p">)</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
<h4 id='code-importableuniqueobject-code' class='heading'><code><a href="Protocols/ImportableUniqueObject.html">ImportableUniqueObject</a></code></h4>
|
||
|
||
<p>Typically, we don’t just keep creating objects every time we import data. Usually we also need to update already existing objects. Implementing the <code><a href="Protocols/ImportableUniqueObject.html">ImportableUniqueObject</a></code> protocol lets you specify a “unique ID” that transactions can use to search existing objects before creating new ones:</p>
|
||
<pre class="highlight swift"><code><span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">ImportableUniqueObject</span><span class="p">:</span> <span class="kt">ImportableObject</span> <span class="p">{</span>
|
||
<span class="kd">typealias</span> <span class="kt">ImportSource</span>
|
||
<span class="kd">typealias</span> <span class="kt">UniqueIDType</span><span class="p">:</span> <span class="kt">ImportableAttributeType</span>
|
||
|
||
<span class="kd">static</span> <span class="k">var</span> <span class="nv">uniqueIDKeyPath</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span> <span class="k">get</span> <span class="p">}</span>
|
||
<span class="k">var</span> <span class="nv">uniqueIDValue</span><span class="p">:</span> <span class="kt">UniqueIDType</span> <span class="p">{</span> <span class="k">get</span> <span class="k">set</span> <span class="p">}</span>
|
||
|
||
<span class="kd">static</span> <span class="kd">func</span> <span class="nf">shouldInsert</span><span class="p">(</span><span class="n">from</span> <span class="nv">source</span><span class="p">:</span> <span class="kt">ImportSource</span><span class="p">,</span> <span class="k">in</span> <span class="nv">transaction</span><span class="p">:</span> <span class="kt">BaseDataTransaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Bool</span>
|
||
<span class="kd">static</span> <span class="kd">func</span> <span class="nf">shouldUpdate</span><span class="p">(</span><span class="n">from</span> <span class="nv">source</span><span class="p">:</span> <span class="kt">ImportSource</span><span class="p">,</span> <span class="k">in</span> <span class="nv">transaction</span><span class="p">:</span> <span class="kt">BaseDataTransaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Bool</span>
|
||
<span class="kd">static</span> <span class="kd">func</span> <span class="nf">uniqueID</span><span class="p">(</span><span class="n">from</span> <span class="nv">source</span><span class="p">:</span> <span class="kt">ImportSource</span><span class="p">,</span> <span class="k">in</span> <span class="nv">transaction</span><span class="p">:</span> <span class="kt">BaseDataTransaction</span><span class="p">)</span> <span class="k">throws</span> <span class="o">-></span> <span class="kt">UniqueIDType</span><span class="p">?</span>
|
||
<span class="kd">func</span> <span class="nf">didInsert</span><span class="p">(</span><span class="n">from</span> <span class="nv">source</span><span class="p">:</span> <span class="kt">ImportSource</span><span class="p">,</span> <span class="k">in</span> <span class="nv">transaction</span><span class="p">:</span> <span class="kt">BaseDataTransaction</span><span class="p">)</span> <span class="k">throws</span>
|
||
<span class="kd">func</span> <span class="nf">update</span><span class="p">(</span><span class="n">from</span> <span class="nv">source</span><span class="p">:</span> <span class="kt">ImportSource</span><span class="p">,</span> <span class="k">in</span> <span class="nv">transaction</span><span class="p">:</span> <span class="kt">BaseDataTransaction</span><span class="p">)</span> <span class="k">throws</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>Notice that it has the same insert methods as <code><a href="Protocols/ImportableObject.html">ImportableObject</a></code>, with additional methods for updates and for specifying the unique ID:</p>
|
||
<pre class="highlight swift"><code><span class="kd">class</span> <span class="k">var</span> <span class="nv">uniqueIDKeyPath</span><span class="p">:</span> <span class="kt">String</span> <span class="p">{</span>
|
||
<span class="k">return</span> <span class="kd">#keyPath(</span><span class="nf">MyPersonEntity.personID</span><span class="kd">)</span>
|
||
<span class="p">}</span>
|
||
<span class="k">var</span> <span class="nv">uniqueIDValue</span><span class="p">:</span> <span class="kt">Int</span> <span class="p">{</span>
|
||
<span class="k">get</span> <span class="p">{</span> <span class="k">return</span> <span class="k">self</span><span class="o">.</span><span class="n">personID</span> <span class="p">}</span>
|
||
<span class="k">set</span> <span class="p">{</span> <span class="k">self</span><span class="o">.</span><span class="n">personID</span> <span class="o">=</span> <span class="n">newValue</span> <span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="kd">class</span> <span class="kd">func</span> <span class="nf">uniqueID</span><span class="p">(</span><span class="n">from</span> <span class="nv">source</span><span class="p">:</span> <span class="kt">ImportSource</span><span class="p">,</span> <span class="k">in</span> <span class="nv">transaction</span><span class="p">:</span> <span class="kt">BaseDataTransaction</span><span class="p">)</span> <span class="k">throws</span> <span class="o">-></span> <span class="kt">Int</span><span class="p">?</span> <span class="p">{</span>
|
||
<span class="k">return</span> <span class="n">source</span><span class="p">[</span><span class="s">"id"</span><span class="p">]</span> <span class="k">as?</span> <span class="kt">Int</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>For <code><a href="Protocols/ImportableUniqueObject.html">ImportableUniqueObject</a></code>, the extraction and assignment of values should be implemented from the <code>update(from:in:)</code> method. The <code>didInsert(from:in:)</code> by default calls <code>update(from:in:)</code>, but you can separate the implementation for inserts and updates if needed.</p>
|
||
|
||
<p>You can then create/update an object by calling a transaction’s <code>importUniqueObject(...)</code> method:</p>
|
||
<pre class="highlight swift"><code><span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="k">let</span> <span class="nv">json</span><span class="p">:</span> <span class="p">[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">Any</span><span class="p">]</span> <span class="o">=</span> <span class="c1">// ...</span>
|
||
<span class="k">try!</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">importUniqueObject</span><span class="p">(</span>
|
||
<span class="kt">Into</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(),</span>
|
||
<span class="nv">source</span><span class="p">:</span> <span class="n">json</span>
|
||
<span class="p">)</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>or multiple objects at once with the <code>importUniqueObjects(...)</code> method:</p>
|
||
<pre class="highlight swift"><code><span class="n">dataStack</span><span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="k">let</span> <span class="nv">jsonArray</span><span class="p">:</span> <span class="p">[[</span><span class="kt">String</span><span class="p">:</span> <span class="kt">AnyObject</span><span class="p">]]</span> <span class="o">=</span> <span class="c1">// ...</span>
|
||
<span class="k">try!</span> <span class="n">transaction</span><span class="o">.</span><span class="nf">importUniqueObjects</span><span class="p">(</span>
|
||
<span class="kt">Into</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(),</span>
|
||
<span class="nv">sourceArray</span><span class="p">:</span> <span class="n">jsonArray</span>
|
||
<span class="p">)</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">completion</span><span class="p">:</span> <span class="p">{</span> <span class="n">_</span> <span class="k">in</span> <span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>As with <code><a href="Protocols/ImportableObject.html">ImportableObject</a></code>, you can control whether to skip importing an object by implementing
|
||
<code>shouldInsert(from:in:)</code> and <code>shouldUpdate(from:in:)</code>, or to cancel all objects by <code>throw</code>ing an error from the <code>uniqueID(from:in:)</code>, <code>didInsert(from:in:)</code> or <code>update(from:in:)</code> methods.</p>
|
||
<h2 id='fetching-and-querying' class='heading'>Fetching and Querying</h2>
|
||
|
||
<p>Before we dive in, be aware that CoreStore distinguishes between <em>fetching</em> and <em>querying</em>:</p>
|
||
|
||
<ul>
|
||
<li>A <em>fetch</em> executes searches from a specific <em>transaction</em> or <em>data stack</em>. This means fetches can include pending objects (i.e. before a transaction calls on <code>commit()</code>.) Use fetches when:
|
||
|
||
<ul>
|
||
<li>results need to be <code>NSManagedObject</code> or <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code> instances</li>
|
||
<li>unsaved objects should be included in the search (though fetches can be configured to exclude unsaved ones)</li>
|
||
</ul></li>
|
||
<li>A <em>query</em> pulls data straight from the persistent store. This means faster searches when computing aggregates such as <em>count</em>, <em>min</em>, <em>max</em>, etc. Use queries when:
|
||
|
||
<ul>
|
||
<li>you need to compute aggregate functions (see below for a list of supported functions)</li>
|
||
<li>results can be raw values like <code>NSString</code>s, <code>NSNumber</code>s, <code>Int</code>s, <code>NSDate</code>s, an <code>NSDictionary</code> of key-values, or any type that conform to <code><a href="Protocols/QueryableAttributeType.html">QueryableAttributeType</a></code>. (See <em>QueryableAttributeType.swift</em> for a list of built-in types)</li>
|
||
<li>only values for specified attribute keys need to be included in the results</li>
|
||
<li>unsaved objects should be ignored</li>
|
||
</ul></li>
|
||
</ul>
|
||
<h4 id='code-from-code-clause' class='heading'><code><a href="Structs/From.html">From</a></code> clause</h4>
|
||
|
||
<p>The search conditions for fetches and queries are specified using <em>clauses</em>. All fetches and queries require a <code><a href="Structs/From.html">From</a></code> clause that indicates the target entity type:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">people</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">fetchAll</span><span class="p">(</span><span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">())</span>
|
||
</code></pre>
|
||
|
||
<p><code>people</code> in the example above will be of type <code>[MyPersonEntity]</code>. The <code>From<MyPersonEntity>()</code> clause indicates a fetch to all persistent stores that <code>MyPersonEntity</code> belong to.</p>
|
||
|
||
<p>If the entity exists in multiple configurations and you need to only search from a particular configuration, indicate in the <code><a href="Structs/From.html">From</a></code> clause the configuration name for the destination persistent store:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">people</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">fetchAll</span><span class="p">(</span><span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(</span><span class="s">"Config1"</span><span class="p">))</span> <span class="c1">// ignore objects in persistent stores other than the "Config1" configuration</span>
|
||
</code></pre>
|
||
|
||
<p>or if the persistent store is the auto-generated “Default” configuration, specify <code>nil</code>:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">person</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">fetchAll</span><span class="p">(</span><span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(</span><span class="kc">nil</span><span class="p">))</span>
|
||
</code></pre>
|
||
|
||
<p>Now we know how to use a <code><a href="Structs/From.html">From</a></code> clause, let’s move on to fetching and querying.</p>
|
||
<h3 id='fetching' class='heading'>Fetching</h3>
|
||
|
||
<p>There are currently 5 fetch methods you can call from <code>CoreStore</code>, from a <code><a href="Classes/DataStack.html">DataStack</a></code> instance, or from a <code><a href="Classes/BaseDataTransaction.html">BaseDataTransaction</a></code> instance. All of the methods below accept the same parameters: a required <code><a href="Structs/From.html">From</a></code> clause, and an optional series of <code><a href="Structs/Where.html">Where</a></code>, <code><a href="Structs/OrderBy.html">OrderBy</a></code>, and/or <code><a href="Structs/Tweak.html">Tweak</a></code> clauses.</p>
|
||
|
||
<ul>
|
||
<li><code>fetchAll(...)</code> - returns an array of all objects that match the criteria.</li>
|
||
<li><code>fetchOne(...)</code> - returns the first object that match the criteria.</li>
|
||
<li><code>fetchCount(...)</code> - returns the number of objects that match the criteria.</li>
|
||
<li><code>fetchObjectIDs(...)</code> - returns an array of <code>NSManagedObjectID</code>s for all objects that match the criteria.</li>
|
||
<li><code>fetchObjectID(...)</code> - returns the <code>NSManagedObjectID</code>s for the first objects that match the criteria.</li>
|
||
</ul>
|
||
|
||
<p>Each method’s purpose is straightforward, but we need to understand how to set the clauses for the fetch.</p>
|
||
<h4 id='code-where-code-clause' class='heading'><code><a href="Structs/Where.html">Where</a></code> clause</h4>
|
||
|
||
<p>The <code><a href="Structs/Where.html">Where</a></code> clause is CoreStore’s <code>NSPredicate</code> wrapper. It specifies the search filter to use when fetching (or querying). It implements all initializers that <code>NSPredicate</code> does (except for <code>-predicateWithBlock:</code>, which Core Data does not support):</p>
|
||
<pre class="highlight swift"><code><span class="k">var</span> <span class="nv">people</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">fetchAll</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(),</span>
|
||
<span class="kt">Where</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(</span><span class="s">"%K > %d"</span><span class="p">,</span> <span class="s">"age"</span><span class="p">,</span> <span class="mi">30</span><span class="p">)</span> <span class="c1">// string format initializer</span>
|
||
<span class="p">)</span>
|
||
<span class="n">people</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">fetchAll</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(),</span>
|
||
<span class="kt">Where</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(</span><span class="kc">true</span><span class="p">)</span> <span class="c1">// boolean initializer</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>If you do have an existing <code>NSPredicate</code> instance already, you can pass that to <code><a href="Structs/Where.html">Where</a></code> as well:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">predicate</span> <span class="o">=</span> <span class="kt">NSPredicate</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
|
||
<span class="k">var</span> <span class="nv">people</span> <span class="o">=</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">fetchAll</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(),</span>
|
||
<span class="kt">Where</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(</span><span class="n">predicate</span><span class="p">)</span> <span class="c1">// predicate initializer</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p><code><a href="Structs/Where.html">Where</a></code> clauses are generic types. To avoid verbose repetition of the generic object type, fetch methods support <strong>Fetch Chain builders</strong>. We can also use Swift’s Smart KeyPaths as the <code><a href="Structs/Where.html">Where</a></code> clause expression:</p>
|
||
<pre class="highlight swift"><code><span class="k">var</span> <span class="nv">people</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">fetchAll</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">()</span>
|
||
<span class="o">.</span><span class="nf">where</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">age</span> <span class="o">></span> <span class="mi">30</span><span class="p">)</span> <span class="c1">// Type-safe!</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p><code><a href="Structs/Where.html">Where</a></code> clauses also implement the <code>&&</code>, <code>||</code>, and <code>!</code> logic operators, so you can provide logical conditions without writing too much <code>AND</code>, <code>OR</code>, and <code>NOT</code> strings:</p>
|
||
<pre class="highlight swift"><code><span class="k">var</span> <span class="nv">people</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">fetchAll</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">()</span>
|
||
<span class="o">.</span><span class="nf">where</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">age</span> <span class="o">></span> <span class="mi">30</span> <span class="o">&&</span> <span class="err">\</span><span class="o">.</span><span class="n">gender</span> <span class="o">==</span> <span class="s">"M"</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>If you do not provide a <code><a href="Structs/Where.html">Where</a></code> clause, all objects that belong to the specified <code><a href="Structs/From.html">From</a></code> will be returned.</p>
|
||
<h4 id='code-orderby-code-clause' class='heading'><code><a href="Structs/OrderBy.html">OrderBy</a></code> clause</h4>
|
||
|
||
<p>The <code><a href="Structs/OrderBy.html">OrderBy</a></code> clause is CoreStore’s <code>NSSortDescriptor</code> wrapper. Use it to specify attribute keys in which to sort the fetch (or query) results with.</p>
|
||
<pre class="highlight swift"><code><span class="k">var</span> <span class="nv">mostValuablePeople</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">fetchAll</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(),</span>
|
||
<span class="kt">OrderBy</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(</span><span class="o">.</span><span class="nf">descending</span><span class="p">(</span><span class="s">"rating"</span><span class="p">),</span> <span class="o">.</span><span class="nf">ascending</span><span class="p">(</span><span class="s">"surname"</span><span class="p">))</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>As seen above, <code><a href="Structs/OrderBy.html">OrderBy</a></code> accepts a list of <code>SortKey</code> enumeration values, which can be either <code>.ascending</code> or <code>.descending</code>.
|
||
As with <code><a href="Structs/Where.html">Where</a></code> clauses, <code><a href="Structs/OrderBy.html">OrderBy</a></code> clauses are also generic types. To avoid verbose repetition of the generic object type, fetch methods support <strong>Fetch Chain builders</strong>. We can also use Swift’s Smart KeyPaths as the <code><a href="Structs/OrderBy.html">OrderBy</a></code> clause expression:</p>
|
||
<pre class="highlight swift"><code><span class="k">var</span> <span class="nv">people</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">fetchAll</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">()</span>
|
||
<span class="o">.</span><span class="nf">orderBy</span><span class="p">(</span><span class="o">.</span><span class="nf">descending</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">rating</span><span class="p">),</span> <span class="o">.</span><span class="nf">ascending</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">surname</span><span class="p">))</span> <span class="c1">// Type-safe!</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>You can use the <code>+</code> and <code>+=</code> operator to append <code><a href="Structs/OrderBy.html">OrderBy</a></code>s together. This is useful when sorting conditionally:</p>
|
||
<pre class="highlight swift"><code><span class="k">var</span> <span class="nv">orderBy</span> <span class="o">=</span> <span class="kt">OrderBy</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(</span><span class="o">.</span><span class="nf">descending</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">rating</span><span class="p">))</span>
|
||
<span class="k">if</span> <span class="n">sortFromYoungest</span> <span class="p">{</span>
|
||
<span class="n">orderBy</span> <span class="o">+=</span> <span class="kt">OrderBy</span><span class="p">(</span><span class="o">.</span><span class="nf">ascending</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">age</span><span class="p">))</span>
|
||
<span class="p">}</span>
|
||
<span class="k">var</span> <span class="nv">mostValuablePeople</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">fetchAll</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(),</span>
|
||
<span class="n">orderBy</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
<h4 id='code-tweak-code-clause' class='heading'><code><a href="Structs/Tweak.html">Tweak</a></code> clause</h4>
|
||
|
||
<p>The <code><a href="Structs/Tweak.html">Tweak</a></code> clause lets you, uh, <em>tweak</em> the fetch (or query). <code><a href="Structs/Tweak.html">Tweak</a></code> exposes the <code>NSFetchRequest</code> in a closure where you can make changes to its properties:</p>
|
||
<pre class="highlight swift"><code><span class="k">var</span> <span class="nv">people</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">fetchAll</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(),</span>
|
||
<span class="kt">Where</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(</span><span class="s">"age > %d"</span><span class="p">,</span> <span class="mi">30</span><span class="p">),</span>
|
||
<span class="kt">OrderBy</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(</span><span class="o">.</span><span class="nf">ascending</span><span class="p">(</span><span class="s">"surname"</span><span class="p">)),</span>
|
||
<span class="kt">Tweak</span> <span class="p">{</span> <span class="p">(</span><span class="n">fetchRequest</span><span class="p">)</span> <span class="o">-></span> <span class="kt">Void</span> <span class="k">in</span>
|
||
<span class="n">fetchRequest</span><span class="o">.</span><span class="n">includesPendingChanges</span> <span class="o">=</span> <span class="kc">false</span>
|
||
<span class="n">fetchRequest</span><span class="o">.</span><span class="n">returnsObjectsAsFaults</span> <span class="o">=</span> <span class="kc">false</span>
|
||
<span class="n">fetchRequest</span><span class="o">.</span><span class="n">includesSubentities</span> <span class="o">=</span> <span class="kc">false</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p><code><a href="Structs/Tweak.html">Tweak</a></code> also supports <strong>Fetch Chain builders</strong>:</p>
|
||
<pre class="highlight swift"><code><span class="k">var</span> <span class="nv">people</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">fetchAll</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(),</span>
|
||
<span class="o">.</span><span class="nf">where</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">age</span> <span class="o">></span> <span class="mi">30</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="nf">orderBy</span><span class="p">(</span><span class="o">.</span><span class="nf">ascending</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">surname</span><span class="p">))</span>
|
||
<span class="o">.</span><span class="n">tweak</span> <span class="p">{</span>
|
||
<span class="nv">$0</span><span class="o">.</span><span class="n">includesPendingChanges</span> <span class="o">=</span> <span class="kc">false</span>
|
||
<span class="nv">$0</span><span class="o">.</span><span class="n">returnsObjectsAsFaults</span> <span class="o">=</span> <span class="kc">false</span>
|
||
<span class="nv">$0</span><span class="o">.</span><span class="n">includesSubentities</span> <span class="o">=</span> <span class="kc">false</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>The clauses are evaluated the order they appear in the fetch/query, so you typically need to set <code><a href="Structs/Tweak.html">Tweak</a></code> as the last clause.
|
||
<code><a href="Structs/Tweak.html">Tweak</a></code>‘s closure is executed only just before the fetch occurs, so make sure that any values captured by the closure is not prone to race conditions.</p>
|
||
|
||
<p>While <code><a href="Structs/Tweak.html">Tweak</a></code> lets you micro-configure the <code>NSFetchRequest</code>, note that CoreStore already preconfigured that <code>NSFetchRequest</code> to suitable defaults. Only use <code><a href="Structs/Tweak.html">Tweak</a></code> when you know what you are doing!</p>
|
||
<h3 id='querying' class='heading'>Querying</h3>
|
||
|
||
<p>One of the functionalities overlooked by other Core Data wrapper libraries is raw properties fetching. If you are familiar with <code>NSDictionaryResultType</code> and <code>-[NSFetchedRequest propertiesToFetch]</code>, you probably know how painful it is to setup a query for raw values and aggregate values. CoreStore makes this easy by exposing the 2 methods below:</p>
|
||
|
||
<ul>
|
||
<li><code>queryValue(...)</code> - returns a single raw value for an attribute or for an aggregate value. If there are multiple results, <code>queryValue(...)</code> only returns the first item.</li>
|
||
<li><code>queryAttributes(...)</code> - returns an array of dictionaries containing attribute keys with their corresponding values.</li>
|
||
</ul>
|
||
|
||
<p>Both methods above accept the same parameters: a required <code><a href="Structs/From.html">From</a></code> clause, a required <code><a href="Structs/Select.html">Select<T></a></code> clause, and an optional series of <code><a href="Structs/Where.html">Where</a></code>, <code><a href="Structs/OrderBy.html">OrderBy</a></code>, <code><a href="Structs/GroupBy.html">GroupBy</a></code>, and/or <code><a href="Structs/Tweak.html">Tweak</a></code> clauses.</p>
|
||
|
||
<p>Setting up the <code><a href="Structs/From.html">From</a></code>, <code><a href="Structs/Where.html">Where</a></code>, <code><a href="Structs/OrderBy.html">OrderBy</a></code>, and <code><a href="Structs/Tweak.html">Tweak</a></code> clauses is similar to how you would when fetching. For querying, you also need to know how to use the <code><a href="Structs/Select.html">Select<T></a></code> and <code><a href="Structs/GroupBy.html">GroupBy</a></code> clauses.</p>
|
||
<h4 id='code-select-lt-t-gt-code-clause' class='heading'><code><a href="Structs/Select.html">Select<T></a></code> clause</h4>
|
||
|
||
<p>The <code><a href="Structs/Select.html">Select<T></a></code> clause specifies the target attribute/aggregate key, as well as the expected return type: </p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">johnsAge</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">queryValue</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(),</span>
|
||
<span class="kt">Select</span><span class="o"><</span><span class="kt">Int</span><span class="o">></span><span class="p">(</span><span class="s">"age"</span><span class="p">),</span>
|
||
<span class="kt">Where</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(</span><span class="s">"name == %@"</span><span class="p">,</span> <span class="s">"John Smith"</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>The example above queries the “age” property for the first object that matches the <code><a href="Structs/Where.html">Where</a></code> condition. <code>johnsAge</code> will be bound to type <code>Int?</code>, as indicated by the <code><a href="Structs/Select.html">Select<Int></a></code> generic type. For <code>queryValue(...)</code>, types that conform to <code><a href="Protocols/QueryableAttributeType.html">QueryableAttributeType</a></code> are allowed as the return type (and therefore as the generic type for <code><a href="Structs/Select.html">Select<T></a></code>).</p>
|
||
|
||
<p>For <code>queryAttributes(...)</code>, only <code>NSDictionary</code> is valid for <code><a href="Structs/Select.html">Select</a></code>, thus you are allowed to omit the generic type:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">allAges</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">queryAttributes</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(),</span>
|
||
<span class="kt">Select</span><span class="p">(</span><span class="s">"age"</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>query methods also support <strong>Query Chain builders</strong>. We can also use Swift’s Smart KeyPaths to use in the expressions:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">johnsAge</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">queryValue</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">()</span>
|
||
<span class="o">.</span><span class="nf">select</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">age</span><span class="p">)</span> <span class="c1">// binds the result to Int</span>
|
||
<span class="o">.</span><span class="nf">where</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">name</span> <span class="o">==</span> <span class="s">"John Smith"</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>If you only need a value for a particular attribute, you can just specify the key name (like we did with <code>Select<Int>("age")</code>), but several aggregate functions can also be used as parameter to <code><a href="Structs/Select.html">Select</a></code>:</p>
|
||
|
||
<ul>
|
||
<li><code>.average(...)</code></li>
|
||
<li><code>.count(...)</code></li>
|
||
<li><code>.maximum(...)</code></li>
|
||
<li><code>.minimum(...)</code></li>
|
||
<li><code>.sum(...)</code></li>
|
||
</ul>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">oldestAge</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">queryValue</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(),</span>
|
||
<span class="kt">Select</span><span class="o"><</span><span class="kt">Int</span><span class="o">></span><span class="p">(</span><span class="o">.</span><span class="nf">maximum</span><span class="p">(</span><span class="s">"age"</span><span class="p">))</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>For <code>queryAttributes(...)</code> which returns an array of dictionaries, you can specify multiple attributes/aggregates to <code><a href="Structs/Select.html">Select</a></code>:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">personJSON</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">queryAttributes</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(),</span>
|
||
<span class="kt">Select</span><span class="p">(</span><span class="s">"name"</span><span class="p">,</span> <span class="s">"age"</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p><code>personJSON</code> will then have the value:</p>
|
||
<pre class="highlight swift"><code><span class="p">[</span>
|
||
<span class="p">[</span>
|
||
<span class="s">"name"</span><span class="p">:</span> <span class="s">"John Smith"</span><span class="p">,</span>
|
||
<span class="s">"age"</span><span class="p">:</span> <span class="mi">30</span>
|
||
<span class="p">],</span>
|
||
<span class="p">[</span>
|
||
<span class="s">"name"</span><span class="p">:</span> <span class="s">"Jane Doe"</span><span class="p">,</span>
|
||
<span class="s">"age"</span><span class="p">:</span> <span class="mi">22</span>
|
||
<span class="p">]</span>
|
||
<span class="p">]</span>
|
||
</code></pre>
|
||
|
||
<p>You can also include an aggregate as well:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">personJSON</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">queryAttributes</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(),</span>
|
||
<span class="kt">Select</span><span class="p">(</span><span class="s">"name"</span><span class="p">,</span> <span class="o">.</span><span class="nf">count</span><span class="p">(</span><span class="s">"friends"</span><span class="p">))</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>which returns:</p>
|
||
<pre class="highlight swift"><code><span class="p">[</span>
|
||
<span class="p">[</span>
|
||
<span class="s">"name"</span><span class="p">:</span> <span class="s">"John Smith"</span><span class="p">,</span>
|
||
<span class="s">"count(friends)"</span><span class="p">:</span> <span class="mi">42</span>
|
||
<span class="p">],</span>
|
||
<span class="p">[</span>
|
||
<span class="s">"name"</span><span class="p">:</span> <span class="s">"Jane Doe"</span><span class="p">,</span>
|
||
<span class="s">"count(friends)"</span><span class="p">:</span> <span class="mi">231</span>
|
||
<span class="p">]</span>
|
||
<span class="p">]</span>
|
||
</code></pre>
|
||
|
||
<p>The <code>"count(friends)"</code> key name was automatically used by CoreStore, but you can specify your own key alias if you need:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">personJSON</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">queryAttributes</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(),</span>
|
||
<span class="kt">Select</span><span class="p">(</span><span class="s">"name"</span><span class="p">,</span> <span class="o">.</span><span class="nf">count</span><span class="p">(</span><span class="s">"friends"</span><span class="p">,</span> <span class="nv">as</span><span class="p">:</span> <span class="s">"friendsCount"</span><span class="p">))</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>which now returns:</p>
|
||
<pre class="highlight swift"><code><span class="p">[</span>
|
||
<span class="p">[</span>
|
||
<span class="s">"name"</span><span class="p">:</span> <span class="s">"John Smith"</span><span class="p">,</span>
|
||
<span class="s">"friendsCount"</span><span class="p">:</span> <span class="mi">42</span>
|
||
<span class="p">],</span>
|
||
<span class="p">[</span>
|
||
<span class="s">"name"</span><span class="p">:</span> <span class="s">"Jane Doe"</span><span class="p">,</span>
|
||
<span class="s">"friendsCount"</span><span class="p">:</span> <span class="mi">231</span>
|
||
<span class="p">]</span>
|
||
<span class="p">]</span>
|
||
</code></pre>
|
||
<h4 id='code-groupby-code-clause' class='heading'><code><a href="Structs/GroupBy.html">GroupBy</a></code> clause</h4>
|
||
|
||
<p>The <code><a href="Structs/GroupBy.html">GroupBy</a></code> clause lets you group results by a specified attribute/aggregate. This is useful only for <code>queryAttributes(...)</code> since <code>queryValue(...)</code> just returns the first value.</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">personJSON</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">queryAttributes</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">(),</span>
|
||
<span class="kt">Select</span><span class="p">(</span><span class="s">"age"</span><span class="p">,</span> <span class="o">.</span><span class="nf">count</span><span class="p">(</span><span class="s">"age"</span><span class="p">,</span> <span class="nv">as</span><span class="p">:</span> <span class="s">"count"</span><span class="p">)),</span>
|
||
<span class="kt">GroupBy</span><span class="p">(</span><span class="s">"age"</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p><code><a href="Structs/GroupBy.html">GroupBy</a></code> clauses are also generic types and support <strong>Query Chain builders</strong>. We can also use Swift’s Smart KeyPaths to use in the expressions:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">personJSON</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">queryAttributes</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">()</span>
|
||
<span class="o">.</span><span class="nf">select</span><span class="p">(</span><span class="o">.</span><span class="nf">attribute</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">age</span><span class="p">),</span> <span class="o">.</span><span class="nf">count</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">age</span><span class="p">,</span> <span class="nv">as</span><span class="p">:</span> <span class="s">"count"</span><span class="p">))</span>
|
||
<span class="o">.</span><span class="nf">groupBy</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">age</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>this returns dictionaries that shows the count for each <code>"age"</code>:</p>
|
||
<pre class="highlight swift"><code><span class="p">[</span>
|
||
<span class="p">[</span>
|
||
<span class="s">"age"</span><span class="p">:</span> <span class="mi">42</span><span class="p">,</span>
|
||
<span class="s">"count"</span><span class="p">:</span> <span class="mi">1</span>
|
||
<span class="p">],</span>
|
||
<span class="p">[</span>
|
||
<span class="s">"age"</span><span class="p">:</span> <span class="mi">22</span><span class="p">,</span>
|
||
<span class="s">"count"</span><span class="p">:</span> <span class="mi">1</span>
|
||
<span class="p">]</span>
|
||
<span class="p">]</span>
|
||
</code></pre>
|
||
<h2 id='logging-and-error-reporting' class='heading'>Logging and error reporting</h2>
|
||
|
||
<p>One unfortunate thing when using some third-party libraries is that they usually pollute the console with their own logging mechanisms. CoreStore provides its own default logging class, but you can plug-in your own favorite logger by implementing the <code><a href="Protocols/CoreStoreLogger.html">CoreStoreLogger</a></code> protocol.</p>
|
||
<pre class="highlight swift"><code><span class="kd">public</span> <span class="kd">protocol</span> <span class="kt">CoreStoreLogger</span> <span class="p">{</span>
|
||
<span class="kd">func</span> <span class="nf">log</span><span class="p">(</span><span class="n">level</span> <span class="nv">level</span><span class="p">:</span> <span class="kt">LogLevel</span><span class="p">,</span> <span class="nv">message</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">fileName</span><span class="p">:</span> <span class="kt">StaticString</span><span class="p">,</span> <span class="nv">lineNumber</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">functionName</span><span class="p">:</span> <span class="kt">StaticString</span><span class="p">)</span>
|
||
<span class="kd">func</span> <span class="nf">log</span><span class="p">(</span><span class="n">error</span> <span class="nv">error</span><span class="p">:</span> <span class="kt">CoreStoreError</span><span class="p">,</span> <span class="nv">message</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">fileName</span><span class="p">:</span> <span class="kt">StaticString</span><span class="p">,</span> <span class="nv">lineNumber</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">functionName</span><span class="p">:</span> <span class="kt">StaticString</span><span class="p">)</span>
|
||
<span class="kd">func</span> <span class="nf">assert</span><span class="p">(</span><span class="kd">@autoclosure</span> <span class="nv">condition</span><span class="p">:</span> <span class="p">()</span> <span class="o">-></span> <span class="kt">Bool</span><span class="p">,</span> <span class="kd">@autoclosure</span> <span class="nv">message</span><span class="p">:</span> <span class="p">()</span> <span class="o">-></span> <span class="kt">String</span><span class="p">,</span> <span class="nv">fileName</span><span class="p">:</span> <span class="kt">StaticString</span><span class="p">,</span> <span class="nv">lineNumber</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">functionName</span><span class="p">:</span> <span class="kt">StaticString</span><span class="p">)</span>
|
||
<span class="kd">func</span> <span class="nf">abort</span><span class="p">(</span><span class="nv">message</span><span class="p">:</span> <span class="kt">String</span><span class="p">,</span> <span class="nv">fileName</span><span class="p">:</span> <span class="kt">StaticString</span><span class="p">,</span> <span class="nv">lineNumber</span><span class="p">:</span> <span class="kt">Int</span><span class="p">,</span> <span class="nv">functionName</span><span class="p">:</span> <span class="kt">StaticString</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>Implement this protocol with your custom class then pass the instance to <code><a href="Enums/CoreStoreDefaults.html#/s:9CoreStore0aB8DefaultsO6loggerAA0aB6Logger_pvpZ">CoreStoreDefaults.logger</a></code>:</p>
|
||
<pre class="highlight swift"><code><span class="kt">CoreStoreDefaults</span><span class="o">.</span><span class="n">logger</span> <span class="o">=</span> <span class="kt">MyLogger</span><span class="p">()</span>
|
||
</code></pre>
|
||
|
||
<p>Doing so channels all logging calls to your logger.</p>
|
||
|
||
<p>Note that to keep the call stack information intact, all calls to these methods are <strong>NOT</strong> thread-managed. Therefore you have to make sure that your logger is thread-safe or you may otherwise have to dispatch your logging implementation to a serial queue.</p>
|
||
|
||
<p>Take special care when implementing <code><a href="Protocols/CoreStoreLogger.html">CoreStoreLogger</a></code>’s <code>assert(...)</code> and <code>abort(...)</code> functions:</p>
|
||
|
||
<ul>
|
||
<li><code>assert(...)</code>: The behavior between <code>DEBUG</code> and release builds, or <code>-O</code> and <code>-Onone</code>, are all left to the implementers’ responsibility. CoreStore calls <code><a href="Protocols/CoreStoreLogger.html#/s:9CoreStore0aB6LoggerP6assert_7message8fileName10lineNumber08functionG0ySbyXK_SSyXKs12StaticStringVSiAJtF">CoreStoreLogger.assert(...)</a></code> only for invalid but usually recoverable errors (for example, early validation failures that may cause an error thrown and handled somewhere else)</li>
|
||
<li><code>abort(...)</code>: This method is <em>the</em> last-chance for your app to <em>synchronously</em> log a fatal error within CoreStore. The app will be terminated right after this function is called (CoreStore calls <code>fatalError()</code> internally)</li>
|
||
</ul>
|
||
|
||
<p>All CoreStore types have very useful (and pretty formatted!) <code>print(...)</code> outputs.
|
||
A couple of examples, <code><a href="Classes/ListMonitor.html">ListMonitor</a></code>:</p>
|
||
|
||
<p><img width="369" alt="screen shot 2016-07-10 at 22 56 44" src="https://cloud.githubusercontent.com/assets/3029684/16713994/ae06e702-46f1-11e6-83a8-dee48b480bab.png" /></p>
|
||
|
||
<p><code>CoreStoreError.mappingModelNotFoundError</code>:</p>
|
||
|
||
<p><img width="506" alt="MappingModelNotFoundError" src="https://cloud.githubusercontent.com/assets/3029684/16713962/e021f548-46f0-11e6-8100-f9b5ea6b4a08.png" /></p>
|
||
|
||
<p>These are all implemented with <code>CustomDebugStringConvertible.debugDescription</code>, so they work with lldb’s <code>po</code> command as well.</p>
|
||
<h2 id='observing-changes-and-notifications' class='heading'>Observing changes and notifications</h2>
|
||
|
||
<p>CoreStore provides type-safe wrappers for observing managed objects:</p>
|
||
|
||
<table><thead>
|
||
<tr>
|
||
<th></th>
|
||
<th>🆕<a href="#observe-a-single-objects-updates"><em>ObjectPublisher</em></a></th>
|
||
<th><a href="#observe-a-single-objects-per-property-updates"><em>ObjectMonitor</em></a></th>
|
||
<th>🆕<a href="#observe-a-diffable-list"><em>ListPublisher</em></a></th>
|
||
<th><a href="#observe-detailed-list-changes"><em>ListMonitor</em></a></th>
|
||
</tr>
|
||
</thead><tbody>
|
||
<tr>
|
||
<td><em>Number of objects</em></td>
|
||
<td>1</td>
|
||
<td>1</td>
|
||
<td>N</td>
|
||
<td>N</td>
|
||
</tr>
|
||
<tr>
|
||
<td><em>Allows multiple observers</em></td>
|
||
<td>✅</td>
|
||
<td>✅</td>
|
||
<td>✅</td>
|
||
<td>✅</td>
|
||
</tr>
|
||
<tr>
|
||
<td><em>Emits fine-grained changes</em></td>
|
||
<td>❌</td>
|
||
<td>✅</td>
|
||
<td>❌</td>
|
||
<td>✅</td>
|
||
</tr>
|
||
<tr>
|
||
<td><em>Emits DiffableDataSource snapshots</em></td>
|
||
<td>✅</td>
|
||
<td>❌</td>
|
||
<td>✅</td>
|
||
<td>❌</td>
|
||
</tr>
|
||
<tr>
|
||
<td><em>Delegate methods</em></td>
|
||
<td>❌</td>
|
||
<td>✅</td>
|
||
<td>❌</td>
|
||
<td>✅</td>
|
||
</tr>
|
||
<tr>
|
||
<td><em>Closure callback</em></td>
|
||
<td>✅</td>
|
||
<td>❌</td>
|
||
<td>✅</td>
|
||
<td>❌</td>
|
||
</tr>
|
||
<tr>
|
||
<td><em>SwiftUI support</em></td>
|
||
<td>✅</td>
|
||
<td>❌</td>
|
||
<td>✅</td>
|
||
<td>❌</td>
|
||
</tr>
|
||
</tbody></table>
|
||
<h3 id='observe-a-single-property' class='heading'>Observe a single property</h3>
|
||
|
||
<p>To get notifications for single property changes in an object, there are two methods depending on the object’s base class.</p>
|
||
|
||
<ul>
|
||
<li><p>For <code>NSManagedObject</code> subclasses: Use the standard KVO method:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">observer</span> <span class="o">=</span> <span class="n">person</span><span class="o">.</span><span class="nf">observe</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">age</span><span class="p">,</span> <span class="nv">options</span><span class="p">:</span> <span class="p">[</span><span class="o">.</span><span class="k">new</span><span class="p">])</span> <span class="p">{</span> <span class="p">(</span><span class="n">person</span><span class="p">,</span> <span class="n">change</span><span class="p">)</span>
|
||
<span class="nf">print</span><span class="p">(</span><span class="s">"Happy </span><span class="se">\(</span><span class="n">change</span><span class="o">.</span><span class="n">newValue</span><span class="se">)</span><span class="s">th birthday!"</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
</code></pre></li>
|
||
<li><p>For <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code> subclasses: Call the <code>observe(...)</code> method directly on the property. You’ll notice that the API itself is a bit similar to the KVO method:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">observer</span> <span class="o">=</span> <span class="n">person</span><span class="o">.</span><span class="n">age</span><span class="o">.</span><span class="nf">observe</span><span class="p">(</span><span class="nv">options</span><span class="p">:</span> <span class="p">[</span><span class="o">.</span><span class="k">new</span><span class="p">])</span> <span class="p">{</span> <span class="p">(</span><span class="n">person</span><span class="p">,</span> <span class="n">change</span><span class="p">)</span>
|
||
<span class="nf">print</span><span class="p">(</span><span class="s">"Happy </span><span class="se">\(</span><span class="n">change</span><span class="o">.</span><span class="n">newValue</span><span class="se">)</span><span class="s">th birthday!"</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
</code></pre></li>
|
||
</ul>
|
||
|
||
<p>For both methods, you will need to keep a reference to the returned <code>observer</code> for the duration of the observation. </p>
|
||
<h3 id='observe-a-single-object-39-s-updates' class='heading'>Observe a single object’s updates</h3>
|
||
|
||
<p>Observers of an <code><a href="Classes/ObjectPublisher.html">ObjectPublisher</a></code> can receive notifications if any of the object’s property changes. You can create an <code><a href="Classes/ObjectPublisher.html">ObjectPublisher</a></code> from the object directly:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">objectPublisher</span><span class="p">:</span> <span class="kt">ObjectPublisher</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span> <span class="o">=</span> <span class="n">person</span><span class="o">.</span><span class="nf">asPublisher</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="n">dataStack</span><span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>or by indexing a <code><a href="Classes/ListPublisher.html">ListPublisher</a></code>‘s <code><a href="Structs/ListSnapshot.html">ListSnapshot</a></code>:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">listPublisher</span><span class="p">:</span> <span class="kt">ListPublisher</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span> <span class="o">=</span> <span class="c1">// ...</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="k">let</span> <span class="nv">objectPublisher</span> <span class="o">=</span> <span class="n">listPublisher</span><span class="o">.</span><span class="n">snapshot</span><span class="p">[</span><span class="n">indexPath</span><span class="p">]</span>
|
||
</code></pre>
|
||
|
||
<p>(See <a href="#observe-a-diffable-list"><code><a href="Classes/ListPublisher.html">ListPublisher</a></code> examples</a> below)</p>
|
||
|
||
<p>To receive notifications, call the <code><a href="Classes/ObjectPublisher.html">ObjectPublisher</a></code>’s <code>addObserve(...)</code> method passing the owner of the callback closure:</p>
|
||
<pre class="highlight swift"><code><span class="n">objectPublisher</span><span class="o">.</span><span class="nf">addObserver</span><span class="p">(</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="p">(</span><span class="n">objectPublisher</span><span class="p">)</span> <span class="k">in</span>
|
||
<span class="k">let</span> <span class="nv">snapshot</span><span class="p">:</span> <span class="kt">ObjectSnapshot</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span> <span class="o">=</span> <span class="n">objectPublisher</span><span class="o">.</span><span class="n">snapshot</span>
|
||
<span class="c1">// handle changes</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>Note that the owner instance will not be retained. You may call <code><a href="Classes/ObjectPublisher.html#/s:9CoreStore15ObjectPublisherC14removeObserveryyqd__Rld__ClF">ObjectPublisher.removeObserver(...)</a></code> explicitly to stop receiving notifications, but the <code><a href="Classes/ObjectPublisher.html">ObjectPublisher</a></code> also discontinues sending events to deallocated observers.</p>
|
||
|
||
<p>The <code><a href="Structs/ObjectSnapshot.html">ObjectSnapshot</a></code> returned from the <code><a href="Classes/ObjectPublisher.html#/s:9CoreStore15ObjectPublisherC8snapshotAA0C8SnapshotVyxGSgvp">ObjectPublisher.snapshot</a></code> property returns a full-copy <code>struct</code> of all properties of the object. This is ideal for managing states as they are thread-safe and are not affected by further changes to the actual object. <code><a href="Classes/ObjectPublisher.html">ObjectPublisher</a></code> automatically updates its <code>snapshot</code> value to the latest state of the object. </p>
|
||
|
||
<p>(A reactive-programming variant of this method is explained in detail in the section on <a href="#objectpublisherreactive"><code><a href="Classes/ObjectPublisher.html">ObjectPublisher</a></code> Combine publishers</a>)</p>
|
||
<h3 id='observe-a-single-object-39-s-per-property-updates' class='heading'>Observe a single object’s per-property updates</h3>
|
||
|
||
<p>If you need to track specifically which properties change in an object, implement the <code><a href="Protocols/ObjectObserver.html">ObjectObserver</a></code> protocol and specify the <code>EntityType</code>:</p>
|
||
<pre class="highlight swift"><code><span class="kd">class</span> <span class="kt">MyViewController</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">,</span> <span class="kt">ObjectObserver</span> <span class="p">{</span>
|
||
<span class="kd">func</span> <span class="nf">objectMonitor</span><span class="p">(</span><span class="nv">monitor</span><span class="p">:</span> <span class="kt">ObjectMonitor</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">,</span> <span class="n">willUpdateObject</span> <span class="nv">object</span><span class="p">:</span> <span class="kt">MyPersonEntity</span><span class="p">)</span> <span class="p">{</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">func</span> <span class="nf">objectMonitor</span><span class="p">(</span><span class="nv">monitor</span><span class="p">:</span> <span class="kt">ObjectMonitor</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">,</span> <span class="n">didUpdateObject</span> <span class="nv">object</span><span class="p">:</span> <span class="kt">MyPersonEntity</span><span class="p">,</span> <span class="nv">changedPersistentKeys</span><span class="p">:</span> <span class="kt">Set</span><span class="o"><</span><span class="kt">KeyPathString</span><span class="o">></span><span class="p">)</span> <span class="p">{</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">func</span> <span class="nf">objectMonitor</span><span class="p">(</span><span class="nv">monitor</span><span class="p">:</span> <span class="kt">ObjectMonitor</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">,</span> <span class="n">didDeleteObject</span> <span class="nv">object</span><span class="p">:</span> <span class="kt">MyPersonEntity</span><span class="p">)</span> <span class="p">{</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>We then need to keep an <code><a href="Classes/ObjectMonitor.html">ObjectMonitor</a></code> instance and register our <code><a href="Protocols/ObjectObserver.html">ObjectObserver</a></code> as an observer:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">person</span><span class="p">:</span> <span class="kt">MyPersonEntity</span> <span class="o">=</span> <span class="c1">// ...</span>
|
||
<span class="k">self</span><span class="o">.</span><span class="n">monitor</span> <span class="o">=</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">monitorObject</span><span class="p">(</span><span class="n">person</span><span class="p">)</span>
|
||
<span class="k">self</span><span class="o">.</span><span class="n">monitor</span><span class="o">.</span><span class="nf">addObserver</span><span class="p">(</span><span class="k">self</span><span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>The controller will then notify our observer whenever the object’s attributes change. You can add multiple <code><a href="Protocols/ObjectObserver.html">ObjectObserver</a></code>s to a single <code><a href="Classes/ObjectMonitor.html">ObjectMonitor</a></code> without any problem. This means you can just share around the <code><a href="Classes/ObjectMonitor.html">ObjectMonitor</a></code> instance to different screens without problem.</p>
|
||
|
||
<p>You can get <code><a href="Classes/ObjectMonitor.html">ObjectMonitor</a></code>’s object through its <code>object</code> property. If the object is deleted, the <code>object</code> property will become <code>nil</code> to prevent further access. </p>
|
||
|
||
<p>While <code><a href="Classes/ObjectMonitor.html">ObjectMonitor</a></code> exposes <code>removeObserver(...)</code> as well, it only stores <code>weak</code> references of the observers and will safely unregister deallocated observers. </p>
|
||
<h3 id='observe-a-diffable-list' class='heading'>Observe a diffable list</h3>
|
||
|
||
<p>Observers of a <code><a href="Classes/ListPublisher.html">ListPublisher</a></code> can receive notifications whenever its fetched result set changes. You can create a <code><a href="Classes/ListPublisher.html">ListPublisher</a></code> by fetching from the <code><a href="Classes/DataStack.html">DataStack</a></code>:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">listPublisher</span> <span class="o">=</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">listPublisher</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span><span class="p">()</span>
|
||
<span class="o">.</span><span class="nf">sectionBy</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">age</span><span class="s">") { "</span><span class="kt">Age</span> <span class="err">\</span><span class="p">(</span><span class="nv">$0</span><span class="p">)</span><span class="s">" } // sections are optional
|
||
.where(</span><span class="err">\</span><span class="s">.title == "</span><span class="kt">Engineer</span><span class="s">")
|
||
.orderBy(.ascending(</span><span class="err">\</span><span class="s">.lastName))
|
||
)
|
||
</span></code></pre>
|
||
|
||
<p>To receive notifications, call the <code><a href="Classes/ListPublisher.html">ListPublisher</a></code>’s <code>addObserve(...)</code> method passing the owner of the callback closure:</p>
|
||
<pre class="highlight swift"><code><span class="n">listPublisher</span><span class="o">.</span><span class="nf">addObserver</span><span class="p">(</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="p">(</span><span class="n">listPublisher</span><span class="p">)</span> <span class="k">in</span>
|
||
<span class="k">let</span> <span class="nv">snapshot</span><span class="p">:</span> <span class="kt">ListSnapshot</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span> <span class="o">=</span> <span class="n">listPublisher</span><span class="o">.</span><span class="n">snapshot</span>
|
||
<span class="c1">// handle changes</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>Note that the owner instance will not be retained. You may call <code><a href="Classes/ListPublisher.html#/s:9CoreStore13ListPublisherC14removeObserveryyqd__Rld__ClF">ListPublisher.removeObserver(...)</a></code> explicitly to stop receiving notifications, but the <code><a href="Classes/ListPublisher.html">ListPublisher</a></code> also discontinues sending events to deallocated observers.</p>
|
||
|
||
<p>The <code><a href="Structs/ListSnapshot.html">ListSnapshot</a></code> returned from the <code><a href="Classes/ListPublisher.html#/s:9CoreStore13ListPublisherC8snapshotAA0C8SnapshotVyxGvp">ListPublisher.snapshot</a></code> property returns a full-copy <code>struct</code> of all sections and <code>NSManagedObject</code> items in the list. This is ideal for managing states as they are thread-safe and are not affected by further changes to the result set. <code><a href="Classes/ListPublisher.html">ListPublisher</a></code> automatically updates its <code>snapshot</code> value to the latest state of the fetch. </p>
|
||
|
||
<p>(A reactive-programming variant of this method is explained in detail in the section on <a href="#listpublisherreactive"><code><a href="Classes/ListPublisher.html">ListPublisher</a></code> Combine publishers</a>)</p>
|
||
|
||
<p>Unlike <code><a href="Classes/ListMonitor.html">ListMonitor</a></code>s (See <a href="#observe-detailed-list-changes"><code><a href="Classes/ListMonitor.html">ListMonitor</a></code> examples</a> below), a <code><a href="Classes/ListPublisher.html">ListPublisher</a></code> does not track detailed inserts, deletes, and moves. In return, a <code><a href="Classes/ListPublisher.html">ListPublisher</a></code> is a lot more lightweight and are designed to work well with <code><a href="Enums/DiffableDataSource/TableViewAdapter.html">DiffableDataSource.TableViewAdapter</a></code>s and <code><a href="Enums/DiffableDataSource/CollectionViewAdapter.html">DiffableDataSource.CollectionViewAdapter</a></code>s:</p>
|
||
<pre class="highlight swift"><code><span class="k">self</span><span class="o">.</span><span class="n">dataSource</span> <span class="o">=</span> <span class="kt">DiffableDataSource</span><span class="o">.</span><span class="kt">CollectionViewAdapter</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span><span class="p">(</span>
|
||
<span class="nv">collectionView</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">collectionView</span><span class="p">,</span>
|
||
<span class="nv">dataStack</span><span class="p">:</span> <span class="kt">CoreStoreDefaults</span><span class="o">.</span><span class="n">dataStack</span><span class="p">,</span>
|
||
<span class="nv">cellProvider</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">collectionView</span><span class="p">,</span> <span class="n">indexPath</span><span class="p">,</span> <span class="n">person</span><span class="p">)</span> <span class="k">in</span>
|
||
<span class="k">let</span> <span class="nv">cell</span> <span class="o">=</span> <span class="n">collectionView</span><span class="o">.</span><span class="nf">dequeueReusableCell</span><span class="p">(</span><span class="nv">withReuseIdentifier</span><span class="p">:</span> <span class="s">"PersonCell"</span><span class="p">)</span> <span class="k">as!</span> <span class="kt">PersonCell</span>
|
||
<span class="n">cell</span><span class="o">.</span><span class="nf">setPerson</span><span class="p">(</span><span class="n">person</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">cell</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
|
||
<span class="c1">// ...</span>
|
||
|
||
<span class="n">listPublisher</span><span class="o">.</span><span class="nf">addObserver</span><span class="p">(</span><span class="k">self</span><span class="p">)</span> <span class="p">{</span> <span class="p">[</span><span class="k">weak</span> <span class="k">self</span><span class="p">]</span> <span class="p">(</span><span class="n">listPublisher</span><span class="p">)</span> <span class="k">in</span>
|
||
<span class="k">self</span><span class="p">?</span><span class="o">.</span><span class="n">dataSource</span><span class="p">?</span><span class="o">.</span><span class="nf">apply</span><span class="p">(</span>
|
||
<span class="n">listPublisher</span><span class="o">.</span><span class="n">snapshot</span><span class="p">,</span> <span class="nv">animatingDifferences</span><span class="p">:</span> <span class="kc">true</span>
|
||
<span class="p">)</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
<h3 id='observe-detailed-list-changes' class='heading'>Observe detailed list changes</h3>
|
||
|
||
<p>If you need to track each object’s inserts, deletes, moves, and updates, implement one of the <code><a href="Protocols/ListObserver.html">ListObserver</a></code> protocols and specify the <code>EntityType</code>:</p>
|
||
<pre class="highlight swift"><code><span class="kd">class</span> <span class="kt">MyViewController</span><span class="p">:</span> <span class="kt">UIViewController</span><span class="p">,</span> <span class="kt">ListObserver</span> <span class="p">{</span>
|
||
<span class="kd">func</span> <span class="nf">listMonitorDidChange</span><span class="p">(</span><span class="nv">monitor</span><span class="p">:</span> <span class="kt">ListMonitor</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">)</span> <span class="p">{</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">func</span> <span class="nf">listMonitorDidRefetch</span><span class="p">(</span><span class="nv">monitor</span><span class="p">:</span> <span class="kt">ListMonitor</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">)</span> <span class="p">{</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>Including <code><a href="Protocols/ListObserver.html">ListObserver</a></code>, there are 3 observer protocols you can implement depending on how detailed you need to handle a change notification:</p>
|
||
|
||
<ul>
|
||
<li><p><code><a href="Protocols/ListObserver.html">ListObserver</a></code>: lets you handle these callback methods:</p>
|
||
<pre class="highlight swift"><code><span class="kd">func</span> <span class="nf">listMonitorWillChange</span><span class="p">(</span><span class="n">_</span> <span class="nv">monitor</span><span class="p">:</span> <span class="kt">ListMonitor</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">)</span>
|
||
<span class="kd">func</span> <span class="nf">listMonitorDidChange</span><span class="p">(</span><span class="n">_</span> <span class="nv">monitor</span><span class="p">:</span> <span class="kt">ListMonitor</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">)</span>
|
||
<span class="kd">func</span> <span class="nf">listMonitorWillRefetch</span><span class="p">(</span><span class="n">_</span> <span class="nv">monitor</span><span class="p">:</span> <span class="kt">ListMonitor</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">)</span>
|
||
<span class="kd">func</span> <span class="nf">listMonitorDidRefetch</span><span class="p">(</span><span class="n">_</span> <span class="nv">monitor</span><span class="p">:</span> <span class="kt">ListMonitor</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p><code>listMonitorDidChange(_:)</code> and <code>listMonitorDidRefetch(_:)</code> implementations are both required. <code>listMonitorDidChange(_:)</code> is called whenever the <code><a href="Classes/ListMonitor.html">ListMonitor</a></code>’s count, order, or filtered objects change. <code>listMonitorDidRefetch(_:)</code> is called when the <code>ListMonitor.refetch()</code> was executed or if the internal persistent store was changed. </p></li>
|
||
<li><p><code><a href="Protocols/ListObjectObserver.html">ListObjectObserver</a></code>: in addition to <code><a href="Protocols/ListObserver.html">ListObserver</a></code> methods, also lets you handle object inserts, updates, and deletes:</p>
|
||
<pre class="highlight swift"><code><span class="kd">func</span> <span class="nf">listMonitor</span><span class="p">(</span><span class="n">_</span> <span class="nv">monitor</span><span class="p">:</span> <span class="kt">ListMonitor</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">,</span> <span class="n">didInsertObject</span> <span class="nv">object</span><span class="p">:</span> <span class="kt">MyPersonEntity</span><span class="p">,</span> <span class="n">toIndexPath</span> <span class="nv">indexPath</span><span class="p">:</span> <span class="kt">IndexPath</span><span class="p">)</span>
|
||
<span class="kd">func</span> <span class="nf">listMonitor</span><span class="p">(</span><span class="n">_</span> <span class="nv">monitor</span><span class="p">:</span> <span class="kt">ListMonitor</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">,</span> <span class="n">didDeleteObject</span> <span class="nv">object</span><span class="p">:</span> <span class="kt">MyPersonEntity</span><span class="p">,</span> <span class="n">fromIndexPath</span> <span class="nv">indexPath</span><span class="p">:</span> <span class="kt">IndexPath</span><span class="p">)</span>
|
||
<span class="kd">func</span> <span class="nf">listMonitor</span><span class="p">(</span><span class="n">_</span> <span class="nv">monitor</span><span class="p">:</span> <span class="kt">ListMonitor</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">,</span> <span class="n">didUpdateObject</span> <span class="nv">object</span><span class="p">:</span> <span class="kt">MyPersonEntity</span><span class="p">,</span> <span class="n">atIndexPath</span> <span class="nv">indexPath</span><span class="p">:</span> <span class="kt">IndexPath</span><span class="p">)</span>
|
||
<span class="kd">func</span> <span class="nf">listMonitor</span><span class="p">(</span><span class="n">_</span> <span class="nv">monitor</span><span class="p">:</span> <span class="kt">ListMonitor</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">,</span> <span class="n">didMoveObject</span> <span class="nv">object</span><span class="p">:</span> <span class="kt">MyPersonEntity</span><span class="p">,</span> <span class="nv">fromIndexPath</span><span class="p">:</span> <span class="kt">IndexPath</span><span class="p">,</span> <span class="nv">toIndexPath</span><span class="p">:</span> <span class="kt">IndexPath</span><span class="p">)</span>
|
||
</code></pre></li>
|
||
<li><p><code><a href="Protocols/ListSectionObserver.html">ListSectionObserver</a></code>: in addition to <code><a href="Protocols/ListObjectObserver.html">ListObjectObserver</a></code> methods, also lets you handle section inserts and deletes:</p>
|
||
<pre class="highlight swift"><code><span class="kd">func</span> <span class="nf">listMonitor</span><span class="p">(</span><span class="n">_</span> <span class="nv">monitor</span><span class="p">:</span> <span class="kt">ListMonitor</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">,</span> <span class="n">didInsertSection</span> <span class="nv">sectionInfo</span><span class="p">:</span> <span class="kt">NSFetchedResultsSectionInfo</span><span class="p">,</span> <span class="n">toSectionIndex</span> <span class="nv">sectionIndex</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span>
|
||
<span class="kd">func</span> <span class="nf">listMonitor</span><span class="p">(</span><span class="n">_</span> <span class="nv">monitor</span><span class="p">:</span> <span class="kt">ListMonitor</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">,</span> <span class="n">didDeleteSection</span> <span class="nv">sectionInfo</span><span class="p">:</span> <span class="kt">NSFetchedResultsSectionInfo</span><span class="p">,</span> <span class="n">fromSectionIndex</span> <span class="nv">sectionIndex</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span>
|
||
</code></pre></li>
|
||
</ul>
|
||
|
||
<p>We then need to create a <code><a href="Classes/ListMonitor.html">ListMonitor</a></code> instance and register our <code><a href="Protocols/ListObserver.html">ListObserver</a></code> as an observer:</p>
|
||
<pre class="highlight swift"><code><span class="k">self</span><span class="o">.</span><span class="n">monitor</span> <span class="o">=</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">monitorList</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">()</span>
|
||
<span class="o">.</span><span class="nf">where</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">age</span> <span class="o">></span> <span class="mi">30</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="nf">orderBy</span><span class="p">(</span><span class="o">.</span><span class="nf">ascending</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">name</span><span class="p">))</span>
|
||
<span class="o">.</span><span class="n">tweak</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="n">fetchBatchSize</span> <span class="o">=</span> <span class="mi">20</span> <span class="p">}</span>
|
||
<span class="p">)</span>
|
||
<span class="k">self</span><span class="o">.</span><span class="n">monitor</span><span class="o">.</span><span class="nf">addObserver</span><span class="p">(</span><span class="k">self</span><span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>Similar to <code><a href="Classes/ObjectMonitor.html">ObjectMonitor</a></code>, a <code><a href="Classes/ListMonitor.html">ListMonitor</a></code> can also have multiple <code><a href="Protocols/ListObserver.html">ListObserver</a></code>s registered to a single <code><a href="Classes/ListMonitor.html">ListMonitor</a></code>.</p>
|
||
|
||
<p>If you have noticed, the <code>monitorList(...)</code> method accepts <code><a href="Structs/Where.html">Where</a></code>, <code><a href="Structs/OrderBy.html">OrderBy</a></code>, and <code><a href="Structs/Tweak.html">Tweak</a></code> clauses exactly like a fetch. As the list maintained by <code><a href="Classes/ListMonitor.html">ListMonitor</a></code> needs to have a deterministic order, at least the <code><a href="Structs/From.html">From</a></code> and <code><a href="Structs/OrderBy.html">OrderBy</a></code> clauses are required.</p>
|
||
|
||
<p>A <code><a href="Classes/ListMonitor.html">ListMonitor</a></code> created from <code>monitorList(...)</code> will maintain a single-section list. You can therefore access its contents with just an index:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">firstPerson</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">monitor</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span>
|
||
</code></pre>
|
||
|
||
<p>If the list needs to be grouped into sections, create the <code><a href="Classes/ListMonitor.html">ListMonitor</a></code> instance with the <code>monitorSectionedList(...)</code> method and a <code><a href="Structs/SectionBy.html">SectionBy</a></code> clause:</p>
|
||
<pre class="highlight swift"><code><span class="k">self</span><span class="o">.</span><span class="n">monitor</span> <span class="o">=</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">monitorSectionedList</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">()</span>
|
||
<span class="o">.</span><span class="nf">sectionBy</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">age</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="nf">where</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">gender</span> <span class="o">==</span> <span class="s">"M"</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="nf">orderBy</span><span class="p">(</span><span class="o">.</span><span class="nf">ascending</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">age</span><span class="p">),</span> <span class="o">.</span><span class="nf">ascending</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">name</span><span class="p">))</span>
|
||
<span class="o">.</span><span class="n">tweak</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="n">fetchBatchSize</span> <span class="o">=</span> <span class="mi">20</span> <span class="p">}</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>A list controller created this way will group the objects by the attribute key indicated by the <code><a href="Structs/SectionBy.html">SectionBy</a></code> clause. One more thing to remember is that the <code><a href="Structs/OrderBy.html">OrderBy</a></code> clause should sort the list in such a way that the <code><a href="Structs/SectionBy.html">SectionBy</a></code> attribute would be sorted together (a requirement shared by <code>NSFetchedResultsController</code>.)</p>
|
||
|
||
<p>The <code><a href="Structs/SectionBy.html">SectionBy</a></code> clause can also be passed a closure to transform the section name into a displayable string:</p>
|
||
<pre class="highlight swift"><code><span class="k">self</span><span class="o">.</span><span class="n">monitor</span> <span class="o">=</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">monitorSectionedList</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">MyPersonEntity</span><span class="o">></span><span class="p">()</span>
|
||
<span class="o">.</span><span class="nf">sectionBy</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">age</span><span class="p">)</span> <span class="p">{</span> <span class="p">(</span><span class="n">sectionName</span><span class="p">)</span> <span class="o">-></span> <span class="kt">String</span><span class="p">?</span> <span class="k">in</span>
|
||
<span class="s">"</span><span class="se">\(</span><span class="n">sectionName</span><span class="se">)</span><span class="s"> years old"</span>
|
||
<span class="p">}</span>
|
||
<span class="o">.</span><span class="nf">orderBy</span><span class="p">(</span><span class="o">.</span><span class="nf">ascending</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">age</span><span class="p">),</span> <span class="o">.</span><span class="nf">ascending</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">name</span><span class="p">))</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>This is useful when implementing a <code>UITableViewDelegate</code>’s section header:</p>
|
||
<pre class="highlight swift"><code><span class="kd">func</span> <span class="nf">tableView</span><span class="p">(</span><span class="n">_</span> <span class="nv">tableView</span><span class="p">:</span> <span class="kt">UITableView</span><span class="p">,</span> <span class="n">titleForHeaderInSection</span> <span class="nv">section</span><span class="p">:</span> <span class="kt">Int</span><span class="p">)</span> <span class="o">-></span> <span class="kt">String</span><span class="p">?</span> <span class="p">{</span>
|
||
<span class="k">let</span> <span class="nv">sectionInfo</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">monitor</span><span class="o">.</span><span class="nf">sectionInfoAtIndex</span><span class="p">(</span><span class="n">section</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">sectionInfo</span><span class="o">.</span><span class="n">name</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>To access the objects of a sectioned list, use an <code>IndexPath</code> or a tuple:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">indexPath</span> <span class="o">=</span> <span class="kt">IndexPath</span><span class="p">(</span><span class="nv">row</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span> <span class="nv">section</span><span class="p">:</span> <span class="mi">1</span><span class="p">)</span>
|
||
<span class="k">let</span> <span class="nv">person1</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">monitor</span><span class="p">[</span><span class="n">indexPath</span><span class="p">]</span>
|
||
<span class="k">let</span> <span class="nv">person2</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">monitor</span><span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">]</span>
|
||
<span class="c1">// person1 and person2 are the same object</span>
|
||
</code></pre>
|
||
<h2 id='type-safe-code-corestoreobject-code-s' class='heading'>Type-safe <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code>s</h2>
|
||
|
||
<p>Starting CoreStore 4.0, we can now create persisted objects without depending on <em>.xcdatamodeld</em> Core Data files. The new <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code> subclass replaces <code>NSManagedObject</code>, and specially-typed properties declared on these classes will be synthesized as Core Data attributes.</p>
|
||
<pre class="highlight swift"><code><span class="kd">class</span> <span class="kt">Animal</span><span class="p">:</span> <span class="kt">CoreStoreObject</span> <span class="p">{</span>
|
||
<span class="kd">@Field</span><span class="o">.</span><span class="kt">Stored</span><span class="p">(</span><span class="s">"species"</span><span class="p">)</span>
|
||
<span class="k">var</span> <span class="nv">species</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="s">""</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">class</span> <span class="kt">Dog</span><span class="p">:</span> <span class="kt">Animal</span> <span class="p">{</span>
|
||
<span class="kd">@Field</span><span class="o">.</span><span class="kt">Stored</span><span class="p">(</span><span class="s">"nickname"</span><span class="p">)</span>
|
||
<span class="k">var</span> <span class="nv">nickname</span><span class="p">:</span> <span class="kt">String</span><span class="p">?</span>
|
||
|
||
<span class="kd">@Field</span><span class="o">.</span><span class="kt">Relationship</span><span class="p">(</span><span class="s">"master"</span><span class="p">)</span>
|
||
<span class="k">var</span> <span class="nv">master</span><span class="p">:</span> <span class="kt">Person</span><span class="p">?</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">class</span> <span class="kt">Person</span><span class="p">:</span> <span class="kt">CoreStoreObject</span> <span class="p">{</span>
|
||
<span class="kd">@Field</span><span class="o">.</span><span class="kt">Stored</span><span class="p">(</span><span class="s">"name"</span><span class="p">)</span>
|
||
<span class="k">var</span> <span class="nv">name</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="s">""</span>
|
||
|
||
<span class="kd">@Field</span><span class="o">.</span><span class="kt">Relationship</span><span class="p">(</span><span class="s">"pets"</span><span class="p">,</span> <span class="nv">inverse</span><span class="p">:</span> <span class="err">\</span><span class="kt">Dog</span><span class="o">.</span><span class="err">$</span><span class="n">master</span><span class="p">)</span>
|
||
<span class="k">var</span> <span class="nv">pets</span><span class="p">:</span> <span class="kt">Set</span><span class="o"><</span><span class="kt">Dog</span><span class="o">></span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>The property names to be saved to Core Data is specified as the <code>keyPath</code> argument. This lets us refactor our Swift code without affecting the underlying database. For example:</p>
|
||
<pre class="highlight swift"><code><span class="kd">class</span> <span class="kt">Person</span><span class="p">:</span> <span class="kt">CoreStoreObject</span> <span class="p">{</span>
|
||
<span class="kd">@Field</span><span class="o">.</span><span class="kt">Stored</span><span class="p">(</span><span class="s">"name"</span><span class="p">)</span>
|
||
<span class="kd">private</span> <span class="k">var</span> <span class="nv">internalName</span><span class="p">:</span> <span class="kt">String</span> <span class="o">=</span> <span class="s">""</span>
|
||
<span class="c1">// note property name is independent of the storage key name</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>Here we used the property name <code>internalName</code> and made it <code>private</code>, but the underlying key-path <code>"name"</code> was unchanged so our model will not trigger a data migration.</p>
|
||
|
||
<p>To tell the <code><a href="Classes/DataStack.html">DataStack</a></code> about these types, add all <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code>s’ entities to a <code><a href="Classes/CoreStoreSchema.html">CoreStoreSchema</a></code>:</p>
|
||
<pre class="highlight swift"><code>
|
||
<span class="kt">CoreStoreDefaults</span><span class="o">.</span><span class="n">dataStack</span> <span class="o">=</span> <span class="kt">DataStack</span><span class="p">(</span>
|
||
<span class="kt">CoreStoreSchema</span><span class="p">(</span>
|
||
<span class="nv">modelVersion</span><span class="p">:</span> <span class="s">"V1"</span><span class="p">,</span>
|
||
<span class="nv">entities</span><span class="p">:</span> <span class="p">[</span>
|
||
<span class="kt">Entity</span><span class="o"><</span><span class="kt">Animal</span><span class="o">></span><span class="p">(</span><span class="s">"Animal"</span><span class="p">,</span> <span class="nv">isAbstract</span><span class="p">:</span> <span class="kc">true</span><span class="p">),</span>
|
||
<span class="kt">Entity</span><span class="o"><</span><span class="kt">Dog</span><span class="o">></span><span class="p">(</span><span class="s">"Dog"</span><span class="p">),</span>
|
||
<span class="kt">Entity</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span><span class="p">(</span><span class="s">"Person"</span><span class="p">)</span>
|
||
<span class="p">]</span>
|
||
<span class="p">)</span>
|
||
<span class="p">)</span>
|
||
<span class="kt">CoreStoreDefaults</span><span class="o">.</span><span class="n">dataStack</span><span class="o">.</span><span class="nf">addStorage</span><span class="p">(</span><span class="cm">/* ... */</span><span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>And that’s all CoreStore needs to build the model; <strong>we don’t need <em>.xcdatamodeld</em> files anymore.</strong></p>
|
||
|
||
<p>In addition, <code>@Field</code> properties can be used to create type-safe key-path strings</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">keyPath</span> <span class="o">=</span> <span class="kt">String</span><span class="p">(</span><span class="nv">keyPath</span><span class="p">:</span> <span class="err">\</span><span class="kt">Dog</span><span class="o">.</span><span class="err">$</span><span class="n">nickname</span><span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>as well as <code><a href="Structs/Where.html">Where</a></code> and <code><a href="Structs/OrderBy.html">OrderBy</a></code> clauses</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">puppies</span> <span class="o">=</span> <span class="k">try</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">fetchAll</span><span class="p">(</span>
|
||
<span class="kt">From</span><span class="o"><</span><span class="kt">Dog</span><span class="o">></span><span class="p">()</span>
|
||
<span class="o">.</span><span class="nf">where</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="err">$</span><span class="n">age</span> <span class="o"><</span> <span class="mi">5</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="nf">orderBy</span><span class="p">(</span><span class="o">.</span><span class="nf">ascending</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="err">$</span><span class="n">age</span><span class="p">))</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>All CoreStore APIs that are usable with <code>NSManagedObject</code>s are also available for <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code>s. These include <code><a href="Classes/ListMonitor.html">ListMonitor</a></code>s, <code><a href="Protocols/ImportableObject.html">ImportableObject</a></code>s, fetching, etc.</p>
|
||
<h3 id='new-code-field-code-property-wrapper-syntax' class='heading'>New <code>@Field</code> Property Wrapper syntax</h3>
|
||
|
||
<blockquote>
|
||
<p>⚠️<strong>Important:</strong> <code>@Field</code> properties are only supported for <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code> subclasses. If you are using <code>NSManagedObject</code>s, you need to keep using <code>@NSManaged</code> for your attributes.</p>
|
||
</blockquote>
|
||
|
||
<p>Starting CoreStore 7.1.0, <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code> properties may be converted to <code>@Field</code> Property Wrappers.</p>
|
||
|
||
<blockquote>
|
||
<p>‼️ Please take note of the warnings below before converting or else the model’s hash might change.</p>
|
||
</blockquote>
|
||
|
||
<p><strong>If conversion is too risky, the current <code>Value.Required</code>, <code>Value.Optional</code>, <code>Transformable.Required</code>, <code>Transformable.Optional</code>, <code>Relationship.ToOne</code>, <code>Relationship.ToManyOrdered</code>, and <code>Relationship.ToManyUnordered</code> will all be supported for while so you can opt to use them as is for now.</strong></p>
|
||
|
||
<blockquote>
|
||
<p>‼️ This cannot be stressed enough, but please make sure to set your schema’s <a href="#versionlocks"><code><a href="Structs/VersionLock.html">VersionLock</a></code></a> before converting!</p>
|
||
</blockquote>
|
||
<h4 id='code-field-stored-code' class='heading'><code>@Field.Stored</code></h4>
|
||
|
||
<p>The <code>@Field.Stored</code> property wrapper is used for persisted value types. This is the replacement for “non-transient” <code>Value.Required</code> and <code>Value.Optional</code> properties.</p>
|
||
|
||
<table>
|
||
<tr><th>Before</th><th><pre lang=swift>@Field.Stored</pre></th></tr>
|
||
<tr>
|
||
<td><pre lang=swift>
|
||
class Person: CoreStoreObject {
|
||
<br />
|
||
let title = Value.Required<String>("title", initial: "Mr.")
|
||
let nickname = Value.Optional<String>("nickname")
|
||
}
|
||
</pre></td>
|
||
<td><pre lang=swift>
|
||
class Person: CoreStoreObject {
|
||
<br />
|
||
@Field.Stored("title")
|
||
var title: String = "Mr."
|
||
<br />
|
||
@Field.Stored("nickname")
|
||
var nickname: String?
|
||
}
|
||
</pre></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<blockquote>
|
||
<p>⚠️ Only <code>Value.Required</code> and <code>Value.Optional</code> that are NOT transient values can be converted to <code>Field.Stored</code>. For transient/computed properties, refer to <a href="#fieldvirtual"><code>@Field.Virtual</code></a> properties in the next section.
|
||
⚠️ When converting, make sure that all parameters, including the default values, are exactly the same or else the model’s hash might change.</p>
|
||
</blockquote>
|
||
<h4 id='code-field-virtual-code' class='heading'><code>@Field.Virtual</code></h4>
|
||
|
||
<p>The <code>@Field.Virtual</code> property wrapper is used for unsaved, computed value types. This is the replacement for “transient” <code>Value.Required</code> and <code>Value.Optional</code> properties.</p>
|
||
|
||
<table>
|
||
<tr><th>Before</th><th><pre lang=swift>@Field.Virtual</pre></th></tr>
|
||
<tr>
|
||
<td><pre lang=swift>
|
||
class Animal: CoreStoreObject {
|
||
<br />
|
||
let speciesPlural = Value.Required<String>(
|
||
"speciesPlural",
|
||
transient: true,
|
||
customGetter: Animal.getSpeciesPlural(_:)
|
||
)
|
||
<br />
|
||
let species = Value.Required<String>("species", initial: "")
|
||
<br />
|
||
static func getSpeciesPlural(_ partialObject: PartialObject<Animal>) -> String? {
|
||
let species = partialObject.value(for: { $0.species })
|
||
return species + "s"
|
||
}
|
||
}
|
||
</pre></td>
|
||
<td><pre lang=swift>
|
||
class Animal: CoreStoreObject {
|
||
<br />
|
||
@Field.Virtual(
|
||
"speciesPlural",
|
||
customGetter: { (object, field) in
|
||
return object.$species.value + "s"
|
||
}
|
||
)
|
||
var speciesPlural: String
|
||
<br />
|
||
@Field.Stored("species")
|
||
var species: String = ""
|
||
}
|
||
</pre></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<blockquote>
|
||
<p>⚠️ Only <code>Value.Required</code> and <code>Value.Optional</code> that ARE transient values can be converted to <code>Field.Virtual</code>. For non-transient properties, refer to <a href="#fieldstored"><code>@Field.Stored</code></a> properties in the previous section.
|
||
⚠️ When converting, make sure that all parameters, including the default values, are exactly the same or else the model’s hash might change.</p>
|
||
</blockquote>
|
||
<h4 id='code-field-coded-code' class='heading'><code>@Field.Coded</code></h4>
|
||
|
||
<p>The <code>@Field.Coded</code> property wrapper is used for binary-codable values. This is the new counterpart, <strong>not replacement</strong>, for <code>Transformable.Required</code> and <code>Transformable.Optional</code> properties. <code>@Field.Coded</code> also supports other encodings such as JSON and custom binary converters.</p>
|
||
|
||
<blockquote>
|
||
<p>‼️ The current <code>Transformable.Required</code> and <code>Transformable.Optional</code> mechanism have no safe one-to-one conversion to <code>@Field.Coded</code>. Please use <code>@Field.Coded</code> only for newly added attributes.</p>
|
||
</blockquote>
|
||
|
||
<table>
|
||
<tr><th>Before</th><th><pre lang=swift>@Field.Coded</pre></th></tr>
|
||
<tr>
|
||
<td><pre lang=swift>
|
||
class Vehicle: CoreStoreObject {
|
||
<br />
|
||
let color = Transformable.Optional<UIColor>("color", initial: .white)
|
||
}
|
||
</pre></td>
|
||
<td><pre lang=swift>
|
||
class Vehicle: CoreStoreObject {
|
||
<br />
|
||
@Field.Coded("color", coder: FieldCoders.NSCoding.self)
|
||
var color: UIColor? = .white
|
||
}
|
||
</pre></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<p>Built-in encoders such as <code><a href="Enums/FieldCoders/NSCoding.html">FieldCoders.NSCoding</a></code>, <code><a href="Enums/FieldCoders/Json.html">FieldCoders.Json</a></code>, and <code><a href="Enums/FieldCoders/Plist.html">FieldCoders.Plist</a></code> are available, and custom encoding/decoding is also supported:</p>
|
||
<pre class="highlight swift"><code><span class="kd">class</span> <span class="kt">Person</span><span class="p">:</span> <span class="kt">CoreStoreObject</span> <span class="p">{</span>
|
||
|
||
<span class="kd">struct</span> <span class="kt">CustomInfo</span><span class="p">:</span> <span class="kt">Codable</span> <span class="p">{</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="kd">@Field</span><span class="o">.</span><span class="kt">Coded</span><span class="p">(</span><span class="s">"otherInfo"</span><span class="p">,</span> <span class="nv">coder</span><span class="p">:</span> <span class="kt">FieldCoders</span><span class="o">.</span><span class="kt">Json</span><span class="o">.</span><span class="k">self</span><span class="p">)</span>
|
||
<span class="k">var</span> <span class="nv">otherInfo</span><span class="p">:</span> <span class="kt">CustomInfo</span><span class="p">?</span>
|
||
|
||
<span class="kd">@Field</span><span class="o">.</span><span class="kt">Coded</span><span class="p">(</span>
|
||
<span class="s">"photo"</span><span class="p">,</span>
|
||
<span class="nv">coder</span><span class="p">:</span> <span class="p">{</span>
|
||
<span class="nv">encode</span><span class="p">:</span> <span class="p">{</span> <span class="nv">$0</span><span class="o">.</span><span class="nf">toData</span><span class="p">()</span> <span class="p">},</span>
|
||
<span class="nv">decode</span><span class="p">:</span> <span class="p">{</span> <span class="kt">Photo</span><span class="p">(</span><span class="nv">fromData</span><span class="p">:</span> <span class="nv">$0</span><span class="p">)</span> <span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
<span class="k">var</span> <span class="nv">photo</span><span class="p">:</span> <span class="kt">Photo</span><span class="p">?</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<blockquote>
|
||
<p>‼️<strong>Important:</strong> Any changes in the encoders/decoders are not reflected in the <code><a href="Structs/VersionLock.html">VersionLock</a></code>, so make sure that the encoder and decoder logic is compatible for all versions of your persistent store.</p>
|
||
</blockquote>
|
||
<h4 id='code-field-relationship-code' class='heading'><code>@Field.Relationship</code></h4>
|
||
|
||
<p>The <code>@Field.Relationship</code> property wrapper is used for link relationships with other <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code>s. This is the replacement for <code>Relationship.ToOne</code>, <code>Relationship.ToManyOrdered</code>, and <code>Relationship.ToManyUnordered</code> properties.</p>
|
||
|
||
<p>The type of relationship is determined by the <code>@Field.Relationship</code> generic type:</p>
|
||
|
||
<ul>
|
||
<li><code>Optional<T></code> : To-one relationship</li>
|
||
<li><code>Array<T></code> : To-many ordered relationship</li>
|
||
<li><code>Set<T></code> : To-many unordered relationship</li>
|
||
</ul>
|
||
|
||
<table>
|
||
<tr><th>Before</th><th><pre lang=swift>@Field.Stored</pre></th></tr>
|
||
<tr>
|
||
<td><pre lang=swift>
|
||
class Pet: CoreStoreObject {
|
||
<br />
|
||
let master = Relationship.ToOne<Person>("master")
|
||
}
|
||
class Person: CoreStoreObject {
|
||
<br />
|
||
let pets: Relationship.ToManyUnordered<Pet>("pets", inverse: \.$master)
|
||
}
|
||
</pre></td>
|
||
<td><pre lang=swift>
|
||
class Pet: CoreStoreObject {
|
||
<br />
|
||
@Field.Relationship("master")
|
||
var master: Person?
|
||
}
|
||
class Person: CoreStoreObject {
|
||
<br />
|
||
@Field.Relationship("pets", inverse: \.$master)
|
||
var pets: Set<Pet>
|
||
}
|
||
</pre></td>
|
||
</tr>
|
||
</table>
|
||
|
||
<blockquote>
|
||
<p>⚠️ When converting, make sure that all parameters, including the default values, are exactly the same or else the model’s hash might change.</p>
|
||
</blockquote>
|
||
|
||
<p>Also note how <code>Relationship</code>s are linked statically with the <code>inverse:</code> argument. <strong>All relationships are required to have an “inverse” relationship</strong>. Unfortunately, due to Swift compiler limitation we can declare the <code>inverse:</code> on only one of the relationship-pair.</p>
|
||
<h4 id='code-field-code-usage-notes' class='heading'><code>@Field</code> usage notes</h4>
|
||
|
||
<p><strong>Accessor syntax</strong></p>
|
||
|
||
<p>When using key-path utilities, properties using <code>@Field</code> property wrappers need to use the <code>$</code> syntax:</p>
|
||
|
||
<ul>
|
||
<li>Before: <code>From<Person>.where(\.title == "Mr.")</code></li>
|
||
<li>After: <code>From<Person>.where(\.$title == "Mr.")</code></li>
|
||
</ul>
|
||
|
||
<p>This applies to property access using <code><a href="Classes/ObjectPublisher.html">ObjectPublisher</a></code>s and <code><a href="Structs/ObjectSnapshot.html">ObjectSnapshot</a></code>s.</p>
|
||
|
||
<ul>
|
||
<li>Before: <code>let name = personSnapshot.name</code></li>
|
||
<li>After: <code>let name = personSnapshot.$name</code></li>
|
||
</ul>
|
||
|
||
<p><strong>Default values vs. Initial values</strong></p>
|
||
|
||
<p>One common mistake when assigning default values to <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code> properties is to assign it a value and expect it to be evaluated whenever an object is created:</p>
|
||
<pre class="highlight swift"><code><span class="c1">// ❌</span>
|
||
<span class="kd">class</span> <span class="kt">Person</span><span class="p">:</span> <span class="kt">CoreStoreObject</span> <span class="p">{</span>
|
||
|
||
<span class="kd">@Field</span><span class="o">.</span><span class="kt">Stored</span><span class="p">(</span><span class="s">"identifier"</span><span class="p">)</span>
|
||
<span class="k">var</span> <span class="nv">identifier</span><span class="p">:</span> <span class="kt">UUID</span> <span class="o">=</span> <span class="kt">UUID</span><span class="p">()</span> <span class="c1">// Wrong!</span>
|
||
|
||
<span class="kd">@Field</span><span class="o">.</span><span class="kt">Stored</span><span class="p">(</span><span class="s">"createdDate"</span><span class="p">)</span>
|
||
<span class="k">var</span> <span class="nv">createdDate</span><span class="p">:</span> <span class="kt">Date</span> <span class="o">=</span> <span class="kt">Date</span><span class="p">()</span> <span class="c1">// Wrong!</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>This default value will be evaluated only when the <code><a href="Classes/DataStack.html">DataStack</a></code> 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 <code>""</code> or <code>0</code>.</p>
|
||
|
||
<p>For actual “initial values”, <code>@Field.Stored</code> and <code>@Field.Coded</code> now supports dynamic evaluation during object creation via the <code>dynamicInitialValue:</code> argument:</p>
|
||
<pre class="highlight swift"><code><span class="c1">// ✅</span>
|
||
<span class="kd">class</span> <span class="kt">Person</span><span class="p">:</span> <span class="kt">CoreStoreObject</span> <span class="p">{</span>
|
||
|
||
<span class="kd">@Field</span><span class="o">.</span><span class="kt">Stored</span><span class="p">(</span><span class="s">"identifier"</span><span class="p">,</span> <span class="nv">dynamicInitialValue</span><span class="p">:</span> <span class="p">{</span> <span class="kt">UUID</span><span class="p">()</span> <span class="p">})</span>
|
||
<span class="k">var</span> <span class="nv">identifier</span><span class="p">:</span> <span class="kt">UUID</span>
|
||
|
||
<span class="kd">@Field</span><span class="o">.</span><span class="kt">Stored</span><span class="p">(</span><span class="s">"createdDate"</span><span class="p">,</span> <span class="nv">dynamicInitialValue</span><span class="p">:</span> <span class="p">{</span> <span class="kt">Date</span><span class="p">()</span> <span class="p">})</span>
|
||
<span class="k">var</span> <span class="nv">createdDate</span><span class="p">:</span> <span class="kt">Date</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>When using this feature, a “default value” should not be assigned (i.e. no <code>=</code> expression). </p>
|
||
<h3 id='code-versionlock-code-s' class='heading'><code><a href="Structs/VersionLock.html">VersionLock</a></code>s</h3>
|
||
|
||
<p>While it is convenient to be able to declare entities only in code, it is worrying that we might accidentally change the <code><a href="Classes/CoreStoreObject.html">CoreStoreObject</a></code>‘s properties and break our users’ model version history. For this, the <code><a href="Classes/CoreStoreSchema.html">CoreStoreSchema</a></code> allows us to “lock” our properties to a particular configuration. Any changes to that <code><a href="Structs/VersionLock.html">VersionLock</a></code> will raise an assertion failure during the <code><a href="Classes/CoreStoreSchema.html">CoreStoreSchema</a></code> initialization, so you can then look for the commit which changed the <code><a href="Structs/VersionLock.html">VersionLock</a></code> hash.</p>
|
||
|
||
<p>To use <code><a href="Structs/VersionLock.html">VersionLock</a></code>s, create the <code><a href="Classes/CoreStoreSchema.html">CoreStoreSchema</a></code>, run the app, and look for a similar log message that is automatically printed to the console:</p>
|
||
|
||
<p><img width="700" alt="VersionLock" src="https://cloud.githubusercontent.com/assets/3029684/26525632/757f1bd0-4398-11e7-9795-4132a2df0538.png" /></p>
|
||
|
||
<p>Copy this dictionary value and use it as the <code>versionLock:</code> argument of the <code><a href="Classes/CoreStoreSchema.html">CoreStoreSchema</a></code> initializer:</p>
|
||
<pre class="highlight swift"><code><span class="kt">CoreStoreSchema</span><span class="p">(</span>
|
||
<span class="nv">modelVersion</span><span class="p">:</span> <span class="s">"V1"</span><span class="p">,</span>
|
||
<span class="nv">entities</span><span class="p">:</span> <span class="p">[</span>
|
||
<span class="kt">Entity</span><span class="o"><</span><span class="kt">Animal</span><span class="o">></span><span class="p">(</span><span class="s">"Animal"</span><span class="p">,</span> <span class="nv">isAbstract</span><span class="p">:</span> <span class="kc">true</span><span class="p">),</span>
|
||
<span class="kt">Entity</span><span class="o"><</span><span class="kt">Dog</span><span class="o">></span><span class="p">(</span><span class="s">"Dog"</span><span class="p">),</span>
|
||
<span class="kt">Entity</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span><span class="p">(</span><span class="s">"Person"</span><span class="p">),</span>
|
||
<span class="p">],</span>
|
||
<span class="nv">versionLock</span><span class="p">:</span> <span class="p">[</span>
|
||
<span class="s">"Animal"</span><span class="p">:</span> <span class="p">[</span><span class="mh">0x1b59d511019695cf</span><span class="p">,</span> <span class="mh">0xdeb97e86c5eff179</span><span class="p">,</span> <span class="mh">0x1cfd80745646cb3</span><span class="p">,</span> <span class="mh">0x4ff99416175b5b9a</span><span class="p">],</span>
|
||
<span class="s">"Dog"</span><span class="p">:</span> <span class="p">[</span><span class="mh">0xe3f0afeb109b283a</span><span class="p">,</span> <span class="mh">0x29998d292938eb61</span><span class="p">,</span> <span class="mh">0x6aab788333cfc2a3</span><span class="p">,</span> <span class="mh">0x492ff1d295910ea7</span><span class="p">],</span>
|
||
<span class="s">"Person"</span><span class="p">:</span> <span class="p">[</span><span class="mh">0x66d8bbfd8b21561f</span><span class="p">,</span> <span class="mh">0xcecec69ecae3570f</span><span class="p">,</span> <span class="mh">0xc4b73d71256214ef</span><span class="p">,</span> <span class="mh">0x89b99bfe3e013e8b</span><span class="p">]</span>
|
||
<span class="p">]</span>
|
||
<span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>You can also get this hash after the <code><a href="Classes/DataStack.html">DataStack</a></code> has been fully set up by printing to the console:</p>
|
||
<pre class="highlight swift"><code><span class="nf">print</span><span class="p">(</span><span class="kt">CoreStoreDefaults</span><span class="o">.</span><span class="n">dataStack</span><span class="o">.</span><span class="n">modelSchema</span><span class="o">.</span><span class="nf">printCoreStoreSchema</span><span class="p">())</span>
|
||
</code></pre>
|
||
|
||
<p>Once the version lock is set, any changes in the properties or to the model will trigger an assertion failure similar to this:</p>
|
||
|
||
<p><img width="700" alt="VersionLock failure" src="https://cloud.githubusercontent.com/assets/3029684/26525666/92f46f0c-4399-11e7-9395-4379f6f20876.png" /></p>
|
||
<h2 id='reactive-programming' class='heading'>Reactive Programming</h2>
|
||
<h3 id='rxswift' class='heading'>RxSwift</h3>
|
||
|
||
<p>RxSwift utilities are available through the <a href="https://github.com/JohnEstropia/RxCoreStore">RxCoreStore</a> external module.</p>
|
||
<h3 id='combine' class='heading'>Combine</h3>
|
||
|
||
<p>Combine publishers are available from the <code><a href="Classes/DataStack.html">DataStack</a></code>, <code><a href="Classes/ListPublisher.html">ListPublisher</a></code>, and <code><a href="Classes/ObjectPublisher.html">ObjectPublisher</a></code>‘s <code>.reactive</code> namespace property.</p>
|
||
<h4 id='code-datastack-reactive-code' class='heading'><code><a href="Classes/DataStack.html#/s:9CoreStore9DataStackC8reactiveAC17ReactiveNamespaceVvp">DataStack.reactive</a></code></h4>
|
||
|
||
<p>Adding a storage through <code>DataStack.reactive.addStorage(_:)</code> returns a publisher that reports a <code><a href="Enums/MigrationProgress.html">MigrationProgress</a></code> <code>enum</code> value. The <code>.migrating</code> value is only emitted if the storage goes through a migration. Refer to the <a href="#setting-up">Setting up</a> section for details on the storage setup process itself.</p>
|
||
<pre class="highlight swift"><code><span class="n">dataStack</span><span class="o">.</span><span class="n">reactive</span>
|
||
<span class="o">.</span><span class="nf">addStorage</span><span class="p">(</span>
|
||
<span class="kt">SQLiteStore</span><span class="p">(</span><span class="nv">fileName</span><span class="p">:</span> <span class="s">"core_data.sqlite"</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
<span class="o">.</span><span class="nf">sink</span><span class="p">(</span>
|
||
<span class="nv">receiveCompletion</span><span class="p">:</span> <span class="p">{</span> <span class="n">result</span> <span class="k">in</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">receiveValue</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">progress</span><span class="p">)</span> <span class="k">in</span>
|
||
<span class="nf">print</span><span class="p">(</span><span class="s">"</span><span class="se">\(</span><span class="nf">round</span><span class="p">(</span><span class="n">progress</span><span class="o">.</span><span class="n">fractionCompleted</span> <span class="o">*</span> <span class="mi">100</span><span class="p">)</span><span class="se">)</span><span class="s"> %"</span><span class="p">)</span> <span class="c1">// 0.0 ~ 1.0</span>
|
||
<span class="k">switch</span> <span class="n">progress</span> <span class="p">{</span>
|
||
<span class="k">case</span> <span class="o">.</span><span class="nf">migrating</span><span class="p">(</span><span class="k">let</span> <span class="nv">storage</span><span class="p">,</span> <span class="k">let</span> <span class="nv">nsProgress</span><span class="p">):</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="k">case</span> <span class="o">.</span><span class="nf">finished</span><span class="p">(</span><span class="k">let</span> <span class="nv">storage</span><span class="p">,</span> <span class="k">let</span> <span class="nv">migrationRequired</span><span class="p">):</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
<span class="o">.</span><span class="nf">store</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="o">&</span><span class="n">cancellables</span><span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p><a href="#saving-and-processing-transactions">Transactions</a> are also available as publishers through <code>DataStack.reactive.perform(_:)</code>, which returns a Combine <code>Future</code> that emits any type returned from the closure parameter:</p>
|
||
<pre class="highlight swift"><code><span class="n">dataStack</span><span class="o">.</span><span class="n">reactive</span>
|
||
<span class="o">.</span><span class="nf">perform</span><span class="p">(</span>
|
||
<span class="nv">asynchronous</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">transaction</span><span class="p">)</span> <span class="o">-></span> <span class="p">(</span><span class="nv">inserted</span><span class="p">:</span> <span class="kt">Set</span><span class="o"><</span><span class="kt">NSManagedObject</span><span class="o">></span><span class="p">,</span> <span class="nv">deleted</span><span class="p">:</span> <span class="kt">Set</span><span class="o"><</span><span class="kt">NSManagedObject</span><span class="o">></span><span class="p">)</span> <span class="k">in</span>
|
||
|
||
<span class="c1">// ...</span>
|
||
<span class="nf">return</span> <span class="p">(</span>
|
||
<span class="n">transaction</span><span class="o">.</span><span class="nf">insertedObjects</span><span class="p">(),</span>
|
||
<span class="n">transaction</span><span class="o">.</span><span class="nf">deletedObjects</span><span class="p">()</span>
|
||
<span class="p">)</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
<span class="o">.</span><span class="nf">sink</span><span class="p">(</span>
|
||
<span class="nv">receiveCompletion</span><span class="p">:</span> <span class="p">{</span> <span class="n">result</span> <span class="k">in</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">receiveValue</span><span class="p">:</span> <span class="p">{</span> <span class="n">value</span> <span class="k">in</span>
|
||
<span class="k">let</span> <span class="nv">inserted</span> <span class="o">=</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">fetchExisting</span><span class="p">(</span><span class="n">value0</span><span class="o">.</span><span class="n">inserted</span><span class="p">)</span>
|
||
<span class="k">let</span> <span class="nv">deleted</span> <span class="o">=</span> <span class="n">dataStack</span><span class="o">.</span><span class="nf">fetchExisting</span><span class="p">(</span><span class="n">value0</span><span class="o">.</span><span class="n">deleted</span><span class="p">)</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
<span class="o">.</span><span class="nf">store</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="o">&</span><span class="n">cancellables</span><span class="p">)</span>
|
||
</code></pre>
|
||
|
||
<p>For importing convenience, <code><a href="Protocols/ImportableObject.html">ImportableObject</a></code> and <code>ImportableUniqueObjects</code> can be imported directly through <code>DataStack.reactive.import[Unique]Object(_:source:)</code> and <code>DataStack.reactive.import[Unique]Objects(_:sourceArray:)</code> without having to create a transaction block. In this case the publisher emits objects that are already usable directly from the main queue:</p>
|
||
<pre class="highlight swift"><code><span class="n">dataStack</span><span class="o">.</span><span class="n">reactive</span>
|
||
<span class="o">.</span><span class="nf">importUniqueObjects</span><span class="p">(</span>
|
||
<span class="kt">Into</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span><span class="p">(),</span>
|
||
<span class="nv">sourceArray</span><span class="p">:</span> <span class="p">[</span>
|
||
<span class="p">[</span><span class="s">"name"</span><span class="p">:</span> <span class="s">"John"</span><span class="p">],</span>
|
||
<span class="p">[</span><span class="s">"name"</span><span class="p">:</span> <span class="s">"Bob"</span><span class="p">],</span>
|
||
<span class="p">[</span><span class="s">"name"</span><span class="p">:</span> <span class="s">"Joe"</span><span class="p">]</span>
|
||
<span class="p">]</span>
|
||
<span class="p">)</span>
|
||
<span class="o">.</span><span class="nf">sink</span><span class="p">(</span>
|
||
<span class="nv">receiveCompletion</span><span class="p">:</span> <span class="p">{</span> <span class="n">result</span> <span class="k">in</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">receiveValue</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">people</span><span class="p">)</span> <span class="k">in</span>
|
||
<span class="kt">XCTAssertEqual</span><span class="p">(</span><span class="n">people</span><span class="p">?</span><span class="o">.</span><span class="n">count</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
<span class="o">.</span><span class="nf">store</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="o">&</span><span class="n">cancellables</span><span class="p">)</span>
|
||
</code></pre>
|
||
<h4 id='code-listpublisher-reactive-code' class='heading'><code><a href="Classes/ListPublisher.html#/s:9CoreStore13ListPublisherC8reactiveAC17ReactiveNamespaceVyx_Gvp">ListPublisher.reactive</a></code></h4>
|
||
|
||
<p><code><a href="Classes/ListPublisher.html">ListPublisher</a></code>s can be used to emit <code><a href="Structs/ListSnapshot.html">ListSnapshot</a></code>s through Combine using <code>ListPublisher.reactive.snapshot(emitInitialValue:)</code>. The snapshot values are emitted in the main queue:</p>
|
||
<pre class="highlight swift"><code><span class="n">listPublisher</span><span class="o">.</span><span class="n">reactive</span>
|
||
<span class="o">.</span><span class="nf">snapshot</span><span class="p">(</span><span class="nv">emitInitialValue</span><span class="p">:</span> <span class="kc">true</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="nf">sink</span><span class="p">(</span>
|
||
<span class="nv">receiveCompletion</span><span class="p">:</span> <span class="p">{</span> <span class="n">result</span> <span class="k">in</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">receiveValue</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">listSnapshot</span><span class="p">)</span> <span class="k">in</span>
|
||
<span class="n">dataSource</span><span class="o">.</span><span class="nf">apply</span><span class="p">(</span>
|
||
<span class="n">listSnapshot</span><span class="p">,</span>
|
||
<span class="nv">animatingDifferences</span><span class="p">:</span> <span class="kc">true</span>
|
||
<span class="p">)</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
<span class="o">.</span><span class="nf">store</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="o">&</span><span class="n">cancellables</span><span class="p">)</span>
|
||
</code></pre>
|
||
<h4 id='code-objectpublisher-reactive-code' class='heading'><code><a href="Classes/ObjectPublisher.html#/s:9CoreStore15ObjectPublisherC8reactiveAC17ReactiveNamespaceVyx_Gvp">ObjectPublisher.reactive</a></code></h4>
|
||
|
||
<p><code><a href="Classes/ObjectPublisher.html">ObjectPublisher</a></code>s can be used to emit <code><a href="Structs/ObjectSnapshot.html">ObjectSnapshot</a></code>s through Combine using <code>ObjectPublisher.reactive.snapshot(emitInitialValue:)</code>. The snapshot values are emitted in the main queue:</p>
|
||
<pre class="highlight swift"><code><span class="n">objectPublisher</span><span class="o">.</span><span class="n">reactive</span>
|
||
<span class="o">.</span><span class="nf">snapshot</span><span class="p">(</span><span class="nv">emitInitialValue</span><span class="p">:</span> <span class="kc">true</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="nf">sink</span><span class="p">(</span>
|
||
<span class="nv">receiveCompletion</span><span class="p">:</span> <span class="p">{</span> <span class="n">result</span> <span class="k">in</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">receiveValue</span><span class="p">:</span> <span class="p">{</span> <span class="p">(</span><span class="n">objectSnapshot</span><span class="p">)</span> <span class="k">in</span>
|
||
<span class="n">tableViewCell</span><span class="o">.</span><span class="nf">setObject</span><span class="p">(</span><span class="n">objectSnapshot</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
<span class="p">)</span>
|
||
<span class="o">.</span><span class="nf">store</span><span class="p">(</span><span class="nv">in</span><span class="p">:</span> <span class="o">&</span><span class="n">tableViewCell</span><span class="o">.</span><span class="n">cancellables</span><span class="p">)</span>
|
||
</code></pre>
|
||
<h2 id='swiftui-utilities' class='heading'>SwiftUI Utilities</h2>
|
||
|
||
<p>Observing list and object changes in SwiftUI can be done through a couple of approaches. One is by creating <a href="#swiftui-views">views that autoupdates their contents</a>, or by declaring <a href="#swiftui-property-wrappers">property wrappers that trigger view updates</a>. Both approaches are implemented almost the same internally, but this lets you be flexible depending on the structure of your custom <code>View</code>s.</p>
|
||
<h3 id='swiftui-views' class='heading'>SwiftUI Views</h3>
|
||
|
||
<p>CoreStore provides <code>View</code> containers that automatically update their contents when data changes.</p>
|
||
<h4 id='code-listreader-code' class='heading'><code><a href="Structs/ListReader.html">ListReader</a></code></h4>
|
||
|
||
<p>A <code><a href="Structs/ListReader.html">ListReader</a></code> observes changes to a <code><a href="Classes/ListPublisher.html">ListPublisher</a></code> and creates its content views dynamically. The builder closure receives a <code><a href="Structs/ListSnapshot.html">ListSnapshot</a></code> value that can be used to create the contents:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">people</span><span class="p">:</span> <span class="kt">ListPublisher</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span>
|
||
|
||
<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="n">some</span> <span class="kt">View</span> <span class="p">{</span>
|
||
<span class="kt">List</span> <span class="p">{</span>
|
||
<span class="kt">ListReader</span><span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="n">people</span><span class="p">)</span> <span class="p">{</span> <span class="n">listSnapshot</span> <span class="k">in</span>
|
||
<span class="kt">ForEach</span><span class="p">(</span><span class="nv">objectIn</span><span class="p">:</span> <span class="n">listSnapshot</span><span class="p">)</span> <span class="p">{</span> <span class="n">person</span> <span class="k">in</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="o">.</span><span class="nf">animation</span><span class="p">(</span><span class="o">.</span><span class="k">default</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>As shown above, a typical use case is to use it together with CoreStore’s <a href="#foreach"><code>ForEach</code> extensions</a>.</p>
|
||
|
||
<p>A <code>KeyPath</code> can also be optionally provided to extract specific properties of the <code><a href="Structs/ListSnapshot.html">ListSnapshot</a></code>:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">people</span><span class="p">:</span> <span class="kt">ListPublisher</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span>
|
||
|
||
<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="n">some</span> <span class="kt">View</span> <span class="p">{</span>
|
||
<span class="kt">ListReader</span><span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="n">people</span><span class="p">,</span> <span class="nv">keyPath</span><span class="p">:</span> <span class="err">\</span><span class="o">.</span><span class="n">count</span><span class="p">)</span> <span class="p">{</span> <span class="n">count</span> <span class="k">in</span>
|
||
<span class="kt">Text</span><span class="p">(</span><span class="s">"Number of members: </span><span class="se">\(</span><span class="n">count</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
<h4 id='code-objectreader-code' class='heading'><code><a href="Structs/ObjectReader.html">ObjectReader</a></code></h4>
|
||
|
||
<p>An <code><a href="Structs/ObjectReader.html">ObjectReader</a></code> observes changes to an <code><a href="Classes/ObjectPublisher.html">ObjectPublisher</a></code> and creates its content views dynamically. The builder closure receives an <code><a href="Structs/ObjectSnapshot.html">ObjectSnapshot</a></code> value that can be used to create the contents:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">person</span><span class="p">:</span> <span class="kt">ObjectPublisher</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span>
|
||
|
||
<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="n">some</span> <span class="kt">View</span> <span class="p">{</span>
|
||
<span class="kt">ObjectReader</span><span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="n">person</span><span class="p">)</span> <span class="p">{</span> <span class="n">objectSnapshot</span> <span class="k">in</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="o">.</span><span class="nf">animation</span><span class="p">(</span><span class="o">.</span><span class="k">default</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>A <code>KeyPath</code> can also be optionally provided to extract specific properties of the <code><a href="Structs/ObjectSnapshot.html">ObjectSnapshot</a></code>:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">person</span><span class="p">:</span> <span class="kt">ObjectPublisher</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span>
|
||
|
||
<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="n">some</span> <span class="kt">View</span> <span class="p">{</span>
|
||
<span class="kt">ObjectReader</span><span class="p">(</span><span class="k">self</span><span class="o">.</span><span class="n">person</span><span class="p">,</span> <span class="nv">keyPath</span><span class="p">:</span> <span class="err">\</span><span class="o">.</span><span class="n">fullName</span><span class="p">)</span> <span class="p">{</span> <span class="n">fullName</span> <span class="k">in</span>
|
||
<span class="kt">Text</span><span class="p">(</span><span class="s">"Name: </span><span class="se">\(</span><span class="n">fullName</span><span class="se">)</span><span class="s">"</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>By default, an <code><a href="Structs/ObjectReader.html">ObjectReader</a></code> does not create its views wheen the object observed is deleted from the store. In those cases, the <code>placeholder:</code> argument can be used to provide a custom <code>View</code> to display when the object is deleted:</p>
|
||
<pre class="highlight swift"><code><span class="k">let</span> <span class="nv">person</span><span class="p">:</span> <span class="kt">ObjectPublisher</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span>
|
||
|
||
<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="n">some</span> <span class="kt">View</span> <span class="p">{</span>
|
||
<span class="kt">ObjectReader</span><span class="p">(</span>
|
||
<span class="k">self</span><span class="o">.</span><span class="n">person</span><span class="p">,</span>
|
||
<span class="nv">content</span><span class="p">:</span> <span class="p">{</span> <span class="n">objectSnapshot</span> <span class="k">in</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">},</span>
|
||
<span class="nv">placeholder</span><span class="p">:</span> <span class="p">{</span> <span class="kt">Text</span><span class="p">(</span><span class="s">"Record not found"</span><span class="p">)</span> <span class="p">}</span>
|
||
<span class="p">)</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
<h3 id='swiftui-property-wrappers' class='heading'>SwiftUI Property Wrappers</h3>
|
||
|
||
<p>As an alternative to <code><a href="Structs/ListReader.html">ListReader</a></code> and <code><a href="Structs/ObjectReader.html">ObjectReader</a></code>, CoreStore also provides property wrappers that trigger view updates when the data changes.</p>
|
||
<h4 id='code-liststate-code' class='heading'><code><a href="Structs/ListState.html">ListState</a></code></h4>
|
||
|
||
<p>A <code><a href="Structs/ListState.html">@ListState</a></code> property exposes a <code><a href="Structs/ListSnapshot.html">ListSnapshot</a></code> value that automatically updates to the latest changes.</p>
|
||
<pre class="highlight swift"><code><span class="kd">@ListState</span>
|
||
<span class="k">var</span> <span class="nv">people</span><span class="p">:</span> <span class="kt">ListSnapshot</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span>
|
||
|
||
<span class="nf">init</span><span class="p">(</span><span class="nv">listPublisher</span><span class="p">:</span> <span class="kt">ListPublisher</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span><span class="p">)</span> <span class="p">{</span>
|
||
<span class="k">self</span><span class="o">.</span><span class="n">_people</span> <span class="o">=</span> <span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="n">listPublisher</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="n">some</span> <span class="kt">View</span> <span class="p">{</span>
|
||
<span class="kt">List</span> <span class="p">{</span>
|
||
<span class="kt">ForEach</span><span class="p">(</span><span class="nv">objectIn</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">people</span><span class="p">)</span> <span class="p">{</span> <span class="n">objectSnapshot</span> <span class="k">in</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="o">.</span><span class="nf">animation</span><span class="p">(</span><span class="o">.</span><span class="k">default</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>As shown above, a typical use case is to use it together with CoreStore’s <a href="#foreach"><code>ForEach</code> extensions</a>.</p>
|
||
|
||
<p>If a <code><a href="Classes/ListPublisher.html">ListPublisher</a></code> instance is not available yet, the fetch can be done inline by providing the fetch clauses and the <code><a href="Classes/DataStack.html">DataStack</a></code> instance. By doing so the property can be declared without an initial value:</p>
|
||
<pre class="highlight swift"><code><span class="kd">@ListState(
|
||
From<Person>()</span>
|
||
<span class="o">.</span><span class="nf">sectionBy</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">age</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="nf">where</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">isMember</span> <span class="o">==</span> <span class="kc">true</span><span class="p">)</span>
|
||
<span class="o">.</span><span class="nf">orderBy</span><span class="p">(</span><span class="o">.</span><span class="nf">ascending</span><span class="p">(</span><span class="err">\</span><span class="o">.</span><span class="n">lastName</span><span class="p">))</span>
|
||
<span class="p">)</span>
|
||
<span class="k">var</span> <span class="nv">people</span><span class="p">:</span> <span class="kt">ListSnapshot</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span>
|
||
|
||
<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="n">some</span> <span class="kt">View</span> <span class="p">{</span>
|
||
<span class="kt">List</span> <span class="p">{</span>
|
||
<span class="kt">ForEach</span><span class="p">(</span><span class="nv">sectionIn</span><span class="p">:</span> <span class="k">self</span><span class="o">.</span><span class="n">people</span><span class="p">)</span> <span class="p">{</span> <span class="n">section</span> <span class="k">in</span>
|
||
<span class="kt">Section</span><span class="p">(</span><span class="nv">header</span><span class="p">:</span> <span class="kt">Text</span><span class="p">(</span><span class="n">section</span><span class="o">.</span><span class="n">sectionID</span><span class="p">))</span> <span class="p">{</span>
|
||
<span class="kt">ForEach</span><span class="p">(</span><span class="nv">objectIn</span><span class="p">:</span> <span class="n">section</span><span class="p">)</span> <span class="p">{</span> <span class="n">person</span> <span class="k">in</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="o">.</span><span class="nf">animation</span><span class="p">(</span><span class="o">.</span><span class="k">default</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>For other initialization variants, refer to the <em>ListState.swift</em> source documentations.</p>
|
||
<h4 id='code-objectstate-code' class='heading'><code><a href="Structs/ObjectState.html">ObjectState</a></code></h4>
|
||
|
||
<p>An <code><a href="Structs/ObjectState.html">@ObjectState</a></code> property exposes an optional <code><a href="Structs/ObjectSnapshot.html">ObjectSnapshot</a></code> value that automatically updates to the latest changes.</p>
|
||
<pre class="highlight swift"><code><span class="kd">@ObjectState</span>
|
||
<span class="k">var</span> <span class="nv">person</span><span class="p">:</span> <span class="kt">ObjectSnapshot</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span><span class="p">?</span>
|
||
|
||
<span class="nf">init</span><span class="p">(</span><span class="nv">objectPublisher</span><span class="p">:</span> <span class="kt">ObjectPublisher</span><span class="o"><</span><span class="kt">Person</span><span class="o">></span><span class="p">)</span> <span class="p">{</span>
|
||
<span class="k">self</span><span class="o">.</span><span class="n">_person</span> <span class="o">=</span> <span class="o">.</span><span class="nf">init</span><span class="p">(</span><span class="n">objectPublisher</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="k">var</span> <span class="nv">body</span><span class="p">:</span> <span class="n">some</span> <span class="kt">View</span> <span class="p">{</span>
|
||
<span class="kt">HStack</span> <span class="p">{</span>
|
||
<span class="k">if</span> <span class="k">let</span> <span class="nv">person</span> <span class="o">=</span> <span class="k">self</span><span class="o">.</span><span class="n">person</span> <span class="p">{</span>
|
||
<span class="kt">AsyncImage</span><span class="p">(</span><span class="n">person</span><span class="o">.</span><span class="err">$</span><span class="n">avatarURL</span><span class="p">)</span>
|
||
<span class="kt">Text</span><span class="p">(</span><span class="n">person</span><span class="o">.</span><span class="err">$</span><span class="n">fullName</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
<span class="k">else</span> <span class="p">{</span>
|
||
<span class="kt">Text</span><span class="p">(</span><span class="s">"Record removed"</span><span class="p">)</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</code></pre>
|
||
|
||
<p>As shown above, the property’s value will be <code>nil</code> if the object has been deleted, so this can be used to display placeholders if needed.</p>
|
||
<h3 id='swiftui-extensions' class='heading'>SwiftUI Extensions</h3>
|
||
|
||
<p>For convenience, CoreStore provides extensions to the standard SwiftUI types.</p>
|
||
<h4 id='code-foreach-code' class='heading'><code>ForEach</code></h4>
|
||
|
||
<p>Several <code>ForEach</code> initializer overloads are available. Choose depending on your input data and the expected closure data. Refer to the table below (Take note of the argument labels as they are important):</p>
|
||
|
||
<table>
|
||
<tr><th>Data</th><th>Example</th></tr>
|
||
<tr>
|
||
<td>
|
||
Signature:
|
||
<pre lang=swift>
|
||
ForEach(_: [ObjectSnapshot<O>])
|
||
</pre>
|
||
Closure:
|
||
<pre lang=swift>
|
||
ObjectSnapshot<O>
|
||
</pre>
|
||
</td>
|
||
<td><pre lang=swift>
|
||
let array: [ObjectSnapshot<Person>]
|
||
<br />
|
||
var body: some View {
|
||
<br />
|
||
List {
|
||
<br />
|
||
ForEach(self.array) { objectSnapshot in
|
||
<br />
|
||
// ...
|
||
}
|
||
}
|
||
}
|
||
</pre></td>
|
||
</tr>
|
||
<tr>
|
||
<td>
|
||
Signature:
|
||
<pre lang=swift>
|
||
ForEach(objectIn: ListSnapshot<O>)
|
||
</pre>
|
||
Closure:
|
||
<pre lang=swift>
|
||
ObjectPublisher<O>
|
||
</pre>
|
||
</td>
|
||
<td><pre lang=swift>
|
||
let listSnapshot: ListSnapshot<Person>
|
||
<br />
|
||
var body: some View {
|
||
<br />
|
||
List {
|
||
<br />
|
||
ForEach(objectIn: self.listSnapshot) { objectPublisher in
|
||
<br />
|
||
// ...
|
||
}
|
||
}
|
||
}
|
||
</pre></td>
|
||
</tr>
|
||
<tr>
|
||
<td>
|
||
Signature:
|
||
<pre lang=swift>
|
||
ForEach(objectIn: [ObjectSnapshot<O>])
|
||
</pre>
|
||
Closure:
|
||
<pre lang=swift>
|
||
ObjectPublisher<O>
|
||
</pre>
|
||
</td>
|
||
<td><pre lang=swift>
|
||
let array: [ObjectSnapshot<Person>]
|
||
<br />
|
||
var body: some View {
|
||
<br />
|
||
List {
|
||
<br />
|
||
ForEach(objectIn: self.array) { objectPublisher in
|
||
<br />
|
||
// ...
|
||
}
|
||
}
|
||
}
|
||
</pre></td>
|
||
</tr>
|
||
<tr>
|
||
<td>
|
||
Signature:
|
||
<pre lang=swift>
|
||
ForEach(sectionIn: ListSnapshot<O>)
|
||
</pre>
|
||
Closure:
|
||
<pre lang=swift>
|
||
[ListSnapshot<O>.SectionInfo]
|
||
</pre>
|
||
</td>
|
||
<td><pre lang=swift>
|
||
let listSnapshot: ListSnapshot<Person>
|
||
<br />
|
||
var body: some View {
|
||
<br />
|
||
List {
|
||
<br />
|
||
ForEach(sectionIn: self.listSnapshot) { sectionInfo in
|
||
<br />
|
||
// ...
|
||
}
|
||
}
|
||
}
|
||
</pre></td>
|
||
</tr>
|
||
<tr>
|
||
<td>
|
||
Signature:
|
||
<pre lang=swift>
|
||
ForEach(objectIn: ListSnapshot<O>.SectionInfo)
|
||
</pre>
|
||
Closure:
|
||
<pre lang=swift>
|
||
ObjectPublisher<O>
|
||
</pre>
|
||
</td>
|
||
<td><pre lang=swift>
|
||
let listSnapshot: ListSnapshot<Person>
|
||
<br />
|
||
var body: some View {
|
||
<br />
|
||
List {
|
||
<br />
|
||
ForEach(sectionIn: self.listSnapshot) { sectionInfo in
|
||
<br />
|
||
ForEach(objectIn: sectionInfo) { objectPublisher in
|
||
<br />
|
||
// ...
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</pre></td>
|
||
</tr>
|
||
</table>
|
||
<h1 id='roadmap' class='heading'>Roadmap</h1>
|
||
<h3 id='prototyping-stage' class='heading'>Prototyping stage</h3>
|
||
|
||
<ul>
|
||
<li>[ ] Widget/Extensions storage-sharing support</li>
|
||
<li>[ ] CloudKit support</li>
|
||
</ul>
|
||
<h3 id='under-consideration' class='heading'>Under consideration</h3>
|
||
|
||
<ul>
|
||
<li>[ ] Derived attributes</li>
|
||
<li>[ ] Cross-storage relationships (via Fetched Attributes)</li>
|
||
</ul>
|
||
<h1 id='installation' class='heading'>Installation</h1>
|
||
<div class="aside aside-requires">
|
||
<p class="aside-title">Requires</p>
|
||
<ul>
|
||
<li>iOS 13 SDK and above</li>
|
||
<li>Swift 5.7 (Xcode 14.0+)</li>
|
||
<li>For previous Swift versions: <a href="https://github.com/JohnEstropia/CoreStore/tree/8.1.0">Swift 5.5</a>, <a href="https://github.com/JohnEstropia/CoreStore/tree/8.0.1">Swift 5.4</a>, <a href="https://github.com/JohnEstropia/CoreStore/tree/7.3.1">Swift 5.3</a></li>
|
||
</ul>
|
||
|
||
</div><ul>
|
||
<li>Dependencies:
|
||
|
||
<ul>
|
||
<li><em>None</em></li>
|
||
</ul></li>
|
||
<li>Other notes:
|
||
|
||
<ul>
|
||
<li>The <code>com.apple.CoreData.ConcurrencyDebug</code> debug argument should be turned off for the app. CoreStore already guarantees safety for you by making the main context read-only, and by only executing transactions serially.</li>
|
||
</ul></li>
|
||
</ul>
|
||
<h3 id='install-with-cocoapods' class='heading'>Install with CocoaPods</h3>
|
||
|
||
<p>In your <code>Podfile</code>, add</p>
|
||
<pre class="highlight plaintext"><code>pod 'CoreStore', '~> 9.1'
|
||
</code></pre>
|
||
|
||
<p>and run </p>
|
||
<pre class="highlight plaintext"><code>pod update
|
||
</code></pre>
|
||
|
||
<p>This installs CoreStore as a framework. Declare <code>import CoreStore</code> in your swift file to use the library.</p>
|
||
<h3 id='install-with-carthage' class='heading'>Install with Carthage</h3>
|
||
|
||
<p>In your <code>Cartfile</code>, add</p>
|
||
<pre class="highlight plaintext"><code>github "JohnEstropia/CoreStore" >= 9.1.0
|
||
</code></pre>
|
||
|
||
<p>and run </p>
|
||
<pre class="highlight plaintext"><code>carthage update
|
||
</code></pre>
|
||
|
||
<p>This installs CoreStore as a framework. Declare <code>import CoreStore</code> in your swift file to use the library.</p>
|
||
<h4 id='install-with-swift-package-manager' class='heading'>Install with Swift Package Manager:</h4>
|
||
<pre class="highlight swift"><code><span class="nv">dependencies</span><span class="p">:</span> <span class="p">[</span>
|
||
<span class="o">.</span><span class="nf">package</span><span class="p">(</span><span class="nv">url</span><span class="p">:</span> <span class="s">"https://github.com/JohnEstropia/CoreStore.git"</span><span class="p">,</span> <span class="nv">from</span><span class="p">:</span> <span class="s">"9.1.0"</span><span class="p">))</span>
|
||
<span class="p">]</span>
|
||
</code></pre>
|
||
|
||
<p>Declare <code>import CoreStore</code> in your swift file to use the library.</p>
|
||
<h3 id='install-as-git-submodule' class='heading'>Install as Git Submodule</h3>
|
||
<pre class="highlight plaintext"><code>git submodule add https://github.com/JohnEstropia/CoreStore.git <destination directory>
|
||
</code></pre>
|
||
|
||
<p>Drag and drop <strong>CoreStore.xcodeproj</strong> to your project.</p>
|
||
<h3 id='install-through-xcode-39-s-swift-package-manager' class='heading'>Install through Xcode’s Swift Package Manager</h3>
|
||
|
||
<p>From the <strong>File</strong> - <strong>Swift Packages</strong> - <strong>Add Package Dependency…</strong> menu, search for </p>
|
||
<pre class="highlight plaintext"><code>CoreStore
|
||
</code></pre>
|
||
|
||
<p>where <code>JohnEstropia</code> is the <em>Owner</em> (forks may appear as well). Then add to your project</p>
|
||
<h1 id='changesets' class='heading'>Changesets</h1>
|
||
|
||
<p>For the full Changelog, refer to the <a href="https://github.com/JohnEstropia/CoreStore/releases">Releases</a> page. </p>
|
||
<h1 id='contact' class='heading'>Contact</h1>
|
||
|
||
<p>You can reach me on Twitter <a href="https://twitter.com/JohnEstropia">@JohnEstropia</a></p>
|
||
|
||
<p>or join our Slack team at <a href="http://swift-corestore-slack.herokuapp.com/">swift-corestore.slack.com</a></p>
|
||
|
||
<p>日本語の対応も可能なので是非!</p>
|
||
<h1 id='who-uses-corestore' class='heading'>Who uses CoreStore?</h1>
|
||
|
||
<p>I’d love to hear about apps using CoreStore. Send me a message and I’ll welcome any feedback!</p>
|
||
<h1 id='license' class='heading'>License</h1>
|
||
|
||
<p>CoreStore is released under an MIT license. See the <a href="https://raw.githubusercontent.com/JohnEstropia/CoreStore/master/LICENSE">LICENSE</a> file for more information</p>
|
||
|
||
</div>
|
||
</section>
|
||
|
||
|
||
</article>
|
||
</div>
|
||
<section class="footer">
|
||
<p>© 2023 <a class="link" href="https://github.com/JohnEstropia" target="_blank" rel="external noopener">John Estropia</a>. All rights reserved. (Last updated: 2023-06-08)</p>
|
||
<p>Generated by <a class="link" href="https://github.com/realm/jazzy" target="_blank" rel="external noopener">jazzy ♪♫ v0.14.3</a>, a <a class="link" href="https://realm.io" target="_blank" rel="external noopener">Realm</a> project.</p>
|
||
</section>
|
||
</body>
|
||
</html>
|