Compare commits

..

185 Commits

Author SHA1 Message Date
John Rommel Estropia f617f2cc53 Simpler API for creating CoreStore-managed NSFetchedResultsControllers, useful for external SDK's that require them 2016-05-28 01:47:48 +09:00
John Rommel Estropia f396b01043 fixed Cartfile dependency 2016-05-26 01:28:26 +09:00
John Rommel Estropia e38093342d version bump, updated dependendencies 2016-05-24 00:42:35 +09:00
John Rommel Estropia c15afcb381 Allow flushing UnsafeDataTransactions to notify its ListMonitors and ObjectMonitors without saving changes to the DataStack (fixes #71) 2016-05-24 00:40:13 +09:00
John Rommel Estropia a97001cdbb fixed scheme 2016-05-24 00:32:54 +09:00
John Estropia c8ca16a982 Merge pull request #69 from bretsko/master
fixed Readme
2016-05-17 14:05:11 +09:00
Oleksandr Bretsko 9e0493a20d fixed Readme
replaced importObjects with importUniqueObjects
2016-05-17 08:43:40 +04:00
John Rommel Estropia c990f7764a version bump 2016-05-07 12:19:30 +08:00
John Rommel Estropia 26ae6293ca merge changes to main context synchronously or asynchronously depending on the caller intention (fixes #65) 2016-05-07 12:18:54 +08:00
John Rommel Estropia b9a2f96d6e renamed refreshAllObjectsAsFaults to refreshAndMergeAllObjects, which is more correct 2016-05-07 12:04:59 +08:00
John Rommel Estropia 74fe81bfa7 Fix problem when using carthage where the iOS7 framework overwrites the iOS 8 framework 2016-05-02 12:02:31 +08:00
John Rommel Estropia dff552b95f version bump 2016-04-01 07:42:00 +09:00
John Rommel Estropia 88cac73b20 fix assertion error 2016-04-01 07:41:23 +09:00
John Rommel Estropia 0c564add46 separate CoreStoreFetchRequest file 2016-04-01 07:04:02 +09:00
John Rommel Estropia 784a315fb9 version bump 2016-04-01 01:32:32 +09:00
John Rommel Estropia 44cfbebedb prevent deadlock on when DataStack gets deallocated 2016-04-01 01:28:39 +09:00
John Rommel Estropia 0b24072259 Workaround CoreData's bug when using NSFetchRequest's affectedStores property 2016-04-01 01:22:16 +09:00
John Rommel Estropia 633ab0a249 cache fetchClauses in property so closures do not retain them indeterminately 2016-03-30 21:16:10 +09:00
John Rommel Estropia 410feda5cd tidy up 2016-03-30 20:47:13 +09:00
John Rommel Estropia 06e952af8a clear FRC delegate on deinit 2016-03-30 20:28:10 +09:00
John Estropia 48a8694720 attempt to fix closure deallocation bug (#58) 2016-03-30 05:22:05 +09:00
John Estropia 11ac362dcc Update README.md 2016-03-27 20:32:14 +09:00
John Estropia 10c20e0d62 Update README.md 2016-03-27 10:28:35 +09:00
John Estropia b914a4b5ed updated Readme 2016-03-25 18:20:24 +09:00
John Estropia 7de13131a4 small demo fix 2016-03-25 18:18:17 +09:00
John Estropia 0c1af09a8d Make demo app compile again (sorry) (fixes #57) 2016-03-25 18:08:03 +09:00
John Rommel Estropia 9f1a351311 add missing scheme 2016-03-23 23:20:24 +09:00
John Rommel Estropia 6e202aa7ca set missing build setting 2016-03-23 22:31:16 +09:00
John Rommel Estropia 783b933294 add missing file 2016-03-23 22:26:39 +09:00
John Rommel Estropia ca49ea3a81 iOS 7 full support 2016-03-23 22:21:05 +09:00
John Estropia 928585029d XCode 7.3 full support 2016-03-23 12:39:11 +09:00
John Estropia dc6d22b6ff updated travis XCode version 2016-03-23 12:14:34 +09:00
John Estropia 5ca0f657cb XCode 7.3 fixes 2016-03-23 12:07:31 +09:00
John Rommel Estropia c323a28c19 fix warnings in demo app 2016-03-12 09:27:59 +09:00
John Estropia 64ad7b3bfd Update README.md 2016-03-11 20:59:52 +09:00
John Estropia df441c5d04 Update README.md 2016-03-11 20:58:33 +09:00
John Estropia 3c2fb28644 added Packaged.swift 2016-03-01 19:42:28 +09:00
John Estropia e66a13632a Merge branch 'develop' 2016-02-25 15:52:59 +09:00
John Estropia 4c78a309bc deprecated NSFetchedResultsController initializer in favor of static factory method 2016-02-25 15:52:49 +09:00
John Estropia 9055daa58c Merge branch 'develop' 2016-02-25 15:11:40 +09:00
John Estropia 3905423038 fix compile error 2016-02-25 15:05:27 +09:00
John Estropia 15353268e2 Allow unsafe transactions to save synchronously 2016-02-25 15:04:05 +09:00
John Estropia c12331f403 Merge pull request #53 from mpurbo/patch-1
added missing closing bracket on sample code.
2016-02-22 22:26:38 +09:00
Mamad Purbo a55a8d389c added missing closing bracket on sample code. 2016-02-22 11:50:28 +09:00
John Rommel Estropia 6e88b14237 fixed readme build status 2016-02-20 12:30:02 +09:00
John Rommel Estropia 53700a267f version bump 2016-02-20 12:28:39 +09:00
John Rommel Estropia 6c95b010e9 utility to update an existing ImportableObject from its ImportSource (fixes #52) 2016-02-20 12:26:23 +09:00
John Rommel Estropia 1af675e8b7 tidy up 2016-02-20 12:05:01 +09:00
John Rommel Estropia b85f16cf68 Merge branch 'master' into develop 2016-02-20 01:25:40 +09:00
John Estropia 37bf4179c8 renamed "Incremental Migrations" to "Progressive Migrations" to match the common term used by the community 2016-02-17 15:34:33 +09:00
John Estropia 240dda6ad6 Merge pull request #50 from ReadmeCritic/master
Correct the spelling of Xcode in README
2016-02-17 10:03:47 +09:00
ReadmeCritic ffe73d0f66 Correct the spelling of Xcode in README 2016-02-16 11:47:36 -08:00
John Rommel Estropia 43c4998e20 fix podspec 2016-02-17 02:32:39 +09:00
John Rommel Estropia 0c4e55f061 Merge branch 'develop' 2016-02-17 02:06:57 +09:00
John Rommel Estropia 8c8953c507 updated .travis.yml 2016-02-17 01:57:28 +09:00
John Rommel Estropia 66892f22a3 comments cleanup 2016-02-17 01:38:26 +09:00
John Rommel Estropia def105e73a added utility to query transactions about pending inserts, updates, and deletes 2016-02-17 00:00:25 +09:00
John Rommel Estropia 57bf123fb2 assert missing classes in model 2016-02-16 07:52:34 +09:00
John Rommel Estropia 8efd6572f0 Refetch monitors when persistent stores are added/removed. Allow unsafe transactions to create their own monitors 2016-02-15 07:56:17 +09:00
John Estropia 405e861907 Merge pull request #47 from ReadmeCritic/master
Correct the spelling of CocoaPods in README
2016-02-12 10:37:49 +09:00
ReadmeCritic 74c48c7486 Correct the spelling of CocoaPods in README 2016-02-11 09:45:40 -08:00
John Rommel Estropia e58b3b0131 fixed some typos in the README 2016-02-11 09:25:32 +09:00
John Rommel Estropia b8fad66bfa tidy up: copyright symbol 2016-02-01 01:07:07 +09:00
John Rommel Estropia ff1155dac3 fix OSX compiler error 2016-01-31 23:27:29 +09:00
John Rommel Estropia 5137649b78 Fix for Swift 2.2 compatibility 2016-01-31 17:23:34 +09:00
John Rommel Estropia 7a95267a9c updated Readme 2016-01-26 12:34:06 +09:00
John Rommel Estropia c07435e866 Added utility for re-faulting all objects in a transaction/dataStack 2016-01-26 12:22:30 +09:00
John Rommel Estropia f5627f0855 fixed demo app framework settings 2016-01-23 01:52:27 +09:00
John Estropia d3ea655eb9 Merge branch 'master' into develop
Conflicts:
	README.md
2016-01-19 12:26:37 +09:00
John Estropia fc83180af8 updated README 2016-01-19 12:25:58 +09:00
John Estropia 0c6246475a update README 2016-01-19 12:24:14 +09:00
John Estropia 087480a3a8 update cartfile 2016-01-19 12:22:14 +09:00
John Estropia 83a04e669e updated .travis.yml 2016-01-19 11:55:22 +09:00
John Estropia d05522bb20 tidy up, set default directory to Caches folder on tvOS 2016-01-19 11:38:11 +09:00
Cihat Gündüz 9081b36cca Add tvOS target + Configure target + Add shared scheme for tvOS 2016-01-17 13:30:45 +01:00
Cihat Gündüz 9322371224 Use tvOS_support feature branch of GCDKit (Carthage + git submodules) 2016-01-17 13:11:58 +01:00
John Estropia 26ab6aacd7 exposed utility for extracting the parent transaction for objects created from UnsafeDataTransactions 2016-01-14 17:54:58 +09:00
John Estropia 3e601c1328 tidy up (WIP: queue check for NSManagedObjectContext property updates) 2016-01-08 20:44:42 +09:00
John Estropia 762b877879 Merge pull request #35 from JohnEstropia/modify-gitignore
Update .gitignore: Add .DS_Store
2016-01-08 18:41:29 +09:00
Hiroshi Kimura 084bdc431f Update .gitignore: Add .DS_Store 2016-01-08 18:40:16 +09:00
John Estropia c63bc389b2 updated travis.yml 2016-01-06 19:29:58 +09:00
John Estropia 71c3abc4f3 Let ListMonitor expose methods for Section Indexes (fixes #32) 2016-01-06 19:16:46 +09:00
John Estropia 863d4d1d5a Merge branch 'master' into develop
# Conflicts:
#	README.md
2016-01-06 18:45:45 +09:00
John Estropia 06d177e8bd Update README.md 2016-01-04 19:38:42 +09:00
John Estropia c2bbd537cf Update README.md 2016-01-04 19:38:11 +09:00
John Estropia 761d349b97 Rename CartFile to Cartfile. Sorry about that 2016-01-04 12:38:05 +09:00
John Rommel Estropia 6c28594e41 fixed shouldUpdateFromImportSource() not called from importUniqueObject() (fixes #31) 2015-12-29 08:25:06 +08:00
John Estropia c229af19a2 added GCDKit to Carthage installation guide 2015-12-18 19:28:02 +09:00
John Estropia 8b8a7c7b08 fix macOSX development target 2015-12-16 14:01:56 +09:00
John Estropia eb828d8e42 update deployment version 2015-12-15 21:20:56 +09:00
John Estropia 88a24540c6 remove IPHONEOS_DEPLOYMENT_TARGET flag 2015-12-15 20:51:48 +09:00
John Estropia 74ded8fb7d update .travis.yml 2015-12-15 19:58:59 +09:00
John Estropia 969f4cefb4 use workspaces for better carthage support 2015-12-15 19:56:07 +09:00
John Estropia 1d2947ad26 added Carthage dependency 2015-12-08 18:34:10 +09:00
John Estropia 49274c3c94 fix swift project settings 2015-12-08 16:35:50 +09:00
John Rommel Estropia c0fbb2655b version bump 2015-12-06 20:32:06 +09:00
John Rommel Estropia cefd6f6cbc fix watchOS travis configuration 2015-12-05 20:28:24 +09:00
John Rommel Estropia 2653f7b977 fix watchOS travis configuration 2015-12-05 20:13:52 +09:00
John Rommel Estropia c1163283f5 fix watchOS travis configuration 2015-12-05 20:03:21 +09:00
John Rommel Estropia 529347b6a2 fix watchOS travis configuration 2015-12-05 19:48:40 +09:00
John Rommel Estropia eba7e99374 fixed yml 2015-12-05 19:35:23 +09:00
John Rommel Estropia 3c4350bd5d enable testability 2015-12-05 19:30:04 +09:00
John Rommel Estropia 88b13189e5 fix yml 2015-12-05 19:23:12 +09:00
John Rommel Estropia 03da3544f6 change GCDKit submodule path to public url to make sir Travis happy 2015-12-05 19:20:27 +09:00
John Rommel Estropia 648b59e1ee fix yml 2015-12-05 19:14:35 +09:00
John Rommel Estropia ab3c8ca812 tidy up 2015-12-05 19:01:07 +09:00
John Rommel Estropia 900a31c541 added build status to readme 2015-12-05 18:55:20 +09:00
John Rommel Estropia 656a107767 test travis ci 2015-12-05 18:50:54 +09:00
John Rommel Estropia 4ce3d5de3c undo interface 2015-12-05 18:21:21 +09:00
John Rommel Estropia dec9757dc2 Merge branch 'develop' of github.com:JohnEstropia/CoreStore into develop 2015-12-05 18:19:33 +09:00
John Estropia 578e4966fc added a convenience initializer for clients that only support NSFetchedResultsController (i.e. Objective-C) 2015-11-24 19:12:04 +09:00
John Estropia b741626574 updated GCDKit 2015-11-24 17:50:23 +09:00
John Estropia f79a77ab78 remove unneeded imports in demo app 2015-11-24 17:02:23 +09:00
John Estropia 1f2a70fd42 minor fixes 2015-11-24 16:06:48 +09:00
John Estropia 718d2c9b7d Added ability to initialize ListMonitors asynchronously. This is a deadlock-preventive measure for apps that heavily recreates ListMonitors while updates and saves are running in the background (because NSFetchedResultController's performFetch() locks the whole NSManagedObjectContext chain up until the NSPersistentStoreCoordinator) 2015-11-24 14:49:43 +09:00
John Estropia c5ff02335e debug flag 2015-11-24 12:32:10 +09:00
John Estropia 66b57faa44 Merge branch 'master' into develop 2015-11-24 11:34:35 +09:00
John Estropia eef5a3d80b allow CoreStore installation both through frameworks or through direct linking 2015-11-24 11:33:52 +09:00
John Estropia bc0757cf06 README: Added link to poll for new features 2015-11-18 19:46:17 +09:00
John Rommel Estropia 0c0a2a382c Deprecated rollback() on async and sync transactions. Added undo utilities to unsafe transactions. 2015-11-14 20:00:40 +09:00
John Rommel Estropia 05b4a7092a updated podspec for OSX 2015-10-30 01:46:47 +09:00
John Rommel Estropia 6aed070e7c version bump for OSX support 2015-10-30 01:36:15 +09:00
John Estropia fcb1d7cbbc OSX support!!!11 2015-10-29 17:00:10 +09:00
John Estropia d074aad111 Merge branch 'master' into develop 2015-10-26 14:42:31 +09:00
John Estropia b7685dc747 allow Sequences of NSManagedObject subclasses as argument to delete() method 2015-10-26 14:41:58 +09:00
John Rommel Estropia a185bc96c0 updated README 2015-10-24 11:41:13 +09:00
John Rommel Estropia 91aef44803 fix pod dependency 2015-10-23 00:49:34 +09:00
John Rommel Estropia dcfb09eda7 attempt sight improvement for the FRC bug workaround #20 2015-10-23 00:40:38 +09:00
John Estropia 9ca83d9c5d watchOS support (fixes #19) 2015-10-20 21:01:10 +09:00
John Estropia b00eaf2d0b fix warnings on watchOS 2015-10-20 20:54:09 +09:00
John Estropia dc8bdf3bad watch OS support 2015-10-20 20:38:58 +09:00
John Estropia 4792c4462e fix section index titles when refetching a listMonitor 2015-10-15 18:29:08 +09:00
John Estropia 524757a7cf just a safety measure for the rare case that the FRC delegate gets released earlier than the FRC 2015-10-13 10:47:48 +09:00
John Rommel Estropia 0b6298a802 tidy up 2015-10-07 00:57:48 +09:00
John Estropia ae77558ae8 Merge pull request #16 from mac-cain13/patch-1
Fix FRCdelegate issue where invalid type is passed
2015-10-07 00:44:15 +09:00
Mathijs Kadijk d33aa9c5cf Fix FRCdelegate issue where invalid type is passed
See also https://forums.developer.apple.com/thread/12184#31850
2015-10-06 15:30:22 +02:00
John Estropia d89319d324 Workaround for Xcode 7.0.1+iOS 9 FRC bug 2015-09-30 11:41:27 +09:00
John Estropia cf9af6eef5 Merge branch 'develop' 2015-09-28 20:46:14 +09:00
John Estropia b9ec66f425 fix entityNameMapping bug 2015-09-28 20:44:19 +09:00
John Estropia 2a8df0596d Update README.md 2015-09-24 23:47:06 +09:00
John Rommel Estropia 83e6a41d88 Merge branch 'master' into develop 2015-09-22 11:40:28 +09:00
John Rommel Estropia c6fe494fe1 updated podspec dependency 2015-09-21 23:30:41 +09:00
John Rommel Estropia 7771e047e2 updated GCDKit version to remove warnings 2015-09-21 23:23:22 +09:00
John Rommel Estropia 5b0439835b Deprecated DetachedDataTransaction in favor of UnsafeDataTransaction. beginDetached() methods are also deprecated; use beginUnsafe() instead. 2015-09-21 15:08:46 +09:00
John Rommel Estropia 622c5aa652 Tighter generics implementations. You can now pass any SequenceType's for methods that previously only accepts Array's. 2015-09-19 19:45:01 +09:00
John Rommel Estropia 114b7ce605 Tighter generics implementations. You can now pass any SequenceType's for methods that previously only accepts Array's. 2015-09-19 18:20:52 +09:00
John Rommel Estropia 7451fbe026 added README sections about ImportableObject and ImportableUniqueObject, as well as the new fetchExisting() method usage 2015-09-19 16:23:59 +09:00
John Rommel Estropia ee2398fcdf updated readme 2015-09-18 07:14:41 +09:00
John Rommel Estropia 8d26501040 WIP: Readme 2015-09-14 07:44:23 +09:00
John Estropia 0709fe95cf use transaction queue for refetching instead of a global queue 2015-09-11 15:06:36 +09:00
John Estropia 656a99fe12 added thread safety checks to ListMonitor to prevent deadlocks after calling refetch() 2015-09-11 13:59:28 +09:00
John Estropia 726e0eabe9 dispatch_group seems more semantically fitting than semaphore 2015-09-11 13:50:29 +09:00
John Estropia 032764b9b7 tidy up 2015-09-10 16:59:55 +09:00
John Estropia 5461bb0736 workaround an NSFetchedResultsController bug in Xcode 7 targeted on iOS 8 devices where errant index paths cause crashes 2015-09-10 16:57:35 +09:00
John Estropia 4c6bc4f494 completed appledocs for importing methods 2015-09-10 13:28:53 +09:00
John Rommel Estropia 63a43a6487 WIP: documentation 2015-09-10 07:21:53 +09:00
John Estropia 6a006d5d7c added a flag to inspect the current state of a refetch 2015-09-08 18:12:52 +09:00
John Estropia 399517e357 ListMonitor is now refetch-able. Implement the listMonitorWillRefetch and listMonitorDidRefetch methods of ListObserver and call the refetch(...) method of ListMonitor 2015-09-08 17:21:15 +09:00
John Estropia f47adc12b3 fix typealias name clash when observing both ListMonitor and ObjectMonitor 2015-09-02 13:44:37 +09:00
John Estropia 0de1733efe oops 2015-09-01 21:05:59 +09:00
John Estropia e627cf8161 test improvements to ListMonitor 2015-09-01 20:08:45 +09:00
John Rommel Estropia 22943d2c76 added warn_unused_result annotations where they seem reasonable 2015-08-29 22:29:03 +09:00
John Estropia 56ea14d53c fix disconnected observers when an observer registers itself to multiple ListMonitors 2015-08-28 12:09:55 +09:00
John Rommel Estropia 21a524d725 tidy up 2015-08-28 08:09:06 +09:00
John Estropia d5e769be6c turns out inout arguments in closures are malloc nightmares. this finally fixes it 2015-08-27 22:24:40 +09:00
John Estropia ea6b6f2c37 bandaid solution for over-deallocated sequences bug in swift (for now) 2015-08-27 19:06:52 +09:00
John Estropia c3ef8a4172 bug fix for dictionaries getting deallocated earlier 2015-08-27 17:28:40 +09:00
John Rommel Estropia 16aabe1f3b optimize fetching objects with NSManagedObjectIDs 2015-08-26 23:59:18 +09:00
John Estropia 10e0cf8d2c tidy up 2015-08-25 20:33:36 +09:00
John Estropia 3a0f53321a improved behavior for #12 (as of Xcode 7 beta 6) 2015-08-25 14:35:22 +09:00
John Estropia 0da43d5132 Merge branch 'develop' of https://github.com/JohnEstropia/CoreStore into develop 2015-08-25 14:27:32 +09:00
John Estropia 69f902ef20 XCode7 beta 6 updates 2015-08-25 14:24:18 +09:00
John Estropia d04b4ca085 added utility for ListMonitor to return the index/indexPath of a specified object 2015-08-24 20:40:19 +09:00
John Estropia 8ed6a78609 CoreStore adapter method 2015-08-24 17:36:40 +09:00
John Estropia 0c9e6afe0e added utility to inspect NSEntityDescription for a given NSManagedObject type 2015-08-24 17:33:27 +09:00
John Rommel Estropia ff4629908a Merge branch 'develop' of github.com:JohnEstropia/CoreStore into develop 2015-08-23 14:37:34 +09:00
John Rommel Estropia d3ffe7a8fc add default implementation for souldUpdateFromImportSource 2015-08-23 14:37:21 +09:00
John Rommel Estropia 3919ada428 Merge branch 'master' into develop 2015-08-23 14:29:44 +09:00
John Rommel Estropia 682b13a8d3 version bump 2015-08-23 14:25:18 +09:00
John Rommel Estropia 2f935de04a temporarily fix an Xcode 7 bug (still present as of beta 5) (temporarily fixes #12) 2015-08-23 14:21:12 +09:00
John Rommel Estropia 006d5e1402 rewrote ImportableObject protocol methods 2015-08-23 14:15:48 +09:00
John Rommel Estropia 093c1d410f temporarily fix an Xcode 7 bug (still present as of beta 5) (temporarily fixes #12) 2015-08-23 14:02:27 +09:00
John Estropia 7555ff3ad0 allow preprocessing dictionary mapping before importing objects 2015-08-21 19:48:47 +09:00
John Estropia 4c16a961ba added default implementation to didInsertFromImportSource 2015-08-21 18:28:02 +09:00
John Estropia 1121d44d7b pass transaction to ImportableObject methods 2015-08-21 18:19:21 +09:00
112 changed files with 7347 additions and 3109 deletions
+5
View File
@@ -2,3 +2,8 @@ CoreStoreDemo/CoreStoreDemo.xcodeproj/project.xcworkspace/xcuserdata
CoreStore.xcodeproj/project.xcworkspace/xcuserdata CoreStore.xcodeproj/project.xcworkspace/xcuserdata
CoreStore.xcodeproj/xcuserdata CoreStore.xcodeproj/xcuserdata
CoreStoreDemo/CoreStoreDemo.xcodeproj/xcuserdata CoreStoreDemo/CoreStoreDemo.xcodeproj/xcuserdata
Carthage/Build
CoreStore.xcworkspace/xcuserdata
.DS_Store
DerivedData
*.orig
+3 -3
View File
@@ -1,3 +1,3 @@
[submodule "Libraries/GCDKit"] [submodule "Carthage/Checkouts/GCDKit"]
path = Libraries/GCDKit path = Carthage/Checkouts/GCDKit
url = git@github.com:JohnEstropia/GCDKit.git url = https://github.com/JohnEstropia/GCDKit.git
+43
View File
@@ -0,0 +1,43 @@
language: objective-c
osx_image: xcode7.3
sudo: false
git:
submodules: false
notifications:
email: false
env:
global:
- LC_CTYPE=en_US.UTF-8
- LANG=en_US.UTF-8
matrix:
- DESTINATION="OS=9.3,name=iPhone 6s" SCHEME="CoreStore iOS" SDK=iphonesimulator9.3 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=9.0,name=iPhone 6 Plus" SCHEME="CoreStore iOS" SDK=iphonesimulator9.3 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=8.4,name=iPhone 6" SCHEME="CoreStore iOS" SDK=iphonesimulator9.3 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=8.3,name=iPhone 5S" SCHEME="CoreStore iOS" SDK=iphonesimulator9.3 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=8.2,name=iPhone 5" SCHEME="CoreStore iOS" SDK=iphonesimulator9.3 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=8.1,name=iPhone 4S" SCHEME="CoreStore iOS" SDK=iphonesimulator9.3 RUN_TESTS="YES" POD_LINT="YES"
- DESTINATION="OS=8.1,name=iPhone 4S" SCHEME="CoreStore iOS7" SDK=iphonesimulator9.3 RUN_TESTS="YES" POD_LINT="YES"
- DESTINATION="arch=x86_64" SCHEME="CoreStore OSX" SDK=macosx10.11 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=2.2,name=Apple Watch - 42mm" SCHEME="CoreStore watchOS" SDK=watchsimulator2.2 RUN_TESTS="NO" POD_LINT="NO"
- DESTINATION="OS=9.2,name=Apple TV 1080p" SCHEME="CoreStore tvOS" SDK=appletvsimulator9.2 RUN_TESTS="YES" POD_LINT="NO"
before_install:
- gem install cocoapods --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.11/Carthage.pkg"
- sudo installer -pkg "Carthage.pkg" -target /
- rm "Carthage.pkg"
before_script:
- carthage update --use-submodules
script:
- set -o pipefail
- xcodebuild -version
- xcodebuild -showsdks
- if [ $RUN_TESTS == "YES" ]; then
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;
fi
- xcodebuild -workspace "CoreStore.xcworkspace" -scheme "CoreStore iOS" -sdk "iphonesimulator9.3" -destination "OS=9.3,name=iPhone 6s" -configuration Debug ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c;
- xcodebuild -workspace "CoreStore.xcworkspace" -scheme "CoreStore iOS" -sdk "iphonesimulator9.3" -destination "OS=9.3,name=iPhone 6s" -configuration Release ONLY_ACTIVE_ARCH=NO clean test | xcpretty -c;
- if [ $POD_LINT == "YES" ]; then
pod lib lint --quick;
fi
+1
View File
@@ -0,0 +1 @@
github "JohnEstropia/GCDKit" == 1.2.5
+1
View File
@@ -0,0 +1 @@
github "JohnEstropia/GCDKit" "1.2.4"
Vendored Submodule
+1
+10 -4
View File
@@ -1,16 +1,22 @@
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = "CoreStore" s.name = "CoreStore"
s.version = "1.2.0" s.version = "1.6.8"
s.license = "MIT" s.license = "MIT"
s.summary = "Simple, elegant, and smart Core Data programming with 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"
s.author = { "John Rommel Estropia" => "rommel.estropia@gmail.com" } s.author = { "John Rommel Estropia" => "rommel.estropia@gmail.com" }
s.source = { :git => "https://github.com/JohnEstropia/CoreStore.git", :tag => s.version.to_s } s.source = { :git => "https://github.com/JohnEstropia/CoreStore.git", :tag => s.version.to_s }
s.ios.deployment_target = "8.0" s.ios.deployment_target = "8.0"
s.osx.deployment_target = "10.10"
s.watchos.deployment_target = "2.0"
s.tvos.deployment_target = "9.0"
s.source_files = "CoreStore", "CoreStore/**/*.{swift}" s.source_files = "CoreStore", "CoreStore/**/*.{swift}"
s.frameworks = "Foundation", "UIKit", "CoreData" s.osx.exclude_files = "CoreStore/Observing/*.{swift}", "CoreStore/Internal/FetchedResultsControllerDelegate.swift", "CoreStore/Internal/CoreStoreFetchedResultsController.swift", "CoreStore/Convenience Helpers/NSFetchedResultsController+Convenience.swift"
s.frameworks = "Foundation", "CoreData"
s.requires_arc = true s.requires_arc = true
s.dependency "GCDKit", "1.1.1" s.pod_target_xcconfig = { 'OTHER_SWIFT_FLAGS' => '-D USE_FRAMEWORKS' }
s.dependency "GCDKit", "1.2.5"
end end
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B52DD1731BE1F8CC00949AFE"
BuildableName = "CoreStore.framework"
BlueprintName = "CoreStore OSX"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B52DD17C1BE1F8CC00949AFE"
BuildableName = "CoreStoreTests.xctest"
BlueprintName = "CoreStoreTests OSX"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B52DD1731BE1F8CC00949AFE"
BuildableName = "CoreStore.framework"
BlueprintName = "CoreStore OSX"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B52DD1731BE1F8CC00949AFE"
BuildableName = "CoreStore.framework"
BlueprintName = "CoreStore OSX"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B52DD1731BE1F8CC00949AFE"
BuildableName = "CoreStore.framework"
BlueprintName = "CoreStore OSX"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -16,7 +16,7 @@
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "2F03A52F19C5C6DA005002A5" BlueprintIdentifier = "2F03A52F19C5C6DA005002A5"
BuildableName = "CoreStore.framework" BuildableName = "CoreStore.framework"
BlueprintName = "CoreStore" BlueprintName = "CoreStore iOS"
ReferencedContainer = "container:CoreStore.xcodeproj"> ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildActionEntry> </BuildActionEntry>
@@ -30,7 +30,7 @@
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "2F03A53A19C5C6DA005002A5" BlueprintIdentifier = "2F03A53A19C5C6DA005002A5"
BuildableName = "CoreStoreTests.xctest" BuildableName = "CoreStoreTests.xctest"
BlueprintName = "CoreStoreTests" BlueprintName = "CoreStoreTests iOS"
ReferencedContainer = "container:CoreStore.xcodeproj"> ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildActionEntry> </BuildActionEntry>
@@ -48,7 +48,7 @@
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "2F03A53A19C5C6DA005002A5" BlueprintIdentifier = "2F03A53A19C5C6DA005002A5"
BuildableName = "CoreStoreTests.xctest" BuildableName = "CoreStoreTests.xctest"
BlueprintName = "CoreStoreTests" BlueprintName = "CoreStoreTests iOS"
ReferencedContainer = "container:CoreStore.xcodeproj"> ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference> </BuildableReference>
</TestableReference> </TestableReference>
@@ -58,7 +58,7 @@
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "2F03A52F19C5C6DA005002A5" BlueprintIdentifier = "2F03A52F19C5C6DA005002A5"
BuildableName = "CoreStore.framework" BuildableName = "CoreStore.framework"
BlueprintName = "CoreStore" BlueprintName = "CoreStore iOS"
ReferencedContainer = "container:CoreStore.xcodeproj"> ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
@@ -80,7 +80,7 @@
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "2F03A52F19C5C6DA005002A5" BlueprintIdentifier = "2F03A52F19C5C6DA005002A5"
BuildableName = "CoreStore.framework" BuildableName = "CoreStore.framework"
BlueprintName = "CoreStore" BlueprintName = "CoreStore iOS"
ReferencedContainer = "container:CoreStore.xcodeproj"> ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
@@ -98,7 +98,7 @@
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "2F03A52F19C5C6DA005002A5" BlueprintIdentifier = "2F03A52F19C5C6DA005002A5"
BuildableName = "CoreStore.framework" BuildableName = "CoreStore.framework"
BlueprintName = "CoreStore" BlueprintName = "CoreStore iOS"
ReferencedContainer = "container:CoreStore.xcodeproj"> ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference> </BuildableReference>
</MacroExpansion> </MacroExpansion>
@@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B5D9E2ED1CA2C317007A9D52"
BuildableName = "CoreStore_iOS7.framework"
BlueprintName = "CoreStore iOS7"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "NO"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2F03A53A19C5C6DA005002A5"
BuildableName = "CoreStoreTests.xctest"
BlueprintName = "CoreStoreTests iOS"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2F03A53A19C5C6DA005002A5"
BuildableName = "CoreStoreTests.xctest"
BlueprintName = "CoreStoreTests iOS"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2F03A52F19C5C6DA005002A5"
BuildableName = "CoreStore.framework"
BlueprintName = "CoreStore iOS"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B5D9E2ED1CA2C317007A9D52"
BuildableName = "CoreStore_iOS7.framework"
BlueprintName = "CoreStore iOS7"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Release">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0720"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "82BA18881C4BBCBA00A0916E"
BuildableName = "CoreStore.framework"
BlueprintName = "CoreStore tvOS"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "82BA18911C4BBCBA00A0916E"
BuildableName = "CoreStoreTests.xctest"
BlueprintName = "CoreStoreTests tvOS"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "82BA18881C4BBCBA00A0916E"
BuildableName = "CoreStore.framework"
BlueprintName = "CoreStore tvOS"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "82BA18881C4BBCBA00A0916E"
BuildableName = "CoreStore.framework"
BlueprintName = "CoreStore tvOS"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "82BA18881C4BBCBA00A0916E"
BuildableName = "CoreStore.framework"
BlueprintName = "CoreStore tvOS"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B563216E1BD65082006C9394"
BuildableName = "CoreStore.framework"
BlueprintName = "CoreStore watchOS"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B563216E1BD65082006C9394"
BuildableName = "CoreStore.framework"
BlueprintName = "CoreStore watchOS"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B563216E1BD65082006C9394"
BuildableName = "CoreStore.framework"
BlueprintName = "CoreStore watchOS"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:CoreStore.xcodeproj">
</FileRef>
<FileRef
location = "group:CoreStoreDemo/CoreStoreDemo.xcodeproj">
</FileRef>
<FileRef
location = "group:Carthage/Checkouts/GCDKit/GCDKit.xcodeproj">
</FileRef>
</Workspace>
@@ -0,0 +1,30 @@
{
"DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "4B60F1BCB491FF717C56441AE7783C74F417BE48",
"DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
},
"DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
"8B2E522D57154DFA93A06982C36315ECBEA4FA97" : 0,
"4B60F1BCB491FF717C56441AE7783C74F417BE48" : 0
},
"DVTSourceControlWorkspaceBlueprintIdentifierKey" : "EBFDEFFE-8BA0-441A-862A-1DE28AA5CD21",
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
"8B2E522D57154DFA93A06982C36315ECBEA4FA97" : "CoreStore\/Carthage\/Checkouts\/GCDKit\/",
"4B60F1BCB491FF717C56441AE7783C74F417BE48" : "CoreStore\/"
},
"DVTSourceControlWorkspaceBlueprintNameKey" : "CoreStore",
"DVTSourceControlWorkspaceBlueprintVersion" : 204,
"DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "CoreStore.xcworkspace",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/JohnEstropia\/CoreStore",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "4B60F1BCB491FF717C56441AE7783C74F417BE48"
},
{
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/JohnEstropia\/GCDKit.git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "8B2E522D57154DFA93A06982C36315ECBEA4FA97"
}
]
}
@@ -0,0 +1,255 @@
//
// NSManagedObject+Convenience.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: - NSFetchedResultsController
public extension NSFetchedResultsController {
/**
Utility for creating an `NSFetchedResultsController` from a `DataStack`. This is useful when an `NSFetchedResultsController` is preferred over the overhead of `ListMonitor`s abstraction.
- parameter dataStack: the `DataStack` to observe objects from
- 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: an `NSFetchedResultsController` that observes a `DataStack`
*/
@nonobjc
public static func createFor<T: NSManagedObject>(dataStack: DataStack, _ from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> NSFetchedResultsController {
return CoreStoreFetchedResultsController<T>(
context: dataStack.mainContext,
fetchRequest: CoreStoreFetchRequest(),
from: from,
sectionBy: sectionBy,
fetchClauses: fetchClauses
)
}
/**
Utility for creating an `NSFetchedResultsController` from a `DataStack`. This is useful when an `NSFetchedResultsController` is preferred over the overhead of `ListMonitor`s abstraction.
- parameter dataStack: the `DataStack` to observe objects from
- 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: an `NSFetchedResultsController` that observes a `DataStack`
*/
@nonobjc
public static func createFor<T: NSManagedObject>(dataStack: DataStack, _ from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController {
return CoreStoreFetchedResultsController<T>(
context: dataStack.mainContext,
fetchRequest: CoreStoreFetchRequest(),
from: from,
sectionBy: sectionBy,
fetchClauses: fetchClauses
)
}
/**
Utility for creating an `NSFetchedResultsController` from a `DataStack`. This is useful when an `NSFetchedResultsController` is preferred over the overhead of `ListMonitor`s abstraction.
- parameter dataStack: the `DataStack` to observe objects from
- 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: an `NSFetchedResultsController` that observes a `DataStack`
*/
@nonobjc
public static func createFor<T: NSManagedObject>(dataStack: DataStack, _ from: From<T>, _ fetchClauses: FetchClause...) -> NSFetchedResultsController {
return CoreStoreFetchedResultsController<T>(
context: dataStack.mainContext,
fetchRequest: CoreStoreFetchRequest(),
from: from,
sectionBy: nil,
fetchClauses: fetchClauses
)
}
/**
Utility for creating an `NSFetchedResultsController` from a `DataStack`. This is useful when an `NSFetchedResultsController` is preferred over the overhead of `ListMonitor`s abstraction.
- parameter dataStack: the `DataStack` to observe objects from
- 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: an `NSFetchedResultsController` that observes a `DataStack`
*/
@nonobjc
public static func createFor<T: NSManagedObject>(dataStack: DataStack, _ from: From<T>, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController {
return CoreStoreFetchedResultsController<T>(
context: dataStack.mainContext,
fetchRequest: CoreStoreFetchRequest(),
from: from,
sectionBy: nil,
fetchClauses: fetchClauses
)
}
/**
Utility for creating an `NSFetchedResultsController` from an `UnsafeDataTransaction`. This is useful when an `NSFetchedResultsController` is preferred over the overhead of `ListMonitor`s abstraction.
- parameter transaction: the `UnsafeDataTransaction` to observe objects from
- 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: an `NSFetchedResultsController` that observes an `UnsafeDataTransaction`
*/
@nonobjc
public static func createFor<T: NSManagedObject>(transaction: UnsafeDataTransaction, _ from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> NSFetchedResultsController {
return CoreStoreFetchedResultsController<T>(
context: transaction.context,
fetchRequest: CoreStoreFetchRequest(),
from: from,
sectionBy: sectionBy,
fetchClauses: fetchClauses
)
}
/**
Utility for creating an `NSFetchedResultsController` from an `UnsafeDataTransaction`. This is useful when an `NSFetchedResultsController` is preferred over the overhead of `ListMonitor`s abstraction.
- parameter transaction: the `UnsafeDataTransaction` to observe objects from
- 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: an `NSFetchedResultsController` that observes an `UnsafeDataTransaction`
*/
@nonobjc
public static func createFor<T: NSManagedObject>(transaction: UnsafeDataTransaction, _ from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController {
return CoreStoreFetchedResultsController<T>(
context: transaction.context,
fetchRequest: CoreStoreFetchRequest(),
from: from,
sectionBy: sectionBy,
fetchClauses: fetchClauses
)
}
/**
Utility for creating an `NSFetchedResultsController` from an `UnsafeDataTransaction`. This is useful when an `NSFetchedResultsController` is preferred over the overhead of `ListMonitor`s abstraction.
- parameter transaction: the `UnsafeDataTransaction` to observe objects from
- 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: an `NSFetchedResultsController` that observes an `UnsafeDataTransaction`
*/
@nonobjc
public static func createFor<T: NSManagedObject>(transaction: UnsafeDataTransaction, _ from: From<T>, _ fetchClauses: FetchClause...) -> NSFetchedResultsController {
return CoreStoreFetchedResultsController<T>(
context: transaction.context,
fetchRequest: CoreStoreFetchRequest(),
from: from,
sectionBy: nil,
fetchClauses: fetchClauses
)
}
/**
Utility for creating an `NSFetchedResultsController` from an `UnsafeDataTransaction`. This is useful when an `NSFetchedResultsController` is preferred over the overhead of `ListMonitor`s abstraction.
- parameter transaction: the `UnsafeDataTransaction` to observe objects from
- 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.
*/
@nonobjc
public static func createFor<T: NSManagedObject>(transaction: UnsafeDataTransaction, _ from: From<T>, _ fetchClauses: [FetchClause]) -> NSFetchedResultsController {
return CoreStoreFetchedResultsController<T>(
context: transaction.context,
fetchRequest: CoreStoreFetchRequest(),
from: from,
sectionBy: nil,
fetchClauses: fetchClauses
)
}
@available(*, deprecated=1.6.8, message="Use NSFetchedResultsController.createFor(_:_:_:_:) instead.")
@nonobjc
public static func createForStack<T: NSManagedObject>(dataStack: DataStack, fetchRequest: NSFetchRequest, from: From<T>? = nil, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) -> NSFetchedResultsController {
return CoreStoreFetchedResultsController<T>(
context: dataStack.mainContext,
fetchRequest: fetchRequest,
from: from,
sectionBy: sectionBy,
fetchClauses: fetchClauses
)
}
@available(*, deprecated=1.5.2, message="Use NSFetchedResultsController.createFor(_:_:_:_:) to create NSFetchedResultsControllers directly")
@nonobjc
public convenience init<T: NSManagedObject>(dataStack: DataStack, fetchRequest: NSFetchRequest, from: From<T>? = nil, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) {
let context = dataStack.mainContext
from?.applyToFetchRequest(fetchRequest, context: context, applyAffectedStores: false)
for clause in fetchClauses {
clause.applyToFetchRequest(fetchRequest)
}
if let from = from {
from.applyAffectedStoresForFetchedRequest(fetchRequest, context: context)
}
else {
guard let from = (fetchRequest.entity.flatMap { $0.managedObjectClassName }).flatMap(NSClassFromString).flatMap(From.init) else {
fatalError("Attempted to create an \(typeName(NSFetchedResultsController)) without a From clause or an NSEntityDescription.")
}
from.applyAffectedStoresForFetchedRequest(fetchRequest, context: context)
}
self.init(
fetchRequest: fetchRequest,
managedObjectContext: context,
sectionNameKeyPath: sectionBy?.sectionKeyPath,
cacheName: nil
)
}
// MARK: Internal
internal static func createFromContext<T: NSManagedObject>(context: NSManagedObjectContext, fetchRequest: NSFetchRequest, from: From<T>? = nil, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) -> NSFetchedResultsController {
return CoreStoreFetchedResultsController<T>(
context: context,
fetchRequest: fetchRequest,
from: from,
sectionBy: sectionBy,
fetchClauses: fetchClauses
)
}
}
@@ -2,7 +2,7 @@
// NSManagedObject+Convenience.swift // NSManagedObject+Convenience.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -37,6 +37,7 @@ public extension NSManagedObject {
- parameter KVCKey: the KVC key - parameter KVCKey: the KVC key
- returns: the primitive value for the KVC key - returns: the primitive value for the KVC key
*/ */
@warn_unused_result
public func accessValueForKVCKey(KVCKey: KeyPath) -> AnyObject? { public func accessValueForKVCKey(KVCKey: KeyPath) -> AnyObject? {
self.willAccessValueForKey(KVCKey) self.willAccessValueForKey(KVCKey)
@@ -2,7 +2,7 @@
// NSProgress+Convenience.swift // NSProgress+Convenience.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -24,15 +24,15 @@
// //
import Foundation import Foundation
#if USE_FRAMEWORKS
import GCDKit import GCDKit
#endif
// MARK: - NSProgress // MARK: - NSProgress
public extension NSProgress { public extension NSProgress {
// MARK: Public
/** /**
Sets a closure that the `NSProgress` calls whenever its `fractionCompleted` changes. You can use this instead of setting up KVO. Sets a closure that the `NSProgress` calls whenever its `fractionCompleted` changes. You can use this instead of setting up KVO.
- parameter closure: the closure to execute on progress change - parameter closure: the closure to execute on progress change
+2 -2
View File
@@ -2,7 +2,7 @@
// CoreStore.h // CoreStore.h
// CoreStore // CoreStore
// //
// Copyright (c) 2014 John Rommel Estropia // Copyright © 2014 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -23,8 +23,8 @@
// SOFTWARE. // SOFTWARE.
// //
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h> #import <CoreData/CoreData.h>
FOUNDATION_EXPORT double CoreStoreVersionNumber; FOUNDATION_EXPORT double CoreStoreVersionNumber;
FOUNDATION_EXPORT const unsigned char CoreStoreVersionString[]; FOUNDATION_EXPORT const unsigned char CoreStoreVersionString[];
+3 -3
View File
@@ -2,7 +2,7 @@
// CoreStore.swift // CoreStore.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2014 John Rommel Estropia // Copyright © 2014 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -24,7 +24,9 @@
// //
import CoreData import CoreData
#if USE_FRAMEWORKS
import GCDKit import GCDKit
#endif
// MARK: - CoreStore // MARK: - CoreStore
@@ -34,8 +36,6 @@ import GCDKit
*/ */
public enum CoreStore { public enum CoreStore {
// MARK: Public
/** /**
The default `DataStack` instance to be used. If `defaultStack` is not set before the first time accessed, a default-configured `DataStack` will be created. The default `DataStack` instance to be used. If `defaultStack` is not set before the first time accessed, a default-configured `DataStack` will be created.
@@ -2,7 +2,7 @@
// BaseDataTransaction+Querying.swift // BaseDataTransaction+Querying.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -31,14 +31,13 @@ import CoreData
public extension BaseDataTransaction { public extension BaseDataTransaction {
// MARK: Public
/** /**
Fetches the `NSManagedObject` instance in the transaction's context from a reference created from a transaction or from a different managed object context. Fetches the `NSManagedObject` 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 - parameter object: a reference to the object created/fetched outside the transaction
- returns: the `NSManagedObject` instance if the object exists in the transaction, or `nil` if not found. - returns: the `NSManagedObject` instance if the object exists in the transaction, or `nil` if not found.
*/ */
@warn_unused_result
public func fetchExisting<T: NSManagedObject>(object: T) -> T? { public func fetchExisting<T: NSManagedObject>(object: T) -> T? {
do { do {
@@ -57,6 +56,7 @@ public extension BaseDataTransaction {
- parameter objectID: the `NSManagedObjectID` for the object - parameter objectID: the `NSManagedObjectID` for the object
- returns: the `NSManagedObject` instance if the object exists in the transaction, or `nil` if not found. - returns: the `NSManagedObject` instance if the object exists in the transaction, or `nil` if not found.
*/ */
@warn_unused_result
public func fetchExisting<T: NSManagedObject>(objectID: NSManagedObjectID) -> T? { public func fetchExisting<T: NSManagedObject>(objectID: NSManagedObjectID) -> T? {
do { do {
@@ -75,19 +75,10 @@ public extension BaseDataTransaction {
- parameter objects: an array of `NSManagedObject`s created/fetched outside the transaction - parameter objects: an array of `NSManagedObject`s created/fetched outside the transaction
- returns: the `NSManagedObject` array for objects that exists in the transaction - returns: the `NSManagedObject` array for objects that exists in the transaction
*/ */
public func fetchExisting<T: NSManagedObject>(objects: [T]) -> [T] { @warn_unused_result
public func fetchExisting<T: NSManagedObject, S: SequenceType where S.Generator.Element == T>(objects: S) -> [T] {
var existingObjects = [T]() return objects.flatMap { (try? self.context.existingObjectWithID($0.objectID)) as? T }
for object in objects {
do {
let existingObject = try self.context.existingObjectWithID(object.objectID) as! T
existingObjects.append(existingObject)
}
catch _ { }
}
return existingObjects
} }
/** /**
@@ -96,19 +87,10 @@ public extension BaseDataTransaction {
- parameter objectIDs: the `NSManagedObjectID` array for the objects - parameter objectIDs: the `NSManagedObjectID` array for the objects
- returns: the `NSManagedObject` array for objects that exists in the transaction - returns: the `NSManagedObject` array for objects that exists in the transaction
*/ */
public func fetchExisting<T: NSManagedObject>(objectIDs: [NSManagedObjectID]) -> [T] { @warn_unused_result
public func fetchExisting<T: NSManagedObject, S: SequenceType where S.Generator.Element == NSManagedObjectID>(objectIDs: S) -> [T] {
var existingObjects = [T]() return objectIDs.flatMap { (try? self.context.existingObjectWithID($0)) as? T }
for objectID in objectIDs {
do {
let existingObject = try self.context.existingObjectWithID(objectID) as! T
existingObjects.append(existingObject)
}
catch _ { }
}
return existingObjects
} }
/** /**
@@ -118,10 +100,11 @@ public extension BaseDataTransaction {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the first `NSManagedObject` instance that satisfies the specified `FetchClause`s - returns: the first `NSManagedObject` instance that satisfies the specified `FetchClause`s
*/ */
@warn_unused_result
public func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> T? { public func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> T? {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(typeName(self)) outside its designated queue." "Attempted to fetch from a \(typeName(self)) outside its designated queue."
) )
@@ -135,10 +118,11 @@ public extension BaseDataTransaction {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the first `NSManagedObject` instance that satisfies the specified `FetchClause`s - returns: the first `NSManagedObject` instance that satisfies the specified `FetchClause`s
*/ */
@warn_unused_result
public func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> T? { public func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> T? {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(typeName(self)) outside its designated queue." "Attempted to fetch from a \(typeName(self)) outside its designated queue."
) )
@@ -152,10 +136,11 @@ public extension BaseDataTransaction {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s - returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s
*/ */
@warn_unused_result
public func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> [T]? { public func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> [T]? {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(typeName(self)) outside its designated queue." "Attempted to fetch from a \(typeName(self)) outside its designated queue."
) )
@@ -169,10 +154,11 @@ public extension BaseDataTransaction {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s - returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s
*/ */
@warn_unused_result
public func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [T]? { public func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [T]? {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(typeName(self)) outside its designated queue." "Attempted to fetch from a \(typeName(self)) outside its designated queue."
) )
@@ -186,10 +172,11 @@ public extension BaseDataTransaction {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s - returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s
*/ */
@warn_unused_result
public func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> Int? { public func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> Int? {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(typeName(self)) outside its designated queue." "Attempted to fetch from a \(typeName(self)) outside its designated queue."
) )
@@ -203,10 +190,11 @@ public extension BaseDataTransaction {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s - returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s
*/ */
@warn_unused_result
public func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> Int? { public func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> Int? {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(typeName(self)) outside its designated queue." "Attempted to fetch from a \(typeName(self)) outside its designated queue."
) )
@@ -220,10 +208,11 @@ public extension BaseDataTransaction {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s - returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s
*/ */
@warn_unused_result
public func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> NSManagedObjectID? { public func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> NSManagedObjectID? {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(typeName(self)) outside its designated queue." "Attempted to fetch from a \(typeName(self)) outside its designated queue."
) )
@@ -237,10 +226,11 @@ public extension BaseDataTransaction {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s - returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s
*/ */
@warn_unused_result
public func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? { public func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(typeName(self)) outside its designated queue." "Attempted to fetch from a \(typeName(self)) outside its designated queue."
) )
@@ -254,10 +244,11 @@ public extension BaseDataTransaction {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s - returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s
*/ */
@warn_unused_result
public func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? { public func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(typeName(self)) outside its designated queue." "Attempted to fetch from a \(typeName(self)) outside its designated queue."
) )
@@ -271,10 +262,11 @@ public extension BaseDataTransaction {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s - returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s
*/ */
@warn_unused_result
public func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? { public func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to fetch from a \(typeName(self)) outside its designated queue." "Attempted to fetch from a \(typeName(self)) outside its designated queue."
) )
@@ -291,7 +283,7 @@ public extension BaseDataTransaction {
public func deleteAll<T: NSManagedObject>(from: From<T>, _ deleteClauses: DeleteClause...) -> Int? { public func deleteAll<T: NSManagedObject>(from: From<T>, _ deleteClauses: DeleteClause...) -> Int? {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to delete from a \(typeName(self)) outside its designated queue." "Attempted to delete from a \(typeName(self)) outside its designated queue."
) )
@@ -308,7 +300,7 @@ public extension BaseDataTransaction {
public func deleteAll<T: NSManagedObject>(from: From<T>, _ deleteClauses: [DeleteClause]) -> Int? { public func deleteAll<T: NSManagedObject>(from: From<T>, _ deleteClauses: [DeleteClause]) -> Int? {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to delete from a \(typeName(self)) outside its designated queue." "Attempted to delete from a \(typeName(self)) outside its designated queue."
) )
@@ -325,10 +317,11 @@ public extension BaseDataTransaction {
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. - 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. - returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/ */
@warn_unused_result
public func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: QueryClause...) -> U? { public func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: QueryClause...) -> U? {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to query from a \(typeName(self)) outside its designated queue." "Attempted to query from a \(typeName(self)) outside its designated queue."
) )
@@ -345,10 +338,11 @@ public extension BaseDataTransaction {
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. - 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. - returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/ */
@warn_unused_result
public func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: [QueryClause]) -> U? { public func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: [QueryClause]) -> U? {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to query from a \(typeName(self)) outside its designated queue." "Attempted to query from a \(typeName(self)) outside its designated queue."
) )
@@ -365,10 +359,11 @@ public extension BaseDataTransaction {
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. - 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. - returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/ */
@warn_unused_result
public func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: QueryClause...) -> [[NSString: AnyObject]]? { public func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: QueryClause...) -> [[NSString: AnyObject]]? {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to query from a \(typeName(self)) outside its designated queue." "Attempted to query from a \(typeName(self)) outside its designated queue."
) )
@@ -385,10 +380,11 @@ public extension BaseDataTransaction {
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. - 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. - returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/ */
@warn_unused_result
public func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? { public func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to query from a \(typeName(self)) outside its designated queue." "Attempted to query from a \(typeName(self)) outside its designated queue."
) )
@@ -2,7 +2,7 @@
// From.swift // From.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -30,115 +30,269 @@ import CoreData
// MARK: - From // MARK: - From
/** /**
A `Form` clause binds the `NSManagedObject` entity type to the generics type system. A `From` clause specifies the source entity and source persistent store for fetch and query methods. A common usage is to just indicate the entity:
```
let person = transaction.fetchOne(From(MyPersonEntity))
```
For cases where multiple `NSPersistentStore`s contain the same entity, the source configuration's name needs to be specified as well:
```
let person = transaction.fetchOne(From<MyPersonEntity>("Configuration1"))
```
*/ */
public struct From<T: NSManagedObject> { public struct From<T: NSManagedObject> {
// MARK: Public /**
Initializes a `From` clause.
Sample Usage:
```
let people = transaction.fetchAll(From<MyPersonEntity>())
```
*/
public init(){ public init(){
self.entityClass = T.self self.init(entityClass: T.self)
self.findPersistentStores = { _ in nil }
} }
/**
Initializes a `From` clause with the specified entity type.
Sample Usage:
```
let people = transaction.fetchAll(From<MyPersonEntity>())
```
- parameter entity: the `NSManagedObject` type to be created
*/
public init(_ entity: T.Type) { public init(_ entity: T.Type) {
self.entityClass = entity self.init(entityClass: entity)
self.findPersistentStores = { _ in nil }
} }
/**
Initializes a `From` clause with the specified entity class.
Sample Usage:
```
let people = transaction.fetchAll(From<MyPersonEntity>())
```
- parameter entityClass: the `NSManagedObject` class type to be created
*/
public init(_ entityClass: AnyClass) { public init(_ entityClass: AnyClass) {
self.entityClass = entityClass self.init(entityClass: entityClass)
self.findPersistentStores = { _ in nil }
} }
public init(_ configurations: String?...) { /**
Initializes a `From` clause with the specified configurations.
Sample Usage:
```
let people = transaction.fetchAll(From<MyPersonEntity>(nil, "Configuration1"))
```
- 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 `nil` to use the default configuration.
- parameter otherConfigurations: an optional list of other configuration names to associate objects from (see `configuration` parameter)
*/
public init(_ configuration: String?, otherConfigurations: String?...) {
self.init(entityClass: T.self, configurations: configurations) self.init(entityClass: T.self, configurations: [configuration] + otherConfigurations)
} }
/**
Initializes a `From` clause with the specified configurations.
Sample Usage:
```
let people = transaction.fetchAll(From<MyPersonEntity>(["Configuration1", "Configuration2"]))
```
- parameter configurations: a list of `NSPersistentStore` configuration names to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
*/
public init(_ configurations: [String?]) { public init(_ configurations: [String?]) {
self.init(entityClass: T.self, configurations: configurations) self.init(entityClass: T.self, configurations: configurations)
} }
public init(_ entity: T.Type, _ configurations: String?...) { /**
Initializes a `From` clause with the specified configurations.
Sample Usage:
```
let people = transaction.fetchAll(From(MyPersonEntity.self, nil, "Configuration1"))
```
- parameter entity: the associated `NSManagedObject` type
- 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 `nil` to use the default configuration.
- parameter otherConfigurations: an optional list of other configuration names to associate objects from (see `configuration` parameter)
*/
public init(_ entity: T.Type, _ configuration: String?, _ otherConfigurations: String?...) {
self.init(entityClass: entity, configurations: configurations) self.init(entityClass: entity, configurations: [configuration] + otherConfigurations)
} }
/**
Initializes a `From` clause with the specified configurations.
Sample Usage:
```
let people = transaction.fetchAll(From(MyPersonEntity.self, ["Configuration1", "Configuration1"]))
```
- parameter entity: the associated `NSManagedObject` type
- parameter configurations: a list of `NSPersistentStore` configuration names to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
*/
public init(_ entity: T.Type, _ configurations: [String?]) { public init(_ entity: T.Type, _ configurations: [String?]) {
self.init(entityClass: entity, configurations: configurations) self.init(entityClass: entity, configurations: configurations)
} }
public init(_ entityClass: AnyClass, _ configurations: String?...) { /**
Initializes a `From` clause with the specified configurations.
Sample Usage:
```
let people = transaction.fetchAll(From(MyPersonEntity.self, nil, "Configuration1"))
```
- parameter entity: 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 `nil` to use the default configuration.
- parameter otherConfigurations: an optional list of other configuration names to associate objects from (see `configuration` parameter)
*/
public init(_ entityClass: AnyClass, _ configuration: String?, _ otherConfigurations: String?...) {
self.init(entityClass: entityClass, configurations: configurations) self.init(entityClass: entityClass, configurations: [configuration] + otherConfigurations)
} }
/**
Initializes a `From` clause with the specified configurations.
Sample Usage:
```
let people = transaction.fetchAll(From(MyPersonEntity.self, ["Configuration1", "Configuration1"]))
```
- parameter entity: the associated `NSManagedObject` entity class
- parameter configurations: a list of `NSPersistentStore` configuration names to associate objects from. This parameter is required if multiple configurations contain the created `NSManagedObject`'s entity type. Set to `nil` to use the default configuration.
*/
public init(_ entityClass: AnyClass, _ configurations: [String?]) { public init(_ entityClass: AnyClass, _ configurations: [String?]) {
self.init(entityClass: entityClass, configurations: configurations) self.init(entityClass: entityClass, configurations: configurations)
} }
public init(_ storeURLs: NSURL...) { /**
Initializes a `From` clause with the specified store URLs.
self.init(entityClass: T.self, storeURLs: storeURLs) - parameter storeURL: the persistent store URL to associate objects from.
- parameter otherStoreURLs: an optional list of other persistent store URLs to associate objects from (see `storeURL` parameter)
*/
public init(_ storeURL: NSURL, _ otherStoreURLs: NSURL...) {
self.init(entityClass: T.self, storeURLs: [storeURL] + otherStoreURLs)
} }
/**
Initializes a `From` clause with the specified store URLs.
- parameter storeURLs: the persistent store URLs to associate objects from.
*/
public init(_ storeURLs: [NSURL]) { public init(_ storeURLs: [NSURL]) {
self.init(entityClass: T.self, storeURLs: storeURLs) self.init(entityClass: T.self, storeURLs: storeURLs)
} }
public init(_ entity: T.Type, _ storeURLs: NSURL...) { /**
Initializes a `From` clause with the specified store URLs.
self.init(entityClass: entity, storeURLs: storeURLs) - parameter entity: the associated `NSManagedObject` type
- parameter storeURL: the persistent store URL to associate objects from.
- parameter otherStoreURLs: an optional list of other persistent store URLs to associate objects from (see `storeURL` parameter)
*/
public init(_ entity: T.Type, _ storeURL: NSURL, _ otherStoreURLs: NSURL...) {
self.init(entityClass: entity, storeURLs: [storeURL] + otherStoreURLs)
} }
/**
Initializes a `From` clause with the specified store URLs.
- parameter entity: the associated `NSManagedObject` type
- parameter storeURLs: the persistent store URLs to associate objects from.
*/
public init(_ entity: T.Type, _ storeURLs: [NSURL]) { public init(_ entity: T.Type, _ storeURLs: [NSURL]) {
self.init(entityClass: entity, storeURLs: storeURLs) self.init(entityClass: entity, storeURLs: storeURLs)
} }
public init(_ entityClass: AnyClass, _ storeURLs: NSURL...) { /**
Initializes a `From` clause with the specified store URLs.
self.init(entityClass: entityClass, storeURLs: storeURLs) - parameter entity: the associated `NSManagedObject` entity class
- parameter storeURL: the persistent store URL to associate objects from.
- parameter otherStoreURLs: an optional list of other persistent store URLs to associate objects from (see `storeURL` parameter)
*/
public init(_ entityClass: AnyClass, _ storeURL: NSURL, _ otherStoreURLs: NSURL...) {
self.init(entityClass: entityClass, storeURLs: [storeURL] + otherStoreURLs)
} }
/**
Initializes a `From` clause with the specified store URLs.
- parameter entity: the associated `NSManagedObject` entity class
- parameter storeURLs: the persistent store URLs to associate objects from.
*/
public init(_ entityClass: AnyClass, _ storeURLs: [NSURL]) { public init(_ entityClass: AnyClass, _ storeURLs: [NSURL]) {
self.init(entityClass: entityClass, storeURLs: storeURLs) self.init(entityClass: entityClass, storeURLs: storeURLs)
} }
public init(_ persistentStores: NSPersistentStore...) { /**
Initializes a `From` clause with the specified `NSPersistentStore`s.
self.init(entityClass: T.self, persistentStores: persistentStores) - parameter persistentStore: the `NSPersistentStore` to associate objects from.
- parameter otherPersistentStores: an optional list of other `NSPersistentStore`s to associate objects from (see `persistentStore` parameter)
*/
public init(_ persistentStore: NSPersistentStore, _ otherPersistentStores: NSPersistentStore...) {
self.init(entityClass: T.self, persistentStores: [persistentStore] + otherPersistentStores)
} }
/**
Initializes a `From` clause with the specified `NSPersistentStore`s.
- parameter persistentStores: the `NSPersistentStore`s to associate objects from.
*/
public init(_ persistentStores: [NSPersistentStore]) { public init(_ persistentStores: [NSPersistentStore]) {
self.init(entityClass: T.self, persistentStores: persistentStores) self.init(entityClass: T.self, persistentStores: persistentStores)
} }
public init(_ entity: T.Type, _ persistentStores: NSPersistentStore...) { /**
Initializes a `From` clause with the specified `NSPersistentStore`s.
self.init(entityClass: entity, persistentStores: persistentStores) - parameter entity: the associated `NSManagedObject` type
- parameter persistentStore: the `NSPersistentStore` to associate objects from.
- parameter otherPersistentStores: an optional list of other `NSPersistentStore`s to associate objects from (see `persistentStore` parameter)
*/
public init(_ entity: T.Type, _ persistentStore: NSPersistentStore, _ otherPersistentStores: NSPersistentStore...) {
self.init(entityClass: entity, persistentStores: [persistentStore] + otherPersistentStores)
} }
/**
Initializes a `From` clause with the specified `NSPersistentStore`s.
- parameter entity: the associated `NSManagedObject` type
- parameter persistentStores: the `NSPersistentStore`s to associate objects from.
*/
public init(_ entity: T.Type, _ persistentStores: [NSPersistentStore]) { public init(_ entity: T.Type, _ persistentStores: [NSPersistentStore]) {
self.init(entityClass: entity, persistentStores: persistentStores) self.init(entityClass: entity, persistentStores: persistentStores)
} }
public init(_ entityClass: AnyClass, _ persistentStores: NSPersistentStore...) { /**
Initializes a `From` clause with the specified `NSPersistentStore`s.
self.init(entityClass: entityClass, persistentStores: persistentStores) - parameter entity: the associated `NSManagedObject` entity class
- parameter persistentStore: the `NSPersistentStore` to associate objects from.
- parameter otherPersistentStores: an optional list of other `NSPersistentStore`s to associate objects from (see `persistentStore` parameter)
*/
public init(_ entityClass: AnyClass, _ persistentStore: NSPersistentStore, _ otherPersistentStores: NSPersistentStore...) {
self.init(entityClass: entityClass, persistentStores: [persistentStore] + otherPersistentStores)
} }
/**
Initializes a `From` clause with the specified `NSPersistentStore`s.
- parameter entity: the associated `NSManagedObject` entity class
- parameter persistentStores: the `NSPersistentStore`s to associate objects from.
*/
public init(_ entityClass: AnyClass, _ persistentStores: [NSPersistentStore]) { public init(_ entityClass: AnyClass, _ persistentStores: [NSPersistentStore]) {
self.init(entityClass: entityClass, persistentStores: persistentStores) self.init(entityClass: entityClass, persistentStores: persistentStores)
@@ -147,10 +301,20 @@ public struct From<T: NSManagedObject> {
// MARK: Internal // MARK: Internal
internal func applyToFetchRequest(fetchRequest: NSFetchRequest, context: NSManagedObjectContext) { internal func applyToFetchRequest(fetchRequest: NSFetchRequest, context: NSManagedObjectContext, applyAffectedStores: Bool = true) {
fetchRequest.entity = context.entityDescriptionForEntityClass(self.entityClass) fetchRequest.entity = context.entityDescriptionForEntityClass(self.entityClass)
fetchRequest.affectedStores = self.findPersistentStores(context: context) if applyAffectedStores {
self.applyAffectedStoresForFetchedRequest(fetchRequest, context: context)
}
}
internal func applyAffectedStoresForFetchedRequest(fetchRequest: NSFetchRequest, context: NSManagedObjectContext) -> Bool {
let stores = self.findPersistentStores(context: context)
fetchRequest.affectedStores = stores
return stores?.isEmpty == false
} }
@@ -160,6 +324,15 @@ public struct From<T: NSManagedObject> {
private let findPersistentStores: (context: NSManagedObjectContext) -> [NSPersistentStore]? private let findPersistentStores: (context: NSManagedObjectContext) -> [NSPersistentStore]?
private init(entityClass: AnyClass) {
self.entityClass = entityClass
self.findPersistentStores = { (context: NSManagedObjectContext) -> [NSPersistentStore]? in
return context.parentStack?.persistentStoresForEntityClass(entityClass)
}
}
private init(entityClass: AnyClass, configurations: [String?]) { private init(entityClass: AnyClass, configurations: [String?]) {
let configurationsSet = Set(configurations.map { $0 ?? Into.defaultConfigurationName }) let configurationsSet = Set(configurations.map { $0 ?? Into.defaultConfigurationName })
@@ -2,7 +2,7 @@
// GroupBy.swift // GroupBy.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -34,8 +34,6 @@ The `GroupBy` clause specifies that the result of a query be grouped accoording
*/ */
public struct GroupBy: QueryClause { public struct GroupBy: QueryClause {
// MARK: Public
/** /**
Initializes a `GroupBy` clause with a list of key path strings Initializes a `GroupBy` clause with a list of key path strings
@@ -72,7 +70,7 @@ public struct GroupBy: QueryClause {
public func applyToFetchRequest(fetchRequest: NSFetchRequest) { public func applyToFetchRequest(fetchRequest: NSFetchRequest) {
if fetchRequest.propertiesToGroupBy != nil { if let keyPaths = fetchRequest.propertiesToGroupBy as? [String] where keyPaths != self.keyPaths {
CoreStore.log( CoreStore.log(
.Warning, .Warning,
@@ -2,7 +2,7 @@
// OrderBy.swift // OrderBy.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -24,6 +24,7 @@
// //
import Foundation import Foundation
import CoreData
public func +(left: OrderBy, right: OrderBy) -> OrderBy { public func +(left: OrderBy, right: OrderBy) -> OrderBy {
@@ -68,8 +69,6 @@ The `OrderBy` clause specifies the sort order for results for a fetch or a query
*/ */
public struct OrderBy: FetchClause, QueryClause, DeleteClause { public struct OrderBy: FetchClause, QueryClause, DeleteClause {
// MARK: Public
/** /**
Initializes a `OrderBy` clause with a list of sort descriptors Initializes a `OrderBy` clause with a list of sort descriptors
@@ -138,7 +137,7 @@ public struct OrderBy: FetchClause, QueryClause, DeleteClause {
public func applyToFetchRequest(fetchRequest: NSFetchRequest) { public func applyToFetchRequest(fetchRequest: NSFetchRequest) {
if fetchRequest.sortDescriptors != nil { if let sortDescriptors = fetchRequest.sortDescriptors where sortDescriptors != self.sortDescriptors {
CoreStore.log( CoreStore.log(
.Warning, .Warning,
@@ -2,7 +2,7 @@
// Select.swift // Select.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -64,25 +64,23 @@ The `SelectTerm` is passed to the `Select` clause to indicate the attributes/agg
*/ */
public enum SelectTerm: StringLiteralConvertible { public enum SelectTerm: StringLiteralConvertible {
// MARK: Public
/** /**
Provides a `SelectTerm` to a `Select` clause for querying an entity attribute. A shorter way to do the same is to assign from the string keypath directly: Provides a `SelectTerm` to a `Select` clause for querying an entity attribute. A shorter way to do the same is to assign from the string keypath directly:
```
let fullName = CoreStore.queryValue( let fullName = CoreStore.queryValue(
From(MyPersonEntity), From(MyPersonEntity),
Select<String>(.Attribute("fullName")), Select<String>(.Attribute("fullName")),
Where("employeeID", isEqualTo: 1111) Where("employeeID", isEqualTo: 1111)
) )
```
is equivalent to: is equivalent to:
```
let fullName = CoreStore.queryValue( let fullName = CoreStore.queryValue(
From(MyPersonEntity), From(MyPersonEntity),
Select<String>("fullName"), Select<String>("fullName"),
Where("employeeID", isEqualTo: 1111) Where("employeeID", isEqualTo: 1111)
) )
```
- parameter keyPath: the attribute name - parameter keyPath: the attribute name
- returns: a `SelectTerm` to a `Select` clause for querying an entity attribute - returns: a `SelectTerm` to a `Select` clause for querying an entity attribute
*/ */
@@ -93,12 +91,12 @@ public enum SelectTerm: StringLiteralConvertible {
/** /**
Provides a `SelectTerm` to a `Select` clause for querying the average value of an attribute. Provides a `SelectTerm` to a `Select` clause for querying the average value of an attribute.
```
let averageAge = CoreStore.queryValue( let averageAge = CoreStore.queryValue(
From(MyPersonEntity), From(MyPersonEntity),
Select<Int>(.Average("age")) Select<Int>(.Average("age"))
) )
```
- parameter keyPath: the attribute name - 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 "average(<attributeName>)" 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 "average(<attributeName>)" is used
- returns: a `SelectTerm` to a `Select` clause for querying the average value of an attribute - returns: a `SelectTerm` to a `Select` clause for querying the average value of an attribute
@@ -115,12 +113,12 @@ public enum SelectTerm: StringLiteralConvertible {
/** /**
Provides a `SelectTerm` to a `Select` clause for a count query. Provides a `SelectTerm` to a `Select` clause for a count query.
```
let numberOfEmployees = CoreStore.queryValue( let numberOfEmployees = CoreStore.queryValue(
From(MyPersonEntity), From(MyPersonEntity),
Select<Int>(.Count("employeeID")) Select<Int>(.Count("employeeID"))
) )
```
- parameter keyPath: the attribute name - 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 "count(<attributeName>)" 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 "count(<attributeName>)" is used
- returns: a `SelectTerm` to a `Select` clause for a count query - returns: a `SelectTerm` to a `Select` clause for a count query
@@ -137,12 +135,12 @@ public enum SelectTerm: StringLiteralConvertible {
/** /**
Provides a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute. Provides a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute.
```
let maximumAge = CoreStore.queryValue( let maximumAge = CoreStore.queryValue(
From(MyPersonEntity), From(MyPersonEntity),
Select<Int>(.Maximum("age")) Select<Int>(.Maximum("age"))
) )
```
- parameter keyPath: the attribute name - 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 "max(<attributeName>)" 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 "max(<attributeName>)" is used
- returns: a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute - returns: a `SelectTerm` to a `Select` clause for querying the maximum value for an attribute
@@ -159,12 +157,12 @@ public enum SelectTerm: StringLiteralConvertible {
/** /**
Provides a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute. Provides a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute.
```
let minimumAge = CoreStore.queryValue( let minimumAge = CoreStore.queryValue(
From(MyPersonEntity), From(MyPersonEntity),
Select<Int>(.Minimum("age")) Select<Int>(.Minimum("age"))
) )
```
- parameter keyPath: the attribute name - 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 "min(<attributeName>)" 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 "min(<attributeName>)" is used
- returns: a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute - returns: a `SelectTerm` to a `Select` clause for querying the minimum value for an attribute
@@ -181,12 +179,12 @@ public enum SelectTerm: StringLiteralConvertible {
/** /**
Provides a `SelectTerm` to a `Select` clause for querying the sum value for an attribute. Provides a `SelectTerm` to a `Select` clause for querying the sum value for an attribute.
```
let totalAge = CoreStore.queryValue( let totalAge = CoreStore.queryValue(
From(MyPersonEntity), From(MyPersonEntity),
Select<Int>(.Sum("age")) Select<Int>(.Sum("age"))
) )
```
- parameter keyPath: the attribute name - 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 "sum(<attributeName>)" 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 "sum(<attributeName>)" 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
@@ -233,19 +231,19 @@ public enum SelectTerm: StringLiteralConvertible {
The `Select` clause indicates the attribute / aggregate value to be queried. The generic type is a `SelectResultType`, and will be used as the return type for the query. The `Select` clause indicates the attribute / aggregate value to be queried. The generic type is a `SelectResultType`, and will be used as the return type for the query.
You can bind the return type by specializing the initializer: You can bind the return type by specializing the initializer:
```
let maximumAge = CoreStore.queryValue( let maximumAge = CoreStore.queryValue(
From(MyPersonEntity), From(MyPersonEntity),
Select<Int>(.Maximum("age")) Select<Int>(.Maximum("age"))
) )
```
or by casting the type of the return value: or by casting the type of the return value:
```
let maximumAge: Int = CoreStore.queryValue( let maximumAge: Int = CoreStore.queryValue(
From(MyPersonEntity), From(MyPersonEntity),
Select(.Maximum("age")) Select(.Maximum("age"))
) )
```
Valid return types depend on the query: Valid return types depend on the query:
- for `queryValue(...)` methods: - for `queryValue(...)` methods:
@@ -271,8 +269,6 @@ Valid return types depend on the query:
*/ */
public struct Select<T: SelectResultType> { public struct Select<T: SelectResultType> {
// MARK: Public
/** /**
The `SelectResultType` type for the query's return value The `SelectResultType` type for the query's return value
*/ */
@@ -406,12 +402,12 @@ extension Int8: SelectValueResultType {
public static func fromResultObject(result: AnyObject) -> Int8? { public static func fromResultObject(result: AnyObject) -> Int8? {
if let value = (result as? NSNumber)?.longLongValue { guard let value = (result as? NSNumber)?.longLongValue else {
return numericCast(value) as Int8
}
return nil return nil
} }
return numericCast(value) as Int8
}
} }
@@ -426,12 +422,12 @@ extension Int16: SelectValueResultType {
public static func fromResultObject(result: AnyObject) -> Int16? { public static func fromResultObject(result: AnyObject) -> Int16? {
if let value = (result as? NSNumber)?.longLongValue { guard let value = (result as? NSNumber)?.longLongValue else {
return numericCast(value) as Int16
}
return nil return nil
} }
return numericCast(value) as Int16
}
} }
@@ -446,12 +442,12 @@ extension Int32: SelectValueResultType {
public static func fromResultObject(result: AnyObject) -> Int32? { public static func fromResultObject(result: AnyObject) -> Int32? {
if let value = (result as? NSNumber)?.longLongValue { guard let value = (result as? NSNumber)?.longLongValue else {
return numericCast(value) as Int32
}
return nil return nil
} }
return numericCast(value) as Int32
}
} }
@@ -482,12 +478,12 @@ extension Int: SelectValueResultType {
public static func fromResultObject(result: AnyObject) -> Int? { public static func fromResultObject(result: AnyObject) -> Int? {
if let value = (result as? NSNumber)?.longLongValue { guard let value = (result as? NSNumber)?.longLongValue else {
return numericCast(value) as Int
}
return nil return nil
} }
return numericCast(value) as Int
}
} }
@@ -2,7 +2,7 @@
// Tweak.swift // Tweak.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -31,9 +31,8 @@ import CoreData
/** /**
The `Tweak` clause allows fine-tuning the `NSFetchRequest` for a fetch or query. The `Tweak` clause allows fine-tuning the `NSFetchRequest` for a fetch or query.
Sample usage: Sample usage:
```
let employees = transaction.fetchAll( let employees = transaction.fetchAll(
From(MyPersonEntity), From(MyPersonEntity),
Tweak { (fetchRequest) -> Void in Tweak { (fetchRequest) -> Void in
@@ -41,11 +40,10 @@ Sample usage:
fetchRequest.fetchLimit = 5 fetchRequest.fetchLimit = 5
} }
) )
```
*/ */
public struct Tweak: FetchClause, QueryClause, DeleteClause { public struct Tweak: FetchClause, QueryClause, DeleteClause {
// MARK: Public
/** /**
Initializes a `Tweak` clause with a closure where the `NSFetchRequest` may be configured. Initializes a `Tweak` clause with a closure where the `NSFetchRequest` may be configured.
@@ -65,5 +63,7 @@ public struct Tweak: FetchClause, QueryClause, DeleteClause {
} }
// MARK: Private
private let customization: (fetchRequest: NSFetchRequest) -> Void private let customization: (fetchRequest: NSFetchRequest) -> Void
} }
@@ -2,7 +2,7 @@
// Where.swift // Where.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -24,6 +24,7 @@
// //
import Foundation import Foundation
import CoreData
public func &&(left: Where, right: Where) -> Where { public func &&(left: Where, right: Where) -> Where {
@@ -49,8 +50,6 @@ The `Where` clause specifies the conditions for a fetch or a query.
*/ */
public struct Where: FetchClause, QueryClause, DeleteClause { public struct Where: FetchClause, QueryClause, DeleteClause {
// MARK: Public
/** /**
Initializes a `Where` clause with an `NSPredicate` Initializes a `Where` clause with an `NSPredicate`
@@ -102,16 +101,38 @@ public struct Where: FetchClause, QueryClause, DeleteClause {
} }
/** /**
Initializes a `Where` clause with a predicate using the specified string format and arguments Initializes a `Where` clause that compares equality
- parameter format: the format string for the predicate - parameter keyPath: the keyPath to compare with
- parameter argumentArray: the arguments for `format` - parameter value: the arguments for the `==` operator
*/ */
public init(_ keyPath: KeyPath, isEqualTo value: NSObject?) { public init(_ keyPath: KeyPath, isEqualTo value: NSObject?) {
self.init(value == nil self.init(value == nil
? NSPredicate(format: "\(keyPath) == nil") ? NSPredicate(format: "\(keyPath) == nil")
: NSPredicate(format: "\(keyPath) == %@", value!)) : NSPredicate(format: "\(keyPath) == %@", argumentArray: [value!]))
}
/**
Initializes a `Where` clause that compares membership
- parameter keyPath: the keyPath to compare with
- parameter list: the array to check membership of
*/
public init(_ keyPath: KeyPath, isMemberOf list: NSArray) {
self.init(NSPredicate(format: "\(keyPath) IN %@", list))
}
/**
Initializes a `Where` clause that compares membership
- parameter keyPath: the keyPath to compare with
- parameter list: the sequence to check membership of
*/
public init<S: SequenceType where S.Generator.Element: NSObject>(_ keyPath: KeyPath, isMemberOf list: S) {
self.init(NSPredicate(format: "\(keyPath) IN %@", Array(list) as NSArray))
} }
public let predicate: NSPredicate public let predicate: NSPredicate
@@ -121,7 +142,7 @@ public struct Where: FetchClause, QueryClause, DeleteClause {
public func applyToFetchRequest(fetchRequest: NSFetchRequest) { public func applyToFetchRequest(fetchRequest: NSFetchRequest) {
if fetchRequest.predicate != nil { if let predicate = fetchRequest.predicate where predicate != self.predicate {
CoreStore.log( CoreStore.log(
.Warning, .Warning,
@@ -2,7 +2,7 @@
// CoreStore+Querying.swift // CoreStore+Querying.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -24,19 +24,20 @@
// //
import Foundation import Foundation
import CoreData
// MARK: - CoreStore // MARK: - CoreStore
public extension CoreStore { public extension CoreStore {
// MARK: Public
/** /**
Using the `defaultStack`, fetches the `NSManagedObject` instance in the `DataStack`'s context from a reference created from a transaction or from a different managed object context. Using the `defaultStack`, fetches the `NSManagedObject` 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` - parameter object: a reference to the object created/fetched outside the `DataStack`
- returns: the `NSManagedObject` instance if the object exists in the `DataStack`, or `nil` if not found. - returns: the `NSManagedObject` instance if the object exists in the `DataStack`, or `nil` if not found.
*/ */
@warn_unused_result
public static func fetchExisting<T: NSManagedObject>(object: T) -> T? { public static func fetchExisting<T: NSManagedObject>(object: T) -> T? {
return self.defaultStack.fetchExisting(object) return self.defaultStack.fetchExisting(object)
@@ -48,6 +49,7 @@ public extension CoreStore {
- parameter objectID: the `NSManagedObjectID` for the object - parameter objectID: the `NSManagedObjectID` for the object
- returns: the `NSManagedObject` instance if the object exists in the `DataStack`, or `nil` if not found. - returns: the `NSManagedObject` instance if the object exists in the `DataStack`, or `nil` if not found.
*/ */
@warn_unused_result
public static func fetchExisting<T: NSManagedObject>(objectID: NSManagedObjectID) -> T? { public static func fetchExisting<T: NSManagedObject>(objectID: NSManagedObjectID) -> T? {
return self.defaultStack.fetchExisting(objectID) return self.defaultStack.fetchExisting(objectID)
@@ -59,7 +61,8 @@ public extension CoreStore {
- parameter objects: an array of `NSManagedObject`s created/fetched outside the `DataStack` - parameter objects: an array of `NSManagedObject`s created/fetched outside the `DataStack`
- returns: the `NSManagedObject` array for objects that exists in the `DataStack` - returns: the `NSManagedObject` array for objects that exists in the `DataStack`
*/ */
public static func fetchExisting<T: NSManagedObject>(objects: [T]) -> [T] { @warn_unused_result
public static func fetchExisting<T: NSManagedObject, S: SequenceType where S.Generator.Element == T>(objects: S) -> [T] {
return self.defaultStack.fetchExisting(objects) return self.defaultStack.fetchExisting(objects)
} }
@@ -70,7 +73,8 @@ public extension CoreStore {
- parameter objectIDs: the `NSManagedObjectID` array for the objects - parameter objectIDs: the `NSManagedObjectID` array for the objects
- returns: the `NSManagedObject` array for objects that exists in the `DataStack` - returns: the `NSManagedObject` array for objects that exists in the `DataStack`
*/ */
public static func fetchExisting<T: NSManagedObject>(objectIDs: [NSManagedObjectID]) -> [T] { @warn_unused_result
public static func fetchExisting<T: NSManagedObject, S: SequenceType where S.Generator.Element == NSManagedObjectID>(objectIDs: S) -> [T] {
return self.defaultStack.fetchExisting(objectIDs) return self.defaultStack.fetchExisting(objectIDs)
} }
@@ -82,6 +86,7 @@ public extension CoreStore {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the first `NSManagedObject` instance that satisfies the specified `FetchClause`s - returns: the first `NSManagedObject` instance that satisfies the specified `FetchClause`s
*/ */
@warn_unused_result
public static func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> T? { public static func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> T? {
return self.defaultStack.fetchOne(from, fetchClauses) return self.defaultStack.fetchOne(from, fetchClauses)
@@ -94,6 +99,7 @@ public extension CoreStore {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the first `NSManagedObject` instance that satisfies the specified `FetchClause`s - returns: the first `NSManagedObject` instance that satisfies the specified `FetchClause`s
*/ */
@warn_unused_result
public static func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> T? { public static func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> T? {
return self.defaultStack.fetchOne(from, fetchClauses) return self.defaultStack.fetchOne(from, fetchClauses)
@@ -106,6 +112,7 @@ public extension CoreStore {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s - returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s
*/ */
@warn_unused_result
public static func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> [T]? { public static func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> [T]? {
return self.defaultStack.fetchAll(from, fetchClauses) return self.defaultStack.fetchAll(from, fetchClauses)
@@ -118,6 +125,7 @@ public extension CoreStore {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s - returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s
*/ */
@warn_unused_result
public static func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [T]? { public static func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [T]? {
return self.defaultStack.fetchAll(from, fetchClauses) return self.defaultStack.fetchAll(from, fetchClauses)
@@ -130,6 +138,7 @@ public extension CoreStore {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s - returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s
*/ */
@warn_unused_result
public static func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> Int? { public static func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> Int? {
return self.defaultStack.fetchCount(from, fetchClauses) return self.defaultStack.fetchCount(from, fetchClauses)
@@ -142,6 +151,7 @@ public extension CoreStore {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s - returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s
*/ */
@warn_unused_result
public static func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> Int? { public static func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> Int? {
return self.defaultStack.fetchCount(from, fetchClauses) return self.defaultStack.fetchCount(from, fetchClauses)
@@ -154,6 +164,7 @@ public extension CoreStore {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s - returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s
*/ */
@warn_unused_result
public static func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> NSManagedObjectID? { public static func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> NSManagedObjectID? {
return self.defaultStack.fetchObjectID(from, fetchClauses) return self.defaultStack.fetchObjectID(from, fetchClauses)
@@ -166,6 +177,7 @@ public extension CoreStore {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s - returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s
*/ */
@warn_unused_result
public static func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? { public static func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? {
return self.defaultStack.fetchObjectID(from, fetchClauses) return self.defaultStack.fetchObjectID(from, fetchClauses)
@@ -178,6 +190,7 @@ public extension CoreStore {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s - returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s
*/ */
@warn_unused_result
public static func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? { public static func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? {
return self.defaultStack.fetchObjectIDs(from, fetchClauses) return self.defaultStack.fetchObjectIDs(from, fetchClauses)
@@ -190,6 +203,7 @@ public extension CoreStore {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s - returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s
*/ */
@warn_unused_result
public static func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? { public static func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? {
return self.defaultStack.fetchObjectIDs(from, fetchClauses) return self.defaultStack.fetchObjectIDs(from, fetchClauses)
@@ -205,6 +219,7 @@ public extension CoreStore {
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. - 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. - returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/ */
@warn_unused_result
public static func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: QueryClause...) -> U? { public static func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: QueryClause...) -> U? {
return self.defaultStack.queryValue(from, selectClause, queryClauses) return self.defaultStack.queryValue(from, selectClause, queryClauses)
@@ -220,6 +235,7 @@ public extension CoreStore {
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. - 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. - returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/ */
@warn_unused_result
public static func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: [QueryClause]) -> U? { public static func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: [QueryClause]) -> U? {
return self.defaultStack.queryValue(from, selectClause, queryClauses) return self.defaultStack.queryValue(from, selectClause, queryClauses)
@@ -235,6 +251,7 @@ public extension CoreStore {
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. - 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. - returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/ */
@warn_unused_result
public static func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: QueryClause...) -> [[NSString: AnyObject]]? { public static func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: QueryClause...) -> [[NSString: AnyObject]]? {
return self.defaultStack.queryAttributes(from, selectClause, queryClauses) return self.defaultStack.queryAttributes(from, selectClause, queryClauses)
@@ -250,6 +267,7 @@ public extension CoreStore {
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. - 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. - returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/ */
@warn_unused_result
public static func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? { public static func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? {
return self.defaultStack.queryAttributes(from, selectClause, queryClauses) return self.defaultStack.queryAttributes(from, selectClause, queryClauses)
@@ -2,7 +2,7 @@
// DataStack+Querying.swift // DataStack+Querying.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -25,21 +25,22 @@
import Foundation import Foundation
import CoreData import CoreData
#if USE_FRAMEWORKS
import GCDKit import GCDKit
#endif
// MARK: - DataStack // MARK: - DataStack
public extension DataStack { public extension DataStack {
// MARK: Public
/** /**
Fetches the `NSManagedObject` instance in the `DataStack`'s context from a reference created from a transaction or from a different managed object context. Fetches the `NSManagedObject` 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` - parameter object: a reference to the object created/fetched outside the `DataStack`
- returns: the `NSManagedObject` instance if the object exists in the `DataStack`, or `nil` if not found. - returns: the `NSManagedObject` instance if the object exists in the `DataStack`, or `nil` if not found.
*/ */
@warn_unused_result
public func fetchExisting<T: NSManagedObject>(object: T) -> T? { public func fetchExisting<T: NSManagedObject>(object: T) -> T? {
do { do {
@@ -58,6 +59,7 @@ public extension DataStack {
- parameter objectID: the `NSManagedObjectID` for the object - parameter objectID: the `NSManagedObjectID` for the object
- returns: the `NSManagedObject` instance if the object exists in the `DataStack`, or `nil` if not found. - returns: the `NSManagedObject` instance if the object exists in the `DataStack`, or `nil` if not found.
*/ */
@warn_unused_result
public func fetchExisting<T: NSManagedObject>(objectID: NSManagedObjectID) -> T? { public func fetchExisting<T: NSManagedObject>(objectID: NSManagedObjectID) -> T? {
do { do {
@@ -76,19 +78,10 @@ public extension DataStack {
- parameter objects: an array of `NSManagedObject`s created/fetched outside the `DataStack` - parameter objects: an array of `NSManagedObject`s created/fetched outside the `DataStack`
- returns: the `NSManagedObject` array for objects that exists in the `DataStack` - returns: the `NSManagedObject` array for objects that exists in the `DataStack`
*/ */
public func fetchExisting<T: NSManagedObject>(objects: [T]) -> [T] { @warn_unused_result
public func fetchExisting<T: NSManagedObject, S: SequenceType where S.Generator.Element == T>(objects: S) -> [T] {
var existingObjects = [T]() return objects.flatMap { (try? self.mainContext.existingObjectWithID($0.objectID)) as? T }
for object in objects {
do {
let existingObject = try self.mainContext.existingObjectWithID(object.objectID) as! T
existingObjects.append(existingObject)
}
catch _ { }
}
return existingObjects
} }
/** /**
@@ -97,19 +90,10 @@ public extension DataStack {
- parameter objectIDs: the `NSManagedObjectID` array for the objects - parameter objectIDs: the `NSManagedObjectID` array for the objects
- returns: the `NSManagedObject` array for objects that exists in the `DataStack` - returns: the `NSManagedObject` array for objects that exists in the `DataStack`
*/ */
public func fetchExisting<T: NSManagedObject>(objectIDs: [NSManagedObjectID]) -> [T] { @warn_unused_result
public func fetchExisting<T: NSManagedObject, S: SequenceType where S.Generator.Element == NSManagedObjectID>(objectIDs: S) -> [T] {
var existingObjects = [T]() return objectIDs.flatMap { (try? self.mainContext.existingObjectWithID($0)) as? T }
for objectID in objectIDs {
do {
let existingObject = try self.mainContext.existingObjectWithID(objectID) as! T
existingObjects.append(existingObject)
}
catch _ { }
}
return existingObjects
} }
/** /**
@@ -119,6 +103,7 @@ public extension DataStack {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the first `NSManagedObject` instance that satisfies the specified `FetchClause`s - returns: the first `NSManagedObject` instance that satisfies the specified `FetchClause`s
*/ */
@warn_unused_result
public func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> T? { public func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> T? {
CoreStore.assert( CoreStore.assert(
@@ -136,6 +121,7 @@ public extension DataStack {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the first `NSManagedObject` instance that satisfies the specified `FetchClause`s - returns: the first `NSManagedObject` instance that satisfies the specified `FetchClause`s
*/ */
@warn_unused_result
public func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> T? { public func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> T? {
CoreStore.assert( CoreStore.assert(
@@ -153,6 +139,7 @@ public extension DataStack {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s - returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s
*/ */
@warn_unused_result
public func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> [T]? { public func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> [T]? {
CoreStore.assert( CoreStore.assert(
@@ -170,6 +157,7 @@ public extension DataStack {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s - returns: all `NSManagedObject` instances that satisfy the specified `FetchClause`s
*/ */
@warn_unused_result
public func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [T]? { public func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [T]? {
CoreStore.assert( CoreStore.assert(
@@ -187,6 +175,7 @@ public extension DataStack {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s - returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s
*/ */
@warn_unused_result
public func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> Int? { public func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> Int? {
CoreStore.assert( CoreStore.assert(
@@ -204,6 +193,7 @@ public extension DataStack {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s - returns: the number `NSManagedObject`s that satisfy the specified `FetchClause`s
*/ */
@warn_unused_result
public func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> Int? { public func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> Int? {
CoreStore.assert( CoreStore.assert(
@@ -221,6 +211,7 @@ public extension DataStack {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s - returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s
*/ */
@warn_unused_result
public func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> NSManagedObjectID? { public func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> NSManagedObjectID? {
CoreStore.assert( CoreStore.assert(
@@ -238,6 +229,7 @@ public extension DataStack {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s - returns: the `NSManagedObjectID` for the first `NSManagedObject` that satisfies the specified `FetchClause`s
*/ */
@warn_unused_result
public func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? { public func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? {
CoreStore.assert( CoreStore.assert(
@@ -255,6 +247,7 @@ public extension DataStack {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s - returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s
*/ */
@warn_unused_result
public func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? { public func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> [NSManagedObjectID]? {
CoreStore.assert( CoreStore.assert(
@@ -272,6 +265,7 @@ public extension DataStack {
- parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - parameter fetchClauses: a series of `FetchClause` instances for the fetch request. Accepts `Where`, `OrderBy`, and `Tweak` clauses.
- returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s - returns: the `NSManagedObjectID` for all `NSManagedObject`s that satisfy the specified `FetchClause`s
*/ */
@warn_unused_result
public func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? { public func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? {
CoreStore.assert( CoreStore.assert(
@@ -292,6 +286,7 @@ public extension DataStack {
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. - 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. - returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/ */
@warn_unused_result
public func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: QueryClause...) -> U? { public func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: QueryClause...) -> U? {
CoreStore.assert( CoreStore.assert(
@@ -312,6 +307,7 @@ public extension DataStack {
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. - 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. - returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/ */
@warn_unused_result
public func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: [QueryClause]) -> U? { public func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: [QueryClause]) -> U? {
CoreStore.assert( CoreStore.assert(
@@ -332,6 +328,7 @@ public extension DataStack {
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. - 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. - returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/ */
@warn_unused_result
public func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: QueryClause...) -> [[NSString: AnyObject]]? { public func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: QueryClause...) -> [[NSString: AnyObject]]? {
CoreStore.assert( CoreStore.assert(
@@ -352,6 +349,7 @@ public extension DataStack {
- parameter queryClauses: a series of `QueryClause` instances for the query request. Accepts `Where`, `OrderBy`, `GroupBy`, and `Tweak` clauses. - 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. - returns: the result of the the query. The type of the return value is specified by the generic type of the `Select<U>` parameter.
*/ */
@warn_unused_result
public func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? { public func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? {
CoreStore.assert( CoreStore.assert(
@@ -2,7 +2,7 @@
// ClauseTypes.swift // ClauseTypes.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2014 John Rommel Estropia // Copyright © 2014 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -29,6 +29,9 @@ import CoreData
// MARK: - FetchClause // MARK: - FetchClause
/**
The `FetchClause` implement clauses used to configure `NSFetchRequest`s.
*/
public protocol FetchClause { public protocol FetchClause {
func applyToFetchRequest(fetchRequest: NSFetchRequest) func applyToFetchRequest(fetchRequest: NSFetchRequest)
@@ -37,6 +40,9 @@ public protocol FetchClause {
// MARK: - QueryClause // MARK: - QueryClause
/**
The `QueryClause` implement clauses used to configure `NSFetchRequest`s.
*/
public protocol QueryClause { public protocol QueryClause {
func applyToFetchRequest(fetchRequest: NSFetchRequest) func applyToFetchRequest(fetchRequest: NSFetchRequest)
@@ -45,6 +51,9 @@ public protocol QueryClause {
// MARK: - DeleteClause // MARK: - DeleteClause
/**
The `DeleteClause` implement clauses used to configure `NSFetchRequest`s.
*/
public protocol DeleteClause { public protocol DeleteClause {
func applyToFetchRequest(fetchRequest: NSFetchRequest) func applyToFetchRequest(fetchRequest: NSFetchRequest)
@@ -2,7 +2,7 @@
// BaseDataTransaction+Importing.swift // BaseDataTransaction+Importing.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -27,246 +27,200 @@ import Foundation
import CoreData import CoreData
public protocol ImportableObject: class { // MARK: - BaseDataTransaction
typealias ImportSource
static func shouldImportFromSource(source: ImportSource) -> Bool
func didInsertFromImportSource(source: ImportSource) throws
func updateFromImportSource(source: ImportSource) throws
}
public extension ImportableObject {
static func shouldImportFromSource(source: ImportSource) -> Bool {
return true
}
}
public protocol ImportableUniqueObject: ImportableObject {
typealias UniqueIDType: NSObject
static var uniqueIDKeyPath: String { get }
var uniqueIDValue: UniqueIDType { get set }
static func uniqueIDFromImportSource(source: ImportSource) throws -> UniqueIDType
}
public extension BaseDataTransaction { public extension BaseDataTransaction {
func importObject<T where T: NSManagedObject, T: ImportableObject>( /**
Creates an `ImportableObject` by importing from the specified import source.
- parameter into: an `Into` clause specifying the entity type
- parameter source: the object to import values from
- returns: the created `ImportableObject` instance, or `nil` if the import was ignored
*/
public func importObject<T where T: NSManagedObject, T: ImportableObject>(
into: Into<T>, into: Into<T>,
source: T.ImportSource) throws -> T? { source: T.ImportSource) throws -> T? {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to import an object of type \(typeName(into.entityClass)) outside the transaction's designated queue." "Attempted to import an object of type \(typeName(into.entityClass)) outside the transaction's designated queue."
) )
return try autoreleasepool { return try autoreleasepool {
if !T.shouldImportFromSource(source) { guard T.shouldInsertFromImportSource(source, inTransaction: self) else {
return nil return nil
} }
let object = self.create(into) let object = self.create(into)
try object.didInsertFromImportSource(source) try object.didInsertFromImportSource(source, inTransaction: self)
return object return object
} }
} }
func importObjects<T where T: NSManagedObject, T: ImportableObject>( /**
into: Into<T>, Updates an existing `ImportableObject` by importing values from the specified import source.
sourceArray: [T.ImportSource]) throws {
- parameter object: the `NSManagedObject` to update
- parameter source: the object to import values from
*/
public func importObject<T where T: NSManagedObject, T: ImportableObject>(
object: T,
source: T.ImportSource) throws {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to import an object of type \(typeName(into.entityClass)) outside the transaction's designated queue." "Attempted to import an object of type \(typeName(object)) outside the transaction's designated queue."
) )
try autoreleasepool { try autoreleasepool {
for source in sourceArray { guard T.shouldInsertFromImportSource(source, inTransaction: self) else {
try autoreleasepool { return
}
let object = self.create(into) try object.didInsertFromImportSource(source, inTransaction: self)
try object.didInsertFromImportSource(source)
}
}
} }
} }
func importObjects<T where T: NSManagedObject, T: ImportableObject>( /**
Creates multiple `ImportableObject`s by importing from the specified array of import sources.
- parameter into: an `Into` clause specifying the entity type
- parameter sourceArray: the array of objects to import values from
- returns: the array of created `ImportableObject` instances
*/
public func importObjects<T, S: SequenceType where T: NSManagedObject, T: ImportableObject, S.Generator.Element == T.ImportSource>(
into: Into<T>, into: Into<T>,
sourceArray: [T.ImportSource], sourceArray: S) throws -> [T] {
postProcess: (sorted: [T]) -> Void) throws {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to import an object of type \(typeName(into.entityClass)) outside the transaction's designated queue."
)
try autoreleasepool {
var objects = [T]()
for source in sourceArray {
try autoreleasepool {
let object = self.create(into)
try object.didInsertFromImportSource(source)
objects.append(object)
}
}
postProcess(sorted: objects)
}
}
func importUniqueObject<T where T: NSManagedObject, T: ImportableUniqueObject>(
into: Into<T>,
source: T.ImportSource) throws -> T? {
CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(),
"Attempted to import an object of type \(typeName(into.entityClass)) outside the transaction's designated queue." "Attempted to import an object of type \(typeName(into.entityClass)) outside the transaction's designated queue."
) )
return try autoreleasepool { return try autoreleasepool {
if !T.shouldImportFromSource(source) { return try sourceArray.flatMap { (source) -> T? in
guard T.shouldInsertFromImportSource(source, inTransaction: self) else {
return nil return nil
} }
return try autoreleasepool {
let object = self.create(into)
try object.didInsertFromImportSource(source, inTransaction: self)
return object
}
}
}
}
/**
Updates an existing `ImportableUniqueObject` or creates a new instance by importing from the specified import source.
- parameter into: an `Into` clause specifying the entity type
- parameter source: the object to import values from
- returns: the created/updated `ImportableUniqueObject` instance, or `nil` if the import was ignored
*/
public func importUniqueObject<T where T: NSManagedObject, T: ImportableUniqueObject>(
into: Into<T>,
source: T.ImportSource) throws -> T? {
CoreStore.assert(
self.isRunningInAllowedQueue(),
"Attempted to import an object of type \(typeName(into.entityClass)) outside the transaction's designated queue."
)
return try autoreleasepool {
let uniqueIDKeyPath = T.uniqueIDKeyPath let uniqueIDKeyPath = T.uniqueIDKeyPath
let uniqueIDValue = try T.uniqueIDFromImportSource(source) guard let uniqueIDValue = try T.uniqueIDFromImportSource(source, inTransaction: self) else {
return nil
}
if let object = self.fetchOne(From(T), Where(uniqueIDKeyPath, isEqualTo: uniqueIDValue)) { if let object = self.fetchOne(From(T), Where(uniqueIDKeyPath, isEqualTo: uniqueIDValue)) {
try object.updateFromImportSource(source) guard T.shouldUpdateFromImportSource(source, inTransaction: self) else {
return nil
}
try object.updateFromImportSource(source, inTransaction: self)
return object return object
} }
else { else {
guard T.shouldInsertFromImportSource(source, inTransaction: self) else {
return nil
}
let object = self.create(into) let object = self.create(into)
object.uniqueIDValue = uniqueIDValue object.uniqueIDValue = uniqueIDValue
try object.didInsertFromImportSource(source) try object.didInsertFromImportSource(source, inTransaction: self)
return object return object
} }
} }
} }
func importUniqueObjects<T where T: NSManagedObject, T: ImportableUniqueObject>( /**
Updates existing `ImportableUniqueObject`s or creates them by importing from the specified array of import sources.
- parameter into: an `Into` clause specifying the entity type
- parameter sourceArray: the array of objects to import values from
- parameter preProcess: a closure that lets the caller tweak the internal `UniqueIDType`-to-`ImportSource` mapping to be used for importing. Callers can remove from/add to/update `mapping` and return the updated array from the closure.
- returns: the array of created/updated `ImportableUniqueObject` instances
*/
public func importUniqueObjects<T, S: SequenceType where T: NSManagedObject, T: ImportableUniqueObject, S.Generator.Element == T.ImportSource>(
into: Into<T>, into: Into<T>,
sourceArray: [T.ImportSource], sourceArray: S,
preProcess: ((mapping: [T.UniqueIDType: T.ImportSource]) throws -> Void)? = nil) throws { @noescape preProcess: (mapping: [T.UniqueIDType: T.ImportSource]) throws -> [T.UniqueIDType: T.ImportSource] = { $0 }) throws -> [T] {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to import an object of type \(typeName(into.entityClass)) outside the transaction's designated queue." "Attempted to import an object of type \(typeName(into.entityClass)) outside the transaction's designated queue."
) )
try autoreleasepool { return try autoreleasepool {
var mapping = Dictionary<T.UniqueIDType, T.ImportSource>() var mapping = Dictionary<T.UniqueIDType, T.ImportSource>()
for source in sourceArray { let sortedIDs = try autoreleasepool {
try autoreleasepool { return try sourceArray.flatMap { (source) -> T.UniqueIDType? in
if !T.shouldImportFromSource(source) { guard let uniqueIDValue = try T.uniqueIDFromImportSource(source, inTransaction: self) else {
return return nil
} }
let uniqueIDValue = try T.uniqueIDFromImportSource(source)
mapping[uniqueIDValue] = source mapping[uniqueIDValue] = source
return uniqueIDValue
} }
} }
if let preProcess = preProcess { mapping = try autoreleasepool { try preProcess(mapping: mapping) }
try autoreleasepool {
try preProcess(mapping: mapping)
}
}
for object in self.fetchAll(From(T), Where("%K IN %@", T.uniqueIDKeyPath, mapping.keys.array)) ?? [] {
try autoreleasepool {
let uniqueIDValue = object.uniqueIDValue
try object.updateFromImportSource(mapping.removeValueForKey(uniqueIDValue)!)
}
}
for (uniqueIDValue, source) in mapping {
try autoreleasepool {
let object = self.create(into)
object.uniqueIDValue = uniqueIDValue
try object.didInsertFromImportSource(source)
}
}
}
}
func importUniqueObjects<T where T: NSManagedObject, T: ImportableUniqueObject>(
into: Into<T>,
sourceArray: [T.ImportSource],
preProcess: ((mapping: [T.UniqueIDType: T.ImportSource]) throws -> Void)? = nil,
postProcess: (sorted: [T]) -> Void) throws {
CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(),
"Attempted to import an object of type \(typeName(into.entityClass)) outside the transaction's designated queue."
)
try autoreleasepool {
var sortedIDs = Array<T.UniqueIDType>()
var mapping = Dictionary<T.UniqueIDType, T.ImportSource>()
for source in sourceArray {
try autoreleasepool {
if !T.shouldImportFromSource(source) {
return
}
let uniqueIDValue = try T.uniqueIDFromImportSource(source)
mapping[uniqueIDValue] = source
sortedIDs.append(uniqueIDValue)
}
}
if let preProcess = preProcess {
try autoreleasepool {
try preProcess(mapping: mapping)
}
}
var objects = Dictionary<T.UniqueIDType, T>() var objects = Dictionary<T.UniqueIDType, T>()
for object in self.fetchAll(From(T), Where("%K IN %@", T.uniqueIDKeyPath, mapping.keys.array)) ?? [] { for object in self.fetchAll(From(T), Where(T.uniqueIDKeyPath, isMemberOf: mapping.keys)) ?? [] {
try autoreleasepool { try autoreleasepool {
let uniqueIDValue = object.uniqueIDValue let uniqueIDValue = object.uniqueIDValue
try object.updateFromImportSource(mapping.removeValueForKey(uniqueIDValue)!)
guard let source = mapping.removeValueForKey(uniqueIDValue)
where T.shouldUpdateFromImportSource(source, inTransaction: self) else {
return
}
try object.updateFromImportSource(source, inTransaction: self)
objects[uniqueIDValue] = object objects[uniqueIDValue] = object
} }
} }
@@ -275,15 +229,20 @@ public extension BaseDataTransaction {
try autoreleasepool { try autoreleasepool {
guard T.shouldInsertFromImportSource(source, inTransaction: self) else {
return
}
let object = self.create(into) let object = self.create(into)
object.uniqueIDValue = uniqueIDValue object.uniqueIDValue = uniqueIDValue
try object.didInsertFromImportSource(source) try object.didInsertFromImportSource(source, inTransaction: self)
objects[uniqueIDValue] = object objects[uniqueIDValue] = object
} }
} }
postProcess(sorted: sortedIDs.flatMap { objects[$0] }) return sortedIDs.flatMap { objects[$0] }
} }
} }
} }
@@ -0,0 +1,85 @@
//
// ImportableObject.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: - ImportableObject
/**
`NSManagedObject` subclasses that conform to the `ImportableObject` protocol can be imported from a specified `ImportSource`. This allows transactions to create and insert instances this way:
```
class MyPersonEntity: NSManagedObject, ImportableObject {
typealias ImportSource = NSDictionary
// ...
}
CoreStore.beginAsynchronous { (transaction) -> Void in
let json: NSDictionary = // ...
let person = try! transaction.importObject(
Into(MyPersonEntity),
source: json
)
// ...
transaction.commit()
}
```
*/
public protocol ImportableObject: class {
/**
The data type for the import source. This is most commonly an `NSDictionary` or another external source such as an `NSUserDefaults`.
*/
associatedtype ImportSource
/**
Return `true` if an object should be created from `source`. Return `false` to ignore and skip `source`. The default implementation returns `true`.
- parameter source: the object to import from
- parameter transaction: the transaction that invoked the import. Use the transaction to fetch or create related objects if needed.
- returns: `true` if an object should be created from `source`. Return `false` to ignore.
*/
static func shouldInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -> Bool
/**
Implements the actual importing of data from `source`. Implementers should pull values from `source` and assign them to the receiver's attributes. Note that throwing from this method will cause subsequent imports that are part of the same `importObjects(:sourceArray:)` call to be cancelled.
- parameter source: the object to import from
- parameter transaction: the transaction that invoked the import. Use the transaction to fetch or create related objects if needed.
*/
func didInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws
}
// MARK: - ImportableObject (Default Implementations)
public extension ImportableObject {
static func shouldInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -> Bool {
return true
}
}
@@ -0,0 +1,137 @@
//
// ImportableUniqueObject.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: - ImportableUniqueObject
/**
`NSManagedObject` subclasses that conform to the `ImportableUniqueObject` protocol can be imported from a specified `ImportSource`. This allows transactions to either update existing objects or create new instances this way:
```
class MyPersonEntity: NSManagedObject, ImportableUniqueObject {
typealias ImportSource = NSDictionary
typealias UniqueIDType = NSString
// ...
}
CoreStore.beginAsynchronous { (transaction) -> Void in
let json: NSDictionary = // ...
let person = try! transaction.importUniqueObject(
Into(MyPersonEntity),
source: json
)
// ...
transaction.commit()
}
```
*/
public protocol ImportableUniqueObject: ImportableObject {
/**
The data type for the import source. This is most commonly an `NSDictionary` or another external source such as an `NSUserDefaults`.
*/
associatedtype ImportSource
/**
The data type for the entity's unique ID attribute
*/
associatedtype UniqueIDType: NSObject
/**
The keyPath to the entity's unique ID attribute
*/
static var uniqueIDKeyPath: String { get }
/**
The object's unique ID value
*/
var uniqueIDValue: UniqueIDType { get set }
/**
Return `true` if an object should be created from `source`. Return `false` to ignore and skip `source`. The default implementation returns the value returned by the `shouldUpdateFromImportSource(:inTransaction:)` implementation.
- parameter source: the object to import from
- parameter transaction: the transaction that invoked the import. Use the transaction to fetch or create related objects if needed.
- returns: `true` if an object should be created from `source`. Return `false` to ignore.
*/
static func shouldInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -> Bool
/**
Return `true` if an object should be updated from `source`. Return `false` to ignore and skip `source`. The default implementation returns `true`.
- parameter source: the object to import from
- parameter transaction: the transaction that invoked the import. Use the transaction to fetch or create related objects if needed.
- returns: `true` if an object should be updated from `source`. Return `false` to ignore.
*/
static func shouldUpdateFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -> Bool
/**
Return the unique ID as extracted from `source`. This method is called before `shouldInsertFromImportSource(...)` or `shouldUpdateFromImportSource(...)`. Return `nil` to skip importing from `source`. Note that throwing from this method will cause subsequent imports that are part of the same `importUniqueObjects(:sourceArray:)` call to be cancelled.
- parameter source: the object to import from
- parameter transaction: the transaction that invoked the import. Use the transaction to fetch or create related objects if needed.
- returns: the unique ID as extracted from `source`, or `nil` to skip importing from `source`.
*/
static func uniqueIDFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws -> UniqueIDType?
/**
Implements the actual importing of data from `source`. This method is called just after the object is created and assigned its unique ID as returned from `uniqueIDFromImportSource(...)`. Implementers should pull values from `source` and assign them to the receiver's attributes. Note that throwing from this method will cause subsequent imports that are part of the same `importUniqueObjects(:sourceArray:)` call to be cancelled. The default implementation simply calls `updateFromImportSource(...)`.
- parameter source: the object to import from
- parameter transaction: the transaction that invoked the import. Use the transaction to fetch or create related objects if needed.
*/
func didInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws
/**
Implements the actual importing of data from `source`. This method is called just after the existing object is fetched using its unique ID. Implementers should pull values from `source` and assign them to the receiver's attributes. Note that throwing from this method will cause subsequent imports that are part of the same `importUniqueObjects(:sourceArray:)` call to be cancelled.
- parameter source: the object to import from
- parameter transaction: the transaction that invoked the import. Use the transaction to fetch or create related objects if needed.
*/
func updateFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws
}
// MARK: - ImportableUniqueObject (Default Implementations)
public extension ImportableUniqueObject {
static func shouldInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -> Bool {
return self.shouldUpdateFromImportSource(source, inTransaction: transaction)
}
static func shouldUpdateFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) -> Bool {
return true
}
func didInsertFromImportSource(source: ImportSource, inTransaction transaction: BaseDataTransaction) throws {
try self.updateFromImportSource(source, inTransaction: transaction)
}
}
+1 -1
View File
@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>FMWK</string> <string>FMWK</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.2.0</string> <string>1.6.8</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
+1 -1
View File
@@ -2,7 +2,7 @@
// NSObject+CoreStore.swift // NSObject+CoreStore.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2014 John Rommel Estropia // Copyright © 2014 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -0,0 +1,43 @@
//
// CoreStoreFetchRequest.swift
// CoreStore
//
// Copyright © 2016 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: - CoreStoreFetchRequest
// Bugfix for NSFetchRequest messing up memory management for `affectedStores`
// http://stackoverflow.com/questions/14396375/nsfetchedresultscontroller-crashes-in-ios-6-if-affectedstores-is-specified
internal final class CoreStoreFetchRequest: NSFetchRequest {
override var affectedStores: [NSPersistentStore]? {
get { return self.safeAffectedStores }
set { self.safeAffectedStores = newValue }
}
private var safeAffectedStores: [NSPersistentStore]?
}
@@ -0,0 +1,110 @@
//
// CoreStoreFetchedResultsController.swift
// CoreStore
//
// Copyright © 2016 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: - CoreStoreFetchedResultsController
@available(OSX, unavailable)
internal final class CoreStoreFetchedResultsController<T: NSManagedObject>: NSFetchedResultsController {
// MARK: Internal
internal convenience init<T>(dataStack: DataStack, fetchRequest: NSFetchRequest, from: From<T>? = nil, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) {
self.init(
context: dataStack.mainContext,
fetchRequest: fetchRequest,
from: from,
sectionBy: sectionBy,
fetchClauses: fetchClauses
)
}
internal init<T>(context: NSManagedObjectContext, fetchRequest: NSFetchRequest, from: From<T>? = nil, sectionBy: SectionBy? = nil, fetchClauses: [FetchClause]) {
CoreStore.assert(
fetchClauses.filter { $0 is OrderBy }.count > 0,
"An \(typeName(NSFetchedResultsController)) requires an OrderBy clause."
)
from?.applyToFetchRequest(fetchRequest, context: context, applyAffectedStores: false)
for clause in fetchClauses {
clause.applyToFetchRequest(fetchRequest)
}
if let from = from {
self.reapplyAffectedStores = { fetchRequest, context in
return from.applyAffectedStoresForFetchedRequest(fetchRequest, context: context)
}
}
else {
guard let from = (fetchRequest.entity.flatMap { $0.managedObjectClassName }).flatMap(NSClassFromString).flatMap(From.init) else {
fatalError("Attempted to create an \(typeName(NSFetchedResultsController)) without a From clause or an NSEntityDescription.")
}
self.reapplyAffectedStores = { fetchRequest, context in
return from.applyAffectedStoresForFetchedRequest(fetchRequest, context: context)
}
}
super.init(
fetchRequest: fetchRequest,
managedObjectContext: context,
sectionNameKeyPath: sectionBy?.sectionKeyPath,
cacheName: nil
)
}
internal func performFetchFromSpecifiedStores() throws {
if !self.reapplyAffectedStores(fetchRequest: self.fetchRequest, context: self.managedObjectContext) {
CoreStore.log(
.Warning,
message: "Attempted to perform a fetch on an \(typeName(NSFetchedResultsController)) but could not find any persistent store for the entity \(typeName(self.fetchRequest.entityName))"
)
}
try self.performFetch()
}
deinit {
self.delegate = nil
}
// MARK: Private
private let reapplyAffectedStores: (fetchRequest: NSFetchRequest, context: NSManagedObjectContext) -> Bool
}
@@ -0,0 +1,219 @@
//
// FetchedResultsControllerDelegate.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: - FetchedResultsControllerHandler
@available(OSX, unavailable)
internal protocol FetchedResultsControllerHandler: class {
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?)
func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType)
func controllerWillChangeContent(controller: NSFetchedResultsController)
func controllerDidChangeContent(controller: NSFetchedResultsController)
func controller(controller: NSFetchedResultsController, sectionIndexTitleForSectionName sectionName: String?) -> String?
}
// MARK: - FetchedResultsControllerDelegate
@available(OSX, unavailable)
internal final class FetchedResultsControllerDelegate: NSObject, NSFetchedResultsControllerDelegate {
// MARK: Internal
internal var enabled = true
internal weak var handler: FetchedResultsControllerHandler?
internal weak var fetchedResultsController: NSFetchedResultsController? {
didSet {
oldValue?.delegate = nil
self.fetchedResultsController?.delegate = self
}
}
deinit {
self.fetchedResultsController?.delegate = nil
}
// MARK: NSFetchedResultsControllerDelegate
@objc dynamic func controllerWillChangeContent(controller: NSFetchedResultsController) {
guard self.enabled else {
return
}
self.deletedSections = []
self.insertedSections = []
self.handler?.controllerWillChangeContent(controller)
}
@objc dynamic func controllerDidChangeContent(controller: NSFetchedResultsController) {
guard self.enabled else {
return
}
self.handler?.controllerDidChangeContent(controller)
}
@objc dynamic func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
guard self.enabled else {
return
}
guard let actualType = NSFetchedResultsChangeType(rawValue: type.rawValue) else {
// This fix is for a bug where iOS passes 0 for NSFetchedResultsChangeType, but this is not a valid enum case.
// Swift will then always execute the first case of the switch causing strange behaviour.
// https://forums.developer.apple.com/thread/12184#31850
return
}
// This whole dance is a workaround for a nasty bug introduced in XCode 7 targeted at iOS 8 devices
// http://stackoverflow.com/questions/31383760/ios-9-attempt-to-delete-and-reload-the-same-index-path/31384014#31384014
// https://forums.developer.apple.com/message/9998#9998
// https://forums.developer.apple.com/message/31849#31849
switch actualType {
case .Update:
guard let section = indexPath?.indexAtPosition(0) else {
return
}
if self.deletedSections.contains(section)
|| self.insertedSections.contains(section) {
return
}
case .Move:
guard let indexPath = indexPath, let newIndexPath = newIndexPath else {
return
}
guard indexPath == newIndexPath else {
break
}
if self.insertedSections.contains(indexPath.indexAtPosition(0)) {
// Observers that handle the .Move change are advised to delete then reinsert the object instead of just moving. This is especially true when indexPath and newIndexPath are equal. For example, calling tableView.moveRowAtIndexPath(_:toIndexPath) when both indexPaths are the same will crash the tableView.
self.handler?.controller(
controller,
didChangeObject: anObject,
atIndexPath: indexPath,
forChangeType: .Move,
newIndexPath: newIndexPath
)
return
}
if self.deletedSections.contains(indexPath.indexAtPosition(0)) {
self.handler?.controller(
controller,
didChangeObject: anObject,
atIndexPath: nil,
forChangeType: .Insert,
newIndexPath: indexPath
)
return
}
self.handler?.controller(
controller,
didChangeObject: anObject,
atIndexPath: indexPath,
forChangeType: .Update,
newIndexPath: nil
)
return
default:
break
}
self.handler?.controller(
controller,
didChangeObject: anObject,
atIndexPath: indexPath,
forChangeType: actualType,
newIndexPath: newIndexPath
)
}
@objc dynamic func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
guard self.enabled else {
return
}
switch type {
case .Delete: self.deletedSections.insert(sectionIndex)
case .Insert: self.insertedSections.insert(sectionIndex)
default: break
}
self.handler?.controller(
controller,
didChangeSection: sectionInfo,
atIndex: sectionIndex,
forChangeType: type
)
}
@objc dynamic func controller(controller: NSFetchedResultsController, sectionIndexTitleForSectionName sectionName: String) -> String? {
return self.handler?.controller(
controller,
sectionIndexTitleForSectionName: sectionName
)
}
// MARK: Private
private var deletedSections = Set<Int>()
private var insertedSections = Set<Int>()
}
+5 -5
View File
@@ -2,7 +2,7 @@
// Functions.swift // Functions.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2014 John Rommel Estropia // Copyright © 2014 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -25,9 +25,9 @@
import Foundation import Foundation
internal func autoreleasepool<T>(@noescape closure: () -> T?) -> T? { internal func autoreleasepool<T>(@noescape closure: () -> T) -> T {
var closureValue: T? var closureValue: T!
ObjectiveC.autoreleasepool { ObjectiveC.autoreleasepool {
closureValue = closure() closureValue = closure()
@@ -36,9 +36,9 @@ internal func autoreleasepool<T>(@noescape closure: () -> T?) -> T? {
return closureValue return closureValue
} }
internal func autoreleasepool<T>(@noescape closure: () throws -> T?) throws -> T? { internal func autoreleasepool<T>(@noescape closure: () throws -> T) throws -> T {
var closureValue: T? var closureValue: T!
var closureError: ErrorType? var closureError: ErrorType?
ObjectiveC.autoreleasepool { ObjectiveC.autoreleasepool {
+4 -3
View File
@@ -2,7 +2,7 @@
// MigrationManager.swift // MigrationManager.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -37,12 +37,13 @@ internal final class MigrationManager: NSMigrationManager, NSProgressReporting {
super.didChangeValueForKey(key) super.didChangeValueForKey(key)
if key == "migrationProgress" { guard key == "migrationProgress" else {
return
}
let progress = self.progress let progress = self.progress
progress.completedUnitCount = Int64(Float(progress.totalUnitCount) * self.migrationProgress) progress.completedUnitCount = Int64(Float(progress.totalUnitCount) * self.migrationProgress)
} }
}
// MARK: NSMigrationManager // MARK: NSMigrationManager
+4 -16
View File
@@ -17,22 +17,10 @@ internal extension NSFileManager {
internal func removeSQLiteStoreAtURL(fileURL: NSURL) { internal func removeSQLiteStoreAtURL(fileURL: NSURL) {
do { _ = try? self.removeItemAtURL(fileURL)
try self.removeItemAtURL(fileURL) let filePath = fileURL.path!
} _ = try? self.removeItemAtPath(filePath.stringByAppendingString("-shm"))
catch _ { } _ = try? self.removeItemAtPath(filePath.stringByAppendingString("-wal"))
do {
try self.removeItemAtPath(fileURL.path!.stringByAppendingString("-shm"))
}
catch _ { }
do {
try self.removeItemAtPath(fileURL.path!.stringByAppendingString("-wal"))
}
catch _ { }
} }
} }
@@ -1,122 +0,0 @@
//
// NSManagedObject+Transaction.swift
// CoreStore
//
// Copyright (c) 2014 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: - NSManagedObject
internal extension NSManagedObject {
// MARK: Internal
internal dynamic class func createInContext(context: NSManagedObjectContext) -> Self {
return self.init(
entity: context.entityDescriptionForEntityType(self)!,
insertIntoManagedObjectContext: context
)
}
internal dynamic class func inContext(context: NSManagedObjectContext, withObjectID objectID: NSManagedObjectID) -> Self? {
return self.typedObjectInContext(context, objectID: objectID)
}
internal func inContext(context: NSManagedObjectContext) -> Self? {
return self.typedObjectInContext(context)
}
internal func deleteFromContext() {
self.managedObjectContext?.deleteObject(self)
}
// MARK: Private
private class func typedObjectInContext<T: NSManagedObject>(context: NSManagedObjectContext, objectID: NSManagedObjectID) -> T? {
do {
let existingObject = try context.existingObjectWithID(objectID)
return (existingObject as! T)
}
catch {
CoreStore.handleError(
error as NSError,
"Failed to load existing \(typeName(self)) in context."
)
return nil
}
}
private func typedObjectInContext<T: NSManagedObject>(context: NSManagedObjectContext) -> T? {
let objectID = self.objectID
if objectID.temporaryID {
var objectIDError: NSError?
let didSucceed = withExtendedLifetime(self.managedObjectContext) { (context: NSManagedObjectContext?) -> Bool in
do {
try context?.obtainPermanentIDsForObjects([self])
return true
}
catch {
objectIDError = error as NSError
return false
}
}
if didSucceed != true {
CoreStore.handleError(
objectIDError ?? NSError(coreStoreErrorCode: .UnknownError),
"Failed to obtain permanent ID for object."
)
return nil
}
}
do {
let existingObject = try context.existingObjectWithID(objectID)
return (existingObject as! T)
}
catch {
CoreStore.handleError(
error as NSError,
"Failed to load existing \(typeName(self)) in context."
)
return nil
}
}
}
@@ -2,7 +2,7 @@
// NSManagedObjectContext+CoreStore.swift // NSManagedObjectContext+CoreStore.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2014 John Rommel Estropia // Copyright © 2014 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -25,7 +25,9 @@
import Foundation import Foundation
import CoreData import CoreData
#if USE_FRAMEWORKS
import GCDKit import GCDKit
#endif
// MARK: - NSManagedObjectContext // MARK: - NSManagedObjectContext
@@ -61,19 +63,28 @@ internal extension NSManagedObjectContext {
internal func entityDescriptionForEntityClass(entity: AnyClass) -> NSEntityDescription? { internal func entityDescriptionForEntityClass(entity: AnyClass) -> NSEntityDescription? {
if let entityName = self.parentStack?.entityNameForEntityClass(entity) { guard let entityName = self.parentStack?.entityNameForEntityClass(entity) else {
return nil
}
return NSEntityDescription.entityForName( return NSEntityDescription.entityForName(
entityName, entityName,
inManagedObjectContext: self inManagedObjectContext: self
) )
} }
return nil
}
internal func setupForCoreStoreWithContextName(contextName: String) { internal func setupForCoreStoreWithContextName(contextName: String) {
#if USE_FRAMEWORKS
self.name = contextName self.name = contextName
#else
if #available(iOS 8.0, *) {
self.name = contextName
}
#endif
self.observerForWillSaveNotification = NotificationObserver( self.observerForWillSaveNotification = NotificationObserver(
notificationName: NSManagedObjectContextWillSaveNotification, notificationName: NSManagedObjectContextWillSaveNotification,
@@ -83,7 +94,7 @@ internal extension NSManagedObjectContext {
let context = note.object as! NSManagedObjectContext let context = note.object as! NSManagedObjectContext
let insertedObjects = context.insertedObjects let insertedObjects = context.insertedObjects
let numberOfInsertedObjects = insertedObjects.count let numberOfInsertedObjects = insertedObjects.count
if numberOfInsertedObjects <= 0 { guard numberOfInsertedObjects > 0 else {
return return
} }
@@ -91,7 +102,6 @@ internal extension NSManagedObjectContext {
do { do {
try context.obtainPermanentIDsForObjects(Array(insertedObjects)) try context.obtainPermanentIDsForObjects(Array(insertedObjects))
return
} }
catch { catch {
@@ -2,7 +2,7 @@
// NSManagedObjectContext+Querying.swift // NSManagedObjectContext+Querying.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -31,7 +31,43 @@ import CoreData
internal extension NSManagedObjectContext { internal extension NSManagedObjectContext {
// MARK: Public // MARK: Internal
internal func fetchExisting<T: NSManagedObject>(object: T) -> T? {
if object.objectID.temporaryID {
do {
try withExtendedLifetime(self) { (context: NSManagedObjectContext) -> Void in
try context.obtainPermanentIDsForObjects([object])
}
}
catch {
CoreStore.handleError(
error as NSError,
"Failed to obtain permanent ID for object."
)
return nil
}
}
do {
let existingObject = try self.existingObjectWithID(object.objectID)
return (existingObject as! T)
}
catch {
CoreStore.handleError(
error as NSError,
"Failed to load existing \(typeName(object)) in context."
)
return nil
}
}
internal func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> T? { internal func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> T? {
@@ -40,7 +76,7 @@ internal extension NSManagedObjectContext {
internal func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> T? { internal func fetchOne<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> T? {
let fetchRequest = NSFetchRequest() let fetchRequest = CoreStoreFetchRequest()
from.applyToFetchRequest(fetchRequest, context: self) from.applyToFetchRequest(fetchRequest, context: self)
fetchRequest.fetchLimit = 1 fetchRequest.fetchLimit = 1
@@ -83,7 +119,7 @@ internal extension NSManagedObjectContext {
internal func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [T]? { internal func fetchAll<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [T]? {
let fetchRequest = NSFetchRequest() let fetchRequest = CoreStoreFetchRequest()
from.applyToFetchRequest(fetchRequest, context: self) from.applyToFetchRequest(fetchRequest, context: self)
fetchRequest.fetchLimit = 0 fetchRequest.fetchLimit = 0
@@ -126,7 +162,7 @@ internal extension NSManagedObjectContext {
internal func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> Int? { internal func fetchCount<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> Int? {
let fetchRequest = NSFetchRequest() let fetchRequest = CoreStoreFetchRequest()
from.applyToFetchRequest(fetchRequest, context: self) from.applyToFetchRequest(fetchRequest, context: self)
for clause in fetchClauses { for clause in fetchClauses {
@@ -159,7 +195,7 @@ internal extension NSManagedObjectContext {
internal func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? { internal func fetchObjectID<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> NSManagedObjectID? {
let fetchRequest = NSFetchRequest() let fetchRequest = CoreStoreFetchRequest()
from.applyToFetchRequest(fetchRequest, context: self) from.applyToFetchRequest(fetchRequest, context: self)
fetchRequest.fetchLimit = 1 fetchRequest.fetchLimit = 1
@@ -202,7 +238,7 @@ internal extension NSManagedObjectContext {
internal func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? { internal func fetchObjectIDs<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> [NSManagedObjectID]? {
let fetchRequest = NSFetchRequest() let fetchRequest = CoreStoreFetchRequest()
from.applyToFetchRequest(fetchRequest, context: self) from.applyToFetchRequest(fetchRequest, context: self)
fetchRequest.fetchLimit = 0 fetchRequest.fetchLimit = 0
@@ -245,7 +281,7 @@ internal extension NSManagedObjectContext {
internal func deleteAll<T: NSManagedObject>(from: From<T>, _ deleteClauses: [DeleteClause]) -> Int? { internal func deleteAll<T: NSManagedObject>(from: From<T>, _ deleteClauses: [DeleteClause]) -> Int? {
let fetchRequest = NSFetchRequest() let fetchRequest = CoreStoreFetchRequest()
from.applyToFetchRequest(fetchRequest, context: self) from.applyToFetchRequest(fetchRequest, context: self)
fetchRequest.fetchLimit = 0 fetchRequest.fetchLimit = 0
@@ -298,7 +334,7 @@ internal extension NSManagedObjectContext {
internal func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: [QueryClause]) -> U? { internal func queryValue<T: NSManagedObject, U: SelectValueResultType>(from: From<T>, _ selectClause: Select<U>, _ queryClauses: [QueryClause]) -> U? {
let fetchRequest = NSFetchRequest() let fetchRequest = CoreStoreFetchRequest()
from.applyToFetchRequest(fetchRequest, context: self) from.applyToFetchRequest(fetchRequest, context: self)
fetchRequest.fetchLimit = 0 fetchRequest.fetchLimit = 0
@@ -347,7 +383,7 @@ internal extension NSManagedObjectContext {
internal func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? { internal func queryAttributes<T: NSManagedObject>(from: From<T>, _ selectClause: Select<NSDictionary>, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? {
let fetchRequest = NSFetchRequest() let fetchRequest = CoreStoreFetchRequest()
from.applyToFetchRequest(fetchRequest, context: self) from.applyToFetchRequest(fetchRequest, context: self)
fetchRequest.fetchLimit = 0 fetchRequest.fetchLimit = 0
@@ -2,7 +2,7 @@
// NSManagedObjectContext+Setup.swift // NSManagedObjectContext+Setup.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -46,7 +46,7 @@ internal extension NSManagedObjectContext {
} }
set { set {
if self.parentContext != nil { guard self.parentContext == nil else {
return return
} }
@@ -82,9 +82,28 @@ internal extension NSManagedObjectContext {
object: rootContext, object: rootContext,
closure: { [weak context] (note) -> Void in closure: { [weak context] (note) -> Void in
context?.performBlock { () -> Void in guard let rootContext = note.object as? NSManagedObjectContext,
let context = context else {
context?.mergeChangesFromContextDidSaveNotification(note) return
}
let mergeChanges = { () -> Void in
let updatedObjects = (note.userInfo?[NSUpdatedObjectsKey] as? Set<NSManagedObject>) ?? []
for object in updatedObjects {
context.objectWithID(object.objectID).willAccessValueForKey(nil)
}
context.mergeChangesFromContextDidSaveNotification(note)
}
if rootContext.isSavingSynchronously == true {
context.performBlockAndWait(mergeChanges)
}
else {
context.performBlock(mergeChanges)
} }
} }
) )
@@ -2,7 +2,7 @@
// NSManagedObjectContext+Transaction.swift // NSManagedObjectContext+Transaction.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -25,7 +25,9 @@
import Foundation import Foundation
import CoreData import CoreData
#if USE_FRAMEWORKS
import GCDKit import GCDKit
#endif
// MARK: - NSManagedObjectContext // MARK: - NSManagedObjectContext
@@ -53,6 +55,35 @@ internal extension NSManagedObjectContext {
} }
} }
internal var isSavingSynchronously: Bool? {
get {
let value: NSNumber? = getAssociatedObjectForKey(
&PropertyKeys.isSavingSynchronously,
inObject: self
)
return value?.boolValue
}
set {
setAssociatedWeakObject(
newValue.flatMap { NSNumber(bool: $0) },
forKey: &PropertyKeys.isSavingSynchronously,
inObject: self
)
}
}
internal func isRunningInAllowedQueue() -> Bool {
guard let parentTransaction = self.parentTransaction else {
return false
}
return parentTransaction.isRunningInAllowedQueue()
}
internal func temporaryContextInTransactionWithConcurrencyType(concurrencyType: NSManagedObjectContextConcurrencyType) -> NSManagedObjectContext { internal func temporaryContextInTransactionWithConcurrencyType(concurrencyType: NSManagedObjectContextConcurrencyType) -> NSManagedObjectContext {
let context = NSManagedObjectContext(concurrencyType: concurrencyType) let context = NSManagedObjectContext(concurrencyType: concurrencyType)
@@ -69,16 +100,18 @@ internal extension NSManagedObjectContext {
var result = SaveResult(hasChanges: false) var result = SaveResult(hasChanges: false)
self.performBlockAndWait { [unowned self] () -> Void in self.performBlockAndWait {
if !self.hasChanges { guard self.hasChanges else {
return return
} }
do { do {
self.isSavingSynchronously = true
try self.save() try self.save()
self.isSavingSynchronously = nil
} }
catch { catch {
@@ -111,25 +144,24 @@ internal extension NSManagedObjectContext {
return result return result
} }
internal func saveAsynchronouslyWithCompletion(completion: ((result: SaveResult) -> Void)?) { internal func saveAsynchronouslyWithCompletion(completion: ((result: SaveResult) -> Void) = { _ in }) {
self.performBlock { () -> Void in self.performBlock {
if !self.hasChanges { guard self.hasChanges else {
if let completion = completion {
GCDQueue.Main.async { GCDQueue.Main.async {
completion(result: SaveResult(hasChanges: false)) completion(result: SaveResult(hasChanges: false))
} }
}
return return
} }
do { do {
self.isSavingSynchronously = false
try self.save() try self.save()
self.isSavingSynchronously = nil
} }
catch { catch {
@@ -138,13 +170,10 @@ internal extension NSManagedObjectContext {
saveError, saveError,
"Failed to save \(typeName(NSManagedObjectContext))." "Failed to save \(typeName(NSManagedObjectContext))."
) )
if let completion = completion {
GCDQueue.Main.async { GCDQueue.Main.async {
completion(result: SaveResult(saveError)) completion(result: SaveResult(saveError))
} }
}
return return
} }
@@ -152,7 +181,7 @@ internal extension NSManagedObjectContext {
parentContext.saveAsynchronouslyWithCompletion(completion) parentContext.saveAsynchronouslyWithCompletion(completion)
} }
else if let completion = completion { else {
GCDQueue.Main.async { GCDQueue.Main.async {
@@ -162,11 +191,24 @@ internal extension NSManagedObjectContext {
} }
} }
internal func refreshAndMergeAllObjects() {
if #available(iOS 8.3, OSX 10.11, *) {
self.refreshAllObjects()
}
else {
self.registeredObjects.forEach { self.refreshObject($0, mergeChanges: true) }
}
}
// MARK: Private // MARK: Private
private struct PropertyKeys { private struct PropertyKeys {
static var parentTransaction: Void? static var parentTransaction: Void?
static var isSavingSynchronously: Void?
} }
} }
@@ -2,7 +2,7 @@
// NSManagedObjectModel+Setup.swift // NSManagedObjectModel+Setup.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -65,10 +65,13 @@ internal extension NSManagedObjectModel {
} }
else if let resolvedVersion = modelVersions.first ?? modelVersionHints.first { else if let resolvedVersion = modelVersions.first ?? modelVersionHints.first {
if !modelVersionHints.isEmpty {
CoreStore.log( CoreStore.log(
.Warning, .Warning,
message: "The MigrationChain leaf versions do not include any of the model file's embedded versions. Resolving to version \"\(resolvedVersion)\"." message: "The MigrationChain leaf versions do not include any of the model file's embedded versions. Resolving to version \"\(resolvedVersion)\"."
) )
}
currentModelVersion = resolvedVersion currentModelVersion = resolvedVersion
} }
else { else {
@@ -150,13 +153,14 @@ internal extension NSManagedObjectModel {
return self.entityNameMapping[NSStringFromClass(entityClass)]! return self.entityNameMapping[NSStringFromClass(entityClass)]!
} }
@nonobjc internal func entityMapping() -> [String: NSManagedObject.Type] { @nonobjc internal func entityTypesMapping() -> [String: NSManagedObject.Type] {
return self.entityNameMapping.reduce([:]) { (var mapping, pair) in var mapping = [String: NSManagedObject.Type]()
self.entityNameMapping.forEach { (className, entityName) in
mapping[pair.0] = (NSClassFromString(pair.1)! as! NSManagedObject.Type) mapping[entityName] = (NSClassFromString(className)! as! NSManagedObject.Type)
return mapping
} }
return mapping
} }
@nonobjc internal func mergedModels() -> [NSManagedObjectModel] { @nonobjc internal func mergedModels() -> [NSManagedObjectModel] {
@@ -192,8 +196,10 @@ internal extension NSManagedObjectModel {
@nonobjc internal subscript(metadata: [String: AnyObject]) -> NSManagedObjectModel? { @nonobjc internal subscript(metadata: [String: AnyObject]) -> NSManagedObjectModel? {
if let modelHashes = metadata[NSStoreModelVersionHashesKey] as? [String : NSData] { guard let modelHashes = metadata[NSStoreModelVersionHashesKey] as? [String : NSData] else {
return nil
}
for modelVersion in self.modelVersions ?? [] { for modelVersion in self.modelVersions ?? [] {
if let versionModel = self[modelVersion] where modelHashes == versionModel.entityVersionHashesByName { if let versionModel = self[modelVersion] where modelHashes == versionModel.entityVersionHashesByName {
@@ -201,7 +207,6 @@ internal extension NSManagedObjectModel {
return versionModel return versionModel
} }
} }
}
return nil return nil
} }
@@ -245,15 +250,16 @@ internal extension NSManagedObjectModel {
return mapping as! [String: String] return mapping as! [String: String]
} }
let mapping = self.entities.reduce([String: String]()) { var mapping = [String: String]()
(var mapping, entityDescription) -> [String: String] in self.entities.forEach {
if let entityName = entityDescription.name { guard let entityName = $0.name else {
let className = entityDescription.managedObjectClassName return
mapping[className] = entityName
} }
return mapping
let className = $0.managedObjectClassName
mapping[className] = entityName
} }
setAssociatedCopiedObject( setAssociatedCopiedObject(
mapping as NSDictionary, mapping as NSDictionary,
@@ -0,0 +1,80 @@
//
// NSPersistentStoreCoordinator+Setup.swift
// CoreStore
//
// 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 CoreData
#if USE_FRAMEWORKS
import GCDKit
#endif
// MARK: - NSPersistentStoreCoordinator
internal extension NSPersistentStoreCoordinator {
internal func performAsynchronously(closure: () -> Void) {
#if USE_FRAMEWORKS
self.performBlock(closure)
#else
if #available(iOS 8.0, *) {
self.performBlock(closure)
}
else {
self.lock()
GCDQueue.Default.async {
closure()
self.unlock()
}
}
#endif
}
internal func performSynchronously(closure: () -> Void) {
#if USE_FRAMEWORKS
self.performBlockAndWait(closure)
#else
if #available(iOS 8.0, *) {
self.performBlockAndWait(closure)
}
else {
self.lock()
autoreleasepool(closure)
self.unlock()
}
#endif
}
}
@@ -2,7 +2,7 @@
// NotificationObserver.swift // NotificationObserver.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2014 John Rommel Estropia // Copyright © 2014 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -36,14 +36,14 @@ internal final class NotificationObserver {
let object: AnyObject? let object: AnyObject?
let observer: NSObjectProtocol let observer: NSObjectProtocol
init(notificationName: String, object: AnyObject?, closure: (note: NSNotification) -> Void) { init(notificationName: String, object: AnyObject?, queue: NSOperationQueue? = nil, closure: (note: NSNotification) -> Void) {
self.notificationName = notificationName self.notificationName = notificationName
self.object = object self.object = object
self.observer = NSNotificationCenter.defaultCenter().addObserverForName( self.observer = NSNotificationCenter.defaultCenter().addObserverForName(
notificationName, notificationName,
object: object, object: object,
queue: nil, queue: queue,
usingBlock: closure usingBlock: closure
) )
} }
+1 -1
View File
@@ -2,7 +2,7 @@
// WeakObject.swift // WeakObject.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
+4 -6
View File
@@ -2,7 +2,7 @@
// CoreStore+Logging.swift // CoreStore+Logging.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -30,8 +30,6 @@ import Foundation
public extension CoreStore { public extension CoreStore {
// MARK: Public
/** /**
The `CoreStoreLogger` instance to be used. The default logger is an instance of a `DefaultLogger`. The `CoreStoreLogger` instance to be used. The default logger is an instance of a `DefaultLogger`.
*/ */
@@ -40,7 +38,7 @@ public extension CoreStore {
// MARK: Internal // MARK: Internal
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(
level: level, level: level,
@@ -51,7 +49,7 @@ public extension CoreStore {
) )
} }
internal static func handleError(error: NSError, _ message: String, fileName: StaticString = __FILE__, lineNumber: Int = __LINE__, functionName: StaticString = __FUNCTION__) { internal static func handleError(error: NSError, _ message: String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) {
self.logger.handleError( self.logger.handleError(
error: error, error: error,
@@ -62,7 +60,7 @@ public extension CoreStore {
) )
} }
internal static func assert(@autoclosure condition: () -> Bool, _ message: String, fileName: StaticString = __FILE__, lineNumber: Int = __LINE__, functionName: StaticString = __FUNCTION__) { internal static func assert(@autoclosure condition: () -> Bool, _ message: String, fileName: StaticString = #file, lineNumber: Int = #line, functionName: StaticString = #function) {
self.logger.assert( self.logger.assert(
condition, condition,
+3 -4
View File
@@ -2,7 +2,7 @@
// CoreStoreLogger.swift // CoreStoreLogger.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -86,7 +86,7 @@ public protocol CoreStoreLogger {
internal func typeName<T>(value: T) -> String { internal func typeName<T>(value: T) -> String {
return "'\(_stdlib_getDemangledTypeName(value))'" return "'\(String(reflecting: value.dynamicType))'"
} }
internal func typeName<T>(value: T.Type) -> String { internal func typeName<T>(value: T.Type) -> String {
@@ -101,6 +101,5 @@ internal func typeName(value: AnyClass) -> String {
internal func typeName(name: String?) -> String { internal func typeName(name: String?) -> String {
let typeName = name ?? "unknown" return "<\(name ?? "unknown")>"
return "<\(typeName)>"
} }
+1 -1
View File
@@ -2,7 +2,7 @@
// DefaultLogger.swift // DefaultLogger.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
+10 -6
View File
@@ -2,7 +2,7 @@
// CoreStore+Migration.swift // CoreStore+Migration.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -25,7 +25,9 @@
import Foundation import Foundation
import CoreData import CoreData
#if USE_FRAMEWORKS
import GCDKit import GCDKit
#endif
// MARK: - CoreStore // MARK: - CoreStore
@@ -35,7 +37,7 @@ public extension CoreStore {
/** /**
Asynchronously adds to the `defaultStack` an SQLite store from the given SQLite file name. Note that using `addSQLiteStore(...)` instead of `addSQLiteStoreAndWait(...)` implies that the migrations are allowed and expected (thus the asynchronous `completion`.) Asynchronously adds to the `defaultStack` an SQLite store from the given SQLite file name. Note that using `addSQLiteStore(...)` instead of `addSQLiteStoreAndWait(...)` implies that the migrations are allowed and expected (thus the asynchronous `completion`.)
- parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory. A new SQLite file will be created if it does not exist. 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" directory (or the "Caches" directory on tvOS). A new SQLite file will be created if it does not exist. 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: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`.
- parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to report failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false. - parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to report failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false.
@@ -56,7 +58,7 @@ public extension CoreStore {
/** /**
Asynchronously adds to the `defaultStack` an SQLite store from the given SQLite file URL. Note that using `addSQLiteStore(...)` instead of `addSQLiteStoreAndWait(...)` implies that the migrations are allowed and expected (thus the asynchronous `completion`.) Asynchronously adds to the `defaultStack` an SQLite store from the given SQLite file URL. Note that using `addSQLiteStore(...)` instead of `addSQLiteStoreAndWait(...)` implies that the migrations are allowed and expected (thus the asynchronous `completion`.)
- parameter fileURL: the local file URL for the SQLite persistent store. A new SQLite file will be created if it does not exist. If not specified, defaults to a file URL pointing to a "<Application name>.sqlite" file in the "Application Support" directory. 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 SQLite persistent store. A new SQLite file will be created if it does not exist. If not specified, defaults to a file URL pointing to a "<Application name>.sqlite" file in the "Application Support" directory (or the "Caches" directory on tvOS). 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: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`.
- parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to report failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false. - parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to report failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false.
@@ -77,7 +79,7 @@ public extension CoreStore {
/** /**
Using the `defaultStack`, migrates an SQLite store with the specified filename to the `DataStack`'s managed object model version WITHOUT adding the migrated store to the data stack. Using the `defaultStack`, migrates an SQLite store with the specified filename to the `DataStack`'s managed object model version WITHOUT adding the migrated store to the data stack.
- parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory. - parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory (or the "Caches" directory on tvOS).
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration. - parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration.
- parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
- parameter sourceBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`. - parameter sourceBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
@@ -96,7 +98,7 @@ public extension CoreStore {
/** /**
Using the `defaultStack`, migrates an SQLite store at the specified file URL and configuration name to the `DataStack`'s managed object model version. This method does NOT add the migrated store to the data stack. Using the `defaultStack`, migrates an SQLite store at the specified file URL and configuration name to the `DataStack`'s managed object model version. This method does NOT add the migrated store to the data stack.
- parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory. - parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory (or the "Caches" directory on tvOS).
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration. - parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration.
- parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
- parameter sourceBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`. - parameter sourceBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
@@ -115,11 +117,12 @@ public extension CoreStore {
/** /**
Using the `defaultStack`, checks for the required migrations needed for the store with the specified filename and configuration to be migrated to the `DataStack`'s managed object model version. This method throws an error if the store does not exist, if inspection of the store failed, or no mapping model was found/inferred. Using the `defaultStack`, checks for the required migrations needed for the store with the specified filename and configuration to be migrated to the `DataStack`'s managed object model version. This method throws an error if the store does not exist, if inspection of the store failed, or no mapping model was found/inferred.
- parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory. - parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory (or the "Caches" directory on tvOS).
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration. - parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration.
- parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`.
:return: an array of `MigrationType`s indicating the chain of migrations required for the store; or `nil` if either inspection of the store failed, or no mapping model was found/inferred. `MigrationType` acts as a `Bool` and evaluates to `false` if no migration is required, and `true` if either a lightweight or custom migration is needed. :return: an array of `MigrationType`s indicating the chain of migrations required for the store; or `nil` if either inspection of the store failed, or no mapping model was found/inferred. `MigrationType` acts as a `Bool` and evaluates to `false` if no migration is required, and `true` if either a lightweight or custom migration is needed.
*/ */
@warn_unused_result
public static func requiredMigrationsForSQLiteStore(fileName fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) throws -> [MigrationType] { public static func requiredMigrationsForSQLiteStore(fileName fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) throws -> [MigrationType] {
return try self.defaultStack.requiredMigrationsForSQLiteStore( return try self.defaultStack.requiredMigrationsForSQLiteStore(
@@ -137,6 +140,7 @@ public extension CoreStore {
- parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`.
:return: a `MigrationType` indicating the type of migration required for the store; or `nil` if either inspection of the store failed, or no mapping model was found/inferred. `MigrationType` acts as a `Bool` and evaluates to `false` if no migration is required, and `true` if either a lightweight or custom migration is needed. :return: a `MigrationType` indicating the type of migration required for the store; or `nil` if either inspection of the store failed, or no mapping model was found/inferred. `MigrationType` acts as a `Bool` and evaluates to `false` if no migration is required, and `true` if either a lightweight or custom migration is needed.
*/ */
@warn_unused_result
public static func requiredMigrationsForSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) throws -> [MigrationType] { public static func requiredMigrationsForSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) throws -> [MigrationType] {
return try self.defaultStack.requiredMigrationsForSQLiteStore( return try self.defaultStack.requiredMigrationsForSQLiteStore(
+40 -28
View File
@@ -2,7 +2,7 @@
// DataStack+Migration.swift // DataStack+Migration.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -25,7 +25,9 @@
import Foundation import Foundation
import CoreData import CoreData
#if USE_FRAMEWORKS
import GCDKit import GCDKit
#endif
// MARK: - DataStack // MARK: - DataStack
@@ -40,7 +42,7 @@ public extension DataStack {
*/ */
public func addInMemoryStore(configuration configuration: String? = nil, completion: (PersistentStoreResult) -> Void) { public func addInMemoryStore(configuration configuration: String? = nil, completion: (PersistentStoreResult) -> Void) {
self.coordinator.performBlock { self.coordinator.performAsynchronously {
do { do {
@@ -76,7 +78,7 @@ public extension DataStack {
/** /**
Asynchronously adds to the stack an SQLite store from the given SQLite file name. Note that using `addSQLiteStore(...)` instead of `addSQLiteStoreAndWait(...)` implies that the migrations are allowed and expected (thus the asynchronous `completion`.) Asynchronously adds to the stack an SQLite store from the given SQLite file name. Note that using `addSQLiteStore(...)` instead of `addSQLiteStoreAndWait(...)` implies that the migrations are allowed and expected (thus the asynchronous `completion`.)
- parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory. A new SQLite file will be created if it does not exist. 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" directory (or the "Caches" directory on tvOS). A new SQLite file will be created if it does not exist. 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: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`.
- parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to report failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false. - parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to report failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false.
@@ -86,7 +88,7 @@ public extension DataStack {
public func addSQLiteStore(fileName fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, resetStoreOnModelMismatch: Bool = false, completion: (PersistentStoreResult) -> Void) throws -> NSProgress? { public func addSQLiteStore(fileName fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, resetStoreOnModelMismatch: Bool = false, completion: (PersistentStoreResult) -> Void) throws -> NSProgress? {
return try self.addSQLiteStore( return try self.addSQLiteStore(
fileURL: applicationSupportDirectory.URLByAppendingPathComponent( fileURL: defaultDirectory.URLByAppendingPathComponent(
fileName, fileName,
isDirectory: false isDirectory: false
), ),
@@ -100,7 +102,7 @@ public extension DataStack {
/** /**
Asynchronously adds to the stack an SQLite store from the given SQLite file URL. Note that using `addSQLiteStore(...)` instead of `addSQLiteStoreAndWait(...)` implies that the migrations are allowed and expected (thus the asynchronous `completion`.) Asynchronously adds to the stack an SQLite store from the given SQLite file URL. Note that using `addSQLiteStore(...)` instead of `addSQLiteStoreAndWait(...)` implies that the migrations are allowed and expected (thus the asynchronous `completion`.)
- parameter fileURL: the local file URL for the SQLite persistent store. A new SQLite file will be created if it does not exist. If not specified, defaults to a file URL pointing to a "<Application name>.sqlite" file in the "Application Support" directory. 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 SQLite persistent store. A new SQLite file will be created if it does not exist. If not specified, defaults to a file URL pointing to a "<Application name>.sqlite" file in the "Application Support" directory (or the "Caches" directory on tvOS). 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: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`.
- parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to report failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false. - parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to report failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false.
@@ -117,15 +119,8 @@ public extension DataStack {
let coordinator = self.coordinator; let coordinator = self.coordinator;
if let store = coordinator.persistentStoreForURL(fileURL) { if let store = coordinator.persistentStoreForURL(fileURL) {
if store.type == NSSQLiteStoreType guard store.type == NSSQLiteStoreType
&& store.configurationName == (configuration ?? Into.defaultConfigurationName) { && store.configurationName == (configuration ?? Into.defaultConfigurationName) else {
GCDQueue.Main.async {
completion(PersistentStoreResult(store))
}
return nil
}
let error = NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL) let error = NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL)
CoreStore.handleError( CoreStore.handleError(
@@ -135,22 +130,26 @@ public extension DataStack {
throw error throw error
} }
let fileManager = NSFileManager.defaultManager() GCDQueue.Main.async {
do {
try fileManager.createDirectoryAtURL( completion(PersistentStoreResult(store))
}
return nil
}
let fileManager = NSFileManager.defaultManager()
_ = try? fileManager.createDirectoryAtURL(
fileURL.URLByDeletingLastPathComponent!, fileURL.URLByDeletingLastPathComponent!,
withIntermediateDirectories: true, withIntermediateDirectories: true,
attributes: nil attributes: nil
) )
}
catch _ { }
do { do {
let metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType( let metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
NSSQLiteStoreType, NSSQLiteStoreType,
URL: fileURL URL: fileURL,
options: self.optionsForSQLiteStore()
) )
return self.upgradeSQLiteStoreIfNeeded( return self.upgradeSQLiteStoreIfNeeded(
@@ -234,7 +233,7 @@ public extension DataStack {
/** /**
Migrates an SQLite store with the specified filename to the `DataStack`'s managed object model version WITHOUT adding the migrated store to the data stack. Migrates an SQLite store with the specified filename to the `DataStack`'s managed object model version WITHOUT adding the migrated store to the data stack.
- parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory. - parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory (or the "Caches" directory on tvOS).
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration. - parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration.
- parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
- parameter sourceBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`. - parameter sourceBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
@@ -243,7 +242,7 @@ public extension DataStack {
public func upgradeSQLiteStoreIfNeeded(fileName fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, completion: (MigrationResult) -> Void) throws -> NSProgress? { public func upgradeSQLiteStoreIfNeeded(fileName fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle]? = nil, completion: (MigrationResult) -> Void) throws -> NSProgress? {
return try self.upgradeSQLiteStoreIfNeeded( return try self.upgradeSQLiteStoreIfNeeded(
fileURL: applicationSupportDirectory.URLByAppendingPathComponent( fileURL: defaultDirectory.URLByAppendingPathComponent(
fileName, fileName,
isDirectory: false isDirectory: false
), ),
@@ -256,7 +255,7 @@ public extension DataStack {
/** /**
Migrates an SQLite store at the specified file URL and configuration name to the `DataStack`'s managed object model version. This method does NOT add the migrated store to the data stack. Migrates an SQLite store at the specified file URL and configuration name to the `DataStack`'s managed object model version. This method does NOT add the migrated store to the data stack.
- parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory. - parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory (or the "Caches" directory on tvOS).
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration. - parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration.
- parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
- parameter sourceBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`. - parameter sourceBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.mainBundle()`.
@@ -269,7 +268,8 @@ public extension DataStack {
metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType( metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
NSSQLiteStoreType, NSSQLiteStoreType,
URL: fileURL URL: fileURL,
options: self.optionsForSQLiteStore()
) )
} }
catch { catch {
@@ -293,15 +293,16 @@ public extension DataStack {
/** /**
Checks for the required migrations needed for the store with the specified filename and configuration to be migrated to the `DataStack`'s managed object model version. This method throws an error if the store does not exist, if inspection of the store failed, or no mapping model was found/inferred. Checks for the required migrations needed for the store with the specified filename and configuration to be migrated to the `DataStack`'s managed object model version. This method throws an error if the store does not exist, if inspection of the store failed, or no mapping model was found/inferred.
- parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory. - parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory (or the "Caches" directory on tvOS).
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration. - parameter configuration: an optional configuration name from the model file. If not specified, defaults to `nil` which indicates the "Default" configuration.
- parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`.
:return: an array of `MigrationType`s indicating the chain of migrations required for the store; or `nil` if either inspection of the store failed, or no mapping model was found/inferred. `MigrationType` acts as a `Bool` and evaluates to `false` if no migration is required, and `true` if either a lightweight or custom migration is needed. :return: an array of `MigrationType`s indicating the chain of migrations required for the store; or `nil` if either inspection of the store failed, or no mapping model was found/inferred. `MigrationType` acts as a `Bool` and evaluates to `false` if no migration is required, and `true` if either a lightweight or custom migration is needed.
*/ */
@warn_unused_result
public func requiredMigrationsForSQLiteStore(fileName fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) throws -> [MigrationType] { public func requiredMigrationsForSQLiteStore(fileName fileName: String, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) throws -> [MigrationType] {
return try requiredMigrationsForSQLiteStore( return try requiredMigrationsForSQLiteStore(
fileURL: applicationSupportDirectory.URLByAppendingPathComponent( fileURL: defaultDirectory.URLByAppendingPathComponent(
fileName, fileName,
isDirectory: false isDirectory: false
), ),
@@ -318,6 +319,7 @@ public extension DataStack {
- parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`. - parameter mappingModelBundles: an optional array of bundles to search mapping model files from. If not set, defaults to the `NSBundle.allBundles()`.
:return: a `MigrationType` indicating the type of migration required for the store; or `nil` if either inspection of the store failed, or no mapping model was found/inferred. `MigrationType` acts as a `Bool` and evaluates to `false` if no migration is required, and `true` if either a lightweight or custom migration is needed. :return: a `MigrationType` indicating the type of migration required for the store; or `nil` if either inspection of the store failed, or no mapping model was found/inferred. `MigrationType` acts as a `Bool` and evaluates to `false` if no migration is required, and `true` if either a lightweight or custom migration is needed.
*/ */
@warn_unused_result
public func requiredMigrationsForSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) throws -> [MigrationType] { public func requiredMigrationsForSQLiteStore(fileURL fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, mappingModelBundles: [NSBundle] = NSBundle.allBundles() as [NSBundle]) throws -> [MigrationType] {
let metadata: [String : AnyObject] let metadata: [String : AnyObject]
@@ -325,7 +327,8 @@ public extension DataStack {
metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType( metadata = try NSPersistentStoreCoordinator.metadataForPersistentStoreOfType(
NSSQLiteStoreType, NSSQLiteStoreType,
URL: fileURL URL: fileURL,
options: self.optionsForSQLiteStore()
) )
} }
catch { catch {
@@ -435,8 +438,17 @@ public extension DataStack {
} }
let migrationOperation = NSBlockOperation() let migrationOperation = NSBlockOperation()
#if USE_FRAMEWORKS
migrationOperation.qualityOfService = .Utility migrationOperation.qualityOfService = .Utility
operations.map { migrationOperation.addDependency($0) } #else
if #available(iOS 8.0, *) {
migrationOperation.qualityOfService = .Utility
}
#endif
operations.forEach { migrationOperation.addDependency($0) }
migrationOperation.addExecutionBlock { () -> Void in migrationOperation.addExecutionBlock { () -> Void in
GCDQueue.Main.async { GCDQueue.Main.async {
+23 -23
View File
@@ -2,7 +2,7 @@
// MigrationChain.swift // MigrationChain.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -30,26 +30,26 @@ import CoreData
// MARK: - MigrationChain // MARK: - MigrationChain
/** /**
A `MigrationChain` indicates the sequence of model versions to be used as the order for incremental migration. This is typically passed to the `DataStack` initializer and will be applied to all stores added to the `DataStack` with `addSQLiteStore(...)` and its variants. A `MigrationChain` indicates the sequence of model versions to be used as the order for progressive migrations. This is typically passed to the `DataStack` initializer and will be applied to all stores added to the `DataStack` with `addSQLiteStore(...)` and its variants.
Initializing with empty values (either `nil`, `[]`, or `[:]`) instructs the `DataStack` to use the .xcdatamodel's current version as the final version, and to disable incremental migrations:
Initializing with empty values (either `nil`, `[]`, or `[:]`) instructs the `DataStack` to use the .xcdatamodel's current version as the final version, and to disable progressive migrations:
```
let dataStack = DataStack(migrationChain: nil) let dataStack = DataStack(migrationChain: nil)
```
This means that the mapping model will be computed from the store's version straight to the `DataStack`'s model version. This means that the mapping model will be computed from the store's version straight to the `DataStack`'s model version.
To support incremental migrations, specify the linear order of versions: To support progressive migrations, specify the linear order of versions:
```
let dataStack = DataStack(migrationChain: let dataStack = DataStack(migrationChain:
["MyAppModel", "MyAppModelV2", "MyAppModelV3", "MyAppModelV4"]) ["MyAppModel", "MyAppModelV2", "MyAppModelV3", "MyAppModelV4"])
```
or for more complex migration paths, a version tree that maps the key-values to the source-destination versions: or for more complex migration paths, a version tree that maps the key-values to the source-destination versions:
```
let dataStack = DataStack(migrationChain: [ let dataStack = DataStack(migrationChain: [
"MyAppModel": "MyAppModelV3", "MyAppModel": "MyAppModelV3",
"MyAppModelV2": "MyAppModelV4", "MyAppModelV2": "MyAppModelV4",
"MyAppModelV3": "MyAppModelV4" "MyAppModelV3": "MyAppModelV4"
]) ])
```
This allows for different migration paths depending on the starting version. The example above resolves to the following paths: This allows for different migration paths depending on the starting version. The example above resolves to the following paths:
- MyAppModel-MyAppModelV3-MyAppModelV4 - MyAppModel-MyAppModelV3-MyAppModelV4
- MyAppModelV2-MyAppModelV4 - MyAppModelV2-MyAppModelV4
@@ -111,16 +111,18 @@ public struct MigrationChain: NilLiteralConvertible, StringLiteralConvertible, D
public init(dictionaryLiteral elements: (String, String)...) { public init(dictionaryLiteral elements: (String, String)...) {
var valid = true var valid = true
let versionTree = elements.reduce([String: String]()) { (var versionTree, tuple: (String, String)) -> [String: String] in var versionTree = [String: String]()
elements.forEach { (sourceVersion, destinationVersion) in
if let _ = versionTree.updateValue(tuple.1, forKey: tuple.0) { guard let _ = versionTree.updateValue(destinationVersion, forKey: sourceVersion) else {
return
}
CoreStore.assert(false, "\(typeName(MigrationChain))'s migration chain could not be created due to ambiguous version paths.") CoreStore.assert(false, "\(typeName(MigrationChain))'s migration chain could not be created due to ambiguous version paths.")
valid = false valid = false
} }
return versionTree
}
let leafVersions = Set( let leafVersions = Set(
elements.filter { (tuple: (String, String)) -> Bool in elements.filter { (tuple: (String, String)) -> Bool in
@@ -166,13 +168,11 @@ public struct MigrationChain: NilLiteralConvertible, StringLiteralConvertible, D
var valid = true var valid = true
for version in elements { for version in elements {
if let lastVersion = lastVersion { if let lastVersion = lastVersion,
let _ = versionTree.updateValue(version, forKey: lastVersion) {
if let _ = versionTree.updateValue(version, forKey: lastVersion) {
valid = false valid = false
} }
}
lastVersion = version lastVersion = version
} }
@@ -203,12 +203,12 @@ public struct MigrationChain: NilLiteralConvertible, StringLiteralConvertible, D
internal func nextVersionFrom(version: String) -> String? { internal func nextVersionFrom(version: String) -> String? {
if let nextVersion = self.versionTree[version] where nextVersion != version { guard let nextVersion = self.versionTree[version] where nextVersion != version else {
return nextVersion
}
return nil return nil
} }
return nextVersion
}
// MARK: Private // MARK: Private
@@ -237,9 +237,9 @@ extension MigrationChain: CustomDebugStringConvertible {
steps.append(nextVersion) steps.append(nextVersion)
version = nextVersion version = nextVersion
} }
paths.append("".join(steps)) paths.append(steps.joinWithSeparator(""))
} }
return "[" + "], [".join(paths) + "]" return "[" + paths.joinWithSeparator("], [") + "]"
} }
} }
+5 -6
View File
@@ -2,7 +2,7 @@
// MigrationResult.swift // MigrationResult.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -31,7 +31,7 @@ import Foundation
/** /**
The `MigrationResult` indicates the result of a migration. The `MigrationResult` indicates the result of a migration.
The `MigrationResult` can be treated as a boolean: The `MigrationResult` can be treated as a boolean:
```
CoreStore.upgradeSQLiteStoreIfNeeded { transaction in CoreStore.upgradeSQLiteStoreIfNeeded { transaction in
// ... // ...
let result = transaction.commit() let result = transaction.commit()
@@ -42,9 +42,9 @@ The `MigrationResult` can be treated as a boolean:
// failed // failed
} }
} }
```
or as an `enum`, where the resulting associated object can also be inspected: or as an `enum`, where the resulting associated object can also be inspected:
```
CoreStore.beginAsynchronous { transaction in CoreStore.beginAsynchronous { transaction in
// ... // ...
let result = transaction.commit() let result = transaction.commit()
@@ -59,8 +59,6 @@ or as an `enum`, where the resulting associated object can also be inspected:
*/ */
public enum MigrationResult { public enum MigrationResult {
// MARK: Public
/** /**
`MigrationResult.Success` indicates either the migration succeeded, or there were no migrations needed. The associated value is an array of `MigrationType`s reflecting the migration steps completed. `MigrationResult.Success` indicates either the migration succeeded, or there were no migrations needed. The associated value is an array of `MigrationType`s reflecting the migration steps completed.
*/ */
@@ -103,6 +101,7 @@ extension MigrationResult: BooleanType {
public var boolValue: Bool { public var boolValue: Bool {
switch self { switch self {
case .Success: return true case .Success: return true
case .Failure: return false case .Failure: return false
} }
+1 -3
View File
@@ -2,7 +2,7 @@
// MigrationType.swift // MigrationType.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -33,8 +33,6 @@ The `MigrationType` specifies the type of migration required for a store.
*/ */
public enum MigrationType: BooleanType { public enum MigrationType: BooleanType {
// MARK: Public
/** /**
Indicates that the persistent store matches the latest model version and no migration is needed Indicates that the persistent store matches the latest model version and no migration is needed
*/ */
+6 -2
View File
@@ -2,7 +2,7 @@
// NSError+CoreStore.swift // NSError+CoreStore.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2014 John Rommel Estropia // Copyright © 2014 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -24,6 +24,10 @@
// //
import Foundation import Foundation
import CoreData
// MARK: - CoreStoreError
/** /**
The `NSError` error domain for `CoreStore`. The `NSError` error domain for `CoreStore`.
@@ -57,7 +61,7 @@ public enum CoreStoreErrorCode: Int {
} }
// MARK: - NSError+CoreStore // MARK: - NSError
public extension NSError { public extension NSError {
+57 -3
View File
@@ -2,7 +2,7 @@
// CoreStore+Observing.swift // CoreStore+Observing.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -29,16 +29,16 @@ import CoreData
// MARK: - CoreStore // MARK: - CoreStore
@available(OSX, unavailable)
public extension CoreStore { public extension CoreStore {
// MARK: Public
/** /**
Using the `defaultStack`, creates a `ObjectMonitor` for the specified `NSManagedObject`. Multiple `ObjectObserver`s may then register themselves to be notified when changes are made to the `NSManagedObject`. Using the `defaultStack`, creates a `ObjectMonitor` for the specified `NSManagedObject`. Multiple `ObjectObserver`s may then register themselves to be notified when changes are made to the `NSManagedObject`.
- parameter object: the `NSManagedObject` to observe changes from - parameter object: the `NSManagedObject` to observe changes from
- returns: a `ObjectMonitor` that monitors changes to `object` - returns: a `ObjectMonitor` that monitors changes to `object`
*/ */
@warn_unused_result
public static func monitorObject<T: NSManagedObject>(object: T) -> ObjectMonitor<T> { public static func monitorObject<T: NSManagedObject>(object: T) -> ObjectMonitor<T> {
return self.defaultStack.monitorObject(object) return self.defaultStack.monitorObject(object)
@@ -51,6 +51,7 @@ public extension CoreStore {
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - 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 - returns: a `ListMonitor` instance that monitors changes to the list
*/ */
@warn_unused_result
public static func monitorList<T: NSManagedObject>(from: From<T>, _ queryClauses: FetchClause...) -> ListMonitor<T> { public static func monitorList<T: NSManagedObject>(from: From<T>, _ queryClauses: FetchClause...) -> ListMonitor<T> {
return self.defaultStack.monitorList(from, queryClauses) return self.defaultStack.monitorList(from, queryClauses)
@@ -63,11 +64,36 @@ public extension CoreStore {
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - 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 - returns: a `ListMonitor` instance that monitors changes to the list
*/ */
@warn_unused_result
public static func monitorList<T: NSManagedObject>(from: From<T>, _ queryClauses: [FetchClause]) -> ListMonitor<T> { public static func monitorList<T: NSManagedObject>(from: From<T>, _ queryClauses: [FetchClause]) -> ListMonitor<T> {
return self.defaultStack.monitorList(from, queryClauses) return self.defaultStack.monitorList(from, queryClauses)
} }
/**
Using the `defaultStack`, asynchronously creates a `ListMonitor` for a list of `NSManagedObject`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<T: NSManagedObject>(createAsynchronously createAsynchronously: (ListMonitor<T>) -> Void, _ from: From<T>, _ fetchClauses: FetchClause...) {
self.defaultStack.monitorList(createAsynchronously: createAsynchronously, from, fetchClauses)
}
/**
Using the `defaultStack`, asynchronously creates a `ListMonitor` for a list of `NSManagedObject`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<T: NSManagedObject>(createAsynchronously createAsynchronously: (ListMonitor<T>) -> Void, _ from: From<T>, _ fetchClauses: [FetchClause]) {
self.defaultStack.monitorList(createAsynchronously: createAsynchronously, from, fetchClauses)
}
/** /**
Using the `defaultStack`, creates a `ListMonitor` for a sectioned list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Using the `defaultStack`, creates a `ListMonitor` for a sectioned list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
@@ -76,6 +102,7 @@ public extension CoreStore {
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - 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 - returns: a `ListMonitor` instance that monitors changes to the list
*/ */
@warn_unused_result
public static func monitorSectionedList<T: NSManagedObject>(from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListMonitor<T> { public static func monitorSectionedList<T: NSManagedObject>(from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListMonitor<T> {
return self.defaultStack.monitorSectionedList(from, sectionBy, fetchClauses) return self.defaultStack.monitorSectionedList(from, sectionBy, fetchClauses)
@@ -89,8 +116,35 @@ public extension CoreStore {
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - 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 - returns: a `ListMonitor` instance that monitors changes to the list
*/ */
@warn_unused_result
public static func monitorSectionedList<T: NSManagedObject>(from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListMonitor<T> { public static func monitorSectionedList<T: NSManagedObject>(from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListMonitor<T> {
return self.defaultStack.monitorSectionedList(from, sectionBy, fetchClauses) return self.defaultStack.monitorSectionedList(from, sectionBy, fetchClauses)
} }
/**
Using the `defaultStack`, asynchronously creates a `ListMonitor` for a sectioned list of `NSManagedObject`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<T: NSManagedObject>(createAsynchronously createAsynchronously: (ListMonitor<T>) -> Void, _ from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) {
self.defaultStack.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses)
}
/**
Using the `defaultStack`, asynchronously creates a `ListMonitor` for a sectioned list of `NSManagedObject`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<T: NSManagedObject>(createAsynchronously createAsynchronously: (ListMonitor<T>) -> Void, _ from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) {
self.defaultStack.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses)
}
} }
+89 -3
View File
@@ -2,7 +2,7 @@
// DataStack+Observing.swift // DataStack+Observing.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -25,21 +25,23 @@
import Foundation import Foundation
import CoreData import CoreData
#if USE_FRAMEWORKS
import GCDKit import GCDKit
#endif
// MARK: - DataStack // MARK: - DataStack
@available(OSX, unavailable)
public extension DataStack { public extension DataStack {
// MARK: Public
/** /**
Creates a `ObjectMonitor` for the specified `NSManagedObject`. Multiple `ObjectObserver`s may then register themselves to be notified when changes are made to the `NSManagedObject`. Creates a `ObjectMonitor` for the specified `NSManagedObject`. Multiple `ObjectObserver`s may then register themselves to be notified when changes are made to the `NSManagedObject`.
- parameter object: the `NSManagedObject` to observe changes from - parameter object: the `NSManagedObject` to observe changes from
- returns: a `ObjectMonitor` that monitors changes to `object` - returns: a `ObjectMonitor` that monitors changes to `object`
*/ */
@warn_unused_result
public func monitorObject<T: NSManagedObject>(object: T) -> ObjectMonitor<T> { public func monitorObject<T: NSManagedObject>(object: T) -> ObjectMonitor<T> {
CoreStore.assert( CoreStore.assert(
@@ -60,6 +62,7 @@ public extension DataStack {
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - 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 - returns: a `ListMonitor` instance that monitors changes to the list
*/ */
@warn_unused_result
public func monitorList<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> ListMonitor<T> { public func monitorList<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> ListMonitor<T> {
return self.monitorList(from, fetchClauses) return self.monitorList(from, fetchClauses)
@@ -72,6 +75,7 @@ public extension DataStack {
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - 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 - returns: a `ListMonitor` instance that monitors changes to the list
*/ */
@warn_unused_result
public func monitorList<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> ListMonitor<T> { public func monitorList<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> ListMonitor<T> {
CoreStore.assert( CoreStore.assert(
@@ -91,6 +95,45 @@ public extension DataStack {
) )
} }
/**
Asynchronously creates a `ListMonitor` for a list of `NSManagedObject`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 func monitorList<T: NSManagedObject>(createAsynchronously createAsynchronously: (ListMonitor<T>) -> Void, _ from: From<T>, _ fetchClauses: FetchClause...) {
self.monitorList(createAsynchronously: createAsynchronously, from, fetchClauses)
}
/**
Asynchronously creates a `ListMonitor` for a list of `NSManagedObject`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 func monitorList<T: NSManagedObject>(createAsynchronously createAsynchronously: (ListMonitor<T>) -> Void, _ from: From<T>, _ fetchClauses: [FetchClause]) {
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to observe objects from \(typeName(self)) outside the main thread."
)
CoreStore.assert(
fetchClauses.filter { $0 is OrderBy }.count > 0,
"A ListMonitor requires an OrderBy clause."
)
_ = ListMonitor(
dataStack: self,
from: from,
sectionBy: nil,
fetchClauses: fetchClauses,
createAsynchronously: createAsynchronously
)
}
/** /**
Creates a `ListMonitor` for a sectioned list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list. Creates a `ListMonitor` for a sectioned list of `NSManagedObject`s that satisfy the specified fetch clauses. Multiple `ListObserver`s may then register themselves to be notified when changes are made to the list.
@@ -99,6 +142,7 @@ public extension DataStack {
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - 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 - returns: a `ListMonitor` instance that monitors changes to the list
*/ */
@warn_unused_result
public func monitorSectionedList<T: NSManagedObject>(from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListMonitor<T> { public func monitorSectionedList<T: NSManagedObject>(from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListMonitor<T> {
return self.monitorSectionedList(from, sectionBy, fetchClauses) return self.monitorSectionedList(from, sectionBy, fetchClauses)
@@ -112,6 +156,7 @@ public extension DataStack {
- parameter fetchClauses: a series of `FetchClause` instances for fetching the object list. Accepts `Where`, `OrderBy`, and `Tweak` clauses. - 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 - returns: a `ListMonitor` instance that monitors changes to the list
*/ */
@warn_unused_result
public func monitorSectionedList<T: NSManagedObject>(from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListMonitor<T> { public func monitorSectionedList<T: NSManagedObject>(from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListMonitor<T> {
CoreStore.assert( CoreStore.assert(
@@ -130,4 +175,45 @@ public extension DataStack {
fetchClauses: fetchClauses fetchClauses: fetchClauses
) )
} }
/**
Asynchronously creates a `ListMonitor` for a sectioned list of `NSManagedObject`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 func monitorSectionedList<T: NSManagedObject>(createAsynchronously createAsynchronously: (ListMonitor<T>) -> Void, _ from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) {
self.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses)
}
/**
Asynchronously creates a `ListMonitor` for a sectioned list of `NSManagedObject`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 func monitorSectionedList<T: NSManagedObject>(createAsynchronously createAsynchronously: (ListMonitor<T>) -> Void, _ from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) {
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to observe objects from \(typeName(self)) outside the main thread."
)
CoreStore.assert(
fetchClauses.filter { $0 is OrderBy }.count > 0,
"A ListMonitor requires an OrderBy clause."
)
_ = ListMonitor(
dataStack: self,
from: from,
sectionBy: sectionBy,
fetchClauses: fetchClauses,
createAsynchronously: createAsynchronously
)
}
} }
File diff suppressed because it is too large Load Diff
+63 -21
View File
@@ -2,7 +2,7 @@
// ListObserver.swift // ListObserver.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -31,46 +31,76 @@ import CoreData
/** /**
Implement the `ListObserver` protocol to observe changes to a list of `NSManagedObject`s. `ListObserver`s may register themselves to a `ListMonitor`'s `addObserver(_:)` method: Implement the `ListObserver` protocol to observe changes to a list of `NSManagedObject`s. `ListObserver`s may register themselves to a `ListMonitor`'s `addObserver(_:)` method:
```
let monitor = CoreStore.monitorList( let monitor = CoreStore.monitorList(
From(MyPersonEntity), From(MyPersonEntity),
OrderBy(.Ascending("lastName")) OrderBy(.Ascending("lastName"))
) )
monitor.addObserver(self) monitor.addObserver(self)
```
*/ */
@available(OSX, unavailable)
public protocol ListObserver: class { public protocol ListObserver: class {
/** /**
The `NSManagedObject` type for the observed list The `NSManagedObject` type for the observed list
*/ */
typealias EntityType: NSManagedObject associatedtype ListEntityType: NSManagedObject
/** /**
Handles processing just before a change to the observed list occurs Handles processing just before a change to the observed list occurs
- parameter monitor: the `ListMonitor` monitoring the list being observed - parameter monitor: the `ListMonitor` monitoring the list being observed
*/ */
func listMonitorWillChange(monitor: ListMonitor<EntityType>) func listMonitorWillChange(monitor: ListMonitor<ListEntityType>)
/** /**
Handles processing right after a change to the observed list occurs Handles processing right after a change to the observed list occurs
- parameter monitor: the `ListMonitor` monitoring the object being observed - parameter monitor: the `ListMonitor` monitoring the object being observed
*/ */
func listMonitorDidChange(monitor: ListMonitor<EntityType>) func listMonitorDidChange(monitor: ListMonitor<ListEntityType>)
/**
This method is broadcast from within the `ListMonitor`'s `refetch(...)` method to let observers prepare for the internal `NSFetchedResultsController`'s pending change to its predicate, sort descriptors, etc. Note that the actual refetch will happen after the `NSFetchedResultsController`'s last `controllerDidChangeContent(_:)` notification completes.
- parameter monitor: the `ListMonitor` monitoring the object being observed
*/
func listMonitorWillRefetch(monitor: ListMonitor<ListEntityType>)
/**
After the `ListMonitor`'s `refetch(...)` method is called, this method is broadcast after the `NSFetchedResultsController`'s last `controllerDidChangeContent(_:)` notification completes.
- parameter monitor: the `ListMonitor` monitoring the object being observed
*/
func listMonitorDidRefetch(monitor: ListMonitor<ListEntityType>)
} }
// MARK: - ListObserver (Default Implementations)
@available(OSX, unavailable)
public extension ListObserver { public extension ListObserver {
/** /**
The default implementation does nothing. The default implementation does nothing.
*/ */
func listMonitorWillChange(monitor: ListMonitor<EntityType>) { } func listMonitorWillChange(monitor: ListMonitor<ListEntityType>) { }
/** /**
The default implementation does nothing. The default implementation does nothing.
*/ */
func listMonitorDidChange(monitor: ListMonitor<EntityType>) { } func listMonitorDidChange(monitor: ListMonitor<ListEntityType>) { }
/**
The default implementation does nothing.
*/
func listMonitorWillRefetch(monitor: ListMonitor<ListEntityType>) { }
/**
The default implementation does nothing.
*/
func listMonitorDidRefetch(monitor: ListMonitor<ListEntityType>) { }
} }
@@ -78,13 +108,15 @@ public extension ListObserver {
/** /**
Implement the `ListObjectObserver` protocol to observe detailed changes to a list's object. `ListObjectObserver`s may register themselves to a `ListMonitor`'s `addObserver(_:)` method: Implement the `ListObjectObserver` protocol to observe detailed changes to a list's object. `ListObjectObserver`s may register themselves to a `ListMonitor`'s `addObserver(_:)` method:
```
let monitor = CoreStore.monitorList( let monitor = CoreStore.monitorList(
From(MyPersonEntity), From(MyPersonEntity),
OrderBy(.Ascending("lastName")) OrderBy(.Ascending("lastName"))
) )
monitor.addObserver(self) monitor.addObserver(self)
```
*/ */
@available(OSX, unavailable)
public protocol ListObjectObserver: ListObserver { public protocol ListObjectObserver: ListObserver {
/** /**
@@ -94,7 +126,7 @@ public protocol ListObjectObserver: ListObserver {
- parameter object: the entity type for the inserted object - parameter object: the entity type for the inserted object
- parameter indexPath: the new `NSIndexPath` for the inserted object - parameter indexPath: the new `NSIndexPath` for the inserted object
*/ */
func listMonitor(monitor: ListMonitor<EntityType>, didInsertObject object: EntityType, toIndexPath indexPath: NSIndexPath) func listMonitor(monitor: ListMonitor<ListEntityType>, didInsertObject object: ListEntityType, toIndexPath indexPath: NSIndexPath)
/** /**
Notifies that an object was deleted from the specified `NSIndexPath` in the list Notifies that an object was deleted from the specified `NSIndexPath` in the list
@@ -103,7 +135,7 @@ public protocol ListObjectObserver: ListObserver {
- parameter object: the entity type for the deleted object - parameter object: the entity type for the deleted object
- parameter indexPath: the `NSIndexPath` for the deleted object - parameter indexPath: the `NSIndexPath` for the deleted object
*/ */
func listMonitor(monitor: ListMonitor<EntityType>, didDeleteObject object: EntityType, fromIndexPath indexPath: NSIndexPath) func listMonitor(monitor: ListMonitor<ListEntityType>, didDeleteObject object: ListEntityType, fromIndexPath indexPath: NSIndexPath)
/** /**
Notifies that an object at the specified `NSIndexPath` was updated Notifies that an object at the specified `NSIndexPath` was updated
@@ -112,7 +144,7 @@ public protocol ListObjectObserver: ListObserver {
- parameter object: the entity type for the updated object - parameter object: the entity type for the updated object
- parameter indexPath: the `NSIndexPath` for the updated object - parameter indexPath: the `NSIndexPath` for the updated object
*/ */
func listMonitor(monitor: ListMonitor<EntityType>, didUpdateObject object: EntityType, atIndexPath indexPath: NSIndexPath) func listMonitor(monitor: ListMonitor<ListEntityType>, didUpdateObject object: ListEntityType, atIndexPath indexPath: NSIndexPath)
/** /**
Notifies that an object's index changed Notifies that an object's index changed
@@ -122,30 +154,34 @@ public protocol ListObjectObserver: ListObserver {
- parameter fromIndexPath: the previous `NSIndexPath` for the moved object - parameter fromIndexPath: the previous `NSIndexPath` for the moved object
- parameter toIndexPath: the new `NSIndexPath` for the moved object - parameter toIndexPath: the new `NSIndexPath` for the moved object
*/ */
func listMonitor(monitor: ListMonitor<EntityType>, didMoveObject object: EntityType, fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) func listMonitor(monitor: ListMonitor<ListEntityType>, didMoveObject object: ListEntityType, fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath)
} }
// MARK: - ListObjectObserver (Default Implementations)
@available(OSX, unavailable)
public extension ListObjectObserver { public extension ListObjectObserver {
/** /**
The default implementation does nothing. The default implementation does nothing.
*/ */
func listMonitor(monitor: ListMonitor<EntityType>, didInsertObject object: EntityType, toIndexPath indexPath: NSIndexPath) { } func listMonitor(monitor: ListMonitor<ListEntityType>, didInsertObject object: ListEntityType, toIndexPath indexPath: NSIndexPath) { }
/** /**
The default implementation does nothing. The default implementation does nothing.
*/ */
func listMonitor(monitor: ListMonitor<EntityType>, didDeleteObject object: EntityType, fromIndexPath indexPath: NSIndexPath) { } func listMonitor(monitor: ListMonitor<ListEntityType>, didDeleteObject object: ListEntityType, fromIndexPath indexPath: NSIndexPath) { }
/** /**
The default implementation does nothing. The default implementation does nothing.
*/ */
func listMonitor(monitor: ListMonitor<EntityType>, didUpdateObject object: EntityType, atIndexPath indexPath: NSIndexPath) { } func listMonitor(monitor: ListMonitor<ListEntityType>, didUpdateObject object: ListEntityType, atIndexPath indexPath: NSIndexPath) { }
/** /**
The default implementation does nothing. The default implementation does nothing.
*/ */
func listMonitor(monitor: ListMonitor<EntityType>, didMoveObject object: EntityType, fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) { } func listMonitor(monitor: ListMonitor<ListEntityType>, didMoveObject object: ListEntityType, fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) { }
} }
@@ -153,14 +189,16 @@ public extension ListObjectObserver {
/** /**
Implement the `ListSectionObserver` protocol to observe changes to a list's section info. `ListSectionObserver`s may register themselves to a `ListMonitor`'s `addObserver(_:)` method: Implement the `ListSectionObserver` protocol to observe changes to a list's section info. `ListSectionObserver`s may register themselves to a `ListMonitor`'s `addObserver(_:)` method:
```
let monitor = CoreStore.monitorSectionedList( let monitor = CoreStore.monitorSectionedList(
From(MyPersonEntity), From(MyPersonEntity),
SectionBy("age") { "Age \($0)" }, SectionBy("age") { "Age \($0)" },
OrderBy(.Ascending("lastName")) OrderBy(.Ascending("lastName"))
) )
monitor.addObserver(self) monitor.addObserver(self)
```
*/ */
@available(OSX, unavailable)
public protocol ListSectionObserver: ListObjectObserver { public protocol ListSectionObserver: ListObjectObserver {
/** /**
@@ -170,7 +208,7 @@ public protocol ListSectionObserver: ListObjectObserver {
- parameter sectionInfo: the `NSFetchedResultsSectionInfo` for the inserted section - parameter sectionInfo: the `NSFetchedResultsSectionInfo` for the inserted section
- parameter sectionIndex: the new section index for the new section - parameter sectionIndex: the new section index for the new section
*/ */
func listMonitor(monitor: ListMonitor<EntityType>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int) func listMonitor(monitor: ListMonitor<ListEntityType>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int)
/** /**
Notifies that a section was inserted at the specified index Notifies that a section was inserted at the specified index
@@ -179,18 +217,22 @@ public protocol ListSectionObserver: ListObjectObserver {
- parameter sectionInfo: the `NSFetchedResultsSectionInfo` for the deleted section - parameter sectionInfo: the `NSFetchedResultsSectionInfo` for the deleted section
- parameter sectionIndex: the previous section index for the deleted section - parameter sectionIndex: the previous section index for the deleted section
*/ */
func listMonitor(monitor: ListMonitor<EntityType>, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int) func listMonitor(monitor: ListMonitor<ListEntityType>, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int)
} }
// MARK: - ListSectionObserver (Default Implementations)
@available(OSX, unavailable)
public extension ListSectionObserver { public extension ListSectionObserver {
/** /**
The default implementation does nothing. The default implementation does nothing.
*/ */
func listMonitor(monitor: ListMonitor<EntityType>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int) { } func listMonitor(monitor: ListMonitor<ListEntityType>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int) { }
/** /**
The default implementation does nothing. The default implementation does nothing.
*/ */
func listMonitor(monitor: ListMonitor<EntityType>, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int) { } func listMonitor(monitor: ListMonitor<ListEntityType>, didDeleteSection sectionInfo: NSFetchedResultsSectionInfo, fromSectionIndex sectionIndex: Int) { }
} }
+83 -109
View File
@@ -2,7 +2,7 @@
// ObjectMonitor.swift // ObjectMonitor.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -25,39 +25,26 @@
import Foundation import Foundation
import CoreData import CoreData
#if USE_FRAMEWORKS
import GCDKit import GCDKit
#endif
private let ObjectMonitorWillChangeObjectNotification = "ObjectMonitorWillChangeObjectNotification"
private let ObjectMonitorDidDeleteObjectNotification = "ObjectMonitorDidDeleteObjectNotification"
private let ObjectMonitorDidUpdateObjectNotification = "ObjectMonitorDidUpdateObjectNotification"
private let UserInfoKeyObject = "UserInfoKeyObject"
private struct NotificationKey {
static var willChangeObject: Void?
static var didDeleteObject: Void?
static var didUpdateObject: Void?
}
// MARK: - ObjectMonitor // MARK: - ObjectMonitor
/** /**
The `ObjectMonitor` monitors changes to a single `NSManagedObject` instance. Observers that implement the `ObjectObserver` protocol may then register themselves to the `ObjectMonitor`'s `addObserver(_:)` method: The `ObjectMonitor` monitors changes to a single `NSManagedObject` instance. Observers that implement the `ObjectObserver` protocol may then register themselves to the `ObjectMonitor`'s `addObserver(_:)` method:
```
let monitor = CoreStore.monitorObject(object) let monitor = CoreStore.monitorObject(object)
monitor.addObserver(self) monitor.addObserver(self)
```
The created `ObjectMonitor` instance needs to be held on (retained) for as long as the object needs to be observed. The created `ObjectMonitor` instance needs to be held on (retained) for as long as the object needs to be observed.
Observers registered via `addObserver(_:)` are not retained. `ObjectMonitor` only keeps a `weak` reference to all observers, thus keeping itself free from retain-cycles. Observers registered via `addObserver(_:)` are not retained. `ObjectMonitor` only keeps a `weak` reference to all observers, thus keeping itself free from retain-cycles.
*/ */
@available(OSX, unavailable)
public final class ObjectMonitor<T: NSManagedObject> { public final class ObjectMonitor<T: NSManagedObject> {
// MARK: Public
/** /**
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.
*/ */
@@ -85,7 +72,7 @@ public final class ObjectMonitor<T: NSManagedObject> {
- parameter observer: an `ObjectObserver` to send change notifications to - parameter observer: an `ObjectObserver` to send change notifications to
*/ */
public func addObserver<U: ObjectObserver where U.EntityType == T>(observer: U) { public func addObserver<U: ObjectObserver where U.ObjectEntityType == T>(observer: U) {
CoreStore.assert( CoreStore.assert(
NSThread.isMainThread(), NSThread.isMainThread(),
@@ -95,47 +82,52 @@ public final class ObjectMonitor<T: NSManagedObject> {
self.removeObserver(observer) self.removeObserver(observer)
self.registerChangeNotification( self.registerChangeNotification(
&NotificationKey.willChangeObject, &self.willChangeObjectKey,
name: ObjectMonitorWillChangeObjectNotification, name: ObjectMonitorWillChangeObjectNotification,
toObserver: observer, toObserver: observer,
callback: { [weak observer] (monitor) -> Void in callback: { [weak observer] (monitor) -> Void in
if let object = monitor.object, let observer = observer { guard let object = monitor.object, let observer = observer else {
observer.objectMonitor(monitor, willUpdateObject: object) return
} }
observer.objectMonitor(monitor, willUpdateObject: object)
} }
) )
self.registerObjectNotification( self.registerObjectNotification(
&NotificationKey.didDeleteObject, &self.didDeleteObjectKey,
name: ObjectMonitorDidDeleteObjectNotification, name: ObjectMonitorDidDeleteObjectNotification,
toObserver: observer, toObserver: observer,
callback: { [weak observer] (monitor, object) -> Void in callback: { [weak observer] (monitor, object) -> Void in
if let observer = observer { guard let observer = observer else {
observer.objectMonitor(monitor, didDeleteObject: object) return
} }
observer.objectMonitor(monitor, didDeleteObject: object)
} }
) )
self.registerObjectNotification( self.registerObjectNotification(
&NotificationKey.didUpdateObject, &self.didUpdateObjectKey,
name: ObjectMonitorDidUpdateObjectNotification, name: ObjectMonitorDidUpdateObjectNotification,
toObserver: observer, toObserver: observer,
callback: { [weak self, weak observer] (monitor, object) -> Void in callback: { [weak self, weak observer] (monitor, object) -> Void in
if let strongSelf = self, let observer = observer { guard let strongSelf = self, let observer = observer else {
return
}
let previousCommitedAttributes = strongSelf.lastCommittedAttributes let previousCommitedAttributes = strongSelf.lastCommittedAttributes
let currentCommitedAttributes = object.committedValuesForKeys(nil) as! [String: NSObject] let currentCommitedAttributes = object.committedValuesForKeys(nil) as! [String: NSObject]
let changedKeys = currentCommitedAttributes.keys.reduce(Set<String>()) { (var changedKeys, key) -> Set<String> in var changedKeys = Set<String>()
for key in currentCommitedAttributes.keys {
if previousCommitedAttributes[key] != currentCommitedAttributes[key] { if previousCommitedAttributes[key] != currentCommitedAttributes[key] {
changedKeys.insert(key) changedKeys.insert(key)
} }
return changedKeys
} }
strongSelf.lastCommittedAttributes = currentCommitedAttributes strongSelf.lastCommittedAttributes = currentCommitedAttributes
@@ -145,7 +137,6 @@ public final class ObjectMonitor<T: NSManagedObject> {
changedPersistentKeys: changedKeys changedPersistentKeys: changedKeys
) )
} }
}
) )
} }
@@ -156,7 +147,7 @@ public final class ObjectMonitor<T: NSManagedObject> {
- parameter observer: an `ObjectObserver` to unregister notifications to - parameter observer: an `ObjectObserver` to unregister notifications to
*/ */
public func removeObserver<U: ObjectObserver where U.EntityType == T>(observer: U) { public func removeObserver<U: ObjectObserver where U.ObjectEntityType == T>(observer: U) {
CoreStore.assert( CoreStore.assert(
NSThread.isMainThread(), NSThread.isMainThread(),
@@ -164,57 +155,67 @@ public final class ObjectMonitor<T: NSManagedObject> {
) )
let nilValue: AnyObject? = nil let nilValue: AnyObject? = nil
setAssociatedRetainedObject(nilValue, forKey: &NotificationKey.willChangeObject, inObject: observer) setAssociatedRetainedObject(nilValue, forKey: &self.willChangeObjectKey, inObject: observer)
setAssociatedRetainedObject(nilValue, forKey: &NotificationKey.didDeleteObject, inObject: observer) setAssociatedRetainedObject(nilValue, forKey: &self.didDeleteObjectKey, inObject: observer)
setAssociatedRetainedObject(nilValue, forKey: &NotificationKey.didUpdateObject, inObject: observer) setAssociatedRetainedObject(nilValue, forKey: &self.didUpdateObjectKey, inObject: observer)
} }
// MARK: Internal // MARK: Internal
internal init(dataStack: DataStack, object: T) { internal convenience init(dataStack: DataStack, object: T) {
let context = dataStack.mainContext self.init(context: dataStack.mainContext, object: object)
}
let fetchRequest = NSFetchRequest() internal convenience init(unsafeTransaction: UnsafeDataTransaction, object: T) {
self.init(context: unsafeTransaction.context, object: object)
}
private init(context: NSManagedObjectContext, object: T) {
let fetchRequest = CoreStoreFetchRequest()
fetchRequest.entity = object.entity fetchRequest.entity = object.entity
fetchRequest.fetchLimit = 0
fetchRequest.fetchLimit = 1
fetchRequest.resultType = .ManagedObjectResultType fetchRequest.resultType = .ManagedObjectResultType
fetchRequest.sortDescriptors = [] fetchRequest.sortDescriptors = []
fetchRequest.includesPendingChanges = false
fetchRequest.shouldRefreshRefetchedObjects = true
let originalObjectID = object.objectID let fetchedResultsController = CoreStoreFetchedResultsController<T>(
Where("SELF", isEqualTo: originalObjectID).applyToFetchRequest(fetchRequest) context: context,
let fetchedResultsController = NSFetchedResultsController(
fetchRequest: fetchRequest, fetchRequest: fetchRequest,
managedObjectContext: context, fetchClauses: [Where("SELF", isEqualTo: object.objectID)]
sectionNameKeyPath: nil,
cacheName: nil
) )
let fetchedResultsControllerDelegate = FetchedResultsControllerDelegate() let fetchedResultsControllerDelegate = FetchedResultsControllerDelegate()
self.originalObjectID = originalObjectID
self.fetchedResultsController = fetchedResultsController self.fetchedResultsController = fetchedResultsController
self.fetchedResultsControllerDelegate = fetchedResultsControllerDelegate self.fetchedResultsControllerDelegate = fetchedResultsControllerDelegate
self.parentStack = dataStack
fetchedResultsControllerDelegate.handler = self fetchedResultsControllerDelegate.handler = self
fetchedResultsControllerDelegate.fetchedResultsController = fetchedResultsController fetchedResultsControllerDelegate.fetchedResultsController = fetchedResultsController
try! fetchedResultsController.performFetch() try! fetchedResultsController.performFetchFromSpecifiedStores()
self.lastCommittedAttributes = (self.object?.committedValuesForKeys(nil) as? [String: NSObject]) ?? [:] self.lastCommittedAttributes = (self.object?.committedValuesForKeys(nil) as? [String: NSObject]) ?? [:]
} }
deinit {
self.fetchedResultsControllerDelegate.fetchedResultsController = nil
}
// MARK: Private // MARK: Private
private let originalObjectID: NSManagedObjectID private let fetchedResultsController: CoreStoreFetchedResultsController<T>
private let fetchedResultsController: NSFetchedResultsController
private let fetchedResultsControllerDelegate: FetchedResultsControllerDelegate private let fetchedResultsControllerDelegate: FetchedResultsControllerDelegate
private var lastCommittedAttributes = [String: NSObject]() private var lastCommittedAttributes = [String: NSObject]()
private weak var parentStack: DataStack?
private var willChangeObjectKey: Void?
private var didDeleteObjectKey: Void?
private var didUpdateObjectKey: Void?
private func registerChangeNotification(notificationKey: UnsafePointer<Void>, name: String, toObserver observer: AnyObject, callback: (monitor: ObjectMonitor<T>) -> Void) { private func registerChangeNotification(notificationKey: UnsafePointer<Void>, name: String, toObserver observer: AnyObject, callback: (monitor: ObjectMonitor<T>) -> Void) {
@@ -224,10 +225,11 @@ public final class ObjectMonitor<T: NSManagedObject> {
object: self, object: self,
closure: { [weak self] (note) -> Void in closure: { [weak self] (note) -> Void in
if let strongSelf = self { guard let strongSelf = self else {
callback(monitor: strongSelf) return
} }
callback(monitor: strongSelf)
} }
), ),
forKey: notificationKey, forKey: notificationKey,
@@ -243,15 +245,13 @@ public final class ObjectMonitor<T: NSManagedObject> {
object: self, object: self,
closure: { [weak self] (note) -> Void in closure: { [weak self] (note) -> Void in
if let strongSelf = self, guard let strongSelf = self,
let userInfo = note.userInfo, let userInfo = note.userInfo,
let object = userInfo[UserInfoKeyObject] as? T { let object = userInfo[UserInfoKeyObject] as? T else {
callback( return
monitor: strongSelf,
object: object
)
} }
callback(monitor: strongSelf, object: object)
} }
), ),
forKey: notificationKey, forKey: notificationKey,
@@ -263,21 +263,34 @@ public final class ObjectMonitor<T: NSManagedObject> {
// MARK: - ObjectMonitor: Equatable // MARK: - ObjectMonitor: Equatable
@available(OSX, unavailable)
public func ==<T: NSManagedObject>(lhs: ObjectMonitor<T>, rhs: ObjectMonitor<T>) -> Bool { public func ==<T: NSManagedObject>(lhs: ObjectMonitor<T>, rhs: ObjectMonitor<T>) -> Bool {
return lhs === rhs return lhs === rhs
} }
@available(OSX, unavailable)
extension ObjectMonitor: Equatable { } extension ObjectMonitor: Equatable { }
// MARK: - ObjectMonitor: FetchedResultsControllerHandler // MARK: - ObjectMonitor: FetchedResultsControllerHandler
@available(OSX, unavailable)
extension ObjectMonitor: FetchedResultsControllerHandler { extension ObjectMonitor: FetchedResultsControllerHandler {
// MARK: FetchedResultsControllerHandler // MARK: FetchedResultsControllerHandler
private func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) { internal func controllerWillChangeContent(controller: NSFetchedResultsController) {
NSNotificationCenter.defaultCenter().postNotificationName(
ObjectMonitorWillChangeObjectNotification,
object: self
)
}
internal func controllerDidChangeContent(controller: NSFetchedResultsController) { }
internal func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
switch type { switch type {
@@ -300,57 +313,18 @@ extension ObjectMonitor: FetchedResultsControllerHandler {
} }
} }
private func controllerWillChangeContent(controller: NSFetchedResultsController) { internal func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) { }
NSNotificationCenter.defaultCenter().postNotificationName( internal func controller(controller: NSFetchedResultsController, sectionIndexTitleForSectionName sectionName: String?) -> String? {
ObjectMonitorWillChangeObjectNotification,
object: self return sectionName
)
} }
} }
// MARK: - FetchedResultsControllerHandler private let ObjectMonitorWillChangeObjectNotification = "ObjectMonitorWillChangeObjectNotification"
private let ObjectMonitorDidDeleteObjectNotification = "ObjectMonitorDidDeleteObjectNotification"
private let ObjectMonitorDidUpdateObjectNotification = "ObjectMonitorDidUpdateObjectNotification"
private protocol FetchedResultsControllerHandler: class { private let UserInfoKeyObject = "UserInfoKeyObject"
func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?)
func controllerWillChangeContent(controller: NSFetchedResultsController)
}
// MARK: - FetchedResultsControllerDelegate
private final class FetchedResultsControllerDelegate: NSObject, NSFetchedResultsControllerDelegate {
// MARK: NSFetchedResultsControllerDelegate
@objc dynamic func controllerWillChangeContent(controller: NSFetchedResultsController) {
self.handler?.controllerWillChangeContent(controller)
}
@objc dynamic func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
self.handler?.controller(controller, didChangeObject: anObject, atIndexPath: indexPath, forChangeType: type, newIndexPath: newIndexPath)
}
// MARK: Private
weak var handler: FetchedResultsControllerHandler?
weak var fetchedResultsController: NSFetchedResultsController? {
didSet {
oldValue?.delegate = nil
self.fetchedResultsController?.delegate = self
}
}
deinit {
self.fetchedResultsController?.delegate = nil
}
}
+14 -9
View File
@@ -2,7 +2,7 @@
// ObjectObserver.swift // ObjectObserver.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -31,16 +31,18 @@ import CoreData
/** /**
Implement the `ObjectObserver` protocol to observe changes to a single `NSManagedObject` instance. `ObjectObserver`s may register themselves to a `ObjectMonitor`'s `addObserver(_:)` method: Implement the `ObjectObserver` protocol to observe changes to a single `NSManagedObject` instance. `ObjectObserver`s may register themselves to a `ObjectMonitor`'s `addObserver(_:)` method:
```
let monitor = CoreStore.monitorObject(object) let monitor = CoreStore.monitorObject(object)
monitor.addObserver(self) monitor.addObserver(self)
```
*/ */
@available(OSX, unavailable)
public protocol ObjectObserver: class { public protocol ObjectObserver: class {
/** /**
The `NSManagedObject` type for the observed object The `NSManagedObject` type for the observed object
*/ */
typealias EntityType: NSManagedObject associatedtype ObjectEntityType: NSManagedObject
/** /**
Handles processing just before a change to the observed `object` occurs Handles processing just before a change to the observed `object` occurs
@@ -48,7 +50,7 @@ public protocol ObjectObserver: class {
- parameter monitor: the `ObjectMonitor` monitoring the object being observed - parameter monitor: the `ObjectMonitor` monitoring the object being observed
- parameter object: the `NSManagedObject` instance being observed - parameter object: the `NSManagedObject` instance being observed
*/ */
func objectMonitor(monitor: ObjectMonitor<EntityType>, willUpdateObject object: EntityType) func objectMonitor(monitor: ObjectMonitor<ObjectEntityType>, willUpdateObject object: ObjectEntityType)
/** /**
Handles processing right after a change to the observed `object` occurs Handles processing right after a change to the observed `object` occurs
@@ -57,7 +59,7 @@ public protocol ObjectObserver: class {
- parameter object: the `NSManagedObject` instance being observed - parameter object: the `NSManagedObject` instance being observed
- parameter changedPersistentKeys: a `Set` of key paths for the attributes that were changed. Note that `changedPersistentKeys` only contains keys for attributes/relationships present in the persistent store, thus transient properties will not be reported. - parameter changedPersistentKeys: a `Set` of key paths for the attributes that were changed. Note that `changedPersistentKeys` only contains keys for attributes/relationships present in the persistent store, thus transient properties will not be reported.
*/ */
func objectMonitor(monitor: ObjectMonitor<EntityType>, didUpdateObject object: EntityType, changedPersistentKeys: Set<KeyPath>) func objectMonitor(monitor: ObjectMonitor<ObjectEntityType>, didUpdateObject object: ObjectEntityType, changedPersistentKeys: Set<KeyPath>)
/** /**
Handles processing right after `object` is deleted Handles processing right after `object` is deleted
@@ -65,24 +67,27 @@ public protocol ObjectObserver: class {
- parameter monitor: the `ObjectMonitor` monitoring the object being observed - parameter monitor: the `ObjectMonitor` monitoring the object being observed
- parameter object: the `NSManagedObject` instance being observed - parameter object: the `NSManagedObject` instance being observed
*/ */
func objectMonitor(monitor: ObjectMonitor<EntityType>, didDeleteObject object: EntityType) func objectMonitor(monitor: ObjectMonitor<ObjectEntityType>, didDeleteObject object: ObjectEntityType)
} }
// MARK: - ObjectObserver (Default Implementations)
@available(OSX, unavailable)
public extension ObjectObserver { public extension ObjectObserver {
/** /**
The default implementation does nothing. The default implementation does nothing.
*/ */
func objectMonitor(monitor: ObjectMonitor<EntityType>, willUpdateObject object: EntityType) { } func objectMonitor(monitor: ObjectMonitor<ObjectEntityType>, willUpdateObject object: ObjectEntityType) { }
/** /**
The default implementation does nothing. The default implementation does nothing.
*/ */
func objectMonitor(monitor: ObjectMonitor<EntityType>, didUpdateObject object: EntityType, changedPersistentKeys: Set<KeyPath>) { } func objectMonitor(monitor: ObjectMonitor<ObjectEntityType>, didUpdateObject object: ObjectEntityType, changedPersistentKeys: Set<KeyPath>) { }
/** /**
The default implementation does nothing. The default implementation does nothing.
*/ */
func objectMonitor(monitor: ObjectMonitor<EntityType>, didDeleteObject object: EntityType) { } func objectMonitor(monitor: ObjectMonitor<ObjectEntityType>, didDeleteObject object: ObjectEntityType) { }
} }
+4 -4
View File
@@ -2,7 +2,7 @@
// SectionBy.swift // SectionBy.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -31,17 +31,17 @@ import CoreData
/** /**
The `SectionBy` clause indicates the key path to use to group the `ListMonitor` objects into sections. An optional closure can also be provided to transform the value into an appropriate section name: The `SectionBy` clause indicates the key path to use to group the `ListMonitor` objects into sections. An optional closure can also be provided to transform the value into an appropriate section name:
```
let monitor = CoreStore.monitorSectionedList( let monitor = CoreStore.monitorSectionedList(
From(MyPersonEntity), From(MyPersonEntity),
SectionBy("age") { "Age \($0)" }, SectionBy("age") { "Age \($0)" },
OrderBy(.Ascending("lastName")) OrderBy(.Ascending("lastName"))
) )
```
*/ */
@available(OSX, unavailable)
public struct SectionBy { public struct SectionBy {
// MARK: Public
/** /**
Initializes a `SectionBy` clause with the key path to use to group `ListMonitor` objects into sections Initializes a `SectionBy` clause with the key path to use to group `ListMonitor` objects into sections
@@ -0,0 +1,198 @@
//
// UnsafeDataTransaction+Observing.swift
// CoreStore
//
// Copyright © 2016 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
#if USE_FRAMEWORKS
import GCDKit
#endif
// MARK: - UnsafeDataTransaction
@available(OSX, unavailable)
public extension UnsafeDataTransaction {
/**
Creates a `ObjectMonitor` for the specified `NSManagedObject`. Multiple `ObjectObserver`s may then register themselves to be notified when changes are made to the `NSManagedObject`.
- parameter object: the `NSManagedObject` to observe changes from
- returns: a `ObjectMonitor` that monitors changes to `object`
*/
@warn_unused_result
public func monitorObject<T: NSManagedObject>(object: T) -> ObjectMonitor<T> {
return ObjectMonitor(
unsafeTransaction: self,
object: object
)
}
/**
Creates a `ListMonitor` for a list of `NSManagedObject`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
*/
@warn_unused_result
public func monitorList<T: NSManagedObject>(from: From<T>, _ fetchClauses: FetchClause...) -> ListMonitor<T> {
return self.monitorList(from, fetchClauses)
}
/**
Creates a `ListMonitor` for a list of `NSManagedObject`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
*/
@warn_unused_result
public func monitorList<T: NSManagedObject>(from: From<T>, _ fetchClauses: [FetchClause]) -> ListMonitor<T> {
CoreStore.assert(
fetchClauses.filter { $0 is OrderBy }.count > 0,
"A ListMonitor requires an OrderBy clause."
)
return ListMonitor(
unsafeTransaction: self,
from: from,
sectionBy: nil,
fetchClauses: fetchClauses
)
}
/**
Asynchronously creates a `ListMonitor` for a list of `NSManagedObject`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 func monitorList<T: NSManagedObject>(createAsynchronously createAsynchronously: (ListMonitor<T>) -> Void, _ from: From<T>, _ fetchClauses: FetchClause...) {
self.monitorList(createAsynchronously: createAsynchronously, from, fetchClauses)
}
/**
Asynchronously creates a `ListMonitor` for a list of `NSManagedObject`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 func monitorList<T: NSManagedObject>(createAsynchronously createAsynchronously: (ListMonitor<T>) -> Void, _ from: From<T>, _ fetchClauses: [FetchClause]) {
CoreStore.assert(
fetchClauses.filter { $0 is OrderBy }.count > 0,
"A ListMonitor requires an OrderBy clause."
)
_ = ListMonitor(
unsafeTransaction: self,
from: from,
sectionBy: nil,
fetchClauses: fetchClauses,
createAsynchronously: createAsynchronously
)
}
/**
Creates a `ListMonitor` for a sectioned list of `NSManagedObject`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
*/
@warn_unused_result
public func monitorSectionedList<T: NSManagedObject>(from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) -> ListMonitor<T> {
return self.monitorSectionedList(from, sectionBy, fetchClauses)
}
/**
Creates a `ListMonitor` for a sectioned list of `NSManagedObject`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
*/
@warn_unused_result
public func monitorSectionedList<T: NSManagedObject>(from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) -> ListMonitor<T> {
CoreStore.assert(
fetchClauses.filter { $0 is OrderBy }.count > 0,
"A ListMonitor requires an OrderBy clause."
)
return ListMonitor(
unsafeTransaction: self,
from: from,
sectionBy: sectionBy,
fetchClauses: fetchClauses
)
}
/**
Asynchronously creates a `ListMonitor` for a sectioned list of `NSManagedObject`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 func monitorSectionedList<T: NSManagedObject>(createAsynchronously createAsynchronously: (ListMonitor<T>) -> Void, _ from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: FetchClause...) {
self.monitorSectionedList(createAsynchronously: createAsynchronously, from, sectionBy, fetchClauses)
}
/**
Asynchronously creates a `ListMonitor` for a sectioned list of `NSManagedObject`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 func monitorSectionedList<T: NSManagedObject>(createAsynchronously createAsynchronously: (ListMonitor<T>) -> Void, _ from: From<T>, _ sectionBy: SectionBy, _ fetchClauses: [FetchClause]) {
CoreStore.assert(
fetchClauses.filter { $0 is OrderBy }.count > 0,
"A ListMonitor requires an OrderBy clause."
)
_ = ListMonitor(
unsafeTransaction: self,
from: from,
sectionBy: sectionBy,
fetchClauses: fetchClauses,
createAsynchronously: createAsynchronously
)
}
}
@@ -2,7 +2,7 @@
// AsynchronousDataTransaction.swift // AsynchronousDataTransaction.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -25,7 +25,9 @@
import Foundation import Foundation
import CoreData import CoreData
#if USE_FRAMEWORKS
import GCDKit import GCDKit
#endif
// MARK: - AsynchronousDataTransaction // MARK: - AsynchronousDataTransaction
@@ -35,8 +37,6 @@ The `AsynchronousDataTransaction` provides an interface for `NSManagedObject` cr
*/ */
public final class AsynchronousDataTransaction: BaseDataTransaction { public final class AsynchronousDataTransaction: BaseDataTransaction {
// MARK: Public
/** /**
Saves the transaction changes. This method should not be used after the `commit()` method was already called once. Saves the transaction changes. This method should not be used after the `commit()` method was already called once.
@@ -54,14 +54,15 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
) )
self.isCommitted = true self.isCommitted = true
let semaphore = GCDSemaphore(0) let group = GCDGroup()
group.enter()
self.context.saveAsynchronouslyWithCompletion { (result) -> Void in self.context.saveAsynchronouslyWithCompletion { (result) -> Void in
self.result = result self.result = result
completion(result: result) completion(result: result)
semaphore.signal() group.leave()
} }
semaphore.wait() group.wait()
} }
/** /**
@@ -112,6 +113,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
- parameter object: the `NSManagedObject` type to be edited - parameter object: the `NSManagedObject` type to be edited
- returns: an editable proxy for the specified `NSManagedObject`. - returns: an editable proxy for the specified `NSManagedObject`.
*/ */
@warn_unused_result
public override func edit<T: NSManagedObject>(object: T?) -> T? { public override func edit<T: NSManagedObject>(object: T?) -> T? {
CoreStore.assert( CoreStore.assert(
@@ -129,6 +131,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
- 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`.
*/ */
@warn_unused_result
public override func edit<T: NSManagedObject>(into: Into<T>, _ objectID: NSManagedObjectID) -> T? { public override func edit<T: NSManagedObject>(into: Into<T>, _ objectID: NSManagedObjectID) -> T? {
CoreStore.assert( CoreStore.assert(
@@ -168,7 +171,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
"Attempted to delete an entities from an already committed \(typeName(self))." "Attempted to delete an entities from an already committed \(typeName(self))."
) )
super.delete([object1, object2] + objects) super.delete(([object1, object2] + objects).flatMap { $0 })
} }
/** /**
@@ -176,7 +179,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
- parameter objects: the `NSManagedObject`s type to be deleted - parameter objects: the `NSManagedObject`s type to be deleted
*/ */
public override func delete(objects: [NSManagedObject?]) { public override func delete<S: SequenceType where S.Generator.Element: NSManagedObject>(objects: S) {
CoreStore.assert( CoreStore.assert(
!self.isCommitted, !self.isCommitted,
@@ -189,14 +192,19 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
/** /**
Rolls back the transaction by resetting the `NSManagedObjectContext`. After calling this method, all `NSManagedObjects` fetched within the transaction will become invalid. This method should not be used after the `commit()` method was already called once. Rolls back the transaction by resetting the `NSManagedObjectContext`. After calling this method, all `NSManagedObjects` fetched within the transaction will become invalid. This method should not be used after the `commit()` method was already called once.
*/ */
public override func rollback() { @available(*, deprecated=1.3.4, message="Resetting the context is inherently unsafe. This method will be removed in the near future. Use `beginUnsafe()` to create transactions with `undo` support.")
public func rollback() {
CoreStore.assert( CoreStore.assert(
!self.isCommitted, !self.isCommitted,
"Attempted to rollback an already committed \(typeName(self))." "Attempted to rollback an already committed \(typeName(self))."
) )
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to rollback a \(typeName(self)) outside its designated queue."
)
super.rollback() self.context.reset()
} }
@@ -206,7 +214,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
self.closure = closure self.closure = closure
super.init(mainContext: mainContext, queue: queue) super.init(mainContext: mainContext, queue: queue, supportsUndo: false, bypassesQueueing: false)
} }
internal func perform() { internal func perform() {
@@ -2,7 +2,7 @@
// BaseDataTransaction.swift // BaseDataTransaction.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2014 John Rommel Estropia // Copyright © 2014 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -25,7 +25,9 @@
import Foundation import Foundation
import CoreData import CoreData
#if USE_FRAMEWORKS
import GCDKit import GCDKit
#endif
// MARK: - BaseDataTransaction // MARK: - BaseDataTransaction
@@ -54,7 +56,7 @@ public /*abstract*/ class BaseDataTransaction {
public func create<T: NSManagedObject>(into: Into<T>) -> T { public func create<T: NSManagedObject>(into: Into<T>) -> T {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to create an entity of type \(typeName(T)) outside its designated queue." "Attempted to create an entity of type \(typeName(T)) outside its designated queue."
) )
@@ -104,14 +106,18 @@ public /*abstract*/ class BaseDataTransaction {
- parameter object: the `NSManagedObject` type to be edited - parameter object: the `NSManagedObject` type to be edited
- returns: an editable proxy for the specified `NSManagedObject`. - returns: an editable proxy for the specified `NSManagedObject`.
*/ */
@warn_unused_result
public func edit<T: NSManagedObject>(object: T?) -> T? { public func edit<T: NSManagedObject>(object: T?) -> T? {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to update an entity of type \(typeName(object)) outside its designated queue." "Attempted to update an entity of type \(typeName(object)) outside its designated queue."
) )
guard let object = object else {
return object?.inContext(self.context) return nil
}
return self.context.fetchExisting(object)
} }
/** /**
@@ -121,10 +127,11 @@ public /*abstract*/ class BaseDataTransaction {
- 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`.
*/ */
@warn_unused_result
public func edit<T: NSManagedObject>(into: Into<T>, _ objectID: NSManagedObjectID) -> T? { public func edit<T: NSManagedObject>(into: Into<T>, _ objectID: NSManagedObjectID) -> T? {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to update an entity of type \(typeName(T)) outside its designated queue." "Attempted to update an entity of type \(typeName(T)) outside its designated queue."
) )
CoreStore.assert( CoreStore.assert(
@@ -132,8 +139,7 @@ public /*abstract*/ class BaseDataTransaction {
|| (into.configuration ?? Into.defaultConfigurationName) == objectID.persistentStore?.configurationName, || (into.configuration ?? Into.defaultConfigurationName) == objectID.persistentStore?.configurationName,
"Attempted to update an entity of type \(typeName(T)) but the specified persistent store do not match the `NSManagedObjectID`." "Attempted to update an entity of type \(typeName(T)) but the specified persistent store do not match the `NSManagedObjectID`."
) )
return self.fetchExisting(objectID) as? T
return (into.entityClass as! NSManagedObject.Type).inContext(self.context, withObjectID: objectID) as? T
} }
/** /**
@@ -144,11 +150,14 @@ public /*abstract*/ class BaseDataTransaction {
public func delete(object: NSManagedObject?) { public func delete(object: NSManagedObject?) {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), 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 {
object?.inContext(self.context)?.deleteFromContext() return
}
self.context.fetchExisting(object)?.deleteFromContext()
} }
/** /**
@@ -160,7 +169,7 @@ public /*abstract*/ class BaseDataTransaction {
*/ */
public func delete(object1: NSManagedObject?, _ object2: NSManagedObject?, _ objects: NSManagedObject?...) { public func delete(object1: NSManagedObject?, _ object2: NSManagedObject?, _ objects: NSManagedObject?...) {
self.delete([object1, object2] + objects) self.delete(([object1, object2] + objects).flatMap { $0 })
} }
/** /**
@@ -168,33 +177,266 @@ public /*abstract*/ class BaseDataTransaction {
- parameter objects: the `NSManagedObject`s to be deleted - parameter objects: the `NSManagedObject`s to be deleted
*/ */
public func delete(objects: [NSManagedObject?]) { public func delete<S: SequenceType where S.Generator.Element: NSManagedObject>(objects: S) {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), 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
for object in objects { objects.forEach { context.fetchExisting($0)?.deleteFromContext() }
object?.inContext(context)?.deleteFromContext()
} }
}
// MARK: Saving changes
/** /**
Rolls back the transaction by resetting the `NSManagedObjectContext`. After calling this method, all `NSManagedObjects` fetched within the transaction will become invalid. Refreshes all registered objects `NSManagedObject`s in the transaction.
*/ */
public func rollback() { public func refreshAndMergeAllObjects() {
CoreStore.assert( CoreStore.assert(
self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext(), self.isRunningInAllowedQueue(),
"Attempted to rollback a \(typeName(self)) outside its designated queue." "Attempted to refresh entities outside their designated queue."
) )
self.context.reset() self.context.refreshAndMergeAllObjects()
}
// 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: a `Set` of pending `NSManagedObject`s that were inserted to the transaction.
*/
public func insertedObjects() -> Set<NSManagedObject> {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to access inserted objects from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access inserted objects from an already committed \(typeName(self))."
)
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.isCurrentExecutionContext(),
"Attempted to access inserted objects from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access inserted objects from an already committed \(typeName(self))."
)
return Set(self.context.insertedObjects.flatMap { $0 as? T })
}
/**
Returns all pending `NSManagedObjectID`s that were inserted to the transaction. This method should not be called after the `commit()` method was called.
- returns: a `Set` of pending `NSManagedObjectID`s that were inserted to the transaction.
*/
public func insertedObjectIDs() -> Set<NSManagedObjectID> {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to access inserted object IDs from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access inserted objects IDs from an already committed \(typeName(self))."
)
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.
- parameter entity: the `NSManagedObject` subclass to filter
- 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> {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to access inserted object IDs from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access inserted objects IDs from an already committed \(typeName(self))."
)
return Set(self.context.insertedObjects.flatMap { $0 as? T }.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: a `Set` of pending `NSManagedObject`s that were updated to the transaction.
*/
public func updatedObjects() -> Set<NSManagedObject> {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to access updated objects from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access updated objects from an already committed \(typeName(self))."
)
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.isCurrentExecutionContext(),
"Attempted to access updated objects from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access updated objects from an already committed \(typeName(self))."
)
return Set(self.context.updatedObjects.flatMap { $0 as? T })
}
/**
Returns all pending `NSManagedObjectID`s that were updated in the transaction. This method should not be called after the `commit()` method was called.
- returns: a `Set` of pending `NSManagedObjectID`s that were updated in the transaction.
*/
public func updatedObjectIDs() -> Set<NSManagedObjectID> {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to access updated object IDs from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access updated object IDs from an already committed \(typeName(self))."
)
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.
- parameter entity: the `NSManagedObject` subclass to filter
- 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> {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to access updated object IDs from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access updated object IDs from an already committed \(typeName(self))."
)
return Set(self.context.updatedObjects.flatMap { $0 as? T }.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: a `Set` of pending `NSManagedObject`s that were deleted from the transaction.
*/
public func deletedObjects() -> Set<NSManagedObject> {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to access deleted objects from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access deleted objects from an already committed \(typeName(self))."
)
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.isCurrentExecutionContext(),
"Attempted to access deleted objects from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access deleted objects from an already committed \(typeName(self))."
)
return Set(self.context.deletedObjects.flatMap { $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.
- parameter entity: the `NSManagedObject` subclass to filter
- returns: a `Set` of pending `NSManagedObjectID`s of the specified type that were deleted from the transaction.
*/
public func deletedObjectIDs() -> Set<NSManagedObjectID> {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to access deleted object IDs from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access deleted object IDs from an already committed \(typeName(self))."
)
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.
- parameter entity: the `NSManagedObject` subclass to filter
- 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> {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to access deleted object IDs from a \(typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to access deleted object IDs from an already committed \(typeName(self))."
)
return Set(self.context.deletedObjects.flatMap { $0 as? T }.map { $0.objectID })
} }
@@ -203,26 +445,38 @@ public /*abstract*/ class BaseDataTransaction {
internal let context: NSManagedObjectContext internal let context: NSManagedObjectContext
internal let transactionQueue: GCDQueue internal let transactionQueue: GCDQueue
internal let childTransactionQueue: GCDQueue = .createSerial("com.corestore.datastack.childtransactionqueue") internal let childTransactionQueue: GCDQueue = .createSerial("com.corestore.datastack.childtransactionqueue")
internal let supportsUndo: Bool
internal let bypassesQueueing: Bool
internal var isCommitted = false internal var isCommitted = false
internal var result: SaveResult? internal var result: SaveResult?
internal init(mainContext: NSManagedObjectContext, queue: GCDQueue) { internal init(mainContext: NSManagedObjectContext, queue: GCDQueue, supportsUndo: Bool, bypassesQueueing: Bool) {
self.transactionQueue = queue
let context = mainContext.temporaryContextInTransactionWithConcurrencyType( let context = mainContext.temporaryContextInTransactionWithConcurrencyType(
queue == .Main queue == .Main
? .MainQueueConcurrencyType ? .MainQueueConcurrencyType
: .PrivateQueueConcurrencyType : .PrivateQueueConcurrencyType
) )
self.transactionQueue = queue
self.context = context self.context = context
self.supportsUndo = supportsUndo
self.bypassesQueueing = bypassesQueueing
context.parentTransaction = self context.parentTransaction = self
if !supportsUndo {
context.undoManager = nil
}
else if context.undoManager == nil {
context.undoManager = NSUndoManager()
}
} }
internal var bypassesQueueing: Bool { internal func isRunningInAllowedQueue() -> Bool {
return false return self.bypassesQueueing || self.transactionQueue.isCurrentExecutionContext()
} }
} }
@@ -2,7 +2,7 @@
// CoreStore+Transaction.swift // CoreStore+Transaction.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -30,8 +30,6 @@ import Foundation
public extension CoreStore { public extension CoreStore {
// MARK: Public
/** /**
Using the `defaultStack`, begins a transaction asynchronously where `NSManagedObject` creates, updates, and deletes can be made. Using the `defaultStack`, begins a transaction asynchronously where `NSManagedObject` creates, updates, and deletes can be made.
@@ -54,12 +52,29 @@ public extension CoreStore {
} }
/** /**
Using the `defaultStack`, begins a non-contiguous transaction where `NSManagedObject` creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms. A detached transaction object should typically be only used from the main queue. Using the `defaultStack`, begins a non-contiguous transaction where `NSManagedObject` creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms. An unsafe transaction object should typically be only used from the main queue.
- returns: a `DetachedDataTransaction` instance where creates, updates, and deletes can be made. - prameter supportsUndo: `undo()`, `redo()`, and `rollback()` methods are only available when this parameter is `true`, otherwise those method will raise an exception. Defaults to `false`. Note that turning on Undo support may heavily impact performance especially on iOS or watchOS where memory is limited.
- returns: a `UnsafeDataTransaction` instance where creates, updates, and deletes can be made.
*/ */
public static func beginDetached() -> DetachedDataTransaction { @warn_unused_result
public static func beginUnsafe(supportsUndo supportsUndo: Bool = false) -> UnsafeDataTransaction {
return self.defaultStack.beginDetached() return self.defaultStack.beginUnsafe(supportsUndo: supportsUndo)
}
/**
Refreshes all registered objects `NSManagedObject`s in the `DataStack`.
*/
public static func refreshAndMergeAllObjects() {
self.defaultStack.refreshAndMergeAllObjects()
}
@available(*, deprecated=1.3.1, renamed="beginUnsafe")
@warn_unused_result
public static func beginDetached() -> UnsafeDataTransaction {
return self.beginUnsafe()
} }
} }
@@ -2,7 +2,7 @@
// DataStack+Transaction.swift // DataStack+Transaction.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -25,15 +25,15 @@
import Foundation import Foundation
import CoreData import CoreData
#if USE_FRAMEWORKS
import GCDKit import GCDKit
#endif
// MARK: - DataStack // MARK: - DataStack
public extension DataStack { public extension DataStack {
// MARK: Public
/** /**
Begins a transaction asynchronously where `NSManagedObject` creates, updates, and deletes can be made. Begins a transaction asynchronously where `NSManagedObject` creates, updates, and deletes can be made.
@@ -64,16 +64,39 @@ public extension DataStack {
/** /**
Begins a non-contiguous transaction where `NSManagedObject` creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms. Begins a non-contiguous transaction where `NSManagedObject` creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms.
- returns: a `DetachedDataTransaction` instance where creates, updates, and deletes can be made. - prameter supportsUndo: `undo()`, `redo()`, and `rollback()` methods are only available when this parameter is `true`, otherwise those method will raise an exception. Defaults to `false`. Note that turning on Undo support may heavily impact performance especially on iOS or watchOS where memory is limited.
- returns: a `UnsafeDataTransaction` instance where creates, updates, and deletes can be made.
*/ */
public func beginDetached() -> DetachedDataTransaction { @warn_unused_result
public func beginUnsafe(supportsUndo supportsUndo: Bool = false) -> UnsafeDataTransaction {
return DetachedDataTransaction( return UnsafeDataTransaction(
mainContext: self.rootSavingContext, mainContext: self.rootSavingContext,
queue: .createSerial( queue: .createSerial(
"com.coreStore.dataStack.detachedTransactionQueue", "com.coreStore.dataStack.unsafeTransactionQueue",
targetQueue: .UserInitiated targetQueue: .UserInitiated
) ),
supportsUndo: supportsUndo
) )
} }
/**
Refreshes all registered objects `NSManagedObject`s in the `DataStack`.
*/
public func refreshAndMergeAllObjects() {
CoreStore.assert(
NSThread.isMainThread(),
"Attempted to refresh entities outside their designated queue."
)
self.mainContext.refreshAndMergeAllObjects()
}
@available(*, deprecated=1.3.1, renamed="beginUnsafe")
@warn_unused_result
public func beginDetached() -> UnsafeDataTransaction {
return self.beginUnsafe()
}
} }
@@ -1,86 +0,0 @@
//
// DetachedDataTransaction.swift
// CoreStore
//
// Copyright (c) 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 GCDKit
// MARK: - DetachedDataTransaction
/**
The `DetachedDataTransaction` provides an interface for non-contiguous `NSManagedObject` creates, updates, and deletes. This is useful for making temporary changes, such as partially filled forms. A detached transaction object should typically be only used from the main queue.
*/
public final class DetachedDataTransaction: BaseDataTransaction {
// MARK: Public
/**
Saves the transaction changes asynchronously. For a `DetachedDataTransaction`, 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 `SaveResult` argument of the block.
*/
public func commit(completion: (result: SaveResult) -> Void) {
self.context.saveAsynchronouslyWithCompletion { (result) -> Void in
self.result = result
completion(result: result)
}
}
/**
Begins a child transaction where `NSManagedObject` creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms.
- returns: a `DetachedDataTransaction` instance where creates, updates, and deletes can be made.
*/
public func beginDetached() -> DetachedDataTransaction {
return DetachedDataTransaction(
mainContext: self.context,
queue: self.transactionQueue
)
}
/**
Returns the `NSManagedObjectContext` for this detached transaction. Use only for cases where external frameworks need an `NSManagedObjectContext` instance to work with.
Note that it is the developer's responsibility to ensure the following:
- that the `DetachedDataTransaction` that owns this context should be strongly referenced and prevented from being deallocated during the context's lifetime
- that all saves will be done either through the `DetachedDataTransaction`'s `commit(...)` method, or by calling `save()` manually on the context, its parent, and all other ancestor contexts if there are any.
*/
public var internalContext: NSManagedObjectContext {
return self.context
}
// MARK: Internal
internal override var bypassesQueueing: Bool {
return true
}
}
+20 -19
View File
@@ -2,7 +2,7 @@
// Into.swift // Into.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2014 John Rommel Estropia // Copyright © 2014 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -30,25 +30,23 @@ import CoreData
// MARK: - Into // MARK: - Into
/** /**
A `Into` clause contains the destination entity and destination persistent store for a `create(...)` method. A common usage is to just indicate the entity: An `Into` clause contains the destination entity and destination persistent store for a `create(...)` method. A common usage is to just indicate the entity:
```
let person = transaction.create(Into(MyPersonEntity)) let person = transaction.create(Into(MyPersonEntity))
```
For cases where multiple `NSPersistentStore`s contain the same entity, the destination configuration's name needs to be specified as well: For cases where multiple `NSPersistentStore`s contain the same entity, the destination configuration's name needs to be specified as well:
```
let person = transaction.create(Into<MyPersonEntity>("Configuration1")) let person = transaction.create(Into<MyPersonEntity>("Configuration1"))
```
This helps the `NSManagedObjectContext` to determine which
*/ */
public struct Into<T: NSManagedObject> { public struct Into<T: NSManagedObject> {
// MARK: Public
/** /**
Initializes an `Into` clause. Initializes an `Into` clause.
Sample Usage: Sample Usage:
```
let person = transaction.create(Into<MyPersonEntity>()) let person = transaction.create(Into<MyPersonEntity>())
```
*/ */
public init(){ public init(){
@@ -60,9 +58,9 @@ public struct Into<T: NSManagedObject> {
/** /**
Initializes an `Into` clause with the specified entity type. Initializes an `Into` clause with the specified entity type.
Sample Usage: Sample Usage:
```
let person = transaction.create(Into(MyPersonEntity)) let person = transaction.create(Into(MyPersonEntity))
```
- parameter entity: the `NSManagedObject` type to be created - parameter entity: the `NSManagedObject` type to be created
*/ */
public init(_ entity: T.Type) { public init(_ entity: T.Type) {
@@ -74,7 +72,10 @@ public struct Into<T: NSManagedObject> {
/** /**
Initializes an `Into` clause with the specified entity class. Initializes an `Into` clause with the specified entity class.
Sample Usage:
```
let person = transaction.create(Into(MyPersonEntity))
```
- parameter entityClass: the `NSManagedObject` class type to be created - parameter entityClass: the `NSManagedObject` class type to be created
*/ */
public init(_ entityClass: AnyClass) { public init(_ entityClass: AnyClass) {
@@ -87,9 +88,9 @@ public struct Into<T: NSManagedObject> {
/** /**
Initializes an `Into` clause with the specified configuration. Initializes an `Into` clause with the specified configuration.
Sample Usage: Sample Usage:
```
let person = transaction.create(Into<MyPersonEntity>("Configuration1")) let person = transaction.create(Into<MyPersonEntity>("Configuration1"))
```
- 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.
*/ */
public init(_ configuration: String?) { public init(_ configuration: String?) {
@@ -102,9 +103,9 @@ public struct Into<T: NSManagedObject> {
/** /**
Initializes an `Into` clause with the specified entity type and configuration. Initializes an `Into` clause with the specified entity type and configuration.
Sample Usage: Sample Usage:
```
let person = transaction.create(Into(MyPersonEntity.self, "Configuration1")) let person = transaction.create(Into(MyPersonEntity.self, "Configuration1"))
```
- parameter entity: the `NSManagedObject` type to be created - parameter entity: the `NSManagedObject` type to be created
- 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.
*/ */
@@ -118,9 +119,9 @@ public struct Into<T: NSManagedObject> {
/** /**
Initializes an `Into` clause with the specified entity class and configuration. Initializes an `Into` clause with the specified entity class and configuration.
Sample Usage: Sample Usage:
```
let person = transaction.create(Into(MyPersonEntity.self, "Configuration1")) let person = transaction.create(Into(MyPersonEntity.self, "Configuration1"))
```
- parameter entityClass: the `NSManagedObject` class type to be created - parameter entityClass: the `NSManagedObject` class type to be created
- 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.
*/ */
@@ -0,0 +1,61 @@
//
// NSManagedObject+Transaction.swift
// CoreStore
//
// Copyright © 2016 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: - NSManagedObject
public extension NSManagedObject {
/**
Returns this object's parent `UnsafeDataTransaction` instance if it was created from one. Returns `nil` if the parent transaction is either an `AsynchronousDataTransaction` or a `SynchronousDataTransaction`, or if the object is not managed by CoreStore.
When using an `UnsafeDataTransaction` and passing around a temporary object, you can use this property to execute fetches and updates to the transaction without having to pass around both the object and the transaction instances.
Note that the internal reference to the transaction is `weak`, and it is still the developer's responsibility to retain a strong reference to the `UnsafeDataTransaction`.
*/
public var unsafeDataTransaction: UnsafeDataTransaction? {
return self.managedObjectContext?.parentTransaction as? UnsafeDataTransaction
}
// MARK: Internal
internal dynamic class func createInContext(context: NSManagedObjectContext) -> Self {
return self.init(
entity: context.entityDescriptionForEntityType(self)!,
insertIntoManagedObjectContext: context
)
}
internal func deleteFromContext() {
self.managedObjectContext?.deleteObject(self)
}
}
@@ -2,7 +2,7 @@
// SaveResult.swift // SaveResult.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2014 John Rommel Estropia // Copyright © 2014 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -31,7 +31,7 @@ import Foundation
/** /**
The `SaveResult` indicates the result of a `commit(...)` for a transaction. The `SaveResult` indicates the result of a `commit(...)` for a transaction.
The `SaveResult` can be treated as a boolean: The `SaveResult` can be treated as a boolean:
```
CoreStore.beginAsynchronous { transaction in CoreStore.beginAsynchronous { transaction in
// ... // ...
let result = transaction.commit() let result = transaction.commit()
@@ -42,9 +42,9 @@ The `SaveResult` can be treated as a boolean:
// failed // failed
} }
} }
```
or as an `enum`, where the resulting associated object can also be inspected: or as an `enum`, where the resulting associated object can also be inspected:
```
CoreStore.beginAsynchronous { transaction in CoreStore.beginAsynchronous { transaction in
// ... // ...
let result = transaction.commit() let result = transaction.commit()
@@ -59,8 +59,6 @@ or as an `enum`, where the resulting associated object can also be inspected:
*/ */
public enum SaveResult { public enum SaveResult {
// MARK: Public
/** /**
`SaveResult.Success` indicates that the `commit()` for the transaction succeeded, either because the save succeeded or because there were no changes to save. The associated value `hasChanges` indicates if there were saved changes or not. `SaveResult.Success` indicates that the `commit()` for the transaction succeeded, either because the save succeeded or because there were no changes to save. The associated value `hasChanges` indicates if there were saved changes or not.
*/ */
@@ -103,6 +101,7 @@ extension SaveResult: BooleanType {
public var boolValue: Bool { public var boolValue: Bool {
switch self { switch self {
case .Success: return true case .Success: return true
case .Failure: return false case .Failure: return false
} }
@@ -2,7 +2,7 @@
// SynchronousDataTransaction.swift // SynchronousDataTransaction.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -25,7 +25,9 @@
import Foundation import Foundation
import CoreData import CoreData
#if USE_FRAMEWORKS
import GCDKit import GCDKit
#endif
// MARK: - SynchronousDataTransaction // MARK: - SynchronousDataTransaction
@@ -35,12 +37,12 @@ The `SynchronousDataTransaction` provides an interface for `NSManagedObject` cre
*/ */
public final class SynchronousDataTransaction: BaseDataTransaction { public final class SynchronousDataTransaction: BaseDataTransaction {
// MARK: Public
/** /**
Saves the transaction changes and waits for completion synchronously. This method should not be used after the `commit()` method was already called once. Saves the transaction changes and waits for completion synchronously. This method should not be used after the `commit()` method was already called once.
- returns: a `SaveResult` containing the success or failure information
*/ */
public func commit() { public func commitAndWait() -> SaveResult {
CoreStore.assert( CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(), self.transactionQueue.isCurrentExecutionContext(),
@@ -52,7 +54,10 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
) )
self.isCommitted = true self.isCommitted = true
self.result = self.context.saveSynchronously()
let result = self.context.saveSynchronously()
self.result = result
return result
} }
/** /**
@@ -103,6 +108,7 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
- parameter object: the `NSManagedObject` type to be edited - parameter object: the `NSManagedObject` type to be edited
- returns: an editable proxy for the specified `NSManagedObject`. - returns: an editable proxy for the specified `NSManagedObject`.
*/ */
@warn_unused_result
public override func edit<T: NSManagedObject>(object: T?) -> T? { public override func edit<T: NSManagedObject>(object: T?) -> T? {
CoreStore.assert( CoreStore.assert(
@@ -120,6 +126,7 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
- 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`.
*/ */
@warn_unused_result
public override func edit<T: NSManagedObject>(into: Into<T>, _ objectID: NSManagedObjectID) -> T? { public override func edit<T: NSManagedObject>(into: Into<T>, _ objectID: NSManagedObjectID) -> T? {
CoreStore.assert( CoreStore.assert(
@@ -159,7 +166,7 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
"Attempted to delete an entities from an already committed \(typeName(self))." "Attempted to delete an entities from an already committed \(typeName(self))."
) )
super.delete([object1, object2] + objects) super.delete(([object1, object2] + objects).flatMap { $0 })
} }
/** /**
@@ -167,7 +174,7 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
- parameter objects: the `NSManagedObject`s to be deleted - parameter objects: the `NSManagedObject`s to be deleted
*/ */
public override func delete(objects: [NSManagedObject?]) { public override func delete<S: SequenceType where S.Generator.Element: NSManagedObject>(objects: S) {
CoreStore.assert( CoreStore.assert(
!self.isCommitted, !self.isCommitted,
@@ -180,19 +187,37 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
/** /**
Rolls back the transaction by resetting the `NSManagedObjectContext`. After calling this method, all `NSManagedObjects` fetched within the transaction will become invalid. This method should not be used after the `commit()` method was already called once. Rolls back the transaction by resetting the `NSManagedObjectContext`. After calling this method, all `NSManagedObjects` fetched within the transaction will become invalid. This method should not be used after the `commit()` method was already called once.
*/ */
public override func rollback() { @available(*, deprecated=1.3.4, message="Resetting the context is inherently unsafe. This method will be removed in the near future. Use `beginUnsafe()` to create transactions with `undo` support.")
public func rollback() {
CoreStore.assert( CoreStore.assert(
!self.isCommitted, !self.isCommitted,
"Attempted to rollback an already committed \(typeName(self))." "Attempted to rollback an already committed \(typeName(self))."
) )
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to rollback a \(typeName(self)) outside its designated queue."
)
super.rollback() self.context.reset()
}
@available(*, deprecated=1.5.2, renamed="commitAndWait")
public func commit() {
self.commitAndWait()
} }
// MARK: Internal // MARK: Internal
internal init(mainContext: NSManagedObjectContext, queue: GCDQueue, closure: (transaction: SynchronousDataTransaction) -> Void) {
self.closure = closure
super.init(mainContext: mainContext, queue: queue, supportsUndo: false, bypassesQueueing: false)
}
internal func performAndWait() -> SaveResult? { internal func performAndWait() -> SaveResult? {
self.transactionQueue.sync { self.transactionQueue.sync {
@@ -210,13 +235,6 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
return self.result return self.result
} }
internal init(mainContext: NSManagedObjectContext, queue: GCDQueue, closure: (transaction: SynchronousDataTransaction) -> Void) {
self.closure = closure
super.init(mainContext: mainContext, queue: queue)
}
// MARK: Private // MARK: Private
@@ -0,0 +1,172 @@
//
// UnsafeDataTransaction.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
#if USE_FRAMEWORKS
import GCDKit
#endif
@available(*, deprecated=1.3.1, renamed="UnsafeDataTransaction")
public typealias DetachedDataTransaction = UnsafeDataTransaction
// MARK: - UnsafeDataTransaction
/**
The `UnsafeDataTransaction` provides an interface for non-contiguous `NSManagedObject` creates, updates, and deletes. This is useful for making temporary changes, such as partially filled forms. An unsafe transaction object should typically be only used from the main queue.
*/
public final class UnsafeDataTransaction: BaseDataTransaction {
/**
Saves the transaction changes asynchronously. For a `UnsafeDataTransaction`, 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 `SaveResult` argument of the block.
*/
public func commit(completion: (result: SaveResult) -> Void) {
self.context.saveAsynchronouslyWithCompletion { (result) -> Void in
self.result = result
completion(result: result)
}
}
/**
Saves the transaction changes and waits for completion synchronously. For a `UnsafeDataTransaction`, multiple commits are allowed, although it is the developer's responsibility to ensure a reasonable leeway to prevent blocking the main thread.
- returns: a `SaveResult` containing the success or failure information
*/
public func commitAndWait() -> SaveResult {
let result = self.context.saveSynchronously()
self.result = result
return result
}
/**
Rolls back the transaction.
*/
public func rollback() {
CoreStore.assert(
self.supportsUndo,
"Attempted to rollback a \(typeName(self)) with Undo support disabled."
)
self.context.rollback()
}
/**
Undo's the last change made to the transaction.
*/
public func undo() {
CoreStore.assert(
self.supportsUndo,
"Attempted to undo a \(typeName(self)) with Undo support disabled."
)
self.context.undo()
}
/**
Immediately flushes all pending changes to the transaction's observers. 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()`)
- throws: an error thrown from `closure`, or an error thrown by Core Data (usually validation errors or conflict errors)
*/
public func flush() throws {
try self.context.save()
}
/**
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()`)
- parameter closure: the closure where changes can be made prior to the flush
- throws: an error thrown from `closure`, or an error thrown by Core Data (usually validation errors or conflict errors)
*/
public func flush(@noescape closure: () throws -> Void) throws {
try closure()
try self.context.save()
}
/**
Redo's the last undone change to the transaction.
*/
public func redo() {
CoreStore.assert(
self.supportsUndo,
"Attempted to redo a \(typeName(self)) with Undo support disabled."
)
self.context.redo()
}
/**
Begins a child transaction where `NSManagedObject` creates, updates, and deletes can be made. This is useful for making temporary changes, such as partially filled forms.
- prameter supportsUndo: `undo()`, `redo()`, and `rollback()` methods are only available when this parameter is `true`, otherwise those method will raise an exception. Defaults to `false`. Note that turning on Undo support may heavily impact performance especially on iOS or watchOS where memory is limited.
- returns: a `UnsafeDataTransaction` instance where creates, updates, and deletes can be made.
*/
@warn_unused_result
public func beginUnsafe(supportsUndo supportsUndo: Bool = false) -> UnsafeDataTransaction {
return UnsafeDataTransaction(
mainContext: self.context,
queue: self.transactionQueue,
supportsUndo: supportsUndo
)
}
/**
Returns the `NSManagedObjectContext` for this unsafe transaction. Use only for cases where external frameworks need an `NSManagedObjectContext` instance to work with.
- Important: Note that it is the developer's responsibility to ensure the following:
- that the `UnsafeDataTransaction` that owns this context should be strongly referenced and prevented from being deallocated during the context's lifetime
- that all saves will be done either through the `UnsafeDataTransaction`'s `commit(...)` method, or by calling `save()` manually on the context, its parent, and all other ancestor contexts if there are any.
*/
public var internalContext: NSManagedObjectContext {
return self.context
}
@available(*, deprecated=1.3.1, renamed="beginUnsafe")
@warn_unused_result
public func beginDetached() -> UnsafeDataTransaction {
return self.beginUnsafe()
}
// MARK: Internal
internal init(mainContext: NSManagedObjectContext, queue: GCDQueue, supportsUndo: Bool) {
super.init(mainContext: mainContext, queue: queue, supportsUndo: supportsUndo, bypassesQueueing: true)
}
}
+15 -5
View File
@@ -2,7 +2,7 @@
// CoreStore+Setup.swift // CoreStore+Setup.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2015 John Rommel Estropia // Copyright © 2015 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -25,7 +25,9 @@
import Foundation import Foundation
import CoreData import CoreData
#if USE_FRAMEWORKS
import GCDKit import GCDKit
#endif
// MARK: - CoreStore // MARK: - CoreStore
@@ -43,9 +45,17 @@ public extension CoreStore {
/** /**
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.
*/ */
public static var entitiesByName: [String: NSManagedObject.Type] { public static var entityTypesByName: [String: NSManagedObject.Type] {
return self.defaultStack.entitiesByName return self.defaultStack.entityTypesByName
}
/**
Returns the `NSEntityDescription` for the specified `NSManagedObject` subclass from `defaultStack`'s model.
*/
public static func entityDescriptionForType(type: NSManagedObject.Type) -> NSEntityDescription? {
return self.defaultStack.entityDescriptionForType(type)
} }
/** /**
@@ -62,7 +72,7 @@ public extension CoreStore {
/** /**
Adds to the `defaultStack` an SQLite store from the given SQLite file name. Adds to the `defaultStack` an SQLite store from the given SQLite file name.
- parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory. A new SQLite file will be created if it does not exist. - parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory (or the "Caches" directory on tvOS). A new SQLite file will be created if it does not exist.
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to nil. - parameter configuration: an optional configuration name from the model file. If not specified, defaults to nil.
- parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false - parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false
- returns: the `NSPersistentStore` added to the stack. - returns: the `NSPersistentStore` added to the stack.
@@ -79,7 +89,7 @@ public extension CoreStore {
/** /**
Adds to the `defaultStack` an SQLite store from the given SQLite file URL. Adds to the `defaultStack` an SQLite store from the given SQLite file URL.
- parameter fileURL: the local file URL for the SQLite persistent store. A new SQLite file will be created if it does not exist. If not specified, defaults to a file URL pointing to a "<Application name>.sqlite" file in the "Application Support" directory. - parameter fileURL: the local file URL for the SQLite persistent store. A new SQLite file will be created if it does not exist. If not specified, defaults to a file URL pointing to a "<Application name>.sqlite" file in the "Application Support" directory (or the "Caches" directory on tvOS).
- parameter configuration: an optional configuration name from the model file. If not specified, defaults to nil. - parameter configuration: an optional configuration name from the model file. If not specified, defaults to nil.
- parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false. - parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false.
- returns: the `NSPersistentStore` added to the stack. - returns: the `NSPersistentStore` added to the stack.
+71 -33
View File
@@ -2,7 +2,7 @@
// DataStack.swift // DataStack.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2014 John Rommel Estropia // Copyright © 2014 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -25,14 +25,22 @@
import Foundation import Foundation
import CoreData import CoreData
#if USE_FRAMEWORKS
import GCDKit import GCDKit
#endif
internal let applicationSupportDirectory = NSFileManager.defaultManager().URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask).first! #if os(tvOS)
internal let deviceDirectorySearchPath = NSSearchPathDirectory.CachesDirectory
#else
internal let deviceDirectorySearchPath = NSSearchPathDirectory.ApplicationSupportDirectory
#endif
internal let defaultDirectory = NSFileManager.defaultManager().URLsForDirectory(deviceDirectorySearchPath, inDomains: .UserDomainMask).first!
internal let applicationName = (NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName") as? String) ?? "CoreData" internal let applicationName = (NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName") as? String) ?? "CoreData"
internal let defaultSQLiteStoreURL = applicationSupportDirectory.URLByAppendingPathComponent(applicationName, isDirectory: false).URLByAppendingPathExtension("sqlite") internal let defaultSQLiteStoreURL = defaultDirectory.URLByAppendingPathComponent(applicationName, isDirectory: false).URLByAppendingPathExtension("sqlite")
// MARK: - DataStack // MARK: - DataStack
@@ -42,14 +50,12 @@ The `DataStack` encapsulates the data model for the Core Data stack. Each `DataS
*/ */
public final class DataStack { public final class DataStack {
// MARK: Public
/** /**
Initializes a `DataStack` from an `NSManagedObjectModel`. Initializes a `DataStack` from an `NSManagedObjectModel`.
- parameter modelName: the name of the (.xcdatamodeld) model file. If not specified, the application name will be used. - parameter modelName: the name of the (.xcdatamodeld) model file. If not specified, the application name will be used.
- 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 migrationChain: the `MigrationChain` that indicates the sequence of model versions to be used as the order for incremental migration. If not specified, will default to a non-migrating data stack. - parameter migrationChain: 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.
*/ */
public required init(modelName: String = applicationName, bundle: NSBundle = NSBundle.mainBundle(), migrationChain: MigrationChain = nil) { public required init(modelName: String = applicationName, bundle: NSBundle = NSBundle.mainBundle(), migrationChain: MigrationChain = nil) {
@@ -84,9 +90,20 @@ public final class DataStack {
/** /**
Returns the entity name-to-class type mapping from the `DataStack`'s model. Returns the entity name-to-class type mapping from the `DataStack`'s model.
*/ */
public var entitiesByName: [String: NSManagedObject.Type] { public var entityTypesByName: [String: NSManagedObject.Type] {
return self.model.entityMapping() return self.model.entityTypesMapping()
}
/**
Returns the `NSEntityDescription` for the specified `NSManagedObject` subclass.
*/
public func entityDescriptionForType(type: NSManagedObject.Type) -> NSEntityDescription? {
return NSEntityDescription.entityForName(
self.model.entityNameForClass(type),
inManagedObjectContext: self.mainContext
)
} }
/** /**
@@ -109,7 +126,7 @@ public final class DataStack {
var store: NSPersistentStore? var store: NSPersistentStore?
var storeError: NSError? var storeError: NSError?
coordinator.performBlockAndWait { coordinator.performSynchronously {
do { do {
@@ -143,7 +160,7 @@ public final class DataStack {
/** /**
Adds to the stack an SQLite store from the given SQLite file name. Adds to the stack an SQLite store from the given SQLite file name.
- parameter fileName: the local filename for the SQLite persistent store in the "Application Support" directory. A new SQLite file will be created if it does not exist. 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" directory (or the "Caches" directory on tvOS). A new SQLite file will be created if it does not exist. 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 resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false - parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false
- returns: the `NSPersistentStore` added to the stack. - returns: the `NSPersistentStore` added to the stack.
@@ -151,7 +168,7 @@ public final class DataStack {
public func addSQLiteStoreAndWait(fileName fileName: String, configuration: String? = nil, resetStoreOnModelMismatch: Bool = false) throws -> NSPersistentStore { public func addSQLiteStoreAndWait(fileName fileName: String, configuration: String? = nil, resetStoreOnModelMismatch: Bool = false) throws -> NSPersistentStore {
return try self.addSQLiteStoreAndWait( return try self.addSQLiteStoreAndWait(
fileURL: applicationSupportDirectory.URLByAppendingPathComponent( fileURL: defaultDirectory.URLByAppendingPathComponent(
fileName, fileName,
isDirectory: false isDirectory: false
), ),
@@ -163,7 +180,7 @@ public final class DataStack {
/** /**
Adds to the stack an SQLite store from the given SQLite file URL. Adds to the stack an SQLite store from the given SQLite file URL.
- parameter fileURL: the local file URL for the SQLite persistent store. A new SQLite file will be created if it does not exist. If not specified, defaults to a file URL pointing to a "<Application name>.sqlite" file in the "Application Support" directory. 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 SQLite persistent store. A new SQLite file will be created if it does not exist. If not specified, defaults to a file URL pointing to a "<Application name>.sqlite" file in the "Application Support" directory (or the "Caches" directory on tvOS). 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 resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false. - parameter resetStoreOnModelMismatch: Set to true to delete the store on model mismatch; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false.
- returns: the `NSPersistentStore` added to the stack. - returns: the `NSPersistentStore` added to the stack.
@@ -178,11 +195,8 @@ public final class DataStack {
let coordinator = self.coordinator; let coordinator = self.coordinator;
if let store = coordinator.persistentStoreForURL(fileURL) { if let store = coordinator.persistentStoreForURL(fileURL) {
if store.type == NSSQLiteStoreType guard store.type == NSSQLiteStoreType
&& store.configurationName == (configuration ?? Into.defaultConfigurationName) { && store.configurationName == (configuration ?? Into.defaultConfigurationName) else {
return store
}
let error = NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL) let error = NSError(coreStoreErrorCode: .DifferentPersistentStoreExistsAtURL)
CoreStore.handleError( CoreStore.handleError(
@@ -193,20 +207,20 @@ public final class DataStack {
throw error throw error
} }
let fileManager = NSFileManager.defaultManager() return store
do { }
try fileManager.createDirectoryAtURL( let fileManager = NSFileManager.defaultManager()
_ = try? fileManager.createDirectoryAtURL(
fileURL.URLByDeletingLastPathComponent!, fileURL.URLByDeletingLastPathComponent!,
withIntermediateDirectories: true, withIntermediateDirectories: true,
attributes: nil attributes: nil
) )
}
catch _ { }
var store: NSPersistentStore? var store: NSPersistentStore?
var storeError: NSError? var storeError: NSError?
coordinator.performBlockAndWait { let options = self.optionsForSQLiteStore()
coordinator.performSynchronously {
do { do {
@@ -214,7 +228,7 @@ public final class DataStack {
NSSQLiteStoreType, NSSQLiteStoreType,
configuration: configuration, configuration: configuration,
URL: fileURL, URL: fileURL,
options: [NSSQLitePragmasOption: ["journal_mode": "WAL"]] options: options
) )
} }
catch { catch {
@@ -235,7 +249,7 @@ public final class DataStack {
fileManager.removeSQLiteStoreAtURL(fileURL) fileManager.removeSQLiteStoreAtURL(fileURL)
var store: NSPersistentStore? var store: NSPersistentStore?
coordinator.performBlockAndWait { coordinator.performSynchronously {
do { do {
@@ -281,11 +295,26 @@ public final class DataStack {
let migrationQueue = NSOperationQueue() let migrationQueue = NSOperationQueue()
migrationQueue.maxConcurrentOperationCount = 1 migrationQueue.maxConcurrentOperationCount = 1
migrationQueue.name = "com.coreStore.migrationOperationQueue" migrationQueue.name = "com.coreStore.migrationOperationQueue"
#if USE_FRAMEWORKS
migrationQueue.qualityOfService = .Utility migrationQueue.qualityOfService = .Utility
migrationQueue.underlyingQueue = dispatch_queue_create("com.coreStore.migrationQueue", DISPATCH_QUEUE_SERIAL) migrationQueue.underlyingQueue = dispatch_queue_create("com.coreStore.migrationQueue", DISPATCH_QUEUE_SERIAL)
#else
if #available(iOS 8.0, *) {
migrationQueue.qualityOfService = .Utility
migrationQueue.underlyingQueue = dispatch_queue_create("com.coreStore.migrationQueue", DISPATCH_QUEUE_SERIAL)
}
#endif
return migrationQueue return migrationQueue
}() }()
internal func optionsForSQLiteStore() -> [String: AnyObject] {
return [NSSQLitePragmasOption: ["journal_mode": "WAL"]]
}
internal func entityNameForEntityClass(entityClass: AnyClass) -> String? { internal func entityNameForEntityClass(entityClass: AnyClass) -> String? {
return self.model.entityNameForClass(entityClass) return self.model.entityNameForClass(entityClass)
@@ -346,11 +375,17 @@ public final class DataStack {
self.configurationStoreMapping[configurationName] = persistentStore self.configurationStoreMapping[configurationName] = persistentStore
for entityDescription in (self.coordinator.managedObjectModel.entitiesForConfiguration(configurationName) ?? []) { for entityDescription in (self.coordinator.managedObjectModel.entitiesForConfiguration(configurationName) ?? []) {
if self.entityConfigurationsMapping[entityDescription.managedObjectClassName] == nil { let managedObjectClassName = entityDescription.managedObjectClassName
CoreStore.assert(
NSClassFromString(managedObjectClassName) != nil,
"The class \(typeName(managedObjectClassName)) for the entity \(typeName(entityDescription.name)) does not exist. Check if the subclass type and module name are properly configured."
)
self.entityConfigurationsMapping[entityDescription.managedObjectClassName] = [] if self.entityConfigurationsMapping[managedObjectClassName] == nil {
self.entityConfigurationsMapping[managedObjectClassName] = []
} }
self.entityConfigurationsMapping[entityDescription.managedObjectClassName]?.insert(configurationName) self.entityConfigurationsMapping[managedObjectClassName]?.insert(configurationName)
} }
} }
} }
@@ -364,13 +399,16 @@ public final class DataStack {
deinit { deinit {
for store in self.coordinator.persistentStores { let coordinator = self.coordinator
coordinator.performAsynchronously {
do { withExtendedLifetime(coordinator) { coordinator in
try self.coordinator.removePersistentStore(store) coordinator.persistentStores.forEach {
}
catch _ { } _ = try? coordinator.removePersistentStore($0)
}
}
} }
} }
} }
@@ -2,7 +2,7 @@
// PersistentStoreResult.swift // PersistentStoreResult.swift
// CoreStore // CoreStore
// //
// Copyright (c) 2014 John Rommel Estropia // Copyright © 2014 John Rommel Estropia
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal // of this software and associated documentation files (the "Software"), to deal
@@ -32,7 +32,7 @@ import CoreData
/** /**
The `PersistentStoreResult` indicates the result of an asynchronous initialization of a persistent store. The `PersistentStoreResult` indicates the result of an asynchronous initialization of a persistent store.
The `PersistentStoreResult` can be treated as a boolean: The `PersistentStoreResult` can be treated as a boolean:
```
try! CoreStore.addSQLiteStore(completion: { (result: PersistentStoreResult) -> Void in try! CoreStore.addSQLiteStore(completion: { (result: PersistentStoreResult) -> Void in
if result { if result {
// succeeded // succeeded
@@ -41,9 +41,9 @@ The `PersistentStoreResult` can be treated as a boolean:
// failed // failed
} }
}) })
```
or as an `enum`, where the resulting associated object can also be inspected: or as an `enum`, where the resulting associated object can also be inspected:
```
try! CoreStore.addSQLiteStore(completion: { (result: PersistentStoreResult) -> Void in try! CoreStore.addSQLiteStore(completion: { (result: PersistentStoreResult) -> Void in
switch result { switch result {
case .Success(let persistentStore): case .Success(let persistentStore):
@@ -56,8 +56,6 @@ or as an `enum`, where the resulting associated object can also be inspected:
*/ */
public enum PersistentStoreResult { public enum PersistentStoreResult {
// MARK: Public
/** /**
`PersistentStoreResult.Success` indicates that the persistent store process succeeded. The associated object for this `enum` value is the related `NSPersistentStore` instance. `PersistentStoreResult.Success` indicates that the persistent store process succeeded. The associated object for this `enum` value is the related `NSPersistentStore` instance.
*/ */
@@ -88,9 +86,7 @@ public enum PersistentStoreResult {
internal init(_ errorCode: CoreStoreErrorCode, userInfo: [NSObject: AnyObject]?) { internal init(_ errorCode: CoreStoreErrorCode, userInfo: [NSObject: AnyObject]?) {
self.init(NSError( self.init(NSError(coreStoreErrorCode: errorCode, userInfo: userInfo))
coreStoreErrorCode: errorCode,
userInfo: userInfo))
} }
} }
@@ -99,8 +95,6 @@ public enum PersistentStoreResult {
extension PersistentStoreResult: BooleanType { extension PersistentStoreResult: BooleanType {
// MARK: Public
public var boolValue: Bool { public var boolValue: Bool {
switch self { switch self {
@@ -35,11 +35,11 @@
B569651A1B30888A0075EE4A /* FetchingResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56965191B30888A0075EE4A /* FetchingResultsViewController.swift */; }; B569651A1B30888A0075EE4A /* FetchingResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B56965191B30888A0075EE4A /* FetchingResultsViewController.swift */; };
B569651C1B30889A0075EE4A /* QueryingResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B569651B1B30889A0075EE4A /* QueryingResultsViewController.swift */; }; B569651C1B30889A0075EE4A /* QueryingResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B569651B1B30889A0075EE4A /* QueryingResultsViewController.swift */; };
B56965291B3582D30075EE4A /* MigrationDemo.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B56965271B3582D30075EE4A /* MigrationDemo.xcdatamodeld */; }; B56965291B3582D30075EE4A /* MigrationDemo.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B56965271B3582D30075EE4A /* MigrationDemo.xcdatamodeld */; };
B583A9201AF5F542001F76AF /* CoreStore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B583A91B1AF5F4F4001F76AF /* CoreStore.framework */; };
B583A9211AF5F542001F76AF /* CoreStore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B583A91B1AF5F4F4001F76AF /* CoreStore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
B5D9C9191B20AB1900E64F0E /* GCDKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5D9C9181B20AB1900E64F0E /* GCDKit.framework */; };
B5D9C91A1B20AB1900E64F0E /* GCDKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B5D9C9181B20AB1900E64F0E /* GCDKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
B5E599321B5240F50084BD5F /* OrganismTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E599311B5240F50084BD5F /* OrganismTableViewCell.swift */; }; B5E599321B5240F50084BD5F /* OrganismTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E599311B5240F50084BD5F /* OrganismTableViewCell.swift */; };
B5E89ACD1C52929C003B04A9 /* GCDKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5BDC9241C202429008147CD /* GCDKit.framework */; };
B5E89ACE1C52929C003B04A9 /* GCDKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B5BDC9241C202429008147CD /* GCDKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
B5E89AD01C5292A2003B04A9 /* CoreStore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5BDC9211C202429008147CD /* CoreStore.framework */; };
B5E89AD11C5292A2003B04A9 /* CoreStore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = B5BDC9211C202429008147CD /* CoreStore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
B5EE25851B36E23C0000406B /* OrganismV1.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EE25841B36E23C0000406B /* OrganismV1.swift */; }; B5EE25851B36E23C0000406B /* OrganismV1.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EE25841B36E23C0000406B /* OrganismV1.swift */; };
B5EE25871B36E2520000406B /* OrganismV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EE25861B36E2520000406B /* OrganismV2.swift */; }; B5EE25871B36E2520000406B /* OrganismV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EE25861B36E2520000406B /* OrganismV2.swift */; };
B5EE258C1B36E40D0000406B /* MigrationsDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EE258B1B36E40D0000406B /* MigrationsDemoViewController.swift */; }; B5EE258C1B36E40D0000406B /* MigrationsDemoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EE258B1B36E40D0000406B /* MigrationsDemoViewController.swift */; };
@@ -47,46 +47,15 @@
B5EE259E1B3EC1B20000406B /* OrganismProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EE259D1B3EC1B20000406B /* OrganismProtocol.swift */; }; B5EE259E1B3EC1B20000406B /* OrganismProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EE259D1B3EC1B20000406B /* OrganismProtocol.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
B583A91A1AF5F4F4001F76AF /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B583A9141AF5F4F3001F76AF /* CoreStore.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2F03A53019C5C6DA005002A5;
remoteInfo = CoreStore;
};
B583A91C1AF5F4F4001F76AF /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B583A9141AF5F4F3001F76AF /* CoreStore.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 2F03A53B19C5C6DA005002A5;
remoteInfo = CoreStoreTests;
};
B583A91E1AF5F512001F76AF /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B583A9141AF5F4F3001F76AF /* CoreStore.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 2F03A52F19C5C6DA005002A5;
remoteInfo = CoreStore;
};
B583A9221AF5F542001F76AF /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = B583A9141AF5F4F3001F76AF /* CoreStore.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = 2F03A52F19C5C6DA005002A5;
remoteInfo = CoreStore;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */ /* Begin PBXCopyFilesBuildPhase section */
B583A9241AF5F542001F76AF /* Embed Frameworks */ = { B5E89ACF1C52929C003B04A9 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase; isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
dstPath = ""; dstPath = "";
dstSubfolderSpec = 10; dstSubfolderSpec = 10;
files = ( files = (
B5D9C91A1B20AB1900E64F0E /* GCDKit.framework in Embed Frameworks */, B5E89ACE1C52929C003B04A9 /* GCDKit.framework in Embed Frameworks */,
B583A9211AF5F542001F76AF /* CoreStore.framework in Embed Frameworks */, B5E89AD11C5292A2003B04A9 /* CoreStore.framework in Embed Frameworks */,
); );
name = "Embed Frameworks"; name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@@ -124,8 +93,8 @@
B56965191B30888A0075EE4A /* FetchingResultsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchingResultsViewController.swift; sourceTree = "<group>"; }; B56965191B30888A0075EE4A /* FetchingResultsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchingResultsViewController.swift; sourceTree = "<group>"; };
B569651B1B30889A0075EE4A /* QueryingResultsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryingResultsViewController.swift; sourceTree = "<group>"; }; B569651B1B30889A0075EE4A /* QueryingResultsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryingResultsViewController.swift; sourceTree = "<group>"; };
B56965281B3582D30075EE4A /* MigrationDemo.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MigrationDemo.xcdatamodel; sourceTree = "<group>"; }; B56965281B3582D30075EE4A /* MigrationDemo.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MigrationDemo.xcdatamodel; sourceTree = "<group>"; };
B583A9141AF5F4F3001F76AF /* CoreStore.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = CoreStore.xcodeproj; path = ../CoreStore.xcodeproj; sourceTree = "<group>"; }; B5BDC9211C202429008147CD /* CoreStore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CoreStore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B5D9C9181B20AB1900E64F0E /* GCDKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = GCDKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; B5BDC9241C202429008147CD /* GCDKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = GCDKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B5E599311B5240F50084BD5F /* OrganismTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OrganismTableViewCell.swift; path = "CoreStoreDemo/MIgrations Demo/OrganismTableViewCell.swift"; sourceTree = SOURCE_ROOT; }; B5E599311B5240F50084BD5F /* OrganismTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = OrganismTableViewCell.swift; path = "CoreStoreDemo/MIgrations Demo/OrganismTableViewCell.swift"; sourceTree = SOURCE_ROOT; };
B5EE25801B36E1B00000406B /* MigrationDemoV2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MigrationDemoV2.xcdatamodel; sourceTree = "<group>"; }; B5EE25801B36E1B00000406B /* MigrationDemoV2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MigrationDemoV2.xcdatamodel; sourceTree = "<group>"; };
B5EE25841B36E23C0000406B /* OrganismV1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrganismV1.swift; sourceTree = "<group>"; }; B5EE25841B36E23C0000406B /* OrganismV1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OrganismV1.swift; sourceTree = "<group>"; };
@@ -141,10 +110,10 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
B5E89ACD1C52929C003B04A9 /* GCDKit.framework in Frameworks */,
B5E89AD01C5292A2003B04A9 /* CoreStore.framework in Frameworks */,
B52977E11B120F8A003D50A5 /* CoreLocation.framework in Frameworks */, B52977E11B120F8A003D50A5 /* CoreLocation.framework in Frameworks */,
B52977DF1B120F83003D50A5 /* MapKit.framework in Frameworks */, B52977DF1B120F83003D50A5 /* MapKit.framework in Frameworks */,
B5D9C9191B20AB1900E64F0E /* GCDKit.framework in Frameworks */,
B583A9201AF5F542001F76AF /* CoreStore.framework in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -175,9 +144,9 @@
B52977E21B120F90003D50A5 /* Frameworks */ = { B52977E21B120F90003D50A5 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
B583A9141AF5F4F3001F76AF /* CoreStore.xcodeproj */,
B5D9C9181B20AB1900E64F0E /* GCDKit.framework */,
B52977E01B120F8A003D50A5 /* CoreLocation.framework */, B52977E01B120F8A003D50A5 /* CoreLocation.framework */,
B5BDC9211C202429008147CD /* CoreStore.framework */,
B5BDC9241C202429008147CD /* GCDKit.framework */,
B52977DE1B120F83003D50A5 /* MapKit.framework */, B52977DE1B120F83003D50A5 /* MapKit.framework */,
); );
name = Frameworks; name = Frameworks;
@@ -275,15 +244,6 @@
path = "Migrations Demo"; path = "Migrations Demo";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
B583A9151AF5F4F3001F76AF /* Products */ = {
isa = PBXGroup;
children = (
B583A91B1AF5F4F4001F76AF /* CoreStore.framework */,
B583A91D1AF5F4F4001F76AF /* CoreStoreTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
@@ -294,13 +254,11 @@
B54AAD451AF4D26E00848AE0 /* Sources */, B54AAD451AF4D26E00848AE0 /* Sources */,
B54AAD461AF4D26E00848AE0 /* Frameworks */, B54AAD461AF4D26E00848AE0 /* Frameworks */,
B54AAD471AF4D26E00848AE0 /* Resources */, B54AAD471AF4D26E00848AE0 /* Resources */,
B583A9241AF5F542001F76AF /* Embed Frameworks */, B5E89ACF1C52929C003B04A9 /* Embed Frameworks */,
); );
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
B583A91F1AF5F512001F76AF /* PBXTargetDependency */,
B583A9231AF5F542001F76AF /* PBXTargetDependency */,
); );
name = CoreStoreDemo; name = CoreStoreDemo;
productName = CoreStoreDemo; productName = CoreStoreDemo;
@@ -333,12 +291,6 @@
mainGroup = B54AAD401AF4D26E00848AE0; mainGroup = B54AAD401AF4D26E00848AE0;
productRefGroup = B54AAD4A1AF4D26E00848AE0 /* Products */; productRefGroup = B54AAD4A1AF4D26E00848AE0 /* Products */;
projectDirPath = ""; projectDirPath = "";
projectReferences = (
{
ProductGroup = B583A9151AF5F4F3001F76AF /* Products */;
ProjectRef = B583A9141AF5F4F3001F76AF /* CoreStore.xcodeproj */;
},
);
projectRoot = ""; projectRoot = "";
targets = ( targets = (
B54AAD481AF4D26E00848AE0 /* CoreStoreDemo */, B54AAD481AF4D26E00848AE0 /* CoreStoreDemo */,
@@ -346,23 +298,6 @@
}; };
/* End PBXProject section */ /* End PBXProject section */
/* Begin PBXReferenceProxy section */
B583A91B1AF5F4F4001F76AF /* CoreStore.framework */ = {
isa = PBXReferenceProxy;
fileType = wrapper.framework;
path = CoreStore.framework;
remoteRef = B583A91A1AF5F4F4001F76AF /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
B583A91D1AF5F4F4001F76AF /* CoreStoreTests.xctest */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
path = CoreStoreTests.xctest;
remoteRef = B583A91C1AF5F4F4001F76AF /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */ /* Begin PBXResourcesBuildPhase section */
B54AAD471AF4D26E00848AE0 /* Resources */ = { B54AAD471AF4D26E00848AE0 /* Resources */ = {
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
@@ -415,19 +350,6 @@
}; };
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
B583A91F1AF5F512001F76AF /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = CoreStore;
targetProxy = B583A91E1AF5F512001F76AF /* PBXContainerItemProxy */;
};
B583A9231AF5F542001F76AF /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = CoreStore;
targetProxy = B583A9221AF5F542001F76AF /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */ /* Begin PBXVariantGroup section */
B54AAD571AF4D26E00848AE0 /* Main.storyboard */ = { B54AAD571AF4D26E00848AE0 /* Main.storyboard */ = {
isa = PBXVariantGroup; isa = PBXVariantGroup;
@@ -3,7 +3,7 @@
// CoreStoreDemo // CoreStoreDemo
// //
// Created by John Rommel Estropia on 2015/05/02. // Created by John Rommel Estropia on 2015/05/02.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved. // Copyright © 2015 John Rommel Estropia. All rights reserved.
// //
import UIKit import UIKit
@@ -3,7 +3,7 @@
// CoreStoreDemo // CoreStoreDemo
// //
// Created by John Rommel Estropia on 2015/06/12. // Created by John Rommel Estropia on 2015/06/12.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved. // Copyright © 2015 John Rommel Estropia. All rights reserved.
// //
import UIKit import UIKit
@@ -37,7 +37,7 @@ private struct Static {
cachedTimeZone.daylightSavingTimeOffset = rawTimeZone.daylightSavingTimeOffset cachedTimeZone.daylightSavingTimeOffset = rawTimeZone.daylightSavingTimeOffset
} }
transaction.commit() transaction.commitAndWait()
} }
return dataStack return dataStack
@@ -99,10 +99,10 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch self.segmentedControl?.selectedSegmentIndex { switch self.segmentedControl?.selectedSegmentIndex {
case .Some(Section.Fetching.rawValue): case Section.Fetching.rawValue?:
return self.fetchingItems.count return self.fetchingItems.count
case .Some(Section.Querying.rawValue): case Section.Querying.rawValue?:
return self.queryingItems.count return self.queryingItems.count
default: default:
@@ -116,10 +116,10 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo
switch self.segmentedControl?.selectedSegmentIndex { switch self.segmentedControl?.selectedSegmentIndex {
case .Some(Section.Fetching.rawValue): case Section.Fetching.rawValue?:
cell.textLabel?.text = self.fetchingItems[indexPath.row].title cell.textLabel?.text = self.fetchingItems[indexPath.row].title
case .Some(Section.Querying.rawValue): case Section.Querying.rawValue?:
cell.textLabel?.text = self.queryingItems[indexPath.row].title cell.textLabel?.text = self.queryingItems[indexPath.row].title
default: default:
@@ -138,10 +138,10 @@ class FetchingAndQueryingDemoViewController: UIViewController, UITableViewDataSo
switch self.segmentedControl?.selectedSegmentIndex { switch self.segmentedControl?.selectedSegmentIndex {
case .Some(Section.Fetching.rawValue): case Section.Fetching.rawValue?:
self.performSegueWithIdentifier("FetchingResultsViewController", sender: indexPath) self.performSegueWithIdentifier("FetchingResultsViewController", sender: indexPath)
case .Some(Section.Querying.rawValue): case Section.Querying.rawValue?:
self.performSegueWithIdentifier("QueryingResultsViewController", sender: indexPath) self.performSegueWithIdentifier("QueryingResultsViewController", sender: indexPath)
default: default:
@@ -3,7 +3,7 @@
// CoreStoreDemo // CoreStoreDemo
// //
// Created by John Rommel Estropia on 2015/06/17. // Created by John Rommel Estropia on 2015/06/17.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved. // Copyright © 2015 John Rommel Estropia. All rights reserved.
// //
import UIKit import UIKit
@@ -3,7 +3,7 @@
// CoreStoreDemo // CoreStoreDemo
// //
// Created by John Rommel Estropia on 2015/06/17. // Created by John Rommel Estropia on 2015/06/17.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved. // Copyright © 2015 John Rommel Estropia. All rights reserved.
// //
import UIKit import UIKit
@@ -16,19 +16,19 @@ class QueryingResultsViewController: UITableViewController {
switch value { switch value {
case .Some(let array as [AnyObject]): case (let array as [AnyObject])?:
self.values = array.map { (item: AnyObject) -> (title: String, detail: String) in self.values = array.map { (item: AnyObject) -> (title: String, detail: String) in
( (
title: item.description, title: item.description,
detail: _stdlib_getDemangledTypeName(item) detail: String(reflecting: item.dynamicType)
) )
} }
case .Some(let item): case let item?:
self.values = [ self.values = [
( (
title: item.description, title: item.description,
detail: _stdlib_getDemangledTypeName(item) detail: String(reflecting: item.dynamicType)
) )
] ]
@@ -3,7 +3,7 @@
// CoreStoreDemo // CoreStoreDemo
// //
// Created by John Rommel Estropia on 2015/06/15. // Created by John Rommel Estropia on 2015/06/15.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved. // Copyright © 2015 John Rommel Estropia. All rights reserved.
// //
import Foundation import Foundation
@@ -3,7 +3,7 @@
// CoreStoreDemo // CoreStoreDemo
// //
// Created by John Rommel Estropia on 2015/05/02. // Created by John Rommel Estropia on 2015/05/02.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved. // Copyright © 2015 John Rommel Estropia. All rights reserved.
// //
import UIKit import UIKit
@@ -12,6 +12,41 @@ import CoreStore
private struct Static { private struct Static {
enum Filter: String {
case All = "All Colors"
case Light = "Light Colors"
case Dark = "Dark Colors"
func next() -> Filter {
switch self {
case All: return .Light
case Light: return .Dark
case Dark: return .All
}
}
func whereClause() -> Where {
switch self {
case .All: return Where(true)
case .Light: return Where("brightness >= 0.9")
case .Dark: return Where("brightness <= 0.4")
}
}
}
static var filter = Filter.All {
didSet {
self.palettes.refetch(self.filter.whereClause())
}
}
static let palettes: ListMonitor<Palette> = { static let palettes: ListMonitor<Palette> = {
try! CoreStore.addSQLiteStoreAndWait( try! CoreStore.addSQLiteStoreAndWait(
@@ -47,21 +82,35 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
super.viewDidLoad() super.viewDidLoad()
self.navigationItem.leftBarButtonItems = [ let navigationItem = self.navigationItem
navigationItem.leftBarButtonItems = [
self.editButtonItem(), self.editButtonItem(),
UIBarButtonItem( UIBarButtonItem(
barButtonSystemItem: .Trash, barButtonSystemItem: .Trash,
target: self, target: self,
action: "resetBarButtonItemTouched:" action: #selector(self.resetBarButtonItemTouched(_:))
) )
] ]
self.navigationItem.rightBarButtonItem = UIBarButtonItem(
let filterBarButton = UIBarButtonItem(
title: Static.filter.rawValue,
style: .Plain,
target: self,
action: #selector(self.filterBarButtonItemTouched(_:))
)
navigationItem.rightBarButtonItems = [
UIBarButtonItem(
barButtonSystemItem: .Add, barButtonSystemItem: .Add,
target: self, target: self,
action: "addBarButtonItemTouched:" action: #selector(self.addBarButtonItemTouched(_:))
) ),
filterBarButton
]
self.filterBarButton = filterBarButton
Static.palettes.addObserver(self) Static.palettes.addObserver(self)
self.setTableEnabled(!Static.palettes.isPendingRefetch)
} }
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
@@ -70,7 +119,7 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
switch (segue.identifier, segue.destinationViewController, sender) { switch (segue.identifier, segue.destinationViewController, sender) {
case (.Some("ObjectObserverDemoViewController"), let destinationViewController as ObjectObserverDemoViewController, let palette as Palette): case ("ObjectObserverDemoViewController"?, let destinationViewController as ObjectObserverDemoViewController, let palette as Palette):
destinationViewController.palette = palette destinationViewController.palette = palette
default: default:
@@ -150,6 +199,18 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
self.tableView.endUpdates() self.tableView.endUpdates()
} }
func listMonitorWillRefetch(monitor: ListMonitor<Palette>) {
self.setTableEnabled(false)
}
func listMonitorDidRefetch(monitor: ListMonitor<Palette>) {
self.filterBarButton?.title = Static.filter.rawValue
self.tableView.reloadData()
self.setTableEnabled(true)
}
// MARK: ListObjectObserver // MARK: ListObjectObserver
@@ -195,6 +256,8 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
// MARK: Private // MARK: Private
private var filterBarButton: UIBarButtonItem?
@IBAction private dynamic func resetBarButtonItemTouched(sender: AnyObject?) { @IBAction private dynamic func resetBarButtonItemTouched(sender: AnyObject?) {
CoreStore.beginAsynchronous { (transaction) -> Void in CoreStore.beginAsynchronous { (transaction) -> Void in
@@ -204,6 +267,11 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
} }
} }
@IBAction private dynamic func filterBarButtonItemTouched(sender: AnyObject?) {
Static.filter = Static.filter.next()
}
@IBAction private dynamic func addBarButtonItemTouched(sender: AnyObject?) { @IBAction private dynamic func addBarButtonItemTouched(sender: AnyObject?) {
CoreStore.beginAsynchronous { (transaction) -> Void in CoreStore.beginAsynchronous { (transaction) -> Void in
@@ -214,5 +282,23 @@ class ListObserverDemoViewController: UITableViewController, ListSectionObserver
transaction.commit() transaction.commit()
} }
} }
private func setTableEnabled(enabled: Bool) {
UIView.animateWithDuration(
0.2,
delay: 0,
options: .BeginFromCurrentState,
animations: { () -> Void in
if let tableView = self.tableView {
tableView.alpha = enabled ? 1.0 : 0.5
tableView.userInteractionEnabled = enabled
}
},
completion: nil
)
}
} }
@@ -3,7 +3,7 @@
// CoreStoreDemo // CoreStoreDemo
// //
// Created by John Rommel Estropia on 2015/05/06. // Created by John Rommel Estropia on 2015/05/06.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved. // Copyright © 2015 John Rommel Estropia. All rights reserved.
// //
import UIKit import UIKit
@@ -22,6 +22,11 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
} }
set { set {
guard self.monitor?.object != newValue else {
return
}
if let palette = newValue { if let palette = newValue {
self.monitor = CoreStore.monitorObject(palette) self.monitor = CoreStore.monitorObject(palette)
@@ -56,7 +61,7 @@ class ObjectObserverDemoViewController: UIViewController, ObjectObserver {
let palette = transaction.create(Into(Palette)) let palette = transaction.create(Into(Palette))
palette.setInitialValues() palette.setInitialValues()
transaction.commit() transaction.commitAndWait()
} }
let palette = CoreStore.fetchOne(From(Palette), OrderBy(.Ascending("hue")))! let palette = CoreStore.fetchOne(From(Palette), OrderBy(.Ascending("hue")))!
@@ -3,7 +3,7 @@
// CoreStoreDemo // CoreStoreDemo
// //
// Created by John Rommel Estropia on 2015/05/24. // Created by John Rommel Estropia on 2015/05/24.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved. // Copyright © 2015 John Rommel Estropia. All rights reserved.
// //
import UIKit import UIKit
@@ -3,7 +3,7 @@
// CoreStoreDemo // CoreStoreDemo
// //
// Created by John Rommel Estropia on 2015/05/05. // Created by John Rommel Estropia on 2015/05/05.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved. // Copyright © 2015 John Rommel Estropia. All rights reserved.
// //
import Foundation import Foundation
@@ -3,7 +3,7 @@
// CoreStoreDemo // CoreStoreDemo
// //
// Created by John Rommel Estropia on 2015/05/05. // Created by John Rommel Estropia on 2015/05/05.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved. // Copyright © 2015 John Rommel Estropia. All rights reserved.
// //
import UIKit import UIKit
@@ -3,7 +3,7 @@
// CoreStoreDemo // CoreStoreDemo
// //
// Created by John Rommel Estropia on 2015/06/05. // Created by John Rommel Estropia on 2015/06/05.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved. // Copyright © 2015 John Rommel Estropia. All rights reserved.
// //
import UIKit import UIKit
@@ -102,20 +102,20 @@ class CustomLoggerViewController: UIViewController, CoreStoreLogger {
switch self.segmentedControl?.selectedSegmentIndex { switch self.segmentedControl?.selectedSegmentIndex {
case .Some(0): case 0?:
self.dataStack.beginAsynchronous { (transaction) -> Void in self.dataStack.beginAsynchronous { (transaction) -> Void in
transaction.create(Into(Palette)) transaction.create(Into(Palette))
} }
case .Some(1): case 1?:
do { do {
try self.dataStack.addSQLiteStoreAndWait(fileName: "emptyStore.sqlite", configuration: "invalidStore") try self.dataStack.addSQLiteStoreAndWait(fileName: "emptyStore.sqlite", configuration: "invalidStore")
} }
catch _ { } catch _ { }
case .Some(2): case 2?:
self.dataStack.beginAsynchronous { (transaction) -> Void in self.dataStack.beginAsynchronous { (transaction) -> Void in
transaction.commit() transaction.commit()
@@ -3,7 +3,7 @@
// CoreStoreDemo // CoreStoreDemo
// //
// Created by John Rommel Estropia on 2015/06/21. // Created by John Rommel Estropia on 2015/06/21.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved. // Copyright © 2015 John Rommel Estropia. All rights reserved.
// //
import UIKit import UIKit
@@ -39,7 +39,7 @@ class MigrationsDemoViewController: UIViewController {
let alert = UIAlertController( let alert = UIAlertController(
title: "Migrations Demo", title: "Migrations Demo",
message: "This demo shows how to run incremental migrations and how to support multiple model versions in a single project.\n\nThe persistent store contains 10000 organisms, which gain/lose properties when the migration evolves/devolves them.\n\nYou can use the \"mutate\" button to change an organism's properties then migrate to a different model to see how its value gets affected.", message: "This demo shows how to run progressive migrations and how to support multiple model versions in a single project.\n\nThe persistent store contains 10000 organisms, which gain/lose properties when the migration evolves/devolves them.\n\nYou can use the \"mutate\" button to change an organism's properties then migrate to a different model to see how its value gets affected.",
preferredStyle: .Alert preferredStyle: .Alert
) )
alert.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: nil)) alert.addAction(UIAlertAction(title: "OK", style: .Cancel, handler: nil))
@@ -162,36 +162,46 @@ class MigrationsDemoViewController: UIViewController {
fileName: "MigrationDemo.sqlite", fileName: "MigrationDemo.sqlite",
completion: { [weak self] (result) -> Void in completion: { [weak self] (result) -> Void in
guard let strongSelf = self else { guard let `self` = self else {
return return
} }
guard case .Success = result else { guard case .Success = result else {
strongSelf.setEnabled(true) self.setEnabled(true)
return return
} }
strongSelf.setDataStack(dataStack, model: model, scrollToSelection: true) self.setDataStack(dataStack, model: model, scrollToSelection: true)
let count = dataStack.queryValue(From(model.entityType), Select<Int>(.Count("dna"))) let count = dataStack.queryValue(
From(model.entityType),
Select<Int>(.Count("dna"))
)
if count > 0 { if count > 0 {
strongSelf.setEnabled(true) self.setEnabled(true)
} }
else { else {
for i: Int64 in 0 ..< 20 {
dataStack.beginAsynchronous { (transaction) -> Void in dataStack.beginAsynchronous { (transaction) -> Void in
for i: Int64 in 1 ..< 10000 { for j: Int64 in 0 ..< 500 {
let organism = transaction.create(Into(model.entityType)) as! OrganismProtocol let organism = transaction.create(Into(model.entityType)) as! OrganismProtocol
organism.dna = i organism.dna = (i * 500) + j + 1
organism.mutate() organism.mutate()
} }
transaction.commit { result -> Void in transaction.commit()
}
}
dataStack.beginAsynchronous { [weak self] (transaction) -> Void in
transaction.commit { _ in
self?.setEnabled(true) self?.setEnabled(true)
} }
@@ -267,7 +277,7 @@ class MigrationsDemoViewController: UIViewController {
self.progressView?.setProgress(Float(progress.fractionCompleted), animated: true) self.progressView?.setProgress(Float(progress.fractionCompleted), animated: true)
self.titleLabel?.text = "Migrating: \(progress.localizedDescription)" self.titleLabel?.text = "Migrating: \(progress.localizedDescription)"
self.organismLabel?.text = "Incremental step \(progress.localizedAdditionalDescription)" self.organismLabel?.text = "Progressive step \(progress.localizedAdditionalDescription)"
} }
private func updateDisplay(reloadData reloadData: Bool, scrollToSelection: Bool, animated: Bool) { private func updateDisplay(reloadData reloadData: Bool, scrollToSelection: Bool, animated: Bool) {
@@ -285,7 +295,7 @@ class MigrationsDemoViewController: UIViewController {
} }
self.titleLabel?.text = organismType self.titleLabel?.text = organismType
self.organismLabel?.text = "\n".join(lines) self.organismLabel?.text = lines.joinWithSeparator("\n")
self.progressView?.progress = 0 self.progressView?.progress = 0
self.headerContainer?.setNeedsLayout() self.headerContainer?.setNeedsLayout()
@@ -3,7 +3,7 @@
// CoreStoreDemo // CoreStoreDemo
// //
// Created by John Rommel Estropia on 2015/06/27. // Created by John Rommel Estropia on 2015/06/27.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved. // Copyright © 2015 John Rommel Estropia. All rights reserved.
// //
import Foundation import Foundation
@@ -3,7 +3,7 @@
// CoreStoreDemo // CoreStoreDemo
// //
// Created by John Rommel Estropia on 2015/06/21. // Created by John Rommel Estropia on 2015/06/21.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved. // Copyright © 2015 John Rommel Estropia. All rights reserved.
// //
import Foundation import Foundation
@@ -3,7 +3,7 @@
// CoreStoreDemo // CoreStoreDemo
// //
// Created by John Rommel Estropia on 2015/06/21. // Created by John Rommel Estropia on 2015/06/21.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved. // Copyright © 2015 John Rommel Estropia. All rights reserved.
// //
import Foundation import Foundation
@@ -3,7 +3,7 @@
// CoreStoreDemo // CoreStoreDemo
// //
// Created by John Rommel Estropia on 2015/06/27. // Created by John Rommel Estropia on 2015/06/27.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved. // Copyright © 2015 John Rommel Estropia. All rights reserved.
// //
import CoreData import CoreData
@@ -3,7 +3,7 @@
// CoreStoreDemo // CoreStoreDemo
// //
// Created by John Rommel Estropia on 2015/06/27. // Created by John Rommel Estropia on 2015/06/27.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved. // Copyright © 2015 John Rommel Estropia. All rights reserved.
// //
import Foundation import Foundation
@@ -3,7 +3,7 @@
// CoreStoreDemo // CoreStoreDemo
// //
// Created by John Rommel Estropia on 2015/06/06. // Created by John Rommel Estropia on 2015/06/06.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved. // Copyright © 2015 John Rommel Estropia. All rights reserved.
// //
import Foundation import Foundation
@@ -3,7 +3,7 @@
// CoreStoreDemo // CoreStoreDemo
// //
// Created by John Rommel Estropia on 2015/06/06. // Created by John Rommel Estropia on 2015/06/06.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved. // Copyright © 2015 John Rommel Estropia. All rights reserved.
// //
import Foundation import Foundation

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