This commit is contained in:
John Rommel Estropia
2017-03-27 01:48:51 +09:00
parent cb6d5b015b
commit 818abfc1a1
12 changed files with 158 additions and 91 deletions

View File

@@ -55,6 +55,7 @@ extension NSManagedObjectID: QueryableAttributeType {
@nonobjc @inline(__always)
public class func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Self? {
@inline(__always)
func forceCast<T: NSManagedObjectID>(_ value: Any) -> T? {
return value as? T
@@ -84,6 +85,7 @@ extension NSNumber: QueryableAttributeType {
@nonobjc @inline(__always)
public class func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Self? {
@inline(__always)
func forceCast<T: NSNumber>(_ value: Any) -> T? {
return value as? T
@@ -121,6 +123,7 @@ extension NSString: QueryableAttributeType {
@nonobjc @inline(__always)
public class func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Self? {
@inline(__always)
func forceCast<T: NSString>(_ value: Any) -> T? {
return value as? T
@@ -147,6 +150,7 @@ extension NSDate: QueryableAttributeType {
@nonobjc @inline(__always)
public class func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Self? {
@inline(__always)
func forceCast<T: NSDate>(_ value: Any) -> T? {
return value as? T
@@ -173,6 +177,7 @@ extension NSData: QueryableAttributeType {
@nonobjc @inline(__always)
public class func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Self? {
@inline(__always)
func forceCast<T: NSData>(_ value: Any) -> T? {
return value as? T
@@ -204,7 +209,7 @@ extension Bool: QueryableAttributeType {
case let decimal as NSDecimalNumber:
// iOS: NSDecimalNumber(string: "0.5").boolValue // true
// OSX: NSDecimalNumber(string: "0.5").boolValue // false
return NSNumber(value: decimal.doubleValue).boolValue
return decimal != NSDecimalNumber.zero
default:
return value.boolValue

View File

@@ -59,17 +59,13 @@ extension NSNumber: ImportableAttributeType {
@nonobjc @inline(__always)
public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? {
func forceCast<T: NSNumber>(_ value: Any) -> T? {
return value as? T
}
return forceCast(value)
return self.cs_fromQueryableNativeType(value)
}
@nonobjc @inline(__always)
public func cs_toImportableNativeType() -> ImportableNativeType {
return self
return self.cs_toQueryableNativeType()
}
}
@@ -89,17 +85,13 @@ extension NSString: ImportableAttributeType {
@nonobjc @inline(__always)
public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? {
func forceCast<T: NSString>(_ value: Any) -> T? {
return value as? T
}
return forceCast(value)
return self.cs_fromQueryableNativeType(value)
}
@nonobjc @inline(__always)
public func cs_toImportableNativeType() -> ImportableNativeType {
return self
return self.cs_toQueryableNativeType()
}
}
@@ -119,17 +111,13 @@ extension NSDate: ImportableAttributeType {
@nonobjc @inline(__always)
public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? {
func forceCast<T: NSDate>(_ value: Any) -> T? {
return value as? T
}
return forceCast(value)
return self.cs_fromQueryableNativeType(value)
}
@nonobjc @inline(__always)
public func cs_toImportableNativeType() -> ImportableNativeType {
return self
return self.cs_toQueryableNativeType()
}
}
@@ -149,17 +137,13 @@ extension NSData: ImportableAttributeType {
@nonobjc @inline(__always)
public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? {
func forceCast<T: NSData>(_ value: Any) -> T? {
return value as? T
}
return forceCast(value)
return self.cs_fromQueryableNativeType(value)
}
@nonobjc @inline(__always)
public func cs_toImportableNativeType() -> ImportableNativeType {
return self
return self.cs_toQueryableNativeType()
}
}
@@ -179,22 +163,13 @@ extension Bool: ImportableAttributeType {
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Bool? {
switch value {
case let decimal as NSDecimalNumber:
// iOS: NSDecimalNumber(string: "0.5").boolValue // true
// OSX: NSDecimalNumber(string: "0.5").boolValue // false
return decimal != NSDecimalNumber.zero
default:
return value.boolValue
}
return self.cs_fromQueryableNativeType(value)
}
@inline(__always)
public func cs_toImportableNativeType() -> ImportableNativeType {
return self as NSNumber
return self.cs_toQueryableNativeType()
}
}
@@ -214,13 +189,13 @@ extension Int16: ImportableAttributeType {
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Int16? {
return value.int16Value
return self.cs_fromQueryableNativeType(value)
}
@inline(__always)
public func cs_toImportableNativeType() -> ImportableNativeType {
return self as NSNumber
return self.cs_toQueryableNativeType()
}
}
@@ -240,13 +215,13 @@ extension Int32: ImportableAttributeType {
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Int32? {
return value.int32Value
return self.cs_fromQueryableNativeType(value)
}
@inline(__always)
public func cs_toImportableNativeType() -> ImportableNativeType {
return self as NSNumber
return self.cs_toQueryableNativeType()
}
}
@@ -266,13 +241,13 @@ extension Int64: ImportableAttributeType {
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Int64? {
return value.int64Value
return self.cs_fromQueryableNativeType(value)
}
@inline(__always)
public func cs_toImportableNativeType() -> ImportableNativeType {
return self as NSNumber
return self.cs_toQueryableNativeType()
}
}
@@ -292,13 +267,13 @@ extension Double: ImportableAttributeType {
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Double? {
return value.doubleValue
return self.cs_fromQueryableNativeType(value)
}
@inline(__always)
public func cs_toImportableNativeType() -> ImportableNativeType {
return self as NSNumber
return self.cs_toQueryableNativeType()
}
}
@@ -318,13 +293,13 @@ extension Float: ImportableAttributeType {
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Float? {
return value.floatValue
return self.cs_fromQueryableNativeType(value)
}
@inline(__always)
public func cs_toImportableNativeType() -> ImportableNativeType {
return self as NSNumber
return self.cs_toQueryableNativeType()
}
}
@@ -344,13 +319,13 @@ extension Date: ImportableAttributeType {
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Date? {
return value as Date
return self.cs_fromQueryableNativeType(value)
}
@inline(__always)
public func cs_toImportableNativeType() -> ImportableNativeType {
return self as NSDate
return self.cs_toQueryableNativeType()
}
}
@@ -370,13 +345,13 @@ extension String: ImportableAttributeType {
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> String? {
return value as String
return self.cs_fromQueryableNativeType(value)
}
@inline(__always)
public func cs_toImportableNativeType() -> ImportableNativeType {
return self as NSString
return self.cs_toQueryableNativeType()
}
}
@@ -396,12 +371,12 @@ extension Data: ImportableAttributeType {
@inline(__always)
public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Data? {
return value as Data
return self.cs_fromQueryableNativeType(value)
}
@inline(__always)
public func cs_toImportableNativeType() -> ImportableNativeType {
return self as NSData
return self.cs_toQueryableNativeType()
}
}

View File

@@ -33,7 +33,7 @@ internal extension CoreStoreFetchRequest {
// MARK: Internal
@nonobjc
@nonobjc @inline(__always)
internal func dynamicCast<U: NSFetchRequestResult>() -> NSFetchRequest<U> {
return unsafeBitCast(self, to: NSFetchRequest<U>.self)

View File

@@ -30,7 +30,7 @@ import Foundation
internal extension DispatchQueue {
@nonobjc
@nonobjc @inline(__always)
internal static func serial(_ label: String, qos: DispatchQoS = .default) -> DispatchQueue {
return DispatchQueue(
@@ -42,7 +42,7 @@ internal extension DispatchQueue {
)
}
@nonobjc
@nonobjc @inline(__always)
internal static func concurrent(_ label: String, qos: DispatchQoS = .default) -> DispatchQueue {
return DispatchQueue(
@@ -54,7 +54,7 @@ internal extension DispatchQueue {
)
}
@nonobjc
@nonobjc @inline(__always)
internal func cs_isCurrentExecutionContext() -> Bool {
let specific = ObjectIdentifier(self)
@@ -63,25 +63,25 @@ internal extension DispatchQueue {
return DispatchQueue.getSpecific(key: Static.specificKey) == specific
}
@nonobjc
@nonobjc @inline(__always)
internal func cs_sync<T>(_ closure: () throws -> T) rethrows -> T {
return try self.sync { try autoreleasepool(invoking: closure) }
}
@nonobjc
@nonobjc @inline(__always)
internal func cs_async(_ closure: @escaping () -> Void) {
self.async { autoreleasepool(invoking: closure) }
}
@nonobjc
@nonobjc @inline(__always)
internal func cs_barrierSync<T>(_ closure: () throws -> T) rethrows -> T {
return try self.sync(flags: .barrier) { try autoreleasepool(invoking: closure) }
}
@nonobjc
@nonobjc @inline(__always)
internal func cs_barrierAsync(_ closure: @escaping () -> Void) {
self.async(flags: .barrier) { autoreleasepool(invoking: closure) }

View File

@@ -27,6 +27,7 @@ import Foundation
// MARK: Associated Objects
@inline(__always)
internal func cs_getAssociatedObjectForKey<T: AnyObject>(_ key: UnsafeRawPointer, inObject object: Any) -> T? {
switch objc_getAssociatedObject(object, key) {
@@ -42,16 +43,19 @@ internal func cs_getAssociatedObjectForKey<T: AnyObject>(_ key: UnsafeRawPointer
}
}
@inline(__always)
internal func cs_setAssociatedRetainedObject<T: AnyObject>(_ associatedObject: T?, forKey key: UnsafeRawPointer, inObject object: Any) {
objc_setAssociatedObject(object, key, associatedObject, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
@inline(__always)
internal func cs_setAssociatedCopiedObject<T: AnyObject>(_ associatedObject: T?, forKey key: UnsafeRawPointer, inObject object: Any) {
objc_setAssociatedObject(object, key, associatedObject, .OBJC_ASSOCIATION_COPY_NONATOMIC)
}
@inline(__always)
internal func cs_setAssociatedWeakObject<T: AnyObject>(_ associatedObject: T?, forKey key: UnsafeRawPointer, inObject object: Any) {
if let associatedObject = associatedObject {
@@ -67,26 +71,31 @@ internal func cs_setAssociatedWeakObject<T: AnyObject>(_ associatedObject: T?, f
// MARK: Printing Utilities
@inline(__always)
internal func cs_typeName<T>(_ value: T) -> String {
return "'\(String(reflecting: type(of: value)))'"
}
@inline(__always)
internal func cs_typeName<T>(_ value: T.Type) -> String {
return "'\(value)'"
}
@inline(__always)
internal func cs_typeName(_ value: AnyClass) -> String {
return "'\(value)'"
}
@inline(__always)
internal func cs_typeName(_ name: String) -> String {
return "<\(name)>"
}
@inline(__always)
internal func cs_typeName(_ name: String?) -> String {
return "<\(name ?? "unknown")>"

View File

@@ -34,6 +34,11 @@ import CoreData
*/
public final class AsynchronousDataTransaction: BaseDataTransaction {
public func cancel() throws -> Never {
throw CoreStoreError.userCancelled
}
/**
Saves the transaction changes. This method should not be used after the `commit()` method was already called once.

View File

@@ -36,12 +36,6 @@ public /*abstract*/ class BaseDataTransaction {
// MARK: Object management
public func cancel() throws -> Never {
throw CoreStoreError.userCancelled
}
/**
Indicates if the transaction has pending changes
*/

View File

@@ -31,6 +31,16 @@ import CoreData
public extension DataStack {
public func perform<T>(asynchronous task: @escaping (_ transaction: AsynchronousDataTransaction) throws -> T, completion: @escaping (TransactionResult<T>) -> Void) {
self.perform(
asynchronous: task,
success: { completion(TransactionResult(userInfo: $0)) },
failure: { completion(TransactionResult(error: $0)) }
)
}
public func perform<T>(asynchronous task: @escaping (_ transaction: AsynchronousDataTransaction) throws -> T, success: @escaping (T) -> Void, failure: @escaping (CoreStoreError) -> Void) {
let transaction = AsynchronousDataTransaction(
@@ -40,28 +50,28 @@ public extension DataStack {
)
transaction.transactionQueue.cs_async {
let userInfo: T
do {
let extraInfo = try task(transaction)
transaction.commit { (result) in
switch result {
case .success:
success(extraInfo)
case .failure(let error):
failure(error)
}
}
userInfo = try task(transaction)
}
catch let error as CoreStoreError {
DispatchQueue.main.async { failure(error) }
return
}
catch let error {
DispatchQueue.main.async { failure(.userError(error: error)) }
return
}
transaction.commit { (result) in
switch result {
case .success: success(userInfo)
case .failure(let error): failure(error)
}
}
}
}
@@ -75,10 +85,10 @@ public extension DataStack {
)
return try transaction.transactionQueue.cs_sync {
let extraInfo: T
let userInfo: T
do {
extraInfo = try task(transaction)
userInfo = try task(transaction)
}
catch let error as CoreStoreError {
@@ -88,17 +98,13 @@ public extension DataStack {
throw CoreStoreError.userError(error: error)
}
let result = waitForObserverNotifications
? transaction.commitAndWait()
: transaction.commit()
switch result {
case .success:
return extraInfo
case .failure(let error):
throw error
case .success: return userInfo
case .failure(let error): throw error
}
}
}

View File

@@ -34,6 +34,11 @@ import CoreData
*/
public final class SynchronousDataTransaction: BaseDataTransaction {
public func cancel() throws -> Never {
throw CoreStoreError.userCancelled
}
/**
Saves the transaction changes and waits for completion synchronously. This method should not be used after the `commit()` or `commitAndWait()` method was already called once.
- Important: Unlike `SynchronousDataTransaction.commit()`, this method waits for all observers to be notified of the changes before returning. This results in more predictable data update order, but may risk triggering deadlocks.

View File

@@ -0,0 +1,58 @@
//
// TransactionResult.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: - TransactionResult
public enum TransactionResult<T> {
case success(T)
case failure(CoreStoreError)
public var boolValue: Bool {
switch self {
case .success: return true
case .failure: return false
}
}
// MARK: Internal
internal init(userInfo: T) {
self = .success(userInfo)
}
internal init(error: CoreStoreError) {
self = .failure(error)
}
}