Compare commits

..

282 Commits

Author SHA1 Message Date
John Rommel Estropia 4b9fddad6a Merge branch 'develop' 2017-12-29 01:09:07 +09:00
John Rommel Estropia fbe7bd7bf8 update podfile 2017-12-29 00:53:48 +09:00
John Rommel Estropia 15edabdbb5 changed keyPath string utility to use String initializer 2017-12-29 00:05:11 +09:00
John Rommel Estropia f447bcfb95 Merge branch 'develop' into prototype/Swift_4_0
# Conflicts:
#	CoreStore.podspec
#	Sources/DataStack+Migration.swift
#	Sources/Info.plist
2017-11-19 15:35:29 +09:00
John Rommel Estropia 15e5e4fdf6 delete shm file after converting to DELETE journal mode 2017-11-19 15:34:09 +09:00
John Rommel Estropia 583c6b7249 minor code cleanup 2017-11-19 13:49:21 +09:00
John Rommel Estropia 18b933957e version bump 2017-11-16 02:28:18 +09:00
John Rommel Estropia 2c7039232e merge sqlite journal files before migration 2017-11-16 02:27:06 +09:00
John Rommel Estropia d3b3b5ff4a added fake progress for lightweight migrations 2017-11-16 02:26:56 +09:00
John Rommel Estropia d90e8d1303 force true lightweight migration 2017-11-16 02:26:44 +09:00
John Rommel Estropia b55dd13dff merge sqlite journal files before migration 2017-11-15 23:50:10 +09:00
John Rommel Estropia 66dd5b6f27 added fake progress for lightweight migrations 2017-11-13 02:44:26 +09:00
John Estropia 49c4b770eb WIP: Readme 2017-11-10 19:19:48 +09:00
John Rommel Estropia 662aaa1e75 force true lightweight migration 2017-11-10 02:48:37 +09:00
John Rommel Estropia dd4e47d7f9 revert null overloads, remove optional objectIDs as condition 2017-11-09 23:26:53 +09:00
John Estropia 9ea5073bc8 Merge branch 'master' into prototype/Swift_4_0
# Conflicts:
#	CoreStore.podspec
#	Sources/Info.plist
2017-11-09 19:52:29 +09:00
John Estropia e314db8f56 version bump 2017-11-09 19:45:21 +09:00
John Estropia 48d936d068 Fix regression for ARC bugfix (fixes #221) 2017-11-09 19:44:27 +09:00
John Estropia b2ff8a15ef add query overloads to == so comparison with nil don't confuse the compiler 2017-11-09 19:27:56 +09:00
John Estropia 8a4d1cd7c6 fix demo app warnings 2017-11-09 19:27:05 +09:00
John Rommel Estropia 8ce26c213d more source docs, deprecated some Where clause utilities 2017-11-04 10:37:54 +09:00
John Estropia f3816b9abf update for Xcode 9.1 2017-11-01 19:49:42 +09:00
John Estropia 21961780d4 force dynamic typing on DynamicObject.Type to mitigate optimization issues 2017-11-01 19:38:38 +09:00
John Rommel Estropia 305e2b61a0 fix compile errors for Xcode 9.1 beta 2017-11-01 19:37:53 +09:00
John Estropia 0430f66240 force dynamic typing on DynamicObject.Type to mitigate optimization issues 2017-11-01 11:33:41 +09:00
John Estropia 65772edd00 Merge pull request #208 from volodg/prototype/Swift_4_0
(Xcode 9.1) fix compile error:
2017-10-29 18:55:13 +09:00
John Rommel Estropia 02d7870d75 fix compile errors for Xcode 9.1 beta 2017-10-29 15:20:58 +09:00
John Rommel Estropia aca1709e13 WIP: documentation 2017-10-29 15:06:57 +09:00
John Rommel Estropia b6ee0b014f WIP: documentations 2017-10-24 00:31:27 +09:00
John Estropia e37186da73 Merge branch 'prototype/Swift_3_2' into prototype/Swift_4_0
# Conflicts:
#	CoreStore.podspec
#	Sources/Info.plist
2017-10-17 13:42:04 +09:00
John Estropia 588fa35c84 version bump 2017-10-17 13:39:57 +09:00
John Estropia f6614cda66 Merge branch 'develop' 2017-10-17 13:39:20 +09:00
John Estropia 639574d8c2 remove inline casts because optimized builds seem to trip on them 2017-10-17 13:38:25 +09:00
John Rommel Estropia 094703155c Merge branch 'master' into prototype/Swift_4_0
# Conflicts:
#	.swift-version
2017-10-13 08:14:20 +09:00
John Rommel Estropia 6dd254e713 Merge branch 'prototype/Swift_3_2' into prototype/Swift_4_0
# Conflicts:
#	CoreStore.podspec
#	Sources/Info.plist
2017-10-13 08:10:56 +09:00
John Rommel Estropia 204025721c version bump 2017-10-13 08:10:26 +09:00
John Rommel Estropia 27ffc1d225 Merge branch 'develop'
# Conflicts:
#	CoreStore.podspec
#	Sources/Info.plist
2017-10-13 08:01:30 +09:00
John Rommel Estropia ba6f0c39d5 swift version 3.2 2017-10-13 08:00:39 +09:00
John Rommel Estropia ab2eac8f6c Swift 3.2 README update 2017-10-13 07:58:37 +09:00
John Rommel Estropia f460a0b30f CI: test demo app 2017-10-12 01:10:08 +09:00
John Rommel Estropia 50bc3ace06 travis 2017-10-12 00:02:39 +09:00
John Rommel Estropia d2ddf2002f version update 2017-10-11 22:40:27 +09:00
John Rommel Estropia b4117eeb02 updated documentation (fixes #198) 2017-10-11 07:50:24 +09:00
Vladimir 106275b2dd fix compile error:
inheritance from 'AnyObject'
2017-10-07 12:21:27 +08:00
John Rommel Estropia 08d9298be0 Merge branch 'prototype/Swift_4_0' into prototype/queryBuilders
# Conflicts:
#	Sources/OrderBy.swift
2017-10-07 01:13:30 +09:00
John Rommel Estropia ff0c4d94fc Merge branch 'prototype/Swift_3_2' into prototype/Swift_4_0 2017-10-07 01:10:28 +09:00
John Rommel Estropia 50e50c0613 Merge branch 'develop' into prototype/Swift_3_2 2017-10-07 01:07:09 +09:00
John Rommel Estropia 5c8a0e425b version bump 2017-10-07 00:59:37 +09:00
John Rommel Estropia 03b71caf7e Merge branch 'develop' 2017-10-07 00:58:04 +09:00
John Rommel Estropia 7ff29d6086 version bump 2017-10-07 00:57:41 +09:00
John Rommel Estropia 8d86425875 always create subclass type from cs_rawObject 2017-10-07 00:56:19 +09:00
John Estropia 97242d9726 added type-erasers for CoreStoreObject property containers 2017-10-02 12:00:45 +09:00
John Estropia 780ff4e60b fix compile errors 2017-10-02 11:10:28 +09:00
John Estropia 11743dfb5f oops 2017-10-02 11:08:44 +09:00
John Estropia 9eaf85388c relax generic type requirements for some Where utilities 2017-10-02 10:33:59 +09:00
John Rommel Estropia 06635c9d2f orderby utilities 2017-10-02 08:04:28 +09:00
John Rommel Estropia 1d2eef7894 revert 2017-10-01 01:01:10 +09:00
John Rommel Estropia 85ed815ec2 WIP: more chain clause builder utilities 2017-10-01 00:45:21 +09:00
John Estropia 096e5493a6 WIP: protocol cleanup 2017-09-29 20:33:10 +09:00
John Estropia 0aa8c03424 Merge pull request #205 from jannon/case-insensitive-orderby
add case-insensitive sortkeys
2017-09-29 19:38:36 +09:00
John Estropia 4ead3c34dd delete errant operator 2017-09-26 11:59:28 +09:00
John Rommel Estropia 645034dde5 keyPath utilities for SectionBy clauses 2017-09-24 10:38:17 +09:00
John Rommel Estropia 85706a3c57 version bump 2017-09-22 23:43:19 +09:00
John Rommel Estropia c5ae4606b9 move cocoapods.yml 2017-09-22 23:41:07 +09:00
John Rommel Estropia fa682215c5 version bump 2017-09-22 23:26:15 +09:00
John Rommel Estropia e814733ae9 Merge branch 'prototype/Swift_4_0' into prototype/queryBuilders
# Conflicts:
#	Sources/DynamicObject.swift
2017-09-22 23:20:16 +09:00
John Rommel Estropia e2236698fa Merge branch 'prototype/Swift_3_2' into prototype/Swift_4_0 2017-09-22 23:18:38 +09:00
John Rommel Estropia be5da632b3 fix segmentation fault 2017-09-22 23:18:25 +09:00
John Estropia dc73cd6dd9 added a yml file for pod try (#200) 2017-09-22 15:48:02 +09:00
John Rommel Estropia 7beb3bec75 rename methods 2017-09-22 08:02:13 +09:00
Jannon Frank 53100b202d add case-insensitive sortkeys 2017-09-21 11:38:29 -07:00
John Rommel Estropia cc84b1f8bd minor 2017-09-22 01:00:03 +09:00
John Rommel Estropia 474f52ed2b alow nil comparison for relationship keypaths 2017-09-22 00:30:02 +09:00
John Rommel Estropia 16225fc4c6 allow optionals in relationship keyPaths 2017-09-22 00:20:55 +09:00
John Rommel Estropia 03bb7619da added queryBuilder utilities for clause sequences 2017-09-22 00:08:01 +09:00
John Rommel Estropia 1bfb7451c3 keyPath utilities for Select queries 2017-09-21 07:56:02 +09:00
John Rommel Estropia 3e082d5ed4 queryBuilders for list monitors 2017-09-20 00:45:38 +09:00
John Rommel Estropia e45d67252c CoreStore querying utilities 2017-09-20 00:24:03 +09:00
John Rommel Estropia 3d427c29c4 added required settings 2017-09-20 00:09:57 +09:00
John Rommel Estropia 1068517b94 Merge branch 'prototype/queryBuilders' of github.com:JohnEstropia/CoreStore into prototype/queryBuilders 2017-09-20 00:02:36 +09:00
John Estropia 0d23ce1598 fix Demo app compiler errors 2017-09-19 16:22:59 +09:00
John Estropia fd1ce20863 set swift version 2017-09-19 16:04:26 +09:00
John Estropia 2c5fa63f40 revert 2017-09-19 15:55:51 +09:00
John Estropia 78e43a37a5 fix OSX compiler errors 2017-09-19 15:54:15 +09:00
John Rommel Estropia fde85a9743 Merge branch 'prototype/Swift_4_0' into prototype/queryBuilders 2017-09-17 01:15:55 +09:00
John Rommel Estropia dbbc0adae5 Merge branch 'prototype/Swift_3_2' into prototype/Swift_4_0 2017-09-17 01:10:11 +09:00
John Rommel Estropia 2a62770552 Merge branch 'develop' into prototype/Swift_3_2 2017-09-10 12:31:30 +09:00
John Rommel Estropia f436b26e8e allow compound keypaths in Select terms 2017-09-08 01:28:24 +09:00
John Rommel Estropia c566226747 Merge branch 'prototype/Swift_4_0' into prototype/queryBuilders 2017-08-31 23:50:12 +09:00
John Rommel Estropia cd405e038e updated demo app 2017-08-31 23:00:17 +09:00
John Estropia 92a37053b0 Merge branch 'prototype/Swift_3_2' into prototype/Swift_4_0 2017-08-31 18:33:28 +09:00
John Estropia 0b57cff27d Merge branch 'develop' into prototype/Swift_3_2
# Conflicts:
#	Sources/DynamicSchema+Convenience.swift
2017-08-31 18:33:12 +09:00
John Estropia 3ebc44b546 optimize redundant casting in printCoreStoreSchema() 2017-08-31 18:28:23 +09:00
John Estropia 68f1027ba7 fix code misuse in readme 2017-08-31 17:55:10 +09:00
John Rommel Estropia 005729be85 Merge branch 'prototype/Swift_4_0' into prototype/queryBuilders 2017-08-26 14:29:16 +09:00
John Estropia 8bac4aa901 Fixed Xcode 9 beta 6 errors 2017-08-24 10:11:12 +09:00
John Estropia da170c7e51 Fixed Xcode 9 beta 6 errors 2017-08-23 18:40:06 +09:00
John Estropia 211e69023e version bump 2017-08-23 12:59:36 +09:00
John Rommel Estropia 2912dcf010 iOS 11 fixed the NSFetchRequest.affectedStores bug 2017-08-23 12:55:02 +09:00
John Rommel Estropia 3e00a3da06 fix demo app errors 2017-08-17 09:24:33 +09:00
John Rommel Estropia a33e248828 Merge branch 'prototype/Swift_4_0' into prototype/queryBuilders
# Conflicts:
#	Sources/NSManagedObjectContext+Querying.swift
2017-08-16 21:09:09 +09:00
John Rommel Estropia 75e14fbbed Merge branch 'prototype/Swift_3_2' into prototype/Swift_4_0
# Conflicts:
#	Sources/NSManagedObjectContext+Querying.swift
2017-08-16 21:08:33 +09:00
John Rommel Estropia 9922deac4d Merge branch 'develop' into prototype/Swift_3_2
# Conflicts:
#	Sources/AttributeProtocol.swift
#	Sources/DynamicSchema+Convenience.swift
#	Sources/ImportableAttributeType.swift
#	Sources/Relationship.swift
#	Sources/RelationshipProtocol.swift
#	Sources/Transformable.swift
#	Sources/Value.swift
2017-08-16 21:07:34 +09:00
John Rommel Estropia 86be046c9f updated podspec 2017-08-16 20:43:10 +09:00
John Rommel Estropia 0f10bc3349 version bump 2017-08-16 20:26:22 +09:00
John Rommel Estropia 9685f0aef2 #191 2017-08-16 20:21:26 +09:00
John Estropia 1e51000155 Merge pull request #191 from sidmani/prototype/Swift_4_0
Corrected type-checking in fetchExisting to preserve input type
2017-08-16 20:20:23 +09:00
John Rommel Estropia 3bd459bb1a updated docs 2017-08-16 20:09:09 +09:00
Sid Mani c89bc3c227 Corrected type-checking in fetchExisting to preserve input type 2017-08-16 00:36:06 -07:00
John Rommel Estropia 0f405a50aa WIP: KeyPath utilities for all clauses 2017-08-10 00:42:29 +09:00
John Estropia f5e1643ef7 minor cleanup 2017-08-10 00:10:15 +09:00
John Rommel Estropia f62137fb58 rename file 2017-08-10 00:10:08 +09:00
John Rommel Estropia 37fbedd799 WIP: keypath utilities for all raw clauses 2017-08-09 08:11:41 +09:00
John Rommel Estropia 28b43f33fa Value.Required now requires an "initial:" parameter. For previous CoreStoreObject users, use the appropriate empty value for your existing properties (0 for numeric types, false for Bool, "" for String) 2017-08-08 18:36:25 +09:00
John Rommel Estropia 81a72f29ea Merge branch 'prototype/Swift_4_0' into prototype/queryBuilders 2017-08-08 08:02:17 +09:00
John Rommel Estropia f7aaf4fb2a Fix compile errors for Xcode 9 beta 5 2017-08-08 08:02:05 +09:00
John Rommel Estropia a52acf2ebd Merge branch 'prototype/Swift_3_2' into prototype/Swift_4_0 2017-08-08 07:56:32 +09:00
John Rommel Estropia 74c64619c3 Fix compile error for Xcode 9 beta 5 2017-08-08 07:56:21 +09:00
John Rommel Estropia 4a5bc6450b complete query utilities 2017-08-08 07:36:52 +09:00
John Rommel Estropia fe69e7c6c4 Merge branch 'prototype/Swift_4_0' into prototype/queryBuilders
# Conflicts:
#	CoreStore.xcodeproj/project.pbxproj
#	Sources/Relationship.swift
#	Sources/Value.swift
2017-08-05 23:02:00 +09:00
John Rommel Estropia ccf7c62aad Merge branch 'prototype/Swift_3_2' into prototype/Swift_4_0 2017-08-05 08:25:49 +09:00
John Rommel Estropia f36cb8af63 iOS 11 fixed the NSFetchRequest.affectedStores bug 2017-08-05 08:25:28 +09:00
John Rommel Estropia 10ccadd96c update demo app 2017-08-05 08:25:03 +09:00
John Rommel Estropia 5c0e78bd53 Deleted EmptyableAttributeType and require "initial" parameter for Value.Required 2017-08-05 00:03:17 +09:00
John Rommel Estropia 8a09688117 Merge branch 'develop' into prototype/Swift_3_2 2017-07-15 21:30:05 +09:00
John Estropia a366bcf1a3 Merge branch 'develop' of github.com:JohnEstropia/CoreStore into develop 2017-07-11 15:47:20 +09:00
John Rommel Estropia fcd4be9011 WIP: chained queries for section monitors 2017-07-10 08:23:59 +09:00
John Rommel Estropia 535eb76adc WIP: query chains! 2017-07-09 10:44:53 +09:00
John Rommel Estropia c6e68ac24f reset transactions' context on deinit to break reference cycles in unsafed many-to-many relationships 2017-07-05 23:32:45 +09:00
John Rommel Estropia aff966aac9 WIP: Query builders 2017-07-05 08:45:10 +09:00
John Rommel Estropia 32743b3aee Merge branch 'prototype/Swift_3_2' into prototype/Swift_4_0 2017-07-04 23:16:49 +09:00
John Rommel Estropia a20ad87583 Merge branch 'develop' into prototype/Swift_3_2 2017-07-04 23:14:06 +09:00
John Estropia a11915db12 minor cleanup 2017-07-04 12:21:46 +09:00
John Rommel Estropia 961f39a806 add internal utilities to force checkpoint operations on SQLite 2017-07-01 17:05:10 +09:00
John Rommel Estropia 3096cb784c add internal utilities to force checkpoint operations on SQLite 2017-07-01 16:45:11 +09:00
John Rommel Estropia 809aa4ff96 allow querying file size on SQLiteStore 2017-07-01 11:50:23 +09:00
John Estropia 8d926d25ec Merge pull request #177 from blender/feature/optional-where-chaining
Add optional && and || operators to Where clause
2017-06-28 13:32:16 +09:00
Tommaso Piazza 790454f514 Add optional && and || operators to Where clause 2017-06-27 13:58:16 +02:00
John Rommel Estropia 9a38707c58 Merge branch 'prototype/Swift_3_2' into prototype/Swift_4_0 2017-06-26 00:36:07 +09:00
John Rommel Estropia fb7e2f7f7f Merge branch 'develop' into prototype/Swift_3_2 2017-06-26 00:34:32 +09:00
John Rommel Estropia f72efc80b2 added utilities for combining Where arrays and OrderBy arrays 2017-06-24 17:42:56 +09:00
John Estropia fcda5399da Merge branch 'prototype/Swift_3_2' into prototype/Swift_4_0 2017-06-23 17:58:49 +09:00
John Estropia fd3a9b00ec fix FRC breaking again for iOS 8 2017-06-23 17:58:34 +09:00
John Estropia f56c37f9ee Merge branch 'prototype/Swift_3_2' into prototype/Swift_4_0 2017-06-23 12:43:20 +09:00
John Estropia 5f5000218a Merge branch 'develop' into prototype/Swift_3_2
# Conflicts:
#	CoreStoreDemo/CoreStoreDemo/List and Object Observers Demo/Palette.swift
#	Sources/Value.swift
2017-06-23 12:43:03 +09:00
John Estropia e8eb309d82 Added source docs on usage of custom getters and setters for CoreStoreObject properties (Value.Required, etc) 2017-06-20 20:32:27 +09:00
John Rommel Estropia d0c3203e63 force reset contexts after autocommit 2017-06-17 21:50:21 +09:00
John Rommel Estropia f5b3901caa fix tests and demo app 2017-06-17 02:13:54 +09:00
John Rommel Estropia 1a99fea820 complete PartialObject utilities 2017-06-17 01:55:36 +09:00
John Rommel Estropia 195b60615b added missing utilities 2017-06-16 01:42:09 +09:00
John Rommel Estropia 2c394965b8 remove direct access to CoreStoreObject.primitiveValues and replace PartialObject<O> 2017-06-16 01:02:23 +09:00
John Rommel Estropia 746d697691 WIP: new PartialObject to act as faster KVC wrappers when implementing custom getters and setters for CoreStoreObject 2017-06-15 08:27:08 +09:00
John Estropia 5689158b43 version bump 2017-06-14 21:50:58 +09:00
John Estropia 6fcdf3d011 fix logger demo 2017-06-14 17:43:34 +09:00
John Estropia e26573c18e converted the Demo app's observer demo to use CoreStoreObject instead of NSManagedObject 2017-06-14 17:39:57 +09:00
John Estropia 801cf8d9f0 Fixed ListMonitor bug for CoreStoreObjects where ListObservers don't get update notifications 2017-06-14 17:37:46 +09:00
John Rommel Estropia eced8f2e93 fixed compile error on release mode 2017-06-12 22:39:06 +09:00
John Rommel Estropia 3b735d07ec fix merge compile errors 2017-06-11 09:33:25 +09:00
John Rommel Estropia 5eb5476e3a Merge branch 'prototype/Swift_3_2' into prototype/Swift_4_0 2017-06-11 09:17:03 +09:00
John Rommel Estropia 6a42a0054e Merge branch 'develop' into prototype/Swift_3_2
# Conflicts:
#	Sources/CoreStoreManagedObject.swift
#	Sources/CoreStoreSchema.swift
#	Sources/NSEntityDescription+DynamicModel.swift
#	Sources/Value.swift
2017-06-11 09:16:34 +09:00
John Rommel Estropia 8c437e19b8 version bump 2017-06-10 23:19:02 +09:00
John Rommel Estropia 9cd3b6c879 code cleanup 2017-06-10 22:13:32 +09:00
John Rommel Estropia fe135acbec Improve CoreStoreObjects KVO behavior 2017-06-10 21:02:36 +09:00
John Estropia 997c5bdcfa uniquify subclass names across model versions 2017-06-09 12:47:22 +09:00
John Estropia 23e12c4539 add constraints to Value.Optional and Value.Required native types 2017-06-09 11:30:09 +09:00
John Estropia ca9798be14 add constraints to Value.Optional and Value.Required native types 2017-06-09 11:29:51 +09:00
John Estropia 6e01a58c85 Swift 4 support 2017-06-09 11:25:28 +09:00
John Estropia f618617053 work around issue that crashes the Swift 3.2 compiler (fixes #171) 2017-06-08 20:18:50 +09:00
John Rommel Estropia 49b8b9c372 Merge branch 'master' into prototype/Swift_3_2 2017-06-08 08:10:59 +09:00
John Rommel Estropia 129f975d96 version bump 2017-06-08 01:17:39 +09:00
John Rommel Estropia a2e463e58c Merge branch 'prototype/customMigrationTest' of github.com:JohnEstropia/CoreStore into prototype/customMigrationTest 2017-06-08 01:07:00 +09:00
John Rommel Estropia 5fd50f0e15 fix migration for CoreStoreObject relationships 2017-06-08 01:06:51 +09:00
John Estropia 7f9a915d71 WIP: Swift 3.2 2017-06-07 20:07:43 +09:00
John Estropia 0a81736b7a Merge branch 'develop' into prototype/customMigrationTest 2017-06-07 12:31:42 +09:00
John Estropia f9b6dd0c6a fix bug when using ObjectMonitor with CoreStoreObjects 2017-06-06 17:40:29 +09:00
John Rommel Estropia 0354401b56 WIP: bugfix for CustomSchemaMappingProvider relationship migration bug 2017-06-06 08:39:59 +09:00
John Rommel Estropia 0304067beb fix ToManyOrdered and ToManyUnordered enumeration crash 2017-06-06 00:49:16 +09:00
John Rommel Estropia ddd83da434 add Where comparison operators for optional values 2017-06-05 23:09:19 +09:00
John Estropia fc7df671de Fixes https://bugs.swift.org/browse/SR-4981 2017-06-05 12:45:35 +09:00
John Rommel Estropia 5fde9030c7 use stronger namespace for CoreStoreObject's internal managed object type 2017-06-05 08:03:37 +09:00
John Rommel Estropia d7b07b3f00 Added alternative way to set keyPathsForValuesAffectingValue(forKey:) for CoreStoreObjects 2017-06-05 01:30:26 +09:00
John Rommel Estropia ddd1ffb29f added ~= operation to create Where clauses for value arrays 2017-06-03 08:51:52 +09:00
John Rommel Estropia 98094000bb Merge branch 'master' into develop 2017-06-02 23:10:31 +09:00
John Estropia d5026ef996 Merge branch 'temp/develop' into develop 2017-06-02 19:35:01 +09:00
John Estropia 2cd913b9dd added more utilities for CoreStoreObject meta 2017-06-02 19:34:54 +09:00
John Estropia ad9520abbc Update README.md 2017-06-02 13:24:15 +09:00
John Estropia c0fc57d10c Update README.md 2017-06-02 11:34:04 +09:00
John Estropia 0cf4d303e4 Added README sample on how to version CoreStoreObjects 2017-06-02 11:32:48 +09:00
John Rommel Estropia 6de397958a fix demo app warnings 2017-06-02 02:06:47 +09:00
John Rommel Estropia 55292a84dc made mapping modell providers public 2017-06-02 02:03:41 +09:00
John Estropia 981b560d53 test travis ci 2017-05-30 11:14:57 +09:00
John Estropia c4c4dd55cd test travis build 2017-05-30 11:06:30 +09:00
John Rommel Estropia 1ddbe20c86 Updated README, fixed demo app, and bumped to 4.0.1 2017-05-28 11:37:40 +09:00
John Rommel Estropia da9e8c1550 disallow "empty" default values on some ImportableAttributeTypes 2017-05-28 10:50:25 +09:00
John Estropia ef0937fec4 make unit tests happy 2017-05-24 12:15:55 +09:00
John Estropia 35885b40de README done! Welcome to CoreStore 4.0! 2017-05-24 12:05:34 +09:00
John Rommel Estropia d669569196 WIP: readme 2017-05-24 00:37:32 +09:00
John Rommel Estropia ae919ff3c8 WIP: readme 2017-05-24 00:35:25 +09:00
John Rommel Estropia 1a7a4690d1 WIP: readme 2017-05-24 00:33:43 +09:00
John Rommel Estropia b9b96d1a35 WIP: Updating README and other docs. Some minor fixes 2017-05-22 01:27:38 +09:00
John Rommel Estropia da3a9590ac accept optionals in setValue 2017-05-21 09:38:56 +09:00
John Rommel Estropia 6b3d75bea1 fix fixits 2017-05-21 09:29:34 +09:00
John Rommel Estropia d44721fef0 fix xcode hints 2017-05-21 09:11:34 +09:00
John Rommel Estropia 3f268e8376 added NSManagedObject.setValue(_:forKvcKey:willSetValue:didSetValue:) 2017-05-21 09:06:29 +09:00
John Rommel Estropia 303fea4ebe fix warnings 2017-05-21 08:51:38 +09:00
John Estropia 77173cdad0 minor fix 2017-05-18 21:10:43 +09:00
John Estropia 1e24a7d739 ListObserver's listMonitorDidChange(_:) and listMonitorDidRefetch(_:) handlers are now required. 2017-05-18 12:59:58 +09:00
John Estropia a3b33bedb8 added more migration error types 2017-05-18 12:59:02 +09:00
John Estropia eaf7544c50 fix error when CoreStoreObject types have deep namespaces 2017-05-17 15:55:33 +09:00
John Estropia 67863120e0 WIP: readme 2017-05-15 11:03:21 +09:00
John Estropia 1b0e305d9a beta bump 2017-05-15 10:23:55 +09:00
John Rommel Estropia 91fda01071 WIP: readme 2017-05-15 08:51:31 +09:00
John Estropia feb0e30735 tag as beta 2017-05-12 14:46:28 +09:00
John Rommel Estropia 9c25336ff6 WIP: documentation 2017-05-12 01:42:19 +09:00
John Estropia 66bef87422 WIP: documentation 2017-05-11 20:55:31 +09:00
John Rommel Estropia dd2949ee18 WIP: documentation 2017-05-11 00:03:13 +09:00
John Estropia e0abb9b0af Merge branch 'develop' into corestore4_develop
# Conflicts:
#	CoreStore.podspec
#	Sources/CoreStoreImportableAttributeType.swift
#	Sources/CoreStoreQueryableAttributeType.swift
#	Sources/Info.plist
#	Sources/NSManagedObjectModel+Setup.swift
2017-05-10 19:11:01 +09:00
John Estropia 42c28b064a remove unneeded swift flags 2017-05-10 12:47:40 +09:00
John Estropia 0b097b8c85 version bump 2017-05-10 12:43:18 +09:00
John Estropia fce5d2f301 Flatten file directories (fixes #159) 2017-05-10 12:42:20 +09:00
John Rommel Estropia 8ff163af30 WIP: documentation 2017-05-10 02:00:47 +09:00
John Estropia 19abedfa9f WIP: documentation 2017-05-09 21:16:03 +09:00
John Estropia 48828fdca3 Custom migrations! 2017-05-09 17:58:41 +09:00
John Rommel Estropia 9d65a27557 WIP: custom migration 2017-05-09 03:10:35 +09:00
John Rommel Estropia 6d04806608 WIP 2017-04-26 08:31:37 +09:00
John Rommel Estropia 54129f7362 Merge branch 'corestore4_develop' of github.com:JohnEstropia/CoreStore into corestore4_develop 2017-04-25 22:08:58 +09:00
John Estropia 54c81d23f5 WIP: dynamic migrations 2017-04-25 18:08:43 +09:00
John Estropia 53ab140341 Improve refetch method (may fix #118) 2017-04-24 11:57:47 +09:00
John Estropia 9dc4331b26 oops 2017-04-21 21:21:47 +09:00
John Estropia e6aa72fb5f support transformable values 2017-04-21 19:35:29 +09:00
John Estropia 274a54451a fix compiler errors 2017-04-21 15:07:26 +09:00
John Estropia fe70b7a27d added tool to convert existing NSManagedObjectModels to the new CoreStoreSchema 2017-04-21 14:54:57 +09:00
John Estropia 02a660e4a6 WIP: Migrations 2017-04-20 20:26:11 +09:00
John Estropia a543a4c94a Added a "userInfo" property to relevant types to allow external code to store custom data 2017-04-20 16:40:27 +09:00
John Estropia fd14a18248 Unify generics usage in ListMonitor and ObjectMonitor 2017-04-20 10:42:47 +09:00
John Estropia b0e2655bdf added a typealias for the object type contained in ListMonitor and ObjectMonitor 2017-04-19 17:04:34 +09:00
John Estropia b6bc7c2edf allow edit() calls to any DynamicObject 2017-04-18 18:14:16 +09:00
John Estropia 1938f0d9de no need to be optional 2017-04-18 12:09:37 +09:00
John Estropia 94e6db669f added a way to lazily-initialize user info data 2017-04-18 12:02:39 +09:00
John Estropia b1972b82f1 added way to store userInfo in DataStack and in transactions 2017-04-18 11:29:16 +09:00
John Rommel Estropia 5ffaca1375 WIP: docs 2017-04-18 08:02:07 +09:00
John Estropia a73306fecb check correct queue for managed object value access 2017-04-12 19:22:18 +09:00
John Estropia 9f3db61ff7 WIP: allow migrations for CoreStoreObjects 2017-04-11 19:13:35 +09:00
John Estropia e5ef4992d3 WIP: migrations for CoreStoreObjects 2017-04-07 21:57:45 +09:00
John Estropia c0ae129b22 NSManagedObject features are now fully supported for CoreStoreObject types. MacOSX 10.12 onwards now support ListMonitors and ObjectMonitors 2017-04-07 20:14:13 +09:00
John Estropia 4aa1a63f9a updated unit tests with easier to understand examples 2017-04-06 20:45:54 +09:00
John Estropia 4fc10afe1e relationships done! 2017-04-06 20:28:17 +09:00
John Estropia 8b77e4e5a0 WIP: prototype for ManagedObjectProtocol 2017-04-05 21:56:19 +09:00
John Estropia 258c237100 It works! (WIP!) 2017-04-04 20:25:40 +09:00
John Estropia 6948db516d WIP: typesafe queries 2017-04-03 22:02:24 +09:00
John Estropia b5d80fd272 WIP: Dynamic models (Goodbye xcdatamodel files!) 2017-04-03 21:41:25 +09:00
John Rommel Estropia cdcd7d0416 removed deprecated functions in unit tests and demo app 2017-04-01 23:08:36 +09:00
John Rommel Estropia a5162239d5 kick-off deprecations with 4.0 version bump 2017-04-01 21:23:50 +09:00
John Rommel Estropia 1ad6ac5769 accept NSManagedObject as Where predicate argument 2017-04-01 17:15:24 +09:00
John Rommel Estropia ecb3d0cfa0 revert prototyped stack setup 2017-04-01 01:31:40 +09:00
John Estropia 81355fd9c5 update travis file for Xcode 8.3 2017-03-31 19:56:19 +09:00
John Estropia 97d7a276fe new auto-commit transaction methods 2017-03-31 19:44:18 +09:00
John Estropia d72d1afe8b Merge branch 'develop' into corestore4_develop
# Conflicts:
#	CoreStore.xcodeproj/project.pbxproj
2017-03-28 11:38:02 +09:00
John Estropia 716e069984 version bump 2017-03-28 11:25:24 +09:00
John Estropia 881ee4af0a remove warnings when calling unsafeBitCast() 2017-03-28 10:54:08 +09:00
John Estropia acc0ce1c32 Merge pull request #157 from ruslanskorb/fetched-objects-dynamicCast
Use `dynamicCast()` to cast `fetchedObjects` to `[T]?` without checking the type.
2017-03-28 10:10:23 +09:00
John Estropia b97f6d6a0a Merge branch 'prototype/mainContextToPSC' into corestore4_develop 2017-03-27 14:08:07 +09:00
John Rommel Estropia 818abfc1a1 WIP 2017-03-27 01:48:51 +09:00
Ruslan Skorb f055c54a66 [ListMonitor] Use dynamicCast() to cast fetchedObjects to [T]? without checking the type.
Type checking takes a time. So there is a performance problem when casting a large number of fetched objects to an array of a certain type using the operator `as?`.
2017-03-24 21:30:46 +02:00
John Estropia cb6d5b015b WIP: prototyping new transaction structure 2017-03-24 21:15:51 +09:00
John Rommel Estropia 494965de23 changed protocol naming 2017-03-10 08:30:12 +09:00
John Estropia fe25a9aa36 remove warnings when calling unsafeBitCast() 2017-03-09 18:59:37 +09:00
John Estropia f21e4e12e0 Merge branch 'develop' into corestore4_develop 2017-03-09 18:08:35 +09:00
John Estropia 92890d1e1d Merge pull request #154 from ruslanskorb/remove-duplicate-code
[FetchedResultsControllerDelegate] Remove duplicate code.
2017-03-07 12:58:18 +09:00
Ruslan Skorb 0c483e0e19 [FetchedResultsControllerDelegate] Remove duplicate code. 2017-03-05 12:20:12 +02:00
John Rommel Estropia 6055685c00 WIP: compilable again now 2017-03-02 22:26:45 +09:00
John Rommel Estropia ee71442b08 Merge branch 'develop' into corestore4_develop 2017-03-02 21:07:30 +09:00
John Estropia 341ec5e771 reverted unsafeDowncast usage to unsafeBitCast (It worked differently) 2017-03-01 19:56:13 +09:00
John Estropia 8569c3c524 WIP: rehaul of type-safe fetching and querying 2017-03-01 19:34:07 +09:00
John Estropia 3224fcf71d move type safety goodness 2017-02-21 19:17:06 +09:00
John Rommel Estropia 9ff1c9d545 declare operators as static functions 2017-02-19 20:05:23 +09:00
John Rommel Estropia c40d17a6ad Merge branch 'develop' of github.com:JohnEstropia/CoreStore into develop 2017-02-19 09:56:40 +09:00
John Estropia 9d5e04854a extra extra type safety for attributes (fetching and importing) 2017-02-17 14:13:16 +09:00
John Estropia d2fd03c1f0 Where clauses are now more strict with the argument types 2017-02-17 10:47:38 +09:00
John Rommel Estropia 7baaee493d unit test for scalar IDs for ImportableUniqueObject 2017-02-13 22:17:03 +09:00
John Estropia 03973790a8 revert recent swizzling update. sorry about that 2017-02-10 20:33:07 +09:00
John Estropia 7ee027f44d Test mainContext fetching directly from PSC. Needs benchmarking esp. with merging 2017-02-09 10:49:50 +09:00
223 changed files with 24290 additions and 11082 deletions
+2
View File
@@ -0,0 +1,2 @@
try:
project: 'CoreStore.xcworkspace'
+1 -1
View File
@@ -1 +1 @@
3.0.1 4.0
+21 -15
View File
@@ -1,5 +1,5 @@
language: objective-c language: objective-c
osx_image: xcode8.2 osx_image: xcode9
sudo: false sudo: false
git: git:
submodules: false submodules: false
@@ -10,23 +10,29 @@ env:
- LC_CTYPE=en_US.UTF-8 - LC_CTYPE=en_US.UTF-8
- LANG=en_US.UTF-8 - LANG=en_US.UTF-8
matrix: matrix:
- DESTINATION="OS=10.1,name=iPhone 7" SCHEME="CoreStore iOS" SDK=iphonesimulator10.2 RUN_TESTS="YES" POD_LINT="NO" - DESTINATION="OS=11.0,name=iPhone 8" SCHEME="CoreStore iOS" SDK=iphonesimulator11.0 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=9.0,name=iPhone 6 Plus" SCHEME="CoreStore iOS" SDK=iphonesimulator10.2 RUN_TESTS="YES" POD_LINT="NO" - DESTINATION="OS=10.3,name=iPhone 7" SCHEME="CoreStore iOS" SDK=iphonesimulator11.0 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=8.4,name=iPhone 6" SCHEME="CoreStore iOS" SDK=iphonesimulator10.2 RUN_TESTS="YES" POD_LINT="NO" - DESTINATION="OS=10.1,name=iPhone 7" SCHEME="CoreStore iOS" SDK=iphonesimulator11.0 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=8.3,name=iPhone 5S" SCHEME="CoreStore iOS" SDK=iphonesimulator10.2 RUN_TESTS="YES" POD_LINT="NO" - DESTINATION="OS=9.0,name=iPhone 6 Plus" SCHEME="CoreStore iOS" SDK=iphonesimulator11.0 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=8.3,name=iPhone 5" SCHEME="CoreStore iOS" SDK=iphonesimulator10.2 RUN_TESTS="YES" POD_LINT="NO" - DESTINATION="OS=8.4,name=iPhone 6" SCHEME="CoreStore iOS" SDK=iphonesimulator11.0 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=8.3,name=iPhone 4S" SCHEME="CoreStore iOS" SDK=iphonesimulator10.2 RUN_TESTS="YES" POD_LINT="NO" - DESTINATION="OS=8.3,name=iPhone 5S" SCHEME="CoreStore iOS" SDK=iphonesimulator11.0 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="arch=x86_64" SCHEME="CoreStore OSX" SDK=macosx10.12 RUN_TESTS="YES" POD_LINT="NO" - DESTINATION="OS=8.3,name=iPhone 5" SCHEME="CoreStore iOS" SDK=iphonesimulator11.0 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=3.1,name=Apple Watch - 42mm" SCHEME="CoreStore watchOS" SDK=watchsimulator3.1 RUN_TESTS="NO" POD_LINT="NO" - DESTINATION="OS=8.3,name=iPhone 4S" SCHEME="CoreStore iOS" SDK=iphonesimulator11.0 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=2.2,name=Apple Watch - 42mm" SCHEME="CoreStore watchOS" SDK=watchsimulator3.1 RUN_TESTS="NO" POD_LINT="NO" - DESTINATION="arch=x86_64" SCHEME="CoreStore OSX" SDK=macosx10.13 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=10.1,name=Apple TV 1080p" SCHEME="CoreStore tvOS" SDK=appletvsimulator10.1 RUN_TESTS="YES" POD_LINT="NO" - DESTINATION="OS=4.0,name=Apple Watch - 42mm" SCHEME="CoreStore watchOS" SDK=watchsimulator4.0 RUN_TESTS="NO" POD_LINT="NO"
- DESTINATION="OS=9.2,name=Apple TV 1080p" SCHEME="CoreStore tvOS" SDK=appletvsimulator10.1 RUN_TESTS="YES" POD_LINT="NO" - DESTINATION="OS=3.2,name=Apple Watch - 42mm" SCHEME="CoreStore watchOS" SDK=watchsimulator4.0 RUN_TESTS="NO" POD_LINT="NO"
- DESTINATION="OS=2.2,name=Apple Watch - 42mm" SCHEME="CoreStore watchOS" SDK=watchsimulator4.0 RUN_TESTS="NO" POD_LINT="NO"
- DESTINATION="OS=11.0,name=Apple TV 1080p" SCHEME="CoreStore tvOS" SDK=appletvsimulator11.0 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=10.2,name=Apple TV 1080p" SCHEME="CoreStore tvOS" SDK=appletvsimulator11.0 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=9.2,name=Apple TV 1080p" SCHEME="CoreStore tvOS" SDK=appletvsimulator11.0 RUN_TESTS="YES" POD_LINT="NO"
before_install: before_install:
- gem install cocoapods --no-rdoc --no-ri --no-document --quiet - gem install cocoapods --no-rdoc --no-ri --no-document --quiet
- gem install xcpretty --no-rdoc --no-ri --no-document --quiet - gem install xcpretty --no-rdoc --no-ri --no-document --quiet
- curl -OlL "https://github.com/Carthage/Carthage/releases/download/0.18/Carthage.pkg" - curl -OlL "https://github.com/Carthage/Carthage/releases/download/0.26.0/Carthage.pkg"
- sudo installer -pkg "Carthage.pkg" -target / - sudo installer -pkg "Carthage.pkg" -target /
- rm "Carthage.pkg" - rm "Carthage.pkg"
- npm install ios-sim -g
- ios-sim start --devicetypeid "com.apple.CoreSimulator.SimDeviceType.iPhone-8, 11.0"
before_script: before_script:
- carthage update --use-submodules - carthage update --use-submodules
script: script:
@@ -37,8 +43,8 @@ script:
xcodebuild -workspace CoreStore.xcworkspace -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c; xcodebuild -workspace CoreStore.xcworkspace -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c;
xcodebuild -workspace CoreStore.xcworkspace -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Release ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c; xcodebuild -workspace CoreStore.xcworkspace -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Release ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c;
fi fi
- xcodebuild -workspace "CoreStore.xcworkspace" -scheme "CoreStore iOS" -sdk "iphonesimulator10.2" -destination "OS=10.1,name=iPhone 7" -configuration Debug ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c; - xcodebuild -workspace "CoreStore.xcworkspace" -scheme "CoreStoreDemo" -sdk "iphonesimulator11.0" -destination "OS=11.0,name=iPhone 8" -configuration Debug ONLY_ACTIVE_ARCH=NO build | xcpretty -c;
- xcodebuild -workspace "CoreStore.xcworkspace" -scheme "CoreStore iOS" -sdk "iphonesimulator10.2" -destination "OS=10.1,name=iPhone 7" -configuration Release ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c; - xcodebuild -workspace "CoreStore.xcworkspace" -scheme "CoreStoreDemo" -sdk "iphonesimulator11.0" -destination "OS=11.0,name=iPhone 8" -configuration Release ONLY_ACTIVE_ARCH=NO build | xcpretty -c;
- if [ $POD_LINT == "YES" ]; then - if [ $POD_LINT == "YES" ]; then
pod lib lint --quick; pod lib lint --quick;
fi fi
+2 -4
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = "CoreStore" s.name = "CoreStore"
s.version = "3.0.2" s.version = "5.0.0"
s.license = "MIT" s.license = "MIT"
s.summary = "Unleashing the real power of Core Data with the elegance and safety of Swift" s.summary = "Unleashing the real power of Core Data with the elegance and safety of Swift"
s.homepage = "https://github.com/JohnEstropia/CoreStore" s.homepage = "https://github.com/JohnEstropia/CoreStore"
@@ -16,7 +16,5 @@ Pod::Spec.new do |s|
s.public_header_files = "Sources/**/*.h" s.public_header_files = "Sources/**/*.h"
s.frameworks = "Foundation", "CoreData" s.frameworks = "Foundation", "CoreData"
s.requires_arc = true s.requires_arc = true
s.pod_target_xcconfig = { 'OTHER_SWIFT_FLAGS[config=Debug]' => '-D USE_FRAMEWORKS -D DEBUG', s.pod_target_xcconfig = { 'OTHER_SWIFT_FLAGS[config=Debug]' => '-D DEBUG' }
'OTHER_SWIFT_FLAGS[config=Release]' => '-D USE_FRAMEWORKS',
'GCC_PREPROCESSOR_DEFINITIONS' => 'USE_FRAMEWORKS=1' }
end end
File diff suppressed because it is too large Load Diff
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0800" LastUpgradeVersion = "0900"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
@@ -26,6 +26,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<Testables> <Testables>
<TestableReference <TestableReference
@@ -55,6 +56,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0800" LastUpgradeVersion = "0900"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
@@ -40,6 +40,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<Testables> <Testables>
<TestableReference <TestableReference
@@ -63,12 +64,18 @@
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
<AdditionalOptions> <AdditionalOptions>
<AdditionalOption
key = "NSZombieEnabled"
value = "YES"
isEnabled = "YES">
</AdditionalOption>
</AdditionalOptions> </AdditionalOptions>
</TestAction> </TestAction>
<LaunchAction <LaunchAction
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0800" LastUpgradeVersion = "0900"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
@@ -26,6 +26,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<Testables> <Testables>
<TestableReference <TestableReference
@@ -55,6 +56,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0800" LastUpgradeVersion = "0900"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
@@ -26,6 +26,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<Testables> <Testables>
</Testables> </Testables>
@@ -36,6 +37,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
@@ -12,7 +12,6 @@
B503FAE11AFDC71700F90881 /* Palette.swift in Sources */ = {isa = PBXBuildFile; fileRef = B503FADD1AFDC71700F90881 /* Palette.swift */; }; B503FAE11AFDC71700F90881 /* Palette.swift in Sources */ = {isa = PBXBuildFile; fileRef = B503FADD1AFDC71700F90881 /* Palette.swift */; };
B503FAE21AFDC71700F90881 /* PaletteTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B503FADE1AFDC71700F90881 /* PaletteTableViewCell.swift */; }; B503FAE21AFDC71700F90881 /* PaletteTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B503FADE1AFDC71700F90881 /* PaletteTableViewCell.swift */; };
B5125C121B521B78003A42C7 /* OrganismV2ToV3.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = B5125C111B521B78003A42C7 /* OrganismV2ToV3.xcmappingmodel */; }; B5125C121B521B78003A42C7 /* OrganismV2ToV3.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = B5125C111B521B78003A42C7 /* OrganismV2ToV3.xcmappingmodel */; };
B5125C141B521BA7003A42C7 /* OrganismV3ToV2.xcmappingmodel in Sources */ = {isa = PBXBuildFile; fileRef = B5125C131B521BA7003A42C7 /* OrganismV3ToV2.xcmappingmodel */; };
B52977D91B120B80003D50A5 /* ObserversViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52977D81B120B80003D50A5 /* ObserversViewController.swift */; }; B52977D91B120B80003D50A5 /* ObserversViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52977D81B120B80003D50A5 /* ObserversViewController.swift */; };
B52977DD1B120F3B003D50A5 /* TransactionsDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52977DC1B120F3B003D50A5 /* TransactionsDemoViewController.swift */; }; B52977DD1B120F3B003D50A5 /* TransactionsDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52977DC1B120F3B003D50A5 /* TransactionsDemoViewController.swift */; };
B52977DF1B120F83003D50A5 /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B52977DE1B120F83003D50A5 /* MapKit.framework */; }; B52977DF1B120F83003D50A5 /* MapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B52977DE1B120F83003D50A5 /* MapKit.framework */; };
@@ -266,12 +265,12 @@
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastSwiftUpdateCheck = 0700; LastSwiftUpdateCheck = 0700;
LastUpgradeCheck = 0800; LastUpgradeCheck = 0900;
ORGANIZATIONNAME = "John Rommel Estropia"; ORGANIZATIONNAME = "John Rommel Estropia";
TargetAttributes = { TargetAttributes = {
B54AAD481AF4D26E00848AE0 = { B54AAD481AF4D26E00848AE0 = {
CreatedOnToolsVersion = 6.3; CreatedOnToolsVersion = 6.3;
LastSwiftMigration = 0800; LastSwiftMigration = 0900;
}; };
}; };
}; };
@@ -331,7 +330,6 @@
B503FAE11AFDC71700F90881 /* Palette.swift in Sources */, B503FAE11AFDC71700F90881 /* Palette.swift in Sources */,
B503FAE21AFDC71700F90881 /* PaletteTableViewCell.swift in Sources */, B503FAE21AFDC71700F90881 /* PaletteTableViewCell.swift in Sources */,
B560070F1B3EC90F00A9A8F9 /* OrganismV2ToV3MigrationPolicy.swift in Sources */, B560070F1B3EC90F00A9A8F9 /* OrganismV2ToV3MigrationPolicy.swift in Sources */,
B5125C141B521BA7003A42C7 /* OrganismV3ToV2.xcmappingmodel in Sources */,
B503FADF1AFDC71700F90881 /* ListObserverDemoViewController.swift in Sources */, B503FADF1AFDC71700F90881 /* ListObserverDemoViewController.swift in Sources */,
B54AAD4F1AF4D26E00848AE0 /* AppDelegate.swift in Sources */, B54AAD4F1AF4D26E00848AE0 /* AppDelegate.swift in Sources */,
B56964D71B231AE90075EE4A /* StackSetupDemo.xcdatamodeld in Sources */, B56964D71B231AE90075EE4A /* StackSetupDemo.xcdatamodeld in Sources */,
@@ -373,13 +371,21 @@
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@@ -407,6 +413,8 @@
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 4.0;
}; };
name = Debug; name = Debug;
}; };
@@ -418,13 +426,21 @@
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES; CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES; CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@@ -443,6 +459,8 @@
IPHONEOS_DEPLOYMENT_TARGET = 8.0; IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 4.0;
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
}; };
name = Release; name = Release;
@@ -456,7 +474,8 @@
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.johnestropia.corestore.demo; PRODUCT_BUNDLE_IDENTIFIER = com.johnestropia.corestore.demo;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 3.0; SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 4.0;
}; };
name = Debug; name = Debug;
}; };
@@ -470,7 +489,8 @@
PRODUCT_BUNDLE_IDENTIFIER = com.johnestropia.corestore.demo; PRODUCT_BUNDLE_IDENTIFIER = com.johnestropia.corestore.demo;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 3.0; SWIFT_SWIFT3_OBJC_INFERENCE = Off;
SWIFT_VERSION = 4.0;
}; };
name = Release; name = Release;
}; };
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "0800" LastUpgradeVersion = "0900"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"
@@ -26,6 +26,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES"> shouldUseLaunchSchemeArgsEnv = "YES">
<Testables> <Testables>
</Testables> </Testables>
@@ -45,6 +46,7 @@
buildConfiguration = "Debug" buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0" launchStyle = "0"
useCustomWorkingDirectory = "NO" useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO" ignoresPersistentStateOnLaunch = "NO"
@@ -18,10 +18,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow? var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? = nil) -> Bool {
application.statusBarStyle = .lightContent application.statusBarStyle = .lightContent
return true return true
} }
} }
@@ -1,39 +1,28 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10112" systemVersion="15D21" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="Ni8-QF-XHB"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="Ni8-QF-XHB">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies> <dependencies>
<deployment identifier="iOS"/> <deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10083"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/> <capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/> <capability name="Constraints to layout margins" minToolsVersion="6.0"/>
<capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/> <capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies> </dependencies>
<customFonts key="customFonts"> <customFonts key="customFonts">
<mutableArray key="AppleSDGothicNeo.ttc"> <array key="AppleSDGothicNeo.ttc">
<string>AppleSDGothicNeo-Regular</string> <string>AppleSDGothicNeo-Regular</string>
</mutableArray> </array>
<mutableArray key="HelveticaNeue.ttc"> <array key="HelveticaNeue.ttc">
<string>HelveticaNeue-Bold</string>
<string>HelveticaNeue-Bold</string>
<string>HelveticaNeue</string> <string>HelveticaNeue</string>
<string>HelveticaNeue</string> <string>HelveticaNeue-Bold</string>
</mutableArray> </array>
<mutableArray key="HelveticaNeueLights.ttc"> <array key="HelveticaNeueLights.ttc">
<string>HelveticaNeue-Light</string>
<string>HelveticaNeue-Light</string>
<string>HelveticaNeue-Light</string>
<string>HelveticaNeue-Light</string> <string>HelveticaNeue-Light</string>
<string>HelveticaNeue-Thin</string> <string>HelveticaNeue-Thin</string>
<string>HelveticaNeue-Light</string> </array>
<string>HelveticaNeue-Light</string>
<string>HelveticaNeue-Light</string>
<string>HelveticaNeue-Light</string>
<string>HelveticaNeue-Light</string>
<string>HelveticaNeue-Light</string>
<string>HelveticaNeue-Light</string>
<string>HelveticaNeue-Light</string>
<string>HelveticaNeue-Light</string>
<string>HelveticaNeue-Light</string>
</mutableArray>
</customFonts> </customFonts>
<scenes> <scenes>
<!--SNS Accounts--> <!--SNS Accounts-->
@@ -41,34 +30,34 @@
<objects> <objects>
<tableViewController id="AW4-lY-JNk" customClass="StackSetupDemoViewController" customModule="CoreStoreDemo" customModuleProvider="target" sceneMemberID="viewController"> <tableViewController id="AW4-lY-JNk" customClass="StackSetupDemoViewController" customModule="CoreStoreDemo" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="S3A-lm-AuA"> <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="S3A-lm-AuA">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.15542715787887573" green="0.2203737199306488" blue="0.2959403395652771" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="separatorColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="separatorColor" red="0.15542715787887573" green="0.2203737199306488" blue="0.2959403395652771" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<view key="tableHeaderView" contentMode="scaleToFill" id="yud-WH-MPa"> <view key="tableHeaderView" contentMode="scaleToFill" id="yud-WH-MPa">
<rect key="frame" x="0.0" y="64" width="600" height="150"/> <rect key="frame" x="0.0" y="0.0" width="375" height="150"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="sns" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="czc-nd-es9"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="sns" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="czc-nd-es9">
<rect key="frame" x="20" y="20" width="560" height="26.5"/> <rect key="frame" x="20" y="20" width="335" height="26.5"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="22"/> <fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="22"/>
<color key="textColor" red="0.92549019610000005" green="0.94117647059999998" blue="0.94509803920000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.90744441747665405" green="0.9265514612197876" blue="0.93116652965545654" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="name" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1eh-91-O2N"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="name" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1eh-91-O2N">
<rect key="frame" x="20" y="70" width="41" height="20.5"/> <rect key="frame" x="20" y="70" width="41" height="20.5"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="17"/> <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="17"/>
<color key="textColor" red="0.92549019610000005" green="0.94117647059999998" blue="0.94509803920000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.90744441747665405" green="0.9265514612197876" blue="0.93116652965545654" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="friends" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="p2y-0T-hQs"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="friends" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="p2y-0T-hQs">
<rect key="frame" x="539.5" y="72" width="40.5" height="17"/> <rect key="frame" x="314.5" y="72" width="40.5" height="17"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="14"/> <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="14"/>
<color key="textColor" red="0.92549019610000005" green="0.94117647059999998" blue="0.94509803920000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.90744441747665405" green="0.9265514612197876" blue="0.93116652965545654" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
<color key="backgroundColor" red="0.20392156862745098" green="0.28627450980392155" blue="0.36862745098039218" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.15542715787887573" green="0.2203737199306488" blue="0.2959403395652771" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstItem="1eh-91-O2N" firstAttribute="top" secondItem="czc-nd-es9" secondAttribute="bottom" constant="23.5" id="F6q-Jt-glI"/> <constraint firstItem="1eh-91-O2N" firstAttribute="top" secondItem="czc-nd-es9" secondAttribute="bottom" constant="23.5" id="F6q-Jt-glI"/>
<constraint firstItem="p2y-0T-hQs" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="1eh-91-O2N" secondAttribute="trailing" constant="20" id="GVS-6o-rmj"/> <constraint firstItem="p2y-0T-hQs" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="1eh-91-O2N" secondAttribute="trailing" constant="20" id="GVS-6o-rmj"/>
@@ -82,24 +71,24 @@
</view> </view>
<prototypes> <prototypes>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="UITableViewCell" textLabel="8b8-lM-Krq" detailTextLabel="hR1-Zb-BOk" style="IBUITableViewCellStyleValue1" id="dMt-nx-EO5"> <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="UITableViewCell" textLabel="8b8-lM-Krq" detailTextLabel="hR1-Zb-BOk" style="IBUITableViewCellStyleValue1" id="dMt-nx-EO5">
<rect key="frame" x="0.0" y="236" width="600" height="44"/> <rect key="frame" x="0.0" y="172" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="dMt-nx-EO5" id="gdK-VV-zNb"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="dMt-nx-EO5" id="gdK-VV-zNb">
<rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/> <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="8b8-lM-Krq"> <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="8b8-lM-Krq">
<rect key="frame" x="15" y="13" width="28.5" height="19"/> <rect key="frame" x="15" y="13" width="28.5" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="16"/> <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="16"/>
<color key="textColor" red="0.17254901959999999" green="0.24313725489999999" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Subtitle" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="hR1-Zb-BOk"> <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Subtitle" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="hR1-Zb-BOk">
<rect key="frame" x="533" y="13" width="52" height="19"/> <rect key="frame" x="308" y="13" width="52" height="19"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="16"/> <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="16"/>
<color key="textColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="calibratedRGB"/> <color key="textColor" red="0.55686274509803924" green="0.55686274509803924" blue="0.57647058823529407" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
@@ -131,23 +120,23 @@
<viewControllerLayoutGuide type="bottom" id="t9A-zf-Iew"/> <viewControllerLayoutGuide type="bottom" id="t9A-zf-Iew"/>
</layoutGuides> </layoutGuides>
<view key="view" contentMode="scaleToFill" id="75P-2m-6cr"> <view key="view" contentMode="scaleToFill" id="75P-2m-6cr">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="WUc-3Y-Quw"> <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="WUc-3Y-Quw">
<rect key="frame" x="0.0" y="324" width="600" height="276"/> <rect key="frame" x="0.0" y="324" width="375" height="343"/>
<color key="backgroundColor" red="0.93725490196078431" green="0.93725490196078431" blue="0.95686274509803926" alpha="1" colorSpace="calibratedRGB"/> <color key="backgroundColor" red="0.93725490196078431" green="0.93725490196078431" blue="0.95686274509803926" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="separatorColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="separatorColor" red="0.15542715787887573" green="0.2203737199306488" blue="0.2959403395652771" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<prototypes> <prototypes>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="OrganismTableViewCell" id="WVb-th-o8c" customClass="OrganismTableViewCell" customModule="CoreStoreDemo" customModuleProvider="target"> <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="OrganismTableViewCell" id="WVb-th-o8c" customClass="OrganismTableViewCell" customModule="CoreStoreDemo" customModuleProvider="target">
<rect key="frame" x="0.0" y="22" width="600" height="44"/> <rect key="frame" x="0.0" y="22" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="WVb-th-o8c" id="JBq-Ml-a9p"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="WVb-th-o8c" id="JBq-Ml-a9p">
<rect key="frame" x="0.0" y="0.0" width="600" height="43.5"/> <rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="OQf-Bd-Zze"> <button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="OQf-Bd-Zze">
<rect key="frame" x="520" y="8" width="72" height="27.5"/> <rect key="frame" x="295" y="8" width="72" height="27.5"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/> <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<inset key="contentEdgeInsets" minX="7" minY="0.0" maxX="7" maxY="0.0"/> <inset key="contentEdgeInsets" minX="7" minY="0.0" maxX="7" maxY="0.0"/>
<state key="normal" title="mutate!"/> <state key="normal" title="mutate!"/>
@@ -156,9 +145,9 @@
</connections> </connections>
</button> </button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VZk-6K-4ut"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VZk-6K-4ut">
<rect key="frame" x="15" y="8" width="495" height="27.5"/> <rect key="frame" x="15" y="8" width="270" height="27.5"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Thin" family="Helvetica Neue" pointSize="17"/> <fontDescription key="fontDescription" name="HelveticaNeue-Thin" family="Helvetica Neue" pointSize="17"/>
<color key="textColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.15542715787887573" green="0.2203737199306488" blue="0.2959403395652771" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
@@ -184,42 +173,42 @@
</connections> </connections>
</tableView> </tableView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XKA-Ub-c2X"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XKA-Ub-c2X">
<rect key="frame" x="0.0" y="64" width="600" height="260"/> <rect key="frame" x="0.0" y="64" width="375" height="260"/>
<subviews> <subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="i7U-bW-juB" customClass="UIButton"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="i7U-bW-juB" customClass="UIButton">
<rect key="frame" x="0.0" y="0.0" width="600" height="260"/> <rect key="frame" x="0.0" y="0.0" width="375" height="260"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Organism" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zxy-nY-P44"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Organism" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="zxy-nY-P44">
<rect key="frame" x="20" y="90.5" width="560" height="26.5"/> <rect key="frame" x="20" y="90.5" width="335" height="26.5"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="22"/> <fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="22"/>
<color key="textColor" red="0.92549019610000005" green="0.94117647059999998" blue="0.94509803920000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.90744441747665405" green="0.9265514612197876" blue="0.93116652965545654" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="attributes" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6Ac-xl-ldZ"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="attributes" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="6Ac-xl-ldZ">
<rect key="frame" x="20" y="131.5" width="560" height="20.5"/> <rect key="frame" x="20" y="131.5" width="335" height="20.5"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="17"/> <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="17"/>
<color key="textColor" red="0.92549019610000005" green="0.94117647059999998" blue="0.94509803920000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.90744441747665405" green="0.9265514612197876" blue="0.93116652965545654" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="rAZ-eJ-sxy"> <segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="rAZ-eJ-sxy">
<rect key="frame" x="20" y="20" width="560" height="29"/> <rect key="frame" x="20" y="20" width="335" height="29"/>
<segments> <segments>
<segment title="First"/> <segment title="First"/>
<segment title="Second"/> <segment title="Second"/>
<segment title=""/> <segment title=""/>
</segments> </segments>
<color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/> <color key="tintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections> <connections>
<action selector="segmentedControlValueChanged:" destination="iVv-Vc-nCL" eventType="valueChanged" id="RwG-kW-RPg"/> <action selector="segmentedControlValueChanged:" destination="iVv-Vc-nCL" eventType="valueChanged" id="RwG-kW-RPg"/>
</connections> </connections>
</segmentedControl> </segmentedControl>
<progressView opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" verticalHuggingPriority="750" progress="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="869-wx-Odb"> <progressView opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" verticalHuggingPriority="750" progress="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="869-wx-Odb">
<rect key="frame" x="20" y="68" width="560" height="2"/> <rect key="frame" x="20" y="68" width="335" height="2"/>
<color key="progressTintColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/> <color key="progressTintColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="trackTintColor" white="1" alpha="0.20000000000000001" colorSpace="calibratedWhite"/> <color key="trackTintColor" red="1" green="1" blue="1" alpha="0.20000000000000001" colorSpace="custom" customColorSpace="sRGB"/>
</progressView> </progressView>
</subviews> </subviews>
<color key="backgroundColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.15542715787887573" green="0.2203737199306488" blue="0.2959403395652771" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstItem="zxy-nY-P44" firstAttribute="leading" secondItem="i7U-bW-juB" secondAttribute="leading" constant="20" id="1u1-Tq-hRn"/> <constraint firstItem="zxy-nY-P44" firstAttribute="leading" secondItem="i7U-bW-juB" secondAttribute="leading" constant="20" id="1u1-Tq-hRn"/>
<constraint firstItem="6Ac-xl-ldZ" firstAttribute="top" secondItem="zxy-nY-P44" secondAttribute="bottom" constant="14.5" id="39B-9l-O3g"/> <constraint firstItem="6Ac-xl-ldZ" firstAttribute="top" secondItem="zxy-nY-P44" secondAttribute="bottom" constant="14.5" id="39B-9l-O3g"/>
@@ -237,7 +226,7 @@
</constraints> </constraints>
</view> </view>
</subviews> </subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="260" id="90R-Mf-iAB"/> <constraint firstAttribute="height" constant="260" id="90R-Mf-iAB"/>
<constraint firstItem="i7U-bW-juB" firstAttribute="leading" secondItem="XKA-Ub-c2X" secondAttribute="leading" id="e9h-f4-c37"/> <constraint firstItem="i7U-bW-juB" firstAttribute="leading" secondItem="XKA-Ub-c2X" secondAttribute="leading" id="e9h-f4-c37"/>
@@ -246,7 +235,7 @@
</constraints> </constraints>
</view> </view>
</subviews> </subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstAttribute="trailing" secondItem="XKA-Ub-c2X" secondAttribute="trailing" id="U0P-51-KT9"/> <constraint firstAttribute="trailing" secondItem="XKA-Ub-c2X" secondAttribute="trailing" id="U0P-51-KT9"/>
<constraint firstAttribute="trailing" secondItem="WUc-3Y-Quw" secondAttribute="trailing" id="i7I-7B-97K"/> <constraint firstAttribute="trailing" secondItem="WUc-3Y-Quw" secondAttribute="trailing" id="i7I-7B-97K"/>
@@ -276,31 +265,31 @@
<objects> <objects>
<tableViewController id="t0d-B0-B7U" sceneMemberID="viewController"> <tableViewController id="t0d-B0-B7U" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="50" sectionHeaderHeight="10" sectionFooterHeight="10" id="uHB-Yr-ujV"> <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="static" style="grouped" separatorStyle="default" rowHeight="50" sectionHeaderHeight="10" sectionFooterHeight="10" id="uHB-Yr-ujV">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.93725490196078431" green="0.93725490196078431" blue="0.95686274509803926" alpha="1" colorSpace="calibratedRGB"/> <color key="backgroundColor" red="0.93725490196078431" green="0.93725490196078431" blue="0.95686274509803926" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<sections> <sections>
<tableViewSection id="wIP-Hn-YfF"> <tableViewSection id="wIP-Hn-YfF">
<cells> <cells>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="Q3n-Df-v1t" detailTextLabel="Hbn-cf-Y7m" style="IBUITableViewCellStyleSubtitle" id="AXm-KE-45G"> <tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="Q3n-Df-v1t" detailTextLabel="Hbn-cf-Y7m" style="IBUITableViewCellStyleSubtitle" id="AXm-KE-45G">
<rect key="frame" x="0.0" y="99" width="600" height="50"/> <rect key="frame" x="0.0" y="35" width="375" height="50"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="AXm-KE-45G" id="9te-Wx-hkf"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="AXm-KE-45G" id="9te-Wx-hkf">
<rect key="frame" x="0.0" y="0.0" width="567" height="49.5"/> <rect key="frame" x="0.0" y="0.0" width="342" height="49.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Accounts" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Q3n-Df-v1t"> <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Accounts" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Q3n-Df-v1t">
<rect key="frame" x="15" y="6" width="82" height="24"/> <rect key="frame" x="15" y="6" width="82" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="20"/> <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="20"/>
<color key="textColor" red="0.17254901960784313" green="0.24313725490196078" blue="0.31372549019607843" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Setting up multiple persistent store configurations" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Hbn-cf-Y7m"> <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Setting up multiple persistent store configurations" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Hbn-cf-Y7m">
<rect key="frame" x="15" y="30" width="263.5" height="13.5"/> <rect key="frame" x="15" y="30" width="263.5" height="13.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="11"/> <fontDescription key="fontDescription" type="system" pointSize="11"/>
<color key="textColor" red="0.17254901959999999" green="0.24313725489999999" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
@@ -310,24 +299,24 @@
</connections> </connections>
</tableViewCell> </tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="vpt-cT-gMo" detailTextLabel="ou9-TZ-8bf" style="IBUITableViewCellStyleSubtitle" id="fsb-zw-8Ii"> <tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="vpt-cT-gMo" detailTextLabel="ou9-TZ-8bf" style="IBUITableViewCellStyleSubtitle" id="fsb-zw-8Ii">
<rect key="frame" x="0.0" y="149" width="600" height="50"/> <rect key="frame" x="0.0" y="85" width="375" height="50"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="fsb-zw-8Ii" id="Upm-AO-Fw3"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="fsb-zw-8Ii" id="Upm-AO-Fw3">
<rect key="frame" x="0.0" y="0.0" width="567" height="49.5"/> <rect key="frame" x="0.0" y="0.0" width="342" height="49.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Colors" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="vpt-cT-gMo"> <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Colors" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="vpt-cT-gMo">
<rect key="frame" x="15" y="6" width="56" height="24"/> <rect key="frame" x="15" y="6" width="56" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="20"/> <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="20"/>
<color key="textColor" red="0.17254901959999999" green="0.24313725489999999" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Observing list changes and single object changes" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="ou9-TZ-8bf"> <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Observing list changes and single object changes" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="ou9-TZ-8bf">
<rect key="frame" x="15" y="30" width="260.5" height="13.5"/> <rect key="frame" x="15" y="30" width="260.5" height="13.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="11"/> <fontDescription key="fontDescription" type="system" pointSize="11"/>
<color key="textColor" red="0.17254901959999999" green="0.24313725489999999" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
@@ -337,24 +326,24 @@
</connections> </connections>
</tableViewCell> </tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="UbU-Kd-yrY" detailTextLabel="uP1-Jc-o9v" style="IBUITableViewCellStyleSubtitle" id="ekW-PJ-mbo"> <tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="UbU-Kd-yrY" detailTextLabel="uP1-Jc-o9v" style="IBUITableViewCellStyleSubtitle" id="ekW-PJ-mbo">
<rect key="frame" x="0.0" y="199" width="600" height="50"/> <rect key="frame" x="0.0" y="135" width="375" height="50"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="ekW-PJ-mbo" id="CYq-mg-PVS"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="ekW-PJ-mbo" id="CYq-mg-PVS">
<rect key="frame" x="0.0" y="0.0" width="567" height="49.5"/> <rect key="frame" x="0.0" y="0.0" width="342" height="49.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Placemarks" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="UbU-Kd-yrY"> <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Placemarks" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="UbU-Kd-yrY">
<rect key="frame" x="15" y="6" width="100.5" height="24"/> <rect key="frame" x="15" y="6" width="100.5" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="20"/> <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="20"/>
<color key="textColor" red="0.17254901959999999" green="0.24313725489999999" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Making changes with transactions" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="uP1-Jc-o9v"> <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Making changes with transactions" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="uP1-Jc-o9v">
<rect key="frame" x="15" y="30" width="179" height="13.5"/> <rect key="frame" x="15" y="30" width="179" height="13.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="11"/> <fontDescription key="fontDescription" type="system" pointSize="11"/>
<color key="textColor" red="0.17254901959999999" green="0.24313725489999999" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
@@ -364,24 +353,24 @@
</connections> </connections>
</tableViewCell> </tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="C8Y-0y-lEG" detailTextLabel="jZw-qE-0ws" style="IBUITableViewCellStyleSubtitle" id="ph1-8z-C1m"> <tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="C8Y-0y-lEG" detailTextLabel="jZw-qE-0ws" style="IBUITableViewCellStyleSubtitle" id="ph1-8z-C1m">
<rect key="frame" x="0.0" y="249" width="600" height="50"/> <rect key="frame" x="0.0" y="185" width="375" height="50"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="ph1-8z-C1m" id="nNz-rd-ksg"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="ph1-8z-C1m" id="nNz-rd-ksg">
<rect key="frame" x="0.0" y="0.0" width="567" height="49.5"/> <rect key="frame" x="0.0" y="0.0" width="342" height="49.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Time Zones" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="C8Y-0y-lEG"> <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Time Zones" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="C8Y-0y-lEG">
<rect key="frame" x="15" y="6" width="101.5" height="24"/> <rect key="frame" x="15" y="6" width="101.5" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="20"/> <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="20"/>
<color key="textColor" red="0.17254901959999999" green="0.24313725489999999" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Fetching objects and raw values" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="jZw-qE-0ws"> <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Fetching objects and raw values" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="jZw-qE-0ws">
<rect key="frame" x="15" y="30" width="168.5" height="13.5"/> <rect key="frame" x="15" y="30" width="168.5" height="13.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="11"/> <fontDescription key="fontDescription" type="system" pointSize="11"/>
<color key="textColor" red="0.17254901959999999" green="0.24313725489999999" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
@@ -391,24 +380,24 @@
</connections> </connections>
</tableViewCell> </tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="ZfY-Aq-Ykq" detailTextLabel="QzD-9b-k1j" style="IBUITableViewCellStyleSubtitle" id="wyK-rk-3tI"> <tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="ZfY-Aq-Ykq" detailTextLabel="QzD-9b-k1j" style="IBUITableViewCellStyleSubtitle" id="wyK-rk-3tI">
<rect key="frame" x="0.0" y="299" width="600" height="50"/> <rect key="frame" x="0.0" y="235" width="375" height="50"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="wyK-rk-3tI" id="fLd-KK-QcW"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="wyK-rk-3tI" id="fLd-KK-QcW">
<rect key="frame" x="0.0" y="0.0" width="567" height="49.5"/> <rect key="frame" x="0.0" y="0.0" width="342" height="49.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Logger" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="ZfY-Aq-Ykq"> <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Logger" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="ZfY-Aq-Ykq">
<rect key="frame" x="15" y="6" width="61" height="24"/> <rect key="frame" x="15" y="6" width="61" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="20"/> <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="20"/>
<color key="textColor" red="0.17254901959999999" green="0.24313725489999999" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Implementing a custom logger" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="QzD-9b-k1j"> <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Implementing a custom logger" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="QzD-9b-k1j">
<rect key="frame" x="15" y="30" width="159" height="13.5"/> <rect key="frame" x="15" y="30" width="159" height="13.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="11"/> <fontDescription key="fontDescription" type="system" pointSize="11"/>
<color key="textColor" red="0.17254901959999999" green="0.24313725489999999" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
@@ -418,24 +407,24 @@
</connections> </connections>
</tableViewCell> </tableViewCell>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="hSG-mG-YBw" detailTextLabel="X9P-TQ-LYh" style="IBUITableViewCellStyleSubtitle" id="xTM-Cf-0if"> <tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" textLabel="hSG-mG-YBw" detailTextLabel="X9P-TQ-LYh" style="IBUITableViewCellStyleSubtitle" id="xTM-Cf-0if">
<rect key="frame" x="0.0" y="349" width="600" height="50"/> <rect key="frame" x="0.0" y="285" width="375" height="50"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="xTM-Cf-0if" id="DfO-BW-krd"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="xTM-Cf-0if" id="DfO-BW-krd">
<rect key="frame" x="0.0" y="0.0" width="567" height="49.5"/> <rect key="frame" x="0.0" y="0.0" width="342" height="49.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Evolution" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="hSG-mG-YBw"> <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Evolution" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="hSG-mG-YBw">
<rect key="frame" x="15" y="6" width="78.5" height="24"/> <rect key="frame" x="15" y="6" width="78.5" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="20"/> <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="20"/>
<color key="textColor" red="0.17254901959999999" green="0.24313725489999999" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Migrating and de-migrating stores" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="X9P-TQ-LYh"> <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Migrating and de-migrating stores" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="X9P-TQ-LYh">
<rect key="frame" x="15" y="30" width="179.5" height="13.5"/> <rect key="frame" x="15" y="30" width="179.5" height="13.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="11"/> <fontDescription key="fontDescription" type="system" pointSize="11"/>
<color key="textColor" red="0.17254901959999999" green="0.24313725489999999" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
@@ -467,69 +456,69 @@
<viewControllerLayoutGuide type="bottom" id="aI4-O3-OCi"/> <viewControllerLayoutGuide type="bottom" id="aI4-O3-OCi"/>
</layoutGuides> </layoutGuides>
<view key="view" contentMode="scaleToFill" id="w8K-eN-RvU"> <view key="view" contentMode="scaleToFill" id="w8K-eN-RvU">
<rect key="frame" x="0.0" y="0.0" width="592" height="268"/> <rect key="frame" x="0.0" y="0.0" width="375" height="301.5"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<view contentMode="scaleToFill" verticalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="NhC-oM-bkd"> <view contentMode="scaleToFill" verticalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="NhC-oM-bkd">
<rect key="frame" x="20" y="69.5" width="552" height="36.5"/> <rect key="frame" x="16" y="89.5" width="343" height="50"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" id="TIX-qi-B34"/> <constraint firstAttribute="height" relation="greaterThanOrEqual" id="TIX-qi-B34"/>
</constraints> </constraints>
</view> </view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="250" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Vfe-Yq-3Xa"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="250" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" adjustsLetterSpacingToFitWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Vfe-Yq-3Xa">
<rect key="frame" x="20" y="116" width="552" height="18"/> <rect key="frame" x="16" y="149.5" width="343" height="18"/>
<constraints> <constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" id="4h9-ha-EzR"/> <constraint firstAttribute="height" relation="greaterThanOrEqual" id="4h9-ha-EzR"/>
</constraints> </constraints>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/> <fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/> <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Hue" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sgg-Md-Nf3"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Hue" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sgg-Md-Nf3">
<rect key="frame" x="20" y="154" width="74" height="18"/> <rect key="frame" x="16" y="187.5" width="74" height="18"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/> <fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/> <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Saturation" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rry-vh-bRK"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Saturation" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rry-vh-bRK">
<rect key="frame" x="20" y="192" width="74" height="18"/> <rect key="frame" x="16" y="225.5" width="74" height="18"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/> <fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/> <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Brightness" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vTa-ly-eyO"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Brightness" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="vTa-ly-eyO">
<rect key="frame" x="20" y="230" width="74" height="18"/> <rect key="frame" x="16" y="263.5" width="74" height="18"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/> <fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/> <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" minValue="0.0" maxValue="359" translatesAutoresizingMaskIntoConstraints="NO" id="YQ6-fq-3Wb"> <slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" minValue="0.0" maxValue="359" translatesAutoresizingMaskIntoConstraints="NO" id="YQ6-fq-3Wb">
<rect key="frame" x="102" y="148" width="472" height="31"/> <rect key="frame" x="98" y="181.5" width="263" height="31"/>
<connections> <connections>
<action selector="hueSliderValueDidChange:" destination="dX3-kR-CYC" eventType="valueChanged" id="9Hy-3h-llE"/> <action selector="hueSliderValueDidChange:" destination="dX3-kR-CYC" eventType="valueChanged" id="9Hy-3h-llE"/>
</connections> </connections>
</slider> </slider>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="xXz-78-tAd"> <slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="1" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="xXz-78-tAd">
<rect key="frame" x="102" y="186" width="472" height="31"/> <rect key="frame" x="98" y="219.5" width="263" height="31"/>
<connections> <connections>
<action selector="saturationSliderValueDidChange:" destination="dX3-kR-CYC" eventType="valueChanged" id="qtU-ua-ZTc"/> <action selector="saturationSliderValueDidChange:" destination="dX3-kR-CYC" eventType="valueChanged" id="qtU-ua-ZTc"/>
</connections> </connections>
</slider> </slider>
<slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="hpy-2d-eOP"> <slider opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" value="0.5" minValue="0.0" maxValue="1" translatesAutoresizingMaskIntoConstraints="NO" id="hpy-2d-eOP">
<rect key="frame" x="102" y="224" width="472" height="31"/> <rect key="frame" x="98" y="257.5" width="263" height="31"/>
<connections> <connections>
<action selector="brightnessSliderValueDidChange:" destination="dX3-kR-CYC" eventType="valueChanged" id="F09-EP-2iD"/> <action selector="brightnessSliderValueDidChange:" destination="dX3-kR-CYC" eventType="valueChanged" id="F09-EP-2iD"/>
</connections> </connections>
</slider> </slider>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="p4O-tf-dgt"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="p4O-tf-dgt">
<rect key="frame" x="20" y="49" width="552" height="20.5"/> <rect key="frame" x="16" y="69" width="343" height="20.5"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/> <fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/> <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstItem="p4O-tf-dgt" firstAttribute="trailing" secondItem="w8K-eN-RvU" secondAttribute="trailingMargin" id="022-ll-1s1"/> <constraint firstItem="p4O-tf-dgt" firstAttribute="trailing" secondItem="w8K-eN-RvU" secondAttribute="trailingMargin" id="022-ll-1s1"/>
<constraint firstItem="vTa-ly-eyO" firstAttribute="top" secondItem="rry-vh-bRK" secondAttribute="bottom" constant="20" id="1hd-Ti-CnT"/> <constraint firstItem="vTa-ly-eyO" firstAttribute="top" secondItem="rry-vh-bRK" secondAttribute="bottom" constant="20" id="1hd-Ti-CnT"/>
@@ -591,23 +580,23 @@
<viewControllerLayoutGuide type="bottom" id="LNL-mj-D7l"/> <viewControllerLayoutGuide type="bottom" id="LNL-mj-D7l"/>
</layoutGuides> </layoutGuides>
<view key="view" contentMode="scaleToFill" id="6x3-vn-Egt"> <view key="view" contentMode="scaleToFill" id="6x3-vn-Egt">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="L5f-tW-lXf"> <containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="L5f-tW-lXf">
<rect key="frame" x="4" y="64" width="592" height="268"/> <rect key="frame" x="0.0" y="64" width="375" height="301.5"/>
<connections> <connections>
<segue destination="5Fw-je-9gI" kind="embed" id="YcI-2Z-ijV"/> <segue destination="5Fw-je-9gI" kind="embed" id="YcI-2Z-ijV"/>
</connections> </connections>
</containerView> </containerView>
<containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="6So-f3-4Gp"> <containerView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="6So-f3-4Gp">
<rect key="frame" x="4" y="332" width="592" height="268"/> <rect key="frame" x="0.0" y="365.5" width="375" height="301.5"/>
<connections> <connections>
<segue destination="sll-yo-mBc" kind="embed" id="AAl-HS-dq2"/> <segue destination="sll-yo-mBc" kind="embed" id="AAl-HS-dq2"/>
</connections> </connections>
</containerView> </containerView>
</subviews> </subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstItem="6So-f3-4Gp" firstAttribute="top" secondItem="L5f-tW-lXf" secondAttribute="bottom" id="3m8-tj-Nd4"/> <constraint firstItem="6So-f3-4Gp" firstAttribute="top" secondItem="L5f-tW-lXf" secondAttribute="bottom" id="3m8-tj-Nd4"/>
<constraint firstAttribute="trailingMargin" secondItem="6So-f3-4Gp" secondAttribute="trailing" constant="-16" id="4L8-wZ-F59"/> <constraint firstAttribute="trailingMargin" secondItem="6So-f3-4Gp" secondAttribute="trailing" constant="-16" id="4L8-wZ-F59"/>
@@ -636,11 +625,11 @@
<navigationBar key="navigationBar" contentMode="scaleToFill" id="00L-5k-Eno"> <navigationBar key="navigationBar" contentMode="scaleToFill" id="00L-5k-Eno">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/> <rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<color key="tintColor" red="0.92549019610000005" green="0.94117647059999998" blue="0.94509803920000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="tintColor" red="0.90744441747665405" green="0.9265514612197876" blue="0.93116652965545654" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="barTintColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="barTintColor" red="0.15542715787887573" green="0.2203737199306488" blue="0.2959403395652771" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<textAttributes key="titleTextAttributes"> <textAttributes key="titleTextAttributes">
<fontDescription key="fontDescription" name="HelveticaNeue-UltraLight" family="Helvetica Neue" pointSize="24"/> <fontDescription key="fontDescription" name="HelveticaNeue-UltraLight" family="Helvetica Neue" pointSize="24"/>
<color key="textColor" red="0.92549019607843142" green="0.94117647058823528" blue="0.94509803921568625" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.90744441747665405" green="0.9265514612197876" blue="0.93116652965545654" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</textAttributes> </textAttributes>
</navigationBar> </navigationBar>
<nil name="viewControllers"/> <nil name="viewControllers"/>
@@ -657,19 +646,19 @@
<objects> <objects>
<tableViewController id="3AE-ED-0oj" customClass="ListObserverDemoViewController" customModule="CoreStoreDemo" customModuleProvider="target" sceneMemberID="viewController"> <tableViewController id="3AE-ED-0oj" customClass="ListObserverDemoViewController" customModule="CoreStoreDemo" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="DAz-BE-6Ca"> <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="DAz-BE-6Ca">
<rect key="frame" x="0.0" y="0.0" width="592" height="268"/> <rect key="frame" x="0.0" y="0.0" width="375" height="301.5"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<prototypes> <prototypes>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="PaletteTableViewCell" id="G3X-70-BCD" customClass="PaletteTableViewCell" customModule="CoreStoreDemo" customModuleProvider="target"> <tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="PaletteTableViewCell" id="G3X-70-BCD" customClass="PaletteTableViewCell" customModule="CoreStoreDemo" customModuleProvider="target">
<rect key="frame" x="0.0" y="66" width="592" height="44"/> <rect key="frame" x="0.0" y="22" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="G3X-70-BCD" id="aT8-nz-i5l"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="G3X-70-BCD" id="aT8-nz-i5l">
<rect key="frame" x="0.0" y="0.0" width="559" height="43.5"/> <rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="uQX-PI-UWF"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="uQX-PI-UWF">
<rect key="frame" x="8" y="8" width="27" height="27"/> <rect key="frame" x="8" y="8" width="27" height="27"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstAttribute="width" secondItem="uQX-PI-UWF" secondAttribute="height" multiplier="1:1" id="9qA-iN-Neb"/> <constraint firstAttribute="width" secondItem="uQX-PI-UWF" secondAttribute="height" multiplier="1:1" id="9qA-iN-Neb"/>
</constraints> </constraints>
@@ -677,7 +666,7 @@
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HJC-5w-lIN"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HJC-5w-lIN">
<rect key="frame" x="45" y="8" width="34.5" height="27"/> <rect key="frame" x="45" y="8" width="34.5" height="27"/>
<fontDescription key="fontDescription" name="HelveticaNeue" family="Helvetica Neue" pointSize="14"/> <fontDescription key="fontDescription" name="HelveticaNeue" family="Helvetica Neue" pointSize="14"/>
<color key="textColor" red="0.17254901959999999" green="0.24313725489999999" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
@@ -716,20 +705,20 @@
<objects> <objects>
<tableViewController id="lCE-i6-UCT" customClass="ListObserverDemoViewController" customModule="CoreStoreDemo" customModuleProvider="target" sceneMemberID="viewController"> <tableViewController id="lCE-i6-UCT" customClass="ListObserverDemoViewController" customModule="CoreStoreDemo" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="Zba-8M-Zd7"> <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="Zba-8M-Zd7">
<rect key="frame" x="0.0" y="0.0" width="592" height="268"/> <rect key="frame" x="0.0" y="0.0" width="375" height="301.5"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<prototypes> <prototypes>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="PaletteTableViewCell" id="zSO-3e-OVq" customClass="PaletteTableViewCell" customModule="CoreStoreDemo" customModuleProvider="target"> <tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="PaletteTableViewCell" id="zSO-3e-OVq" customClass="PaletteTableViewCell" customModule="CoreStoreDemo" customModuleProvider="target">
<rect key="frame" x="0.0" y="66" width="592" height="44"/> <rect key="frame" x="0.0" y="22" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="zSO-3e-OVq" id="cHA-by-n4b"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="zSO-3e-OVq" id="cHA-by-n4b">
<rect key="frame" x="0.0" y="0.0" width="559" height="43.5"/> <rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5uq-Yi-XwH"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5uq-Yi-XwH">
<rect key="frame" x="8" y="8" width="27" height="27"/> <rect key="frame" x="8" y="8" width="27" height="27"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstAttribute="width" secondItem="5uq-Yi-XwH" secondAttribute="height" multiplier="1:1" id="oOe-HC-VyN"/> <constraint firstAttribute="width" secondItem="5uq-Yi-XwH" secondAttribute="height" multiplier="1:1" id="oOe-HC-VyN"/>
</constraints> </constraints>
@@ -737,7 +726,7 @@
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Zyu-PC-WmO"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Zyu-PC-WmO">
<rect key="frame" x="45" y="8" width="34.5" height="27"/> <rect key="frame" x="45" y="8" width="34.5" height="27"/>
<fontDescription key="fontDescription" name="HelveticaNeue" family="Helvetica Neue" pointSize="14"/> <fontDescription key="fontDescription" name="HelveticaNeue" family="Helvetica Neue" pointSize="14"/>
<color key="textColor" red="0.17254901959999999" green="0.24313725489999999" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
@@ -780,11 +769,11 @@
<navigationBar key="navigationBar" contentMode="scaleToFill" id="6XA-6M-yvZ"> <navigationBar key="navigationBar" contentMode="scaleToFill" id="6XA-6M-yvZ">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/> <rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<color key="tintColor" red="0.74117647060000003" green="0.76470588240000004" blue="0.78039215689999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="tintColor" red="0.68773996829986572" green="0.71417498588562012" blue="0.73246318101882935" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="barTintColor" red="0.17254901959999999" green="0.24313725489999999" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="barTintColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<textAttributes key="titleTextAttributes"> <textAttributes key="titleTextAttributes">
<fontDescription key="fontDescription" name="HelveticaNeue-Thin" family="Helvetica Neue" pointSize="20"/> <fontDescription key="fontDescription" name="HelveticaNeue-Thin" family="Helvetica Neue" pointSize="20"/>
<color key="textColor" red="0.74117647060000003" green="0.76470588240000004" blue="0.78039215689999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.68773996829986572" green="0.71417498588562012" blue="0.73246318101882935" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</textAttributes> </textAttributes>
</navigationBar> </navigationBar>
<nil name="viewControllers"/> <nil name="viewControllers"/>
@@ -805,19 +794,19 @@
<viewControllerLayoutGuide type="bottom" id="RZg-hi-T8O"/> <viewControllerLayoutGuide type="bottom" id="RZg-hi-T8O"/>
</layoutGuides> </layoutGuides>
<view key="view" contentMode="scaleToFill" id="k4s-iL-Krh"> <view key="view" contentMode="scaleToFill" id="k4s-iL-Krh">
<rect key="frame" x="0.0" y="64" width="600" height="536"/> <rect key="frame" x="0.0" y="64" width="375" height="603"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<mapView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" mapType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="V2U-0R-Ts0"> <mapView verifyAmbiguity="ignoreSizes" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" ambiguous="YES" mapType="standard" translatesAutoresizingMaskIntoConstraints="NO" id="V2U-0R-Ts0">
<rect key="frame" x="0.0" y="0.0" width="600" height="536"/> <rect key="frame" x="0.0" y="0.0" width="375" height="558"/>
<connections> <connections>
<outlet property="delegate" destination="jPl-fH-NlD" id="Sjn-YC-haS"/> <outlet property="delegate" destination="jPl-fH-NlD" id="Sjn-YC-haS"/>
</connections> </connections>
</mapView> </mapView>
</subviews> </subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstItem="RZg-hi-T8O" firstAttribute="top" secondItem="V2U-0R-Ts0" secondAttribute="bottom" id="GcS-Jz-Wcm"/> <constraint firstItem="RZg-hi-T8O" firstAttribute="top" secondItem="V2U-0R-Ts0" secondAttribute="bottom" id="N9r-9J-68d"/>
<constraint firstItem="V2U-0R-Ts0" firstAttribute="top" secondItem="k4s-iL-Krh" secondAttribute="top" id="S5Z-Da-V6J"/> <constraint firstItem="V2U-0R-Ts0" firstAttribute="top" secondItem="k4s-iL-Krh" secondAttribute="top" id="S5Z-Da-V6J"/>
<constraint firstAttribute="trailing" secondItem="V2U-0R-Ts0" secondAttribute="trailing" id="YPc-RK-5ib"/> <constraint firstAttribute="trailing" secondItem="V2U-0R-Ts0" secondAttribute="trailing" id="YPc-RK-5ib"/>
<constraint firstItem="V2U-0R-Ts0" firstAttribute="leading" secondItem="k4s-iL-Krh" secondAttribute="leading" id="hk5-Rz-FyU"/> <constraint firstItem="V2U-0R-Ts0" firstAttribute="leading" secondItem="k4s-iL-Krh" secondAttribute="leading" id="hk5-Rz-FyU"/>
@@ -831,7 +820,7 @@
</viewController> </viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="YnG-TD-zxQ" userLabel="First Responder" sceneMemberID="firstResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="YnG-TD-zxQ" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects> </objects>
<point key="canvasLocation" x="3694" y="2020"/> <point key="canvasLocation" x="3693.5" y="2019.5"/>
</scene> </scene>
<!--Logger--> <!--Logger-->
<scene sceneID="n7W-0g-bbY"> <scene sceneID="n7W-0g-bbY">
@@ -842,30 +831,30 @@
<viewControllerLayoutGuide type="bottom" id="Cwn-Jd-4Lr"/> <viewControllerLayoutGuide type="bottom" id="Cwn-Jd-4Lr"/>
</layoutGuides> </layoutGuides>
<view key="view" contentMode="scaleToFill" id="n9M-Je-Dj0"> <view key="view" contentMode="scaleToFill" id="n9M-Je-Dj0">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" directionalLockEnabled="YES" alwaysBounceVertical="YES" indicatorStyle="white" editable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="TpK-gX-CTN"> <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" directionalLockEnabled="YES" alwaysBounceVertical="YES" indicatorStyle="white" editable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="TpK-gX-CTN">
<rect key="frame" x="0.0" y="142" width="600" height="458"/> <rect key="frame" x="0.0" y="142" width="375" height="525"/>
<color key="backgroundColor" red="0.20392156862745098" green="0.28627450980392155" blue="0.36862745098039218" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.15542715787887573" green="0.2203737199306488" blue="0.2959403395652771" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="textColor" red="0.94509803921568625" green="0.7686274509803922" blue="0.058823529411764705" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.92706394195556641" green="0.72759377956390381" blue="0.064024783670902252" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="fontDescription" name="AppleSDGothicNeo-Regular" family="Apple SD Gothic Neo" pointSize="15"/> <fontDescription key="fontDescription" name="AppleSDGothicNeo-Regular" family="Apple SD Gothic Neo" pointSize="15"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/> <textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView> </textView>
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="bordered" momentary="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4iq-B4-k0p"> <segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="bordered" momentary="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4iq-B4-k0p">
<rect key="frame" x="20" y="94" width="560" height="29"/> <rect key="frame" x="20" y="94" width="335" height="29"/>
<segments> <segments>
<segment title="Log"/> <segment title="Log"/>
<segment title="Error"/> <segment title="Error"/>
<segment title="Assert"/> <segment title="Assert"/>
</segments> </segments>
<color key="tintColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="tintColor" red="0.15542715787887573" green="0.2203737199306488" blue="0.2959403395652771" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections> <connections>
<action selector="segmentedControlValueChanged:" destination="5yy-0N-QDU" eventType="valueChanged" id="2pp-Vt-2Os"/> <action selector="segmentedControlValueChanged:" destination="5yy-0N-QDU" eventType="valueChanged" id="2pp-Vt-2Os"/>
</connections> </connections>
</segmentedControl> </segmentedControl>
</subviews> </subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstItem="4iq-B4-k0p" firstAttribute="leading" secondItem="n9M-Je-Dj0" secondAttribute="leading" constant="20" id="6SM-eN-NxP"/> <constraint firstItem="4iq-B4-k0p" firstAttribute="leading" secondItem="n9M-Je-Dj0" secondAttribute="leading" constant="20" id="6SM-eN-NxP"/>
<constraint firstItem="TpK-gX-CTN" firstAttribute="leading" secondItem="n9M-Je-Dj0" secondAttribute="leading" id="AAw-IG-taz"/> <constraint firstItem="TpK-gX-CTN" firstAttribute="leading" secondItem="n9M-Je-Dj0" secondAttribute="leading" id="AAw-IG-taz"/>
@@ -901,11 +890,11 @@
<navigationBar key="navigationBar" contentMode="scaleToFill" id="wJo-mp-1pS"> <navigationBar key="navigationBar" contentMode="scaleToFill" id="wJo-mp-1pS">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/> <rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<color key="tintColor" red="0.74117647058823533" green="0.76470588235294112" blue="0.7803921568627451" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="tintColor" red="0.68773996829986572" green="0.71417498588562012" blue="0.73246318101882935" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="barTintColor" red="0.17254901960784313" green="0.24313725490196078" blue="0.31372549019607843" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="barTintColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<textAttributes key="titleTextAttributes"> <textAttributes key="titleTextAttributes">
<fontDescription key="fontDescription" name="HelveticaNeue-Thin" family="Helvetica Neue" pointSize="20"/> <fontDescription key="fontDescription" name="HelveticaNeue-Thin" family="Helvetica Neue" pointSize="20"/>
<color key="textColor" red="0.74117647060000003" green="0.76470588240000004" blue="0.78039215689999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.68773996829986572" green="0.71417498588562012" blue="0.73246318101882935" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</textAttributes> </textAttributes>
</navigationBar> </navigationBar>
<nil name="viewControllers"/> <nil name="viewControllers"/>
@@ -926,30 +915,30 @@
<viewControllerLayoutGuide type="bottom" id="DlN-cN-JXd"/> <viewControllerLayoutGuide type="bottom" id="DlN-cN-JXd"/>
</layoutGuides> </layoutGuides>
<view key="view" contentMode="scaleToFill" id="eC3-ql-d2o"> <view key="view" contentMode="scaleToFill" id="eC3-ql-d2o">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="WGY-kX-mAx"> <tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" translatesAutoresizingMaskIntoConstraints="NO" id="WGY-kX-mAx">
<rect key="frame" x="0.0" y="64" width="600" height="536"/> <rect key="frame" x="0.0" y="64" width="375" height="603"/>
<color key="backgroundColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.15542715787887573" green="0.2203737199306488" blue="0.2959403395652771" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="separatorColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="separatorColor" red="0.15542715787887573" green="0.2203737199306488" blue="0.2959403395652771" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<view key="tableHeaderView" contentMode="scaleToFill" id="iaH-1W-Sbo"> <view key="tableHeaderView" contentMode="scaleToFill" id="iaH-1W-Sbo">
<rect key="frame" x="0.0" y="0.0" width="600" height="80"/> <rect key="frame" x="0.0" y="0.0" width="375" height="80"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="bordered" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="YVj-dA-fyV"> <segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="bordered" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="YVj-dA-fyV">
<rect key="frame" x="20" y="26" width="560" height="29"/> <rect key="frame" x="20" y="26" width="335" height="29"/>
<segments> <segments>
<segment title="Fetch"/> <segment title="Fetch"/>
<segment title="Query"/> <segment title="Query"/>
</segments> </segments>
<color key="tintColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="tintColor" red="0.15542715787887573" green="0.2203737199306488" blue="0.2959403395652771" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<connections> <connections>
<action selector="segmentedControlValueChanged:" destination="qbj-MK-nIY" eventType="valueChanged" id="Wok-dl-uq7"/> <action selector="segmentedControlValueChanged:" destination="qbj-MK-nIY" eventType="valueChanged" id="Wok-dl-uq7"/>
</connections> </connections>
</segmentedControl> </segmentedControl>
</subviews> </subviews>
<color key="backgroundColor" white="1" alpha="0.80000000000000004" colorSpace="calibratedWhite"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="0.80000000000000004" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstItem="YVj-dA-fyV" firstAttribute="leading" secondItem="iaH-1W-Sbo" secondAttribute="leading" constant="20" id="1dX-t7-dyR"/> <constraint firstItem="YVj-dA-fyV" firstAttribute="leading" secondItem="iaH-1W-Sbo" secondAttribute="leading" constant="20" id="1dX-t7-dyR"/>
<constraint firstAttribute="centerY" secondItem="YVj-dA-fyV" secondAttribute="centerY" id="HXU-Z7-jfu"/> <constraint firstAttribute="centerY" secondItem="YVj-dA-fyV" secondAttribute="centerY" id="HXU-Z7-jfu"/>
@@ -958,17 +947,17 @@
</view> </view>
<prototypes> <prototypes>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="UITableViewCell" textLabel="6db-P0-6Ms" style="IBUITableViewCellStyleDefault" id="vUr-WV-qur"> <tableViewCell contentMode="scaleToFill" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="UITableViewCell" textLabel="6db-P0-6Ms" style="IBUITableViewCellStyleDefault" id="vUr-WV-qur">
<rect key="frame" x="0.0" y="102" width="600" height="44"/> <rect key="frame" x="0.0" y="102" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="vUr-WV-qur" id="Vr0-hE-cn9"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="vUr-WV-qur" id="Vr0-hE-cn9">
<rect key="frame" x="0.0" y="0.0" width="567" height="43.5"/> <rect key="frame" x="0.0" y="0.0" width="342" height="43.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="6db-P0-6Ms"> <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="Title" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="6db-P0-6Ms">
<rect key="frame" x="15" y="0.0" width="550" height="43.5"/> <rect key="frame" x="15" y="0.0" width="325" height="43.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="18"/> <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="18"/>
<color key="textColor" red="0.17254901959999999" green="0.24313725489999999" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
@@ -981,7 +970,7 @@
</connections> </connections>
</tableView> </tableView>
</subviews> </subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstItem="WGY-kX-mAx" firstAttribute="top" secondItem="dgu-PC-LiB" secondAttribute="bottom" id="0pS-cU-ibk"/> <constraint firstItem="WGY-kX-mAx" firstAttribute="top" secondItem="dgu-PC-LiB" secondAttribute="bottom" id="0pS-cU-ibk"/>
<constraint firstAttribute="trailing" secondItem="WGY-kX-mAx" secondAttribute="trailing" id="NeY-g5-CaB"/> <constraint firstAttribute="trailing" secondItem="WGY-kX-mAx" secondAttribute="trailing" id="NeY-g5-CaB"/>
@@ -1006,30 +995,30 @@
<objects> <objects>
<tableViewController id="tIs-pN-OgO" customClass="FetchingResultsViewController" customModule="CoreStoreDemo" customModuleProvider="target" sceneMemberID="viewController"> <tableViewController id="tIs-pN-OgO" customClass="FetchingResultsViewController" customModule="CoreStoreDemo" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="60" sectionHeaderHeight="36" sectionFooterHeight="22" id="tVl-tT-UDk"> <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="60" sectionHeaderHeight="36" sectionFooterHeight="22" id="tVl-tT-UDk">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.15542715787887573" green="0.2203737199306488" blue="0.2959403395652771" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="separatorColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="separatorColor" red="0.15542715787887573" green="0.2203737199306488" blue="0.2959403395652771" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<prototypes> <prototypes>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="UITableViewCell" textLabel="RgX-yK-1L2" detailTextLabel="QZ4-A2-x4h" style="IBUITableViewCellStyleSubtitle" id="uBt-Iy-nWP"> <tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="UITableViewCell" textLabel="RgX-yK-1L2" detailTextLabel="QZ4-A2-x4h" style="IBUITableViewCellStyleSubtitle" id="uBt-Iy-nWP">
<rect key="frame" x="0.0" y="100" width="600" height="60"/> <rect key="frame" x="0.0" y="36" width="375" height="60"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="uBt-Iy-nWP" id="6SD-ur-9zp"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="uBt-Iy-nWP" id="6SD-ur-9zp">
<rect key="frame" x="0.0" y="0.0" width="600" height="59.5"/> <rect key="frame" x="0.0" y="0.0" width="375" height="59.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="name" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="RgX-yK-1L2"> <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="name" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="RgX-yK-1L2">
<rect key="frame" x="15" y="11" width="48.5" height="24"/> <rect key="frame" x="15" y="11" width="48.5" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="20"/> <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="20"/>
<color key="textColor" red="0.17254901959999999" green="0.24313725489999999" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="offset" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="QZ4-A2-x4h"> <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="offset" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="QZ4-A2-x4h">
<rect key="frame" x="15" y="35" width="31" height="13.5"/> <rect key="frame" x="15" y="35" width="31" height="13.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="11"/> <fontDescription key="fontDescription" type="system" pointSize="11"/>
<color key="textColor" red="0.17254901959999999" green="0.24313725489999999" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
@@ -1052,30 +1041,30 @@
<objects> <objects>
<tableViewController id="o9D-Xm-13g" customClass="QueryingResultsViewController" customModule="CoreStoreDemo" customModuleProvider="target" sceneMemberID="viewController"> <tableViewController id="o9D-Xm-13g" customClass="QueryingResultsViewController" customModule="CoreStoreDemo" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="60" sectionHeaderHeight="36" sectionFooterHeight="22" id="bMh-zR-xwu"> <tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="60" sectionHeaderHeight="36" sectionFooterHeight="22" id="bMh-zR-xwu">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="0.15542715787887573" green="0.2203737199306488" blue="0.2959403395652771" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color key="separatorColor" red="0.20392156859999999" green="0.28627450980000002" blue="0.36862745099999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="separatorColor" red="0.15542715787887573" green="0.2203737199306488" blue="0.2959403395652771" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<prototypes> <prototypes>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="UITableViewCell" textLabel="Syt-QJ-KXg" detailTextLabel="yHS-dP-IKS" style="IBUITableViewCellStyleSubtitle" id="q7Q-aF-Ftl"> <tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" reuseIdentifier="UITableViewCell" textLabel="Syt-QJ-KXg" detailTextLabel="yHS-dP-IKS" style="IBUITableViewCellStyleSubtitle" id="q7Q-aF-Ftl">
<rect key="frame" x="0.0" y="100" width="600" height="60"/> <rect key="frame" x="0.0" y="36" width="375" height="60"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="q7Q-aF-Ftl" id="fc3-eg-yes"> <tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="q7Q-aF-Ftl" id="fc3-eg-yes">
<rect key="frame" x="0.0" y="0.0" width="600" height="59.5"/> <rect key="frame" x="0.0" y="0.0" width="375" height="59.5"/>
<autoresizingMask key="autoresizingMask"/> <autoresizingMask key="autoresizingMask"/>
<subviews> <subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="name" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Syt-QJ-KXg"> <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="name" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Syt-QJ-KXg">
<rect key="frame" x="15" y="11" width="48.5" height="24"/> <rect key="frame" x="15" y="11" width="48.5" height="24"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="20"/> <fontDescription key="fontDescription" name="HelveticaNeue-Light" family="Helvetica Neue" pointSize="20"/>
<color key="textColor" red="0.17254901959999999" green="0.24313725489999999" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="offset" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="yHS-dP-IKS"> <label opaque="NO" multipleTouchEnabled="YES" contentMode="left" text="offset" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="yHS-dP-IKS">
<rect key="frame" x="15" y="35" width="31" height="13.5"/> <rect key="frame" x="15" y="35" width="31" height="13.5"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="11"/> <fontDescription key="fontDescription" type="system" pointSize="11"/>
<color key="textColor" red="0.17254901959999999" green="0.24313725489999999" blue="0.31372549020000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" red="0.13079629838466644" green="0.184075728058815" blue="0.24594299495220184" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
@@ -1,36 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="10169.1" systemVersion="15D21" minimumToolsVersion="Automatic"> <model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="12141" systemVersion="16F73" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
<entity name="Palette" representedClassName="CoreStoreDemo.Palette">
<attribute name="brightness" optional="YES" attributeType="Float" defaultValueString="0.0" syncable="YES"/>
<attribute name="colorName" optional="YES" transient="YES" attributeType="String" syncable="YES"/>
<attribute name="hue" optional="YES" attributeType="Integer 32" defaultValueString="0.0" syncable="YES"/>
<attribute name="saturation" optional="YES" attributeType="Float" defaultValueString="0.0" syncable="YES"/>
<userInfo/>
</entity>
<entity name="Place" representedClassName="CoreStoreDemo.Place" syncable="YES"> <entity name="Place" representedClassName="CoreStoreDemo.Place" syncable="YES">
<attribute name="latitude" optional="YES" attributeType="Double" defaultValueString="0.0" syncable="YES"/> <attribute name="latitude" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="longitude" optional="YES" attributeType="Double" defaultValueString="0.0" syncable="YES"/> <attribute name="longitude" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="subtitle" optional="YES" attributeType="String" syncable="YES"/> <attribute name="subtitle" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="title" optional="YES" attributeType="String" syncable="YES"/> <attribute name="title" optional="YES" attributeType="String" syncable="YES"/>
</entity> </entity>
<entity name="TimeZone" representedClassName="CoreStoreDemo.TimeZone" syncable="YES"> <entity name="TimeZone" representedClassName="CoreStoreDemo.TimeZone" syncable="YES">
<attribute name="abbreviation" optional="YES" attributeType="String" syncable="YES"/> <attribute name="abbreviation" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="daylightSavingTimeOffset" optional="YES" attributeType="Double" defaultValueString="0.0" syncable="YES"/> <attribute name="daylightSavingTimeOffset" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="NO" syncable="YES"/>
<attribute name="hasDaylightSavingTime" optional="YES" attributeType="Boolean" syncable="YES"/> <attribute name="hasDaylightSavingTime" optional="YES" attributeType="Boolean" usesScalarValueType="NO" syncable="YES"/>
<attribute name="name" optional="YES" attributeType="String" syncable="YES"/> <attribute name="name" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="secondsFromGMT" optional="YES" attributeType="Integer 32" defaultValueString="0.0" syncable="YES"/> <attribute name="secondsFromGMT" optional="YES" attributeType="Integer 32" defaultValueString="0.0" usesScalarValueType="NO" syncable="YES"/>
</entity> </entity>
<configuration name="FetchingAndQueryingDemo"> <configuration name="FetchingAndQueryingDemo">
<memberEntity name="TimeZone"/> <memberEntity name="TimeZone"/>
</configuration> </configuration>
<configuration name="ObservingDemo">
<memberEntity name="Palette"/>
</configuration>
<configuration name="TransactionsDemo"> <configuration name="TransactionsDemo">
<memberEntity name="Place"/> <memberEntity name="Place"/>
</configuration> </configuration>
<elements> <elements>
<element name="Palette" positionX="261" positionY="189" width="128" height="105"/>
<element name="Place" positionX="261" positionY="225" width="128" height="105"/> <element name="Place" positionX="261" positionY="225" width="128" height="105"/>
<element name="TimeZone" positionX="297" positionY="270" width="128" height="120"/> <element name="TimeZone" positionX="297" positionY="270" width="128" height="120"/>
</elements> </elements>
@@ -22,8 +22,8 @@ private struct Static {
localStorageOptions: .recreateStoreOnModelMismatch localStorageOptions: .recreateStoreOnModelMismatch
) )
) )
_ = try? dataStack.perform(
_ = dataStack.beginSynchronous { (transaction) -> Void in synchronous: { (transaction) in
transaction.deleteAll(From<TimeZone>()) transaction.deleteAll(From<TimeZone>())
@@ -38,10 +38,8 @@ private struct Static {
cachedTimeZone.hasDaylightSavingTime = rawTimeZone.isDaylightSavingTime cachedTimeZone.hasDaylightSavingTime = rawTimeZone.isDaylightSavingTime
cachedTimeZone.daylightSavingTimeOffset = rawTimeZone.daylightSavingTimeOffset cachedTimeZone.daylightSavingTimeOffset = rawTimeZone.daylightSavingTimeOffset
} }
_ = transaction.commitAndWait()
} }
)
return dataStack return dataStack
}() }()
} }
@@ -167,8 +165,8 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo
fetch: { () -> [TimeZone] in fetch: { () -> [TimeZone] in
return Static.timeZonesStack.fetchAll( return Static.timeZonesStack.fetchAll(
From<TimeZone>(), From<TimeZone>()
OrderBy(.ascending(#keyPath(TimeZone.name))) .orderBy(.ascending(\.name))
)! )!
} }
), ),
@@ -177,9 +175,13 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo
fetch: { () -> [TimeZone] in fetch: { () -> [TimeZone] in
return Static.timeZonesStack.fetchAll( return Static.timeZonesStack.fetchAll(
From<TimeZone>(), From<TimeZone>()
Where("%K BEGINSWITH[c] %@", #keyPath(TimeZone.name), "Asia"), .where(
OrderBy(.ascending(#keyPath(TimeZone.secondsFromGMT))) format: "%K BEGINSWITH[c] %@",
#keyPath(TimeZone.name),
"Asia"
)
.orderBy(.ascending(\.secondsFromGMT))
)! )!
} }
), ),
@@ -188,10 +190,15 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo
fetch: { () -> [TimeZone] in fetch: { () -> [TimeZone] in
return Static.timeZonesStack.fetchAll( return Static.timeZonesStack.fetchAll(
From<TimeZone>(), From<TimeZone>()
Where("%K BEGINSWITH[c] %@", #keyPath(TimeZone.name), "America") .where(
|| Where("%K BEGINSWITH[c] %@", #keyPath(TimeZone.name), "Europe"), format: "%K BEGINSWITH[c] %@ OR %K BEGINSWITH[c] %@",
OrderBy(.ascending(#keyPath(TimeZone.secondsFromGMT))) #keyPath(TimeZone.name),
"America",
#keyPath(TimeZone.name),
"Europe"
)
.orderBy(.ascending(\.secondsFromGMT))
)! )!
} }
), ),
@@ -200,9 +207,13 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo
fetch: { () -> [TimeZone] in fetch: { () -> [TimeZone] in
return Static.timeZonesStack.fetchAll( return Static.timeZonesStack.fetchAll(
From<TimeZone>(), From<TimeZone>()
!Where("%K BEGINSWITH[c] %@", #keyPath(TimeZone.name), "America"), .where(
OrderBy(.ascending(#keyPath(TimeZone.secondsFromGMT))) format: "%K BEGINSWITH[c] %@",
#keyPath(TimeZone.name),
"America"
)
.orderBy(.ascending(\.secondsFromGMT))
)! )!
} }
), ),
@@ -211,23 +222,23 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo
fetch: { () -> [TimeZone] in fetch: { () -> [TimeZone] in
return Static.timeZonesStack.fetchAll( return Static.timeZonesStack.fetchAll(
From<TimeZone>(), From<TimeZone>()
Where("hasDaylightSavingTime", isEqualTo: true), .where(\.hasDaylightSavingTime == true)
OrderBy(.ascending(#keyPath(TimeZone.name))) .orderBy(.ascending(\.name))
)! )!
} }
) )
] ]
private let queryingItems = [ private let queryingItems: [(title: String, query: () -> Any)] = [
( (
title: "Number of Time Zones", title: "Number of Time Zones",
query: { () -> Any in query: { () -> Any in
return Static.timeZonesStack.queryValue( return Static.timeZonesStack.queryValue(
From<TimeZone>(), From<TimeZone>()
Select<NSNumber>(.count(#keyPath(TimeZone.name))) .select(NSNumber.self, .count(\.name))
)! )! as Any
} }
), ),
( (
@@ -235,10 +246,10 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo
query: { () -> Any in query: { () -> Any in
return Static.timeZonesStack.queryValue( return Static.timeZonesStack.queryValue(
From<TimeZone>(), From<TimeZone>()
Select<String>(#keyPath(TimeZone.abbreviation)), .select(String.self, .attribute(\.abbreviation))
Where("%K ENDSWITH[c] %@", #keyPath(TimeZone.name), "Tokyo") .where(format: "%K ENDSWITH[c] %@", #keyPath(TimeZone.name), "Tokyo")
)! )! as Any
} }
), ),
( (
@@ -246,9 +257,13 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo
query: { () -> Any in query: { () -> Any in
return Static.timeZonesStack.queryAttributes( return Static.timeZonesStack.queryAttributes(
From<TimeZone>(), From<TimeZone>()
Select<NSDictionary>(#keyPath(TimeZone.name), #keyPath(TimeZone.abbreviation)), .select(
OrderBy(.ascending(#keyPath(TimeZone.name))) NSDictionary.self,
.attribute(\.name),
.attribute(\.abbreviation)
)
.orderBy(.ascending(\.name))
)! )!
} }
), ),
@@ -257,10 +272,17 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo
query: { () -> Any in query: { () -> Any in
return Static.timeZonesStack.queryAttributes( return Static.timeZonesStack.queryAttributes(
From<TimeZone>(), From<TimeZone>()
Select<NSDictionary>(.count(#keyPath(TimeZone.abbreviation)), #keyPath(TimeZone.abbreviation)), .select(
GroupBy(#keyPath(TimeZone.abbreviation)), NSDictionary.self,
OrderBy(.ascending(#keyPath(TimeZone.secondsFromGMT)), .ascending(#keyPath(TimeZone.name))) .count(\.abbreviation),
.attribute(\.abbreviation)
)
.groupBy(\.abbreviation)
.orderBy(
.ascending(\.secondsFromGMT),
.ascending(\.name)
)
)! )!
} }
), ),
@@ -269,13 +291,17 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo
query: { () -> Any in query: { () -> Any in
return Static.timeZonesStack.queryAttributes( return Static.timeZonesStack.queryAttributes(
From<TimeZone>(), From<TimeZone>()
Select<NSDictionary>( .select(
.count(#keyPath(TimeZone.hasDaylightSavingTime), as: "numberOfCountries"), NSDictionary.self,
#keyPath(TimeZone.hasDaylightSavingTime) .count(\.hasDaylightSavingTime, as: "numberOfCountries"),
), .attribute(\.hasDaylightSavingTime)
GroupBy(#keyPath(TimeZone.hasDaylightSavingTime)), )
OrderBy(.descending(#keyPath(TimeZone.hasDaylightSavingTime))) .groupBy(\.hasDaylightSavingTime)
.orderBy(
.descending(\.hasDaylightSavingTime),
.ascending(\.name)
)
)! )!
} }
) )
+2 -2
View File
@@ -17,11 +17,11 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.0.1</string> <string>4.0.3</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>4</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>UILaunchStoryboardName</key> <key>UILaunchStoryboardName</key>
@@ -10,7 +10,7 @@ import UIKit
import CoreStore import CoreStore
private struct Static { struct ColorsDemo {
enum Filter: String { enum Filter: String {
@@ -28,13 +28,13 @@ private struct Static {
} }
} }
func whereClause() -> Where { func whereClause() -> Where<Palette> {
switch self { switch self {
case .all: return Where(true) case .all: return .init()
case .light: return Where("%K >= %@", #keyPath(Palette.brightness), 0.9) case .light: return (\Palette.brightness >= 0.9)
case .dark: return Where("%K <= %@", #keyPath(Palette.brightness), 0.4) case .dark: return (\Palette.brightness <= 0.4)
} }
} }
} }
@@ -43,24 +43,40 @@ private struct Static {
didSet { didSet {
self.palettes.refetch(self.filter.whereClause()) self.palettes.refetch(
self.filter.whereClause(),
OrderBy<Palette>(.ascending(\.hue))
)
} }
} }
static let stack: DataStack = {
return DataStack(
CoreStoreSchema(
modelVersion: "ColorsDemo",
entities: [
Entity<Palette>("Palette"),
],
versionLock: [
"Palette": [0x8c25aa53c7c90a28, 0xa243a34d25f1a3a7, 0x56565b6935b6055a, 0x4f988bb257bf274f]
]
)
)
}()
static let palettes: ListMonitor<Palette> = { static let palettes: ListMonitor<Palette> = {
try! CoreStore.addStorageAndWait( try! ColorsDemo.stack.addStorageAndWait(
SQLiteStore( SQLiteStore(
fileName: "ColorsDemo.sqlite", fileName: "ColorsDemo.sqlite",
configuration: "ObservingDemo",
localStorageOptions: .recreateStoreOnModelMismatch localStorageOptions: .recreateStoreOnModelMismatch
) )
) )
return ColorsDemo.stack.monitorSectionedList(
return CoreStore.monitorSectionedList( From<Palette>()
From<Palette>(), .sectionBy(\.colorName)
SectionBy(#keyPath(Palette.colorName)), .orderBy(.ascending(\.hue))
OrderBy(.ascending(#keyPath(Palette.hue)))
) )
}() }()
} }
@@ -74,7 +90,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
deinit { deinit {
Static.palettes.removeObserver(self) ColorsDemo.palettes.removeObserver(self)
} }
@@ -95,7 +111,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
] ]
let filterBarButton = UIBarButtonItem( let filterBarButton = UIBarButtonItem(
title: Static.filter.rawValue, title: ColorsDemo.filter.rawValue,
style: .plain, style: .plain,
target: self, target: self,
action: #selector(self.filterBarButtonItemTouched(_:)) action: #selector(self.filterBarButtonItemTouched(_:))
@@ -110,9 +126,9 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
] ]
self.filterBarButton = filterBarButton self.filterBarButton = filterBarButton
Static.palettes.addObserver(self) ColorsDemo.palettes.addObserver(self)
self.setTable(enabled: !Static.palettes.isPendingRefetch) self.setTable(enabled: !ColorsDemo.palettes.isPendingRefetch)
} }
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
@@ -134,19 +150,19 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
override func numberOfSections(in tableView: UITableView) -> Int { override func numberOfSections(in tableView: UITableView) -> Int {
return Static.palettes.numberOfSections() return ColorsDemo.palettes.numberOfSections()
} }
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return Static.palettes.numberOfObjectsInSection(section) return ColorsDemo.palettes.numberOfObjectsInSection(section)
} }
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "PaletteTableViewCell") as! PaletteTableViewCell let cell = tableView.dequeueReusableCell(withIdentifier: "PaletteTableViewCell") as! PaletteTableViewCell
let palette = Static.palettes[indexPath] let palette = ColorsDemo.palettes[indexPath]
cell.colorView?.backgroundColor = palette.color cell.colorView?.backgroundColor = palette.color
cell.label?.text = palette.colorText cell.label?.text = palette.colorText
@@ -162,7 +178,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
self.performSegue( self.performSegue(
withIdentifier: "ObjectObserverDemoViewController", withIdentifier: "ObjectObserverDemoViewController",
sender: Static.palettes[indexPath] sender: ColorsDemo.palettes[indexPath]
) )
} }
@@ -171,12 +187,14 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
switch editingStyle { switch editingStyle {
case .delete: case .delete:
let palette = Static.palettes[indexPath] let palette = ColorsDemo.palettes[indexPath]
CoreStore.beginAsynchronous{ (transaction) -> Void in ColorsDemo.stack.perform(
asynchronous: { (transaction) in
transaction.delete(palette) transaction.delete(palette)
transaction.commit { (result) -> Void in } },
} completion: { _ in }
)
default: default:
break break
@@ -185,7 +203,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return Static.palettes.sectionInfoAtIndex(section).name return ColorsDemo.palettes.sectionInfoAtIndex(section).name
} }
@@ -208,7 +226,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
func listMonitorDidRefetch(_ monitor: ListMonitor<Palette>) { func listMonitorDidRefetch(_ monitor: ListMonitor<Palette>) {
self.filterBarButton?.title = Static.filter.rawValue self.filterBarButton?.title = ColorsDemo.filter.rawValue
self.tableView.reloadData() self.tableView.reloadData()
self.setTable(enabled: true) self.setTable(enabled: true)
} }
@@ -230,7 +248,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
if let cell = self.tableView.cellForRow(at: indexPath) as? PaletteTableViewCell { if let cell = self.tableView.cellForRow(at: indexPath) as? PaletteTableViewCell {
let palette = Static.palettes[indexPath] let palette = ColorsDemo.palettes[indexPath]
cell.colorView?.backgroundColor = palette.color cell.colorView?.backgroundColor = palette.color
cell.label?.text = palette.colorText cell.label?.text = palette.colorText
} }
@@ -263,27 +281,30 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
@IBAction private dynamic func resetBarButtonItemTouched(_ sender: AnyObject?) { @IBAction private dynamic func resetBarButtonItemTouched(_ sender: AnyObject?) {
CoreStore.beginAsynchronous { (transaction) -> Void in ColorsDemo.stack.perform(
asynchronous: { (transaction) in
transaction.deleteAll(From<Palette>()) transaction.deleteAll(From<Palette>())
transaction.commit() },
} completion: { _ in }
)
} }
@IBAction private dynamic func filterBarButtonItemTouched(_ sender: AnyObject?) { @IBAction private dynamic func filterBarButtonItemTouched(_ sender: AnyObject?) {
Static.filter = Static.filter.next() ColorsDemo.filter = ColorsDemo.filter.next()
} }
@IBAction private dynamic func addBarButtonItemTouched(_ sender: AnyObject?) { @IBAction private dynamic func addBarButtonItemTouched(_ sender: AnyObject?) {
CoreStore.beginAsynchronous { (transaction) -> Void in ColorsDemo.stack.perform(
asynchronous: { (transaction) in
let palette = transaction.create(Into<Palette>()) let palette = transaction.create(Into<Palette>())
palette.setInitialValues() palette.setInitialValues(in: transaction)
},
transaction.commit() completion: { _ in }
} )
} }
private func setTable(enabled: Bool) { private func setTable(enabled: Bool) {
@@ -29,7 +29,7 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
if let palette = newValue { if let palette = newValue {
self.monitor = CoreStore.monitorObject(palette) self.monitor = ColorsDemo.stack.monitorObject(palette)
} }
else { else {
@@ -50,22 +50,22 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
if let palette = CoreStore.fetchOne(From<Palette>(), OrderBy(.ascending(#keyPath(Palette.hue)))) { if let palette = ColorsDemo.stack.fetchOne(From<Palette>().orderBy(.ascending(\.hue))) {
self.monitor = CoreStore.monitorObject(palette) self.monitor = ColorsDemo.stack.monitorObject(palette)
} }
else { else {
CoreStore.beginSynchronous { (transaction) -> Void in _ = try? ColorsDemo.stack.perform(
synchronous: { (transaction) in
let palette = transaction.create(Into(Palette.self)) let palette = transaction.create(Into<Palette>())
palette.setInitialValues() palette.setInitialValues(in: transaction)
_ = transaction.commitAndWait()
} }
)
let palette = CoreStore.fetchOne(From<Palette>(), OrderBy(.ascending(#keyPath(Palette.hue))))! let palette = ColorsDemo.stack.fetchOne(From<Palette>().orderBy(.ascending(\.hue)))!
self.monitor = CoreStore.monitorObject(palette) self.monitor = ColorsDemo.stack.monitorObject(palette)
} }
super.init(coder: aDecoder) super.init(coder: aDecoder)
@@ -85,7 +85,7 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
// MARK: ObjectObserver // MARK: ObjectObserver
func objectMonitor(_ monitor: ObjectMonitor<Palette>, didUpdateObject object: Palette, changedPersistentKeys: Set<KeyPath>) { func objectMonitor(_ monitor: ObjectMonitor<Palette>, didUpdateObject object: Palette, changedPersistentKeys: Set<KeyPathString>) {
self.reloadPaletteInfo(object, changedKeys: changedPersistentKeys) self.reloadPaletteInfo(object, changedKeys: changedPersistentKeys)
} }
@@ -121,54 +121,62 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
@IBAction dynamic func hueSliderValueDidChange(_ sender: AnyObject?) { @IBAction dynamic func hueSliderValueDidChange(_ sender: AnyObject?) {
let hue = self.hueSlider?.value ?? 0 let hue = self.hueSlider?.value ?? 0
CoreStore.beginAsynchronous { [weak self] (transaction) -> Void in ColorsDemo.stack.perform(
asynchronous: { [weak self] (transaction) in
if let palette = transaction.edit(self?.monitor?.object) { if let palette = transaction.edit(self?.monitor?.object) {
palette.hue = Int32(hue) palette.hue .= Int(hue)
transaction.commit()
}
} }
},
completion: { _ in }
)
} }
@IBAction dynamic func saturationSliderValueDidChange(_ sender: AnyObject?) { @IBAction dynamic func saturationSliderValueDidChange(_ sender: AnyObject?) {
let saturation = self.saturationSlider?.value ?? 0 let saturation = self.saturationSlider?.value ?? 0
CoreStore.beginAsynchronous { [weak self] (transaction) -> Void in ColorsDemo.stack.perform(
asynchronous: { [weak self] (transaction) in
if let palette = transaction.edit(self?.monitor?.object) { if let palette = transaction.edit(self?.monitor?.object) {
palette.saturation = saturation palette.saturation .= saturation
transaction.commit()
}
} }
},
completion: { _ in }
)
} }
@IBAction dynamic func brightnessSliderValueDidChange(_ sender: AnyObject?) { @IBAction dynamic func brightnessSliderValueDidChange(_ sender: AnyObject?) {
let brightness = self.brightnessSlider?.value ?? 0 let brightness = self.brightnessSlider?.value ?? 0
CoreStore.beginAsynchronous { [weak self] (transaction) -> Void in ColorsDemo.stack.perform(
asynchronous: { [weak self] (transaction) in
if let palette = transaction.edit(self?.monitor?.object) { if let palette = transaction.edit(self?.monitor?.object) {
palette.brightness = brightness palette.brightness .= brightness
transaction.commit()
}
} }
},
completion: { _ in }
)
} }
@IBAction dynamic func deleteBarButtonTapped(_ sender: AnyObject?) { @IBAction dynamic func deleteBarButtonTapped(_ sender: AnyObject?) {
CoreStore.beginAsynchronous { [weak self] (transaction) -> Void in ColorsDemo.stack.perform(
asynchronous: { [weak self] (transaction) in
transaction.delete(self?.monitor?.object) transaction.delete(self?.monitor?.object)
transaction.commit() },
} completion: { _ in }
)
} }
func reloadPaletteInfo(_ palette: Palette, changedKeys: Set<String>?) { func reloadPaletteInfo(_ palette: Palette, changedKeys: Set<String>?) {
self.colorNameLabel?.text = palette.colorName self.colorNameLabel?.text = palette.colorName.value
let color = palette.color let color = palette.color
self.colorNameLabel?.textColor = color self.colorNameLabel?.textColor = color
@@ -176,17 +184,17 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
self.hsbLabel?.text = palette.colorText self.hsbLabel?.text = palette.colorText
if changedKeys == nil || changedKeys?.contains(#keyPath(Palette.hue)) == true { if changedKeys == nil || changedKeys?.contains(Palette.keyPath{ $0.hue }) == true {
self.hueSlider?.value = Float(palette.hue) self.hueSlider?.value = Float(palette.hue.value)
} }
if changedKeys == nil || changedKeys?.contains(#keyPath(Palette.saturation)) == true { if changedKeys == nil || changedKeys?.contains(Palette.keyPath{ $0.saturation }) == true {
self.saturationSlider?.value = palette.saturation self.saturationSlider?.value = palette.saturation.value
} }
if changedKeys == nil || changedKeys?.contains(#keyPath(Palette.brightness)) == true { if changedKeys == nil || changedKeys?.contains(Palette.keyPath{ $0.brightness }) == true {
self.brightnessSlider?.value = palette.brightness self.brightnessSlider?.value = palette.brightness.value
} }
} }
} }
@@ -14,24 +14,27 @@ import CoreStore
// MARK: - Palette // MARK: - Palette
class Palette: NSManagedObject { final class Palette: CoreStoreObject {
@NSManaged var hue: Int32 let hue = Value.Required<Int>("hue", initial: 0)
@NSManaged var saturation: Float let saturation = Value.Required<Float>("saturation", initial: 0)
@NSManaged var brightness: Float let brightness = Value.Required<Float>("brightness", initial: 0)
@objc dynamic var colorName: String { let colorName = Value.Optional<String>(
"colorName",
isTransient: true,
customGetter: Palette.getColorName
)
get { private static func getColorName(_ partialObject: PartialObject<Palette>) -> String? {
let KVCKey = #keyPath(Palette.colorName) if let colorName = partialObject.primitiveValue(for: { $0.colorName }) {
if let colorName = self.accessValueForKVCKey(KVCKey) as? String {
return colorName return colorName
} }
let colorName: String let colorName: String
switch self.hue % 360 { switch partialObject.value(for: { $0.hue }) % 360 {
case 0 ..< 20: colorName = "Lower Reds" case 0 ..< 20: colorName = "Lower Reds"
case 20 ..< 57: colorName = "Oranges and Browns" case 20 ..< 57: colorName = "Oranges and Browns"
@@ -44,34 +47,32 @@ class Palette: NSManagedObject {
default: colorName = "Upper Reds" default: colorName = "Upper Reds"
} }
self.setPrimitiveValue(colorName, forKey: KVCKey) partialObject.setPrimitiveValue(colorName, for: { $0.colorName })
return colorName return colorName
} }
set { }
self.setValue(newValue, forKVCKey: #keyPath(Palette.colorName)) extension Palette {
}
}
var color: UIColor { var color: UIColor {
return UIColor( return UIColor(
hue: CGFloat(self.hue) / 360.0, hue: CGFloat(self.hue.value) / 360.0,
saturation: CGFloat(self.saturation), saturation: CGFloat(self.saturation.value),
brightness: CGFloat(self.brightness), brightness: CGFloat(self.brightness.value),
alpha: 1.0 alpha: 1.0
) )
} }
var colorText: String { var colorText: String {
return "H: \(self.hue)˚, S: \(round(self.saturation * 100.0))%, B: \(round(self.brightness * 100.0))%" return "H: \(self.hue.value)˚, S: \(round(self.saturation.value * 100.0))%, B: \(round(self.brightness.value * 100.0))%"
} }
func setInitialValues() { func setInitialValues(in transaction: BaseDataTransaction) {
self.hue = Int32(arc4random_uniform(360)) self.hue .= Int(arc4random_uniform(360))
self.saturation = 1.0 self.saturation .= Float(1.0)
self.brightness = Float(arc4random_uniform(70) + 30) / 100.0 self.brightness .= Float(arc4random_uniform(70) + 30) / 100.0
} }
} }
@@ -98,10 +98,9 @@ class CustomLoggerViewController: UIViewController, CoreStoreLogger {
switch self.segmentedControl?.selectedSegmentIndex { switch self.segmentedControl?.selectedSegmentIndex {
case 0?: case 0?:
self.dataStack.beginAsynchronous { (transaction) -> Void in let request = NSFetchRequest<NSFetchRequestResult>()
Where<NSManagedObject>(true).applyToFetchRequest(request)
_ = transaction.create(Into<Palette>()) Where<NSManagedObject>(false).applyToFetchRequest(request)
}
case 1?: case 1?:
_ = try? dataStack.addStorageAndWait( _ = try? dataStack.addStorageAndWait(
@@ -112,10 +111,9 @@ class CustomLoggerViewController: UIViewController, CoreStoreLogger {
) )
case 2?: case 2?:
self.dataStack.beginAsynchronous { (transaction) -> Void in DispatchQueue.global(qos: .background).async {
transaction.commit() _ = self.dataStack.fetchOne(From<Place>())
transaction.commit()
} }
default: default:
@@ -46,7 +46,7 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD
self.present(alert, animated: true, completion: nil) self.present(alert, animated: true, completion: nil)
let modelMetadata = withExtendedLifetime(DataStack(modelName: "MigrationDemo")) { let modelMetadata = withExtendedLifetime(DataStack(xcodeModelName: "MigrationDemo")) {
(dataStack: DataStack) -> ModelMetadata in (dataStack: DataStack) -> ModelMetadata in
let models = self.models let models = self.models
@@ -60,7 +60,7 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD
} }
for model in models { for model in models {
if model.version == storeVersion { if model.schemaHistory.currentModelVersion == storeVersion {
return model return model
} }
@@ -91,6 +91,11 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD
} }
} }
func listMonitorDidRefetch(_ monitor: ListMonitor<NSManagedObject>) {
self.listMonitorDidChange(monitor)
}
// MARK: UITableViewDataSource // MARK: UITableViewDataSource
@objc dynamic func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { @objc dynamic func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
@@ -104,7 +109,7 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD
let dna = (self.listMonitor?[indexPath] as? OrganismProtocol)?.dna.description ?? "" let dna = (self.listMonitor?[indexPath] as? OrganismProtocol)?.dna.description ?? ""
cell.dnaLabel?.text = "DNA: \(dna)" cell.dnaLabel?.text = "DNA: \(dna)"
cell.mutateButtonHandler = { [weak self] _ -> Void in cell.mutateButtonHandler = { [weak self] () -> Void in
guard let `self` = self, guard let `self` = self,
let dataStack = self.dataStack, let dataStack = self.dataStack,
@@ -115,16 +120,17 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD
self.setSelectedIndexPath(indexPath, scrollToSelection: false) self.setSelectedIndexPath(indexPath, scrollToSelection: false)
self.setEnabled(false) self.setEnabled(false)
dataStack.beginAsynchronous { [weak self] (transaction) -> Void in dataStack.perform(
asynchronous: { (transaction) in
let organism = transaction.edit(organism) as! OrganismProtocol let organism = transaction.edit(organism) as! OrganismProtocol
organism.mutate() organism.mutate()
},
transaction.commit { _ -> Void in completion: { [weak self] _ in
self?.setEnabled(true) self?.setEnabled(true)
} }
} )
} }
return cell return cell
} }
@@ -140,29 +146,47 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD
// MARK: Private // MARK: Private
private typealias ModelMetadata = (label: String, version: String, entityType: AnyClass, migrationChain: MigrationChain) private typealias ModelMetadata = (label: String, entityType: NSManagedObject.Type, schemaHistory: SchemaHistory)
private let models: [ModelMetadata] = [ private let models: [ModelMetadata] = [
( (
label: "Model V1", label: "Model V1",
version: "MigrationDemo",
entityType: OrganismV1.self, entityType: OrganismV1.self,
schemaHistory: SchemaHistory(
XcodeDataModelSchema.from(
modelName: "MigrationDemo",
migrationChain: ["MigrationDemoV3", "MigrationDemoV2", "MigrationDemo"] migrationChain: ["MigrationDemoV3", "MigrationDemoV2", "MigrationDemo"]
), ),
migrationChain: ["MigrationDemoV3", "MigrationDemoV2", "MigrationDemo"]
)
),
( (
label: "Model V2", label: "Model V2",
version: "MigrationDemoV2",
entityType: OrganismV2.self, entityType: OrganismV2.self,
schemaHistory: SchemaHistory(
XcodeDataModelSchema.from(
modelName: "MigrationDemo",
migrationChain: [ migrationChain: [
"MigrationDemo": "MigrationDemoV2", "MigrationDemo": "MigrationDemoV2",
"MigrationDemoV3": "MigrationDemoV2" "MigrationDemoV3": "MigrationDemoV2"
] ]
), ),
migrationChain: [
"MigrationDemo": "MigrationDemoV2",
"MigrationDemoV3": "MigrationDemoV2"
]
)
),
( (
label: "Model V3", label: "Model V3",
version: "MigrationDemoV3",
entityType: OrganismV3.self, entityType: OrganismV3.self,
schemaHistory: SchemaHistory(
XcodeDataModelSchema.from(
modelName: "MigrationDemo",
migrationChain: ["MigrationDemo", "MigrationDemoV2", "MigrationDemoV3"] migrationChain: ["MigrationDemo", "MigrationDemoV2", "MigrationDemoV3"]
),
migrationChain: ["MigrationDemo", "MigrationDemoV2", "MigrationDemoV3"]
)
) )
] ]
@@ -209,21 +233,44 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD
private func selectModelVersion(_ model: ModelMetadata) { private func selectModelVersion(_ model: ModelMetadata) {
if self.dataStack?.modelVersion == model.version { if self.dataStack?.modelVersion == model.schemaHistory.currentModelVersion {
return return
} }
self.set(dataStack: nil, model: nil, scrollToSelection: false) // explicitly trigger NSPersistentStore cleanup by deallocating the stack self.set(dataStack: nil, model: nil, scrollToSelection: false) // explicitly trigger NSPersistentStore cleanup by deallocating the stack
let dataStack = DataStack( let dataStack = DataStack(schemaHistory: model.schemaHistory)
modelName: "MigrationDemo",
migrationChain: model.migrationChain
)
self.setEnabled(false) self.setEnabled(false)
let progress = dataStack.addStorage( let progress = dataStack.addStorage(
SQLiteStore(fileName: "MigrationDemo.sqlite"), SQLiteStore(
fileName: "MigrationDemo.sqlite",
migrationMappingProviders: [
CustomSchemaMappingProvider(
from: "MigrationDemoV3",
to: "MigrationDemoV2",
entityMappings: [
.transformEntity(
sourceEntity: "Organism",
destinationEntity: "Organism",
transformer: { (source, createDestination) in
let destination = createDestination()
destination.enumerateAttributes { (attribute, sourceAttribute) in
if let sourceAttribute = sourceAttribute {
destination[attribute] = source[sourceAttribute]
}
}
destination["numberOfFlippers"] = source["numberOfLimbs"]
}
)
]
)
]
),
completion: { [weak self] (result) -> Void in completion: { [weak self] (result) -> Void in
guard let `self` = self else { guard let `self` = self else {
@@ -240,8 +287,8 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD
self.set(dataStack: dataStack, model: model, scrollToSelection: true) self.set(dataStack: dataStack, model: model, scrollToSelection: true)
let count = dataStack.queryValue( let count = dataStack.queryValue(
From(model.entityType), From<NSManagedObject>(model.entityType)
Select<Int>(.count(#keyPath(OrganismV1.dna))))! .select(Int.self, .count(#keyPath(OrganismV1.dna))))!
if count > 0 { if count > 0 {
self.setEnabled(true) self.setEnabled(true)
@@ -250,7 +297,8 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD
for i: Int64 in 0 ..< 20 { for i: Int64 in 0 ..< 20 {
dataStack.beginAsynchronous { (transaction) -> Void in dataStack.perform(
asynchronous: { (transaction) in
for j: Int64 in 0 ..< 500 { for j: Int64 in 0 ..< 500 {
@@ -258,17 +306,17 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD
organism.dna = (i * 500) + j + 1 organism.dna = (i * 500) + j + 1
organism.mutate() organism.mutate()
} }
},
transaction.commit() completion: { _ in }
)
} }
} dataStack.perform(
dataStack.beginAsynchronous { [weak self] (transaction) -> Void in asynchronous: { _ in },
completion: { [weak self] _ in
transaction.commit { _ in
self?.setEnabled(true) self?.setEnabled(true)
} }
} )
} }
} }
) )
@@ -311,10 +359,20 @@ class MigrationsDemoViewController: UIViewController, ListObserver, UITableViewD
if let dataStack = dataStack, let model = model { if let dataStack = dataStack, let model = model {
self.segmentedControl?.selectedSegmentIndex = self.models.map { $0.version }.index(of: model.version)! self.segmentedControl?.selectedSegmentIndex = self.models
.index(
where: { (arg) -> Bool in
let (_, _, schemaHistory) = arg
return schemaHistory.currentModelVersion == model.schemaHistory.currentModelVersion
}
)!
self._dataStack = dataStack self._dataStack = dataStack
let listMonitor = dataStack.monitorList(From(model.entityType), OrderBy(.descending("dna"))) let listMonitor = dataStack.monitorList(
From(model.entityType),
OrderBy<NSManagedObject>(.descending(#keyPath(OrganismV1.dna)))
)
listMonitor.addObserver(self) listMonitor.addObserver(self)
self._listMonitor = listMonitor self._listMonitor = listMonitor
@@ -1,4 +1,4 @@
<?xml version="1.0" standalone="no"?> <?xml version="1.0"?>
<!DOCTYPE database SYSTEM "file:///System/Library/DTDs/CoreData.dtd"> <!DOCTYPE database SYSTEM "file:///System/Library/DTDs/CoreData.dtd">
<database> <database>
@@ -10,7 +10,7 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>NSPersistenceFrameworkVersion</key> <key>NSPersistenceFrameworkVersion</key>
<integer>526</integer> <integer>754</integer>
<key>NSStoreModelVersionHashes</key> <key>NSStoreModelVersionHashes</key>
<dict> <dict>
<key>XDDevAttributeMapping</key> <key>XDDevAttributeMapping</key>
@@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="11198.3" systemVersion="15F34" minimumToolsVersion="Xcode 4.3" sourceLanguage="Swift" userDefinedModelVersionIdentifier=""> <model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="12141" systemVersion="16E195" minimumToolsVersion="Xcode 4.3" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="Organism" representedClassName="CoreStoreDemo.OrganismV3" syncable="YES"> <entity name="Organism" representedClassName="CoreStoreDemo.OrganismV3" syncable="YES">
<attribute name="dna" optional="YES" attributeType="Integer 64" usesScalarValueType="NO" syncable="YES"/> <attribute name="dna" optional="YES" attributeType="Integer 64" usesScalarValueType="NO" syncable="YES"/>
<attribute name="hasHead" attributeType="Boolean" usesScalarValueType="NO" syncable="YES"/> <attribute name="hasHead" attributeType="Boolean" usesScalarValueType="NO" syncable="YES"/>
<attribute name="hasTail" attributeType="Boolean" usesScalarValueType="NO" syncable="YES"/> <attribute name="hasTail" attributeType="Boolean" usesScalarValueType="NO" syncable="YES"/>
<attribute name="hasVertebrae" attributeType="Boolean" usesScalarValueType="NO" syncable="YES"/> <attribute name="hasVertebrae" attributeType="Boolean" usesScalarValueType="NO" syncable="YES"/>
<attribute name="numberOfLimbs" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/> <attribute name="numberOfLimbs" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" elementID="numberOfFlippers" syncable="YES"/>
</entity> </entity>
<elements> <elements>
<element name="Organism" positionX="-36" positionY="9" width="128" height="120"/> <element name="Organism" positionX="-36" positionY="9" width="128" height="120"/>
@@ -17,7 +17,7 @@ private struct Static {
static let facebookStack: DataStack = { static let facebookStack: DataStack = {
let dataStack = DataStack(modelName: "StackSetupDemo") let dataStack = DataStack(xcodeModelName: "StackSetupDemo")
try! dataStack.addStorageAndWait( try! dataStack.addStorageAndWait(
SQLiteStore( SQLiteStore(
fileName: "AccountsDemo_FB_Male.sqlite", fileName: "AccountsDemo_FB_Male.sqlite",
@@ -33,7 +33,8 @@ private struct Static {
) )
) )
_ = dataStack.beginSynchronous { (transaction) -> Void in _ = try? dataStack.perform(
synchronous: { (transaction) in
transaction.deleteAll(From<UserAccount>()) transaction.deleteAll(From<UserAccount>())
@@ -46,16 +47,15 @@ private struct Static {
account2.accountType = "Facebook" account2.accountType = "Facebook"
account2.name = "Jane Doe HCD" account2.name = "Jane Doe HCD"
account2.friends = 314 account2.friends = 314
_ = transaction.commitAndWait()
} }
)
return dataStack return dataStack
}() }()
static let twitterStack: DataStack = { static let twitterStack: DataStack = {
let dataStack = DataStack(modelName: "StackSetupDemo") let dataStack = DataStack(xcodeModelName: "StackSetupDemo")
try! dataStack.addStorageAndWait( try! dataStack.addStorageAndWait(
SQLiteStore( SQLiteStore(
fileName: "AccountsDemo_TW_Male.sqlite", fileName: "AccountsDemo_TW_Male.sqlite",
@@ -71,7 +71,8 @@ private struct Static {
) )
) )
_ = dataStack.beginSynchronous { (transaction) -> Void in _ = try? dataStack.perform(
synchronous: { (transaction) in
transaction.deleteAll(From<UserAccount>()) transaction.deleteAll(From<UserAccount>())
@@ -84,10 +85,8 @@ private struct Static {
account2.accountType = "Twitter" account2.accountType = "Twitter"
account2.name = "#janedoe_hcd" account2.name = "#janedoe_hcd"
account2.friends = 100 account2.friends = 100
_ = transaction.commitAndWait()
} }
)
return dataStack return dataStack
}() }()
} }
@@ -28,13 +28,13 @@ private struct Static {
var place = CoreStore.fetchOne(From<Place>()) var place = CoreStore.fetchOne(From<Place>())
if place == nil { if place == nil {
_ = CoreStore.beginSynchronous { (transaction) -> Void in _ = try? CoreStore.perform(
synchronous: { (transaction) in
let place = transaction.create(Into<Place>()) let place = transaction.create(Into<Place>())
place.setInitialValues() place.setInitialValues()
_ = transaction.commitAndWait()
} }
)
place = CoreStore.fetchOne(From<Place>()) place = CoreStore.fetchOne(From<Place>())
} }
@@ -131,7 +131,7 @@ class TransactionsDemoViewController: UIViewController, MKMapViewDelegate, Objec
// none // none
} }
func objectMonitor(_ monitor: ObjectMonitor<Place>, didUpdateObject object: Place, changedPersistentKeys: Set<KeyPath>) { func objectMonitor(_ monitor: ObjectMonitor<Place>, didUpdateObject object: Place, changedPersistentKeys: Set<KeyPathString>) {
if let mapView = self.mapView { if let mapView = self.mapView {
@@ -169,23 +169,26 @@ class TransactionsDemoViewController: UIViewController, MKMapViewDelegate, Objec
gesture.location(in: mapView), gesture.location(in: mapView),
toCoordinateFrom: mapView toCoordinateFrom: mapView
) )
CoreStore.beginAsynchronous { (transaction) -> Void in CoreStore.perform(
asynchronous: { (transaction) in
let place = transaction.edit(Static.placeController.object) let place = transaction.edit(Static.placeController.object)
place?.coordinate = coordinate place?.coordinate = coordinate
transaction.commit { (_) -> Void in } },
} completion: { _ in }
)
} }
} }
@IBAction dynamic func refreshButtonTapped(_ sender: AnyObject?) { @IBAction dynamic func refreshButtonTapped(_ sender: AnyObject?) {
_ = CoreStore.beginSynchronous { (transaction) -> Void in _ = try? CoreStore.perform(
synchronous: { (transaction) in
let place = transaction.edit(Static.placeController.object) let place = transaction.edit(Static.placeController.object)
place?.setInitialValues() place?.setInitialValues()
_ = transaction.commitAndWait()
} }
)
} }
func geocode(place: Place) { func geocode(place: Place) {
+2 -2
View File
@@ -37,10 +37,10 @@ class BaseTestCase: XCTestCase {
@nonobjc @nonobjc
@discardableResult @discardableResult
func prepareStack<T>(configurations: [String?] = [nil], _ closure: (_ dataStack: DataStack) -> T) -> T { func prepareStack<T>(configurations: [ModelConfiguration] = [nil], _ closure: (_ dataStack: DataStack) -> T) -> T {
let stack = DataStack( let stack = DataStack(
modelName: "Model", xcodeModelName: "Model",
bundle: Bundle(for: type(of: self)) bundle: Bundle(for: type(of: self))
) )
do { do {
@@ -2,8 +2,25 @@
// BaseTestDataTestCase.swift // BaseTestDataTestCase.swift
// CoreStore // CoreStore
// //
// Created by John Rommel Estropia on 2016/06/11. // Copyright © 2017 John Rommel Estropia
// Copyright © 2016 John Rommel Estropia. All rights reserved. //
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
// //
import Foundation import Foundation
@@ -17,7 +34,7 @@ import CoreStore
class BaseTestDataTestCase: BaseTestCase { class BaseTestDataTestCase: BaseTestCase {
@nonobjc @nonobjc
let dateFormatter: DateFormatter = { let dateFormatter: DateFormatter = cs_lazy {
let formatter = DateFormatter() let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX") formatter.locale = Locale(identifier: "en_US_POSIX")
@@ -25,12 +42,13 @@ class BaseTestDataTestCase: BaseTestCase {
formatter.calendar = Calendar(identifier: Calendar.Identifier.gregorian) formatter.calendar = Calendar(identifier: Calendar.Identifier.gregorian)
formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ" formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"
return formatter return formatter
}() }
@nonobjc @nonobjc
func prepareTestDataForStack(_ stack: DataStack, configurations: [String?] = [nil]) { func prepareTestDataForStack(_ stack: DataStack, configurations: [ModelConfiguration] = [nil]) {
stack.beginSynchronous { (transaction) in try! stack.perform(
synchronous: { (transaction) in
for (configurationIndex, configuration) in configurations.enumerated() { for (configurationIndex, configuration) in configurations.enumerated() {
@@ -70,7 +88,7 @@ class BaseTestDataTestCase: BaseTestCase {
} }
} }
} }
_ = transaction.commitAndWait()
} }
)
} }
} }
+24 -36
View File
@@ -163,7 +163,7 @@
- (void)test_ThatDataStacks_BridgeCorrectly { - (void)test_ThatDataStacks_BridgeCorrectly {
CSDataStack *dataStack = [[CSDataStack alloc] CSDataStack *dataStack = [[CSDataStack alloc]
initWithModelName:@"Model" initWithXcodeModelName:@"Model"
bundle:[NSBundle bundleForClass:[self class]] bundle:[NSBundle bundleForClass:[self class]]
versionChain:nil]; versionChain:nil];
XCTAssertNotNil(dataStack); XCTAssertNotNil(dataStack);
@@ -201,7 +201,7 @@
[CSCoreStore [CSCoreStore
setDefaultStack:[[CSDataStack alloc] setDefaultStack:[[CSDataStack alloc]
initWithModelName:@"Model" initWithXcodeModelName:@"Model"
bundle:[NSBundle bundleForClass:[self class]] bundle:[NSBundle bundleForClass:[self class]]
versionChain:nil]]; versionChain:nil]];
[CSCoreStore [CSCoreStore
@@ -212,15 +212,27 @@
CSUnsafeDataTransaction *transaction = [CSCoreStore beginUnsafe]; CSUnsafeDataTransaction *transaction = [CSCoreStore beginUnsafe];
XCTAssertNotNil(transaction); XCTAssertNotNil(transaction);
XCTAssert([transaction isKindOfClass:[CSUnsafeDataTransaction class]]); XCTAssert([transaction isKindOfClass:[CSUnsafeDataTransaction class]]);
NSError *error;
BOOL result = [transaction commitAndWaitWithError:&error];
XCTAssertTrue(result);
XCTAssertNil(error);
} }
{ {
XCTestExpectation *expectation = [self expectationWithDescription:@"sync"]; XCTestExpectation *expectation = [self expectationWithDescription:@"sync"];
[CSCoreStore beginSynchronous:^(CSSynchronousDataTransaction * _Nonnull transaction) { NSError *error;
BOOL result = [CSCoreStore
beginSynchronous:^(CSSynchronousDataTransaction * _Nonnull transaction) {
XCTAssertNotNil(transaction); XCTAssertNotNil(transaction);
XCTAssert([transaction isKindOfClass:[CSSynchronousDataTransaction class]]); XCTAssert([transaction isKindOfClass:[CSSynchronousDataTransaction class]]);
NSError *error;
XCTAssertTrue([transaction commitAndWaitWithError:&error]);
XCTAssertNil(error);
[expectation fulfill]; [expectation fulfill];
}]; }
error:&error];
XCTAssertTrue(result);
XCTAssertNil(error);
} }
{ {
XCTestExpectation *expectation = [self expectationWithDescription:@"async"]; XCTestExpectation *expectation = [self expectationWithDescription:@"async"];
@@ -228,42 +240,18 @@
XCTAssertNotNil(transaction); XCTAssertNotNil(transaction);
XCTAssert([transaction isKindOfClass:[CSAsynchronousDataTransaction class]]); XCTAssert([transaction isKindOfClass:[CSAsynchronousDataTransaction class]]);
[transaction
commitWithSuccess:^{
[expectation fulfill]; [expectation fulfill];
}
failure:^(CSError *error){
XCTFail();
}];
}]; }];
} }
[self waitForExpectationsWithTimeout:10 handler:nil]; [self waitForExpectationsWithTimeout:10 handler:nil];
} }
#if TARGET_OS_IOS || TARGET_OS_WATCHOS || TARGET_OS_TV
- (void)test_ThatDataStacks_CanCreateCustomFetchedResultsControllers {
[CSCoreStore
setDefaultStack:[[CSDataStack alloc]
initWithModelName:@"Model"
bundle:[NSBundle bundleForClass:[self class]]
versionChain:nil]];
[CSCoreStore
addInMemoryStorageAndWait:[CSInMemoryStore new]
error:nil];
NSFetchedResultsController *controller =
[[CSCoreStore defaultStack]
createFetchedResultsControllerFrom:CSFromClass([TestEntity1 class])
sectionBy:[CSSectionBy keyPath:CSKeyPath(TestEntity1, testString)]
fetchClauses:@[CSWhereFormat(@"%K > %d", CSKeyPath(TestEntity1, testEntityID), 100),
CSOrderByKeys(CSSortAscending(CSKeyPath(TestEntity1, testString)), nil),
CSTweakRequest(^(NSFetchRequest *fetchRequest) { fetchRequest.fetchLimit = 10; })]];
XCTAssertNotNil(controller);
XCTAssertEqualObjects(controller.fetchRequest.entity.managedObjectClassName, [[TestEntity1 class] description]);
XCTAssertEqualObjects(controller.sectionNameKeyPath, CSKeyPath(TestEntity1, testString));
XCTAssertEqualObjects(controller.fetchRequest.predicate,
CSWhereFormat(@"%K > %d", CSKeyPath(TestEntity1, testEntityID), 100).predicate);
XCTAssertEqualObjects(controller.fetchRequest.sortDescriptors,
CSOrderByKeys(CSSortAscending(CSKeyPath(TestEntity1, testString)), nil).sortDescriptors);
XCTAssertEqual(controller.fetchRequest.fetchLimit, 10);
}
#endif
@end @end
+9 -12
View File
@@ -27,10 +27,9 @@
import CoreStore import CoreStore
#if os(iOS) || os(watchOS) || os(tvOS)
// MARK: - ConvenienceTests // MARK: - ConvenienceTests
@available(OSX 10.12, *)
class ConvenienceTests: BaseTestCase { class ConvenienceTests: BaseTestCase {
@objc @objc
@@ -41,8 +40,8 @@ class ConvenienceTests: BaseTestCase {
let controller = stack.createFetchedResultsController( let controller = stack.createFetchedResultsController(
From<TestEntity1>(), From<TestEntity1>(),
SectionBy(#keyPath(TestEntity1.testString)), SectionBy(#keyPath(TestEntity1.testString)),
Where("%@ > %d", #keyPath(TestEntity1.testEntityID), 100), Where<TestEntity1>("%@ > %d", #keyPath(TestEntity1.testEntityID), 100),
OrderBy(.ascending(#keyPath(TestEntity1.testString))), OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testString))),
Tweak { $0.fetchLimit = 10 } Tweak { $0.fetchLimit = 10 }
) )
XCTAssertEqual(controller.managedObjectContext, stack.mainContext) XCTAssertEqual(controller.managedObjectContext, stack.mainContext)
@@ -50,11 +49,11 @@ class ConvenienceTests: BaseTestCase {
XCTAssertEqual(controller.sectionNameKeyPath, #keyPath(TestEntity1.testString)) XCTAssertEqual(controller.sectionNameKeyPath, #keyPath(TestEntity1.testString))
XCTAssertEqual( XCTAssertEqual(
controller.fetchRequest.sortDescriptors!, controller.fetchRequest.sortDescriptors!,
OrderBy(.ascending(#keyPath(TestEntity1.testString))).sortDescriptors OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testString))).sortDescriptors
) )
XCTAssertEqual( XCTAssertEqual(
controller.fetchRequest.predicate, controller.fetchRequest.predicate,
Where("%@ > %d", #keyPath(TestEntity1.testEntityID), 100).predicate Where<TestEntity1>("%@ > %d", #keyPath(TestEntity1.testEntityID), 100).predicate
) )
XCTAssertEqual(controller.fetchRequest.fetchLimit, 10) XCTAssertEqual(controller.fetchRequest.fetchLimit, 10)
} }
@@ -70,8 +69,8 @@ class ConvenienceTests: BaseTestCase {
let controller = transaction.createFetchedResultsController( let controller = transaction.createFetchedResultsController(
From<TestEntity1>(), From<TestEntity1>(),
SectionBy(#keyPath(TestEntity1.testString)), SectionBy(#keyPath(TestEntity1.testString)),
Where("%@ > %d", #keyPath(TestEntity1.testEntityID), 100), Where<TestEntity1>("%@ > %d", #keyPath(TestEntity1.testEntityID), 100),
OrderBy(.ascending(#keyPath(TestEntity1.testString))), OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testString))),
Tweak { $0.fetchLimit = 10 } Tweak { $0.fetchLimit = 10 }
) )
XCTAssertEqual(controller.managedObjectContext, transaction.context) XCTAssertEqual(controller.managedObjectContext, transaction.context)
@@ -79,16 +78,14 @@ class ConvenienceTests: BaseTestCase {
XCTAssertEqual(controller.sectionNameKeyPath, #keyPath(TestEntity1.testString)) XCTAssertEqual(controller.sectionNameKeyPath, #keyPath(TestEntity1.testString))
XCTAssertEqual( XCTAssertEqual(
controller.fetchRequest.sortDescriptors!, controller.fetchRequest.sortDescriptors!,
OrderBy(.ascending(#keyPath(TestEntity1.testString))).sortDescriptors OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testString))).sortDescriptors
) )
XCTAssertEqual( XCTAssertEqual(
controller.fetchRequest.predicate, controller.fetchRequest.predicate,
Where("%@ > %d", #keyPath(TestEntity1.testEntityID), 100).predicate Where<TestEntity1>("%@ > %d", #keyPath(TestEntity1.testEntityID), 100).predicate
) )
XCTAssertEqual(controller.fetchRequest.fetchLimit, 10) XCTAssertEqual(controller.fetchRequest.fetchLimit, 10)
} }
} }
} }
} }
#endif
+308
View File
@@ -0,0 +1,308 @@
//
// DynamicModelTests.swift
// CoreStore
//
// Copyright © 2017 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import XCTest
@testable
import CoreStore
#if os(OSX)
typealias Color = NSColor
#else
typealias Color = UIColor
#endif
class Animal: CoreStoreObject {
let species = Value.Required<String>("species", initial: "Swift")
let master = Relationship.ToOne<Person>("master")
let color = Transformable.Optional<Color>("color")
}
class Dog: Animal {
let nickname = Value.Optional<String>("nickname")
let age = Value.Required<Int>("age", initial: 1)
let friends = Relationship.ToManyOrdered<Dog>("friends")
let friendedBy = Relationship.ToManyUnordered<Dog>("friendedBy", inverse: { $0.friends })
}
class Person: CoreStoreObject {
let title = Value.Required<String>(
"title",
initial: "Mr.",
customSetter: Person.setTitle
)
let name = Value.Required<String>(
"name",
initial: "",
customSetter: Person.setName
)
let displayName = Value.Optional<String>(
"displayName",
isTransient: true,
customGetter: Person.getDisplayName(_:),
affectedByKeyPaths: Person.keyPathsAffectingDisplayName()
)
let pets = Relationship.ToManyUnordered<Animal>("pets", inverse: { $0.master })
private static func setTitle(_ partialObject: PartialObject<Person>, _ newValue: String) {
partialObject.setPrimitiveValue(newValue, for: { $0.title })
partialObject.setPrimitiveValue(nil, for: { $0.displayName })
}
private static func setName(_ partialObject: PartialObject<Person>, _ newValue: String) {
partialObject.setPrimitiveValue(newValue, for: { $0.name })
partialObject.setPrimitiveValue(nil, for: { $0.displayName })
}
static func getDisplayName(_ partialObject: PartialObject<Person>) -> String? {
if let displayName = partialObject.primitiveValue(for: { $0.displayName }) {
return displayName
}
let title = partialObject.value(for: { $0.title })
let name = partialObject.value(for: { $0.name })
let displayName = "\(title) \(name)"
partialObject.setPrimitiveValue(displayName, for: { $0.displayName })
return displayName
}
static func keyPathsAffectingDisplayName() -> Set<String> {
return [
String(keyPath: \Person.title),
String(keyPath: \Person.name)
]
}
}
// MARK: - DynamicModelTests
class DynamicModelTests: BaseTestDataTestCase {
@objc
dynamic func test_ThatDynamicModels_CanBeDeclaredCorrectly() {
let dataStack = DataStack(
CoreStoreSchema(
modelVersion: "V1",
entities: [
Entity<Animal>("Animal"),
Entity<Dog>("Dog"),
Entity<Person>("Person")
],
versionLock: [
"Animal": [0x1b59d511019695cf, 0xdeb97e86c5eff179, 0x1cfd80745646cb3, 0x4ff99416175b5b9a],
"Dog": [0xe3f0afeb109b283a, 0x29998d292938eb61, 0x6aab788333cfc2a3, 0x492ff1d295910ea7],
"Person": [0x66d8bbfd8b21561f, 0xcecec69ecae3570f, 0xc4b73d71256214ef, 0x89b99bfe3e013e8b]
]
)
)
self.prepareStack(dataStack, configurations: [nil]) { (stack) in
let k1 = String(keyPath: \Animal.species)
XCTAssertEqual(k1, "species")
let k2 = String(keyPath: \Dog.species)
XCTAssertEqual(k2, "species")
let k3 = String(keyPath: \Dog.nickname)
XCTAssertEqual(k3, "nickname")
let updateDone = self.expectation(description: "update-done")
let fetchDone = self.expectation(description: "fetch-done")
stack.perform(
asynchronous: { (transaction) in
let animal = transaction.create(Into<Animal>())
XCTAssertEqual(animal.species.value, "Swift")
XCTAssertTrue(type(of: animal.species.value) == String.self)
animal.species .= "Sparrow"
XCTAssertEqual(animal.species.value, "Sparrow")
animal.color .= .yellow
XCTAssertEqual(animal.color.value, Color.yellow)
let dog = transaction.create(Into<Dog>())
XCTAssertEqual(dog.species.value, "Swift")
XCTAssertEqual(dog.nickname.value, nil)
XCTAssertEqual(dog.age.value, 1)
dog.species .= "Dog"
XCTAssertEqual(dog.species.value, "Dog")
dog.nickname .= "Spot"
XCTAssertEqual(dog.nickname.value, "Spot")
let person = transaction.create(Into<Person>())
XCTAssertTrue(person.pets.value.isEmpty)
XCTAssertEqual(
type(of: person.rawObject!).keyPathsForValuesAffectingValue(forKey: "displayName"),
["title", "name"]
)
person.name .= "Joe"
XCTAssertEqual(person.rawObject!.value(forKey: "name") as! String?, "Joe")
XCTAssertEqual(person.rawObject!.value(forKey: "displayName") as! String?, "Mr. Joe")
person.rawObject!.setValue("AAAA", forKey: "displayName")
XCTAssertEqual(person.rawObject!.value(forKey: "displayName") as! String?, "AAAA")
person.name .= "John"
XCTAssertEqual(person.name.value, "John")
XCTAssertEqual(person.displayName.value, "Mr. John") // Custom getter
person.title .= "Sir"
XCTAssertEqual(person.displayName.value, "Sir John")
person.pets.value.insert(dog)
XCTAssertEqual(person.pets.count, 1)
XCTAssertEqual(person.pets.value.first, dog)
XCTAssertEqual(person.pets.value.first?.master.value, person)
XCTAssertEqual(dog.master.value, person)
XCTAssertEqual(dog.master.value?.pets.value.first, dog)
},
success: { _ in
updateDone.fulfill()
},
failure: { _ in
XCTFail()
}
)
stack.perform(
asynchronous: { (transaction) in
let p1 = Where<Animal>({ $0.species == "Sparrow" })
XCTAssertEqual(p1.predicate, NSPredicate(format: "%K == %@", "species", "Sparrow"))
let bird = transaction.fetchOne(From<Animal>(), p1)
XCTAssertNotNil(bird)
XCTAssertEqual(bird!.species.value, "Sparrow")
let p2 = Where<Dog>({ $0.nickname == "Spot" })
XCTAssertEqual(p2.predicate, NSPredicate(format: "%K == %@", "nickname", "Spot"))
let dog = transaction.fetchOne(From<Dog>().where(\.nickname == "Spot"))
XCTAssertNotNil(dog)
XCTAssertEqual(dog!.nickname.value, "Spot")
XCTAssertEqual(dog!.species.value, "Dog")
let person = transaction.fetchOne(From<Person>())
XCTAssertNotNil(person)
XCTAssertEqual(person!.pets.value.first, dog)
let p3 = Where<Dog>({ $0.age == 10 })
XCTAssertEqual(p3.predicate, NSPredicate(format: "%K == %d", "age", 10))
_ = transaction.fetchAll(
From<Dog>()
.where(\Animal.species == "Dog" && \.age == 10)
)
_ = transaction.fetchAll(
From<Dog>()
.where(\.age == 10 && \Animal.species == "Dog")
.orderBy(.ascending({ $0.species }))
)
_ = transaction.fetchAll(
From<Dog>(),
Where<Dog>({ $0.age > 10 && $0.age <= 15 })
)
_ = transaction.fetchAll(
From<Dog>(),
Where<Dog>({ $0.species == "Dog" && $0.age == 10 })
)
_ = transaction.fetchAll(
From<Dog>(),
Where<Dog>({ $0.age == 10 && $0.species == "Dog" })
)
_ = transaction.fetchAll(
From<Dog>(),
Where<Dog>({ $0.age > 10 && $0.age <= 15 })
)
_ = transaction.fetchAll(
From<Dog>(),
(\Dog.age > 10 && \Dog.age <= 15)
)
},
success: { _ in
fetchDone.fulfill()
},
failure: { _ in
XCTFail()
}
)
self.waitAndCheckExpectations()
}
}
@objc
dynamic func test_ThatDynamicModelKeyPaths_CanBeCreated() {
XCTAssertEqual(String(keyPath: \Animal.species), "species")
XCTAssertEqual(String(keyPath: \Dog.species), "species")
}
@nonobjc
func prepareStack(_ dataStack: DataStack, configurations: [ModelConfiguration] = [nil], _ closure: (_ dataStack: DataStack) -> Void) {
do {
try configurations.forEach { (configuration) in
try dataStack.addStorageAndWait(
SQLiteStore(
fileURL: SQLiteStore.defaultRootDirectory
.appendingPathComponent(UUID().uuidString)
.appendingPathComponent("\(type(of: self))_\((configuration ?? "-null-")).sqlite"),
configuration: configuration,
localStorageOptions: .recreateStoreOnModelMismatch
)
)
}
}
catch let error as NSError {
XCTFail(error.coreStoreDumpString)
}
closure(dataStack)
}
}
+8 -3
View File
@@ -85,16 +85,21 @@ final class ErrorTests: XCTestCase {
let dummyURL = URL(string: "file:///test1/test2.sqlite")! let dummyURL = URL(string: "file:///test1/test2.sqlite")!
let model = NSManagedObjectModel.fromBundle(Bundle(for: type(of: self)), modelName: "Model") let schemaHistory = SchemaHistory(
XcodeDataModelSchema.from(
modelName: "Model",
bundle: Bundle(for: type(of: self))
)
)
let version = "1.0.0" let version = "1.0.0"
let error = CoreStoreError.mappingModelNotFound(localStoreURL: dummyURL, targetModel: model, targetModelVersion: version) let error = CoreStoreError.mappingModelNotFound(localStoreURL: dummyURL, targetModel: schemaHistory.rawModel, targetModelVersion: version)
XCTAssertEqual((error as NSError).domain, CoreStoreErrorDomain) XCTAssertEqual((error as NSError).domain, CoreStoreErrorDomain)
XCTAssertEqual((error as NSError).code, CoreStoreErrorCode.mappingModelNotFound.rawValue) XCTAssertEqual((error as NSError).code, CoreStoreErrorCode.mappingModelNotFound.rawValue)
let userInfo: NSDictionary = [ let userInfo: NSDictionary = [
"localStoreURL": dummyURL, "localStoreURL": dummyURL,
"targetModel": model, "targetModel": schemaHistory.rawModel,
"targetModelVersion": version "targetModelVersion": version
] ]
let objcError = error.bridgeToObjectiveC let objcError = error.bridgeToObjectiveC
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -38,7 +38,7 @@ final class FromTests: BaseTestCase {
do { do {
let from = From() let from = From<NSManagedObject>()
XCTAssert(from.entityClass === NSManagedObject.self) XCTAssert(from.entityClass === NSManagedObject.self)
XCTAssertNil(from.configurations) XCTAssertNil(from.configurations)
} }
+4 -4
View File
@@ -38,14 +38,14 @@ final class GroupByTests: BaseTestCase {
do { do {
let groupBy = GroupBy() let groupBy = GroupBy<NSManagedObject>()
XCTAssertEqual(groupBy, GroupBy([] as [String])) XCTAssertEqual(groupBy, GroupBy([] as [String]))
XCTAssertNotEqual(groupBy, GroupBy("key")) XCTAssertNotEqual(groupBy, GroupBy("key"))
XCTAssertTrue(groupBy.keyPaths.isEmpty) XCTAssertTrue(groupBy.keyPaths.isEmpty)
} }
do { do {
let groupBy = GroupBy("key1") let groupBy = GroupBy<NSManagedObject>("key1")
XCTAssertEqual(groupBy, GroupBy("key1")) XCTAssertEqual(groupBy, GroupBy("key1"))
XCTAssertEqual(groupBy, GroupBy(["key1"])) XCTAssertEqual(groupBy, GroupBy(["key1"]))
XCTAssertNotEqual(groupBy, GroupBy("key2")) XCTAssertNotEqual(groupBy, GroupBy("key2"))
@@ -53,7 +53,7 @@ final class GroupByTests: BaseTestCase {
} }
do { do {
let groupBy = GroupBy("key1", "key2") let groupBy = GroupBy<NSManagedObject>("key1", "key2")
XCTAssertEqual(groupBy, GroupBy("key1", "key2")) XCTAssertEqual(groupBy, GroupBy("key1", "key2"))
XCTAssertEqual(groupBy, GroupBy(["key1", "key2"])) XCTAssertEqual(groupBy, GroupBy(["key1", "key2"]))
XCTAssertNotEqual(groupBy, GroupBy("key2", "key1")) XCTAssertNotEqual(groupBy, GroupBy("key2", "key1"))
@@ -66,7 +66,7 @@ final class GroupByTests: BaseTestCase {
self.prepareStack { (dataStack) in self.prepareStack { (dataStack) in
let groupBy = GroupBy(#keyPath(TestEntity1.testString)) let groupBy = GroupBy<NSManagedObject>(#keyPath(TestEntity1.testString))
let request = CoreStoreFetchRequest() let request = CoreStoreFetchRequest()
_ = From<TestEntity1>().applyToFetchRequest(request, context: dataStack.mainContext) _ = From<TestEntity1>().applyToFetchRequest(request, context: dataStack.mainContext)
+144 -148
View File
@@ -36,40 +36,40 @@ class ImportTests: BaseTestDataTestCase {
@objc @objc
dynamic func test_ThatAttributeProtocols_BehaveCorrectly() { dynamic func test_ThatAttributeProtocols_BehaveCorrectly() {
XCTAssertEqual(NSNumber.cs_fromNativeType(NSNumber(value: true))?.boolValue, true) XCTAssertEqual(NSNumber.cs_fromQueryableNativeType(NSNumber(value: true))?.boolValue, true)
XCTAssertEqual(NSNumber.cs_fromNativeType(NSNumber(value: Int16.max))?.int16Value, Int16.max) XCTAssertEqual(NSNumber.cs_fromQueryableNativeType(NSNumber(value: Int16.max))?.int16Value, Int16.max)
XCTAssertEqual(NSNumber.cs_fromNativeType(NSNumber(value: Int32.max))?.int32Value, Int32.max) XCTAssertEqual(NSNumber.cs_fromQueryableNativeType(NSNumber(value: Int32.max))?.int32Value, Int32.max)
XCTAssertEqual(NSNumber.cs_fromNativeType(NSNumber(value: Int64.max))?.int64Value, Int64.max) XCTAssertEqual(NSNumber.cs_fromQueryableNativeType(NSNumber(value: Int64.max))?.int64Value, Int64.max)
XCTAssertEqual(NSNumber.cs_fromNativeType(NSNumber(value: MAXFLOAT))?.floatValue, MAXFLOAT) XCTAssertEqual(NSNumber.cs_fromQueryableNativeType(NSNumber(value: MAXFLOAT))?.floatValue, MAXFLOAT)
XCTAssertEqual(NSNumber.cs_fromNativeType(NSNumber(value: Double(MAXFLOAT)))?.doubleValue, Double(MAXFLOAT)) XCTAssertEqual(NSNumber.cs_fromQueryableNativeType(NSNumber(value: Double(MAXFLOAT)))?.doubleValue, Double(MAXFLOAT))
XCTAssertEqual(NSDecimalNumber.cs_fromNativeType(NSDecimalNumber(string: "1"))?.boolValue, true) XCTAssertEqual(NSDecimalNumber.cs_fromQueryableNativeType(NSDecimalNumber(string: "1"))?.boolValue, true)
XCTAssertEqual(NSDecimalNumber.cs_fromNativeType(NSDecimalNumber(string: Int16.max.description))?.int16Value, Int16.max) XCTAssertEqual(NSDecimalNumber.cs_fromQueryableNativeType(NSDecimalNumber(string: Int16.max.description))?.int16Value, Int16.max)
XCTAssertEqual(NSDecimalNumber.cs_fromNativeType(NSDecimalNumber(string: Int32.max.description))?.int32Value, Int32.max) XCTAssertEqual(NSDecimalNumber.cs_fromQueryableNativeType(NSDecimalNumber(string: Int32.max.description))?.int32Value, Int32.max)
XCTAssertEqual(NSDecimalNumber.cs_fromNativeType(NSDecimalNumber(string: Int64.max.description))?.int64Value, Int64.max) XCTAssertEqual(NSDecimalNumber.cs_fromQueryableNativeType(NSDecimalNumber(string: Int64.max.description))?.int64Value, Int64.max)
XCTAssertEqual(NSDecimalNumber.cs_fromNativeType(NSDecimalNumber(string: MAXFLOAT.description))?.doubleValue, NSDecimalNumber(string: MAXFLOAT.description).doubleValue) XCTAssertEqual(NSDecimalNumber.cs_fromQueryableNativeType(NSDecimalNumber(string: MAXFLOAT.description))?.doubleValue, NSDecimalNumber(string: MAXFLOAT.description).doubleValue)
XCTAssertEqual(NSDecimalNumber.cs_fromNativeType(NSDecimalNumber(string: MAXFLOAT.description))?.floatValue, NSDecimalNumber(string: MAXFLOAT.description).floatValue) XCTAssertEqual(NSDecimalNumber.cs_fromQueryableNativeType(NSDecimalNumber(string: MAXFLOAT.description))?.floatValue, NSDecimalNumber(string: MAXFLOAT.description).floatValue)
XCTAssertEqual(NSNumber.cs_fromNativeType(NSDecimalNumber(string: "1"))?.boolValue, true) XCTAssertEqual(NSNumber.cs_fromQueryableNativeType(NSDecimalNumber(string: "1"))?.boolValue, true)
XCTAssertEqual(NSNumber.cs_fromNativeType(NSDecimalNumber(string: Int16.max.description))?.int16Value, Int16.max) XCTAssertEqual(NSNumber.cs_fromQueryableNativeType(NSDecimalNumber(string: Int16.max.description))?.int16Value, Int16.max)
XCTAssertEqual(NSNumber.cs_fromNativeType(NSDecimalNumber(string: Int32.max.description))?.int32Value, Int32.max) XCTAssertEqual(NSNumber.cs_fromQueryableNativeType(NSDecimalNumber(string: Int32.max.description))?.int32Value, Int32.max)
XCTAssertEqual(NSNumber.cs_fromNativeType(NSDecimalNumber(string: Int64.max.description))?.int64Value, Int64.max) XCTAssertEqual(NSNumber.cs_fromQueryableNativeType(NSDecimalNumber(string: Int64.max.description))?.int64Value, Int64.max)
XCTAssertEqual(NSNumber.cs_fromNativeType(NSDecimalNumber(string: MAXFLOAT.description))?.doubleValue, NSDecimalNumber(string: MAXFLOAT.description).doubleValue) XCTAssertEqual(NSNumber.cs_fromQueryableNativeType(NSDecimalNumber(string: MAXFLOAT.description))?.doubleValue, NSDecimalNumber(string: MAXFLOAT.description).doubleValue)
XCTAssertEqual(NSNumber.cs_fromNativeType(NSDecimalNumber(string: MAXFLOAT.description))?.floatValue, NSDecimalNumber(string: MAXFLOAT.description).floatValue) XCTAssertEqual(NSNumber.cs_fromQueryableNativeType(NSDecimalNumber(string: MAXFLOAT.description))?.floatValue, NSDecimalNumber(string: MAXFLOAT.description).floatValue)
XCTAssertNil(NSDecimalNumber.cs_fromNativeType(NSNumber(value: true))) XCTAssertNil(NSDecimalNumber.cs_fromQueryableNativeType(NSNumber(value: true)))
XCTAssertNil(NSDecimalNumber.cs_fromNativeType(NSNumber(value: Int16.max))) XCTAssertNil(NSDecimalNumber.cs_fromQueryableNativeType(NSNumber(value: Int16.max)))
XCTAssertNil(NSDecimalNumber.cs_fromNativeType(NSNumber(value: Int32.max))) XCTAssertNil(NSDecimalNumber.cs_fromQueryableNativeType(NSNumber(value: Int32.max)))
XCTAssertNil(NSDecimalNumber.cs_fromNativeType(NSNumber(value: Int64.max))) XCTAssertNil(NSDecimalNumber.cs_fromQueryableNativeType(NSNumber(value: Int64.max)))
XCTAssertNil(NSDecimalNumber.cs_fromNativeType(NSNumber(value: MAXFLOAT))) XCTAssertNil(NSDecimalNumber.cs_fromQueryableNativeType(NSNumber(value: MAXFLOAT)))
XCTAssertNil(NSDecimalNumber.cs_fromNativeType(NSNumber(value: Double(MAXFLOAT)))) XCTAssertNil(NSDecimalNumber.cs_fromQueryableNativeType(NSNumber(value: Double(MAXFLOAT))))
XCTAssertEqual(true.cs_toNativeType(), NSNumber(value: true)) XCTAssertEqual(true.cs_toQueryableNativeType(), NSNumber(value: true))
XCTAssertEqual(Int16.max.cs_toNativeType(), NSNumber(value: Int16.max)) XCTAssertEqual(Int16.max.cs_toQueryableNativeType(), NSNumber(value: Int16.max))
XCTAssertEqual(Int32.max.cs_toNativeType(), NSNumber(value: Int32.max)) XCTAssertEqual(Int32.max.cs_toQueryableNativeType(), NSNumber(value: Int32.max))
XCTAssertEqual(Int64.max.cs_toNativeType(), NSNumber(value: Int64.max)) XCTAssertEqual(Int64.max.cs_toQueryableNativeType(), NSNumber(value: Int64.max))
XCTAssertEqual(MAXFLOAT.cs_toNativeType(), NSNumber(value: MAXFLOAT)) XCTAssertEqual(MAXFLOAT.cs_toQueryableNativeType(), NSNumber(value: MAXFLOAT))
XCTAssertEqual(Double(MAXFLOAT).cs_toNativeType(), NSNumber(value: Double(MAXFLOAT))) XCTAssertEqual(Double(MAXFLOAT).cs_toQueryableNativeType(), NSNumber(value: Double(MAXFLOAT)))
} }
@objc @objc
@@ -77,10 +77,11 @@ class ImportTests: BaseTestDataTestCase {
self.prepareStack { (stack) in self.prepareStack { (stack) in
stack.beginSynchronous { (transaction) in
do { do {
try stack.perform(
synchronous: { (transaction) in
let object = try transaction.importObject( let object = try transaction.importObject(
Into<TestEntity1>(), Into<TestEntity1>(),
source: [ source: [
@@ -96,11 +97,13 @@ class ImportTests: BaseTestDataTestCase {
XCTAssertNil(object) XCTAssertNil(object)
XCTAssertEqual(transaction.fetchCount(From<TestEntity1>()), 0) XCTAssertEqual(transaction.fetchCount(From<TestEntity1>()), 0)
} }
)
}
catch { catch {
XCTFail() XCTFail()
} }
} XCTAssertEqual(stack.fetchCount(From<TestEntity1>()), 0)
} }
} }
@@ -109,12 +112,15 @@ class ImportTests: BaseTestDataTestCase {
self.prepareStack { (stack) in self.prepareStack { (stack) in
stack.beginSynchronous { (transaction) in
let errorExpectation = self.expectation(description: "error")
do { do {
let _ = try transaction.importObject( let errorExpectation = self.expectation(description: "error")
try stack.perform(
synchronous: { (transaction) in
do {
_ = try transaction.importObject(
Into<TestEntity1>(), Into<TestEntity1>(),
source: [ source: [
#keyPath(TestEntity1.testBoolean): NSNumber(value: true), #keyPath(TestEntity1.testBoolean): NSNumber(value: true),
@@ -143,13 +149,14 @@ class ImportTests: BaseTestDataTestCase {
XCTAssertNil(object?.testData) XCTAssertNil(object?.testData)
XCTAssertNil(object?.testDate) XCTAssertNil(object?.testDate)
} }
}
)
self.checkExpectationsImmediately()
}
catch { catch {
XCTFail() XCTFail()
} }
self.checkExpectationsImmediately()
transaction.context.reset()
}
} }
} }
@@ -158,10 +165,11 @@ class ImportTests: BaseTestDataTestCase {
self.prepareStack { (stack) in self.prepareStack { (stack) in
stack.beginSynchronous { (transaction) in
do { do {
try stack.perform(
synchronous: { (transaction) in
let object = try transaction.importObject( let object = try transaction.importObject(
Into<TestEntity1>(), Into<TestEntity1>(),
source: [ source: [
@@ -203,12 +211,12 @@ class ImportTests: BaseTestDataTestCase {
XCTAssertEqual(object?.testData, ("nil:TestEntity1:2" as NSString).data(using: String.Encoding.utf8.rawValue)!) XCTAssertEqual(object?.testData, ("nil:TestEntity1:2" as NSString).data(using: String.Encoding.utf8.rawValue)!)
XCTAssertEqual(object?.testDate, self.dateFormatter.date(from: "2000-01-02T00:00:00Z")!) XCTAssertEqual(object?.testDate, self.dateFormatter.date(from: "2000-01-02T00:00:00Z")!)
} }
)
}
catch { catch {
XCTFail() XCTFail()
} }
transaction.context.reset()
}
} }
} }
@@ -217,10 +225,11 @@ class ImportTests: BaseTestDataTestCase {
self.prepareStack { (stack) in self.prepareStack { (stack) in
stack.beginSynchronous { (transaction) in
do { do {
try stack.perform(
synchronous: { (transaction) in
let sourceArray: [TestEntity1.ImportSource] = [ let sourceArray: [TestEntity1.ImportSource] = [
[ [
#keyPath(TestEntity1.testBoolean): NSNumber(value: true), #keyPath(TestEntity1.testBoolean): NSNumber(value: true),
@@ -257,12 +266,12 @@ class ImportTests: BaseTestDataTestCase {
XCTAssertEqual(object.testData, dictionary[(#keyPath(TestEntity1.testData))] as? Data) XCTAssertEqual(object.testData, dictionary[(#keyPath(TestEntity1.testData))] as? Data)
XCTAssertEqual(object.testDate, dictionary[(#keyPath(TestEntity1.testDate))] as? Date) XCTAssertEqual(object.testDate, dictionary[(#keyPath(TestEntity1.testDate))] as? Date)
} }
)
}
catch { catch {
XCTFail() XCTFail()
} }
transaction.context.reset()
}
} }
} }
@@ -271,9 +280,12 @@ class ImportTests: BaseTestDataTestCase {
self.prepareStack { (stack) in self.prepareStack { (stack) in
stack.beginSynchronous { (transaction) in do {
let errorExpectation = self.expectation(description: "error") let errorExpectation = self.expectation(description: "error")
try stack.perform(
synchronous: { (transaction) in
do { do {
let sourceArray: [TestEntity1.ImportSource] = [ let sourceArray: [TestEntity1.ImportSource] = [
@@ -316,13 +328,14 @@ class ImportTests: BaseTestDataTestCase {
XCTAssertNil(object?.testData) XCTAssertNil(object?.testData)
XCTAssertNil(object?.testDate) XCTAssertNil(object?.testDate)
} }
}
)
self.checkExpectationsImmediately()
}
catch { catch {
XCTFail() XCTFail()
} }
self.checkExpectationsImmediately()
transaction.context.reset()
}
} }
} }
@@ -331,10 +344,11 @@ class ImportTests: BaseTestDataTestCase {
self.prepareStack { (stack) in self.prepareStack { (stack) in
stack.beginSynchronous { (transaction) in
do { do {
try stack.perform(
synchronous: { (transaction) in
let sourceArray: [TestEntity1.ImportSource] = [ let sourceArray: [TestEntity1.ImportSource] = [
[ [
#keyPath(TestEntity1.testBoolean): NSNumber(value: true), #keyPath(TestEntity1.testBoolean): NSNumber(value: true),
@@ -374,12 +388,12 @@ class ImportTests: BaseTestDataTestCase {
XCTAssertEqual(object.testDate, dictionary[(#keyPath(TestEntity1.testDate))] as? Date) XCTAssertEqual(object.testDate, dictionary[(#keyPath(TestEntity1.testDate))] as? Date)
} }
} }
)
}
catch { catch {
XCTFail() XCTFail()
} }
transaction.context.reset()
}
} }
} }
@@ -389,8 +403,10 @@ class ImportTests: BaseTestDataTestCase {
self.prepareStack { (stack) in self.prepareStack { (stack) in
self.prepareTestDataForStack(stack) self.prepareTestDataForStack(stack)
do {
stack.beginSynchronous { (transaction) in try stack.perform(
synchronous: { (transaction) in
do { do {
@@ -410,10 +426,6 @@ class ImportTests: BaseTestDataTestCase {
XCTAssertNil(object) XCTAssertNil(object)
XCTAssertEqual(transaction.fetchCount(From<TestEntity1>()), 5) XCTAssertEqual(transaction.fetchCount(From<TestEntity1>()), 5)
} }
catch {
XCTFail()
}
do { do {
let object = try transaction.importUniqueObject( let object = try transaction.importUniqueObject(
@@ -432,7 +444,7 @@ class ImportTests: BaseTestDataTestCase {
XCTAssertNil(object) XCTAssertNil(object)
XCTAssertEqual(transaction.fetchCount(From<TestEntity1>()), 5) XCTAssertEqual(transaction.fetchCount(From<TestEntity1>()), 5)
let existingObjects = transaction.fetchAll(From<TestEntity1>(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 105)) let existingObjects = transaction.fetchAll(From<TestEntity1>(), Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 105))
XCTAssertNotNil(existingObjects) XCTAssertNotNil(existingObjects)
XCTAssertEqual(existingObjects?.count, 1) XCTAssertEqual(existingObjects?.count, 1)
@@ -445,13 +457,15 @@ class ImportTests: BaseTestDataTestCase {
XCTAssertEqual(existingObject?.testData, ("nil:TestEntity1:5" as NSString).data(using: String.Encoding.utf8.rawValue)!) XCTAssertEqual(existingObject?.testData, ("nil:TestEntity1:5" as NSString).data(using: String.Encoding.utf8.rawValue)!)
XCTAssertEqual(existingObject?.testDate, self.dateFormatter.date(from: "2000-01-05T00:00:00Z")!) XCTAssertEqual(existingObject?.testDate, self.dateFormatter.date(from: "2000-01-05T00:00:00Z")!)
} }
}
)
}
catch { catch {
XCTFail() XCTFail()
} }
} }
} }
}
@objc @objc
dynamic func test_ThatImportUniqueObjects_ImportsLastOfImportSourcesWithSameIDs() { dynamic func test_ThatImportUniqueObjects_ImportsLastOfImportSourcesWithSameIDs() {
@@ -459,11 +473,11 @@ class ImportTests: BaseTestDataTestCase {
self.prepareStack { (stack) in self.prepareStack { (stack) in
self.prepareTestDataForStack(stack) self.prepareTestDataForStack(stack)
stack.beginSynchronous { (transaction) in
do { do {
try stack.perform(
synchronous: { (transaction) in
let sourceArray: [TestEntity1.ImportSource] = [ let sourceArray: [TestEntity1.ImportSource] = [
[ [
#keyPath(TestEntity1.testEntityID): NSNumber(value: 106), #keyPath(TestEntity1.testEntityID): NSNumber(value: 106),
@@ -502,12 +516,12 @@ class ImportTests: BaseTestDataTestCase {
XCTAssertEqual(object.testData, dictionary[(#keyPath(TestEntity1.testData))] as? Data) XCTAssertEqual(object.testData, dictionary[(#keyPath(TestEntity1.testData))] as? Data)
XCTAssertEqual(object.testDate, dictionary[(#keyPath(TestEntity1.testDate))] as? Date) XCTAssertEqual(object.testDate, dictionary[(#keyPath(TestEntity1.testDate))] as? Date)
} }
)
}
catch { catch {
XCTFail() XCTFail()
} }
transaction.context.reset()
}
} }
} }
@@ -517,11 +531,11 @@ class ImportTests: BaseTestDataTestCase {
self.prepareStack { (stack) in self.prepareStack { (stack) in
self.prepareTestDataForStack(stack) self.prepareTestDataForStack(stack)
stack.beginSynchronous { (transaction) in
do { do {
try stack.perform(
synchronous: { (transaction) in
let sourceArray: [TestEntity1.ImportSource] = [ let sourceArray: [TestEntity1.ImportSource] = [
[ [
#keyPath(TestEntity1.testEntityID): NSNumber(value: 106), #keyPath(TestEntity1.testEntityID): NSNumber(value: 106),
@@ -551,7 +565,9 @@ class ImportTests: BaseTestDataTestCase {
XCTAssertEqual(objects.count, 2) XCTAssertEqual(objects.count, 2)
zip(objects, sourceArray) zip(objects, sourceArray)
.forEach { object, dictionary in .forEach {
let (object, dictionary) = $0
XCTAssertEqual(object.testEntityID, dictionary[(#keyPath(TestEntity1.testEntityID))] as? NSNumber) XCTAssertEqual(object.testEntityID, dictionary[(#keyPath(TestEntity1.testEntityID))] as? NSNumber)
XCTAssertEqual(object.testBoolean, dictionary[(#keyPath(TestEntity1.testBoolean))] as? NSNumber) XCTAssertEqual(object.testBoolean, dictionary[(#keyPath(TestEntity1.testBoolean))] as? NSNumber)
XCTAssertEqual(object.testNumber, dictionary[(#keyPath(TestEntity1.testNumber))] as? NSNumber) XCTAssertEqual(object.testNumber, dictionary[(#keyPath(TestEntity1.testNumber))] as? NSNumber)
@@ -561,12 +577,12 @@ class ImportTests: BaseTestDataTestCase {
XCTAssertEqual(object.testDate, dictionary[(#keyPath(TestEntity1.testDate))] as? Date) XCTAssertEqual(object.testDate, dictionary[(#keyPath(TestEntity1.testDate))] as? Date)
} }
} }
)
}
catch { catch {
XCTFail() XCTFail()
} }
transaction.context.reset()
}
} }
} }
@@ -576,11 +592,11 @@ class ImportTests: BaseTestDataTestCase {
self.prepareStack { (stack) in self.prepareStack { (stack) in
self.prepareTestDataForStack(stack) self.prepareTestDataForStack(stack)
stack.beginSynchronous { (transaction) in
do { do {
try stack.perform(
synchronous: { (transaction) in
let errorExpectation = self.expectation(description: "error") let errorExpectation = self.expectation(description: "error")
do { do {
@@ -604,7 +620,7 @@ class ImportTests: BaseTestDataTestCase {
errorExpectation.fulfill() errorExpectation.fulfill()
XCTAssertEqual(transaction.fetchCount(From<TestEntity1>()), 6) XCTAssertEqual(transaction.fetchCount(From<TestEntity1>()), 6)
let object = transaction.fetchOne(From<TestEntity1>(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 106)) let object = transaction.fetchOne(From<TestEntity1>(), Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 106))
XCTAssertNotNil(object) XCTAssertNotNil(object)
XCTAssertEqual(object?.testEntityID, NSNumber(value: 106)) XCTAssertEqual(object?.testEntityID, NSNumber(value: 106))
XCTAssertNil(object?.testBoolean) XCTAssertNil(object?.testBoolean)
@@ -614,13 +630,11 @@ class ImportTests: BaseTestDataTestCase {
XCTAssertNil(object?.testData) XCTAssertNil(object?.testData)
XCTAssertNil(object?.testDate) XCTAssertNil(object?.testDate)
} }
catch {
XCTFail()
}
self.checkExpectationsImmediately() self.checkExpectationsImmediately()
} }
do { )
try stack.perform(
synchronous: { (transaction) in
let errorExpectation = self.expectation(description: "error") let errorExpectation = self.expectation(description: "error")
do { do {
@@ -645,7 +659,7 @@ class ImportTests: BaseTestDataTestCase {
errorExpectation.fulfill() errorExpectation.fulfill()
XCTAssertEqual(transaction.fetchCount(From<TestEntity1>()), 6) XCTAssertEqual(transaction.fetchCount(From<TestEntity1>()), 6)
let existingObjects = transaction.fetchAll(From<TestEntity1>(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 105)) let existingObjects = transaction.fetchAll(From<TestEntity1>(), Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 105))
XCTAssertNotNil(existingObjects) XCTAssertNotNil(existingObjects)
XCTAssertEqual(existingObjects?.count, 1) XCTAssertEqual(existingObjects?.count, 1)
@@ -659,14 +673,14 @@ class ImportTests: BaseTestDataTestCase {
XCTAssertEqual(existingObject?.testData, ("nil:TestEntity1:5" as NSString).data(using: String.Encoding.utf8.rawValue)!) XCTAssertEqual(existingObject?.testData, ("nil:TestEntity1:5" as NSString).data(using: String.Encoding.utf8.rawValue)!)
XCTAssertEqual(existingObject?.testDate, self.dateFormatter.date(from: "2000-01-05T00:00:00Z")!) XCTAssertEqual(existingObject?.testDate, self.dateFormatter.date(from: "2000-01-05T00:00:00Z")!)
} }
self.checkExpectationsImmediately()
}
)
}
catch { catch {
XCTFail() XCTFail()
} }
self.checkExpectationsImmediately()
}
transaction.context.reset()
}
} }
} }
@@ -676,8 +690,10 @@ class ImportTests: BaseTestDataTestCase {
self.prepareStack { (stack) in self.prepareStack { (stack) in
self.prepareTestDataForStack(stack) self.prepareTestDataForStack(stack)
do {
stack.beginSynchronous { (transaction) in try stack.perform(
synchronous: { (transaction) in
do { do {
@@ -704,10 +720,6 @@ class ImportTests: BaseTestDataTestCase {
XCTAssertEqual(object?.testData, ("nil:TestEntity1:6" as NSString).data(using: String.Encoding.utf8.rawValue)!) XCTAssertEqual(object?.testData, ("nil:TestEntity1:6" as NSString).data(using: String.Encoding.utf8.rawValue)!)
XCTAssertEqual(object?.testDate, self.dateFormatter.date(from: "2000-01-06T00:00:00Z")!) XCTAssertEqual(object?.testDate, self.dateFormatter.date(from: "2000-01-06T00:00:00Z")!)
} }
catch {
XCTFail()
}
do { do {
let object = try transaction.importUniqueObject( let object = try transaction.importUniqueObject(
@@ -733,19 +745,20 @@ class ImportTests: BaseTestDataTestCase {
XCTAssertEqual(object?.testData, ("nil:TestEntity1:7" as NSString).data(using: String.Encoding.utf8.rawValue)!) XCTAssertEqual(object?.testData, ("nil:TestEntity1:7" as NSString).data(using: String.Encoding.utf8.rawValue)!)
XCTAssertEqual(object?.testDate, self.dateFormatter.date(from: "2000-01-07T00:00:00Z")!) XCTAssertEqual(object?.testDate, self.dateFormatter.date(from: "2000-01-07T00:00:00Z")!)
let existingObjects = transaction.fetchAll(From<TestEntity1>(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 106)) let existingObjects = transaction.fetchAll(From<TestEntity1>(), Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 106))
XCTAssertNotNil(existingObjects) XCTAssertNotNil(existingObjects)
XCTAssertEqual(existingObjects?.count, 1) XCTAssertEqual(existingObjects?.count, 1)
let existingObject = existingObjects?[0] let existingObject = existingObjects?[0]
XCTAssertEqual(existingObject, object) XCTAssertEqual(existingObject, object)
} }
}
)
}
catch { catch {
XCTFail() XCTFail()
} }
transaction.context.reset()
}
} }
} }
@@ -755,11 +768,11 @@ class ImportTests: BaseTestDataTestCase {
self.prepareStack { (stack) in self.prepareStack { (stack) in
self.prepareTestDataForStack(stack) self.prepareTestDataForStack(stack)
stack.beginSynchronous { (transaction) in
do { do {
try stack.perform(
synchronous: { (transaction) in
let sourceArray: [TestEntity1.ImportSource] = [ let sourceArray: [TestEntity1.ImportSource] = [
[ [
#keyPath(TestEntity1.testEntityID): NSNumber(value: 106), #keyPath(TestEntity1.testEntityID): NSNumber(value: 106),
@@ -798,12 +811,12 @@ class ImportTests: BaseTestDataTestCase {
XCTAssertEqual(object.testData, dictionary[(#keyPath(TestEntity1.testData))] as? Data) XCTAssertEqual(object.testData, dictionary[(#keyPath(TestEntity1.testData))] as? Data)
XCTAssertEqual(object.testDate, dictionary[(#keyPath(TestEntity1.testDate))] as? Date) XCTAssertEqual(object.testDate, dictionary[(#keyPath(TestEntity1.testDate))] as? Date)
} }
)
}
catch { catch {
XCTFail() XCTFail()
} }
transaction.context.reset()
}
} }
} }
@@ -813,8 +826,10 @@ class ImportTests: BaseTestDataTestCase {
self.prepareStack { (stack) in self.prepareStack { (stack) in
self.prepareTestDataForStack(stack) self.prepareTestDataForStack(stack)
do {
stack.beginSynchronous { (transaction) in try stack.perform(
synchronous: { (transaction) in
let errorExpectation = self.expectation(description: "error") let errorExpectation = self.expectation(description: "error")
do { do {
@@ -851,17 +866,15 @@ class ImportTests: BaseTestDataTestCase {
errorExpectation.fulfill() errorExpectation.fulfill()
XCTAssertEqual(transaction.fetchCount(From<TestEntity1>()), 5) XCTAssertEqual(transaction.fetchCount(From<TestEntity1>()), 5)
XCTAssertNil(transaction.fetchOne(From<TestEntity1>(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 106))) XCTAssertNil(transaction.fetchOne(From<TestEntity1>(), Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 106)))
XCTAssertNil(transaction.fetchOne(From<TestEntity1>(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 107))) XCTAssertNil(transaction.fetchOne(From<TestEntity1>(), Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 107)))
}
catch {
XCTFail()
} }
transaction.unsafeContext().reset()
self.checkExpectationsImmediately() self.checkExpectationsImmediately()
transaction.context.reset()
} }
stack.beginSynchronous { (transaction) in )
try stack.perform(
synchronous: { (transaction) in
let errorExpectation = self.expectation(description: "error") let errorExpectation = self.expectation(description: "error")
do { do {
@@ -897,7 +910,7 @@ class ImportTests: BaseTestDataTestCase {
errorExpectation.fulfill() errorExpectation.fulfill()
let object = transaction.fetchOne(From<TestEntity1>(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 106)) let object = transaction.fetchOne(From<TestEntity1>(), Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 106))
XCTAssertNotNil(object) XCTAssertNotNil(object)
XCTAssertEqual(object?.testEntityID, NSNumber(value: 106)) XCTAssertEqual(object?.testEntityID, NSNumber(value: 106))
XCTAssertNil(object?.testBoolean) XCTAssertNil(object?.testBoolean)
@@ -907,14 +920,12 @@ class ImportTests: BaseTestDataTestCase {
XCTAssertNil(object?.testData) XCTAssertNil(object?.testData)
XCTAssertNil(object?.testDate) XCTAssertNil(object?.testDate)
} }
catch {
XCTFail()
}
self.checkExpectationsImmediately()
transaction.context.reset() transaction.context.reset()
self.checkExpectationsImmediately()
} }
stack.beginSynchronous { (transaction) in )
try stack.perform(
synchronous: { (transaction) in
let errorExpectation = self.expectation(description: "error") let errorExpectation = self.expectation(description: "error")
do { do {
@@ -942,7 +953,7 @@ class ImportTests: BaseTestDataTestCase {
errorExpectation.fulfill() errorExpectation.fulfill()
XCTAssertEqual(transaction.fetchCount(From<TestEntity1>()), 5) XCTAssertEqual(transaction.fetchCount(From<TestEntity1>()), 5)
let object = transaction.fetchOne(From<TestEntity1>(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 105)) let object = transaction.fetchOne(From<TestEntity1>(), Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 105))
XCTAssertNotNil(object) XCTAssertNotNil(object)
XCTAssertEqual(object?.testEntityID, NSNumber(value: 105)) XCTAssertEqual(object?.testEntityID, NSNumber(value: 105))
XCTAssertEqual(object?.testBoolean, NSNumber(value: true)) XCTAssertEqual(object?.testBoolean, NSNumber(value: true))
@@ -952,20 +963,22 @@ class ImportTests: BaseTestDataTestCase {
XCTAssertEqual(object?.testData, ("nil:TestEntity1:5" as NSString).data(using: String.Encoding.utf8.rawValue)!) XCTAssertEqual(object?.testData, ("nil:TestEntity1:5" as NSString).data(using: String.Encoding.utf8.rawValue)!)
XCTAssertEqual(object?.testDate, self.dateFormatter.date(from: "2000-01-05T00:00:00Z")!) XCTAssertEqual(object?.testDate, self.dateFormatter.date(from: "2000-01-05T00:00:00Z")!)
let existingObjects = transaction.fetchAll(From<TestEntity1>(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 105)) let existingObjects = transaction.fetchAll(From<TestEntity1>(), Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 105))
XCTAssertNotNil(existingObjects) XCTAssertNotNil(existingObjects)
XCTAssertEqual(existingObjects?.count, 1) XCTAssertEqual(existingObjects?.count, 1)
let existingObject = existingObjects?[0] let existingObject = existingObjects?[0]
XCTAssertEqual(existingObject, object) XCTAssertEqual(existingObject, object)
} }
transaction.context.reset()
self.checkExpectationsImmediately()
}
)
}
catch { catch {
XCTFail() XCTFail()
} }
self.checkExpectationsImmediately()
transaction.context.reset()
}
} }
} }
@@ -975,11 +988,11 @@ class ImportTests: BaseTestDataTestCase {
self.prepareStack { (stack) in self.prepareStack { (stack) in
self.prepareTestDataForStack(stack) self.prepareTestDataForStack(stack)
stack.beginSynchronous { (transaction) in
do { do {
try stack.perform(
synchronous: { (transaction) in
let sourceArray: [TestEntity1.ImportSource] = [ let sourceArray: [TestEntity1.ImportSource] = [
[ [
#keyPath(TestEntity1.testEntityID): NSNumber(value: 105), #keyPath(TestEntity1.testEntityID): NSNumber(value: 105),
@@ -1019,19 +1032,19 @@ class ImportTests: BaseTestDataTestCase {
XCTAssertEqual(object.testData, dictionary[(#keyPath(TestEntity1.testData))] as? Data) XCTAssertEqual(object.testData, dictionary[(#keyPath(TestEntity1.testData))] as? Data)
XCTAssertEqual(object.testDate, dictionary[(#keyPath(TestEntity1.testDate))] as? Date) XCTAssertEqual(object.testDate, dictionary[(#keyPath(TestEntity1.testDate))] as? Date)
} }
let existingObjects = transaction.fetchAll(From<TestEntity1>(), Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 105)) let existingObjects = transaction.fetchAll(From<TestEntity1>(), Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 105))
XCTAssertNotNil(existingObjects) XCTAssertNotNil(existingObjects)
XCTAssertEqual(existingObjects?.count, 1) XCTAssertEqual(existingObjects?.count, 1)
let existingObject = existingObjects?[0] let existingObject = existingObjects?[0]
XCTAssertEqual(existingObject, objects[0]) XCTAssertEqual(existingObject, objects[0])
} }
)
}
catch { catch {
XCTFail() XCTFail()
} }
transaction.context.reset()
}
} }
} }
} }
@@ -1083,42 +1096,25 @@ extension TestEntity1: ImportableUniqueObject {
// MARK: ImportableUniqueObject // MARK: ImportableUniqueObject
typealias UniqueIDType = NSNumber typealias UniqueIDType = Int64
static var uniqueIDKeyPath: String { static var uniqueIDKeyPath: String {
return #keyPath(TestEntity1.testEntityID) return #keyPath(TestEntity1.testEntityID)
} }
var uniqueIDValue: NSNumber {
get {
guard let ID = self.testEntityID else {
XCTFail()
return 0
}
return ID
}
set {
self.testEntityID = newValue
}
}
static func shouldUpdate(from source: ImportSource, in transaction: BaseDataTransaction) -> Bool { static func shouldUpdate(from source: ImportSource, in transaction: BaseDataTransaction) -> Bool {
return source["skip_update"] == nil return source["skip_update"] == nil
} }
static func uniqueID(from source: ImportSource, in transaction: BaseDataTransaction) throws -> NSNumber? { static func uniqueID(from source: ImportSource, in transaction: BaseDataTransaction) throws -> Int64? {
if let _ = source["throw_on_id"] { if let _ = source["throw_on_id"] {
throw TestIDError() throw TestIDError()
} }
return source[(#keyPath(TestEntity1.testEntityID))] as? NSNumber return source[(#keyPath(TestEntity1.testEntityID))] as? Int64
} }
func update(from source: ImportSource, in transaction: BaseDataTransaction) throws { func update(from source: ImportSource, in transaction: BaseDataTransaction) throws {
+12 -77
View File
@@ -36,7 +36,7 @@ final class IntoTests: XCTestCase {
@objc @objc
dynamic func test_ThatIntoClauseConstants_AreCorrect() { dynamic func test_ThatIntoClauseConstants_AreCorrect() {
XCTAssertEqual(Into<NSManagedObject>.defaultConfigurationName, "PF_DEFAULT_CONFIGURATION_NAME") XCTAssertEqual(DataStack.defaultConfigurationName, "PF_DEFAULT_CONFIGURATION_NAME")
} }
@objc @objc
@@ -44,7 +44,7 @@ final class IntoTests: XCTestCase {
do { do {
let into = Into() let into = Into<NSManagedObject>()
XCTAssert(into.entityClass === NSManagedObject.self) XCTAssert(into.entityClass === NSManagedObject.self)
XCTAssertNil(into.configuration) XCTAssertNil(into.configuration)
XCTAssertTrue(into.inferStoreIfPossible) XCTAssertTrue(into.inferStoreIfPossible)
@@ -58,14 +58,7 @@ final class IntoTests: XCTestCase {
} }
do { do {
let into = Into<TestEntity1>() let into = Into(TestEntity1.self)
XCTAssert(into.entityClass === TestEntity1.self)
XCTAssertNil(into.configuration)
XCTAssertTrue(into.inferStoreIfPossible)
}
do {
let into = Into(TestEntity1.self as AnyClass)
XCTAssert(into.entityClass === TestEntity1.self) XCTAssert(into.entityClass === TestEntity1.self)
XCTAssertNil(into.configuration) XCTAssertNil(into.configuration)
XCTAssertTrue(into.inferStoreIfPossible) XCTAssertTrue(into.inferStoreIfPossible)
@@ -84,13 +77,6 @@ final class IntoTests: XCTestCase {
XCTAssertEqual(into.configuration, "Config1") XCTAssertEqual(into.configuration, "Config1")
XCTAssertFalse(into.inferStoreIfPossible) XCTAssertFalse(into.inferStoreIfPossible)
} }
do {
let into = Into(TestEntity1.self as AnyClass, "Config1")
XCTAssert(into.entityClass === TestEntity1.self)
XCTAssertEqual(into.configuration, "Config1")
XCTAssertFalse(into.inferStoreIfPossible)
}
} }
@objc @objc
@@ -98,43 +84,30 @@ final class IntoTests: XCTestCase {
do { do {
let into = Into() let into = Into<NSManagedObject>()
XCTAssertEqual(into, Into())
XCTAssertEqual(into, Into<NSManagedObject>()) XCTAssertEqual(into, Into<NSManagedObject>())
XCTAssertEqual(into, Into(NSManagedObject.self as AnyClass)) XCTAssertEqual(into, Into(NSManagedObject.self))
XCTAssertFalse(into == Into<TestEntity1>()) XCTAssertNotEqual(into, Into<NSManagedObject>(TestEntity1.self))
XCTAssertNotEqual(into, Into<NSManagedObject>("Config1")) XCTAssertNotEqual(into, Into<NSManagedObject>("Config1"))
} }
do { do {
let into = Into<TestEntity1>() let into = Into<TestEntity1>()
XCTAssertEqual(into, Into<TestEntity1>()) XCTAssertEqual(into, Into<TestEntity1>())
XCTAssertEqual(into, Into(TestEntity1.self as AnyClass)) XCTAssertEqual(into, Into(TestEntity1.self))
XCTAssertFalse(into == Into<TestEntity2>())
XCTAssertNotEqual(into, Into<TestEntity1>("Config1")) XCTAssertNotEqual(into, Into<TestEntity1>("Config1"))
} }
do { do {
let into = Into<TestEntity1>() let into = Into(TestEntity1.self)
XCTAssertEqual(into, Into<TestEntity1>())
XCTAssertEqual(into, Into(TestEntity1.self as AnyClass))
XCTAssertFalse(into == Into<TestEntity2>())
XCTAssertNotEqual(into, Into<TestEntity1>("Config1"))
}
do {
let into = Into(TestEntity1.self as AnyClass)
XCTAssert(into == Into<TestEntity1>()) XCTAssert(into == Into<TestEntity1>())
XCTAssertEqual(into, Into(TestEntity1.self)) XCTAssertEqual(into, Into(TestEntity1.self))
XCTAssertFalse(into == Into<TestEntity2>())
XCTAssertFalse(into == Into<TestEntity1>("Config1")) XCTAssertFalse(into == Into<TestEntity1>("Config1"))
} }
do { do {
let into = Into<TestEntity1>("Config1") let into = Into<TestEntity1>("Config1")
XCTAssertEqual(into, Into(TestEntity1.self, "Config1")) XCTAssertEqual(into, Into(TestEntity1.self, "Config1"))
XCTAssertEqual(into, Into(TestEntity1.self as AnyClass, "Config1"))
XCTAssertFalse(into == Into<TestEntity2>("Config1"))
XCTAssertNotEqual(into, Into<TestEntity1>("Config2")) XCTAssertNotEqual(into, Into<TestEntity1>("Config2"))
} }
do { do {
@@ -142,16 +115,14 @@ final class IntoTests: XCTestCase {
let into = Into(TestEntity1.self, "Config1") let into = Into(TestEntity1.self, "Config1")
XCTAssertEqual(into, Into(TestEntity1.self, "Config1")) XCTAssertEqual(into, Into(TestEntity1.self, "Config1"))
XCTAssertEqual(into, Into<TestEntity1>("Config1")) XCTAssertEqual(into, Into<TestEntity1>("Config1"))
XCTAssertFalse(into == Into<TestEntity2>("Config1"))
XCTAssertNotEqual(into, Into<TestEntity1>("Config2")) XCTAssertNotEqual(into, Into<TestEntity1>("Config2"))
} }
do { do {
let into = Into(TestEntity1.self as AnyClass, "Config1") let into = Into(TestEntity1.self, "Config1")
XCTAssert(into == Into<TestEntity1>("Config1")) XCTAssertEqual(into, Into<TestEntity1>("Config1"))
XCTAssertEqual(into, Into(TestEntity1.self, "Config1")) XCTAssertEqual(into, Into(TestEntity1.self, "Config1"))
XCTAssertFalse(into == Into<TestEntity2>("Config1")) XCTAssertNotEqual(into, Into<TestEntity1>("Config2"))
XCTAssertFalse(into == Into<TestEntity1>("Config2"))
} }
} }
@@ -160,45 +131,9 @@ final class IntoTests: XCTestCase {
do { do {
let into = Into() let into = Into<NSManagedObject>()
let objcInto = into.bridgeToObjectiveC let objcInto = into.bridgeToObjectiveC
XCTAssertEqual(into, objcInto.bridgeToSwift) XCTAssertEqual(into, objcInto.bridgeToSwift)
} }
do {
let into = Into<TestEntity1>()
let objcInto = into.bridgeToObjectiveC
XCTAssertTrue(into == objcInto.bridgeToSwift)
}
do {
let into = Into(TestEntity1.self as AnyClass)
let objcInto = into.bridgeToObjectiveC
XCTAssertEqual(into, objcInto.bridgeToSwift)
}
do {
let into = Into(TestEntity1.self as AnyClass)
let objcInto = into.bridgeToObjectiveC
XCTAssertEqual(into, objcInto.bridgeToSwift)
}
do {
let into = Into<TestEntity1>("Config1")
let objcInto = into.bridgeToObjectiveC
XCTAssertTrue(into == objcInto.bridgeToSwift)
}
do {
let into = Into(TestEntity1.self, "Config1")
let objcInto = into.bridgeToObjectiveC
XCTAssertTrue(into == objcInto.bridgeToSwift)
}
do {
let into = Into(TestEntity1.self as AnyClass, "Config1")
let objcInto = into.bridgeToObjectiveC
XCTAssertTrue(into == objcInto.bridgeToSwift)
}
} }
} }
+69 -75
View File
@@ -29,10 +29,9 @@ import XCTest
import CoreStore import CoreStore
#if os(iOS) || os(watchOS) || os(tvOS)
// MARK: - ListObserverTests // MARK: - ListObserverTests
@available(OSX 10.12, *)
class ListObserverTests: BaseTestDataTestCase { class ListObserverTests: BaseTestDataTestCase {
@objc @objc
@@ -44,7 +43,7 @@ class ListObserverTests: BaseTestDataTestCase {
let monitor = stack.monitorSectionedList( let monitor = stack.monitorSectionedList(
From<TestEntity1>(), From<TestEntity1>(),
SectionBy(#keyPath(TestEntity1.testBoolean)), SectionBy(#keyPath(TestEntity1.testBoolean)),
OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID))) OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID)))
) )
monitor.addObserver(observer) monitor.addObserver(observer)
@@ -55,7 +54,7 @@ class ListObserverTests: BaseTestDataTestCase {
var events = 0 var events = 0
let willChangeExpectation = self.expectation( let willChangeExpectation = self.expectation(
forNotification: "listMonitorWillChange:", forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"),
object: observer, object: observer,
handler: { (note) -> Bool in handler: { (note) -> Bool in
@@ -69,7 +68,7 @@ class ListObserverTests: BaseTestDataTestCase {
} }
) )
let didInsertSectionExpectation = self.expectation( let didInsertSectionExpectation = self.expectation(
forNotification: "listMonitor:didInsertSection:toSectionIndex:", forNotification: NSNotification.Name(rawValue: "listMonitor:didInsertSection:toSectionIndex:"),
object: observer, object: observer,
handler: { (note) -> Bool in handler: { (note) -> Bool in
@@ -89,7 +88,7 @@ class ListObserverTests: BaseTestDataTestCase {
} }
) )
let didInsertObjectExpectation = self.expectation( let didInsertObjectExpectation = self.expectation(
forNotification: "listMonitor:didInsertObject:toIndexPath:", forNotification: NSNotification.Name(rawValue: "listMonitor:didInsertObject:toIndexPath:"),
object: observer, object: observer,
handler: { (note) -> Bool in handler: { (note) -> Bool in
@@ -103,8 +102,8 @@ class ListObserverTests: BaseTestDataTestCase {
) )
let indexPath = userInfo?["indexPath"] as? NSIndexPath let indexPath = userInfo?["indexPath"] as? NSIndexPath
XCTAssertEqual(indexPath?.section, 0) XCTAssertEqual(indexPath?.index(atPosition: 0), 0)
XCTAssertEqual(indexPath?.row, 0) XCTAssertEqual(indexPath?.index(atPosition: 1), 0)
let object = userInfo?["object"] as? TestEntity1 let object = userInfo?["object"] as? TestEntity1
XCTAssertEqual(object?.testBoolean, NSNumber(value: true)) XCTAssertEqual(object?.testBoolean, NSNumber(value: true))
@@ -121,7 +120,7 @@ class ListObserverTests: BaseTestDataTestCase {
} }
) )
let didChangeExpectation = self.expectation( let didChangeExpectation = self.expectation(
forNotification: "listMonitorDidChange:", forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"),
object: observer, object: observer,
handler: { (note) -> Bool in handler: { (note) -> Bool in
@@ -134,7 +133,8 @@ class ListObserverTests: BaseTestDataTestCase {
} }
) )
let saveExpectation = self.expectation(description: "save") let saveExpectation = self.expectation(description: "save")
stack.beginAsynchronous { (transaction) in stack.perform(
asynchronous: { (transaction) -> Bool in
let object = transaction.create(Into<TestEntity1>()) let object = transaction.create(Into<TestEntity1>())
object.testBoolean = NSNumber(value: true) object.testBoolean = NSNumber(value: true)
@@ -144,19 +144,18 @@ class ListObserverTests: BaseTestDataTestCase {
object.testData = ("nil:TestEntity1:1" as NSString).data(using: String.Encoding.utf8.rawValue)! object.testData = ("nil:TestEntity1:1" as NSString).data(using: String.Encoding.utf8.rawValue)!
object.testDate = self.dateFormatter.date(from: "2000-01-01T00:00:00Z")! object.testDate = self.dateFormatter.date(from: "2000-01-01T00:00:00Z")!
transaction.commit { (result) in return transaction.hasChanges
},
success: { (hasChanges) in
switch result {
case .success(let hasChanges):
XCTAssertTrue(hasChanges) XCTAssertTrue(hasChanges)
saveExpectation.fulfill() saveExpectation.fulfill()
},
failure: { _ in
case .failure:
XCTFail() XCTFail()
} }
} )
}
self.waitAndCheckExpectations() self.waitAndCheckExpectations()
} }
} }
@@ -172,7 +171,7 @@ class ListObserverTests: BaseTestDataTestCase {
let monitor = stack.monitorSectionedList( let monitor = stack.monitorSectionedList(
From<TestEntity1>(), From<TestEntity1>(),
SectionBy(#keyPath(TestEntity1.testBoolean)), SectionBy(#keyPath(TestEntity1.testBoolean)),
OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID))) OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID)))
) )
monitor.addObserver(observer) monitor.addObserver(observer)
@@ -186,7 +185,7 @@ class ListObserverTests: BaseTestDataTestCase {
var events = 0 var events = 0
let willChangeExpectation = self.expectation( let willChangeExpectation = self.expectation(
forNotification: "listMonitorWillChange:", forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"),
object: observer, object: observer,
handler: { (note) -> Bool in handler: { (note) -> Bool in
@@ -199,10 +198,9 @@ class ListObserverTests: BaseTestDataTestCase {
return events == 0 return events == 0
} }
) )
for _ in 1 ... 2 {
let didUpdateObjectExpectation = self.expectation( let didUpdateObjectExpectation = self.expectation(
forNotification: "listMonitor:didUpdateObject:atIndexPath:", forNotification: NSNotification.Name(rawValue: "listMonitor:didUpdateObject:atIndexPath:"),
object: observer, object: observer,
handler: { (note) -> Bool in handler: { (note) -> Bool in
@@ -221,8 +219,8 @@ class ListObserverTests: BaseTestDataTestCase {
switch object?.testEntityID { switch object?.testEntityID {
case NSNumber(value: 101)?: case NSNumber(value: 101)?:
XCTAssertEqual(indexPath?.section, 1) XCTAssertEqual(indexPath?.index(atPosition: 0), 1)
XCTAssertEqual(indexPath?.row, 0) XCTAssertEqual(indexPath?.index(atPosition: 1), 0)
XCTAssertEqual(object?.testBoolean, NSNumber(value: true)) XCTAssertEqual(object?.testBoolean, NSNumber(value: true))
XCTAssertEqual(object?.testNumber, NSNumber(value: 11)) XCTAssertEqual(object?.testNumber, NSNumber(value: 11))
@@ -232,8 +230,8 @@ class ListObserverTests: BaseTestDataTestCase {
XCTAssertEqual(object?.testDate, self.dateFormatter.date(from: "2000-01-11T00:00:00Z")!) XCTAssertEqual(object?.testDate, self.dateFormatter.date(from: "2000-01-11T00:00:00Z")!)
case NSNumber(value: 102)?: case NSNumber(value: 102)?:
XCTAssertEqual(indexPath?.section, 0) XCTAssertEqual(indexPath?.index(atPosition: 0), 0)
XCTAssertEqual(indexPath?.row, 0) XCTAssertEqual(indexPath?.index(atPosition: 1), 0)
XCTAssertEqual(object?.testBoolean, NSNumber(value: false)) XCTAssertEqual(object?.testBoolean, NSNumber(value: false))
XCTAssertEqual(object?.testNumber, NSNumber(value: 22)) XCTAssertEqual(object?.testNumber, NSNumber(value: 22))
@@ -252,9 +250,8 @@ class ListObserverTests: BaseTestDataTestCase {
return events == 1 || events == 2 return events == 1 || events == 2
} }
) )
}
let didChangeExpectation = self.expectation( let didChangeExpectation = self.expectation(
forNotification: "listMonitorDidChange:", forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"),
object: observer, object: observer,
handler: { (note) -> Bool in handler: { (note) -> Bool in
@@ -268,11 +265,12 @@ class ListObserverTests: BaseTestDataTestCase {
} }
) )
let saveExpectation = self.expectation(description: "save") let saveExpectation = self.expectation(description: "save")
stack.beginAsynchronous { (transaction) in stack.perform(
asynchronous: { (transaction) -> Bool in
if let object = transaction.fetchOne( if let object = transaction.fetchOne(
From<TestEntity1>(), From<TestEntity1>(),
Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) { Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) {
object.testNumber = NSNumber(value: 11) object.testNumber = NSNumber(value: 11)
object.testDecimal = NSDecimalNumber(string: "11") object.testDecimal = NSDecimalNumber(string: "11")
@@ -286,7 +284,7 @@ class ListObserverTests: BaseTestDataTestCase {
} }
if let object = transaction.fetchOne( if let object = transaction.fetchOne(
From<TestEntity1>(), From<TestEntity1>(),
Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) { Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) {
object.testNumber = NSNumber(value: 22) object.testNumber = NSNumber(value: 22)
object.testDecimal = NSDecimalNumber(string: "22") object.testDecimal = NSDecimalNumber(string: "22")
@@ -298,19 +296,18 @@ class ListObserverTests: BaseTestDataTestCase {
XCTFail() XCTFail()
} }
transaction.commit { (result) in return transaction.hasChanges
},
success: { (hasChanges) in
switch result {
case .success(let hasChanges):
XCTAssertTrue(hasChanges) XCTAssertTrue(hasChanges)
saveExpectation.fulfill() saveExpectation.fulfill()
},
failure: { _ in
case .failure:
XCTFail() XCTFail()
} }
} )
}
self.waitAndCheckExpectations() self.waitAndCheckExpectations()
} }
} }
@@ -326,14 +323,14 @@ class ListObserverTests: BaseTestDataTestCase {
let monitor = stack.monitorSectionedList( let monitor = stack.monitorSectionedList(
From<TestEntity1>(), From<TestEntity1>(),
SectionBy(#keyPath(TestEntity1.testBoolean)), SectionBy(#keyPath(TestEntity1.testBoolean)),
OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID))) OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID)))
) )
monitor.addObserver(observer) monitor.addObserver(observer)
var events = 0 var events = 0
let willChangeExpectation = self.expectation( let willChangeExpectation = self.expectation(
forNotification: "listMonitorWillChange:", forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"),
object: observer, object: observer,
handler: { (note) -> Bool in handler: { (note) -> Bool in
@@ -347,7 +344,7 @@ class ListObserverTests: BaseTestDataTestCase {
} }
) )
let didMoveObjectExpectation = self.expectation( let didMoveObjectExpectation = self.expectation(
forNotification: "listMonitor:didMoveObject:fromIndexPath:toIndexPath:", forNotification: NSNotification.Name(rawValue: "listMonitor:didMoveObject:fromIndexPath:toIndexPath:"),
object: observer, object: observer,
handler: { (note) -> Bool in handler: { (note) -> Bool in
@@ -361,12 +358,12 @@ class ListObserverTests: BaseTestDataTestCase {
) )
let fromIndexPath = userInfo?["fromIndexPath"] as? NSIndexPath let fromIndexPath = userInfo?["fromIndexPath"] as? NSIndexPath
XCTAssertEqual(fromIndexPath?.section, 0) XCTAssertEqual(fromIndexPath?.index(atPosition: 0), 0)
XCTAssertEqual(fromIndexPath?.row, 0) XCTAssertEqual(fromIndexPath?.index(atPosition: 1), 0)
let toIndexPath = userInfo?["toIndexPath"] as? NSIndexPath let toIndexPath = userInfo?["toIndexPath"] as? NSIndexPath
XCTAssertEqual(toIndexPath?.section, 1) XCTAssertEqual(toIndexPath?.index(atPosition: 0), 1)
XCTAssertEqual(toIndexPath?.row, 1) XCTAssertEqual(toIndexPath?.index(atPosition: 1), 1)
let object = userInfo?["object"] as? TestEntity1 let object = userInfo?["object"] as? TestEntity1
XCTAssertEqual(object?.testEntityID, NSNumber(value: 102)) XCTAssertEqual(object?.testEntityID, NSNumber(value: 102))
@@ -380,7 +377,7 @@ class ListObserverTests: BaseTestDataTestCase {
} }
) )
let didChangeExpectation = self.expectation( let didChangeExpectation = self.expectation(
forNotification: "listMonitorDidChange:", forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"),
object: observer, object: observer,
handler: { (note) -> Bool in handler: { (note) -> Bool in
@@ -394,11 +391,12 @@ class ListObserverTests: BaseTestDataTestCase {
} }
) )
let saveExpectation = self.expectation(description: "save") let saveExpectation = self.expectation(description: "save")
stack.beginAsynchronous { (transaction) in stack.perform(
asynchronous: { (transaction) -> Bool in
if let object = transaction.fetchOne( if let object = transaction.fetchOne(
From<TestEntity1>(), From<TestEntity1>(),
Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) { Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 102)) {
object.testBoolean = NSNumber(value: true) object.testBoolean = NSNumber(value: true)
} }
@@ -406,19 +404,18 @@ class ListObserverTests: BaseTestDataTestCase {
XCTFail() XCTFail()
} }
transaction.commit { (result) in return transaction.hasChanges
},
success: { (hasChanges) in
switch result {
case .success(let hasChanges):
XCTAssertTrue(hasChanges) XCTAssertTrue(hasChanges)
saveExpectation.fulfill() saveExpectation.fulfill()
},
failure: { _ in
case .failure:
XCTFail() XCTFail()
} }
} )
}
self.waitAndCheckExpectations() self.waitAndCheckExpectations()
} }
} }
@@ -434,14 +431,14 @@ class ListObserverTests: BaseTestDataTestCase {
let monitor = stack.monitorSectionedList( let monitor = stack.monitorSectionedList(
From<TestEntity1>(), From<TestEntity1>(),
SectionBy(#keyPath(TestEntity1.testBoolean)), SectionBy(#keyPath(TestEntity1.testBoolean)),
OrderBy(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID))) OrderBy<TestEntity1>(.ascending(#keyPath(TestEntity1.testBoolean)), .ascending(#keyPath(TestEntity1.testEntityID)))
) )
monitor.addObserver(observer) monitor.addObserver(observer)
var events = 0 var events = 0
let willChangeExpectation = self.expectation( let willChangeExpectation = self.expectation(
forNotification: "listMonitorWillChange:", forNotification: NSNotification.Name(rawValue: "listMonitorWillChange:"),
object: observer, object: observer,
handler: { (note) -> Bool in handler: { (note) -> Bool in
@@ -454,10 +451,8 @@ class ListObserverTests: BaseTestDataTestCase {
return events == 0 return events == 0
} }
) )
for _ in 1 ... 2 {
let didUpdateObjectExpectation = self.expectation( let didUpdateObjectExpectation = self.expectation(
forNotification: "listMonitor:didDeleteObject:fromIndexPath:", forNotification: NSNotification.Name(rawValue: "listMonitor:didDeleteObject:fromIndexPath:"),
object: observer, object: observer,
handler: { (note) -> Bool in handler: { (note) -> Bool in
@@ -473,7 +468,7 @@ class ListObserverTests: BaseTestDataTestCase {
let indexPath = userInfo?["indexPath"] as? NSIndexPath let indexPath = userInfo?["indexPath"] as? NSIndexPath
XCTAssertEqual(indexPath?.section, 0) XCTAssertEqual(indexPath?.section, 0)
XCTAssert(indexPath?.row == 0 || indexPath?.row == 1) XCTAssert(indexPath?.index(atPosition: 1) == 0 || indexPath?.index(atPosition: 1) == 1)
let object = userInfo?["object"] as? TestEntity1 let object = userInfo?["object"] as? TestEntity1
XCTAssertEqual(object?.isDeleted, true) XCTAssertEqual(object?.isDeleted, true)
@@ -485,9 +480,8 @@ class ListObserverTests: BaseTestDataTestCase {
return events == 1 || events == 2 return events == 1 || events == 2
} }
) )
}
let didDeleteSectionExpectation = self.expectation( let didDeleteSectionExpectation = self.expectation(
forNotification: "listMonitor:didDeleteSection:fromSectionIndex:", forNotification: NSNotification.Name(rawValue: "listMonitor:didDeleteSection:fromSectionIndex:"),
object: observer, object: observer,
handler: { (note) -> Bool in handler: { (note) -> Bool in
@@ -515,7 +509,7 @@ class ListObserverTests: BaseTestDataTestCase {
} }
) )
let didChangeExpectation = self.expectation( let didChangeExpectation = self.expectation(
forNotification: "listMonitorDidChange:", forNotification: NSNotification.Name(rawValue: "listMonitorDidChange:"),
object: observer, object: observer,
handler: { (note) -> Bool in handler: { (note) -> Bool in
@@ -529,25 +523,26 @@ class ListObserverTests: BaseTestDataTestCase {
} }
) )
let saveExpectation = self.expectation(description: "save") let saveExpectation = self.expectation(description: "save")
stack.beginAsynchronous { (transaction) in stack.perform(
asynchronous: { (transaction) -> Bool in
transaction.deleteAll( let count = transaction.deleteAll(
From<TestEntity1>(), From<TestEntity1>(),
Where(#keyPath(TestEntity1.testBoolean), isEqualTo: false) Where<TestEntity1>(#keyPath(TestEntity1.testBoolean), isEqualTo: false)
) )
transaction.commit { (result) in XCTAssertEqual(count, 2)
return transaction.hasChanges
},
success: { (hasChanges) in
switch result {
case .success(let hasChanges):
XCTAssertTrue(hasChanges) XCTAssertTrue(hasChanges)
saveExpectation.fulfill() saveExpectation.fulfill()
},
failure: { _ in
case .failure:
XCTFail() XCTFail()
} }
} )
}
self.waitAndCheckExpectations() self.waitAndCheckExpectations()
} }
} }
@@ -556,6 +551,7 @@ class ListObserverTests: BaseTestDataTestCase {
// MARK: TestListObserver // MARK: TestListObserver
@available(OSX 10.12, *)
class TestListObserver: ListSectionObserver { class TestListObserver: ListSectionObserver {
// MARK: ListObserver // MARK: ListObserver
@@ -678,5 +674,3 @@ class TestListObserver: ListSectionObserver {
) )
} }
} }
#endif
+8 -8
View File
@@ -37,8 +37,8 @@ final class MigrationChainTests: XCTestCase {
dynamic func test_ThatNilMigrationChains_HaveNoVersions() { dynamic func test_ThatNilMigrationChains_HaveNoVersions() {
let chain: MigrationChain = nil let chain: MigrationChain = nil
XCTAssertTrue(chain.valid) XCTAssertTrue(chain.isValid)
XCTAssertTrue(chain.empty) XCTAssertTrue(chain.isEmpty)
XCTAssertFalse(chain.contains("version1")) XCTAssertFalse(chain.contains("version1"))
XCTAssertNil(chain.nextVersionFrom("version1")) XCTAssertNil(chain.nextVersionFrom("version1"))
@@ -48,8 +48,8 @@ final class MigrationChainTests: XCTestCase {
dynamic func test_ThatStringMigrationChains_HaveOneVersion() { dynamic func test_ThatStringMigrationChains_HaveOneVersion() {
let chain: MigrationChain = "version1" let chain: MigrationChain = "version1"
XCTAssertTrue(chain.valid) XCTAssertTrue(chain.isValid)
XCTAssertTrue(chain.empty) XCTAssertTrue(chain.isEmpty)
XCTAssertTrue(chain.contains("version1")) XCTAssertTrue(chain.contains("version1"))
XCTAssertFalse(chain.contains("version2")) XCTAssertFalse(chain.contains("version2"))
@@ -62,8 +62,8 @@ final class MigrationChainTests: XCTestCase {
dynamic func test_ThatArrayMigrationChains_HaveLinearVersions() { dynamic func test_ThatArrayMigrationChains_HaveLinearVersions() {
let chain: MigrationChain = ["version1", "version2", "version3", "version4"] let chain: MigrationChain = ["version1", "version2", "version3", "version4"]
XCTAssertTrue(chain.valid) XCTAssertTrue(chain.isValid)
XCTAssertFalse(chain.empty) XCTAssertFalse(chain.isEmpty)
XCTAssertTrue(chain.contains("version1")) XCTAssertTrue(chain.contains("version1"))
XCTAssertTrue(chain.contains("version2")) XCTAssertTrue(chain.contains("version2"))
@@ -86,8 +86,8 @@ final class MigrationChainTests: XCTestCase {
"version2": "version3", "version2": "version3",
"version3": "version4" "version3": "version4"
] ]
XCTAssertTrue(chain.valid) XCTAssertTrue(chain.isValid)
XCTAssertFalse(chain.empty) XCTAssertFalse(chain.isEmpty)
XCTAssertTrue(chain.contains("version1")) XCTAssertTrue(chain.contains("version1"))
XCTAssertTrue(chain.contains("version2")) XCTAssertTrue(chain.contains("version2"))
@@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="10174" systemVersion="15F34" minimumToolsVersion="Xcode 4.3"> <model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="11759" systemVersion="16C67" minimumToolsVersion="Xcode 4.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
<entity name="TestEntity1AAA" representedClassName="CoreStoreTests.TestEntity1" syncable="YES"> <entity name="TestEntity1AAA" representedClassName="CoreStoreTests.TestEntity1" syncable="YES">
<attribute name="testBoolean" optional="YES" attributeType="Boolean" syncable="YES"/> <attribute name="testBoolean" optional="YES" attributeType="Boolean" usesScalarValueType="NO" syncable="YES"/>
<attribute name="testData" optional="YES" attributeType="Binary" syncable="YES"/> <attribute name="testData" optional="YES" attributeType="Binary" syncable="YES"/>
<attribute name="testDate" optional="YES" attributeType="Date" syncable="YES"/> <attribute name="testDate" optional="YES" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
<attribute name="testDecimal" optional="YES" attributeType="Decimal" syncable="YES"/> <attribute name="testDecimal" optional="YES" attributeType="Decimal" syncable="YES"/>
<attribute name="testEntityID" optional="YES" attributeType="Integer 64" syncable="YES"/> <attribute name="testEntityID" optional="YES" attributeType="Integer 64" usesScalarValueType="NO" syncable="YES"/>
<attribute name="testNil" optional="YES" attributeType="String" syncable="YES"/> <attribute name="testNil" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="testNumber" optional="YES" attributeType="Integer 32" syncable="YES"/> <attribute name="testNumber" optional="YES" attributeType="Integer 32" usesScalarValueType="NO" syncable="YES"/>
<attribute name="testString" optional="YES" attributeType="String" syncable="YES"/> <attribute name="testString" optional="YES" attributeType="String" syncable="YES"/>
</entity> </entity>
<entity name="TestEntity2" representedClassName="CoreStoreTests.TestEntity2" syncable="YES"> <entity name="TestEntity2" representedClassName="CoreStoreTests.TestEntity2" syncable="YES">
<attribute name="testBoolean" optional="YES" attributeType="Boolean" syncable="YES"/> <attribute name="testBoolean" optional="YES" attributeType="Boolean" usesScalarValueType="NO" syncable="YES"/>
<attribute name="testData" optional="YES" attributeType="Binary" syncable="YES"/> <attribute name="testData" optional="YES" attributeType="Binary" syncable="YES"/>
<attribute name="testDate" optional="YES" attributeType="Date" syncable="YES"/> <attribute name="testDate" optional="YES" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
<attribute name="testDecimal" optional="YES" attributeType="Decimal" syncable="YES"/> <attribute name="testDecimal" optional="YES" attributeType="Decimal" syncable="YES"/>
<attribute name="testEntityID" optional="YES" attributeType="Integer 64" syncable="YES"/> <attribute name="testEntityID" optional="YES" attributeType="Integer 64" usesScalarValueType="NO" syncable="YES"/>
<attribute name="testNil" optional="YES" attributeType="String" syncable="YES"/> <attribute name="testNil" optional="YES" attributeType="String" syncable="YES"/>
<attribute name="testNumber" optional="YES" attributeType="Integer 32" syncable="YES"/> <attribute name="testNumber" optional="YES" attributeType="Integer 32" usesScalarValueType="NO" syncable="YES"/>
<attribute name="testString" optional="YES" attributeType="String" syncable="YES"/> <attribute name="testString" optional="YES" attributeType="String" syncable="YES"/>
</entity> </entity>
<configuration name="Config1"> <configuration name="Config1">
+26 -28
View File
@@ -29,10 +29,9 @@ import XCTest
import CoreStore import CoreStore
#if os(iOS) || os(watchOS) || os(tvOS)
// MARK: - ObjectObserverTests // MARK: - ObjectObserverTests
@available(OSX 10.12, *)
class ObjectObserverTests: BaseTestDataTestCase { class ObjectObserverTests: BaseTestDataTestCase {
@objc @objc
@@ -44,7 +43,7 @@ class ObjectObserverTests: BaseTestDataTestCase {
guard let object = stack.fetchOne( guard let object = stack.fetchOne(
From<TestEntity1>(), From<TestEntity1>(),
Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) else { Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) else {
XCTFail() XCTFail()
return return
@@ -59,7 +58,7 @@ class ObjectObserverTests: BaseTestDataTestCase {
var events = 0 var events = 0
let willUpdateExpectation = self.expectation( let willUpdateExpectation = self.expectation(
forNotification: "objectMonitor:willUpdateObject:", forNotification: NSNotification.Name(rawValue: "objectMonitor:willUpdateObject:"),
object: observer, object: observer,
handler: { (note) -> Bool in handler: { (note) -> Bool in
@@ -76,7 +75,7 @@ class ObjectObserverTests: BaseTestDataTestCase {
} }
) )
let didUpdateExpectation = self.expectation( let didUpdateExpectation = self.expectation(
forNotification: "objectMonitor:didUpdateObject:changedPersistentKeys:", forNotification: NSNotification.Name(rawValue: "objectMonitor:didUpdateObject:changedPersistentKeys:"),
object: observer, object: observer,
handler: { (note) -> Bool in handler: { (note) -> Bool in
@@ -105,29 +104,29 @@ class ObjectObserverTests: BaseTestDataTestCase {
} }
) )
let saveExpectation = self.expectation(description: "save") let saveExpectation = self.expectation(description: "save")
stack.beginAsynchronous { (transaction) in stack.perform(
asynchronous: { (transaction) -> Bool in
guard let object = transaction.edit(object) else { guard let object = transaction.edit(object) else {
XCTFail() XCTFail()
return try transaction.cancel()
} }
object.testNumber = NSNumber(value: 10) object.testNumber = NSNumber(value: 10)
object.testString = "nil:TestEntity1:10" object.testString = "nil:TestEntity1:10"
transaction.commit { (result) in return transaction.hasChanges
},
success: { (hasChanges) in
switch result {
case .success(let hasChanges):
XCTAssertTrue(hasChanges) XCTAssertTrue(hasChanges)
saveExpectation.fulfill() saveExpectation.fulfill()
},
failure: { _ in
case .failure:
XCTFail() XCTFail()
} }
} )
}
self.waitAndCheckExpectations() self.waitAndCheckExpectations()
} }
} }
@@ -141,7 +140,7 @@ class ObjectObserverTests: BaseTestDataTestCase {
guard let object = stack.fetchOne( guard let object = stack.fetchOne(
From<TestEntity1>(), From<TestEntity1>(),
Where(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) else { Where<TestEntity1>(#keyPath(TestEntity1.testEntityID), isEqualTo: 101)) else {
XCTFail() XCTFail()
return return
@@ -156,7 +155,7 @@ class ObjectObserverTests: BaseTestDataTestCase {
var events = 0 var events = 0
let didDeleteExpectation = self.expectation( let didDeleteExpectation = self.expectation(
forNotification: "objectMonitor:didDeleteObject:", forNotification: NSNotification.Name(rawValue: "objectMonitor:didDeleteObject:"),
object: observer, object: observer,
handler: { (note) -> Bool in handler: { (note) -> Bool in
@@ -173,29 +172,29 @@ class ObjectObserverTests: BaseTestDataTestCase {
} }
) )
let saveExpectation = self.expectation(description: "save") let saveExpectation = self.expectation(description: "save")
stack.beginAsynchronous { (transaction) in stack.perform(
asynchronous: { (transaction) -> Bool in
guard let object = transaction.edit(object) else { guard let object = transaction.edit(object) else {
XCTFail() XCTFail()
return try transaction.cancel()
} }
transaction.delete(object) transaction.delete(object)
transaction.commit { (result) in return transaction.hasChanges
},
success: { (hasChanges) in
switch result {
case .success(let hasChanges):
XCTAssertTrue(hasChanges) XCTAssertTrue(hasChanges)
XCTAssertTrue(monitor.isObjectDeleted) XCTAssertTrue(monitor.isObjectDeleted)
saveExpectation.fulfill() saveExpectation.fulfill()
},
failure: { _ in
case .failure:
XCTFail() XCTFail()
} }
} )
}
self.waitAndCheckExpectations() self.waitAndCheckExpectations()
} }
} }
@@ -204,6 +203,7 @@ class ObjectObserverTests: BaseTestDataTestCase {
// MARK: TestObjectObserver // MARK: TestObjectObserver
@available(OSX 10.12, *)
class TestObjectObserver: ObjectObserver { class TestObjectObserver: ObjectObserver {
typealias ObjectEntityType = TestEntity1 typealias ObjectEntityType = TestEntity1
@@ -219,7 +219,7 @@ class TestObjectObserver: ObjectObserver {
) )
} }
func objectMonitor(_ monitor: ObjectMonitor<TestEntity1>, didUpdateObject object: TestEntity1, changedPersistentKeys: Set<KeyPath>) { func objectMonitor(_ monitor: ObjectMonitor<TestEntity1>, didUpdateObject object: TestEntity1, changedPersistentKeys: Set<String>) {
NotificationCenter.default.post( NotificationCenter.default.post(
name: NSNotification.Name(rawValue: "objectMonitor:didUpdateObject:changedPersistentKeys:"), name: NSNotification.Name(rawValue: "objectMonitor:didUpdateObject:changedPersistentKeys:"),
@@ -242,5 +242,3 @@ class TestObjectObserver: ObjectObserver {
) )
} }
} }
#endif
+44 -44
View File
@@ -38,21 +38,21 @@ final class OrderByTests: XCTestCase {
do { do {
let orderBy = OrderBy() let orderBy = OrderBy<NSManagedObject>()
XCTAssertEqual(orderBy, OrderBy([NSSortDescriptor]())) XCTAssertEqual(orderBy, OrderBy<NSManagedObject>([NSSortDescriptor]()))
XCTAssertNotEqual(orderBy, OrderBy(NSSortDescriptor(key: "key", ascending: false))) XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(NSSortDescriptor(key: "key", ascending: false)))
XCTAssertTrue(orderBy.sortDescriptors.isEmpty) XCTAssertTrue(orderBy.sortDescriptors.isEmpty)
} }
do { do {
let sortDescriptor = NSSortDescriptor(key: "key1", ascending: true) let sortDescriptor = NSSortDescriptor(key: "key1", ascending: true)
let orderBy = OrderBy(sortDescriptor) let orderBy = OrderBy<NSManagedObject>(sortDescriptor)
XCTAssertEqual(orderBy, OrderBy(sortDescriptor)) XCTAssertEqual(orderBy, OrderBy(sortDescriptor))
XCTAssertEqual(orderBy, OrderBy(.ascending("key1"))) XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1")))
XCTAssertNotEqual(orderBy, OrderBy(.ascending("key2"))) XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key2")))
XCTAssertNotEqual(orderBy, OrderBy(.descending("key1"))) XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.descending("key1")))
XCTAssertNotEqual(orderBy, OrderBy(NSSortDescriptor(key: "key1", ascending: false))) XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(NSSortDescriptor(key: "key1", ascending: false)))
XCTAssertEqual(orderBy, OrderBy([sortDescriptor])) XCTAssertEqual(orderBy, OrderBy<NSManagedObject>([sortDescriptor]))
XCTAssertEqual(orderBy.sortDescriptors, [sortDescriptor]) XCTAssertEqual(orderBy.sortDescriptors, [sortDescriptor])
} }
do { do {
@@ -61,76 +61,76 @@ final class OrderByTests: XCTestCase {
NSSortDescriptor(key: "key1", ascending: true), NSSortDescriptor(key: "key1", ascending: true),
NSSortDescriptor(key: "key2", ascending: false) NSSortDescriptor(key: "key2", ascending: false)
] ]
let orderBy = OrderBy(sortDescriptors) let orderBy = OrderBy<NSManagedObject>(sortDescriptors)
XCTAssertEqual(orderBy, OrderBy(sortDescriptors)) XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(sortDescriptors))
XCTAssertEqual(orderBy, OrderBy(.ascending("key1"), .descending("key2"))) XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2")))
XCTAssertNotEqual( XCTAssertNotEqual(
orderBy, orderBy,
OrderBy( OrderBy<NSManagedObject>(
[ [
NSSortDescriptor(key: "key1", ascending: false), NSSortDescriptor(key: "key1", ascending: false),
NSSortDescriptor(key: "key2", ascending: false) NSSortDescriptor(key: "key2", ascending: false)
] ]
) )
) )
XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .ascending("key2"))) XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .ascending("key2")))
XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .descending("key3"))) XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key3")))
XCTAssertEqual(orderBy.sortDescriptors, sortDescriptors) XCTAssertEqual(orderBy.sortDescriptors, sortDescriptors)
} }
do { do {
let orderBy = OrderBy(.ascending("key1")) let orderBy = OrderBy<NSManagedObject>(.ascending("key1"))
let sortDescriptor = NSSortDescriptor(key: "key1", ascending: true) let sortDescriptor = NSSortDescriptor(key: "key1", ascending: true)
XCTAssertEqual(orderBy, OrderBy(sortDescriptor)) XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(sortDescriptor))
XCTAssertEqual(orderBy, OrderBy(.ascending("key1"))) XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1")))
XCTAssertNotEqual(orderBy, OrderBy(.descending("key1"))) XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.descending("key1")))
XCTAssertNotEqual(orderBy, OrderBy(.ascending("key2"))) XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key2")))
XCTAssertEqual(orderBy, OrderBy([sortDescriptor])) XCTAssertEqual(orderBy, OrderBy<NSManagedObject>([sortDescriptor]))
XCTAssertEqual(orderBy.sortDescriptors, [sortDescriptor]) XCTAssertEqual(orderBy.sortDescriptors, [sortDescriptor])
} }
do { do {
let orderBy = OrderBy(.ascending("key1"), .descending("key2")) let orderBy = OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2"))
let sortDescriptors = [ let sortDescriptors = [
NSSortDescriptor(key: "key1", ascending: true), NSSortDescriptor(key: "key1", ascending: true),
NSSortDescriptor(key: "key2", ascending: false) NSSortDescriptor(key: "key2", ascending: false)
] ]
XCTAssertEqual(orderBy, OrderBy(sortDescriptors)) XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(sortDescriptors))
XCTAssertEqual(orderBy, OrderBy(.ascending("key1"), .descending("key2"))) XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2")))
XCTAssertNotEqual( XCTAssertNotEqual(
orderBy, orderBy,
OrderBy( OrderBy<NSManagedObject>(
[ [
NSSortDescriptor(key: "key1", ascending: false), NSSortDescriptor(key: "key1", ascending: false),
NSSortDescriptor(key: "key2", ascending: false) NSSortDescriptor(key: "key2", ascending: false)
] ]
) )
) )
XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .ascending("key2"))) XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .ascending("key2")))
XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .descending("key3"))) XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key3")))
XCTAssertEqual(orderBy.sortDescriptors, sortDescriptors) XCTAssertEqual(orderBy.sortDescriptors, sortDescriptors)
} }
do { do {
let sortKeys: [SortKey] = [.ascending("key1"), .descending("key2")] let sortKeys: [OrderBy<NSManagedObject>.SortKey] = [.ascending("key1"), .descending("key2")]
let orderBy = OrderBy(sortKeys) let orderBy = OrderBy<NSManagedObject>(sortKeys)
let sortDescriptors = [ let sortDescriptors = [
NSSortDescriptor(key: "key1", ascending: true), NSSortDescriptor(key: "key1", ascending: true),
NSSortDescriptor(key: "key2", ascending: false) NSSortDescriptor(key: "key2", ascending: false)
] ]
XCTAssertEqual(orderBy, OrderBy(sortDescriptors)) XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(sortDescriptors))
XCTAssertEqual(orderBy, OrderBy(.ascending("key1"), .descending("key2"))) XCTAssertEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2")))
XCTAssertNotEqual( XCTAssertNotEqual(
orderBy, orderBy,
OrderBy( OrderBy<NSManagedObject>(
[ [
NSSortDescriptor(key: "key1", ascending: false), NSSortDescriptor(key: "key1", ascending: false),
NSSortDescriptor(key: "key2", ascending: false) NSSortDescriptor(key: "key2", ascending: false)
] ]
) )
) )
XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .ascending("key2"))) XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .ascending("key2")))
XCTAssertNotEqual(orderBy, OrderBy(.ascending("key1"), .descending("key3"))) XCTAssertNotEqual(orderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key3")))
XCTAssertEqual(orderBy.sortDescriptors, sortDescriptors) XCTAssertEqual(orderBy.sortDescriptors, sortDescriptors)
} }
} }
@@ -138,15 +138,15 @@ final class OrderByTests: XCTestCase {
@objc @objc
dynamic func test_ThatOrderByClauseOperations_ComputeCorrectly() { dynamic func test_ThatOrderByClauseOperations_ComputeCorrectly() {
let orderBy1 = OrderBy(.ascending("key1")) let orderBy1 = OrderBy<NSManagedObject>(.ascending("key1"))
let orderBy2 = OrderBy(.descending("key2")) let orderBy2 = OrderBy<NSManagedObject>(.descending("key2"))
let orderBy3 = OrderBy(.ascending("key3")) let orderBy3 = OrderBy<NSManagedObject>(.ascending("key3"))
do { do {
let plusOrderBy = orderBy1 + orderBy2 + orderBy3 let plusOrderBy = orderBy1 + orderBy2 + orderBy3
XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1"), .descending("key2"), .ascending("key3"))) XCTAssertEqual(plusOrderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2"), .ascending("key3")))
XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1")) + OrderBy(.descending("key2"), .ascending("key3"))) XCTAssertEqual(plusOrderBy, OrderBy<NSManagedObject>(.ascending("key1")) + OrderBy<NSManagedObject>(.descending("key2"), .ascending("key3")))
XCTAssertNotEqual(plusOrderBy, orderBy1 + orderBy3 + orderBy2) XCTAssertNotEqual(plusOrderBy, orderBy1 + orderBy3 + orderBy2)
XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy1 + orderBy3) XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy1 + orderBy3)
XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy3 + orderBy1) XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy3 + orderBy1)
@@ -158,14 +158,14 @@ final class OrderByTests: XCTestCase {
var plusOrderBy = orderBy1 var plusOrderBy = orderBy1
plusOrderBy += orderBy2 plusOrderBy += orderBy2
XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1"), .descending("key2"))) XCTAssertEqual(plusOrderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2")))
XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1")) + OrderBy(.descending("key2"))) XCTAssertEqual(plusOrderBy, OrderBy<NSManagedObject>(.ascending("key1")) + OrderBy<NSManagedObject>(.descending("key2")))
XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy1) XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy1)
XCTAssertEqual(plusOrderBy.sortDescriptors, orderBy1.sortDescriptors + orderBy2.sortDescriptors) XCTAssertEqual(plusOrderBy.sortDescriptors, orderBy1.sortDescriptors + orderBy2.sortDescriptors)
plusOrderBy += orderBy3 plusOrderBy += orderBy3
XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1"), .descending("key2"), .ascending("key3"))) XCTAssertEqual(plusOrderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2"), .ascending("key3")))
XCTAssertEqual(plusOrderBy, OrderBy(.ascending("key1"), .descending("key2")) + OrderBy(.ascending("key3"))) XCTAssertEqual(plusOrderBy, OrderBy<NSManagedObject>(.ascending("key1"), .descending("key2")) + OrderBy<NSManagedObject>(.ascending("key3")))
XCTAssertNotEqual(plusOrderBy, orderBy1 + orderBy3 + orderBy2) XCTAssertNotEqual(plusOrderBy, orderBy1 + orderBy3 + orderBy2)
XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy1 + orderBy3) XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy1 + orderBy3)
XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy3 + orderBy1) XCTAssertNotEqual(plusOrderBy, orderBy2 + orderBy3 + orderBy1)
@@ -178,7 +178,7 @@ final class OrderByTests: XCTestCase {
@objc @objc
dynamic func test_ThatOrderByClauses_ApplyToFetchRequestsCorrectly() { dynamic func test_ThatOrderByClauses_ApplyToFetchRequestsCorrectly() {
let orderBy = OrderBy(.ascending("key")) let orderBy = OrderBy<NSManagedObject>(.ascending("key"))
let request = CoreStoreFetchRequest() let request = CoreStoreFetchRequest()
orderBy.applyToFetchRequest(request) orderBy.applyToFetchRequest(request)
XCTAssertNotNil(request.sortDescriptors) XCTAssertNotNil(request.sortDescriptors)
File diff suppressed because it is too large Load Diff
+3 -6
View File
@@ -29,10 +29,9 @@ import XCTest
import CoreStore import CoreStore
#if os(iOS) || os(watchOS) || os(tvOS)
//MARK: - SectionByTests //MARK: - SectionByTests
@available(OSX 10.12, *)
final class SectionByTests: XCTestCase { final class SectionByTests: XCTestCase {
@objc @objc
@@ -40,18 +39,16 @@ final class SectionByTests: XCTestCase {
do { do {
let sectionBy = SectionBy("key") let sectionBy = SectionBy<NSManagedObject>("key")
XCTAssertEqual(sectionBy.sectionKeyPath, "key") XCTAssertEqual(sectionBy.sectionKeyPath, "key")
XCTAssertEqual(sectionBy.sectionIndexTransformer("key"), "key") XCTAssertEqual(sectionBy.sectionIndexTransformer("key"), "key")
} }
do { do {
let sectionBy = SectionBy("key") { $0.flatMap { "\($0):suffix" } } let sectionBy = SectionBy<NSManagedObject>("key") { $0.flatMap { "\($0):suffix" } }
XCTAssertEqual(sectionBy.sectionKeyPath, "key") XCTAssertEqual(sectionBy.sectionKeyPath, "key")
XCTAssertEqual(sectionBy.sectionIndexTransformer("key"), "key:suffix") XCTAssertEqual(sectionBy.sectionIndexTransformer("key"), "key:suffix")
XCTAssertNil(sectionBy.sectionIndexTransformer(nil)) XCTAssertNil(sectionBy.sectionIndexTransformer(nil))
} }
} }
} }
#endif
+19 -19
View File
@@ -38,7 +38,7 @@ final class SelectTests: XCTestCase {
do { do {
let term: SelectTerm = "attribute" let term: SelectTerm<NSManagedObject> = "attribute"
XCTAssertEqual(term, SelectTerm.attribute("attribute")) XCTAssertEqual(term, SelectTerm.attribute("attribute"))
XCTAssertNotEqual(term, SelectTerm.attribute("attribute2")) XCTAssertNotEqual(term, SelectTerm.attribute("attribute2"))
XCTAssertNotEqual(term, SelectTerm.average("attribute")) XCTAssertNotEqual(term, SelectTerm.average("attribute"))
@@ -58,7 +58,7 @@ final class SelectTests: XCTestCase {
} }
do { do {
let term = SelectTerm.attribute("attribute") let term = SelectTerm<NSManagedObject>.attribute("attribute")
XCTAssertNotEqual(term, SelectTerm.attribute("attribute2")) XCTAssertNotEqual(term, SelectTerm.attribute("attribute2"))
XCTAssertNotEqual(term, SelectTerm.average("attribute")) XCTAssertNotEqual(term, SelectTerm.average("attribute"))
XCTAssertNotEqual(term, SelectTerm.count("attribute")) XCTAssertNotEqual(term, SelectTerm.count("attribute"))
@@ -82,7 +82,7 @@ final class SelectTests: XCTestCase {
do { do {
let term = SelectTerm.average("attribute") let term = SelectTerm<NSManagedObject>.average("attribute")
XCTAssertEqual(term, SelectTerm.average("attribute")) XCTAssertEqual(term, SelectTerm.average("attribute"))
XCTAssertNotEqual(term, SelectTerm.average("attribute", as: "alias")) XCTAssertNotEqual(term, SelectTerm.average("attribute", as: "alias"))
XCTAssertNotEqual(term, SelectTerm.average("attribute2")) XCTAssertNotEqual(term, SelectTerm.average("attribute2"))
@@ -106,7 +106,7 @@ final class SelectTests: XCTestCase {
} }
do { do {
let term = SelectTerm.average("attribute", as: "alias") let term = SelectTerm<NSManagedObject>.average("attribute", as: "alias")
XCTAssertEqual(term, SelectTerm.average("attribute", as: "alias")) XCTAssertEqual(term, SelectTerm.average("attribute", as: "alias"))
XCTAssertNotEqual(term, SelectTerm.average("attribute", as: "alias2")) XCTAssertNotEqual(term, SelectTerm.average("attribute", as: "alias2"))
XCTAssertNotEqual(term, SelectTerm.average("attribute2")) XCTAssertNotEqual(term, SelectTerm.average("attribute2"))
@@ -135,7 +135,7 @@ final class SelectTests: XCTestCase {
do { do {
let term = SelectTerm.count("attribute") let term = SelectTerm<NSManagedObject>.count("attribute")
XCTAssertEqual(term, SelectTerm.count("attribute")) XCTAssertEqual(term, SelectTerm.count("attribute"))
XCTAssertNotEqual(term, SelectTerm.count("attribute", as: "alias")) XCTAssertNotEqual(term, SelectTerm.count("attribute", as: "alias"))
XCTAssertNotEqual(term, SelectTerm.count("attribute2")) XCTAssertNotEqual(term, SelectTerm.count("attribute2"))
@@ -159,7 +159,7 @@ final class SelectTests: XCTestCase {
} }
do { do {
let term = SelectTerm.count("attribute", as: "alias") let term = SelectTerm<NSManagedObject>.count("attribute", as: "alias")
XCTAssertEqual(term, SelectTerm.count("attribute", as: "alias")) XCTAssertEqual(term, SelectTerm.count("attribute", as: "alias"))
XCTAssertNotEqual(term, SelectTerm.count("attribute", as: "alias2")) XCTAssertNotEqual(term, SelectTerm.count("attribute", as: "alias2"))
XCTAssertNotEqual(term, SelectTerm.count("attribute2")) XCTAssertNotEqual(term, SelectTerm.count("attribute2"))
@@ -188,7 +188,7 @@ final class SelectTests: XCTestCase {
do { do {
let term = SelectTerm.maximum("attribute") let term = SelectTerm<NSManagedObject>.maximum("attribute")
XCTAssertEqual(term, SelectTerm.maximum("attribute")) XCTAssertEqual(term, SelectTerm.maximum("attribute"))
XCTAssertNotEqual(term, SelectTerm.maximum("attribute", as: "alias")) XCTAssertNotEqual(term, SelectTerm.maximum("attribute", as: "alias"))
XCTAssertNotEqual(term, SelectTerm.maximum("attribute2")) XCTAssertNotEqual(term, SelectTerm.maximum("attribute2"))
@@ -212,7 +212,7 @@ final class SelectTests: XCTestCase {
} }
do { do {
let term = SelectTerm.maximum("attribute", as: "alias") let term = SelectTerm<NSManagedObject>.maximum("attribute", as: "alias")
XCTAssertEqual(term, SelectTerm.maximum("attribute", as: "alias")) XCTAssertEqual(term, SelectTerm.maximum("attribute", as: "alias"))
XCTAssertNotEqual(term, SelectTerm.maximum("attribute", as: "alias2")) XCTAssertNotEqual(term, SelectTerm.maximum("attribute", as: "alias2"))
XCTAssertNotEqual(term, SelectTerm.maximum("attribute2")) XCTAssertNotEqual(term, SelectTerm.maximum("attribute2"))
@@ -241,7 +241,7 @@ final class SelectTests: XCTestCase {
do { do {
let term = SelectTerm.minimum("attribute") let term = SelectTerm<NSManagedObject>.minimum("attribute")
XCTAssertEqual(term, SelectTerm.minimum("attribute")) XCTAssertEqual(term, SelectTerm.minimum("attribute"))
XCTAssertNotEqual(term, SelectTerm.minimum("attribute", as: "alias")) XCTAssertNotEqual(term, SelectTerm.minimum("attribute", as: "alias"))
XCTAssertNotEqual(term, SelectTerm.minimum("attribute2")) XCTAssertNotEqual(term, SelectTerm.minimum("attribute2"))
@@ -265,7 +265,7 @@ final class SelectTests: XCTestCase {
} }
do { do {
let term = SelectTerm.minimum("attribute", as: "alias") let term = SelectTerm<NSManagedObject>.minimum("attribute", as: "alias")
XCTAssertEqual(term, SelectTerm.minimum("attribute", as: "alias")) XCTAssertEqual(term, SelectTerm.minimum("attribute", as: "alias"))
XCTAssertNotEqual(term, SelectTerm.minimum("attribute", as: "alias2")) XCTAssertNotEqual(term, SelectTerm.minimum("attribute", as: "alias2"))
XCTAssertNotEqual(term, SelectTerm.minimum("attribute2")) XCTAssertNotEqual(term, SelectTerm.minimum("attribute2"))
@@ -294,7 +294,7 @@ final class SelectTests: XCTestCase {
do { do {
let term = SelectTerm.sum("attribute") let term = SelectTerm<NSManagedObject>.sum("attribute")
XCTAssertEqual(term, SelectTerm.sum("attribute")) XCTAssertEqual(term, SelectTerm.sum("attribute"))
XCTAssertNotEqual(term, SelectTerm.sum("attribute", as: "alias")) XCTAssertNotEqual(term, SelectTerm.sum("attribute", as: "alias"))
XCTAssertNotEqual(term, SelectTerm.sum("attribute2")) XCTAssertNotEqual(term, SelectTerm.sum("attribute2"))
@@ -318,7 +318,7 @@ final class SelectTests: XCTestCase {
} }
do { do {
let term = SelectTerm.sum("attribute", as: "alias") let term = SelectTerm<NSManagedObject>.sum("attribute", as: "alias")
XCTAssertEqual(term, SelectTerm.sum("attribute", as: "alias")) XCTAssertEqual(term, SelectTerm.sum("attribute", as: "alias"))
XCTAssertNotEqual(term, SelectTerm.sum("attribute", as: "alias2")) XCTAssertNotEqual(term, SelectTerm.sum("attribute", as: "alias2"))
XCTAssertNotEqual(term, SelectTerm.sum("attribute2")) XCTAssertNotEqual(term, SelectTerm.sum("attribute2"))
@@ -347,7 +347,7 @@ final class SelectTests: XCTestCase {
do { do {
let term = SelectTerm.objectID() let term = SelectTerm<NSManagedObject>.objectID()
XCTAssertEqual(term, SelectTerm.objectID()) XCTAssertEqual(term, SelectTerm.objectID())
XCTAssertNotEqual(term, SelectTerm.objectID(as: "alias")) XCTAssertNotEqual(term, SelectTerm.objectID(as: "alias"))
XCTAssertNotEqual(term, SelectTerm.attribute("attribute")) XCTAssertNotEqual(term, SelectTerm.attribute("attribute"))
@@ -368,7 +368,7 @@ final class SelectTests: XCTestCase {
} }
do { do {
let term = SelectTerm.objectID(as: "alias") let term = SelectTerm<NSManagedObject>.objectID(as: "alias")
XCTAssertEqual(term, SelectTerm.objectID(as: "alias")) XCTAssertEqual(term, SelectTerm.objectID(as: "alias"))
XCTAssertNotEqual(term, SelectTerm.objectID(as: "alias2")) XCTAssertNotEqual(term, SelectTerm.objectID(as: "alias2"))
XCTAssertNotEqual(term, SelectTerm.objectID()) XCTAssertNotEqual(term, SelectTerm.objectID())
@@ -393,12 +393,12 @@ final class SelectTests: XCTestCase {
@objc @objc
dynamic func test_ThatSelectClauses_ConfigureCorrectly() { dynamic func test_ThatSelectClauses_ConfigureCorrectly() {
let term1 = SelectTerm.attribute("attribute1") let term1 = SelectTerm<NSManagedObject>.attribute("attribute1")
let term2 = SelectTerm.attribute("attribute2") let term2 = SelectTerm<NSManagedObject>.attribute("attribute2")
let term3 = SelectTerm.attribute("attribute3") let term3 = SelectTerm<NSManagedObject>.attribute("attribute3")
do { do {
let select = Select<Int>(term1, term2, term3) let select = Select<NSManagedObject, Int>(term1, term2, term3)
XCTAssertEqual(select.selectTerms, [term1, term2, term3]) XCTAssertEqual(select.selectTerms, [term1, term2, term3])
XCTAssertNotEqual(select.selectTerms, [term1, term3, term2]) XCTAssertNotEqual(select.selectTerms, [term1, term3, term2])
XCTAssertNotEqual(select.selectTerms, [term2, term1, term3]) XCTAssertNotEqual(select.selectTerms, [term2, term1, term3])
@@ -408,7 +408,7 @@ final class SelectTests: XCTestCase {
} }
do { do {
let select = Select<Int>([term1, term2, term3]) let select = Select<NSManagedObject, Int>([term1, term2, term3])
XCTAssertEqual(select.selectTerms, [term1, term2, term3]) XCTAssertEqual(select.selectTerms, [term1, term2, term3])
XCTAssertNotEqual(select.selectTerms, [term1, term3, term2]) XCTAssertNotEqual(select.selectTerms, [term1, term3, term2])
XCTAssertNotEqual(select.selectTerms, [term2, term1, term3]) XCTAssertNotEqual(select.selectTerms, [term2, term1, term3])
+37 -29
View File
@@ -36,10 +36,14 @@ class SetupTests: BaseTestDataTestCase {
do { do {
let model = NSManagedObjectModel.mergedModel(from: [Bundle(for: type(of: self))])! let schemaHistory = SchemaHistory(
XcodeDataModelSchema.from(
let stack = DataStack(model: model, migrationChain: nil) modelName: "Model",
XCTAssertEqual(stack.coordinator.managedObjectModel, model) bundle: Bundle(for: type(of: self))
)
)
let stack = DataStack(schemaHistory: schemaHistory)
XCTAssertEqual(stack.coordinator.managedObjectModel, schemaHistory.rawModel)
XCTAssertEqual(stack.rootSavingContext.persistentStoreCoordinator, stack.coordinator) XCTAssertEqual(stack.rootSavingContext.persistentStoreCoordinator, stack.coordinator)
XCTAssertNil(stack.rootSavingContext.parent) XCTAssertNil(stack.rootSavingContext.parent)
XCTAssertFalse(stack.rootSavingContext.isDataStackContext) XCTAssertFalse(stack.rootSavingContext.isDataStackContext)
@@ -47,29 +51,29 @@ class SetupTests: BaseTestDataTestCase {
XCTAssertEqual(stack.mainContext.parent, stack.rootSavingContext) XCTAssertEqual(stack.mainContext.parent, stack.rootSavingContext)
XCTAssertTrue(stack.mainContext.isDataStackContext) XCTAssertTrue(stack.mainContext.isDataStackContext)
XCTAssertFalse(stack.mainContext.isTransactionContext) XCTAssertFalse(stack.mainContext.isTransactionContext)
XCTAssertEqual(stack.model, model) XCTAssertEqual(stack.schemaHistory.rawModel, schemaHistory.rawModel)
XCTAssertTrue(stack.migrationChain.valid) XCTAssertTrue(stack.schemaHistory.migrationChain.isValid)
XCTAssertTrue(stack.migrationChain.empty) XCTAssertTrue(stack.schemaHistory.migrationChain.isEmpty)
XCTAssertTrue(stack.migrationChain.rootVersions.isEmpty) XCTAssertTrue(stack.schemaHistory.migrationChain.rootVersions.isEmpty)
XCTAssertTrue(stack.migrationChain.leafVersions.isEmpty) XCTAssertTrue(stack.schemaHistory.migrationChain.leafVersions.isEmpty)
CoreStore.defaultStack = stack CoreStore.defaultStack = stack
XCTAssertEqual(CoreStore.defaultStack, stack) XCTAssertEqual(CoreStore.defaultStack, stack)
} }
do { do {
let migrationChain: MigrationChain = ["version1", "version2", "version3"] let migrationChain: MigrationChain = ["version1", "version2", "version3", "Model"]
let stack = self.expectLogger([.logWarning]) { let stack = self.expectLogger([.logWarning]) {
DataStack( DataStack(
modelName: "Model", xcodeModelName: "Model",
bundle: Bundle(for: type(of: self)), bundle: Bundle(for: type(of: self)),
migrationChain: migrationChain migrationChain: migrationChain
) )
} }
XCTAssertEqual(stack.modelVersion, "Model") XCTAssertEqual(stack.modelVersion, "Model")
XCTAssertEqual(stack.migrationChain, migrationChain) XCTAssertEqual(stack.schemaHistory.migrationChain, migrationChain)
CoreStore.defaultStack = stack CoreStore.defaultStack = stack
XCTAssertEqual(CoreStore.defaultStack, stack) XCTAssertEqual(CoreStore.defaultStack, stack)
@@ -80,7 +84,7 @@ class SetupTests: BaseTestDataTestCase {
dynamic func test_ThatInMemoryStores_SetupCorrectly() { dynamic func test_ThatInMemoryStores_SetupCorrectly() {
let stack = DataStack( let stack = DataStack(
modelName: "Model", xcodeModelName: "Model",
bundle: Bundle(for: type(of: self)) bundle: Bundle(for: type(of: self))
) )
do { do {
@@ -135,7 +139,7 @@ class SetupTests: BaseTestDataTestCase {
dynamic func test_ThatSQLiteStores_SetupCorrectly() { dynamic func test_ThatSQLiteStores_SetupCorrectly() {
let stack = DataStack( let stack = DataStack(
modelName: "Model", xcodeModelName: "Model",
bundle: Bundle(for: type(of: self)) bundle: Bundle(for: type(of: self))
) )
do { do {
@@ -203,14 +207,13 @@ class SetupTests: BaseTestDataTestCase {
do { do {
let stack = DataStack( let stack = DataStack(
modelName: "Model", xcodeModelName: "Model",
bundle: Bundle(for: type(of: self)) bundle: Bundle(for: type(of: self))
) )
try! stack.addStorageAndWait(sqliteStore) try! stack.addStorageAndWait(sqliteStore)
self.prepareTestDataForStack(stack) self.prepareTestDataForStack(stack)
} }
XCTAssertTrue(fileManager.fileExists(atPath: sqliteStore.fileURL.path)) XCTAssertTrue(fileManager.fileExists(atPath: sqliteStore.fileURL.path))
XCTAssertTrue(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-wal")))
XCTAssertTrue(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-shm"))) XCTAssertTrue(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-shm")))
return try NSPersistentStoreCoordinator.metadataForPersistentStore( return try NSPersistentStoreCoordinator.metadataForPersistentStore(
@@ -223,10 +226,13 @@ class SetupTests: BaseTestDataTestCase {
let metadata = try createStore() let metadata = try createStore()
let stack = DataStack( let stack = DataStack(
modelName: "Model", xcodeModelName: "Model",
bundle: Bundle(for: type(of: self)) bundle: Bundle(for: type(of: self))
) )
try sqliteStore.eraseStorageAndWait(metadata: metadata, soureModelHint: stack.model[metadata]) try sqliteStore.cs_eraseStorageAndWait(
metadata: metadata,
soureModelHint: stack.schemaHistory.schema(for: metadata)?.rawModel()
)
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path)) XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path))
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-wal"))) XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-wal")))
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-shm"))) XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-shm")))
@@ -238,7 +244,7 @@ class SetupTests: BaseTestDataTestCase {
do { do {
let metadata = try createStore() let metadata = try createStore()
try sqliteStore.eraseStorageAndWait(metadata: metadata, soureModelHint: nil) try sqliteStore.cs_eraseStorageAndWait(metadata: metadata, soureModelHint: nil)
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path)) XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path))
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-wal"))) XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-wal")))
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-shm"))) XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-shm")))
@@ -253,12 +259,12 @@ class SetupTests: BaseTestDataTestCase {
dynamic func test_ThatLegacySQLiteStores_SetupCorrectly() { dynamic func test_ThatLegacySQLiteStores_SetupCorrectly() {
let stack = DataStack( let stack = DataStack(
modelName: "Model", xcodeModelName: "Model",
bundle: Bundle(for: type(of: self)) bundle: Bundle(for: type(of: self))
) )
do { do {
let sqliteStore = LegacySQLiteStore() let sqliteStore = SQLiteStore.legacy()
do { do {
try stack.addStorageAndWait(sqliteStore) try stack.addStorageAndWait(sqliteStore)
@@ -273,7 +279,7 @@ class SetupTests: BaseTestDataTestCase {
} }
do { do {
let sqliteStore = LegacySQLiteStore( let sqliteStore = SQLiteStore.legacy(
fileName: "ConfigStore1.sqlite", fileName: "ConfigStore1.sqlite",
configuration: "Config1", configuration: "Config1",
localStorageOptions: .recreateStoreOnModelMismatch localStorageOptions: .recreateStoreOnModelMismatch
@@ -292,7 +298,7 @@ class SetupTests: BaseTestDataTestCase {
} }
do { do {
let sqliteStore = LegacySQLiteStore( let sqliteStore = SQLiteStore.legacy(
fileName: "ConfigStore2.sqlite", fileName: "ConfigStore2.sqlite",
configuration: "Config2", configuration: "Config2",
localStorageOptions: .recreateStoreOnModelMismatch localStorageOptions: .recreateStoreOnModelMismatch
@@ -315,20 +321,19 @@ class SetupTests: BaseTestDataTestCase {
dynamic func test_ThatLegacySQLiteStores_DeleteFilesCorrectly() { dynamic func test_ThatLegacySQLiteStores_DeleteFilesCorrectly() {
let fileManager = FileManager.default let fileManager = FileManager.default
let sqliteStore = LegacySQLiteStore() let sqliteStore = SQLiteStore.legacy()
func createStore() throws -> [String: Any] { func createStore() throws -> [String: Any] {
do { do {
let stack = DataStack( let stack = DataStack(
modelName: "Model", xcodeModelName: "Model",
bundle: Bundle(for: type(of: self)) bundle: Bundle(for: type(of: self))
) )
try! stack.addStorageAndWait(sqliteStore) try! stack.addStorageAndWait(sqliteStore)
self.prepareTestDataForStack(stack) self.prepareTestDataForStack(stack)
} }
XCTAssertTrue(fileManager.fileExists(atPath: sqliteStore.fileURL.path)) XCTAssertTrue(fileManager.fileExists(atPath: sqliteStore.fileURL.path))
XCTAssertTrue(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-wal")))
XCTAssertTrue(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-shm"))) XCTAssertTrue(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-shm")))
return try NSPersistentStoreCoordinator.metadataForPersistentStore( return try NSPersistentStoreCoordinator.metadataForPersistentStore(
@@ -341,10 +346,13 @@ class SetupTests: BaseTestDataTestCase {
let metadata = try createStore() let metadata = try createStore()
let stack = DataStack( let stack = DataStack(
modelName: "Model", xcodeModelName: "Model",
bundle: Bundle(for: type(of: self)) bundle: Bundle(for: type(of: self))
) )
try sqliteStore.eraseStorageAndWait(metadata: metadata, soureModelHint: stack.model[metadata]) try sqliteStore.cs_eraseStorageAndWait(
metadata: metadata,
soureModelHint: stack.schemaHistory.schema(for: metadata)?.rawModel()
)
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path)) XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path))
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-wal"))) XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-wal")))
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-shm"))) XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-shm")))
@@ -356,7 +364,7 @@ class SetupTests: BaseTestDataTestCase {
do { do {
let metadata = try createStore() let metadata = try createStore()
try sqliteStore.eraseStorageAndWait(metadata: metadata, soureModelHint: nil) try sqliteStore.cs_eraseStorageAndWait(metadata: metadata, soureModelHint: nil)
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path)) XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path))
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-wal"))) XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-wal")))
XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-shm"))) XCTAssertFalse(fileManager.fileExists(atPath: sqliteStore.fileURL.path.appending("-shm")))
+26 -42
View File
@@ -86,7 +86,7 @@ final class StorageInterfaceTests: XCTestCase {
XCTAssertEqual(store.storeOptions as NSDictionary?, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary) XCTAssertEqual(store.storeOptions as NSDictionary?, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary)
XCTAssertEqual(store.fileURL, SQLiteStore.defaultFileURL) XCTAssertEqual(store.fileURL, SQLiteStore.defaultFileURL)
XCTAssertEqual(store.mappingModelBundles, Bundle.allBundles) XCTAssertTrue(store.migrationMappingProviders.isEmpty)
XCTAssertEqual(store.localStorageOptions, .none) XCTAssertEqual(store.localStorageOptions, .none)
} }
@@ -96,12 +96,15 @@ final class StorageInterfaceTests: XCTestCase {
let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory()) let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory())
.appendingPathComponent(NSUUID().uuidString, isDirectory: false)! .appendingPathComponent(NSUUID().uuidString, isDirectory: false)!
.appendingPathExtension("db") .appendingPathExtension("db")
let bundles = [Bundle(for: type(of: self))] let mappingProvider = XcodeSchemaMappingProvider(
from: "V1", to: "V2",
mappingModelBundle: Bundle(for: type(of: self))
)
let store = SQLiteStore( let store = SQLiteStore(
fileURL: fileURL, fileURL: fileURL,
configuration: "config1", configuration: "config1",
mappingModelBundles: bundles, migrationMappingProviders: [mappingProvider],
localStorageOptions: .recreateStoreOnModelMismatch localStorageOptions: .recreateStoreOnModelMismatch
) )
XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType) XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType)
@@ -109,7 +112,7 @@ final class StorageInterfaceTests: XCTestCase {
XCTAssertEqual(store.storeOptions as NSDictionary?, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary) XCTAssertEqual(store.storeOptions as NSDictionary?, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary)
XCTAssertEqual(store.fileURL, fileURL) XCTAssertEqual(store.fileURL, fileURL)
XCTAssertEqual(store.mappingModelBundles, bundles) XCTAssertEqual(store.migrationMappingProviders as! [XcodeSchemaMappingProvider], [mappingProvider])
XCTAssertEqual(store.localStorageOptions, [.recreateStoreOnModelMismatch]) XCTAssertEqual(store.localStorageOptions, [.recreateStoreOnModelMismatch])
} }
@@ -117,12 +120,14 @@ final class StorageInterfaceTests: XCTestCase {
dynamic func test_ThatFileNameSQLiteStores_ConfigureCorrectly() { dynamic func test_ThatFileNameSQLiteStores_ConfigureCorrectly() {
let fileName = UUID().uuidString + ".db" let fileName = UUID().uuidString + ".db"
let bundles = [Bundle(for: type(of: self))] let mappingProvider = XcodeSchemaMappingProvider(
from: "V1", to: "V2",
mappingModelBundle: Bundle(for: type(of: self))
)
let store = SQLiteStore( let store = SQLiteStore(
fileName: fileName, fileName: fileName,
configuration: "config1", configuration: "config1",
mappingModelBundles: bundles, migrationMappingProviders: [mappingProvider],
localStorageOptions: .recreateStoreOnModelMismatch localStorageOptions: .recreateStoreOnModelMismatch
) )
XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType) XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType)
@@ -131,7 +136,7 @@ final class StorageInterfaceTests: XCTestCase {
XCTAssertEqual(store.fileURL.deletingLastPathComponent(), SQLiteStore.defaultRootDirectory) XCTAssertEqual(store.fileURL.deletingLastPathComponent(), SQLiteStore.defaultRootDirectory)
XCTAssertEqual(store.fileURL.lastPathComponent, fileName) XCTAssertEqual(store.fileURL.lastPathComponent, fileName)
XCTAssertEqual(store.mappingModelBundles, bundles) XCTAssertEqual(store.migrationMappingProviders as! [XcodeSchemaMappingProvider], [mappingProvider])
XCTAssertEqual(store.localStorageOptions, [.recreateStoreOnModelMismatch]) XCTAssertEqual(store.localStorageOptions, [.recreateStoreOnModelMismatch])
} }
@@ -152,65 +157,44 @@ final class StorageInterfaceTests: XCTestCase {
.appendingPathComponent(DataStack.applicationName, isDirectory: false) .appendingPathComponent(DataStack.applicationName, isDirectory: false)
.appendingPathExtension("sqlite") .appendingPathExtension("sqlite")
XCTAssertEqual(LegacySQLiteStore.defaultRootDirectory, legacyDefaultRootDirectory) XCTAssertEqual(SQLiteStore.legacyDefaultRootDirectory, legacyDefaultRootDirectory)
XCTAssertEqual(LegacySQLiteStore.defaultFileURL, legacyDefaultFileURL) XCTAssertEqual(SQLiteStore.legacyDefaultFileURL, legacyDefaultFileURL)
} }
@objc @objc
dynamic func test_ThatDefaultLegacySQLiteStores_ConfigureCorrectly() { dynamic func test_ThatDefaultLegacySQLiteStores_ConfigureCorrectly() {
let store = LegacySQLiteStore() let store = SQLiteStore.legacy()
XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType) XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType)
XCTAssertNil(store.configuration) XCTAssertNil(store.configuration)
XCTAssertEqual(store.storeOptions as NSDictionary?, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary) XCTAssertEqual(store.storeOptions as NSDictionary?, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary)
XCTAssertEqual(store.fileURL, LegacySQLiteStore.defaultFileURL) XCTAssertEqual(store.fileURL, SQLiteStore.legacyDefaultFileURL)
XCTAssertEqual(store.mappingModelBundles, Bundle.allBundles) XCTAssertTrue(store.migrationMappingProviders.isEmpty)
XCTAssertEqual(store.localStorageOptions, .none) XCTAssertEqual(store.localStorageOptions, .none)
} }
@objc
dynamic func test_ThatFileURLLegacySQLiteStores_ConfigureCorrectly() {
let fileURL = NSURL(fileURLWithPath: NSTemporaryDirectory())
.appendingPathComponent(NSUUID().uuidString, isDirectory: false)!
.appendingPathExtension("db")
let bundles = [Bundle(for: type(of: self))]
let store = LegacySQLiteStore(
fileURL: fileURL,
configuration: "config1",
mappingModelBundles: bundles,
localStorageOptions: .recreateStoreOnModelMismatch
)
XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType)
XCTAssertEqual(store.configuration, "config1")
XCTAssertEqual(store.storeOptions as NSDictionary?, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary)
XCTAssertEqual(store.fileURL, fileURL)
XCTAssertEqual(store.mappingModelBundles, bundles)
XCTAssertEqual(store.localStorageOptions, [.recreateStoreOnModelMismatch])
}
@objc @objc
dynamic func test_ThatFileNameLegacySQLiteStores_ConfigureCorrectly() { dynamic func test_ThatFileNameLegacySQLiteStores_ConfigureCorrectly() {
let fileName = UUID().uuidString + ".db" let fileName = UUID().uuidString + ".db"
let bundles = [Bundle(for: type(of: self))] let mappingProvider = XcodeSchemaMappingProvider(
from: "V1", to: "V2",
let store = LegacySQLiteStore( mappingModelBundle: Bundle(for: type(of: self))
)
let store = SQLiteStore.legacy(
fileName: fileName, fileName: fileName,
configuration: "config1", configuration: "config1",
mappingModelBundles: bundles, migrationMappingProviders: [mappingProvider],
localStorageOptions: .recreateStoreOnModelMismatch localStorageOptions: .recreateStoreOnModelMismatch
) )
XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType) XCTAssertEqual(type(of: store).storeType, NSSQLiteStoreType)
XCTAssertEqual(store.configuration, "config1") XCTAssertEqual(store.configuration, "config1")
XCTAssertEqual(store.storeOptions as NSDictionary?, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary) XCTAssertEqual(store.storeOptions as NSDictionary?, [NSSQLitePragmasOption: ["journal_mode": "WAL"]] as NSDictionary)
XCTAssertEqual(store.fileURL.deletingLastPathComponent(), LegacySQLiteStore.defaultRootDirectory) XCTAssertEqual(store.fileURL.deletingLastPathComponent(), SQLiteStore.legacyDefaultRootDirectory)
XCTAssertEqual(store.fileURL.lastPathComponent, fileName) XCTAssertEqual(store.fileURL.lastPathComponent, fileName)
XCTAssertEqual(store.mappingModelBundles, bundles) XCTAssertEqual(store.migrationMappingProviders as! [XcodeSchemaMappingProvider], [mappingProvider])
XCTAssertEqual(store.localStorageOptions, [.recreateStoreOnModelMismatch]) XCTAssertEqual(store.localStorageOptions, [.recreateStoreOnModelMismatch])
} }
} }
File diff suppressed because it is too large Load Diff
+155 -188
View File
@@ -29,60 +29,85 @@ import XCTest
import CoreStore import CoreStore
// MARK: - XCTAssertAllEqual
private func XCTAssertAllEqual<D>(_ whereClauses: Where<D>...) {
XCTAssertAllEqual(whereClauses)
}
private func XCTAssertAllEqual<D>(_ whereClauses: [Where<D>]) {
for i in whereClauses.indices {
for j in whereClauses.indices where j != i {
XCTAssertEqual(whereClauses[i], whereClauses[j])
}
}
}
//MARK: - WhereTests //MARK: - WhereTests
final class WhereTests: XCTestCase { final class WhereTests: XCTestCase {
@objc
dynamic func test_ThatDynamicModelKeyPaths_CanBeCreated() {
XCTAssertEqual(String(keyPath: \TestEntity1.testEntityID), "testEntityID")
}
@objc @objc
dynamic func test_ThatWhereClauses_ConfigureCorrectly() { dynamic func test_ThatWhereClauses_ConfigureCorrectly() {
do { do {
let whereClause = Where() let whereClause = Where<NSManagedObject>()
XCTAssertEqual(whereClause, Where(true)) XCTAssertEqual(whereClause, Where<NSManagedObject>(true))
XCTAssertNotEqual(whereClause, Where(false)) XCTAssertNotEqual(whereClause, Where<NSManagedObject>(false))
XCTAssertEqual(whereClause.predicate, NSPredicate(value: true)) XCTAssertEqual(whereClause.predicate, NSPredicate(value: true))
} }
do { do {
let whereClause = Where(true) let whereClause = Where<NSManagedObject>(true)
XCTAssertEqual(whereClause, Where()) XCTAssertEqual(whereClause, Where<NSManagedObject>())
XCTAssertNotEqual(whereClause, Where(false)) XCTAssertNotEqual(whereClause, Where<NSManagedObject>(false))
XCTAssertEqual(whereClause.predicate, NSPredicate(value: true)) XCTAssertEqual(whereClause.predicate, NSPredicate(value: true))
} }
do { do {
let predicate = NSPredicate(format: "%K == %@", "key", "value") let predicate = NSPredicate(format: "%K == %@", "key", "value")
let whereClause = Where(predicate) let whereClause = Where<NSManagedObject>(predicate)
XCTAssertEqual(whereClause, Where(predicate)) XCTAssertEqual(whereClause, Where<NSManagedObject>(predicate))
XCTAssertEqual(whereClause.predicate, predicate) XCTAssertEqual(whereClause.predicate, predicate)
} }
do { do {
let whereClause = Where("%K == %@", "key", "value") let whereClause = Where<NSManagedObject>("%K == %@", "key", "value")
let predicate = NSPredicate(format: "%K == %@", "key", "value") let predicate = NSPredicate(format: "%K == %@", "key", "value")
XCTAssertEqual(whereClause, Where(predicate)) XCTAssertEqual(whereClause, Where<NSManagedObject>(predicate))
XCTAssertEqual(whereClause.predicate, predicate) XCTAssertEqual(whereClause.predicate, predicate)
} }
do { do {
let whereClause = Where("%K == %@", argumentArray: ["key", "value"]) let whereClause = Where<NSManagedObject>("%K == %@", argumentArray: ["key", "value"])
let predicate = NSPredicate(format: "%K == %@", "key", "value") let predicate = NSPredicate(format: "%K == %@", "key", "value")
XCTAssertEqual(whereClause, Where(predicate)) XCTAssertEqual(whereClause, Where<NSManagedObject>(predicate))
XCTAssertEqual(whereClause.predicate, predicate) XCTAssertEqual(whereClause.predicate, predicate)
} }
do { do {
let whereClause = Where("key", isEqualTo: "value") let whereClause = Where<NSManagedObject>("key", isEqualTo: "value")
let predicate = NSPredicate(format: "%K == %@", "key", "value") let predicate = NSPredicate(format: "%K == %@", "key", "value")
XCTAssertEqual(whereClause, Where(predicate)) XCTAssertEqual(whereClause, Where<NSManagedObject>(predicate))
XCTAssertEqual(whereClause.predicate, predicate) XCTAssertEqual(whereClause.predicate, predicate)
} }
do { do {
let whereClause = Where("key", isMemberOf: ["value1", "value2", "value3"]) let whereClause = Where<NSManagedObject>("key", isMemberOf: ["value1", "value2", "value3"])
let predicate = NSPredicate(format: "%K IN %@", "key", ["value1", "value2", "value3"]) let predicate = NSPredicate(format: "%K IN %@", "key", ["value1", "value2", "value3"])
XCTAssertEqual(whereClause, Where(predicate)) XCTAssertEqual(whereClause, Where<NSManagedObject>(predicate))
XCTAssertEqual(whereClause.predicate, predicate) XCTAssertEqual(whereClause.predicate, predicate)
} }
} }
@@ -93,211 +118,123 @@ final class WhereTests: XCTestCase {
do { do {
let value: Int = 100 let value: Int = 100
let whereClause1 = Where("%K == %d", "key", value) XCTAssertAllEqual(
let whereClause2 = Where("%K == %d", "key", value as AnyObject) Where<NSManagedObject>("%K == %d", "key", value),
let whereClause3 = Where("%K == %d", "key", NSNumber(value: value)) Where<NSManagedObject>("%K == %d", "key", value as AnyObject),
let whereClause4 = Where("%K == %@", "key", value) Where<NSManagedObject>("%K == %d", "key", NSNumber(value: value)),
let whereClause5 = Where("%K == %@", "key", value as AnyObject) Where<NSManagedObject>("%K == %@", "key", value),
let whereClause6 = Where("%K == %@", "key", NSNumber(value: value)) Where<NSManagedObject>("%K == %@", "key", value as AnyObject),
XCTAssertEqual(whereClause1, whereClause2) Where<NSManagedObject>("%K == %@", "key", NSNumber(value: value)),
XCTAssertEqual(whereClause1, whereClause3) Where<NSManagedObject>("key", isEqualTo: value),
XCTAssertEqual(whereClause1, whereClause4) Where<NSManagedObject>("key", isEqualTo: NSNumber(value: value))
XCTAssertEqual(whereClause1, whereClause5) )
XCTAssertEqual(whereClause1, whereClause6)
XCTAssertEqual(whereClause2, whereClause3)
XCTAssertEqual(whereClause2, whereClause4)
XCTAssertEqual(whereClause2, whereClause5)
XCTAssertEqual(whereClause2, whereClause6)
XCTAssertEqual(whereClause3, whereClause4)
XCTAssertEqual(whereClause3, whereClause5)
XCTAssertEqual(whereClause3, whereClause6)
XCTAssertEqual(whereClause4, whereClause5)
XCTAssertEqual(whereClause4, whereClause6)
XCTAssertEqual(whereClause5, whereClause6)
} }
do { do {
let value = NSNumber(value: 100) let value = NSNumber(value: 100)
let whereClause1 = Where("%K == %d", "key", value) XCTAssertAllEqual(
let whereClause2 = Where("%K == %d", "key", value as AnyObject) Where<NSManagedObject>("%K == %d", "key", value),
let whereClause3 = Where("%K == %d", "key", value.intValue) Where<NSManagedObject>("%K == %d", "key", value as AnyObject),
let whereClause4 = Where("%K == %@", "key", value) Where<NSManagedObject>("%K == %d", "key", value.intValue),
let whereClause5 = Where("%K == %@", "key", value as AnyObject) Where<NSManagedObject>("%K == %@", "key", value),
let whereClause6 = Where("%K == %@", "key", value.intValue) Where<NSManagedObject>("%K == %@", "key", value as AnyObject),
XCTAssertEqual(whereClause1, whereClause2) Where<NSManagedObject>("%K == %@", "key", value.intValue),
XCTAssertEqual(whereClause1, whereClause3) Where<NSManagedObject>("key", isEqualTo: value),
XCTAssertEqual(whereClause1, whereClause4) Where<NSManagedObject>("key", isEqualTo: value.intValue)
XCTAssertEqual(whereClause1, whereClause5) )
XCTAssertEqual(whereClause1, whereClause6)
XCTAssertEqual(whereClause2, whereClause3)
XCTAssertEqual(whereClause2, whereClause4)
XCTAssertEqual(whereClause2, whereClause5)
XCTAssertEqual(whereClause2, whereClause6)
XCTAssertEqual(whereClause3, whereClause4)
XCTAssertEqual(whereClause3, whereClause5)
XCTAssertEqual(whereClause3, whereClause6)
XCTAssertEqual(whereClause4, whereClause5)
XCTAssertEqual(whereClause4, whereClause6)
XCTAssertEqual(whereClause5, whereClause6)
} }
do { do {
let value: Int64 = Int64.max let value: Int64 = Int64.max
let whereClause1 = Where("%K == %d", "key", value) XCTAssertAllEqual(
let whereClause2 = Where("%K == %d", "key", value as AnyObject) Where<NSManagedObject>("%K == %d", "key", value),
let whereClause3 = Where("%K == %d", "key", NSNumber(value: value)) Where<NSManagedObject>("%K == %d", "key", value as AnyObject),
let whereClause4 = Where("%K == %@", "key", value) Where<NSManagedObject>("%K == %d", "key", NSNumber(value: value)),
let whereClause5 = Where("%K == %@", "key", value as AnyObject) Where<NSManagedObject>("%K == %@", "key", value),
let whereClause6 = Where("%K == %@", "key", NSNumber(value: value)) Where<NSManagedObject>("%K == %@", "key", value as AnyObject),
XCTAssertEqual(whereClause1, whereClause2) Where<NSManagedObject>("%K == %@", "key", NSNumber(value: value)),
XCTAssertEqual(whereClause1, whereClause3) Where<NSManagedObject>("key", isEqualTo: value),
XCTAssertEqual(whereClause1, whereClause4) Where<NSManagedObject>("key", isEqualTo: NSNumber(value: value))
XCTAssertEqual(whereClause1, whereClause5) )
XCTAssertEqual(whereClause1, whereClause6)
XCTAssertEqual(whereClause2, whereClause3)
XCTAssertEqual(whereClause2, whereClause4)
XCTAssertEqual(whereClause2, whereClause5)
XCTAssertEqual(whereClause2, whereClause6)
XCTAssertEqual(whereClause3, whereClause4)
XCTAssertEqual(whereClause3, whereClause5)
XCTAssertEqual(whereClause3, whereClause6)
XCTAssertEqual(whereClause4, whereClause5)
XCTAssertEqual(whereClause4, whereClause6)
XCTAssertEqual(whereClause5, whereClause6)
} }
do { do {
let value = NSNumber(value: Int64.max) let value = NSNumber(value: Int64.max)
let whereClause1 = Where("%K == %d", "key", value) XCTAssertAllEqual(
let whereClause2 = Where("%K == %d", "key", value as AnyObject) Where<NSManagedObject>("%K == %d", "key", value),
let whereClause3 = Where("%K == %d", "key", value.int64Value) Where<NSManagedObject>("%K == %d", "key", value as AnyObject),
let whereClause4 = Where("%K == %@", "key", value) Where<NSManagedObject>("%K == %d", "key", value.int64Value),
let whereClause5 = Where("%K == %@", "key", value as AnyObject) Where<NSManagedObject>("%K == %@", "key", value),
let whereClause6 = Where("%K == %@", "key", value.int64Value) Where<NSManagedObject>("%K == %@", "key", value as AnyObject),
XCTAssertEqual(whereClause1, whereClause2) Where<NSManagedObject>("%K == %@", "key", value.int64Value),
XCTAssertEqual(whereClause1, whereClause3) Where<NSManagedObject>("key", isEqualTo: value),
XCTAssertEqual(whereClause1, whereClause4) Where<NSManagedObject>("key", isEqualTo: value.int64Value)
XCTAssertEqual(whereClause1, whereClause5) )
XCTAssertEqual(whereClause1, whereClause6)
XCTAssertEqual(whereClause2, whereClause3)
XCTAssertEqual(whereClause2, whereClause4)
XCTAssertEqual(whereClause2, whereClause5)
XCTAssertEqual(whereClause2, whereClause6)
XCTAssertEqual(whereClause3, whereClause4)
XCTAssertEqual(whereClause3, whereClause5)
XCTAssertEqual(whereClause3, whereClause6)
XCTAssertEqual(whereClause4, whereClause5)
XCTAssertEqual(whereClause4, whereClause6)
XCTAssertEqual(whereClause5, whereClause6)
} }
do { do {
let value: String = "value" let value: String = "value"
let whereClause1 = Where("%K == %s", "key", value) XCTAssertAllEqual(
let whereClause2 = Where("%K == %s", "key", value as AnyObject) Where<NSManagedObject>("%K == %s", "key", value),
let whereClause3 = Where("%K == %s", "key", NSString(string: value)) Where<NSManagedObject>("%K == %s", "key", value as AnyObject),
let whereClause4 = Where("%K == %@", "key", value) Where<NSManagedObject>("%K == %s", "key", NSString(string: value)),
let whereClause5 = Where("%K == %@", "key", value as AnyObject) Where<NSManagedObject>("%K == %@", "key", value),
let whereClause6 = Where("%K == %@", "key", NSString(string: value)) Where<NSManagedObject>("%K == %@", "key", value as AnyObject),
XCTAssertEqual(whereClause1, whereClause2) Where<NSManagedObject>("%K == %@", "key", NSString(string: value)),
XCTAssertEqual(whereClause1, whereClause3) Where<NSManagedObject>("key", isEqualTo: value),
XCTAssertEqual(whereClause1, whereClause4) Where<NSManagedObject>("key", isEqualTo: value as NSString),
XCTAssertEqual(whereClause1, whereClause5) Where<NSManagedObject>("key", isEqualTo: NSString(string: value))
XCTAssertEqual(whereClause1, whereClause6) )
XCTAssertEqual(whereClause2, whereClause3)
XCTAssertEqual(whereClause2, whereClause4)
XCTAssertEqual(whereClause2, whereClause5)
XCTAssertEqual(whereClause2, whereClause6)
XCTAssertEqual(whereClause3, whereClause4)
XCTAssertEqual(whereClause3, whereClause5)
XCTAssertEqual(whereClause3, whereClause6)
XCTAssertEqual(whereClause4, whereClause5)
XCTAssertEqual(whereClause4, whereClause6)
XCTAssertEqual(whereClause5, whereClause6)
} }
do { do {
let value = NSString(string: "value") let value = NSString(string: "value")
let whereClause1 = Where("%K == %s", "key", value) XCTAssertAllEqual(
let whereClause2 = Where("%K == %s", "key", value as String) Where<NSManagedObject>("%K == %s", "key", value),
let whereClause3 = Where("%K == %s", "key", value as String as AnyObject) Where<NSManagedObject>("%K == %s", "key", value as String),
let whereClause4 = Where("%K == %@", "key", value) Where<NSManagedObject>("%K == %s", "key", value as String as AnyObject),
let whereClause5 = Where("%K == %@", "key", value as String) Where<NSManagedObject>("%K == %@", "key", value),
let whereClause6 = Where("%K == %@", "key", value as String as AnyObject) Where<NSManagedObject>("%K == %@", "key", value as String),
XCTAssertEqual(whereClause1, whereClause2) Where<NSManagedObject>("%K == %@", "key", value as String as AnyObject),
XCTAssertEqual(whereClause1, whereClause3) Where<NSManagedObject>("key", isEqualTo: value),
XCTAssertEqual(whereClause1, whereClause4) Where<NSManagedObject>("key", isEqualTo: value as String),
XCTAssertEqual(whereClause1, whereClause5) Where<NSManagedObject>("key", isEqualTo: value as String as NSString)
XCTAssertEqual(whereClause1, whereClause6) )
XCTAssertEqual(whereClause2, whereClause3)
XCTAssertEqual(whereClause2, whereClause4)
XCTAssertEqual(whereClause2, whereClause5)
XCTAssertEqual(whereClause2, whereClause6)
XCTAssertEqual(whereClause3, whereClause4)
XCTAssertEqual(whereClause3, whereClause5)
XCTAssertEqual(whereClause3, whereClause6)
XCTAssertEqual(whereClause4, whereClause5)
XCTAssertEqual(whereClause4, whereClause6)
XCTAssertEqual(whereClause5, whereClause6)
} }
do { do {
let value: [Int] = [100, 200] let value: [Int] = [100, 200]
let whereClause1 = Where("%K == %@", "key", value) XCTAssertAllEqual(
let whereClause2 = Where("%K == %@", "key", value as AnyObject) Where<NSManagedObject>("%K IN %@", "key", value),
let whereClause3 = Where("%K == %@", "key", value as [AnyObject]) Where<NSManagedObject>("%K IN %@", "key", value as AnyObject),
let whereClause4 = Where("%K == %@", "key", value as NSArray) Where<NSManagedObject>("%K IN %@", "key", value as [AnyObject]),
let whereClause5 = Where("%K == %@", "key", NSArray(array: value)) Where<NSManagedObject>("%K IN %@", "key", value as NSArray),
let whereClause6 = Where("%K == %@", "key", value as AnyObject as! NSArray) Where<NSManagedObject>("%K IN %@", "key", NSArray(array: value)),
XCTAssertEqual(whereClause1, whereClause2) Where<NSManagedObject>("%K IN %@", "key", value as AnyObject as! NSArray),
XCTAssertEqual(whereClause1, whereClause3) Where<NSManagedObject>("key", isMemberOf: value)
XCTAssertEqual(whereClause1, whereClause4) )
XCTAssertEqual(whereClause1, whereClause5)
XCTAssertEqual(whereClause1, whereClause6)
XCTAssertEqual(whereClause2, whereClause3)
XCTAssertEqual(whereClause2, whereClause4)
XCTAssertEqual(whereClause2, whereClause5)
XCTAssertEqual(whereClause2, whereClause6)
XCTAssertEqual(whereClause3, whereClause4)
XCTAssertEqual(whereClause3, whereClause5)
XCTAssertEqual(whereClause3, whereClause6)
XCTAssertEqual(whereClause4, whereClause5)
XCTAssertEqual(whereClause4, whereClause6)
XCTAssertEqual(whereClause5, whereClause6)
} }
do { do {
let value: [Int64] = [Int64.min, 100, Int64.max] let value: [Int64] = [Int64.min, 100, Int64.max]
let whereClause1 = Where("%K == %@", "key", value) XCTAssertAllEqual(
let whereClause2 = Where("%K == %@", "key", value as AnyObject) Where<NSManagedObject>("%K IN %@", "key", value),
let whereClause3 = Where("%K == %@", "key", value as [AnyObject]) Where<NSManagedObject>("%K IN %@", "key", value as AnyObject),
let whereClause4 = Where("%K == %@", "key", value as NSArray) Where<NSManagedObject>("%K IN %@", "key", value as [AnyObject]),
let whereClause5 = Where("%K == %@", "key", NSArray(array: value)) Where<NSManagedObject>("%K IN %@", "key", value as NSArray),
let whereClause6 = Where("%K == %@", "key", value as AnyObject as! NSArray) Where<NSManagedObject>("%K IN %@", "key", NSArray(array: value)),
XCTAssertEqual(whereClause1, whereClause2) Where<NSManagedObject>("%K IN %@", "key", value as AnyObject as! NSArray),
XCTAssertEqual(whereClause1, whereClause3) Where<NSManagedObject>("key", isMemberOf: value)
XCTAssertEqual(whereClause1, whereClause4) )
XCTAssertEqual(whereClause1, whereClause5)
XCTAssertEqual(whereClause1, whereClause6)
XCTAssertEqual(whereClause2, whereClause3)
XCTAssertEqual(whereClause2, whereClause4)
XCTAssertEqual(whereClause2, whereClause5)
XCTAssertEqual(whereClause2, whereClause6)
XCTAssertEqual(whereClause3, whereClause4)
XCTAssertEqual(whereClause3, whereClause5)
XCTAssertEqual(whereClause3, whereClause6)
XCTAssertEqual(whereClause4, whereClause5)
XCTAssertEqual(whereClause4, whereClause6)
XCTAssertEqual(whereClause5, whereClause6)
} }
} }
@objc @objc
dynamic func test_ThatWhereClauseOperations_ComputeCorrectly() { dynamic func test_ThatWhereClauseOperations_ComputeCorrectly() {
let whereClause1 = Where("key1", isEqualTo: "value1") let whereClause1 = Where<NSManagedObject>("key1", isEqualTo: "value1")
let whereClause2 = Where("key2", isEqualTo: "value2") let whereClause2 = Where<NSManagedObject>("key2", isEqualTo: "value2")
let whereClause3 = Where("key3", isEqualTo: "value3") let whereClause3 = Where<NSManagedObject>("key3", isEqualTo: "value3")
do { do {
@@ -325,6 +262,21 @@ final class WhereTests: XCTestCase {
XCTAssertEqual(andWhere.predicate, andPredicate) XCTAssertEqual(andWhere.predicate, andPredicate)
XCTAssertEqual(andWhere, whereClause1 && whereClause2 && whereClause3) XCTAssertEqual(andWhere, whereClause1 && whereClause2 && whereClause3)
} }
do {
let andWhere = whereClause1 && whereClause2 && whereClause3
let noneWhere: Where<NSManagedObject>? = nil
let someWhere: Where<NSManagedObject>? = Where<NSManagedObject>("key4", isEqualTo: "value4")
let finalNoneWhere = andWhere &&? noneWhere
let finalSomeWhere = andWhere &&? someWhere
let unwrappedFinalSomeWhere = andWhere && someWhere!
XCTAssertEqual(andWhere.predicate, finalNoneWhere.predicate)
XCTAssertEqual(finalSomeWhere.predicate, unwrappedFinalSomeWhere.predicate)
}
do { do {
let orWhere = whereClause1 || whereClause2 || whereClause3 let orWhere = whereClause1 || whereClause2 || whereClause3
@@ -341,12 +293,27 @@ final class WhereTests: XCTestCase {
XCTAssertEqual(orWhere.predicate, orPredicate) XCTAssertEqual(orWhere.predicate, orPredicate)
XCTAssertEqual(orWhere, whereClause1 || whereClause2 || whereClause3) XCTAssertEqual(orWhere, whereClause1 || whereClause2 || whereClause3)
} }
do {
let orWhere = whereClause1 || whereClause2 || whereClause3
let noneWhere: Where<NSManagedObject>? = nil
let someWhere: Where<NSManagedObject>? = Where<NSManagedObject>("key4", isEqualTo: "value4")
let finalNoneWhere = orWhere &&? noneWhere
let finalSomeWhere = orWhere &&? someWhere
let unwrappedFinalSomeWhere = orWhere && someWhere!
XCTAssertEqual(orWhere.predicate, finalNoneWhere.predicate)
XCTAssertEqual(finalSomeWhere.predicate, unwrappedFinalSomeWhere.predicate)
}
} }
@objc @objc
dynamic func test_ThatWhereClauses_ApplyToFetchRequestsCorrectly() { dynamic func test_ThatWhereClauses_ApplyToFetchRequestsCorrectly() {
let whereClause = Where("key", isEqualTo: "value") let whereClause = Where<NSManagedObject>("key", isEqualTo: "value")
let request = CoreStoreFetchRequest() let request = CoreStoreFetchRequest()
whereClause.applyToFetchRequest(request) whereClause.applyToFetchRequest(request)
XCTAssertNotNil(request.predicate) XCTAssertNotNil(request.predicate)
+688 -306
View File
File diff suppressed because it is too large Load Diff
@@ -30,72 +30,77 @@ import CoreData
// MARK: - AsynchronousDataTransaction // MARK: - AsynchronousDataTransaction
/** /**
The `AsynchronousDataTransaction` provides an interface for `NSManagedObject` creates, updates, and deletes. A transaction object should typically be only used from within a transaction block initiated from `DataStack.beginAsynchronous(_:)`, or from `CoreStore.beginAsynchronous(_:)`. The `AsynchronousDataTransaction` provides an interface for `DynamicObject` creates, updates, and deletes. A transaction object should typically be only used from within a transaction block initiated from `DataStack.perform(asynchronous:...)`, or from `CoreStore.perform(synchronous:...)`.
*/ */
public final class AsynchronousDataTransaction: BaseDataTransaction { public final class AsynchronousDataTransaction: BaseDataTransaction {
/** /**
Saves the transaction changes. This method should not be used after the `commit()` method was already called once. Cancels a transaction by throwing `CoreStoreError.userCancelled`.
```
- parameter completion: the block executed after the save completes. Success or failure is reported by the `SaveResult` argument of the block. try transaction.cancel()
```
- Important: Never use `try?` or `try!` on a `cancel()` call. Always use `try`. Using `try?` will swallow the cancellation and the transaction will proceed to commit as normal. Using `try!` will crash the app as `cancel()` will *always* throw an error.
*/ */
public func commit(_ completion: @escaping (_ result: SaveResult) -> Void = { _ in }) { public func cancel() throws -> Never {
CoreStore.assert( throw CoreStoreError.userCancelled
self.transactionQueue.cs_isCurrentExecutionContext(),
"Attempted to commit a \(cs_typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to commit a \(cs_typeName(self)) more than once."
)
self.isCommitted = true
let group = DispatchGroup()
group.enter()
self.context.saveAsynchronouslyWithCompletion { (result) -> Void in
self.result = result
completion(result)
group.leave()
}
group.wait()
} }
// MARK: - Result
/** /**
Begins a child transaction synchronously where NSManagedObject creates, updates, and deletes can be made. This method should not be used after the `commit()` method was already called once. The `Result` contains the success or failure information for a completed transaction
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
- returns: a `SaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
*/ */
@discardableResult public enum Result<T> {
public func beginSynchronous(_ closure: @escaping (_ transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
CoreStore.assert( /**
self.transactionQueue.cs_isCurrentExecutionContext(), `Result<T>.success` indicates that the transaction succeeded, either because the save succeeded or because there were no changes to save. The associated `userInfo` is the value returned from the transaction closure.
"Attempted to begin a child transaction from a \(cs_typeName(self)) outside its designated queue." */
) case success(userInfo: T)
CoreStore.assert(
!self.isCommitted,
"Attempted to begin a child transaction from an already committed \(cs_typeName(self))."
)
return SynchronousDataTransaction( /**
mainContext: self.context, `Result<T>.failure` indicates that the transaction either failed or was cancelled. The associated object for this value is a `CoreStoreError` enum value.
queue: self.childTransactionQueue, */
closure: closure).performAndWait() case failure(error: CoreStoreError)
/**
Returns `true` if the result indicates `.success`, `false` if the result is `.failure`.
*/
public var boolValue: Bool {
switch self {
case .success: return true
case .failure: return false
}
} }
// MARK: Internal
internal init(userInfo: T) {
self = .success(userInfo: userInfo)
}
internal init(error: CoreStoreError) {
self = .failure(error: error)
}
}
// MARK: -
// MARK: BaseDataTransaction // MARK: BaseDataTransaction
/** /**
Creates a new `NSManagedObject` with the specified entity type. Creates a new `NSManagedObject` or `CoreStoreObject` with the specified entity type.
- parameter into: the `Into` clause indicating the destination `NSManagedObject` entity type and the destination configuration - parameter into: the `Into` clause indicating the destination `NSManagedObject` or `CoreStoreObject` entity type and the destination configuration
- returns: a new `NSManagedObject` instance of the specified entity type. - returns: a new `NSManagedObject` or `CoreStoreObject` instance of the specified entity type.
*/ */
public override func create<T: NSManagedObject>(_ into: Into<T>) -> T { public override func create<D>(_ into: Into<D>) -> D {
CoreStore.assert( CoreStore.assert(
!self.isCommitted, !self.isCommitted,
@@ -106,12 +111,12 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
} }
/** /**
Returns an editable proxy of a specified `NSManagedObject`. This method should not be used after the `commit()` method was already called once. Returns an editable proxy of a specified `NSManagedObject` or `CoreStoreObject`.
- parameter object: the `NSManagedObject` type to be edited - parameter object: the `NSManagedObject` or `CoreStoreObject` to be edited
- returns: an editable proxy for the specified `NSManagedObject`. - returns: an editable proxy for the specified `NSManagedObject` or `CoreStoreObject`.
*/ */
public override func edit<T: NSManagedObject>(_ object: T?) -> T? { public override func edit<D: DynamicObject>(_ object: D?) -> D? {
CoreStore.assert( CoreStore.assert(
!self.isCommitted, !self.isCommitted,
@@ -122,13 +127,13 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
} }
/** /**
Returns an editable proxy of the object with the specified `NSManagedObjectID`. This method should not be used after the `commit()` method was already called once. Returns an editable proxy of the object with the specified `NSManagedObjectID`.
- parameter into: an `Into` clause specifying the entity type - parameter into: an `Into` clause specifying the entity type
- parameter objectID: the `NSManagedObjectID` for the object to be edited - parameter objectID: the `NSManagedObjectID` for the object to be edited
- returns: an editable proxy for the specified `NSManagedObject`. - returns: an editable proxy for the specified `NSManagedObject` or `CoreStoreObject`.
*/ */
public override func edit<T: NSManagedObject>(_ into: Into<T>, _ objectID: NSManagedObjectID) -> T? { public override func edit<D>(_ into: Into<D>, _ objectID: NSManagedObjectID) -> D? {
CoreStore.assert( CoreStore.assert(
!self.isCommitted, !self.isCommitted,
@@ -139,11 +144,11 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
} }
/** /**
Deletes a specified `NSManagedObject`. This method should not be used after the `commit()` method was already called once. Deletes a specified `NSManagedObject` or `CoreStoreObject`.
- parameter object: the `NSManagedObject` type to be deleted - parameter object: the `NSManagedObject` or `CoreStoreObject` to be deleted
*/ */
public override func delete(_ object: NSManagedObject?) { public override func delete<D: DynamicObject>(_ object: D?) {
CoreStore.assert( CoreStore.assert(
!self.isCommitted, !self.isCommitted,
@@ -154,13 +159,13 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
} }
/** /**
Deletes the specified `NSManagedObject`s. Deletes the specified `DynamicObject`s.
- parameter object1: the `NSManagedObject` type to be deleted - parameter object1: the `DynamicObject` to be deleted
- parameter object2: another `NSManagedObject` type to be deleted - parameter object2: another `DynamicObject` to be deleted
- parameter objects: other `NSManagedObject`s type to be deleted - parameter objects: other `DynamicObject`s to be deleted
*/ */
public override func delete(_ object1: NSManagedObject?, _ object2: NSManagedObject?, _ objects: NSManagedObject?...) { public override func delete<D: DynamicObject>(_ object1: D?, _ object2: D?, _ objects: D?...) {
CoreStore.assert( CoreStore.assert(
!self.isCommitted, !self.isCommitted,
@@ -171,11 +176,11 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
} }
/** /**
Deletes the specified `NSManagedObject`s. Deletes the specified `DynamicObject`s.
- parameter objects: the `NSManagedObject`s type to be deleted - parameter objects: the `DynamicObject`s to be deleted
*/ */
public override func delete<S: Sequence>(_ objects: S) where S.Iterator.Element: NSManagedObject { public override func delete<S: Sequence>(_ objects: S) where S.Iterator.Element: DynamicObject {
CoreStore.assert( CoreStore.assert(
!self.isCommitted, !self.isCommitted,
@@ -188,47 +193,86 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
// MARK: Internal // MARK: Internal
internal init(mainContext: NSManagedObjectContext, queue: DispatchQueue, closure: @escaping (_ transaction: AsynchronousDataTransaction) -> Void) { internal init(mainContext: NSManagedObjectContext, queue: DispatchQueue) {
self.closure = closure
super.init(mainContext: mainContext, queue: queue, supportsUndo: false, bypassesQueueing: false) super.init(mainContext: mainContext, queue: queue, supportsUndo: false, bypassesQueueing: false)
} }
internal func perform() { internal func autoCommit(_ completion: @escaping (_ hasChanges: Bool, _ error: CoreStoreError?) -> Void) {
self.transactionQueue.async { self.isCommitted = true
let group = DispatchGroup()
group.enter()
self.context.saveAsynchronouslyWithCompletion { (hasChanges, error) -> Void in
self.closure(self) completion(hasChanges, error)
if !self.isCommitted && self.hasChanges { self.result = (hasChanges, error)
group.leave()
}
group.wait()
self.context.reset()
}
// MARK: Deprecated
@available(*, deprecated, message: "Use the new auto-commiting methods `DataStack.perform(asynchronous:completion:)` or `DataStack.perform(asynchronous:success:failure:)`. Please read the documentation on the behavior of the new methods.")
public func commit(_ completion: @escaping (_ result: SaveResult) -> Void = { _ in }) {
CoreStore.assert(
self.transactionQueue.cs_isCurrentExecutionContext(),
"Attempted to commit a \(cs_typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to commit a \(cs_typeName(self)) more than once."
)
self.autoCommit { (hasChanges, error) in
if let error = error {
completion(SaveResult(error))
}
else {
completion(SaveResult(hasChanges: hasChanges))
}
}
}
@available(*, deprecated, message: "Secondary tasks spawned from AsynchronousDataTransactions and SynchronousDataTransactions are no longer supported. ")
@discardableResult
public func beginSynchronous(_ closure: @escaping (_ transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
CoreStore.assert(
self.transactionQueue.cs_isCurrentExecutionContext(),
"Attempted to begin a child transaction from a \(cs_typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to begin a child transaction from an already committed \(cs_typeName(self))."
)
let childTransaction = SynchronousDataTransaction(
mainContext: self.context,
queue: self.childTransactionQueue
)
childTransaction.transactionQueue.cs_sync {
closure(childTransaction)
if !childTransaction.isCommitted && childTransaction.hasChanges {
CoreStore.log( CoreStore.log(
.warning, .warning,
message: "The closure for the \(cs_typeName(self)) completed without being committed. All changes made within the transaction were discarded." message: "The closure for the \(cs_typeName(childTransaction)) completed without being committed. All changes made within the transaction were discarded."
) )
} }
} }
} switch childTransaction.result {
internal func performAndWait() -> SaveResult? { case .none: return nil
case .some(let hasChanges, nil): return SaveResult(hasChanges: hasChanges)
self.transactionQueue.sync { case .some(_, let error?): return SaveResult(error)
self.closure(self)
if !self.isCommitted && self.hasChanges {
CoreStore.log(
.warning,
message: "The closure for the \(cs_typeName(self)) completed without being committed. All changes made within the transaction were discarded."
)
} }
} }
return self.result
}
// MARK: Private
private let closure: (_ transaction: AsynchronousDataTransaction) -> Void
} }
+47
View File
@@ -0,0 +1,47 @@
//
// AttributeProtocol.swift
// CoreStore
//
// Copyright © 2017 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import Foundation
import CoreData
// MARK: - AttributeProtocol
internal protocol AttributeProtocol: class {
static var attributeType: NSAttributeType { get }
var keyPath: KeyPathString { get }
var isOptional: Bool { get }
var isIndexed: Bool { get }
var isTransient: Bool { get }
var versionHashModifier: () -> String? { get }
var renamingIdentifier: () -> String? { get }
var defaultValue: () -> Any? { get }
var affectedByKeyPaths: () -> Set<String> { get }
weak var parentObject: CoreStoreObject? { get set }
var getter: CoreStoreManagedObject.CustomGetter? { get }
var setter: CoreStoreManagedObject.CustomSetter? { get }
}
@@ -39,9 +39,9 @@ public extension BaseDataTransaction {
- throws: an `Error` thrown from any of the `ImportableObject` methods - throws: an `Error` thrown from any of the `ImportableObject` methods
- returns: the created `ImportableObject` instance, or `nil` if the import was ignored - returns: the created `ImportableObject` instance, or `nil` if the import was ignored
*/ */
public func importObject<T>( public func importObject<D: ImportableObject>(
_ into: Into<T>, _ into: Into<D>,
source: T.ImportSource) throws -> T? where T: NSManagedObject, T: ImportableObject { source: D.ImportSource) throws -> D? {
CoreStore.assert( CoreStore.assert(
self.isRunningInAllowedQueue(), self.isRunningInAllowedQueue(),
@@ -50,8 +50,7 @@ public extension BaseDataTransaction {
return try autoreleasepool { return try autoreleasepool {
let entityType = into.entityClass as! T.Type let entityType = into.entityClass
guard entityType.shouldInsert(from: source, in: self) else { guard entityType.shouldInsert(from: source, in: self) else {
return nil return nil
@@ -70,9 +69,9 @@ public extension BaseDataTransaction {
- parameter source: the object to import values from - parameter source: the object to import values from
- throws: an `Error` thrown from any of the `ImportableObject` methods - throws: an `Error` thrown from any of the `ImportableObject` methods
*/ */
public func importObject<T>( public func importObject<D: ImportableObject>(
_ object: T, _ object: D,
source: T.ImportSource) throws where T: NSManagedObject, T: ImportableObject { source: D.ImportSource) throws {
CoreStore.assert( CoreStore.assert(
self.isRunningInAllowedQueue(), self.isRunningInAllowedQueue(),
@@ -98,9 +97,9 @@ public extension BaseDataTransaction {
- throws: an `Error` thrown from any of the `ImportableObject` methods - throws: an `Error` thrown from any of the `ImportableObject` methods
- returns: the array of created `ImportableObject` instances - returns: the array of created `ImportableObject` instances
*/ */
public func importObjects<T, S: Sequence>( public func importObjects<D: ImportableObject, S: Sequence>(
_ into: Into<T>, _ into: Into<D>,
sourceArray: S) throws -> [T] where T: NSManagedObject, T: ImportableObject, S.Iterator.Element == T.ImportSource { sourceArray: S) throws -> [D] where S.Iterator.Element == D.ImportSource {
CoreStore.assert( CoreStore.assert(
self.isRunningInAllowedQueue(), self.isRunningInAllowedQueue(),
@@ -109,9 +108,9 @@ public extension BaseDataTransaction {
return try autoreleasepool { return try autoreleasepool {
return try sourceArray.flatMap { (source) -> T? in return try sourceArray.flatMap { (source) -> D? in
let entityType = into.entityClass as! T.Type let entityType = into.entityClass
guard entityType.shouldInsert(from: source, in: self) else { guard entityType.shouldInsert(from: source, in: self) else {
return nil return nil
@@ -134,9 +133,9 @@ public extension BaseDataTransaction {
- throws: an `Error` thrown from any of the `ImportableUniqueObject` methods - throws: an `Error` thrown from any of the `ImportableUniqueObject` methods
- returns: the created/updated `ImportableUniqueObject` instance, or `nil` if the import was ignored - returns: the created/updated `ImportableUniqueObject` instance, or `nil` if the import was ignored
*/ */
public func importUniqueObject<T>( public func importUniqueObject<D: ImportableUniqueObject>(
_ into: Into<T>, _ into: Into<D>,
source: T.ImportSource) throws -> T? where T: NSManagedObject, T: ImportableUniqueObject { source: D.ImportSource) throws -> D? {
CoreStore.assert( CoreStore.assert(
self.isRunningInAllowedQueue(), self.isRunningInAllowedQueue(),
@@ -145,14 +144,14 @@ public extension BaseDataTransaction {
return try autoreleasepool { return try autoreleasepool {
let entityType = into.entityClass as! T.Type let entityType = into.entityClass
let uniqueIDKeyPath = entityType.uniqueIDKeyPath let uniqueIDKeyPath = entityType.uniqueIDKeyPath
guard let uniqueIDValue = try entityType.uniqueID(from: source, in: self) else { guard let uniqueIDValue = try entityType.uniqueID(from: source, in: self) else {
return nil return nil
} }
if let object = self.fetchOne(From(entityType), Where(uniqueIDKeyPath, isEqualTo: uniqueIDValue)) { if let object = self.fetchOne(From(entityType), Where<D>(uniqueIDKeyPath, isEqualTo: uniqueIDValue)) {
guard entityType.shouldUpdate(from: source, in: self) else { guard entityType.shouldUpdate(from: source, in: self) else {
@@ -178,7 +177,7 @@ public extension BaseDataTransaction {
/** /**
Updates existing `ImportableUniqueObject`s or creates them by importing from the specified array of import sources. Updates existing `ImportableUniqueObject`s or creates them by importing from the specified array of import sources.
`ImportableUniqueObject` methods are called on the objects in the same order as they are in the `sourceArray`, and are returned in an array with that same order. `ImportableUniqueObject` methods are called on the objects in the same order as they are in the `sourceArray`, and are returned in an array with that same order.
- Warning: If `sourceArray` contains multiple import sources with same ID, no merging will occur and ONLY THE LAST duplicate will be imported. - Warning: If `sourceArray` contains multiple import sources with same ID, only the last `ImportSource` of the duplicates will be imported.
- parameter into: an `Into` clause specifying the entity type - parameter into: an `Into` clause specifying the entity type
- parameter sourceArray: the array of objects to import values from - parameter sourceArray: the array of objects to import values from
@@ -186,10 +185,10 @@ public extension BaseDataTransaction {
- throws: an `Error` thrown from any of the `ImportableUniqueObject` methods - throws: an `Error` thrown from any of the `ImportableUniqueObject` methods
- returns: the array of created/updated `ImportableUniqueObject` instances - returns: the array of created/updated `ImportableUniqueObject` instances
*/ */
public func importUniqueObjects<T, S: Sequence>( public func importUniqueObjects<D: ImportableUniqueObject, S: Sequence>(
_ into: Into<T>, _ into: Into<D>,
sourceArray: S, sourceArray: S,
preProcess: @escaping (_ mapping: [T.UniqueIDType: T.ImportSource]) throws -> [T.UniqueIDType: T.ImportSource] = { $0 }) throws -> [T] where T: NSManagedObject, T: ImportableUniqueObject, S.Iterator.Element == T.ImportSource { preProcess: @escaping (_ mapping: [D.UniqueIDType: D.ImportSource]) throws -> [D.UniqueIDType: D.ImportSource] = { $0 }) throws -> [D] where S.Iterator.Element == D.ImportSource {
CoreStore.assert( CoreStore.assert(
self.isRunningInAllowedQueue(), self.isRunningInAllowedQueue(),
@@ -198,11 +197,11 @@ public extension BaseDataTransaction {
return try autoreleasepool { return try autoreleasepool {
let entityType = into.entityClass as! T.Type let entityType = into.entityClass
var importSourceByID = Dictionary<T.UniqueIDType, T.ImportSource>() var importSourceByID = Dictionary<D.UniqueIDType, D.ImportSource>()
let sortedIDs = try autoreleasepool { let sortedIDs = try autoreleasepool {
return try sourceArray.flatMap { (source) -> T.UniqueIDType? in return try sourceArray.flatMap { (source) -> D.UniqueIDType? in
guard let uniqueIDValue = try entityType.uniqueID(from: source, in: self) else { guard let uniqueIDValue = try entityType.uniqueID(from: source, in: self) else {
@@ -215,12 +214,12 @@ public extension BaseDataTransaction {
importSourceByID = try autoreleasepool { try preProcess(importSourceByID) } importSourceByID = try autoreleasepool { try preProcess(importSourceByID) }
var existingObjectsByID = Dictionary<T.UniqueIDType, T>() var existingObjectsByID = Dictionary<D.UniqueIDType, D>()
self.fetchAll(From(entityType), Where(entityType.uniqueIDKeyPath, isMemberOf: sortedIDs))? self.fetchAll(From(entityType), Where<D>(entityType.uniqueIDKeyPath, isMemberOf: sortedIDs))?
.forEach { existingObjectsByID[$0.uniqueIDValue] = $0 } .forEach { existingObjectsByID[$0.uniqueIDValue] = $0 }
var processedObjectIDs = Set<T.UniqueIDType>() var processedObjectIDs = Set<D.UniqueIDType>()
var result = [T]() var result = [D]()
for objectID in sortedIDs where !processedObjectIDs.contains(objectID) { for objectID in sortedIDs where !processedObjectIDs.contains(objectID) {
+552
View File
@@ -0,0 +1,552 @@
//
// BaseDataTransaction+Querying.swift
// CoreStore
//
// Copyright © 2015 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import Foundation
import CoreData
// MARK: - DataTransaction
extension BaseDataTransaction: FetchableSource, QueryableSource {
/**
Deletes all `DynamicObject`s that satisfy the specified `DeleteClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter deleteClauses: a series of `DeleteClause` instances for the delete request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the number of `DynamicObject`s deleted
*/
@discardableResult
public func deleteAll<D>(_ from: From<D>, _ deleteClauses: DeleteClause...) -> Int? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to delete from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.deleteAll(from, deleteClauses)
}
/**
Deletes all `DynamicObject`s that satisfy the specified `DeleteClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter deleteClauses: a series of `DeleteClause` instances for the delete request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the number of `DynamicObject`s deleted
*/
@discardableResult
public func deleteAll<D>(_ from: From<D>, _ deleteClauses: [DeleteClause]) -> Int? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to delete from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.deleteAll(from, deleteClauses)
}
/**
Deletes all `DynamicObject`s that satisfy the specified conditions.
```
transaction.deleteAll(From<Person>().where(\.age > 50))
```
- parameter clauseChain: a `FetchChainableBuilderType` clause chain created from a `From` clause
- returns: the number of `DynamicObject`s deleted
*/
@discardableResult
public func deleteAll<B: FetchChainableBuilderType>(_ clauseChain: B) -> Int? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to delete from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.deleteAll(clauseChain.from, clauseChain.fetchClauses)
}
// MARK: FetchableSource
/**
Fetches the `DynamicObject` instance in the transaction's context from a reference created from a transaction or from a different managed object context.
- parameter object: a reference to the object created/fetched outside the transaction
- returns: the `DynamicObject` instance if the object exists in the transaction, or `nil` if not found.
*/
public func fetchExisting<D: DynamicObject>(_ object: D) -> D? {
return self.context.fetchExisting(object)
}
/**
Fetches the `DynamicObject` instance in the transaction's context from an `NSManagedObjectID`.
- parameter objectID: the `NSManagedObjectID` for the object
- returns: the `DynamicObject` instance if the object exists in the transaction, or `nil` if not found.
*/
public func fetchExisting<D: DynamicObject>(_ objectID: NSManagedObjectID) -> D? {
return self.context.fetchExisting(objectID)
}
/**
Fetches the `DynamicObject` instances in the transaction's context from references created from a transaction or from a different managed object context.
- parameter objects: an array of `DynamicObject`s created/fetched outside the transaction
- returns: the `DynamicObject` array for objects that exists in the transaction
*/
public func fetchExisting<D: DynamicObject, S: Sequence>(_ objects: S) -> [D] where S.Iterator.Element == D {
return self.context.fetchExisting(objects)
}
/**
Fetches the `DynamicObject` instances in the transaction's context from a list of `NSManagedObjectID`.
- parameter objectIDs: the `NSManagedObjectID` array for the objects
- returns: the `DynamicObject` array for objects that exists in the transaction
*/
public func fetchExisting<D: DynamicObject, S: Sequence>(_ objectIDs: S) -> [D] where S.Iterator.Element == NSManagedObjectID {
return self.context.fetchExisting(objectIDs)
}
/**
Fetches the first `DynamicObject` instance that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the first `DynamicObject` instance that satisfies the specified `FetchClause`s
*/
public func fetchOne<D>(_ from: From<D>, _ fetchClauses: FetchClause...) -> D? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.fetchOne(from, fetchClauses)
}
/**
Fetches the first `DynamicObject` instance that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the first `DynamicObject` instance that satisfies the specified `FetchClause`s
*/
public func fetchOne<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) -> D? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.fetchOne(from, fetchClauses)
}
/**
Fetches the first `DynamicObject` instance that satisfies the specified `FetchChainableBuilderType` built from a chain of clauses.
```
let youngestTeen = transaction.fetchOne(
From<MyPersonEntity>()
.where(\.age > 18)
.orderBy(.ascending(\.age))
)
```
- parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses
- returns: the first `DynamicObject` instance that satisfies the specified `FetchChainableBuilderType`
*/
public func fetchOne<B: FetchChainableBuilderType>(_ clauseChain: B) -> B.ObjectType? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.fetchOne(clauseChain)
}
/**
Fetches all `DynamicObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: all `DynamicObject` instances that satisfy the specified `FetchClause`s
*/
public func fetchAll<D>(_ from: From<D>, _ fetchClauses: FetchClause...) -> [D]? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.fetchAll(from, fetchClauses)
}
/**
Fetches all `DynamicObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: all `DynamicObject` instances that satisfy the specified `FetchClause`s
*/
public func fetchAll<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) -> [D]? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.fetchAll(from, fetchClauses)
}
/**
Fetches all `DynamicObject` instances that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses.
```
let people = transaction.fetchAll(
From<MyPersonEntity>()
.where(\.age > 18)
.orderBy(.ascending(\.age))
)
```
- parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses
- returns: all `DynamicObject` instances that satisfy the specified `FetchChainableBuilderType`
*/
public func fetchAll<B: FetchChainableBuilderType>(_ clauseChain: B) -> [B.ObjectType]? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.fetchAll(clauseChain)
}
/**
Fetches the number of `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the number `DynamicObject`s that satisfy the specified `FetchClause`s
*/
public func fetchCount<D>(_ from: From<D>, _ fetchClauses: FetchClause...) -> Int? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.fetchCount(from, fetchClauses)
}
/**
Fetches the number of `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the number `DynamicObject`s that satisfy the specified `FetchClause`s
*/
public func fetchCount<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) -> Int? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.fetchCount(from, fetchClauses)
}
/**
Fetches the number of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses.
```
let numberOfAdults = transaction.fetchCount(
From<MyPersonEntity>()
.where(\.age > 18)
.orderBy(.ascending(\.age))
)
```
- parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses
- returns: the number `DynamicObject`s that satisfy the specified `FetchChainableBuilderType`
*/
public func fetchCount<B: FetchChainableBuilderType>(_ clauseChain: B) -> Int? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.fetchCount(clauseChain)
}
/**
Fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s
*/
public func fetchObjectID<D>(_ from: From<D>, _ fetchClauses: FetchClause...) -> NSManagedObjectID? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.fetchObjectID(from, fetchClauses)
}
/**
Fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s
*/
public func fetchObjectID<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.fetchObjectID(from, fetchClauses)
}
/**
Fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchChainableBuilderType` built from a chain of clauses.
```
let youngestTeenID = transaction.fetchObjectID(
From<MyPersonEntity>()
.where(\.age > 18)
.orderBy(.ascending(\.age))
)
```
- parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses
- returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchChainableBuilderType`
*/
public func fetchObjectID<B: FetchChainableBuilderType>(_ clauseChain: B) -> NSManagedObjectID? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.fetchObjectID(clauseChain)
}
/**
Fetches the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s
*/
public func fetchObjectIDs<D>(_ from: From<D>, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.fetchObjectIDs(from, fetchClauses)
}
/**
Fetches the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s
*/
public func fetchObjectIDs<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.fetchObjectIDs(from, fetchClauses)
}
/**
Fetches the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses.
```
let idsOfAdults = transaction.fetchObjectIDs(
From<MyPersonEntity>()
.where(\.age > 18)
.orderBy(.ascending(\.age))
)
```
- parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses
- returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchChainableBuilderType`
*/
public func fetchObjectIDs<B: FetchChainableBuilderType>(_ clauseChain: B) -> [NSManagedObjectID]? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.fetchObjectIDs(clauseChain)
}
// MARK: QueryableSource
/**
Queries aggregate values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
- parameter from: a `From` clause indicating the entity type
- parameter selectClause: a `Select<U>` clause indicating the properties to fetch, and with the generic type indicating the return type.
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
- returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/
public func queryValue<D, U: QueryableAttributeType>(_ from: From<D>, _ selectClause: Select<D, U>, _ queryClauses: QueryClause...) -> U? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to query from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.queryValue(from, selectClause, queryClauses)
}
/**
Queries aggregate values or aggregates as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
- parameter from: a `From` clause indicating the entity type
- parameter selectClause: a `Select<U>` clause indicating the properties to fetch, and with the generic type indicating the return type.
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
- returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/
public func queryValue<D, U: QueryableAttributeType>(_ from: From<D>, _ selectClause: Select<D, U>, _ queryClauses: [QueryClause]) -> U? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to query from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.queryValue(from, selectClause, queryClauses)
}
/**
Queries a property value or aggregate as specified by the `QueryChainableBuilderType` built from a chain of clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
```
let averageAdultAge = transaction.queryValue(
From<MyPersonEntity>()
.select(Int.self, .average(\.age))
.where(\.age > 18)
)
```
- parameter clauseChain: a `QueryChainableBuilderType` indicating the property/aggregate to fetch and the series of queries for the request.
- returns: the result of the the query as specified by the `QueryChainableBuilderType`
*/
public func queryValue<B: QueryChainableBuilderType>(_ clauseChain: B) -> B.ResultType? where B.ResultType: QueryableAttributeType {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to query from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.queryValue(clauseChain.from, clauseChain.select, clauseChain.queryClauses)
}
/**
Queries a dictionary of attribute values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
- parameter from: a `From` clause indicating the entity type
- parameter selectClause: a `Select<U>` clause indicating the properties to fetch, and with the generic type indicating the return type.
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
- returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/
public func queryAttributes<D>(_ from: From<D>, _ selectClause: Select<D, NSDictionary>, _ queryClauses: QueryClause...) -> [[String: Any]]? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to query from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.queryAttributes(from, selectClause, queryClauses)
}
/**
Queries a dictionary of attribute values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
- parameter from: a `From` clause indicating the entity type
- parameter selectClause: a `Select<U>` clause indicating the properties to fetch, and with the generic type indicating the return type.
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
- returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/
public func queryAttributes<D>(_ from: From<D>, _ selectClause: Select<D, NSDictionary>, _ queryClauses: [QueryClause]) -> [[String: Any]]? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to query from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.queryAttributes(from, selectClause, queryClauses)
}
/**
Queries a dictionary of attribute values or as specified by the `QueryChainableBuilderType` built from a chain of clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
```
let results = dataStack.queryAttributes(
From<MyPersonEntity>()
.select(
NSDictionary.self,
.attribute(\.age, as: "age"),
.count(\.age, as: "numberOfPeople")
)
.groupBy(\.age)
)
for dictionary in results! {
let age = dictionary["age"] as! Int
let count = dictionary["numberOfPeople"] as! Int
print("There are \(count) people who are \(age) years old."
}
```
- parameter clauseChain: a `QueryChainableBuilderType` indicating the properties to fetch and the series of queries for the request.
- returns: the result of the the query as specified by the `QueryChainableBuilderType`
*/
public func queryAttributes<B: QueryChainableBuilderType>(_ clauseChain: B) -> [[String: Any]]? where B.ResultType == NSDictionary {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to query from a \(cs_typeName(self)) outside its designated queue."
)
return self.context.queryAttributes(clauseChain.from, clauseChain.select, clauseChain.queryClauses)
}
// MARK: FetchableSource, QueryableSource
/**
The internal `NSManagedObjectContext` managed by this instance. Using this context directly should typically be avoided, and is provided by CoreStore only for extremely specialized cases.
*/
public func unsafeContext() -> NSManagedObjectContext {
return self.context
}
// MARK: Obsoleted
@available(*, obsoleted: 3.1, renamed: "unsafeContext()")
public func internalContext() -> NSManagedObjectContext {
fatalError()
}
}
@@ -45,32 +45,36 @@ public /*abstract*/ class BaseDataTransaction {
} }
/** /**
Creates a new `NSManagedObject` with the specified entity type. Creates a new `NSManagedObject` or `CoreStoreObject` with the specified entity type.
- parameter into: the `Into` clause indicating the destination `NSManagedObject` entity type and the destination configuration - parameter into: the `Into` clause indicating the destination `NSManagedObject` or `CoreStoreObject` entity type and the destination configuration
- returns: a new `NSManagedObject` instance of the specified entity type. - returns: a new `NSManagedObject` or `CoreStoreObject` instance of the specified entity type.
*/ */
public func create<T: NSManagedObject>(_ into: Into<T>) -> T { public func create<D>(_ into: Into<D>) -> D {
let entityClass = (into.entityClass as! T.Type) let entityClass = into.entityClass
CoreStore.assert( CoreStore.assert(
self.isRunningInAllowedQueue(), self.isRunningInAllowedQueue(),
"Attempted to create an entity of type \(cs_typeName(entityClass)) outside its designated queue." "Attempted to create an entity of type \(cs_typeName(entityClass)) outside its designated queue."
) )
let context = self.context let context = self.context
let dataStack = context.parentStack!
let entityIdentifier = EntityIdentifier(entityClass)
if into.inferStoreIfPossible { if into.inferStoreIfPossible {
switch context.parentStack!.persistentStoreForEntityClass( switch dataStack.persistentStore(
entityClass, for: entityIdentifier,
configuration: nil, configuration: nil,
inferStoreIfPossible: true inferStoreIfPossible: true
) { ) {
case (let persistentStore?, _): case (let persistentStore?, _):
let object = entityClass.createInContext(context) return entityClass.cs_forceCreate(
context.assign(object, to: persistentStore) entityDescription: dataStack.entityDescription(for: entityIdentifier)!,
return object into: context,
assignTo: persistentStore
)
case (nil, true): case (nil, true):
CoreStore.abort("Attempted to create an entity of type \(cs_typeName(entityClass)) with ambiguous destination persistent store, but the configuration name was not specified.") CoreStore.abort("Attempted to create an entity of type \(cs_typeName(entityClass)) with ambiguous destination persistent store, but the configuration name was not specified.")
@@ -81,17 +85,19 @@ public /*abstract*/ class BaseDataTransaction {
} }
else { else {
switch context.parentStack!.persistentStoreForEntityClass( switch dataStack.persistentStore(
entityClass, for: entityIdentifier,
configuration: into.configuration configuration: into.configuration
?? type(of: into).defaultConfigurationName, ?? DataStack.defaultConfigurationName,
inferStoreIfPossible: false inferStoreIfPossible: false
) { ) {
case (let persistentStore?, _): case (let persistentStore?, _):
let object = entityClass.createInContext(context) return entityClass.cs_forceCreate(
context.assign(object, to: persistentStore) entityDescription: dataStack.entityDescription(for: entityIdentifier)!,
return object into: context,
assignTo: persistentStore
)
case (nil, true): case (nil, true):
CoreStore.abort("Attempted to create an entity of type \(cs_typeName(entityClass)) with ambiguous destination persistent store, but the configuration name was not specified.") CoreStore.abort("Attempted to create an entity of type \(cs_typeName(entityClass)) with ambiguous destination persistent store, but the configuration name was not specified.")
@@ -110,12 +116,12 @@ public /*abstract*/ class BaseDataTransaction {
} }
/** /**
Returns an editable proxy of a specified `NSManagedObject`. Returns an editable proxy of a specified `NSManagedObject` or `CoreStoreObject`.
- parameter object: the `NSManagedObject` type to be edited - parameter object: the `NSManagedObject` or `CoreStoreObject` type to be edited
- returns: an editable proxy for the specified `NSManagedObject`. - returns: an editable proxy for the specified `NSManagedObject` or `CoreStoreObject`.
*/ */
public func edit<T: NSManagedObject>(_ object: T?) -> T? { public func edit<D: DynamicObject>(_ object: D?) -> D? {
CoreStore.assert( CoreStore.assert(
self.isRunningInAllowedQueue(), self.isRunningInAllowedQueue(),
@@ -133,9 +139,9 @@ public /*abstract*/ class BaseDataTransaction {
- parameter into: an `Into` clause specifying the entity type - parameter into: an `Into` clause specifying the entity type
- parameter objectID: the `NSManagedObjectID` for the object to be edited - parameter objectID: the `NSManagedObjectID` for the object to be edited
- returns: an editable proxy for the specified `NSManagedObject`. - returns: an editable proxy for the specified `NSManagedObject` or `CoreStoreObject`.
*/ */
public func edit<T: NSManagedObject>(_ into: Into<T>, _ objectID: NSManagedObjectID) -> T? { public func edit<D>(_ into: Into<D>, _ objectID: NSManagedObjectID) -> D? {
CoreStore.assert( CoreStore.assert(
self.isRunningInAllowedQueue(), self.isRunningInAllowedQueue(),
@@ -143,56 +149,54 @@ public /*abstract*/ class BaseDataTransaction {
) )
CoreStore.assert( CoreStore.assert(
into.inferStoreIfPossible into.inferStoreIfPossible
|| (into.configuration ?? Into.defaultConfigurationName) == objectID.persistentStore?.configurationName, || (into.configuration ?? DataStack.defaultConfigurationName) == objectID.persistentStore?.configurationName,
"Attempted to update an entity of type \(cs_typeName(into.entityClass)) but the specified persistent store do not match the `NSManagedObjectID`." "Attempted to update an entity of type \(cs_typeName(into.entityClass)) but the specified persistent store do not match the `NSManagedObjectID`."
) )
return self.fetchExisting(objectID) as? T return self.fetchExisting(objectID)
} }
/** /**
Deletes a specified `NSManagedObject`. Deletes a specified `NSManagedObject` or `CoreStoreObject`.
- parameter object: the `NSManagedObject` to be deleted - parameter object: the `NSManagedObject` or `CoreStoreObject` to be deleted
*/ */
public func delete(_ object: NSManagedObject?) { public func delete<D: DynamicObject>(_ object: D?) {
CoreStore.assert( CoreStore.assert(
self.isRunningInAllowedQueue(), self.isRunningInAllowedQueue(),
"Attempted to delete an entity outside its designated queue." "Attempted to delete an entity outside its designated queue."
) )
guard let object = object else { let context = self.context
object
return .flatMap(context.fetchExisting)
} .flatMap({ context.delete($0.cs_toRaw()) })
self.context.fetchExisting(object)?.deleteFromContext()
} }
/** /**
Deletes the specified `NSManagedObject`s. Deletes the specified `NSManagedObject`s or `CoreStoreObject`s.
- parameter object1: the `NSManagedObject` to be deleted - parameter object1: the `NSManagedObject` or `CoreStoreObject` to be deleted
- parameter object2: another `NSManagedObject` to be deleted - parameter object2: another `NSManagedObject` or `CoreStoreObject` to be deleted
- parameter objects: other `NSManagedObject`s to be deleted - parameter objects: other `NSManagedObject`s or `CoreStoreObject`s to be deleted
*/ */
public func delete(_ object1: NSManagedObject?, _ object2: NSManagedObject?, _ objects: NSManagedObject?...) { public func delete<D: DynamicObject>(_ object1: D?, _ object2: D?, _ objects: D?...) {
self.delete(([object1, object2] + objects).flatMap { $0 }) self.delete(([object1, object2] + objects).flatMap { $0 })
} }
/** /**
Deletes the specified `NSManagedObject`s. Deletes the specified `NSManagedObject`s or `CoreStoreObject`s.
- parameter objects: the `NSManagedObject`s to be deleted - parameter objects: the `NSManagedObject`s or `CoreStoreObject`s to be deleted
*/ */
public func delete<S: Sequence>(_ objects: S) where S.Iterator.Element: NSManagedObject { public func delete<S: Sequence>(_ objects: S) where S.Iterator.Element: DynamicObject {
CoreStore.assert( CoreStore.assert(
self.isRunningInAllowedQueue(), self.isRunningInAllowedQueue(),
"Attempted to delete entities outside their designated queue." "Attempted to delete entities outside their designated queue."
) )
let context = self.context let context = self.context
objects.forEach { context.fetchExisting($0)?.deleteFromContext() } objects.forEach { context.fetchExisting($0).flatMap({ context.delete($0.cs_toRaw()) }) }
} }
/** /**
@@ -204,7 +208,6 @@ public /*abstract*/ class BaseDataTransaction {
self.isRunningInAllowedQueue(), self.isRunningInAllowedQueue(),
"Attempted to refresh entities outside their designated queue." "Attempted to refresh entities outside their designated queue."
) )
self.context.refreshAndMergeAllObjects() self.context.refreshAndMergeAllObjects()
} }
@@ -212,11 +215,12 @@ public /*abstract*/ class BaseDataTransaction {
// MARK: Inspecting Pending Objects // MARK: Inspecting Pending Objects
/** /**
Returns all pending `NSManagedObject`s that were inserted to the transaction. This method should not be called after the `commit()` method was called. Returns all pending `DynamicObject`s of the specified type that were inserted to the transaction. This method should not be called after the `commit()` method was called.
- returns: a `Set` of pending `NSManagedObject`s that were inserted to the transaction. - parameter entity: the `DynamicObject` subclass to filter
- returns: a `Set` of pending `DynamicObject`s of the specified type that were inserted to the transaction.
*/ */
public func insertedObjects() -> Set<NSManagedObject> { public func insertedObjects<D: DynamicObject>(_ entity: D.Type) -> Set<D> {
CoreStore.assert( CoreStore.assert(
self.transactionQueue.cs_isCurrentExecutionContext(), self.transactionQueue.cs_isCurrentExecutionContext(),
@@ -226,28 +230,7 @@ public /*abstract*/ class BaseDataTransaction {
!self.isCommitted, !self.isCommitted,
"Attempted to access inserted objects from an already committed \(cs_typeName(self))." "Attempted to access inserted objects from an already committed \(cs_typeName(self))."
) )
return Set(self.context.insertedObjects.flatMap({ entity.cs_matches(object: $0) ? entity.cs_fromRaw(object: $0) : nil }))
return self.context.insertedObjects
}
/**
Returns all pending `NSManagedObject`s of the specified type that were inserted to the transaction. This method should not be called after the `commit()` method was called.
- parameter entity: the `NSManagedObject` subclass to filter
- returns: a `Set` of pending `NSManagedObject`s of the specified type that were inserted to the transaction.
*/
public func insertedObjects<T: NSManagedObject>(_ entity: T.Type) -> Set<T> {
CoreStore.assert(
self.transactionQueue.cs_isCurrentExecutionContext(),
"Attempted to access inserted objects from a \(cs_typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access inserted objects from an already committed \(cs_typeName(self))."
)
return Set(self.context.insertedObjects.flatMap { $0 as? T })
} }
/** /**
@@ -265,17 +248,16 @@ public /*abstract*/ class BaseDataTransaction {
!self.isCommitted, !self.isCommitted,
"Attempted to access inserted objects IDs from an already committed \(cs_typeName(self))." "Attempted to access inserted objects IDs from an already committed \(cs_typeName(self))."
) )
return Set(self.context.insertedObjects.map { $0.objectID }) return Set(self.context.insertedObjects.map { $0.objectID })
} }
/** /**
Returns all pending `NSManagedObjectID`s of the specified type that were inserted to the transaction. This method should not be called after the `commit()` method was called. Returns all pending `NSManagedObjectID`s of the specified type that were inserted to the transaction. This method should not be called after the `commit()` method was called.
- parameter entity: the `NSManagedObject` subclass to filter - parameter entity: the `DynamicObject` subclass to filter
- returns: a `Set` of pending `NSManagedObjectID`s of the specified type that were inserted to the transaction. - returns: a `Set` of pending `NSManagedObjectID`s of the specified type that were inserted to the transaction.
*/ */
public func insertedObjectIDs<T: NSManagedObject>(_ entity: T.Type) -> Set<NSManagedObjectID> { public func insertedObjectIDs<D: DynamicObject>(_ entity: D.Type) -> Set<NSManagedObjectID> {
CoreStore.assert( CoreStore.assert(
self.transactionQueue.cs_isCurrentExecutionContext(), self.transactionQueue.cs_isCurrentExecutionContext(),
@@ -285,16 +267,16 @@ public /*abstract*/ class BaseDataTransaction {
!self.isCommitted, !self.isCommitted,
"Attempted to access inserted objects IDs from an already committed \(cs_typeName(self))." "Attempted to access inserted objects IDs from an already committed \(cs_typeName(self))."
) )
return Set(self.context.insertedObjects.flatMap({ entity.cs_matches(object: $0) ? $0.objectID : nil }))
return Set(self.context.insertedObjects.filter { $0.isKind(of: entity) }.map { $0.objectID })
} }
/** /**
Returns all pending `NSManagedObject`s that were updated in the transaction. This method should not be called after the `commit()` method was called. Returns all pending `DynamicObject`s of the specified type that were updated in the transaction. This method should not be called after the `commit()` method was called.
- returns: a `Set` of pending `NSManagedObject`s that were updated to the transaction. - parameter entity: the `DynamicObject` subclass to filter
- returns: a `Set` of pending `DynamicObject`s of the specified type that were updated in the transaction.
*/ */
public func updatedObjects() -> Set<NSManagedObject> { public func updatedObjects<D: DynamicObject>(_ entity: D.Type) -> Set<D> {
CoreStore.assert( CoreStore.assert(
self.transactionQueue.cs_isCurrentExecutionContext(), self.transactionQueue.cs_isCurrentExecutionContext(),
@@ -304,28 +286,7 @@ public /*abstract*/ class BaseDataTransaction {
!self.isCommitted, !self.isCommitted,
"Attempted to access updated objects from an already committed \(cs_typeName(self))." "Attempted to access updated objects from an already committed \(cs_typeName(self))."
) )
return Set(self.context.updatedObjects.flatMap({ entity.cs_matches(object: $0) ? entity.cs_fromRaw(object: $0) : nil }))
return self.context.updatedObjects
}
/**
Returns all pending `NSManagedObject`s of the specified type that were updated in the transaction. This method should not be called after the `commit()` method was called.
- parameter entity: the `NSManagedObject` subclass to filter
- returns: a `Set` of pending `NSManagedObject`s of the specified type that were updated in the transaction.
*/
public func updatedObjects<T: NSManagedObject>(_ entity: T.Type) -> Set<T> {
CoreStore.assert(
self.transactionQueue.cs_isCurrentExecutionContext(),
"Attempted to access updated objects from a \(cs_typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access updated objects from an already committed \(cs_typeName(self))."
)
return Set(self.context.updatedObjects.filter { $0.isKind(of: entity) }.map { $0 as! T })
} }
/** /**
@@ -343,17 +304,16 @@ public /*abstract*/ class BaseDataTransaction {
!self.isCommitted, !self.isCommitted,
"Attempted to access updated object IDs from an already committed \(cs_typeName(self))." "Attempted to access updated object IDs from an already committed \(cs_typeName(self))."
) )
return Set(self.context.updatedObjects.map { $0.objectID }) return Set(self.context.updatedObjects.map { $0.objectID })
} }
/** /**
Returns all pending `NSManagedObjectID`s of the specified type that were updated in the transaction. This method should not be called after the `commit()` method was called. Returns all pending `NSManagedObjectID`s of the specified type that were updated in the transaction. This method should not be called after the `commit()` method was called.
- parameter entity: the `NSManagedObject` subclass to filter - parameter entity: the `DynamicObject` subclass to filter
- returns: a `Set` of pending `NSManagedObjectID`s of the specified type that were updated in the transaction. - returns: a `Set` of pending `NSManagedObjectID`s of the specified type that were updated in the transaction.
*/ */
public func updatedObjectIDs<T: NSManagedObject>(_ entity: T.Type) -> Set<NSManagedObjectID> { public func updatedObjectIDs<D: DynamicObject>(_ entity: D.Type) -> Set<NSManagedObjectID> {
CoreStore.assert( CoreStore.assert(
self.transactionQueue.cs_isCurrentExecutionContext(), self.transactionQueue.cs_isCurrentExecutionContext(),
@@ -363,16 +323,16 @@ public /*abstract*/ class BaseDataTransaction {
!self.isCommitted, !self.isCommitted,
"Attempted to access updated object IDs from an already committed \(cs_typeName(self))." "Attempted to access updated object IDs from an already committed \(cs_typeName(self))."
) )
return Set(self.context.updatedObjects.flatMap({ entity.cs_matches(object: $0) ? $0.objectID : nil }))
return Set(self.context.updatedObjects.filter { $0.isKind(of: entity) }.map { $0.objectID })
} }
/** /**
Returns all pending `NSManagedObject`s that were deleted from the transaction. This method should not be called after the `commit()` method was called. Returns all pending `DynamicObject`s of the specified type that were deleted from the transaction. This method should not be called after the `commit()` method was called.
- returns: a `Set` of pending `NSManagedObject`s that were deleted from the transaction. - parameter entity: the `DynamicObject` subclass to filter
- returns: a `Set` of pending `DynamicObject`s of the specified type that were deleted from the transaction.
*/ */
public func deletedObjects() -> Set<NSManagedObject> { public func deletedObjects<D: DynamicObject>(_ entity: D.Type) -> Set<D> {
CoreStore.assert( CoreStore.assert(
self.transactionQueue.cs_isCurrentExecutionContext(), self.transactionQueue.cs_isCurrentExecutionContext(),
@@ -382,34 +342,13 @@ public /*abstract*/ class BaseDataTransaction {
!self.isCommitted, !self.isCommitted,
"Attempted to access deleted objects from an already committed \(cs_typeName(self))." "Attempted to access deleted objects from an already committed \(cs_typeName(self))."
) )
return Set(self.context.deletedObjects.flatMap({ entity.cs_matches(object: $0) ? entity.cs_fromRaw(object: $0) : nil }))
return self.context.deletedObjects
}
/**
Returns all pending `NSManagedObject`s of the specified type that were deleted from the transaction. This method should not be called after the `commit()` method was called.
- parameter entity: the `NSManagedObject` subclass to filter
- returns: a `Set` of pending `NSManagedObject`s of the specified type that were deleted from the transaction.
*/
public func deletedObjects<T: NSManagedObject>(_ entity: T.Type) -> Set<T> {
CoreStore.assert(
self.transactionQueue.cs_isCurrentExecutionContext(),
"Attempted to access deleted objects from a \(cs_typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access deleted objects from an already committed \(cs_typeName(self))."
)
return Set(self.context.deletedObjects.filter { $0.isKind(of: entity) }.map { $0 as! T })
} }
/** /**
Returns all pending `NSManagedObjectID`s of the specified type that were deleted from the transaction. This method should not be called after the `commit()` method was called. Returns all pending `NSManagedObjectID`s of the specified type that were deleted from the transaction. This method should not be called after the `commit()` method was called.
- parameter entity: the `NSManagedObject` subclass to filter - parameter entity: the `DynamicObject` subclass to filter
- returns: a `Set` of pending `NSManagedObjectID`s of the specified type that were deleted from the transaction. - returns: a `Set` of pending `NSManagedObjectID`s of the specified type that were deleted from the transaction.
*/ */
public func deletedObjectIDs() -> Set<NSManagedObjectID> { public func deletedObjectIDs() -> Set<NSManagedObjectID> {
@@ -422,17 +361,16 @@ public /*abstract*/ class BaseDataTransaction {
!self.isCommitted, !self.isCommitted,
"Attempted to access deleted object IDs from an already committed \(cs_typeName(self))." "Attempted to access deleted object IDs from an already committed \(cs_typeName(self))."
) )
return Set(self.context.deletedObjects.map { $0.objectID }) return Set(self.context.deletedObjects.map { $0.objectID })
} }
/** /**
Returns all pending `NSManagedObjectID`s of the specified type that were deleted from the transaction. This method should not be called after the `commit()` method was called. Returns all pending `NSManagedObjectID`s of the specified type that were deleted from the transaction. This method should not be called after the `commit()` method was called.
- parameter entity: the `NSManagedObject` subclass to filter - parameter entity: the `DynamicObject` subclass to filter
- returns: a `Set` of pending `NSManagedObjectID`s of the specified type that were deleted from the transaction. - returns: a `Set` of pending `NSManagedObjectID`s of the specified type that were deleted from the transaction.
*/ */
public func deletedObjectIDs<T: NSManagedObject>(_ entity: T.Type) -> Set<NSManagedObjectID> { public func deletedObjectIDs<D: DynamicObject>(_ entity: D.Type) -> Set<NSManagedObjectID> {
CoreStore.assert( CoreStore.assert(
self.transactionQueue.cs_isCurrentExecutionContext(), self.transactionQueue.cs_isCurrentExecutionContext(),
@@ -442,20 +380,34 @@ public /*abstract*/ class BaseDataTransaction {
!self.isCommitted, !self.isCommitted,
"Attempted to access deleted object IDs from an already committed \(cs_typeName(self))." "Attempted to access deleted object IDs from an already committed \(cs_typeName(self))."
) )
return Set(self.context.deletedObjects.flatMap({ entity.cs_matches(object: $0) ? $0.objectID : nil }))
return Set(self.context.deletedObjects.filter { $0.isKind(of: entity) }.map { $0.objectID })
} }
// MARK: 3rd Party Utilities
/**
Allow external libraries to store custom data in the transaction. App code should rarely have a need for this.
```
enum Static {
static var myDataKey: Void?
}
transaction.userInfo[&Static.myDataKey] = myObject
```
- Important: Do not use this method to store thread-sensitive data.
*/
public let userInfo = UserInfo()
// MARK: Internal // MARK: Internal
internal let context: NSManagedObjectContext internal let context: NSManagedObjectContext
internal let transactionQueue: DispatchQueue internal let transactionQueue: DispatchQueue
internal let childTransactionQueue = DispatchQueue.serial("com.corestore.datastack.childtransactionqueue") internal let childTransactionQueue = DispatchQueue.serial("com.corestore.datastack.childTransactionQueue")
internal let supportsUndo: Bool internal let supportsUndo: Bool
internal let bypassesQueueing: Bool internal let bypassesQueueing: Bool
internal var isCommitted = false internal var isCommitted = false
internal var result: SaveResult? internal var result: (hasChanges: Bool, error: CoreStoreError?)?
internal init(mainContext: NSManagedObjectContext, queue: DispatchQueue, supportsUndo: Bool, bypassesQueueing: Bool) { internal init(mainContext: NSManagedObjectContext, queue: DispatchQueue, supportsUndo: Bool, bypassesQueueing: Bool) {
@@ -485,4 +437,54 @@ public /*abstract*/ class BaseDataTransaction {
return self.bypassesQueueing || self.transactionQueue.cs_isCurrentExecutionContext() return self.bypassesQueueing || self.transactionQueue.cs_isCurrentExecutionContext()
} }
deinit {
self.context.reset()
}
// MARK: Deprecated
@available(*, deprecated, message: "Use insertedObjects(_:) and pass the specific entity type")
public func insertedObjects() -> Set<NSManagedObject> {
CoreStore.assert(
self.transactionQueue.cs_isCurrentExecutionContext(),
"Attempted to access inserted objects from a \(cs_typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access inserted objects from an already committed \(cs_typeName(self))."
)
return self.context.insertedObjects
}
@available(*, deprecated, message: "Use updatedObjects(_:) and pass the specific entity type")
public func updatedObjects() -> Set<NSManagedObject> {
CoreStore.assert(
self.transactionQueue.cs_isCurrentExecutionContext(),
"Attempted to access updated objects from a \(cs_typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access updated objects from an already committed \(cs_typeName(self))."
)
return self.context.updatedObjects
}
@available(*, deprecated, message: "Use deletedObjects(_:) and pass the specific entity type")
public func deletedObjects() -> Set<NSManagedObject> {
CoreStore.assert(
self.transactionQueue.cs_isCurrentExecutionContext(),
"Attempted to access deleted objects from a \(cs_typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access deleted objects from an already committed \(cs_typeName(self))."
)
return self.context.deletedObjects
}
} }
@@ -35,37 +35,34 @@ import CoreData
- SeeAlso: `AsynchronousDataTransaction` - SeeAlso: `AsynchronousDataTransaction`
*/ */
@objc @objc
public final class CSAsynchronousDataTransaction: CSBaseDataTransaction { public final class CSAsynchronousDataTransaction: CSBaseDataTransaction, CoreStoreObjectiveCType {
/** /**
Saves the transaction changes. This method should not be used after the `-commitWithCompletion:` method was already called once. Saves the transaction changes. This method should not be used after the `-commitWithCompletion:` method was already called once.
- parameter completion: the block executed after the save completes. Success or failure is reported by the `CSSaveResult` argument of the block. - parameter success: the block executed if the save succeeds.
- parameter failure: the block executed if the save fails. A `CSError` is reported as the argument of the block.
*/ */
@objc @objc
public func commitWithCompletion(_ completion: ((_ result: CSSaveResult) -> Void)?) { public func commitWithSuccess(_ success: (() -> Void)?, failure: ((CSError) -> Void)?) {
self.bridgeToSwift.commit { (result) in CoreStore.assert(
self.bridgeToSwift.transactionQueue.cs_isCurrentExecutionContext(),
"Attempted to commit a \(cs_typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.bridgeToSwift.isCommitted,
"Attempted to commit a \(cs_typeName(self)) more than once."
)
self.bridgeToSwift.autoCommit { (_, error) in
completion?(result.bridgeToObjectiveC) if let error = error {
}
failure?(error.bridgeToObjectiveC)
} }
else {
/** success?()
Begins a child transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made. This method should not be used after the `-commitWithCompletion:` method was already called once.
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
- returns: a `CSSaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
*/
@objc
@discardableResult
public func beginSynchronous(_ closure: @escaping (_ transaction: CSSynchronousDataTransaction) -> Void) -> CSSaveResult? {
return bridge {
self.bridgeToSwift.beginSynchronous { (transaction) in
closure(transaction.bridgeToObjectiveC)
} }
} }
} }
@@ -145,9 +142,9 @@ public final class CSAsynchronousDataTransaction: CSBaseDataTransaction {
public typealias SwiftType = AsynchronousDataTransaction public typealias SwiftType = AsynchronousDataTransaction
public override var bridgeToSwift: AsynchronousDataTransaction { public var bridgeToSwift: AsynchronousDataTransaction {
return super.bridgeToSwift as! AsynchronousDataTransaction return super.swiftTransaction as! AsynchronousDataTransaction
} }
public required init(_ swiftValue: AsynchronousDataTransaction) { public required init(_ swiftValue: AsynchronousDataTransaction) {
@@ -155,9 +152,44 @@ public final class CSAsynchronousDataTransaction: CSBaseDataTransaction {
super.init(swiftValue as BaseDataTransaction) super.init(swiftValue as BaseDataTransaction)
} }
public required init(_ swiftValue: BaseDataTransaction) { public required override init(_ swiftValue: BaseDataTransaction) {
super.init(swiftValue as! AsynchronousDataTransaction) super.init(swiftValue)
}
// MARK: Deprecated
@available(*, deprecated, message: "Use the new -[CSAsynchronousDataTransaction commitWithSuccess:failure:] method.")
@objc
public func commitWithCompletion(_ completion: ((_ result: CSSaveResult) -> Void)?) {
CoreStore.assert(
self.bridgeToSwift.transactionQueue.cs_isCurrentExecutionContext(),
"Attempted to commit a \(cs_typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.bridgeToSwift.isCommitted,
"Attempted to commit a \(cs_typeName(self)) more than once."
)
self.bridgeToSwift.commit { (result) in
completion?(result.bridgeToObjectiveC)
}
}
@available(*, deprecated, message: "Secondary tasks spawned from CSAsynchronousDataTransactions and CSSynchronousDataTransactions are no longer supported. ")
@objc
@discardableResult
public func beginSynchronous(_ closure: @escaping (_ transaction: CSSynchronousDataTransaction) -> Void) -> CSSaveResult? {
return bridge {
self.bridgeToSwift.beginSynchronous { (transaction) in
closure(transaction.bridgeToObjectiveC)
}
}
} }
} }
@@ -40,7 +40,7 @@ public extension CSBaseDataTransaction {
@objc @objc
public func fetchExistingObject(_ object: NSManagedObject) -> Any? { public func fetchExistingObject(_ object: NSManagedObject) -> Any? {
return self.bridgeToSwift.context.fetchExisting(object) return self.swiftTransaction.context.fetchExisting(object) as NSManagedObject?
} }
/** /**
@@ -52,7 +52,7 @@ public extension CSBaseDataTransaction {
@objc @objc
public func fetchExistingObjectWithID(_ objectID: NSManagedObjectID) -> Any? { public func fetchExistingObjectWithID(_ objectID: NSManagedObjectID) -> Any? {
return self.bridgeToSwift.context.fetchExisting(objectID) return self.swiftTransaction.context.fetchExisting(objectID) as NSManagedObject?
} }
/** /**
@@ -64,7 +64,7 @@ public extension CSBaseDataTransaction {
@objc @objc
public func fetchExistingObjects(_ objects: [NSManagedObject]) -> [Any] { public func fetchExistingObjects(_ objects: [NSManagedObject]) -> [Any] {
return self.bridgeToSwift.context.fetchExisting(objects) return self.swiftTransaction.context.fetchExisting(objects) as [NSManagedObject]
} }
/** /**
@@ -76,7 +76,7 @@ public extension CSBaseDataTransaction {
@objc @objc
public func fetchExistingObjectsWithIDs(_ objectIDs: [NSManagedObjectID]) -> [Any] { public func fetchExistingObjectsWithIDs(_ objectIDs: [NSManagedObjectID]) -> [Any] {
return self.bridgeToSwift.context.fetchExisting(objectIDs) return self.swiftTransaction.context.fetchExisting(objectIDs) as [NSManagedObject]
} }
/** /**
@@ -90,10 +90,10 @@ public extension CSBaseDataTransaction {
public func fetchOneFrom(_ from: CSFrom, fetchClauses: [CSFetchClause]) -> Any? { public func fetchOneFrom(_ from: CSFrom, fetchClauses: [CSFetchClause]) -> Any? {
CoreStore.assert( CoreStore.assert(
self.bridgeToSwift.isRunningInAllowedQueue(), self.swiftTransaction.isRunningInAllowedQueue(),
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue." "Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
) )
return self.bridgeToSwift.context.fetchOne(from, fetchClauses) return self.swiftTransaction.context.fetchOne(from, fetchClauses)
} }
/** /**
@@ -107,10 +107,10 @@ public extension CSBaseDataTransaction {
public func fetchAllFrom(_ from: CSFrom, fetchClauses: [CSFetchClause]) -> [Any]? { public func fetchAllFrom(_ from: CSFrom, fetchClauses: [CSFetchClause]) -> [Any]? {
CoreStore.assert( CoreStore.assert(
self.bridgeToSwift.isRunningInAllowedQueue(), self.swiftTransaction.isRunningInAllowedQueue(),
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue." "Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
) )
return self.bridgeToSwift.context.fetchAll(from, fetchClauses) return self.swiftTransaction.context.fetchAll(from, fetchClauses)
} }
/** /**
@@ -124,10 +124,10 @@ public extension CSBaseDataTransaction {
public func fetchCountFrom(_ from: CSFrom, fetchClauses: [CSFetchClause]) -> NSNumber? { public func fetchCountFrom(_ from: CSFrom, fetchClauses: [CSFetchClause]) -> NSNumber? {
CoreStore.assert( CoreStore.assert(
self.bridgeToSwift.isRunningInAllowedQueue(), self.swiftTransaction.isRunningInAllowedQueue(),
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue." "Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
) )
return self.bridgeToSwift.context return self.swiftTransaction.context
.fetchCount(from, fetchClauses) .fetchCount(from, fetchClauses)
.flatMap { NSNumber(value: $0) } .flatMap { NSNumber(value: $0) }
} }
@@ -143,10 +143,10 @@ public extension CSBaseDataTransaction {
public func fetchObjectIDFrom(_ from: CSFrom, fetchClauses: [CSFetchClause]) -> NSManagedObjectID? { public func fetchObjectIDFrom(_ from: CSFrom, fetchClauses: [CSFetchClause]) -> NSManagedObjectID? {
CoreStore.assert( CoreStore.assert(
self.bridgeToSwift.isRunningInAllowedQueue(), self.swiftTransaction.isRunningInAllowedQueue(),
"Attempted to fetch from a \(cs_typeName(self)) outside its designated queue." "Attempted to fetch from a \(cs_typeName(self)) outside its designated queue."
) )
return self.bridgeToSwift.context.fetchObjectID(from, fetchClauses) return self.swiftTransaction.context.fetchObjectID(from, fetchClauses)
} }
/** /**
@@ -163,10 +163,10 @@ public extension CSBaseDataTransaction {
public func queryValueFrom(_ from: CSFrom, selectClause: CSSelect, queryClauses: [CSQueryClause]) -> Any? { public func queryValueFrom(_ from: CSFrom, selectClause: CSSelect, queryClauses: [CSQueryClause]) -> Any? {
CoreStore.assert( CoreStore.assert(
self.bridgeToSwift.isRunningInAllowedQueue(), self.swiftTransaction.isRunningInAllowedQueue(),
"Attempted to query from a \(cs_typeName(self)) outside its designated queue." "Attempted to query from a \(cs_typeName(self)) outside its designated queue."
) )
return self.bridgeToSwift.context.queryValue(from, selectClause, queryClauses) return self.swiftTransaction.context.queryValue(from, selectClause, queryClauses)
} }
/** /**
@@ -183,9 +183,9 @@ public extension CSBaseDataTransaction {
public func queryAttributesFrom(_ from: CSFrom, selectClause: CSSelect, queryClauses: [CSQueryClause]) -> [[String: Any]]? { public func queryAttributesFrom(_ from: CSFrom, selectClause: CSSelect, queryClauses: [CSQueryClause]) -> [[String: Any]]? {
CoreStore.assert( CoreStore.assert(
self.bridgeToSwift.isRunningInAllowedQueue(), self.swiftTransaction.isRunningInAllowedQueue(),
"Attempted to query from a \(cs_typeName(self)) outside its designated queue." "Attempted to query from a \(cs_typeName(self)) outside its designated queue."
) )
return self.bridgeToSwift.context.queryAttributes(from, selectClause, queryClauses) return self.swiftTransaction.context.queryAttributes(from, selectClause, queryClauses)
} }
} }
@@ -35,7 +35,7 @@ import CoreData
- SeeAlso: `BaseDataTransaction` - SeeAlso: `BaseDataTransaction`
*/ */
@objc @objc
public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType { public class CSBaseDataTransaction: NSObject {
// MARK: Object management // MARK: Object management
@@ -45,7 +45,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
@objc @objc
public var hasChanges: Bool { public var hasChanges: Bool {
return self.bridgeToSwift.hasChanges return self.swiftTransaction.hasChanges
} }
/** /**
@@ -57,7 +57,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
@objc @objc
public func createInto(_ into: CSInto) -> Any { public func createInto(_ into: CSInto) -> Any {
return self.bridgeToSwift.create(into.bridgeToSwift) return self.swiftTransaction.create(into.bridgeToSwift)
} }
/** /**
@@ -69,7 +69,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
@objc @objc
public func editObject(_ object: NSManagedObject?) -> Any? { public func editObject(_ object: NSManagedObject?) -> Any? {
return self.bridgeToSwift.edit(object) return self.swiftTransaction.edit(object)
} }
/** /**
@@ -82,7 +82,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
@objc @objc
public func editInto(_ into: CSInto, objectID: NSManagedObjectID) -> Any? { public func editInto(_ into: CSInto, objectID: NSManagedObjectID) -> Any? {
return self.bridgeToSwift.edit(into.bridgeToSwift, objectID) return self.swiftTransaction.edit(into.bridgeToSwift, objectID)
} }
/** /**
@@ -93,7 +93,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
@objc @objc
public func deleteObject(_ object: NSManagedObject?) { public func deleteObject(_ object: NSManagedObject?) {
self.bridgeToSwift.delete(object) self.swiftTransaction.delete(object)
} }
/** /**
@@ -104,7 +104,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
@objc @objc
public func deleteObjects(_ objects: [NSManagedObject]) { public func deleteObjects(_ objects: [NSManagedObject]) {
self.bridgeToSwift.delete(objects) self.swiftTransaction.delete(objects)
} }
/** /**
@@ -113,23 +113,12 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
@objc @objc
public func refreshAndMergeAllObjects() { public func refreshAndMergeAllObjects() {
self.bridgeToSwift.refreshAndMergeAllObjects() self.swiftTransaction.refreshAndMergeAllObjects()
} }
// MARK: Inspecting Pending Objects // MARK: Inspecting Pending Objects
/**
Returns all pending `NSManagedObject`s that were inserted to the transaction. This method should not be called after the `-commit*:` method was called.
- returns: an `NSSet` of pending `NSManagedObject`s that were inserted to the transaction.
*/
@objc
public func insertedObjects() -> Set<NSManagedObject> {
return self.bridgeToSwift.insertedObjects()
}
/** /**
Returns all pending `NSManagedObject`s of the specified type that were inserted to the transaction. This method should not be called after the `-commit*:` method was called. Returns all pending `NSManagedObject`s of the specified type that were inserted to the transaction. This method should not be called after the `-commit*:` method was called.
@@ -139,7 +128,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
@objc @objc
public func insertedObjectsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObject> { public func insertedObjectsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObject> {
return self.bridgeToSwift.insertedObjects(entity) return self.swiftTransaction.insertedObjects(entity)
} }
/** /**
@@ -150,7 +139,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
@objc @objc
public func insertedObjectIDs() -> Set<NSManagedObjectID> { public func insertedObjectIDs() -> Set<NSManagedObjectID> {
return self.bridgeToSwift.insertedObjectIDs() return self.swiftTransaction.insertedObjectIDs()
} }
/** /**
@@ -162,18 +151,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
@objc @objc
public func insertedObjectIDsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObjectID> { public func insertedObjectIDsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObjectID> {
return self.bridgeToSwift.insertedObjectIDs(entity) return self.swiftTransaction.insertedObjectIDs(entity)
}
/**
Returns all pending `NSManagedObject`s that were updated in the transaction. This method should not be called after the `-commit*:` method was called.
- returns: an `NSSet` of pending `NSManagedObject`s that were updated to the transaction.
*/
@objc
public func updatedObjects() -> Set<NSManagedObject> {
return self.bridgeToSwift.updatedObjects()
} }
/** /**
@@ -185,7 +163,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
@objc @objc
public func updatedObjectsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObject> { public func updatedObjectsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObject> {
return self.bridgeToSwift.updatedObjects(entity) return self.swiftTransaction.updatedObjects(entity)
} }
/** /**
@@ -196,7 +174,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
@objc @objc
public func updatedObjectIDs() -> Set<NSManagedObjectID> { public func updatedObjectIDs() -> Set<NSManagedObjectID> {
return self.bridgeToSwift.updatedObjectIDs() return self.swiftTransaction.updatedObjectIDs()
} }
/** /**
@@ -208,18 +186,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
@objc @objc
public func updatedObjectIDsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObjectID> { public func updatedObjectIDsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObjectID> {
return self.bridgeToSwift.updatedObjectIDs(entity) return self.swiftTransaction.updatedObjectIDs(entity)
}
/**
Returns all pending `NSManagedObject`s that were deleted from the transaction. This method should not be called after the `-commit*:` method was called.
- returns: an `NSSet` of pending `NSManagedObject`s that were deleted from the transaction.
*/
@objc
public func deletedObjects() -> Set<NSManagedObject> {
return self.bridgeToSwift.deletedObjects()
} }
/** /**
@@ -231,19 +198,18 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
@objc @objc
public func deletedObjectsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObject> { public func deletedObjectsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObject> {
return self.bridgeToSwift.deletedObjects(entity) return self.swiftTransaction.deletedObjects(entity)
} }
/** /**
Returns all pending `NSManagedObjectID`s of the specified type that were deleted from the transaction. This method should not be called after the `-commit*:` method was called. Returns all pending `NSManagedObjectID`s of the specified type that were deleted from the transaction. This method should not be called after the `-commit*:` method was called.
- parameter entity: the `NSManagedObject` subclass to filter
- returns: an `NSSet` of pending `NSManagedObjectID`s of the specified type that were deleted from the transaction. - returns: an `NSSet` of pending `NSManagedObjectID`s of the specified type that were deleted from the transaction.
*/ */
@objc @objc
public func deletedObjectIDs() -> Set<NSManagedObjectID> { public func deletedObjectIDs() -> Set<NSManagedObjectID> {
return self.bridgeToSwift.deletedObjectIDs() return self.swiftTransaction.deletedObjectIDs()
} }
/** /**
@@ -255,7 +221,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
@objc @objc
public func deletedObjectIDsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObjectID> { public func deletedObjectIDsOfType(_ entity: NSManagedObject.Type) -> Set<NSManagedObjectID> {
return self.bridgeToSwift.deletedObjectIDs(entity) return self.swiftTransaction.deletedObjectIDs(entity)
} }
@@ -263,7 +229,7 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
public override var hash: Int { public override var hash: Int {
return ObjectIdentifier(self.bridgeToSwift).hashValue return ObjectIdentifier(self.swiftTransaction).hashValue
} }
public override func isEqual(_ object: Any?) -> Bool { public override func isEqual(_ object: Any?) -> Bool {
@@ -272,25 +238,41 @@ public class CSBaseDataTransaction: NSObject, CoreStoreObjectiveCType {
return false return false
} }
return self.bridgeToSwift === object.bridgeToSwift return self.swiftTransaction === object.swiftTransaction
} }
// MARK: CoreStoreObjectiveCType // MARK: Internal
public required init(_ swiftValue: BaseDataTransaction) { internal let swiftTransaction: BaseDataTransaction
internal init(_ swiftValue: BaseDataTransaction) {
self.swiftTransaction = swiftValue self.swiftTransaction = swiftValue
super.init() super.init()
} }
public var bridgeToSwift: BaseDataTransaction {
return self.swiftTransaction // MARK: Deprecated
@available(*, deprecated, message: "Use -[insertedObjectsOfType:] and pass the specific entity class")
@objc
public func insertedObjects() -> Set<NSManagedObject> {
return self.swiftTransaction.insertedObjects()
} }
@available(*, deprecated, message: "Use -[updatedObjectsOfType:] and pass the specific entity class")
@objc
public func updatedObjects() -> Set<NSManagedObject> {
// MARK: Private return self.swiftTransaction.updatedObjects()
}
private let swiftTransaction: BaseDataTransaction @available(*, deprecated, message: "Use -[deletedObjectsOfType:] and pass the specific entity class")
@objc
public func deletedObjects() -> Set<NSManagedObject> {
return self.swiftTransaction.deletedObjects()
}
} }
@@ -27,10 +27,9 @@ import Foundation
import CoreData import CoreData
#if os(iOS) || os(watchOS) || os(tvOS)
// MARK: - CSCoreStore // MARK: - CSCoreStore
@available(OSX 10.12, *)
public extension CSCoreStore { public extension CSCoreStore {
/** /**
@@ -112,5 +111,3 @@ public extension CSCoreStore {
) )
} }
} }
#endif
@@ -44,21 +44,9 @@ public extension CSCoreStore {
Returns the entity name-to-class type mapping from the `defaultStack`'s model. Returns the entity name-to-class type mapping from the `defaultStack`'s model.
*/ */
@objc @objc
public static var entityClassesByName: [String: NSManagedObject.Type] { public static func entityTypesByNameForType(_ type: NSManagedObject.Type) -> [EntityName: NSManagedObject.Type] {
return CoreStore.entityTypesByName return CoreStore.entityTypesByName(for: type)
}
/**
Returns the entity class for the given entity name from the `defaultStack`'s model.
- parameter name: the entity name
- returns: the `NSManagedObject` class for the given entity name, or `nil` if not found
*/
@objc
public static func entityClassWithName(_ name: String) -> NSManagedObject.Type? {
return CoreStore.entityTypesByName[name]
} }
/** /**
@@ -137,4 +125,21 @@ public extension CSCoreStore {
return self.defaultStack.addSQLiteStorageAndWait(storage, error: error) return self.defaultStack.addSQLiteStorageAndWait(storage, error: error)
} }
// MARK: Deprecated
@available(*, deprecated, message: "Use the new +entityTypesByNameForType: method passing `[NSManagedObject class]` as argument.")
@objc
public static var entityClassesByName: [EntityName: NSManagedObject.Type] {
return CoreStore.entityTypesByName
}
@available(*, deprecated, message: "Use the new +entityTypesByNameForType: method passing `[NSManagedObject class]` as argument.")
@objc
public static func entityClassWithName(_ name: EntityName) -> NSManagedObject.Type? {
return CoreStore.entityTypesByName[name]
}
} }
@@ -38,10 +38,7 @@ public extension CSCoreStore {
@objc @objc
public static func beginAsynchronous(_ closure: @escaping (_ transaction: CSAsynchronousDataTransaction) -> Void) { public static func beginAsynchronous(_ closure: @escaping (_ transaction: CSAsynchronousDataTransaction) -> Void) {
return CoreStore.beginAsynchronous { (transaction) in self.defaultStack.beginAsynchronous(closure)
closure(transaction.bridgeToObjectiveC)
}
} }
/** /**
@@ -51,16 +48,9 @@ public extension CSCoreStore {
- returns: a `CSSaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously - returns: a `CSSaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
*/ */
@objc @objc
@discardableResult public static func beginSynchronous(_ closure: @escaping (_ transaction: CSSynchronousDataTransaction) -> Void, error: NSErrorPointer) -> Bool {
public static func beginSynchronous(_ closure: @escaping (_ transaction: CSSynchronousDataTransaction) -> Void) -> CSSaveResult? {
return bridge { return self.defaultStack.beginSynchronous(closure, error: error)
CoreStore.beginSynchronous { (transaction) in
closure(transaction.bridgeToObjectiveC)
}
}
} }
/** /**
@@ -101,4 +91,15 @@ public extension CSCoreStore {
CoreStore.refreshAndMergeAllObjects() CoreStore.refreshAndMergeAllObjects()
} }
// MARK: Deprecated
@available(*, deprecated, message: "Use the new +[CSCoreStore beginSynchronous:error:] API that reports failure using an error instance.")
@objc
@discardableResult
public static func beginSynchronous(_ closure: @escaping (_ transaction: CSSynchronousDataTransaction) -> Void) -> CSSaveResult? {
return self.defaultStack.beginSynchronous(closure)
}
} }
@@ -27,10 +27,9 @@ import Foundation
import CoreData import CoreData
#if os(iOS) || os(watchOS) || os(tvOS)
// MARK: - CSDataStack // MARK: - CSDataStack
@available(OSX 10.12, *)
public extension CSDataStack { public extension CSDataStack {
/** /**
@@ -42,10 +41,7 @@ public extension CSDataStack {
@objc @objc
public func monitorObject(_ object: NSManagedObject) -> CSObjectMonitor { public func monitorObject(_ object: NSManagedObject) -> CSObjectMonitor {
return bridge { return self.bridgeToSwift.monitorObject(object).bridgeToObjectiveC
self.bridgeToSwift.monitorObject(object)
}
} }
/** /**
@@ -66,9 +62,7 @@ public extension CSDataStack {
fetchClauses.contains { $0 is CSOrderBy }, fetchClauses.contains { $0 is CSOrderBy },
"A CSListMonitor requires a CSOrderBy clause." "A CSListMonitor requires a CSOrderBy clause."
) )
return bridge { return ListMonitor(
ListMonitor(
dataStack: self.bridgeToSwift, dataStack: self.bridgeToSwift,
from: from.bridgeToSwift, from: from.bridgeToSwift,
sectionBy: nil, sectionBy: nil,
@@ -76,8 +70,7 @@ public extension CSDataStack {
fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest as! NSFetchRequest<NSFetchRequestResult>) } fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest as! NSFetchRequest<NSFetchRequestResult>) }
} }
) ).bridgeToObjectiveC
}
} }
/** /**
@@ -132,9 +125,7 @@ public extension CSDataStack {
fetchClauses.contains { $0 is CSOrderBy }, fetchClauses.contains { $0 is CSOrderBy },
"A CSListMonitor requires an CSOrderBy clause." "A CSListMonitor requires an CSOrderBy clause."
) )
return bridge { return ListMonitor(
ListMonitor(
dataStack: self.bridgeToSwift, dataStack: self.bridgeToSwift,
from: from.bridgeToSwift, from: from.bridgeToSwift,
sectionBy: sectionBy.bridgeToSwift, sectionBy: sectionBy.bridgeToSwift,
@@ -142,8 +133,7 @@ public extension CSDataStack {
fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest as! NSFetchRequest<NSFetchRequestResult>) } fetchClauses.forEach { $0.applyToFetchRequest(fetchRequest as! NSFetchRequest<NSFetchRequestResult>) }
} }
) ).bridgeToObjectiveC
}
} }
/** /**
@@ -179,5 +169,3 @@ public extension CSDataStack {
) )
} }
} }
#endif
@@ -40,7 +40,7 @@ public extension CSDataStack {
@objc @objc
public func fetchExistingObject(_ object: NSManagedObject) -> Any? { public func fetchExistingObject(_ object: NSManagedObject) -> Any? {
return self.bridgeToSwift.mainContext.fetchExisting(object) return self.bridgeToSwift.mainContext.fetchExisting(object) as NSManagedObject?
} }
/** /**
@@ -52,7 +52,7 @@ public extension CSDataStack {
@objc @objc
public func fetchExistingObjectWithID(_ objectID: NSManagedObjectID) -> Any? { public func fetchExistingObjectWithID(_ objectID: NSManagedObjectID) -> Any? {
return self.bridgeToSwift.mainContext.fetchExisting(objectID) return self.bridgeToSwift.mainContext.fetchExisting(objectID) as NSManagedObject?
} }
/** /**
@@ -64,7 +64,7 @@ public extension CSDataStack {
@objc @objc
public func fetchExistingObjects(_ objects: [NSManagedObject]) -> [Any] { public func fetchExistingObjects(_ objects: [NSManagedObject]) -> [Any] {
return self.bridgeToSwift.mainContext.fetchExisting(objects) return self.bridgeToSwift.mainContext.fetchExisting(objects) as [NSManagedObject]
} }
/** /**
@@ -76,7 +76,7 @@ public extension CSDataStack {
@objc @objc
public func fetchExistingObjectsWithIDs(_ objectIDs: [NSManagedObjectID]) -> [Any] { public func fetchExistingObjectsWithIDs(_ objectIDs: [NSManagedObjectID]) -> [Any] {
return self.bridgeToSwift.mainContext.fetchExisting(objectIDs) return self.bridgeToSwift.mainContext.fetchExisting(objectIDs) as [NSManagedObject]
} }
/** /**
@@ -38,27 +38,57 @@ public extension CSDataStack {
@objc @objc
public func beginAsynchronous(_ closure: @escaping (_ transaction: CSAsynchronousDataTransaction) -> Void) { public func beginAsynchronous(_ closure: @escaping (_ transaction: CSAsynchronousDataTransaction) -> Void) {
return self.bridgeToSwift.beginAsynchronous { (transaction) in self.bridgeToSwift.perform(
asynchronous: { (transaction) in
closure(transaction.bridgeToObjectiveC) let csTransaction = transaction.bridgeToObjectiveC
closure(csTransaction)
if !transaction.isCommitted && transaction.hasChanges {
CoreStore.log(
.warning,
message: "The closure for the \(cs_typeName(csTransaction)) completed without being committed. All changes made within the transaction were discarded."
)
} }
try transaction.cancel()
},
completion: { _ in }
)
} }
/** /**
Begins a transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made. Begins a transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made.
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`. - parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
- returns: a `CSSaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously - parameter error: the `CSError` pointer that indicates the reason in case of an failure
- returns: `YES` if the commit succeeded, `NO` if the commit failed. If `NO`, the `error` argument will hold error information.
*/ */
@objc @objc
@discardableResult public func beginSynchronous(_ closure: @escaping (_ transaction: CSSynchronousDataTransaction) -> Void, error: NSErrorPointer) -> Bool {
public func beginSynchronous(_ closure: @escaping (_ transaction: CSSynchronousDataTransaction) -> Void) -> CSSaveResult? {
return bridge { return bridge(error) {
self.bridgeToSwift.beginSynchronous { (transaction) in do {
closure(transaction.bridgeToObjectiveC) try self.bridgeToSwift.perform(
synchronous: { (transaction) in
let csTransaction = transaction.bridgeToObjectiveC
closure(csTransaction)
if !transaction.isCommitted && transaction.hasChanges {
CoreStore.log(
.warning,
message: "The closure for the \(cs_typeName(csTransaction)) completed without being committed. All changes made within the transaction were discarded."
)
}
try transaction.cancel()
}
)
}
catch CoreStoreError.userCancelled {
return
} }
} }
} }
@@ -101,4 +131,21 @@ public extension CSDataStack {
self.bridgeToSwift.refreshAndMergeAllObjects() self.bridgeToSwift.refreshAndMergeAllObjects()
} }
// MARK: Deprecated
@available(*, deprecated, message: "Use the new -[CSDataStack beginSynchronous:error:] API that reports failure using an error instance.")
@objc
@discardableResult
public func beginSynchronous(_ closure: @escaping (_ transaction: CSSynchronousDataTransaction) -> Void) -> CSSaveResult? {
return bridge {
self.bridgeToSwift.beginSynchronous { (transaction) in
closure(transaction.bridgeToObjectiveC)
}
}
}
} }
@@ -49,75 +49,22 @@ public final class CSDataStack: NSObject, CoreStoreObjectiveCType {
/** /**
Initializes a `CSDataStack` from the model with the specified `modelName` in the specified `bundle`. Initializes a `CSDataStack` from the model with the specified `modelName` in the specified `bundle`.
- parameter modelName: the name of the (.xcdatamodeld) model file. If not specified, the application name (CFBundleName) will be used if it exists, or "CoreData" if it the bundle name was not set. - parameter xcodeModelName: the name of the (.xcdatamodeld) model file. If not specified, the application name (CFBundleName) will be used if it exists, or "CoreData" if it the bundle name was not set.
- parameter bundle: an optional bundle to load models from. If not specified, the main bundle will be used. - parameter bundle: an optional bundle to load models from. If not specified, the main bundle will be used.
- parameter versionChain: the version strings that indicate the sequence of model versions to be used as the order for progressive migrations. If not specified, will default to a non-migrating data stack. - parameter versionChain: the version strings that indicate the sequence of model versions to be used as the order for progressive migrations. If not specified, will default to a non-migrating data stack.
*/ */
@objc @objc
public convenience init(modelName: String?, bundle: Bundle?, versionChain: [String]?) { public convenience init(xcodeModelName: XcodeDataModelFileName?, bundle: Bundle?, versionChain: [String]?) {
self.init( self.init(
DataStack( DataStack(
modelName: modelName ?? DataStack.applicationName, xcodeModelName: xcodeModelName ?? DataStack.applicationName,
bundle: bundle ?? Bundle.main, bundle: bundle ?? Bundle.main,
migrationChain: versionChain.flatMap { MigrationChain($0) } ?? nil migrationChain: versionChain.flatMap { MigrationChain($0) } ?? nil
) )
) )
} }
/**
Initializes a `CSDataStack` from the model with the specified `modelName` in the specified `bundle`.
- parameter modelName: the name of the (.xcdatamodeld) model file. If not specified, the application name (CFBundleName) will be used if it exists, or "CoreData" if it the bundle name was not set.
- parameter bundle: an optional bundle to load models from. If not specified, the main bundle will be used.
- parameter versionTree: the version strings that indicate the sequence of model versions to be used as the order for progressive migrations. If not specified, will default to a non-migrating data stack.
*/
@objc
public convenience init(modelName: String?, bundle: Bundle?, versionTree: [String: String]?) {
self.init(
DataStack(
modelName: modelName ?? DataStack.applicationName,
bundle: bundle ?? Bundle.main,
migrationChain: versionTree.flatMap { MigrationChain($0) } ?? nil
)
)
}
/**
Initializes a `DataStack` from an `NSManagedObjectModel`.
- parameter model: the `NSManagedObjectModel` for the stack
- parameter versionChain: the `MigrationChain` that indicates the sequence of model versions to be used as the order for progressive migrations. If not specified, will default to a non-migrating data stack.
*/
@objc
public convenience init(model: NSManagedObjectModel, versionChain: [String]?) {
self.init(
DataStack(
model: model,
migrationChain: versionChain.flatMap { MigrationChain($0) } ?? nil
)
)
}
/**
Initializes a `DataStack` from an `NSManagedObjectModel`.
- parameter model: the `NSManagedObjectModel` for the stack
- parameter versionTree: the `MigrationChain` that indicates the sequence of model versions to be used as the order for progressive migrations. If not specified, will default to a non-migrating data stack.
*/
@objc
public convenience init(model: NSManagedObjectModel, versionTree: [String]?) {
self.init(
DataStack(
model: model,
migrationChain: versionTree.flatMap { MigrationChain($0) } ?? nil
)
)
}
/** /**
Returns the stack's model version. The version string is the same as the name of the version-specific .xcdatamodeld file. Returns the stack's model version. The version string is the same as the name of the version-specific .xcdatamodeld file.
*/ */
@@ -128,23 +75,12 @@ public final class CSDataStack: NSObject, CoreStoreObjectiveCType {
} }
/** /**
Returns the entity name-to-class type mapping from the stack's model. Returns the entity name-to-class type mapping from the `CSDataStack`'s model.
*/ */
@objc @objc
public var entityClassesByName: [String: NSManagedObject.Type] { public func entityTypesByNameForType(_ type: NSManagedObject.Type) -> [EntityName: NSManagedObject.Type] {
return self.bridgeToSwift.entityTypesByName return self.bridgeToSwift.entityTypesByName(for: type)
}
/**
Returns the entity class for the given entity name from the stack's's model.
- parameter name: the entity name
- returns: the `NSManagedObject` class for the given entity name, or `nil` if not found
*/
@objc
public func entityClassWithName(_ name: String) -> NSManagedObject.Type? {
return self.bridgeToSwift.entityTypesByName[name]
} }
/** /**
@@ -170,7 +106,7 @@ public final class CSDataStack: NSObject, CoreStoreObjectiveCType {
return bridge(error) { return bridge(error) {
try self.bridgeToSwift.addStorageAndWait(InMemoryStore.self) try self.bridgeToSwift.addStorageAndWait(InMemoryStore())
} }
} }
@@ -188,7 +124,7 @@ public final class CSDataStack: NSObject, CoreStoreObjectiveCType {
return bridge(error) { return bridge(error) {
try self.bridgeToSwift.addStorageAndWait(SQLiteStore.self) try self.bridgeToSwift.addStorageAndWait(SQLiteStore())
} }
} }
@@ -268,6 +204,60 @@ public final class CSDataStack: NSObject, CoreStoreObjectiveCType {
self.bridgeToSwift = swiftValue self.bridgeToSwift = swiftValue
super.init() super.init()
} }
// MARK: Deprecated
@available(*, deprecated, message: "Use the -[initWithXcodeModelName:bundle:versionChain:] initializer.")
@objc
public convenience init(modelName: XcodeDataModelFileName?, bundle: Bundle?, versionChain: [String]?) {
self.init(
DataStack(
xcodeModelName: modelName ?? DataStack.applicationName,
bundle: bundle ?? Bundle.main,
migrationChain: versionChain.flatMap { MigrationChain($0) } ?? nil
)
)
}
@available(*, deprecated, message: "Use the -[initWithModelName:bundle:versionChain:] initializer.")
@objc
public convenience init(model: NSManagedObjectModel, versionChain: [String]?) {
self.init(
DataStack(
model: model,
migrationChain: versionChain.flatMap { MigrationChain($0) } ?? nil
)
)
}
@available(*, deprecated, message: "Use the -[initWithModelName:bundle:versionTree:] initializer.")
@objc
public convenience init(model: NSManagedObjectModel, versionTree: [String]?) {
self.init(
DataStack(
model: model,
migrationChain: versionTree.flatMap { MigrationChain($0) } ?? nil
)
)
}
@available(*, deprecated, message: "Use the new -entityTypesByNameForType: method passing `[NSManagedObject class]` as argument.")
@objc
public var entityClassesByName: [EntityName: NSManagedObject.Type] {
return self.bridgeToSwift.entityTypesByName
}
@available(*, deprecated, message: "Use the new -entityTypesByNameForType: method passing `[NSManagedObject class]` as argument.")
@objc
public func entityClassWithName(_ name: EntityName) -> NSManagedObject.Type? {
return self.bridgeToSwift.entityTypesByName[name]
}
} }
+51
View File
@@ -0,0 +1,51 @@
//
// CSDynamicSchema.swift
// CoreStore
//
// Copyright © 2017 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import CoreData
import Foundation
// MARK: - CSDynamicSchema
/**
The `CSDynamicSchema` serves as the Objective-C bridging type for `DynamicSchema`.
- SeeAlso: `DynamicSchema`
*/
@objc
public protocol CSDynamicSchema {
/**
The version string for this model schema.
*/
@objc
var modelVersion: ModelVersion { get }
/**
Do not call this directly. The `NSManagedObjectModel` for this schema may be created lazily and using this method directly may affect the integrity of the model.
*/
@objc
func rawModel() -> NSManagedObjectModel
}
@@ -137,6 +137,16 @@ public enum CSErrorCode: Int {
An internal SDK call failed with the specified "NSError" userInfo key. An internal SDK call failed with the specified "NSError" userInfo key.
*/ */
case internalError case internalError
/**
The transaction was terminated by a user-thrown error with the specified "Error" userInfo key.
*/
case userError
/**
The transaction was cancelled by the user.
*/
case userCancelled
} }
@@ -209,12 +219,23 @@ extension CoreStoreError: CoreStoreSwiftType, _ObjectiveCBridgeableError {
self = .progressiveMigrationRequired(localStoreURL: localStoreURL) self = .progressiveMigrationRequired(localStoreURL: localStoreURL)
case .internalError: case .internalError:
guard case let NSError as NSError = info["NSError"] else { guard case let nsError as NSError = info["NSError"] else {
self = .unknown self = .unknown
return return
} }
self = .internalError(NSError: NSError) self = .internalError(NSError: nsError)
case .userError:
guard case let error as Error = info["Error"] else {
self = .unknown
return
}
self = .userError(error: error)
case .userCancelled:
self = .userCancelled
} }
} }
} }
@@ -35,7 +35,7 @@ import CoreData
- SeeAlso: `From` - SeeAlso: `From`
*/ */
@objc @objc
public final class CSFrom: NSObject, CoreStoreObjectiveCType { public final class CSFrom: NSObject {
/** /**
The associated `NSManagedObject` entity class The associated `NSManagedObject` entity class
@@ -71,7 +71,7 @@ public final class CSFrom: NSObject, CoreStoreObjectiveCType {
- parameter entityClass: the `NSManagedObject` class type to be created - parameter entityClass: the `NSManagedObject` class type to be created
*/ */
@objc @objc
public convenience init(entityClass: AnyClass) { public convenience init(entityClass: NSManagedObject.Type) {
self.init(From(entityClass)) self.init(From(entityClass))
} }
@@ -82,10 +82,11 @@ public final class CSFrom: NSObject, CoreStoreObjectiveCType {
MyPersonEntity *people = [transaction fetchAllFrom: MyPersonEntity *people = [transaction fetchAllFrom:
CSFromClass([MyPersonEntity class], @"Config1")]; CSFromClass([MyPersonEntity class], @"Config1")];
``` ```
- parameter entityClass: the associated `NSManagedObject` entity class
- parameter configuration: the `NSPersistentStore` configuration name to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `[NSNull null]` to use the default configuration. - parameter configuration: the `NSPersistentStore` configuration name to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `[NSNull null]` to use the default configuration.
*/ */
@objc @objc
public convenience init(entityClass: AnyClass, configuration: Any) { public convenience init(entityClass: NSManagedObject.Type, configuration: Any) {
switch configuration { switch configuration {
@@ -107,13 +108,13 @@ public final class CSFrom: NSObject, CoreStoreObjectiveCType {
CSFromClass([MyPersonEntity class], CSFromClass([MyPersonEntity class],
@[[NSNull null], @"Config1"])]; @[[NSNull null], @"Config1"])];
``` ```
- parameter entity: the associated `NSManagedObject` entity class - parameter entityClass: the associated `NSManagedObject` entity class
- parameter configurations: an array of the `NSPersistentStore` configuration names to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `[NSNull null]` to use the default configuration. - parameter configurations: an array of the `NSPersistentStore` configuration names to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `[NSNull null]` to use the default configuration.
*/ */
@objc @objc
public convenience init(entityClass: AnyClass, configurations: [Any]) { public convenience init(entityClass: NSManagedObject.Type, configurations: [Any]) {
var arguments = [String?]() var arguments = [ModelConfiguration]()
for configuration in configurations { for configuration in configurations {
switch configuration { switch configuration {
@@ -144,7 +145,7 @@ public final class CSFrom: NSObject, CoreStoreObjectiveCType {
public let bridgeToSwift: From<NSManagedObject> public let bridgeToSwift: From<NSManagedObject>
public init<T: NSManagedObject>(_ swiftValue: From<T>) { public init<D: NSManagedObject>(_ swiftValue: From<D>) {
self.bridgeToSwift = swiftValue.downcast() self.bridgeToSwift = swiftValue.downcast()
super.init() super.init()
@@ -154,7 +155,7 @@ public final class CSFrom: NSObject, CoreStoreObjectiveCType {
// MARK: - From // MARK: - From
extension From: CoreStoreSwiftType { extension From where D: NSManagedObject {
// MARK: CoreStoreSwiftType // MARK: CoreStoreSwiftType
@@ -162,4 +163,16 @@ extension From: CoreStoreSwiftType {
return CSFrom(self) return CSFrom(self)
} }
// MARK: FilePrivate
fileprivate func downcast() -> From<NSManagedObject> {
return From<NSManagedObject>(
entityClass: self.entityClass,
configurations: self.configurations,
findPersistentStores: self.findPersistentStores
)
}
} }
@@ -35,13 +35,13 @@ import CoreData
- SeeAlso: `GroupBy` - SeeAlso: `GroupBy`
*/ */
@objc @objc
public final class CSGroupBy: NSObject, CSQueryClause, CoreStoreObjectiveCType { public final class CSGroupBy: NSObject, CSQueryClause {
/** /**
The list of key path strings to group results with The list of key path strings to group results with
*/ */
@objc @objc
public var keyPaths: [KeyPath] { public var keyPaths: [KeyPathString] {
return self.bridgeToSwift.keyPaths return self.bridgeToSwift.keyPaths
} }
@@ -52,7 +52,7 @@ public final class CSGroupBy: NSObject, CSQueryClause, CoreStoreObjectiveCType {
- parameter keyPath: a key path string to group results with - parameter keyPath: a key path string to group results with
*/ */
@objc @objc
public convenience init(keyPath: KeyPath) { public convenience init(keyPath: KeyPathString) {
self.init(GroupBy(keyPath)) self.init(GroupBy(keyPath))
} }
@@ -63,7 +63,7 @@ public final class CSGroupBy: NSObject, CSQueryClause, CoreStoreObjectiveCType {
- parameter keyPaths: a list of key path strings to group results with - parameter keyPaths: a list of key path strings to group results with
*/ */
@objc @objc
public convenience init(keyPaths: [KeyPath]) { public convenience init(keyPaths: [KeyPathString]) {
self.init(GroupBy(keyPaths)) self.init(GroupBy(keyPaths))
} }
@@ -102,11 +102,11 @@ public final class CSGroupBy: NSObject, CSQueryClause, CoreStoreObjectiveCType {
// MARK: CoreStoreObjectiveCType // MARK: CoreStoreObjectiveCType
public let bridgeToSwift: GroupBy public let bridgeToSwift: GroupBy<NSManagedObject>
public init(_ swiftValue: GroupBy) { public init<D: NSManagedObject>(_ swiftValue: GroupBy<D>) {
self.bridgeToSwift = swiftValue self.bridgeToSwift = swiftValue.downcast()
super.init() super.init()
} }
} }
@@ -114,7 +114,7 @@ public final class CSGroupBy: NSObject, CSQueryClause, CoreStoreObjectiveCType {
// MARK: - GroupBy // MARK: - GroupBy
extension GroupBy: CoreStoreSwiftType { extension GroupBy where D: NSManagedObject {
// MARK: CoreStoreSwiftType // MARK: CoreStoreSwiftType
@@ -122,4 +122,12 @@ extension GroupBy: CoreStoreSwiftType {
return CSGroupBy(self) return CSGroupBy(self)
} }
// MARK: FilePrivate
fileprivate func downcast() -> GroupBy<NSManagedObject> {
return GroupBy<NSManagedObject>(self.keyPaths)
}
} }
@@ -43,7 +43,7 @@ public final class CSInMemoryStore: NSObject, CSStorageInterface, CoreStoreObjec
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration. - parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration.
*/ */
@objc @objc
public convenience init(configuration: String?) { public convenience init(configuration: ModelConfiguration) {
self.init(InMemoryStore(configuration: configuration)) self.init(InMemoryStore(configuration: configuration))
} }
@@ -70,7 +70,7 @@ public final class CSInMemoryStore: NSObject, CSStorageInterface, CoreStoreObjec
The configuration name in the model file The configuration name in the model file
*/ */
@objc @objc
public var configuration: String? { public var configuration: ModelConfiguration {
return self.bridgeToSwift.configuration return self.bridgeToSwift.configuration
} }
@@ -35,13 +35,13 @@ import CoreData
- SeeAlso: `Into` - SeeAlso: `Into`
*/ */
@objc @objc
public final class CSInto: NSObject, CoreStoreObjectiveCType { public final class CSInto: NSObject {
/** /**
The associated `NSManagedObject` entity class The associated `NSManagedObject` entity class
*/ */
@objc @objc
public var entityClass: AnyClass { public var entityClass: NSManagedObject.Type {
return self.bridgeToSwift.entityClass return self.bridgeToSwift.entityClass
} }
@@ -51,7 +51,7 @@ public final class CSInto: NSObject, CoreStoreObjectiveCType {
May contain a `String` to pertain to a named configuration, or `nil` to pertain to the default configuration May contain a `String` to pertain to a named configuration, or `nil` to pertain to the default configuration
*/ */
@objc @objc
public var configuration: String? { public var configuration: ModelConfiguration {
return self.bridgeToSwift.configuration return self.bridgeToSwift.configuration
} }
@@ -65,7 +65,7 @@ public final class CSInto: NSObject, CoreStoreObjectiveCType {
- parameter entityClass: the `NSManagedObject` class type to be created - parameter entityClass: the `NSManagedObject` class type to be created
*/ */
@objc @objc
public convenience init(entityClass: AnyClass) { public convenience init(entityClass: NSManagedObject.Type) {
self.init(Into(entityClass)) self.init(Into(entityClass))
} }
@@ -80,7 +80,7 @@ public final class CSInto: NSObject, CoreStoreObjectiveCType {
- parameter configuration: the `NSPersistentStore` configuration name to associate the object to. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration. - parameter configuration: the `NSPersistentStore` configuration name to associate the object to. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
*/ */
@objc @objc
public convenience init(entityClass: AnyClass, configuration: String?) { public convenience init(entityClass: NSManagedObject.Type, configuration: ModelConfiguration) {
self.init(Into(entityClass, configuration)) self.init(Into(entityClass, configuration))
} }
@@ -112,7 +112,7 @@ public final class CSInto: NSObject, CoreStoreObjectiveCType {
public let bridgeToSwift: Into<NSManagedObject> public let bridgeToSwift: Into<NSManagedObject>
public required init<T: NSManagedObject>(_ swiftValue: Into<T>) { public required init<D: NSManagedObject>(_ swiftValue: Into<D>) {
self.bridgeToSwift = swiftValue.downcast() self.bridgeToSwift = swiftValue.downcast()
super.init() super.init()
@@ -122,7 +122,7 @@ public final class CSInto: NSObject, CoreStoreObjectiveCType {
// MARK: - Into // MARK: - Into
extension Into: CoreStoreSwiftType { extension Into where D: NSManagedObject {
// MARK: CoreStoreSwiftType // MARK: CoreStoreSwiftType
@@ -130,4 +130,16 @@ extension Into: CoreStoreSwiftType {
return CSInto(self) return CSInto(self)
} }
// MARK: FilePrivate
fileprivate func downcast() -> Into<NSManagedObject> {
return Into<NSManagedObject>(
entityClass: self.entityClass,
configuration: self.configuration,
inferStoreIfPossible: self.inferStoreIfPossible
)
}
} }
@@ -27,8 +27,6 @@ import Foundation
import CoreData import CoreData
#if os(iOS) || os(watchOS) || os(tvOS)
// MARK: - CSListMonitor // MARK: - CSListMonitor
/** /**
@@ -36,8 +34,9 @@ import CoreData
- SeeAlso: `ListMonitor` - SeeAlso: `ListMonitor`
*/ */
@available(OSX 10.12, *)
@objc @objc
public final class CSListMonitor: NSObject, CoreStoreObjectiveCType { public final class CSListMonitor: NSObject {
// MARK: Public (Accessors) // MARK: Public (Accessors)
@@ -546,7 +545,8 @@ public final class CSListMonitor: NSObject, CoreStoreObjectiveCType {
// MARK: - ListMonitor // MARK: - ListMonitor
extension ListMonitor: CoreStoreSwiftType { @available(OSX 10.12, *)
extension ListMonitor where ListMonitor.ObjectType: NSManagedObject {
// MARK: CoreStoreSwiftType // MARK: CoreStoreSwiftType
@@ -554,6 +554,16 @@ extension ListMonitor: CoreStoreSwiftType {
return CSListMonitor(self) return CSListMonitor(self)
} }
}
#endif
// MARK: FilePrivate
fileprivate func downcast() -> ListMonitor<NSManagedObject> {
func noWarnUnsafeBitCast<T, U>(_ x: T, to type: U.Type) -> U {
return unsafeBitCast(x, to: type)
}
return noWarnUnsafeBitCast(self, to: ListMonitor<NSManagedObject>.self)
}
}
@@ -27,8 +27,6 @@ import Foundation
import CoreData import CoreData
#if os(iOS) || os(watchOS) || os(tvOS)
// MARK: - CSListObserver // MARK: - CSListObserver
/** /**
@@ -42,8 +40,9 @@ import CoreData
- SeeAlso: `ListObserver` - SeeAlso: `ListObserver`
*/ */
@available(OSX 10.12, *)
@objc @objc
public protocol CSListObserver: class, AnyObject { public protocol CSListObserver: class {
/** /**
Handles processing just before a change to the observed list occurs Handles processing just before a change to the observed list occurs
@@ -92,6 +91,7 @@ public protocol CSListObserver: class, AnyObject {
- SeeAlso: `ListObjectObserver` - SeeAlso: `ListObjectObserver`
*/ */
@available(OSX 10.12, *)
@objc @objc
public protocol CSListObjectObserver: CSListObserver { public protocol CSListObjectObserver: CSListObserver {
@@ -152,6 +152,7 @@ public protocol CSListObjectObserver: CSListObserver {
- SeeAlso: `ListSectionObserver` - SeeAlso: `ListSectionObserver`
*/ */
@available(OSX 10.12, *)
@objc @objc
public protocol CSListSectionObserver: CSListObjectObserver { public protocol CSListSectionObserver: CSListObjectObserver {
@@ -175,5 +176,3 @@ public protocol CSListSectionObserver: CSListObjectObserver {
@objc @objc
optional func listMonitor(_ monitor: CSListMonitor, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int) optional func listMonitor(_ monitor: CSListMonitor, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int)
} }
#endif
@@ -27,8 +27,6 @@ import Foundation
import CoreData import CoreData
#if os(iOS) || os(watchOS) || os(tvOS)
// MARK: - CSObjectMonitor // MARK: - CSObjectMonitor
/** /**
@@ -36,8 +34,9 @@ import CoreData
- SeeAlso: `ObjectMonitor` - SeeAlso: `ObjectMonitor`
*/ */
@available(OSX 10.12, *)
@objc @objc
public final class CSObjectMonitor: NSObject, CoreStoreObjectiveCType { public final class CSObjectMonitor: NSObject {
/** /**
Returns the `NSManagedObject` instance being observed, or `nil` if the object was already deleted. Returns the `NSManagedObject` instance being observed, or `nil` if the object was already deleted.
@@ -138,7 +137,8 @@ public final class CSObjectMonitor: NSObject, CoreStoreObjectiveCType {
// MARK: - ObjectMonitor // MARK: - ObjectMonitor
extension ObjectMonitor: CoreStoreSwiftType { @available(OSX 10.12, *)
extension ObjectMonitor where ObjectMonitor.ObjectType: NSManagedObject {
// MARK: CoreStoreSwiftType // MARK: CoreStoreSwiftType
@@ -146,6 +146,16 @@ extension ObjectMonitor: CoreStoreSwiftType {
return CSObjectMonitor(self) return CSObjectMonitor(self)
} }
}
#endif
// MARK: FilePrivate
fileprivate func downcast() -> ObjectMonitor<NSManagedObject> {
func noWarnUnsafeBitCast<T, U>(_ x: T, to type: U.Type) -> U {
return unsafeBitCast(x, to: type)
}
return noWarnUnsafeBitCast(self, to: ObjectMonitor<NSManagedObject>.self)
}
}
@@ -27,8 +27,6 @@ import Foundation
import CoreData import CoreData
#if os(iOS) || os(watchOS) || os(tvOS)
// MARK: - CSObjectObserver // MARK: - CSObjectObserver
/** /**
@@ -40,8 +38,9 @@ import CoreData
- SeeAlso: `ObjectObserver` - SeeAlso: `ObjectObserver`
*/ */
@available(OSX 10.12, *)
@objc @objc
public protocol CSObjectObserver: class, AnyObject { public protocol CSObjectObserver: class {
/** /**
Handles processing just before a change to the observed `object` occurs Handles processing just before a change to the observed `object` occurs
@@ -71,5 +70,3 @@ public protocol CSObjectObserver: class, AnyObject {
@objc @objc
optional func objectMonitor(_ monitor: CSObjectMonitor, didDeleteObject object: Any) optional func objectMonitor(_ monitor: CSObjectMonitor, didDeleteObject object: Any)
} }
#endif
@@ -35,7 +35,7 @@ import CoreData
- SeeAlso: `OrderBy` - SeeAlso: `OrderBy`
*/ */
@objc @objc
public final class CSOrderBy: NSObject, CSFetchClause, CSQueryClause, CSDeleteClause, CoreStoreObjectiveCType { public final class CSOrderBy: NSObject, CSFetchClause, CSQueryClause, CSDeleteClause {
/** /**
The list of sort descriptors The list of sort descriptors
@@ -110,11 +110,11 @@ public final class CSOrderBy: NSObject, CSFetchClause, CSQueryClause, CSDeleteCl
// MARK: CoreStoreObjectiveCType // MARK: CoreStoreObjectiveCType
public let bridgeToSwift: OrderBy public let bridgeToSwift: OrderBy<NSManagedObject>
public init(_ swiftValue: OrderBy) { public init<D: NSManagedObject>(_ swiftValue: OrderBy<D>) {
self.bridgeToSwift = swiftValue self.bridgeToSwift = swiftValue.downcast()
super.init() super.init()
} }
} }
@@ -122,7 +122,7 @@ public final class CSOrderBy: NSObject, CSFetchClause, CSQueryClause, CSDeleteCl
// MARK: - OrderBy // MARK: - OrderBy
extension OrderBy: CoreStoreSwiftType { extension OrderBy where D: NSManagedObject {
// MARK: CoreStoreSwiftType // MARK: CoreStoreSwiftType
@@ -130,4 +130,12 @@ extension OrderBy: CoreStoreSwiftType {
return CSOrderBy(self) return CSOrderBy(self)
} }
// MARK: FilePrivate
fileprivate func downcast() -> OrderBy<NSManagedObject> {
return OrderBy<NSManagedObject>(self.sortDescriptors)
}
} }
@@ -40,19 +40,18 @@ public final class CSSQLiteStore: NSObject, CSLocalStorage, CoreStoreObjectiveCT
/** /**
Initializes an SQLite store interface from the given SQLite file URL. When this instance is passed to the `CSDataStack`'s `-addStorage*:` methods, a new SQLite file will be created if it does not exist. Initializes an SQLite store interface from the given SQLite file URL. When this instance is passed to the `CSDataStack`'s `-addStorage*:` methods, a new SQLite file will be created if it does not exist.
- Important: Initializing `CSSQLiteStore`s with custom migration mapping models is currently not supported. Create an `SQLiteStore` instance from Swift code and bridge the instance to Objective-C using its `SQLiteStore.bridgeToObjectiveC` property.
- parameter fileURL: the local file URL for the target SQLite persistent store. Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them. - parameter fileURL: the local file URL for the target SQLite persistent store. Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them.
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration. Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them. - parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration. Note that if you have multiple configurations, you will need to specify a different `fileURL` explicitly for each of them.
- parameter mappingModelBundles: a list of `NSBundle`s from which to search mapping models for migration.
- parameter localStorageOptions: When the `CSSQLiteStore` is passed to the `CSDataStack`'s `addStorage()` methods, tells the `CSDataStack` how to setup the persistent store. Defaults to `CSLocalStorageOptionsNone`. - parameter localStorageOptions: When the `CSSQLiteStore` is passed to the `CSDataStack`'s `addStorage()` methods, tells the `CSDataStack` how to setup the persistent store. Defaults to `CSLocalStorageOptionsNone`.
*/ */
@objc @objc
public convenience init(fileURL: URL, configuration: String?, mappingModelBundles: [Bundle]?, localStorageOptions: Int) { public convenience init(fileURL: URL, configuration: ModelConfiguration, localStorageOptions: Int) {
self.init( self.init(
SQLiteStore( SQLiteStore(
fileURL: fileURL, fileURL: fileURL,
configuration: configuration, configuration: configuration,
mappingModelBundles: mappingModelBundles ?? Bundle.allBundles,
localStorageOptions: LocalStorageOptions(rawValue: localStorageOptions) localStorageOptions: LocalStorageOptions(rawValue: localStorageOptions)
) )
) )
@@ -61,29 +60,27 @@ public final class CSSQLiteStore: NSObject, CSLocalStorage, CoreStoreObjectiveCT
/** /**
Initializes an SQLite store interface from the given SQLite file name. When this instance is passed to the `CSDataStack`'s `-addStorage*:` methods, a new SQLite file will be created if it does not exist. Initializes an SQLite store interface from the given SQLite file name. When this instance is passed to the `CSDataStack`'s `-addStorage*:` methods, a new SQLite file will be created if it does not exist.
- Warning: The default SQLite file location for the `CSLegacySQLiteStore` and `CSSQLiteStore` are different. If the app was depending on CoreStore's default directories prior to 2.0.0, make sure to use `CSLegacySQLiteStore` instead of `CSSQLiteStore`. - Important: Initializing `CSSQLiteStore`s with custom migration mapping models is currently not supported. Create an `SQLiteStore` instance from Swift code and bridge the instance to Objective-C using its `SQLiteStore.bridgeToObjectiveC` property.
- parameter fileName: the local filename for the SQLite persistent store in the "Application Support/<bundle id>" directory (or the "Caches/<bundle id>" directory on tvOS). Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them. - parameter fileName: the local filename for the SQLite persistent store in the "Application Support/<bundle id>" directory (or the "Caches/<bundle id>" directory on tvOS). Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them.
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration. Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them. - parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil`, the "Default" configuration. Note that if you have multiple configurations, you will need to specify a different `fileName` explicitly for each of them.
- parameter mappingModelBundles: a list of `NSBundle`s from which to search mapping models for migration
- parameter localStorageOptions: When the `CSSQLiteStore` is passed to the `CSDataStack`'s `addStorage()` methods, tells the `CSDataStack` how to setup the persistent store. Defaults to `[CSLocalStorageOptions none]`. - parameter localStorageOptions: When the `CSSQLiteStore` is passed to the `CSDataStack`'s `addStorage()` methods, tells the `CSDataStack` how to setup the persistent store. Defaults to `[CSLocalStorageOptions none]`.
*/ */
@objc @objc
public convenience init(fileName: String, configuration: String?, mappingModelBundles: [Bundle]?, localStorageOptions: Int) { public convenience init(fileName: String, configuration: ModelConfiguration, localStorageOptions: Int) {
self.init( self.init(
SQLiteStore( SQLiteStore(
fileName: fileName, fileName: fileName,
configuration: configuration, configuration: configuration,
mappingModelBundles: mappingModelBundles ?? Bundle.allBundles,
localStorageOptions: LocalStorageOptions(rawValue: localStorageOptions) localStorageOptions: LocalStorageOptions(rawValue: localStorageOptions)
) )
) )
} }
/** /**
Initializes an `CSSQLiteStore` with an all-default settings: a `fileURL` pointing to a "<Application name>.sqlite" file in the "Application Support/<bundle id>" directory (or the "Caches/<bundle id>" directory on tvOS), a `nil` `configuration` pertaining to the "Default" configuration, a `mappingModelBundles` set to search all `NSBundle`s, and `localStorageOptions` set to `.AllowProgresiveMigration`. Initializes an `CSSQLiteStore` with an all-default settings: a `fileURL` pointing to a "<Application name>.sqlite" file in the "Application Support/<bundle id>" directory (or the "Caches/<bundle id>" directory on tvOS), a `nil` `configuration` pertaining to the "Default" configuration, and `localStorageOptions` set to `[CSLocalStorageOptions none]`.
- Warning: The default SQLite file location for the `CSLegacySQLiteStore` and `CSSQLiteStore` are different. If the app was depending on CoreStore's default directories prior to 2.0.0, make sure to use `CSLegacySQLiteStore` instead of `CSSQLiteStore`. - Important: Initializing `CSSQLiteStore`s with custom migration mapping models is currently not supported. Create an `SQLiteStore` instance from Swift code and bridge the instance to Objective-C using its `SQLiteStore.bridgeToObjectiveC` property.
*/ */
@objc @objc
public convenience override init() { public convenience override init() {
@@ -104,12 +101,12 @@ public final class CSSQLiteStore: NSObject, CSLocalStorage, CoreStoreObjectiveCT
} }
/** /**
The `NSBundle`s from which to search mapping models for migrations An array of `SchemaMappingProvider`s that provides the complete mapping models for custom migrations. This is currently only supported for Swift code.
*/ */
@objc @objc
public var mappingModelBundles: [Bundle] { public var migrationMappingProviders: [Any] {
return self.bridgeToSwift.mappingModelBundles return self.bridgeToSwift.migrationMappingProviders
} }
/** /**
@@ -133,7 +130,7 @@ public final class CSSQLiteStore: NSObject, CSLocalStorage, CoreStoreObjectiveCT
/** /**
The configuration name in the model file The configuration name in the model file
*/ */
public var configuration: String? { public var configuration: ModelConfiguration {
return self.bridgeToSwift.configuration return self.bridgeToSwift.configuration
} }
@@ -154,11 +151,11 @@ public final class CSSQLiteStore: NSObject, CSLocalStorage, CoreStoreObjectiveCT
Called by the `CSDataStack` to perform actual deletion of the store file from disk. Do not call directly! The `sourceModel` argument is a hint for the existing store's model version. For `CSSQLiteStore`, this converts the database's WAL journaling mode to DELETE before deleting the file. Called by the `CSDataStack` to perform actual deletion of the store file from disk. Do not call directly! The `sourceModel` argument is a hint for the existing store's model version. For `CSSQLiteStore`, this converts the database's WAL journaling mode to DELETE before deleting the file.
*/ */
@objc @objc
public func eraseStorageAndWait(metadata: NSDictionary, soureModelHint: NSManagedObjectModel?, error: NSErrorPointer) -> Bool { public func cs_eraseStorageAndWait(metadata: NSDictionary, soureModelHint: NSManagedObjectModel?, error: NSErrorPointer) -> Bool {
return bridge(error) { return bridge(error) {
try self.bridgeToSwift.eraseStorageAndWait(metadata: metadata as! [String: Any], soureModelHint: soureModelHint) try self.bridgeToSwift.cs_eraseStorageAndWait(metadata: metadata as! [String: Any], soureModelHint: soureModelHint)
} }
} }
@@ -194,6 +191,23 @@ public final class CSSQLiteStore: NSObject, CSLocalStorage, CoreStoreObjectiveCT
self.bridgeToSwift = swiftValue self.bridgeToSwift = swiftValue
super.init() super.init()
} }
// MARK: Obsoleted
@available(*, obsoleted: 3.1, message: "The `mappingModelBundles` argument of this method is ignored. Use the new -[CSSQLiteStore initWithFileURL:configuration:localStorageOptions:]) initializer instead.")
@objc
public convenience init(fileURL: URL, configuration: ModelConfiguration, mappingModelBundles: [Bundle]?, localStorageOptions: Int) {
fatalError()
}
@available(*, obsoleted: 3.1, message: "The `mappingModelBundles` argument of this method is ignored. Use the new -[CSSQLiteStore initWithFileName:configuration:localStorageOptions:]) initializer instead.")
@objc
public convenience init(fileName: String, configuration: ModelConfiguration, mappingModelBundles: [Bundle]?, localStorageOptions: Int) {
fatalError()
}
} }
@@ -29,35 +29,22 @@ import CoreData
// MARK: - CSSaveResult // MARK: - CSSaveResult
/** @available(*, deprecated, message: "Use APIs that report failures with `CSError`s instead.")
The `CSSaveResult` serves as the Objective-C bridging type for `SaveResult`.
- SeeAlso: `SaveResult`
*/
@objc @objc
public final class CSSaveResult: NSObject, CoreStoreObjectiveCType { public final class CSSaveResult: NSObject, CoreStoreObjectiveCType {
/**
`YES` if the `commit` operation for the transaction succeeded, either because the save succeeded or because there were no changes to save. Returns `NO` to indicate failure.
*/
@objc @objc
public var isSuccess: Bool { public var isSuccess: Bool {
return self.bridgeToSwift.boolValue return self.bridgeToSwift.boolValue
} }
/**
`YES` if the `commit` operation for the transaction failed, or `NO` otherwise. When `YES`, the `error` property returns the actual `NSError` for the failure.
*/
@objc @objc
public var isFailure: Bool { public var isFailure: Bool {
return !self.bridgeToSwift.boolValue return !self.bridgeToSwift.boolValue
} }
/**
`YES` if the `commit` operation for the transaction succeeded and if there was an actual change made. Returns `NO` otherwise.
*/
@objc @objc
public var hasChanges: Bool { public var hasChanges: Bool {
@@ -68,9 +55,6 @@ public final class CSSaveResult: NSObject, CoreStoreObjectiveCType {
return hasChanges return hasChanges
} }
/**
The `NSError` for a failed `commit` operation, or `nil` if the `commit` succeeded
*/
@objc @objc
public var error: NSError? { public var error: NSError? {
@@ -81,14 +65,6 @@ public final class CSSaveResult: NSObject, CoreStoreObjectiveCType {
return error.bridgeToObjectiveC return error.bridgeToObjectiveC
} }
/**
If the result was a success, the `success` block is executed with a `BOOL` argument that indicates if there were any changes made. If the result was a failure, the `failure` block is executed with an `NSError` argument pertaining to the actual error.
The blocks are executed immediately as `@noescape` and will not be retained.
- parameter success: the block to execute on success. The block passes a `BOOL` argument that indicates if there were any changes made.
- parameter failure: the block to execute on failure. The block passes an `NSError` argument that pertains to the actual error.
*/
@objc @objc
public func handleSuccess(_ success: (_ hasChanges: Bool) -> Void, failure: (_ error: NSError) -> Void) { public func handleSuccess(_ success: (_ hasChanges: Bool) -> Void, failure: (_ error: NSError) -> Void) {
@@ -102,13 +78,6 @@ public final class CSSaveResult: NSObject, CoreStoreObjectiveCType {
} }
} }
/**
If the result was a success, the `success` block is executed with a `BOOL` argument that indicates if there were any changes made. If the result was a failure, this method does nothing.
The block is executed immediately as `@noescape` and will not be retained.
- parameter success: the block to execute on success. The block passes a `BOOL` argument that indicates if there were any changes made.
*/
@objc @objc
public func handleSuccess(_ success: (_ hasChanges: Bool) -> Void) { public func handleSuccess(_ success: (_ hasChanges: Bool) -> Void) {
@@ -119,13 +88,6 @@ public final class CSSaveResult: NSObject, CoreStoreObjectiveCType {
success(hasChanges) success(hasChanges)
} }
/**
If the result was a failure, the `failure` block is executed with an `NSError` argument pertaining to the actual error. If the result was a success, this method does nothing.
The block is executed immediately as `@noescape` and will not be retained.
- parameter failure: the block to execute on failure. The block passes an `NSError` argument that pertains to the actual error.
*/
@objc @objc
public func handleFailure(_ failure: (_ error: NSError) -> Void) { public func handleFailure(_ failure: (_ error: NSError) -> Void) {
@@ -173,6 +135,7 @@ public final class CSSaveResult: NSObject, CoreStoreObjectiveCType {
// MARK: - SaveResult // MARK: - SaveResult
@available(*, deprecated, message: "Use the new DataStack.perform(asynchronous:...) and DataStack.perform(synchronous:...) family of APIs")
extension SaveResult: CoreStoreSwiftType { extension SaveResult: CoreStoreSwiftType {
// MARK: CoreStoreSwiftType // MARK: CoreStoreSwiftType
@@ -27,8 +27,6 @@ import Foundation
import CoreData import CoreData
#if os(iOS) || os(watchOS) || os(tvOS)
// MARK: - CSSectionBy // MARK: - CSSectionBy
/** /**
@@ -36,8 +34,9 @@ import CoreData
- SeeAlso: `SectionBy` - SeeAlso: `SectionBy`
*/ */
@available(OSX 10.12, *)
@objc @objc
public final class CSSectionBy: NSObject, CoreStoreObjectiveCType { public final class CSSectionBy: NSObject {
/** /**
Initializes a `CSSectionBy` clause with the key path to use to group `CSListMonitor` objects into sections Initializes a `CSSectionBy` clause with the key path to use to group `CSListMonitor` objects into sections
@@ -46,9 +45,9 @@ public final class CSSectionBy: NSObject, CoreStoreObjectiveCType {
- returns: a `CSSectionBy` clause with the key path to use to group `CSListMonitor` objects into sections - returns: a `CSSectionBy` clause with the key path to use to group `CSListMonitor` objects into sections
*/ */
@objc @objc
public static func keyPath(_ sectionKeyPath: KeyPath) -> CSSectionBy { public static func keyPath(_ sectionKeyPath: KeyPathString) -> CSSectionBy {
return self.init(SectionBy(sectionKeyPath)) return self.init(SectionBy<NSManagedObject>(sectionKeyPath))
} }
/** /**
@@ -59,9 +58,9 @@ public final class CSSectionBy: NSObject, CoreStoreObjectiveCType {
- returns: a `CSSectionBy` clause with the key path to use to group `CSListMonitor` objects into sections - returns: a `CSSectionBy` clause with the key path to use to group `CSListMonitor` objects into sections
*/ */
@objc @objc
public static func keyPath(_ sectionKeyPath: KeyPath, sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> CSSectionBy { public static func keyPath(_ sectionKeyPath: KeyPathString, sectionIndexTransformer: @escaping (_ sectionName: String?) -> String?) -> CSSectionBy {
return self.init(SectionBy(sectionKeyPath, sectionIndexTransformer)) return self.init(SectionBy<NSManagedObject>(sectionKeyPath, sectionIndexTransformer))
} }
@@ -75,11 +74,11 @@ public final class CSSectionBy: NSObject, CoreStoreObjectiveCType {
// MARK: CoreStoreObjectiveCType // MARK: CoreStoreObjectiveCType
public let bridgeToSwift: SectionBy public let bridgeToSwift: SectionBy<NSManagedObject>
public init(_ swiftValue: SectionBy) { public init<D>(_ swiftValue: SectionBy<D>) {
self.bridgeToSwift = swiftValue self.bridgeToSwift = swiftValue.downcast()
super.init() super.init()
} }
} }
@@ -87,7 +86,8 @@ public final class CSSectionBy: NSObject, CoreStoreObjectiveCType {
// MARK: - SectionBy // MARK: - SectionBy
extension SectionBy: CoreStoreSwiftType { @available(OSX 10.12, *)
extension SectionBy {
// MARK: CoreStoreSwiftType // MARK: CoreStoreSwiftType
@@ -95,6 +95,12 @@ extension SectionBy: CoreStoreSwiftType {
return CSSectionBy(self) return CSSectionBy(self)
} }
}
#endif
// MARK: FilePrivate
fileprivate func downcast() -> SectionBy<NSManagedObject> {
return SectionBy<NSManagedObject>(self.sectionKeyPath, self.sectionIndexTransformer)
}
}
@@ -35,7 +35,7 @@ import CoreData
- SeeAlso: `SelectTerm` - SeeAlso: `SelectTerm`
*/ */
@objc @objc
public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType { public final class CSSelectTerm: NSObject {
/** /**
Provides a `CSSelectTerm` to a `CSSelect` clause for querying an entity attribute. Provides a `CSSelectTerm` to a `CSSelect` clause for querying an entity attribute.
@@ -48,7 +48,7 @@ public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType {
- parameter keyPath: the attribute name - parameter keyPath: the attribute name
*/ */
@objc @objc
public convenience init(keyPath: KeyPath) { public convenience init(keyPath: KeyPathString) {
self.init(.attribute(keyPath)) self.init(.attribute(keyPath))
} }
@@ -65,7 +65,7 @@ public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType {
- returns: a `CSSelectTerm` to a `CSSelect` clause for querying the average value of an attribute - returns: a `CSSelectTerm` to a `CSSelect` clause for querying the average value of an attribute
*/ */
@objc @objc
public static func average(_ keyPath: KeyPath, as alias: KeyPath?) -> CSSelectTerm { public static func average(_ keyPath: KeyPathString, as alias: KeyPathString?) -> CSSelectTerm {
return self.init(.average(keyPath, as: alias)) return self.init(.average(keyPath, as: alias))
} }
@@ -82,7 +82,7 @@ public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType {
- returns: a `SelectTerm` to a `Select` clause for a count query - returns: a `SelectTerm` to a `Select` clause for a count query
*/ */
@objc @objc
public static func count(_ keyPath: KeyPath, as alias: KeyPath?) -> CSSelectTerm { public static func count(_ keyPath: KeyPathString, as alias: KeyPathString?) -> CSSelectTerm {
return self.init(.count(keyPath, as: alias)) return self.init(.count(keyPath, as: alias))
} }
@@ -99,7 +99,7 @@ public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType {
- returns: a `CSSelectTerm` to a `CSSelect` clause for querying the maximum value for an attribute - returns: a `CSSelectTerm` to a `CSSelect` clause for querying the maximum value for an attribute
*/ */
@objc @objc
public static func maximum(_ keyPath: KeyPath, as alias: KeyPath?) -> CSSelectTerm { public static func maximum(_ keyPath: KeyPathString, as alias: KeyPathString?) -> CSSelectTerm {
return self.init(.maximum(keyPath, as: alias)) return self.init(.maximum(keyPath, as: alias))
} }
@@ -116,7 +116,7 @@ public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType {
- returns: a `CSSelectTerm` to a `CSSelect` clause for querying the minimum value for an attribute - returns: a `CSSelectTerm` to a `CSSelect` clause for querying the minimum value for an attribute
*/ */
@objc @objc
public static func minimum(_ keyPath: KeyPath, as alias: KeyPath?) -> CSSelectTerm { public static func minimum(_ keyPath: KeyPathString, as alias: KeyPathString?) -> CSSelectTerm {
return self.init(.minimum(keyPath, as: alias)) return self.init(.minimum(keyPath, as: alias))
} }
@@ -133,7 +133,7 @@ public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType {
- returns: a `CSSelectTerm` to a `CSSelect` clause for querying the sum value for an attribute - returns: a `CSSelectTerm` to a `CSSelect` clause for querying the sum value for an attribute
*/ */
@objc @objc
public static func sum(_ keyPath: KeyPath, as alias: KeyPath?) -> CSSelectTerm { public static func sum(_ keyPath: KeyPathString, as alias: KeyPathString?) -> CSSelectTerm {
return self.init(.sum(keyPath, as: alias)) return self.init(.sum(keyPath, as: alias))
} }
@@ -146,12 +146,11 @@ public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType {
select:[CSSelect objectIDForTerm:[CSSelectTerm objectIDAs:nil]] select:[CSSelect objectIDForTerm:[CSSelectTerm objectIDAs:nil]]
fetchClauses:@[[CSWhere keyPath:@"employeeID" isEqualTo: @1111]]]; fetchClauses:@[[CSWhere keyPath:@"employeeID" isEqualTo: @1111]]];
``` ```
- parameter keyPath: the attribute name
- parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "objecID" is used - parameter alias: the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "objecID" is used
- returns: a `SelectTerm` to a `Select` clause for querying the sum value for an attribute - returns: a `SelectTerm` to a `Select` clause for querying the sum value for an attribute
*/ */
@objc @objc
public static func objectIDAs(_ alias: KeyPath? = nil) -> CSSelectTerm { public static func objectIDAs(_ alias: KeyPathString? = nil) -> CSSelectTerm {
return self.init(.objectID(as: alias)) return self.init(.objectID(as: alias))
} }
@@ -176,11 +175,11 @@ public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType {
// MARK: CoreStoreObjectiveCType // MARK: CoreStoreObjectiveCType
public let bridgeToSwift: SelectTerm public let bridgeToSwift: SelectTerm<NSManagedObject>
public init(_ swiftValue: SelectTerm) { public init<D: NSManagedObject>(_ swiftValue: SelectTerm<D>) {
self.bridgeToSwift = swiftValue self.bridgeToSwift = swiftValue.downcast()
super.init() super.init()
} }
} }
@@ -188,7 +187,7 @@ public final class CSSelectTerm: NSObject, CoreStoreObjectiveCType {
// MARK: - SelectTerm // MARK: - SelectTerm
extension SelectTerm: CoreStoreSwiftType { extension SelectTerm where D: NSManagedObject {
// MARK: CoreStoreSwiftType // MARK: CoreStoreSwiftType
@@ -196,6 +195,24 @@ extension SelectTerm: CoreStoreSwiftType {
return CSSelectTerm(self) return CSSelectTerm(self)
} }
// MARK: FilePrivate
fileprivate func downcast() -> SelectTerm<NSManagedObject> {
switch self {
case ._attribute(let keyPath):
return SelectTerm<NSManagedObject>._attribute(keyPath)
case ._aggregate(let function, let keyPath, let alias, let nativeType):
return SelectTerm<NSManagedObject>._aggregate(function: function, keyPath: keyPath, alias: alias, nativeType: nativeType)
case ._identity(let alias, let nativeType):
return SelectTerm<NSManagedObject>._identity(alias: alias, nativeType: nativeType)
}
}
} }
@@ -217,11 +234,12 @@ public final class CSSelect: NSObject {
select:CSSelectNumber(CSAggregateMax(@"age")) select:CSSelectNumber(CSAggregateMax(@"age"))
// ... // ...
``` ```
- parameter term: the `CSSelectTerm` specifying the attribute/aggregate value to query - parameter numberTerm: the `CSSelectTerm` specifying the attribute/aggregate value to query
*/ */
@objc
public convenience init(numberTerm: CSSelectTerm) { public convenience init(numberTerm: CSSelectTerm) {
self.init(Select<NSNumber>(numberTerm.bridgeToSwift)) self.init(Select<NSManagedObject, NSNumber>(numberTerm.bridgeToSwift))
} }
/** /**
@@ -232,11 +250,12 @@ public final class CSSelect: NSObject {
select:CSSelectDecimal(CSAggregateAverage(@"price")) select:CSSelectDecimal(CSAggregateAverage(@"price"))
// ... // ...
``` ```
- parameter term: the `CSSelectTerm` specifying the attribute/aggregate value to query - parameter decimalTerm: the `CSSelectTerm` specifying the attribute/aggregate value to query
*/ */
@objc
public convenience init(decimalTerm: CSSelectTerm) { public convenience init(decimalTerm: CSSelectTerm) {
self.init(Select<NSDecimalNumber>(decimalTerm.bridgeToSwift)) self.init(Select<NSManagedObject, NSDecimalNumber>(decimalTerm.bridgeToSwift))
} }
/** /**
@@ -247,11 +266,12 @@ public final class CSSelect: NSObject {
select:CSSelectString(CSAttribute(@"fullname")) select:CSSelectString(CSAttribute(@"fullname"))
// ... // ...
``` ```
- parameter term: the `CSSelectTerm` specifying the attribute/aggregate value to query - parameter stringTerm: the `CSSelectTerm` specifying the attribute/aggregate value to query
*/ */
@objc
public convenience init(stringTerm: CSSelectTerm) { public convenience init(stringTerm: CSSelectTerm) {
self.init(Select<NSString>(stringTerm.bridgeToSwift)) self.init(Select<NSManagedObject, NSString>(stringTerm.bridgeToSwift))
} }
/** /**
@@ -262,11 +282,12 @@ public final class CSSelect: NSObject {
select:CSSelectDate(CSAggregateMax(@"updatedDate")) select:CSSelectDate(CSAggregateMax(@"updatedDate"))
// ... // ...
``` ```
- parameter term: the `CSSelectTerm` specifying the attribute/aggregate value to query - parameter dateTerm: the `CSSelectTerm` specifying the attribute/aggregate value to query
*/ */
@objc
public convenience init(dateTerm: CSSelectTerm) { public convenience init(dateTerm: CSSelectTerm) {
self.init(Select<Date>(dateTerm.bridgeToSwift)) self.init(Select<NSManagedObject, Date>(dateTerm.bridgeToSwift))
} }
/** /**
@@ -277,11 +298,12 @@ public final class CSSelect: NSObject {
select:CSSelectData(CSAttribute(@"imageData")) select:CSSelectData(CSAttribute(@"imageData"))
// ... // ...
``` ```
- parameter term: the `CSSelectTerm` specifying the attribute/aggregate value to query - parameter dataTerm: the `CSSelectTerm` specifying the attribute/aggregate value to query
*/ */
@objc
public convenience init(dataTerm: CSSelectTerm) { public convenience init(dataTerm: CSSelectTerm) {
self.init(Select<Data>(dataTerm.bridgeToSwift)) self.init(Select<NSManagedObject, Data>(dataTerm.bridgeToSwift))
} }
/** /**
@@ -292,11 +314,11 @@ public final class CSSelect: NSObject {
select:CSSelectObjectID() select:CSSelectObjectID()
// ... // ...
``` ```
- parameter term: the `CSSelectTerm` specifying the attribute/aggregate value to query
*/ */
@objc
public convenience init(objectIDTerm: ()) { public convenience init(objectIDTerm: ()) {
self.init(Select<NSManagedObjectID>(.objectID())) self.init(Select<NSManagedObject, NSManagedObjectID>(.objectID()))
} }
/** /**
@@ -309,9 +331,10 @@ public final class CSSelect: NSObject {
- parameter term: the `CSSelectTerm` specifying the attribute/aggregate value to query - parameter term: the `CSSelectTerm` specifying the attribute/aggregate value to query
- returns: a `CSSelect` clause for querying an entity attribute - returns: a `CSSelect` clause for querying an entity attribute
*/ */
@objc
public static func dictionaryForTerm(_ term: CSSelectTerm) -> CSSelect { public static func dictionaryForTerm(_ term: CSSelectTerm) -> CSSelect {
return self.init(Select<NSDictionary>(term.bridgeToSwift)) return self.init(Select<NSManagedObject, NSDictionary>(term.bridgeToSwift))
} }
/** /**
@@ -327,9 +350,10 @@ public final class CSSelect: NSObject {
- parameter terms: the `CSSelectTerm`s specifying the attribute/aggregate values to query - parameter terms: the `CSSelectTerm`s specifying the attribute/aggregate values to query
- returns: a `CSSelect` clause for querying an entity attribute - returns: a `CSSelect` clause for querying an entity attribute
*/ */
@objc
public static func dictionaryForTerms(_ terms: [CSSelectTerm]) -> CSSelect { public static func dictionaryForTerms(_ terms: [CSSelectTerm]) -> CSSelect {
return self.init(Select<NSDictionary>(terms.map { $0.bridgeToSwift })) return self.init(Select<NSManagedObject, NSDictionary>(terms.map { $0.bridgeToSwift }))
} }
@@ -359,18 +383,18 @@ public final class CSSelect: NSObject {
// MARK: CoreStoreObjectiveCType // MARK: CoreStoreObjectiveCType
public init<T: SelectValueResultType>(_ swiftValue: Select<T>) { public init<D: NSManagedObject, T: QueryableAttributeType>(_ swiftValue: Select<D, T>) {
self.attributeType = T.attributeType self.attributeType = T.cs_rawAttributeType
self.selectTerms = swiftValue.selectTerms self.selectTerms = swiftValue.selectTerms.map({ $0.downcast() })
self.bridgeToSwift = swiftValue self.bridgeToSwift = swiftValue
super.init() super.init()
} }
public init<T: SelectResultType>(_ swiftValue: Select<T>) { public init<D: NSManagedObject, T>(_ swiftValue: Select<D, T>) {
self.attributeType = .undefinedAttributeType self.attributeType = .undefinedAttributeType
self.selectTerms = swiftValue.selectTerms self.selectTerms = swiftValue.selectTerms.map({ $0.downcast() })
self.bridgeToSwift = swiftValue self.bridgeToSwift = swiftValue
super.init() super.init()
} }
@@ -379,7 +403,95 @@ public final class CSSelect: NSObject {
// MARK: Internal // MARK: Internal
internal let attributeType: NSAttributeType internal let attributeType: NSAttributeType
internal let selectTerms: [SelectTerm] internal let selectTerms: [SelectTerm<NSManagedObject>]
// MARK: Internal
internal func applyToFetchRequest(_ fetchRequest: NSFetchRequest<NSFetchRequestResult>) {
fetchRequest.includesPendingChanges = false
fetchRequest.resultType = .dictionaryResultType
func attributeDescription(for keyPath: String, in entity: NSEntityDescription) -> NSAttributeDescription? {
let components = keyPath.components(separatedBy: ".")
switch components.count {
case 0:
return nil
case 1:
return entity.attributesByName[components[0]]
default:
guard let relationship = entity.relationshipsByName[components[0]],
let destinationEntity = relationship.destinationEntity else {
return nil
}
return attributeDescription(
for: components.dropFirst().joined(separator: "."),
in: destinationEntity
)
}
}
var propertiesToFetch = [Any]()
for term in self.selectTerms {
switch term {
case ._attribute(let keyPath):
propertiesToFetch.append(keyPath)
case ._aggregate(let function, let keyPath, let alias, let nativeType):
let entityDescription = fetchRequest.entity!
if let attributeDescription = attributeDescription(for: keyPath, in: entityDescription) {
let expressionDescription = NSExpressionDescription()
expressionDescription.name = alias
if nativeType == .undefinedAttributeType {
expressionDescription.expressionResultType = attributeDescription.attributeType
}
else {
expressionDescription.expressionResultType = nativeType
}
expressionDescription.expression = NSExpression(
forFunction: function,
arguments: [NSExpression(forKeyPath: keyPath)]
)
propertiesToFetch.append(expressionDescription)
}
else {
CoreStore.log(
.warning,
message: "The key path \"\(keyPath)\" could not be resolved in entity \(cs_typeName(entityDescription.managedObjectClassName)) as an attribute and will be ignored by \(cs_typeName(self)) query clause."
)
}
case ._identity(let alias, let nativeType):
let expressionDescription = NSExpressionDescription()
expressionDescription.name = alias
if nativeType == .undefinedAttributeType {
expressionDescription.expressionResultType = .objectIDAttributeType
}
else {
expressionDescription.expressionResultType = nativeType
}
expressionDescription.expression = NSExpression.expressionForEvaluatedObject()
propertiesToFetch.append(expressionDescription)
}
}
fetchRequest.propertiesToFetch = propertiesToFetch
}
// MARK: Private // MARK: Private
@@ -390,7 +502,7 @@ public final class CSSelect: NSObject {
// MARK: - Select // MARK: - Select
extension Select: CoreStoreSwiftType { extension Select where D: NSManagedObject {
// MARK: CoreStoreSwiftType // MARK: CoreStoreSwiftType
@@ -398,4 +510,12 @@ extension Select: CoreStoreSwiftType {
return CSSelect(self) return CSSelect(self)
} }
// MARK: FilePrivate
fileprivate func downcast() -> Select<NSManagedObject, T> {
return Select<NSManagedObject, T>(self.selectTerms.map({ $0.downcast() }))
}
} }
@@ -152,7 +152,7 @@ public final class CSSetupResult: NSObject {
// MARK: CoreStoreObjectiveCType // MARK: CoreStoreObjectiveCType
public required init<T: StorageInterface>(_ swiftValue: SetupResult<T>) where T: CoreStoreSwiftType, T.ObjectiveCType: CSStorageInterface { public required init<T>(_ swiftValue: SetupResult<T>) where T: CoreStoreSwiftType, T.ObjectiveCType: CSStorageInterface {
switch swiftValue { switch swiftValue {
@@ -47,7 +47,7 @@ public protocol CSStorageInterface {
The configuration name in the model file The configuration name in the model file
*/ */
@objc @objc
var configuration: String? { get } var configuration: ModelConfiguration { get }
/** /**
The options dictionary for the `NSPersistentStore` The options dictionary for the `NSPersistentStore`
@@ -106,10 +106,10 @@ public protocol CSLocalStorage: CSStorageInterface {
var fileURL: URL { get } var fileURL: URL { get }
/** /**
The `NSBundle`s from which to search mapping models for migrations An array of `SchemaMappingProvider`s that provides the complete mapping models for custom migrations. This is currently only supported for Swift code.
*/ */
@objc @objc
var mappingModelBundles: [Bundle] { get } var migrationMappingProviders: [Any] { get }
/** /**
Options that tell the `CSDataStack` how to setup the persistent store Options that tell the `CSDataStack` how to setup the persistent store
@@ -121,5 +121,5 @@ public protocol CSLocalStorage: CSStorageInterface {
Called by the `CSDataStack` to perform actual deletion of the store file from disk. Do not call directly! The `sourceModel` argument is a hint for the existing store's model version. Implementers can use the `sourceModel` to perform necessary store operations. (SQLite stores for example, can convert WAL journaling mode to DELETE before deleting) Called by the `CSDataStack` to perform actual deletion of the store file from disk. Do not call directly! The `sourceModel` argument is a hint for the existing store's model version. Implementers can use the `sourceModel` to perform necessary store operations. (SQLite stores for example, can convert WAL journaling mode to DELETE before deleting)
*/ */
@objc @objc
func eraseStorageAndWait(metadata: NSDictionary, soureModelHint: NSManagedObjectModel?, error: NSErrorPointer) -> Bool func cs_eraseStorageAndWait(metadata: NSDictionary, soureModelHint: NSManagedObjectModel?, error: NSErrorPointer) -> Bool
} }
@@ -35,37 +35,22 @@ import CoreData
- SeeAlso: `SynchronousDataTransaction` - SeeAlso: `SynchronousDataTransaction`
*/ */
@objc @objc
public final class CSSynchronousDataTransaction: CSBaseDataTransaction { public final class CSSynchronousDataTransaction: CSBaseDataTransaction, CoreStoreObjectiveCType {
/** /**
Saves the transaction changes and waits for completion synchronously. This method should not be used after the `-commitAndWait` method was already called once. Saves the transaction changes and waits for completion synchronously. This method should not be used after the `-commitAndWaitWithError:` method was already called once.
- returns: a `CSSaveResult` containing the success or failure information - parameter error: the `CSError` pointer that indicates the reason in case of an failure
- returns: `YES` if the commit succeeded, `NO` if the commit failed. If `NO`, the `error` argument will hold error information.
*/ */
@objc @objc
public func commitAndWait() -> CSSaveResult { public func commitAndWait(error: NSErrorPointer) -> Bool {
return bridge { return bridge(error) {
self.bridgeToSwift.commitAndWait() if case (_, let error?) = self.bridgeToSwift.context.saveSynchronously(waitForMerge: true) {
}
}
/** throw error
Begins a child transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made. This method should not be used after the `-commitAndWait` method was already called once.
- parameter closure: the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
- returns: a `CSSaveResult` value indicating success or failure, or `nil` if the transaction was not comitted synchronously
*/
@objc
@discardableResult
public func beginSynchronous(_ closure: @escaping (_ transaction: CSSynchronousDataTransaction) -> Void) -> CSSaveResult? {
return bridge {
self.bridgeToSwift.beginSynchronous { (transaction) in
closure(transaction.bridgeToObjectiveC)
} }
} }
} }
@@ -144,9 +129,9 @@ public final class CSSynchronousDataTransaction: CSBaseDataTransaction {
public typealias SwiftType = SynchronousDataTransaction public typealias SwiftType = SynchronousDataTransaction
public override var bridgeToSwift: SynchronousDataTransaction { public var bridgeToSwift: SynchronousDataTransaction {
return super.bridgeToSwift as! SynchronousDataTransaction return super.swiftTransaction as! SynchronousDataTransaction
} }
public required init(_ swiftValue: SynchronousDataTransaction) { public required init(_ swiftValue: SynchronousDataTransaction) {
@@ -154,9 +139,36 @@ public final class CSSynchronousDataTransaction: CSBaseDataTransaction {
super.init(swiftValue as BaseDataTransaction) super.init(swiftValue as BaseDataTransaction)
} }
public required init(_ swiftValue: BaseDataTransaction) { public required override init(_ swiftValue: BaseDataTransaction) {
super.init(swiftValue as! SynchronousDataTransaction) super.init(swiftValue)
}
// MARK: Deprecated
@available(*, deprecated, message: "Use the new -[CSSynchronousDataTransaction commitAndWaitWithError:] method")
@objc
public func commitAndWait() -> CSSaveResult {
return bridge {
self.bridgeToSwift.commitAndWait()
}
}
@available(*, deprecated, message: "Secondary tasks spawned from CSAsynchronousDataTransactions and CSSynchronousDataTransactions are no longer supported. ")
@objc
@discardableResult
public func beginSynchronous(_ closure: @escaping (_ transaction: CSSynchronousDataTransaction) -> Void) -> CSSaveResult? {
return bridge {
self.bridgeToSwift.beginSynchronous { (transaction) in
closure(transaction.bridgeToObjectiveC)
}
}
} }
} }
+115
View File
@@ -0,0 +1,115 @@
//
// CSUnsafeDataModelSchema.swift
// CoreStore
//
// Copyright © 2017 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import CoreData
import Foundation
// MARK: - CSUnsafeDataModelSchema
/**
The `CSUnsafeDataModelSchema` serves as the Objective-C bridging type for `UnsafeDataModelSchema`.
- SeeAlso: `UnsafeDataModelSchema`
*/
@objc
public final class CSUnsafeDataModelSchema: NSObject, CSDynamicSchema, CoreStoreObjectiveCType {
/**
Initializes a `CSUnsafeDataModelSchema` from an `NSManagedObjectModel`.
- parameter modelName: the model version, typically the file name of an *.xcdatamodeld file (without the file extension)
- parameter model: the `NSManagedObjectModel`
*/
@objc
public required init(modelName: ModelVersion, model: NSManagedObjectModel) {
self.bridgeToSwift = UnsafeDataModelSchema(
modelName: modelName,
model: model
)
}
// MARK: NSObject
public override var hash: Int {
return ObjectIdentifier(self.bridgeToSwift).hashValue
}
public override func isEqual(_ object: Any?) -> Bool {
guard let object = object as? CSUnsafeDataModelSchema else {
return false
}
return self.bridgeToSwift === object.bridgeToSwift
}
public override var description: String {
return "(\(String(reflecting: type(of: self)))) \(self.bridgeToSwift.coreStoreDumpString)"
}
// MARK: CSDynamicSchema
@objc
public var modelVersion: ModelVersion {
return self.bridgeToSwift.modelVersion
}
@objc
public func rawModel() -> NSManagedObjectModel {
return self.bridgeToSwift.rawModel()
}
// MARK: CoreStoreObjectiveCType
public let bridgeToSwift: UnsafeDataModelSchema
public required init(_ swiftValue: UnsafeDataModelSchema) {
self.bridgeToSwift = swiftValue
super.init()
}
}
// MARK: - UnsafeDataModelSchema
extension UnsafeDataModelSchema: CoreStoreSwiftType {
// MARK: CoreStoreSwiftType
public var bridgeToObjectiveC: CSUnsafeDataModelSchema {
return CSUnsafeDataModelSchema(self)
}
}
@@ -35,33 +35,48 @@ import CoreData
- SeeAlso: `UnsafeDataTransaction` - SeeAlso: `UnsafeDataTransaction`
*/ */
@objc @objc
public final class CSUnsafeDataTransaction: CSBaseDataTransaction { public final class CSUnsafeDataTransaction: CSBaseDataTransaction, CoreStoreObjectiveCType {
/** /**
Saves the transaction changes asynchronously. For a `CSUnsafeDataTransaction`, multiple commits are allowed, although it is the developer's responsibility to ensure a reasonable leeway to prevent blocking the main thread. Saves the transaction changes asynchronously. For a `CSUnsafeDataTransaction`, multiple commits are allowed, although it is the developer's responsibility to ensure a reasonable leeway to prevent blocking the main thread.
- parameter completion: the block executed after the save completes. Success or failure is reported by the `CSSaveResult` argument of the block. - parameter success: the block executed if the save succeeds.
- parameter failure: the block executed if the save fails. A `CSError` is reported as the argument of the block.
*/ */
@objc @objc
public func commit(_ completion: ((_ result: CSSaveResult) -> Void)?) { public func commitWithSuccess(_ success: (() -> Void)?, _ failure: ((CSError) -> Void)?) {
self.bridgeToSwift.commit { (result) in self.bridgeToSwift.context.saveAsynchronouslyWithCompletion { (_, error) in
completion?(result.bridgeToObjectiveC) defer {
withExtendedLifetime(self, {})
}
if let error = error {
failure?(error.bridgeToObjectiveC)
}
else {
success?()
}
} }
} }
/** /**
Saves the transaction changes and waits for completion synchronously. For a `CSUnsafeDataTransaction`, multiple commits are allowed, although it is the developer's responsibility to ensure a reasonable leeway to prevent blocking the main thread. Saves the transaction changes and waits for completion synchronously. For a `CSUnsafeDataTransaction`, multiple commits are allowed, although it is the developer's responsibility to ensure a reasonable leeway to prevent blocking the main thread.
- returns: a `CSSaveResult` containing the success or failure information - parameter error: the `CSError` pointer that indicates the reason in case of an failure
- returns: `YES` if the commit succeeded, `NO` if the commit failed. If `NO`, the `error` argument will hold error information.
*/ */
@objc @objc
public func commitAndWait() -> CSSaveResult { public func commitAndWait(error: NSErrorPointer) -> Bool {
return bridge { return bridge(error) {
self.bridgeToSwift.commitAndWait() if case (_, let error?) = self.bridgeToSwift.context.saveSynchronously(waitForMerge: true) {
throw error
}
} }
} }
@@ -107,7 +122,7 @@ public final class CSUnsafeDataTransaction: CSBaseDataTransaction {
Flushes all pending changes to the transaction's observers at the end of the `closure`'s execution. This is useful in conjunction with `ListMonitor`s and `ObjectMonitor`s created from `UnsafeDataTransaction`s used to manage temporary "scratch" data. Flushes all pending changes to the transaction's observers at the end of the `closure`'s execution. This is useful in conjunction with `ListMonitor`s and `ObjectMonitor`s created from `UnsafeDataTransaction`s used to manage temporary "scratch" data.
- Important: Note that unlike `commit()`, `flush()` does not propagate/save updates to the `DataStack` and the persistent store. However, the flushed changes will be seen by children transactions created further from the current transaction (i.e. through `transaction.beginUnsafe()`) - Important: Note that unlike `commit()`, `flush()` does not propagate/save updates to the `DataStack` and the persistent store. However, the flushed changes will be seen by children transactions created further from the current transaction (i.e. through `transaction.beginUnsafe()`)
- parameter closure: the closure where changes can be made prior to the flush - parameter block: the block where changes can be made prior to the flush
*/ */
@objc @objc
public func flush(_ block: () -> Void) { public func flush(_ block: () -> Void) {
@@ -156,7 +171,7 @@ public final class CSUnsafeDataTransaction: CSBaseDataTransaction {
- that all saves will be done either through the `CSUnsafeDataTransaction`'s `-commit:` or `-commitAndWait` method, or by calling `-save:` manually on the context, its parent, and all other ancestor contexts if there are any. - that all saves will be done either through the `CSUnsafeDataTransaction`'s `-commit:` or `-commitAndWait` method, or by calling `-save:` manually on the context, its parent, and all other ancestor contexts if there are any.
*/ */
@objc @objc
public var internalContext: NSManagedObjectContext { public func unsafeContext() -> NSManagedObjectContext {
return self.bridgeToSwift.context return self.bridgeToSwift.context
} }
@@ -174,9 +189,9 @@ public final class CSUnsafeDataTransaction: CSBaseDataTransaction {
public typealias SwiftType = UnsafeDataTransaction public typealias SwiftType = UnsafeDataTransaction
public override var bridgeToSwift: UnsafeDataTransaction { public var bridgeToSwift: UnsafeDataTransaction {
return super.bridgeToSwift as! UnsafeDataTransaction return super.swiftTransaction as! UnsafeDataTransaction
} }
public required init(_ swiftValue: UnsafeDataTransaction) { public required init(_ swiftValue: UnsafeDataTransaction) {
@@ -184,9 +199,54 @@ public final class CSUnsafeDataTransaction: CSBaseDataTransaction {
super.init(swiftValue as BaseDataTransaction) super.init(swiftValue as BaseDataTransaction)
} }
public required init(_ swiftValue: BaseDataTransaction) { public required override init(_ swiftValue: BaseDataTransaction) {
super.init(swiftValue as! UnsafeDataTransaction) super.init(swiftValue)
}
// MARK: Deprecated
@available(*, deprecated, renamed: "unsafeContext()")
@objc
public var internalContext: NSManagedObjectContext {
return self.bridgeToSwift.context
}
@available(*, deprecated, message: "Use the new -[CSUnsafeDataTransaction commitWithSuccess:failure:] method")
@objc
public func commit(_ completion: ((_ result: CSSaveResult) -> Void)?) {
self.bridgeToSwift.context.saveAsynchronouslyWithCompletion { (hasChanges, error) in
defer {
withExtendedLifetime(self, {})
}
if let error = error {
completion?(SaveResult(error).bridgeToObjectiveC)
}
else {
completion?(SaveResult(hasChanges: hasChanges).bridgeToObjectiveC)
}
}
}
@available(*, deprecated, message: "Use the new -[CSUnsafeDataTransaction commitAndWaitWithError:] method")
@objc
public func commitAndWait() -> CSSaveResult {
return bridge { () -> SaveResult in
switch self.bridgeToSwift.context.saveSynchronously(waitForMerge: true) {
case (let hasChanges, nil): return SaveResult(hasChanges: hasChanges)
case (_, let error?): return SaveResult(error)
}
}
} }
} }
@@ -35,7 +35,7 @@ import CoreData
- SeeAlso: `Where` - SeeAlso: `Where`
*/ */
@objc @objc
public final class CSWhere: NSObject, CSFetchClause, CSQueryClause, CSDeleteClause, CoreStoreObjectiveCType { public final class CSWhere: NSObject, CSFetchClause, CSQueryClause, CSDeleteClause {
/** /**
The internal `NSPredicate` instance for the `Where` clause The internal `NSPredicate` instance for the `Where` clause
@@ -85,9 +85,11 @@ public final class CSWhere: NSObject, CSFetchClause, CSQueryClause, CSDeleteClau
- parameter value: the arguments for the `==` operator - parameter value: the arguments for the `==` operator
*/ */
@objc @objc
public convenience init(keyPath: KeyPath, isEqualTo value: NSObject?) { public convenience init(keyPath: KeyPathString, isEqualTo value: CoreDataNativeType?) {
self.init(Where(keyPath, isEqualTo: value)) self.init(value == nil || value is NSNull
? Where("\(keyPath) == nil")
: Where("\(keyPath) == %@", value!))
} }
/** /**
@@ -97,9 +99,9 @@ public final class CSWhere: NSObject, CSFetchClause, CSQueryClause, CSDeleteClau
- parameter list: the array to check membership of - parameter list: the array to check membership of
*/ */
@objc @objc
public convenience init(keyPath: KeyPath, isMemberOf list: [NSObject]) { public convenience init(keyPath: KeyPathString, isMemberOf list: [CoreDataNativeType]) {
self.init(Where(keyPath, isMemberOf: list)) self.init(Where("\(keyPath) IN %@", list as NSArray))
} }
/** /**
@@ -147,11 +149,11 @@ public final class CSWhere: NSObject, CSFetchClause, CSQueryClause, CSDeleteClau
// MARK: CoreStoreObjectiveCType // MARK: CoreStoreObjectiveCType
public let bridgeToSwift: Where public let bridgeToSwift: Where<NSManagedObject>
public init(_ swiftValue: Where) { public init<D: NSManagedObject>(_ swiftValue: Where<D>) {
self.bridgeToSwift = swiftValue self.bridgeToSwift = swiftValue.downcast()
super.init() super.init()
} }
} }
@@ -159,7 +161,7 @@ public final class CSWhere: NSObject, CSFetchClause, CSQueryClause, CSDeleteClau
// MARK: - Where // MARK: - Where
extension Where: CoreStoreSwiftType { extension Where where D: NSManagedObject {
// MARK: CoreStoreSwiftType // MARK: CoreStoreSwiftType
@@ -167,4 +169,12 @@ extension Where: CoreStoreSwiftType {
return CSWhere(self) return CSWhere(self)
} }
// MARK: FilePrivate
fileprivate func downcast() -> Where<NSManagedObject> {
return Where<NSManagedObject>(self.predicate)
}
} }
+115
View File
@@ -0,0 +1,115 @@
//
// CSXcodeDataModelSchema.swift
// CoreStore
//
// Copyright © 2017 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import CoreData
import Foundation
// MARK: - CSXcodeDataModelSchema
/**
The `CSXcodeDataModelSchema` serves as the Objective-C bridging type for `XcodeDataModelSchema`.
- SeeAlso: `XcodeDataModelSchema`
*/
@objc
public final class CSXcodeDataModelSchema: NSObject, CSDynamicSchema, CoreStoreObjectiveCType {
/**
Initializes an `CSXcodeDataModelSchema` from an *.xcdatamodeld file URL.
- parameter modelName: the model version, typically the file name of an *.xcdatamodeld file (without the file extension)
- parameter modelVersionFileURL: the file URL that points to the .xcdatamodeld's "momd" file.
*/
@objc
public required init(modelName: ModelVersion, modelVersionFileURL: URL) {
self.bridgeToSwift = XcodeDataModelSchema(
modelName: modelName,
modelVersionFileURL: modelVersionFileURL
)
}
// MARK: NSObject
public override var hash: Int {
return ObjectIdentifier(self.bridgeToSwift).hashValue
}
public override func isEqual(_ object: Any?) -> Bool {
guard let object = object as? CSXcodeDataModelSchema else {
return false
}
return self.bridgeToSwift === object.bridgeToSwift
}
public override var description: String {
return "(\(String(reflecting: type(of: self)))) \(self.bridgeToSwift.coreStoreDumpString)"
}
// MARK: CSDynamicSchema
@objc
public var modelVersion: ModelVersion {
return self.bridgeToSwift.modelVersion
}
@objc
public func rawModel() -> NSManagedObjectModel {
return self.bridgeToSwift.rawModel()
}
// MARK: CoreStoreObjectiveCType
public let bridgeToSwift: XcodeDataModelSchema
public required init(_ swiftValue: XcodeDataModelSchema) {
self.bridgeToSwift = swiftValue
super.init()
}
}
// MARK: - XcodeDataModelSchema
extension XcodeDataModelSchema: CoreStoreSwiftType {
// MARK: CoreStoreSwiftType
public var bridgeToObjectiveC: CSXcodeDataModelSchema {
return CSXcodeDataModelSchema(self)
}
}
+81
View File
@@ -0,0 +1,81 @@
//
// CoreDataNativeType.swift
// CoreStore
//
// Copyright © 2017 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import Foundation
import CoreData
// MARK: - CoreDataNativeType
/**
Objective-C Foundation types that are natively supported by Core Data managed attributes all conform to `CoreDataNativeType`.
*/
@objc
public protocol CoreDataNativeType: class, NSObjectProtocol {}
// MARK: - NSNumber
extension NSNumber: CoreDataNativeType {}
// MARK: - NSString
extension NSString: CoreDataNativeType {}
// MARK: - NSDate
extension NSDate: CoreDataNativeType {}
// MARK: - NSData
extension NSData: CoreDataNativeType {}
// MARK: - NSSet
extension NSSet: CoreDataNativeType {}
// MARK: - NSOrderedSet
extension NSOrderedSet: CoreDataNativeType {}
// MARK: - NSManagedObject
extension NSManagedObject: CoreDataNativeType {}
// MARK: - NSManagedObjectID
extension NSManagedObjectID: CoreDataNativeType {}
// MARK: - NSNull
extension NSNull: CoreDataNativeType {}
@@ -48,8 +48,7 @@ extension AsynchronousDataTransaction: CustomDebugStringConvertible, CoreStoreDe
("context", self.context), ("context", self.context),
("supportsUndo", self.supportsUndo), ("supportsUndo", self.supportsUndo),
("bypassesQueueing", self.bypassesQueueing), ("bypassesQueueing", self.bypassesQueueing),
("isCommitted", self.isCommitted), ("isCommitted", self.isCommitted)
("result", self.result as Any)
) )
} }
} }
@@ -142,6 +141,13 @@ extension CoreStoreError: CustomDebugStringConvertible, CoreStoreDebugStringConv
case .internalError(let NSError): case .internalError(let NSError):
firstLine = ".internalError" firstLine = ".internalError"
info.append(("NSError", NSError)) info.append(("NSError", NSError))
case .userError(error: let error):
firstLine = ".userError"
info.append(("Error", error))
case .userCancelled:
firstLine = ".userCancelled"
} }
return createFormattedString( return createFormattedString(
@@ -152,6 +158,32 @@ extension CoreStoreError: CustomDebugStringConvertible, CoreStoreDebugStringConv
} }
// MARK: - CoreStoreSchema
extension CoreStoreSchema: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
// MARK: CustomDebugStringConvertible
public var debugDescription: String {
return formattedDebugDescription(self)
}
// MARK: CoreStoreDebugStringConvertible
public var coreStoreDumpString: String {
return createFormattedString(
"(", ")",
("modelVersion", self.modelVersion),
("entitiesByConfiguration", self.entitiesByConfiguration),
("rawModel", self.rawModel())
)
}
}
// MARK: - DataStack // MARK: - DataStack
extension DataStack: CustomDebugStringConvertible, CoreStoreDebugStringConvertible { extension DataStack: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
@@ -173,14 +205,40 @@ extension DataStack: CustomDebugStringConvertible, CoreStoreDebugStringConvertib
("coordinator", self.coordinator), ("coordinator", self.coordinator),
("rootSavingContext", self.rootSavingContext), ("rootSavingContext", self.rootSavingContext),
("mainContext", self.mainContext), ("mainContext", self.mainContext),
("model", self.model), ("schemaHistory", self.schemaHistory),
("migrationChain", self.migrationChain),
("coordinator.persistentStores", self.coordinator.persistentStores) ("coordinator.persistentStores", self.coordinator.persistentStores)
) )
} }
} }
// MARK: - Entity
extension Entity: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
// MARK: CustomDebugStringConvertible
public var debugDescription: String {
return formattedDebugDescription(self)
}
// MARK: CoreStoreDebugStringConvertible
public var coreStoreDumpString: String {
return createFormattedString(
"(", ")",
("type", self.type),
("entityName", self.entityName),
("isAbstract", self.isAbstract),
("versionHashModifier", self.versionHashModifier as Any)
)
}
}
// MARK: - From // MARK: - From
extension From: CustomDebugStringConvertible, CoreStoreDebugStringConvertible { extension From: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
@@ -316,9 +374,9 @@ extension Into: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
} }
// MARK: - LegacySQLiteStore // MARK: - UnsafeDataModelSchema
extension LegacySQLiteStore: CustomDebugStringConvertible, CoreStoreDebugStringConvertible { extension UnsafeDataModelSchema: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
// MARK: CustomDebugStringConvertible // MARK: CustomDebugStringConvertible
@@ -334,20 +392,16 @@ extension LegacySQLiteStore: CustomDebugStringConvertible, CoreStoreDebugStringC
return createFormattedString( return createFormattedString(
"(", ")", "(", ")",
("configuration", self.configuration as Any), ("modelVersion", self.modelVersion),
("storeOptions", self.storeOptions as Any), ("rawModel", self.rawModel())
("fileURL", self.fileURL),
("mappingModelBundles", self.mappingModelBundles),
("localStorageOptions", self.localStorageOptions)
) )
} }
} }
#if os(iOS) || os(watchOS) || os(tvOS)
// MARK: - ListMonitor // MARK: - ListMonitor
@available(OSX 10.12, *)
private struct CoreStoreFetchedSectionInfoWrapper: CoreStoreDebugStringConvertible { private struct CoreStoreFetchedSectionInfoWrapper: CoreStoreDebugStringConvertible {
let sectionInfo: NSFetchedResultsSectionInfo let sectionInfo: NSFetchedResultsSectionInfo
@@ -362,6 +416,7 @@ private struct CoreStoreFetchedSectionInfoWrapper: CoreStoreDebugStringConvertib
} }
} }
@available(OSX 10.12, *)
extension ListMonitor: CustomDebugStringConvertible, CoreStoreDebugStringConvertible { extension ListMonitor: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
// MARK: CustomDebugStringConvertible // MARK: CustomDebugStringConvertible
@@ -384,7 +439,6 @@ extension ListMonitor: CustomDebugStringConvertible, CoreStoreDebugStringConvert
) )
} }
} }
#endif
// MARK: - LocalStorageOptions // MARK: - LocalStorageOptions
@@ -451,7 +505,7 @@ extension MigrationChain: CustomDebugStringConvertible, CoreStoreDebugStringConv
public var coreStoreDumpString: String { public var coreStoreDumpString: String {
guard self.valid else { guard self.isValid else {
return "<invalid migration chain>" return "<invalid migration chain>"
} }
@@ -554,10 +608,9 @@ extension MigrationType: CustomDebugStringConvertible, CoreStoreDebugStringConve
} }
#if os(iOS) || os(watchOS) || os(tvOS)
// MARK: - ObjectMonitor // MARK: - ObjectMonitor
@available(OSX 10.12, *)
extension ObjectMonitor: CustomDebugStringConvertible, CoreStoreDebugStringConvertible { extension ObjectMonitor: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
// MARK: CustomDebugStringConvertible // MARK: CustomDebugStringConvertible
@@ -579,7 +632,7 @@ extension ObjectMonitor: CustomDebugStringConvertible, CoreStoreDebugStringConve
) )
} }
} }
#endif
// MARK: - OrderBy // MARK: - OrderBy
@@ -607,6 +660,7 @@ extension OrderBy: CustomDebugStringConvertible, CoreStoreDebugStringConvertible
// MARK: - SaveResult // MARK: - SaveResult
@available(*, deprecated, message: "Use the new DataStack.perform(asynchronous:...) and DataStack.perform(synchronous:...) family of APIs")
extension SaveResult: CustomDebugStringConvertible, CoreStoreDebugStringConvertible { extension SaveResult: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
// MARK: CustomDebugStringConvertible // MARK: CustomDebugStringConvertible
@@ -639,10 +693,9 @@ extension SaveResult: CustomDebugStringConvertible, CoreStoreDebugStringConverti
} }
#if os(iOS) || os(watchOS) || os(tvOS)
// MARK: - SectionBy // MARK: - SectionBy
@available(OSX 10.12, *)
extension SectionBy: CustomDebugStringConvertible, CoreStoreDebugStringConvertible { extension SectionBy: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
// MARK: CustomDebugStringConvertible // MARK: CustomDebugStringConvertible
@@ -663,7 +716,32 @@ extension SectionBy: CustomDebugStringConvertible, CoreStoreDebugStringConvertib
) )
} }
} }
#endif
// MARK: - SchemaHistory
extension SchemaHistory: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
// MARK: CustomDebugStringConvertible
public var debugDescription: String {
return formattedDebugDescription(self)
}
// MARK: CoreStoreDebugStringConvertible
public var coreStoreDumpString: String {
return createFormattedString(
"(", ")",
("currentModelVersion", self.currentModelVersion),
("migrationChain", self.migrationChain),
("schemaByVersion", self.schemaByVersion)
)
}
}
// MARK: - Select // MARK: - Select
@@ -789,8 +867,9 @@ extension SQLiteStore: CustomDebugStringConvertible, CoreStoreDebugStringConvert
("configuration", self.configuration as Any), ("configuration", self.configuration as Any),
("storeOptions", self.storeOptions as Any), ("storeOptions", self.storeOptions as Any),
("fileURL", self.fileURL), ("fileURL", self.fileURL),
("mappingModelBundles", self.mappingModelBundles), ("migrationMappingProviders", self.migrationMappingProviders),
("localStorageOptions", self.localStorageOptions) ("localStorageOptions", self.localStorageOptions),
("fileSize", self.fileSize() as Any)
) )
} }
} }
@@ -817,8 +896,7 @@ extension SynchronousDataTransaction: CustomDebugStringConvertible, CoreStoreDeb
("context", self.context), ("context", self.context),
("supportsUndo", self.supportsUndo), ("supportsUndo", self.supportsUndo),
("bypassesQueueing", self.bypassesQueueing), ("bypassesQueueing", self.bypassesQueueing),
("isCommitted", self.isCommitted), ("isCommitted", self.isCommitted)
("result", self.result as Any)
) )
} }
} }
@@ -894,6 +972,80 @@ extension Where: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
} }
// MARK: - VersionLock
extension VersionLock: CustomStringConvertible, CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
// MARK: CustomStringConvertible
public var description: String {
var string = "["
if self.hashesByEntityName.isEmpty {
string.append(":]")
return string
}
for (index, keyValue) in self.hashesByEntityName.sorted(by: { $0.key < $1.key }).enumerated() {
let data = keyValue.value
let count = data.count
let bytes = data.withUnsafeBytes { (pointer: UnsafePointer<HashElement>) in
return (0 ..< (count / MemoryLayout<HashElement>.size))
.map({ "\("0x\(String(pointer[$0], radix: 16, uppercase: false))")" })
}
string.append("\(index == 0 ? "\n" : ",\n")\"\(keyValue.key)\": [\(bytes.joined(separator: ", "))]")
}
string.indent(1)
string.append("\n]")
return string
}
// MARK: CustomDebugStringConvertible
public var debugDescription: String {
return formattedDebugDescription(self)
}
// MARK: CoreStoreDebugStringConvertible
public var coreStoreDumpString: String {
return self.description
}
}
// MARK: - XcodeDataModelSchema
extension XcodeDataModelSchema: CustomDebugStringConvertible, CoreStoreDebugStringConvertible {
// MARK: CustomDebugStringConvertible
public var debugDescription: String {
return formattedDebugDescription(self)
}
// MARK: CoreStoreDebugStringConvertible
public var coreStoreDumpString: String {
return createFormattedString(
"(", ")",
("modelVersion", self.modelVersion),
("modelVersionFileURL", self.modelVersionFileURL),
("rawModel", self.rawModel())
)
}
}
// MARK: - Private: Utilities // MARK: - Private: Utilities
private typealias DumpInfo = [(key: String, value: Any)] private typealias DumpInfo = [(key: String, value: Any)]
@@ -941,15 +1093,6 @@ fileprivate extension String {
return String(repeating: " ", count: level * 4) return String(repeating: " ", count: level * 4)
} }
fileprivate func trimSwiftModuleName() -> String {
if self.hasPrefix("Swift.") {
return self.substring(from: "Swift.".endIndex)
}
return self
}
fileprivate mutating func indent(_ level: Int) { fileprivate mutating func indent(_ level: Int) {
self = self.replacingOccurrences(of: "\n", with: "\n\(String.indention(level))") self = self.replacingOccurrences(of: "\n", with: "\n\(String.indention(level))")
@@ -970,7 +1113,7 @@ public protocol CoreStoreDebugStringConvertible {
} }
// MARK: - Private: // MARK: - Standard Types:
extension Array: CoreStoreDebugStringConvertible { extension Array: CoreStoreDebugStringConvertible {
@@ -1063,6 +1206,8 @@ extension NSAttributeType: CoreStoreDebugStringConvertible {
case .binaryDataAttributeType: return ".binaryDataAttributeType" case .binaryDataAttributeType: return ".binaryDataAttributeType"
case .transformableAttributeType: return ".transformableAttributeType" case .transformableAttributeType: return ".transformableAttributeType"
case .objectIDAttributeType: return ".objectIDAttributeType" case .objectIDAttributeType: return ".objectIDAttributeType"
case .UUIDAttributeType: return ".UUIDAttributeType"
case .URIAttributeType: return ".URIAttributeType"
} }
} }
} }
@@ -1212,7 +1357,15 @@ extension NSSortDescriptor: CoreStoreDebugStringConvertible {
} }
} }
extension URL: CoreStoreDebugStringConvertible { extension NSString: CoreStoreDebugStringConvertible {
public var coreStoreDumpString: String {
return "\"\(self)\""
}
}
extension NSURL: CoreStoreDebugStringConvertible {
public var coreStoreDumpString: String { public var coreStoreDumpString: String {
@@ -1247,3 +1400,11 @@ extension String: CoreStoreDebugStringConvertible {
return "\"\(self)\"" return "\"\(self)\""
} }
} }
extension URL: CoreStoreDebugStringConvertible {
public var coreStoreDumpString: String {
return "\"\(self)\""
}
}
@@ -38,6 +38,7 @@ public extension CoreStore {
// MARK: Internal // MARK: Internal
@inline(__always)
internal static func log(_ level: LogLevel, message: String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) { internal static func log(_ level: LogLevel, message: String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) {
self.logger.log( self.logger.log(
@@ -49,6 +50,7 @@ public extension CoreStore {
) )
} }
@inline(__always)
internal static func log(_ error: CoreStoreError, _ message: String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) { internal static func log(_ error: CoreStoreError, _ message: String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) {
self.logger.log( self.logger.log(
@@ -60,7 +62,8 @@ public extension CoreStore {
) )
} }
internal static func assert( _ condition: @autoclosure () -> Bool, _ message: String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) { @inline(__always)
internal static func assert( _ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) {
self.logger.assert( self.logger.assert(
condition, condition,
@@ -71,6 +74,7 @@ public extension CoreStore {
) )
} }
@inline(__always)
internal static func abort(_ message: String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) -> Never { internal static func abort(_ message: String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) -> Never {
self.logger.abort( self.logger.abort(
@@ -31,27 +31,6 @@ import CoreData
public extension CoreStore { public extension CoreStore {
/**
Asynchronously adds a `StorageInterface` with default settings to the `defaultStack`. Migrations are also initiated by default.
```
CoreStore.addStorage(
InMemoryStore.self,
completion: { result in
switch result {
case .success(let storage): // ...
case .failure(let error): // ...
}
}
)
```
- parameter storeType: the storage type
- parameter completion: the closure to be executed on the main queue when the process completes, either due to success or failure. The closure's `SetupResult` argument indicates the result. Note that the `StorageInterface` associated to the `SetupResult.success` may not always be the same instance as the parameter argument if a previous `StorageInterface` was already added at the same URL and with the same configuration.
*/
public static func addStorage<T: StorageInterface>(_ storeType: T.Type, completion: @escaping (SetupResult<T>) -> Void) where T: DefaultInitializableStore {
self.defaultStack.addStorage(storeType.init(), completion: completion)
}
/** /**
Asynchronously adds a `StorageInterface` to the `defaultStack`. Migrations are also initiated by default. Asynchronously adds a `StorageInterface` to the `defaultStack`. Migrations are also initiated by default.
``` ```
@@ -68,33 +47,11 @@ public extension CoreStore {
- parameter storage: the storage - parameter storage: the storage
- parameter completion: the closure to be executed on the main queue when the process completes, either due to success or failure. The closure's `SetupResult` argument indicates the result. Note that the `StorageInterface` associated to the `SetupResult.success` may not always be the same instance as the parameter argument if a previous `StorageInterface` was already added at the same URL and with the same configuration. - parameter completion: the closure to be executed on the main queue when the process completes, either due to success or failure. The closure's `SetupResult` argument indicates the result. Note that the `StorageInterface` associated to the `SetupResult.success` may not always be the same instance as the parameter argument if a previous `StorageInterface` was already added at the same URL and with the same configuration.
*/ */
public static func addStorage<T: StorageInterface>(_ storage: T, completion: @escaping (SetupResult<T>) -> Void) { public static func addStorage<T>(_ storage: T, completion: @escaping (SetupResult<T>) -> Void) {
self.defaultStack.addStorage(storage, completion: completion) self.defaultStack.addStorage(storage, completion: completion)
} }
/**
Asynchronously adds a `LocalStorage` with default settings to the `defaultStack`. Migrations are also initiated by default.
```
let migrationProgress = CoreStore.addStorage(
SQLiteStore.self,
completion: { result in
switch result {
case .success(let storage): // ...
case .failure(let error): // ...
}
}
)
```
- parameter storeType: the local storage type
- parameter completion: the closure to be executed on the main queue when the process completes, either due to success or failure. The closure's `SetupResult` argument indicates the result. Note that the `LocalStorage` associated to the `SetupResult.success` may not always be the same instance as the parameter argument if a previous `LocalStorage` was already added at the same URL and with the same configuration.
- returns: a `Progress` instance if a migration has started, or `nil` if either no migrations are required or if a failure occured.
*/
public static func addStorage<T: LocalStorage>(_ storeType: T.Type, completion: @escaping (SetupResult<T>) -> Void) -> Progress? where T: DefaultInitializableStore {
return self.defaultStack.addStorage(storeType.init(), completion: completion)
}
/** /**
Asynchronously adds a `LocalStorage` to the `defaultStack`. Migrations are also initiated by default. Asynchronously adds a `LocalStorage` to the `defaultStack`. Migrations are also initiated by default.
``` ```
+236
View File
@@ -0,0 +1,236 @@
//
// CoreStore+Observing.swift
// CoreStore
//
// Copyright © 2015 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import Foundation
import CoreData
// MARK: - CoreStore
@available(OSX 10.12, *)
public extension CoreStore {
/**
Using the `defaultStack`, creates an `ObjectMonitor` for the specified `DynamicObject`. Multiple `ObjectObserver`s may then register themselves to be notified when changes are made to the `DynamicObject`.
- parameter object: the `DynamicObject` to observe changes from
- returns: a `ObjectMonitor` that monitors changes to `object`
*/
public static func monitorObject<D>(_ object: D) -> ObjectMonitor<D> {
return self.defaultStack.monitorObject(object)
}
/**
Using the `defaultStack`, creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: a `ListMonitor` instance that monitors changes to the list
*/
public static func monitorList<D>(_ from: From<D>, _ fetchClauses: FetchClause...) -> ListMonitor<D> {
return self.defaultStack.monitorList(from, fetchClauses)
}
/**
Using the `defaultStack`, creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: a `ListMonitor` instance that monitors changes to the list
*/
public static func monitorList<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) -> ListMonitor<D> {
return self.defaultStack.monitorList(from, fetchClauses)
}
/**
Creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses.
```
let monitor = CoreStore.monitorList(
From<MyPersonEntity>()
.where(\.age > 18)
.orderBy(.ascending(\.age))
)
```
- parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses
- returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType`
*/
public static func monitorList<B: FetchChainableBuilderType>(_ clauseChain: B) -> ListMonitor<B.ObjectType> {
return self.defaultStack.monitorList(clauseChain.from, clauseChain.fetchClauses)
}
/**
Using the `defaultStack`, asynchronously creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed.
- parameter createAsynchronously: the closure that receives the created `ListMonitor` instance
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
*/
public static func monitorList<D>(createAsynchronously: @escaping (ListMonitor<D>) -> Void, _ from: From<D>, _ fetchClauses: FetchClause...) {
self.defaultStack.monitorList(createAsynchronously: createAsynchronously, from, fetchClauses)
}
/**
Using the `defaultStack`, asynchronously creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed.
- parameter createAsynchronously: the closure that receives the created `ListMonitor` instance
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
*/
public static func monitorList<D>(createAsynchronously: @escaping (ListMonitor<D>) -> Void, _ from: From<D>, _ fetchClauses: [FetchClause]) {
self.defaultStack.monitorList(createAsynchronously: createAsynchronously, from, fetchClauses)
}
/**
Asynchronously creates a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed.
```
CoreStore.monitorList(
{ (monitor) in
self.monitor = monitor
},
From<MyPersonEntity>()
.where(\.age > 18)
.orderBy(.ascending(\.age))
)
```
- parameter createAsynchronously: the closure that receives the created `ListMonitor` instance
- parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses
- returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType`
*/
public static func monitorList<B: FetchChainableBuilderType>(createAsynchronously: @escaping (ListMonitor<B.ObjectType>) -> Void, _ clauseChain: B) {
self.defaultStack.monitorList(
createAsynchronously: createAsynchronously,
clauseChain.from,
clauseChain.fetchClauses
)
}
/**
Using the `defaultStack`, creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
- parameter from: a `From` clause indicating the entity type
- parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: a `ListMonitor` instance that monitors changes to the list
*/
public static func monitorSectionedList<D>(_ from: From<D>, _ sectionBy: SectionBy<D>, _ fetchClauses: FetchClause...) -> ListMonitor<D> {
return self.defaultStack.monitorSectionedList(from, sectionBy, fetchClauses)
}
/**
Using the `defaultStack`, creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
- parameter from: a `From` clause indicating the entity type
- parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: a `ListMonitor` instance that monitors changes to the list
*/
public static func monitorSectionedList<D>(_ from: From<D>, _ sectionBy: SectionBy<D>, _ fetchClauses: [FetchClause]) -> ListMonitor<D> {
return self.defaultStack.monitorSectionedList(from, sectionBy, fetchClauses)
}
/**
Creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` built from a chain of clauses.
```
let monitor = CoreStore.monitorSectionedList(
From<MyPersonEntity>()
.sectionBy(\.age, { "\($0!) years old" })
.where(\.age > 18)
.orderBy(.ascending(\.age))
)
```
- parameter clauseChain: a `SectionMonitorBuilderType` built from a chain of clauses
- returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType`
*/
public static func monitorSectionedList<B: SectionMonitorBuilderType>(_ clauseChain: B) -> ListMonitor<B.ObjectType> {
return self.defaultStack.monitorSectionedList(
clauseChain.from,
clauseChain.sectionBy,
clauseChain.fetchClauses
)
}
/**
Using the `defaultStack`, asynchronously creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed.
- parameter createAsynchronously: the closure that receives the created `ListMonitor` instance
- parameter from: a `From` clause indicating the entity type
- parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
*/
public static func monitorSectionedList<D>(createAsynchronously: @escaping (ListMonitor<D>) -> Void, _ from: From<D>, _ sectionBy: SectionBy<D>, _ fetchClauses: FetchClause...) {
self.defaultStack.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses)
}
/**
Using the `defaultStack`, asynchronously creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Since `NSFetchedResultsController` greedily locks the persistent store on initial fetch, you may prefer this method instead of the synchronous counterpart to avoid deadlocks while background updates/saves are being executed.
- parameter createAsynchronously: the closure that receives the created `ListMonitor` instance
- parameter from: a `From` clause indicating the entity type
- parameter sectionBy: a `SectionBy` clause indicating the keyPath for the attribute to use when sorting the list into sections.
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
*/
public static func monitorSectionedList<D>(createAsynchronously: @escaping (ListMonitor<D>) -> Void, _ from: From<D>, _ sectionBy: SectionBy<D>, _ fetchClauses: [FetchClause]) {
self.defaultStack.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses)
}
/**
Asynchronously creates a `ListMonitor` for a sectioned list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType` built from a chain of clauses.
```
CoreStore.monitorSectionedList(
{ (monitor) in
self.monitor = monitor
},
From<MyPersonEntity>()
.sectionBy(\.age, { "\($0!) years old" })
.where(\.age > 18)
.orderBy(.ascending(\.age))
)
```
- parameter clauseChain: a `SectionMonitorBuilderType` built from a chain of clauses
- returns: a `ListMonitor` for a list of `DynamicObject`s that satisfy the specified `SectionMonitorBuilderType`
*/
public static func monitorSectionedList<B: SectionMonitorBuilderType>(createAsynchronously: @escaping (ListMonitor<B.ObjectType>) -> Void, _ clauseChain: B) {
self.defaultStack.monitorSectionedList(
createAsynchronously: createAsynchronously,
clauseChain.from,
clauseChain.sectionBy,
clauseChain.fetchClauses
)
}
}
+389
View File
@@ -0,0 +1,389 @@
//
// CoreStore+Querying.swift
// CoreStore
//
// Copyright © 2015 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import Foundation
import CoreData
// MARK: - CoreStore
public extension CoreStore {
/**
Using the `defaultStack`, fetches the `DynamicObject` instance in the `DataStack`'s context from a reference created from a transaction or from a different managed object context.
- parameter object: a reference to the object created/fetched outside the `DataStack`
- returns: the `DynamicObject` instance if the object exists in the `DataStack`, or `nil` if not found.
*/
public static func fetchExisting<D: DynamicObject>(_ object: D) -> D? {
return self.defaultStack.fetchExisting(object)
}
/**
Using the `defaultStack`, fetches the `DynamicObject` instance in the `DataStack`'s context from an `NSManagedObjectID`.
- parameter objectID: the `NSManagedObjectID` for the object
- returns: the `DynamicObject` instance if the object exists in the `DataStack`, or `nil` if not found.
*/
public static func fetchExisting<D: DynamicObject>(_ objectID: NSManagedObjectID) -> D? {
return self.defaultStack.fetchExisting(objectID)
}
/**
Using the `defaultStack`, fetches the `DynamicObject` instances in the `DataStack`'s context from references created from a transaction or from a different managed object context.
- parameter objects: an array of `DynamicObject`s created/fetched outside the `DataStack`
- returns: the `DynamicObject` array for objects that exists in the `DataStack`
*/
public static func fetchExisting<D: DynamicObject, S: Sequence>(_ objects: S) -> [D] where S.Iterator.Element == D {
return self.defaultStack.fetchExisting(objects)
}
/**
Using the `defaultStack`, fetches the `DynamicObject` instances in the `DataStack`'s context from a list of `NSManagedObjectID`.
- parameter objectIDs: the `NSManagedObjectID` array for the objects
- returns: the `DynamicObject` array for objects that exists in the `DataStack`
*/
public static func fetchExisting<D: DynamicObject, S: Sequence>(_ objectIDs: S) -> [D] where S.Iterator.Element == NSManagedObjectID {
return self.defaultStack.fetchExisting(objectIDs)
}
/**
Using the `defaultStack`, fetches the first `DynamicObject` instance that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the first `DynamicObject` instance that satisfies the specified `FetchClause`s
*/
public static func fetchOne<D>(_ from: From<D>, _ fetchClauses: FetchClause...) -> D? {
return self.defaultStack.fetchOne(from, fetchClauses)
}
/**
Using the `defaultStack`, fetches the first `DynamicObject` instance that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the first `DynamicObject` instance that satisfies the specified `FetchClause`s
*/
public static func fetchOne<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) -> D? {
return self.defaultStack.fetchOne(from, fetchClauses)
}
/**
Fetches the first `DynamicObject` instance that satisfies the specified `FetchChainableBuilderType` built from a chain of clauses.
```
let youngestTeen = CoreStore.fetchOne(
From<MyPersonEntity>()
.where(\.age > 18)
.orderBy(.ascending(\.age))
)
```
- parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses
- returns: the first `DynamicObject` instance that satisfies the specified `FetchChainableBuilderType`
*/
public static func fetchOne<B: FetchChainableBuilderType>(_ clauseChain: B) -> B.ObjectType? {
return self.defaultStack.fetchOne(clauseChain)
}
/**
Using the `defaultStack`, fetches all `DynamicObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: all `DynamicObject` instances that satisfy the specified `FetchClause`s
*/
public static func fetchAll<D>(_ from: From<D>, _ fetchClauses: FetchClause...) -> [D]? {
return self.defaultStack.fetchAll(from, fetchClauses)
}
/**
Using the `defaultStack`, fetches all `DynamicObject` instances that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: all `DynamicObject` instances that satisfy the specified `FetchClause`s
*/
public static func fetchAll<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) -> [D]? {
return self.defaultStack.fetchAll(from, fetchClauses)
}
/**
Fetches all `DynamicObject` instances that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses.
```
let people = CoreStore.fetchAll(
From<MyPersonEntity>()
.where(\.age > 18)
.orderBy(.ascending(\.age))
)
```
- parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses
- returns: all `DynamicObject` instances that satisfy the specified `FetchChainableBuilderType`
*/
public static func fetchAll<B: FetchChainableBuilderType>(_ clauseChain: B) -> [B.ObjectType]? {
return self.defaultStack.fetchAll(clauseChain)
}
/**
Using the `defaultStack`, fetches the number of `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the number `DynamicObject`s that satisfy the specified `FetchClause`s
*/
public static func fetchCount<D>(_ from: From<D>, _ fetchClauses: FetchClause...) -> Int? {
return self.defaultStack.fetchCount(from, fetchClauses)
}
/**
Using the `defaultStack`, fetches the number of `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the number `DynamicObject`s that satisfy the specified `FetchClause`s
*/
public static func fetchCount<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) -> Int? {
return self.defaultStack.fetchCount(from, fetchClauses)
}
/**
Fetches the number of `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses.
```
let numberOfAdults = CoreStore.fetchCount(
From<MyPersonEntity>()
.where(\.age > 18)
.orderBy(.ascending(\.age))
)
```
- parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses
- returns: the number `DynamicObject`s that satisfy the specified `FetchChainableBuilderType`
*/
public static func fetchCount<B: FetchChainableBuilderType>(_ clauseChain: B) -> Int? {
return self.defaultStack.fetchCount(clauseChain)
}
/**
Using the `defaultStack`, fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s
*/
public static func fetchObjectID<D>(_ from: From<D>, _ fetchClauses: FetchClause...) -> NSManagedObjectID? {
return self.defaultStack.fetchObjectID(from, fetchClauses)
}
/**
Using the `defaultStack`, fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchClause`s
*/
public static func fetchObjectID<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? {
return self.defaultStack.fetchObjectID(from, fetchClauses)
}
/**
Fetches the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchChainableBuilderType` built from a chain of clauses.
```
let youngestTeenID = CoreStore.fetchObjectID(
From<MyPersonEntity>()
.where(\.age > 18)
.orderBy(.ascending(\.age))
)
```
- parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses
- returns: the `NSManagedObjectID` for the first `DynamicObject` that satisfies the specified `FetchChainableBuilderType`
*/
public static func fetchObjectID<B: FetchChainableBuilderType>(_ clauseChain: B) -> NSManagedObjectID? {
return self.defaultStack.fetchObjectID(clauseChain)
}
/**
Using the `defaultStack`, fetches the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s
*/
public static func fetchObjectIDs<D>(_ from: From<D>, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? {
return self.defaultStack.fetchObjectIDs(from, fetchClauses)
}
/**
Using the `defaultStack`, fetches the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- parameter from: a `From` clause indicating the entity type
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchClause`s
*/
public static func fetchObjectIDs<D>(_ from: From<D>, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? {
return self.defaultStack.fetchObjectIDs(from, fetchClauses)
}
/**
Fetches the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchChainableBuilderType` built from a chain of clauses.
```
let idsOfAdults = transaction.fetchObjectIDs(
From<MyPersonEntity>()
.where(\.age > 18)
.orderBy(.ascending(\.age))
)
```
- parameter clauseChain: a `FetchChainableBuilderType` built from a chain of clauses
- returns: the `NSManagedObjectID` for all `DynamicObject`s that satisfy the specified `FetchChainableBuilderType`
*/
public static func fetchObjectIDs<B: FetchChainableBuilderType>(_ clauseChain: B) -> [NSManagedObjectID]? {
return self.defaultStack.fetchObjectIDs(clauseChain)
}
/**
Using the `defaultStack`, queries aggregate values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
- parameter from: a `From` clause indicating the entity type
- parameter selectClause: a `Select<U>` clause indicating the properties to fetch, and with the generic type indicating the return type.
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
- returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/
public static func queryValue<D, U: QueryableAttributeType>(_ from: From<D>, _ selectClause: Select<D, U>, _ queryClauses: QueryClause...) -> U? {
return self.defaultStack.queryValue(from, selectClause, queryClauses)
}
/**
Using the `defaultStack`, queries aggregate values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
- parameter from: a `From` clause indicating the entity type
- parameter selectClause: a `Select<U>` clause indicating the properties to fetch, and with the generic type indicating the return type.
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
- returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/
public static func queryValue<D, U: QueryableAttributeType>(_ from: From<D>, _ selectClause: Select<D, U>, _ queryClauses: [QueryClause]) -> U? {
return self.defaultStack.queryValue(from, selectClause, queryClauses)
}
/**
Queries a property value or aggregate as specified by the `QueryChainableBuilderType` built from a chain of clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
```
let averageAdultAge = CoreStore.queryValue(
From<MyPersonEntity>()
.select(Int.self, .average(\.age))
.where(\.age > 18)
)
```
- parameter clauseChain: a `QueryChainableBuilderType` indicating the property/aggregate to fetch and the series of queries for the request.
- returns: the result of the the query as specified by the `QueryChainableBuilderType`
*/
public static func queryValue<B: QueryChainableBuilderType>(_ clauseChain: B) -> B.ResultType? where B.ResultType: QueryableAttributeType {
return self.defaultStack.queryValue(clauseChain)
}
/**
Using the `defaultStack`, queries a dictionary of attribtue values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
- parameter from: a `From` clause indicating the entity type
- parameter selectClause: a `Select<U>` clause indicating the properties to fetch, and with the generic type indicating the return type.
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
- returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/
public static func queryAttributes<D>(_ from: From<D>, _ selectClause: Select<D, NSDictionary>, _ queryClauses: QueryClause...) -> [[String: Any]]? {
return self.defaultStack.queryAttributes(from, selectClause, queryClauses)
}
/**
Using the `defaultStack`, queries a dictionary of attribute values as specified by the `QueryClause`s. Requires at least a `Select` clause, and optional `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
- parameter from: a `From` clause indicating the entity type
- parameter selectClause: a `Select<U>` clause indicating the properties to fetch, and with the generic type indicating the return type.
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses.
- returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/
public static func queryAttributes<D>(_ from: From<D>, _ selectClause: Select<D, NSDictionary>, _ queryClauses: [QueryClause]) -> [[String: Any]]? {
return self.defaultStack.queryAttributes(from, selectClause, queryClauses)
}
/**
Queries a dictionary of attribute values or as specified by the `QueryChainableBuilderType` built from a chain of clauses.
A "query" differs from a "fetch" in that it only retrieves values already stored in the persistent store. As such, values from unsaved transactions or contexts will not be incorporated in the query result.
```
let results = CoreStore.queryAttributes(
From<MyPersonEntity>()
.select(
NSDictionary.self,
.attribute(\.age, as: "age"),
.count(\.age, as: "numberOfPeople")
)
.groupBy(\.age)
)
for dictionary in results! {
let age = dictionary["age"] as! Int
let count = dictionary["numberOfPeople"] as! Int
print("There are \(count) people who are \(age) years old."
}
```
- parameter clauseChain: a `QueryChainableBuilderType` indicating the properties to fetch and the series of queries for the request.
- returns: the result of the the query as specified by the `QueryChainableBuilderType`
*/
public static func queryAttributes<B: QueryChainableBuilderType>(_ clauseChain: B) -> [[String: Any]]? where B.ResultType == NSDictionary {
return self.defaultStack.queryAttributes(clauseChain)
}
}

Some files were not shown because too many files have changed in this diff Show More