From a543a4c94ab64a63c7e9e66020955c516f2aa909 Mon Sep 17 00:00:00 2001 From: John Estropia Date: Thu, 20 Apr 2017 16:40:27 +0900 Subject: [PATCH] Added a "userInfo" property to relevant types to allow external code to store custom data --- CoreStore.xcodeproj/project.pbxproj | 10 ++ Sources/Convenience/UserInfo.swift | 115 ++++++++++++++++++ Sources/Observing/ListMonitor.swift | 15 +++ Sources/Observing/ObjectMonitor.swift | 15 +++ Sources/Setup/DataStack.swift | 56 +-------- .../Transactions/BaseDataTransaction.swift | 63 +--------- 6 files changed, 160 insertions(+), 114 deletions(-) create mode 100644 Sources/Convenience/UserInfo.swift diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index 7df750f..8be28c6 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -434,6 +434,10 @@ B5A991ED1E9DC2CE0091A2E3 /* VersionLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A991EB1E9DC2CE0091A2E3 /* VersionLock.swift */; }; B5A991EE1E9DC2CE0091A2E3 /* VersionLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A991EB1E9DC2CE0091A2E3 /* VersionLock.swift */; }; B5A991EF1E9DC2CE0091A2E3 /* VersionLock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A991EB1E9DC2CE0091A2E3 /* VersionLock.swift */; }; + B5A9921F1EA898710091A2E3 /* UserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A9921E1EA898710091A2E3 /* UserInfo.swift */; }; + B5A992201EA898720091A2E3 /* UserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A9921E1EA898710091A2E3 /* UserInfo.swift */; }; + B5A992211EA898720091A2E3 /* UserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A9921E1EA898710091A2E3 /* UserInfo.swift */; }; + B5A992221EA898720091A2E3 /* UserInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5A9921E1EA898710091A2E3 /* UserInfo.swift */; }; B5AEFAB51C9962AE00AD137F /* CoreStoreBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AEFAB41C9962AE00AD137F /* CoreStoreBridge.swift */; }; B5AEFAB61C9962AE00AD137F /* CoreStoreBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AEFAB41C9962AE00AD137F /* CoreStoreBridge.swift */; }; B5AEFAB71C9962AE00AD137F /* CoreStoreBridge.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5AEFAB41C9962AE00AD137F /* CoreStoreBridge.swift */; }; @@ -757,6 +761,7 @@ B5A261201B64BFDB006EB6D3 /* MigrationType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationType.swift; sourceTree = ""; }; B5A5F2651CAEC50F004AB9AF /* CSSelect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSSelect.swift; sourceTree = ""; }; B5A991EB1E9DC2CE0091A2E3 /* VersionLock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VersionLock.swift; sourceTree = ""; }; + B5A9921E1EA898710091A2E3 /* UserInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserInfo.swift; sourceTree = ""; }; B5AD60CD1C90141E00F2B2E8 /* Package.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = SOURCE_ROOT; }; B5AEFAB41C9962AE00AD137F /* CoreStoreBridge.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreBridge.swift; sourceTree = ""; }; B5BDC91A1C202269008147CD /* Cartfile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Cartfile; path = ../Cartfile; sourceTree = ""; }; @@ -1323,6 +1328,7 @@ B512607E1E97A18000402229 /* CoreStoreObject+Convenience.swift */, B5FAD6A81B50A4B300714891 /* Progress+Convenience.swift */, B5202CF91C04688100DED140 /* NSFetchedResultsController+Convenience.swift */, + B5A9921E1EA898710091A2E3 /* UserInfo.swift */, ); path = Convenience; sourceTree = ""; @@ -1692,6 +1698,7 @@ B52FD3AA1E3B3EF10001D919 /* NSManagedObject+Logging.swift in Sources */, B52F74411E9B8724005F3DAC /* LegacyXcodeDataModel.swift in Sources */, B51FE5AB1CD4D00300E54258 /* CoreStore+CustomDebugStringConvertible.swift in Sources */, + B5A9921F1EA898710091A2E3 /* UserInfo.swift in Sources */, B54A6A551BA15F2A007870FD /* FetchedResultsControllerDelegate.swift in Sources */, B5D339E21E948C3600C880DE /* Value.swift in Sources */, B5A261211B64BFDB006EB6D3 /* MigrationType.swift in Sources */, @@ -1864,6 +1871,7 @@ B52FD3AB1E3B3EF10001D919 /* NSManagedObject+Logging.swift in Sources */, B52F74421E9B8724005F3DAC /* LegacyXcodeDataModel.swift in Sources */, B51FE5AD1CD4D00300E54258 /* CoreStore+CustomDebugStringConvertible.swift in Sources */, + B5A992201EA898720091A2E3 /* UserInfo.swift in Sources */, B5FE4DAD1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */, B5D339E31E948C3600C880DE /* Value.swift in Sources */, 82BA18C51C4BBD5300A0916E /* ListObserver.swift in Sources */, @@ -2036,6 +2044,7 @@ B52FD3AD1E3B3EF10001D919 /* NSManagedObject+Logging.swift in Sources */, B52F74441E9B8724005F3DAC /* LegacyXcodeDataModel.swift in Sources */, B5ECDC2D1CA81CC700C7F112 /* CSDataStack+Transaction.swift in Sources */, + B5A992221EA898720091A2E3 /* UserInfo.swift in Sources */, B5D7A5BA1CA3BF8F005C752B /* CSInto.swift in Sources */, B5D339E51E948C3600C880DE /* Value.swift in Sources */, B5A5F26A1CAEC50F004AB9AF /* CSSelect.swift in Sources */, @@ -2208,6 +2217,7 @@ B52FD3AC1E3B3EF10001D919 /* NSManagedObject+Logging.swift in Sources */, B52F74431E9B8724005F3DAC /* LegacyXcodeDataModel.swift in Sources */, B51FE5AE1CD4D00300E54258 /* CoreStore+CustomDebugStringConvertible.swift in Sources */, + B5A992211EA898720091A2E3 /* UserInfo.swift in Sources */, B563218C1BD65216006C9394 /* DataStack+Transaction.swift in Sources */, B5D339E41E948C3600C880DE /* Value.swift in Sources */, B53FBA0E1CAB5E6500F0D40A /* CSCoreStore+Migrating.swift in Sources */, diff --git a/Sources/Convenience/UserInfo.swift b/Sources/Convenience/UserInfo.swift new file mode 100644 index 0000000..389c7cf --- /dev/null +++ b/Sources/Convenience/UserInfo.swift @@ -0,0 +1,115 @@ +// +// UserInfo.swift +// CoreStore +// +// Copyright © 2017 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation + + +// MARK: UserInfo + +/** + The `UserInfo` class is provided by several CoreStore types such as `DataStack`, `ListMonitor`, `ObjectMonitor` and transactions to allow external libraries or user apps to store their own custom data. + ``` + enum Static { + static var myDataKey: Void? + } + CoreStore.defaultStack.userInfo[&Static.myDataKey] = myObject + ``` + - Important: Do not use this class to store thread-sensitive data. + */ +public final class UserInfo { + + /** + Allows external libraries to store custom data. App code should rarely have a need for this. + ``` + enum Static { + static var myDataKey: Void? + } + CoreStore.defaultStack.userInfo[&Static.myDataKey] = myObject + ``` + - Important: Do not use this method to store thread-sensitive data. + - parameter key: the key for custom data. Make sure this is a static pointer that will never be changed. + */ + public subscript(key: UnsafeRawPointer) -> Any? { + + get { + + self.lock.lock() + defer { + + self.lock.unlock() + } + return self.data[key] + } + set { + + self.lock.lock() + defer { + + self.lock.unlock() + } + self.data[key] = newValue + } + } + + /** + Allows external libraries to store custom data in the `DataStack`. App code should rarely have a need for this. + ``` + enum Static { + static var myDataKey: Void? + } + CoreStore.defaultStack.userInfo[&Static.myDataKey, lazyInit: { MyObject() }] = myObject + ``` + - Important: Do not use this method to store thread-sensitive data. + - parameter key: the key for custom data. Make sure this is a static pointer that will never be changed. + - parameter lazyInit: a closure to use to lazily-initialize the data + - returns: A custom data identified by `key` + */ + public subscript(key: UnsafeRawPointer, lazyInit closure: () -> Any) -> Any { + + self.lock.lock() + defer { + + self.lock.unlock() + } + if let value = self.data[key] { + + return value + } + let value = closure() + self.data[key] = value + return value + } + + + // MARK: Internal + + internal init() {} + + + // MARK: Private + + private var data: [UnsafeRawPointer: Any] = [:] + private let lock = NSRecursiveLock() +} diff --git a/Sources/Observing/ListMonitor.swift b/Sources/Observing/ListMonitor.swift index 54e9be1..505c5fe 100644 --- a/Sources/Observing/ListMonitor.swift +++ b/Sources/Observing/ListMonitor.swift @@ -578,6 +578,21 @@ public final class ListMonitor: Hashable { } + // MARK: Public (3rd Party Utilities) + + /** + Allow external libraries to store custom data in the `ListMonitor`. App code should rarely have a need for this. + ``` + enum Static { + static var myDataKey: Void? + } + monitor.userInfo[&Static.myDataKey] = myObject + ``` + - Important: Do not use this method to store thread-sensitive data. + */ + private let userInfo = UserInfo() + + // MARK: Equatable public static func == (lhs: ListMonitor, rhs: ListMonitor) -> Bool { diff --git a/Sources/Observing/ObjectMonitor.swift b/Sources/Observing/ObjectMonitor.swift index 3550a57..85cbb1b 100644 --- a/Sources/Observing/ObjectMonitor.swift +++ b/Sources/Observing/ObjectMonitor.swift @@ -110,6 +110,21 @@ public final class ObjectMonitor: Equatable { } + // MARK: Public (3rd Party Utilities) + + /** + Allow external libraries to store custom data in the `ObjectMonitor`. App code should rarely have a need for this. + ``` + enum Static { + static var myDataKey: Void? + } + monitor.userInfo[&Static.myDataKey] = myObject + ``` + - Important: Do not use this method to store thread-sensitive data. + */ + private let userInfo = UserInfo() + + // MARK: Equatable public static func == (lhs: ObjectMonitor, rhs: ObjectMonitor) -> Bool { diff --git a/Sources/Setup/DataStack.swift b/Sources/Setup/DataStack.swift index 7df7412..f07c32b 100644 --- a/Sources/Setup/DataStack.swift +++ b/Sources/Setup/DataStack.swift @@ -450,61 +450,11 @@ public final class DataStack: Equatable { enum Static { static var myDataKey: Void? } - CoreStore.defaultStack[userInfoKey: &Static.myDataKey] = myObject + CoreStore.defaultStack.userInfo[&Static.myDataKey] = myObject ``` - Important: Do not use this method to store thread-sensitive data. - - parameter userInfoKey: the key for custom data. Make sure this is a static pointer that will never be changed. */ - public subscript(userInfoKey key: UnsafeRawPointer) -> Any? { - - get { - - self.userInfoLock.lock() - defer { - - self.userInfoLock.unlock() - } - return self.userInfo[key] - } - set { - - self.userInfoLock.lock() - defer { - - self.userInfoLock.unlock() - } - self.userInfo[key] = newValue - } - } - - /** - Allow external libraries to store custom data in the `DataStack`. App code should rarely have a need for this. - ``` - enum Static { - static var myDataKey: Void? - } - CoreStore.defaultStack[userInfoKey: &Static.myDataKey, lazyInit: { MyObject() }] = myObject - ``` - - Important: Do not use this method to store thread-sensitive data. - - parameter userInfoKey: the key for custom data. Make sure this is a static pointer that will never be changed. - - parameter lazyInit: a closure to use to lazily-initialize the data - - returns: A custom data identified by `userInfoKey` - */ - public subscript(userInfoKey key: UnsafeRawPointer, lazyInit closure: () -> Any) -> Any { - - self.userInfoLock.lock() - defer { - - self.userInfoLock.unlock() - } - if let value = self.userInfo[key] { - - return value - } - let value = closure() - self.userInfo[key] = value - return value - } + private let userInfo = UserInfo() // MARK: Equatable @@ -636,8 +586,6 @@ public final class DataStack: Equatable { private var persistentStoresByFinalConfiguration = [String: NSPersistentStore]() private var finalConfigurationsByEntityIdentifier = [EntityIdentifier: Set]() - private var userInfo: [UnsafeRawPointer: Any] = [:] - private let userInfoLock = NSRecursiveLock() deinit { diff --git a/Sources/Transactions/BaseDataTransaction.swift b/Sources/Transactions/BaseDataTransaction.swift index 6c6acf9..4a1a45b 100644 --- a/Sources/Transactions/BaseDataTransaction.swift +++ b/Sources/Transactions/BaseDataTransaction.swift @@ -444,64 +444,13 @@ public /*abstract*/ class BaseDataTransaction { Allow external libraries to store custom data in the transaction. App code should rarely have a need for this. ``` enum Static { - static var myDataKey: Void? + static var myDataKey: Void? } - transaction[userInfoKey: &Static.myDataKey] = myObject + transaction.userInfo[&Static.myDataKey] = myObject ``` - Important: Do not use this method to store thread-sensitive data. - - parameter userInfoKey: the key for custom data. Make sure this is a static pointer that will never be changed. - - returns: A custom data identified by `userInfoKey` */ - public subscript(userInfoKey key: UnsafeRawPointer) -> Any? { - - get { - - self.userInfoLock.lock() - defer { - - self.userInfoLock.unlock() - } - return self.userInfo[key] - } - set { - - self.userInfoLock.lock() - defer { - - self.userInfoLock.unlock() - } - self.userInfo[key] = newValue - } - } - - /** - Allow external libraries to store custom data in the transaction. App code should rarely have a need for this. - ``` - enum Static { - static var myDataKey: Void? - } - CoreStore.defaultStack[userInfoKey: &Static.myDataKey, lazyInit: { MyObject() }] = myObject - ``` - - Important: Do not use this method to store thread-sensitive data. - - parameter userInfoKey: the key for custom data. Make sure this is a static pointer that will never be changed. - - parameter lazyInit: a closure to use to lazily-initialize the data - - returns: A custom data identified by `userInfoKey` - */ - public subscript(userInfoKey key: UnsafeRawPointer, lazyInit closure: () -> Any) -> Any { - - self.userInfoLock.lock() - defer { - - self.userInfoLock.unlock() - } - if let value = self.userInfo[key] { - - return value - } - let value = closure() - self.userInfo[key] = value - return value - } + private let userInfo = UserInfo() // MARK: Internal @@ -542,10 +491,4 @@ public /*abstract*/ class BaseDataTransaction { return self.bypassesQueueing || self.transactionQueue.cs_isCurrentExecutionContext() } - - - // MARK: Private - - private var userInfo: [UnsafeRawPointer: Any] = [:] - private let userInfoLock = NSRecursiveLock() }