Skip to content

NSPredicate partial implementation #716

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 17, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 77 additions & 6 deletions Foundation/NSPredicate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ open class NSPredicate : NSObject, NSSecureCoding, NSCopying {
private enum PredicateKind {
case boolean(Bool)
case block((Any?, [String : Any]?) -> Bool)
// TODO: case for init(format:argumentArray:)
// TODO: case for init(fromMetadataQueryString:)
case format(String)
case metadataQuery(String)
}

private let kind: PredicateKind
Expand All @@ -26,19 +26,72 @@ open class NSPredicate : NSObject, NSSecureCoding, NSCopying {
}

public required init?(coder aDecoder: NSCoder) {
NSUnimplemented()
guard aDecoder.allowsKeyedCoding else {
preconditionFailure("Unkeyed coding is unsupported.")
}

let encodedBool = aDecoder.decodeBool(forKey: "NS.boolean.value")
self.kind = .boolean(encodedBool)

super.init()
}

open func encode(with aCoder: NSCoder) {
NSUnimplemented()
guard aCoder.allowsKeyedCoding else {
preconditionFailure("Unkeyed coding is unsupported.")
}

//TODO: store kind key for .boolean, .format, .metadataQuery

switch self.kind {
case .boolean(let value):
aCoder.encode(value, forKey: "NS.boolean.value")
case .block:
preconditionFailure("NSBlockPredicate cannot be encoded or decoded.")
case .format:
NSUnimplemented()
case .metadataQuery:
NSUnimplemented()
}
}

open override func copy() -> Any {
return copy(with: nil)
}

open func copy(with zone: NSZone? = nil) -> Any {
NSUnimplemented()
switch self.kind {
case .boolean(let value):
return NSPredicate(value: value)
case .block(let block):
return NSPredicate(block: block)
case .format:
NSUnimplemented()
case .metadataQuery:
NSUnimplemented()
}
}

open override func isEqual(_ object: Any?) -> Bool {
if let other = object as? NSPredicate {
if other === self {
return true
} else {
switch (other.kind, self.kind) {
case (.boolean(let otherBool), .boolean(let selfBool)):
return otherBool == selfBool
case (.format, .format):
NSUnimplemented()
case (.metadataQuery, .metadataQuery):
NSUnimplemented()
default:
// NSBlockPredicate returns false even for copy
return false
}
}
}

return false
}

// Parse predicateFormat and return an appropriate predicate
Expand All @@ -58,7 +111,21 @@ open class NSPredicate : NSObject, NSSecureCoding, NSCopying {
super.init()
}

open var predicateFormat: String { NSUnimplemented() } // returns the format string of the predicate
open var predicateFormat: String {
switch self.kind {
case .boolean(let value):
return value ? "TRUEPREDICATE" : "FALSEPREDICATE"
case .block:
// TODO: Bring NSBlockPredicate's predicateFormat to macOS's Foundation version
// let address = unsafeBitCast(block, to: Int.self)
// return String(format:"BLOCKPREDICATE(%2X)", address)
return "BLOCKPREDICATE"
case .format:
NSUnimplemented()
case .metadataQuery:
NSUnimplemented()
}
}

open func withSubstitutionVariables(_ variables: [String : Any]) -> Self { NSUnimplemented() } // substitute constant values for variables

Expand All @@ -76,6 +143,10 @@ open class NSPredicate : NSObject, NSSecureCoding, NSCopying {
return value
case let .block(block):
return block(object, bindings)
case .format:
NSUnimplemented()
case .metadataQuery:
NSUnimplemented()
}
} // single pass evaluation substituting variables from the bindings dictionary for any variable expressions encountered

Expand Down
13 changes: 13 additions & 0 deletions TestFoundation/TestNSPredicate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ class TestNSPredicate: XCTestCase {
("test_filterNSMutableSet", test_filterNSMutableSet),
("test_filterNSOrderedSet", test_filterNSOrderedSet),
("test_filterNSMutableOrderedSet", test_filterNSMutableOrderedSet),
("test_NSCoding", test_NSCoding),
("test_copy", test_copy),
]
}

Expand Down Expand Up @@ -94,4 +96,15 @@ class TestNSPredicate: XCTestCase {
expectedOrderedSet.addObjects(from: expectedArray)
XCTAssertEqual(expectedOrderedSet, orderedSet)
}

func test_NSCoding() {
let predicateA = NSPredicate(value: true)
let predicateB = NSKeyedUnarchiver.unarchiveObject(with: NSKeyedArchiver.archivedData(withRootObject: predicateA)) as! NSPredicate
XCTAssertEqual(predicateA, predicateB, "Archived then unarchived uuid must be equal.")
}

func test_copy() {
let predicate = NSPredicate(value: true)
XCTAssert(predicate.isEqual(predicate.copy()))
}
}