Skip to content

Commit dd396f1

Browse files
authored
Merge pull request #2234 from millenomi/nssortdescriptor
Parity: NSSortDescriptor & _CFSwiftArrayReplaceValues
2 parents afe0d3f + 4d511b1 commit dd396f1

File tree

14 files changed

+402
-51
lines changed

14 files changed

+402
-51
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ if(ENABLE_TESTING)
480480
TestFoundation/TestNSRange.swift
481481
TestFoundation/TestNSRegularExpression.swift
482482
TestFoundation/TestNSSet.swift
483+
TestFoundation/TestNSSortDescriptor.swift
483484
TestFoundation/TestNSString.swift
484485
TestFoundation/TestNSTextCheckingResult.swift
485486
TestFoundation/TestNSURLRequest.swift

CoreFoundation/Base.subproj/ForSwiftFoundationOnly.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ struct _NSArrayBridge {
9494
CFIndex (*_Nonnull count)(CFTypeRef obj);
9595
_Nonnull CFTypeRef (*_Nonnull objectAtIndex)(CFTypeRef obj, CFIndex index);
9696
void (*_Nonnull getObjects)(CFTypeRef array, CFRange range, CFTypeRef _Nullable *_Nonnull values);
97+
Boolean (*_Nonnull isSubclassOfNSMutableArray)(CFTypeRef array);
9798
};
9899

99100
struct _NSMutableArrayBridge {

CoreFoundation/Collections.subproj/CFArray.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -966,9 +966,12 @@ void CFArraySortValues(CFMutableArrayRef array, CFRange range, CFComparatorFunct
966966
__CFArrayValidateRange(array, range, __PRETTY_FUNCTION__);
967967
CFAssert1(NULL != comparator, __kCFLogAssertion, "%s(): pointer to comparator function may not be NULL", __PRETTY_FUNCTION__);
968968
Boolean immutable = false;
969-
if (CF_IS_OBJC(CFArrayGetTypeID(), array) || CF_IS_SWIFT(CFArrayGetTypeID(), array)) {
969+
if (CF_IS_OBJC(CFArrayGetTypeID(), array)) {
970970
BOOL result;
971-
result = CF_OBJC_CALLV((NSMutableArray *)array, isKindOfClass:[NSMutableArray class]); // TODO: Fixme for swift (we need a isKindOfClass replacement: (array as? NSMutableArray) != nil)
971+
result = CF_OBJC_CALLV((NSMutableArray *)array, isKindOfClass:[NSMutableArray class]);
972+
immutable = !result;
973+
} else if (CF_IS_SWIFT(CFArrayGetTypeID(), array)) {
974+
Boolean result = __CFSwiftBridge.NSArray.isSubclassOfNSMutableArray(array);
972975
immutable = !result;
973976
} else if (__kCFArrayImmutable == __CFArrayGetType(array)) {
974977
immutable = true;

Foundation.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
03B6F5841F15F339004F25AF /* TestURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 03B6F5831F15F339004F25AF /* TestURLProtocol.swift */; };
1212
1513A8432044893F00539722 /* FileManager_XDG.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1513A8422044893F00539722 /* FileManager_XDG.swift */; };
1313
1520469B1D8AEABE00D02E36 /* HTTPServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1520469A1D8AEABE00D02E36 /* HTTPServer.swift */; };
14+
152EF3942283457C001E1269 /* TestNSSortDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 152EF3932283457B001E1269 /* TestNSSortDescriptor.swift */; };
1415
153CC8352215E00200BFE8F3 /* ScannerAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 153CC8322214C3D100BFE8F3 /* ScannerAPI.swift */; };
1516
153E951120111DC500F250BE /* CFKnownLocations.h in Headers */ = {isa = PBXBuildFile; fileRef = 153E950F20111DC500F250BE /* CFKnownLocations.h */; settings = {ATTRIBUTES = (Private, ); }; };
1617
153E951220111DC500F250BE /* CFKnownLocations.c in Sources */ = {isa = PBXBuildFile; fileRef = 153E951020111DC500F250BE /* CFKnownLocations.c */; };
@@ -556,6 +557,7 @@
556557
03B6F5831F15F339004F25AF /* TestURLProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestURLProtocol.swift; sourceTree = "<group>"; };
557558
1513A8422044893F00539722 /* FileManager_XDG.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManager_XDG.swift; sourceTree = "<group>"; };
558559
1520469A1D8AEABE00D02E36 /* HTTPServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPServer.swift; sourceTree = "<group>"; };
560+
152EF3932283457B001E1269 /* TestNSSortDescriptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestNSSortDescriptor.swift; sourceTree = "<group>"; };
559561
153CC8322214C3D100BFE8F3 /* ScannerAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScannerAPI.swift; sourceTree = "<group>"; };
560562
153E950F20111DC500F250BE /* CFKnownLocations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CFKnownLocations.h; sourceTree = "<group>"; };
561563
153E951020111DC500F250BE /* CFKnownLocations.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = CFKnownLocations.c; sourceTree = "<group>"; };
@@ -1641,6 +1643,7 @@
16411643
EA66F6401BF1619600136161 /* TestPropertyListSerialization.swift */,
16421644
E876A73D1C1180E000F279EC /* TestNSRange.swift */,
16431645
5B0C6C211C1E07E600705A0E /* TestNSRegularExpression.swift */,
1646+
152EF3932283457B001E1269 /* TestNSSortDescriptor.swift */,
16441647
61E0117B1C1B554D000037DD /* TestRunLoop.swift */,
16451648
844DC3321C17584F005611F9 /* TestScanner.swift */,
16461649
EA66F6411BF1619600136161 /* TestNSSet.swift */,
@@ -2624,6 +2627,7 @@
26242627
B90C57BB1EEEEA5A005208AE /* TestFileManager.swift in Sources */,
26252628
A058C2021E529CF100B07AA1 /* TestMassFormatter.swift in Sources */,
26262629
5B13B3381C582D4C00651CE2 /* TestNotificationQueue.swift in Sources */,
2630+
152EF3942283457C001E1269 /* TestNSSortDescriptor.swift in Sources */,
26272631
CC5249C01D341D23007CB54D /* TestUnitConverter.swift in Sources */,
26282632
5B13B3331C582D4C00651CE2 /* TestJSONSerialization.swift in Sources */,
26292633
5B13B33C1C582D4C00651CE2 /* TestNSOrderedSet.swift in Sources */,

Foundation/NSArray.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,12 @@ open class NSArray : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSCo
683683
override open var _cfTypeID: CFTypeID {
684684
return CFArrayGetTypeID()
685685
}
686+
687+
open func sortedArray(using sortDescriptors: [NSSortDescriptor]) -> [Any] {
688+
let copied = mutableCopy() as! NSMutableArray
689+
copied.sort(using: sortDescriptors)
690+
return copied._swiftObject
691+
}
686692
}
687693

688694
extension NSArray : _CFBridgeable, _SwiftBridgeable {
@@ -939,6 +945,28 @@ open class NSMutableArray : NSArray {
939945
open func sort(options opts: NSSortOptions = [], usingComparator cmptr: Comparator) {
940946
self.setArray(self.sortedArray(options: opts, usingComparator: cmptr))
941947
}
948+
949+
open func sort(using sortDescriptors: [NSSortDescriptor]) {
950+
var descriptors = sortDescriptors._nsObject
951+
CFArraySortValues(_cfMutableObject, CFRangeMake(0, count), { (lhsPointer, rhsPointer, context) -> CFComparisonResult in
952+
let descriptors = context!.assumingMemoryBound(to: NSArray.self).pointee._swiftObject
953+
954+
for item in descriptors {
955+
let descriptor = item as! NSSortDescriptor
956+
let result =
957+
descriptor.compare(__SwiftValue.fetch(Unmanaged<AnyObject>.fromOpaque(lhsPointer!).takeUnretainedValue())!,
958+
to: __SwiftValue.fetch(Unmanaged<AnyObject>.fromOpaque(rhsPointer!).takeUnretainedValue())!)
959+
960+
if result == .orderedAscending {
961+
return kCFCompareLessThan
962+
} else if result == .orderedDescending {
963+
return kCFCompareGreaterThan
964+
}
965+
}
966+
967+
return kCFCompareEqualTo
968+
}, &descriptors)
969+
}
942970
}
943971

944972
extension NSArray : Sequence {

Foundation/NSCFArray.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,11 @@ internal func _CFSwiftArrayRemoveAllValues(_ array: AnyObject) {
125125
}
126126

127127
internal func _CFSwiftArrayReplaceValues(_ array: AnyObject, _ range: CFRange, _ newValues: UnsafeMutablePointer<Unmanaged<AnyObject>>, _ newCount: CFIndex) {
128-
NSUnimplemented()
129-
// (array as! NSMutableArray).replaceObjectsInRange(NSRange(location: range.location, length: range.length), withObjectsFrom: newValues.array(newCount))
128+
let buffer: UnsafeBufferPointer<Unmanaged<AnyObject>> = UnsafeBufferPointer(start: newValues, count: newCount)
129+
let replacements = Array(buffer).map { $0.takeUnretainedValue() }
130+
(array as! NSMutableArray).replaceObjects(in: NSRange(location: range.location, length: range.length), withObjectsFrom: replacements)
131+
}
132+
133+
internal func _CFSwiftArrayIsSubclassOfNSMutableArray(_ array: AnyObject) -> _DarwinCompatibleBoolean {
134+
return array is NSMutableArray ? true : false
130135
}

Foundation/NSDate.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,3 +427,10 @@ extension NSDateInterval : _StructTypeBridgeable {
427427
return DateInterval._unconditionallyBridgeFromObjectiveC(self)
428428
}
429429
}
430+
431+
extension NSDateInterval : _SwiftBridgeable {
432+
var _swiftObject: DateInterval {
433+
return _bridgeToSwift()
434+
}
435+
}
436+

Foundation/NSIndexPath.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,10 @@ extension NSIndexPath : _StructTypeBridgeable {
120120
return IndexPath._unconditionallyBridgeFromObjectiveC(self)
121121
}
122122
}
123+
124+
extension NSIndexPath : _SwiftBridgeable {
125+
var _swiftObject: IndexPath {
126+
return _bridgeToSwift()
127+
}
128+
}
129+

Foundation/NSOrderedSet.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ open class NSOrderedSet: NSObject, NSCopying, NSMutableCopying, NSSecureCoding,
255255
// being made.
256256
public var array: [Any] {
257257
if type(of: self) === NSOrderedSet.self || type(of: self) === NSMutableOrderedSet.self {
258-
return _orderedStorage._storage
258+
return _orderedStorage._swiftObject
259259
} else {
260260
var result: [Any] = []
261261
result.reserveCapacity(self.count)
@@ -268,7 +268,7 @@ open class NSOrderedSet: NSObject, NSCopying, NSMutableCopying, NSSecureCoding,
268268

269269
public var set: Set<AnyHashable> {
270270
if type(of: self) === NSOrderedSet.self || type(of: self) === NSMutableOrderedSet.self {
271-
return _storage._storage
271+
return _storage._swiftObject
272272
} else {
273273
var result: Set<AnyHashable> = []
274274
result.reserveCapacity(self.count)
@@ -392,6 +392,10 @@ open class NSOrderedSet: NSObject, NSCopying, NSMutableCopying, NSSecureCoding,
392392
public convenience init(set: Set<AnyHashable>, copyItems flag: Bool) {
393393
self.init(array: Array(set), copyItems: flag)
394394
}
395+
396+
open func sortedArray(using sortDescriptors: [NSSortDescriptor]) -> [Any] {
397+
return self.array._nsObject.sortedArray(using: sortDescriptors)
398+
}
395399
}
396400

397401

@@ -664,6 +668,10 @@ open class NSMutableOrderedSet: NSOrderedSet {
664668
let sortedSubrange = _mutableOrderedStorage.sortedArray(from: range, options: opts, usingComparator: cmptr)
665669
_mutableOrderedStorage.replaceObjects(in: range, withObjectsFrom: sortedSubrange)
666670
}
671+
672+
open func sort(using sortDescriptors: [NSSortDescriptor]) {
673+
_mutableOrderedStorage.sort(using: sortDescriptors)
674+
}
667675
}
668676

669677

Foundation/NSSet.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,10 @@ open class NSSet : NSObject, NSCopying, NSMutableCopying, NSSecureCoding, NSCodi
320320
}
321321
return result
322322
}
323+
324+
open func sortedArray(using sortDescriptors: [NSSortDescriptor]) -> [Any] {
325+
return allObjects._nsObject.sortedArray(using: sortDescriptors)
326+
}
323327
}
324328

325329
extension NSSet : _CFBridgeable, _SwiftBridgeable {

Foundation/NSSortDescriptor.swift

Lines changed: 91 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -7,72 +7,118 @@
77
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
88
//
99

10+
import CoreFoundation
1011

11-
open class NSSortDescriptor: NSObject, NSSecureCoding, NSCopying {
12-
13-
public required init?(coder aDecoder: NSCoder) {
14-
NSUnimplemented()
15-
}
16-
17-
open func encode(with aCoder: NSCoder) {
18-
NSUnimplemented()
19-
}
20-
21-
static public var supportsSecureCoding: Bool {
22-
return true
23-
}
24-
12+
// In swift-corelibs-foundation, key-value coding is not available. Since encoding and decoding a NSSortDescriptor requires interpreting key paths, NSSortDescriptor does not conform to NSCoding or NSSecureCoding in swift-corelibs-foundation only.
13+
open class NSSortDescriptor: NSObject, NSCopying {
2514
open override func copy() -> Any {
2615
return copy(with: nil)
2716
}
2817

2918
open func copy(with zone: NSZone? = nil) -> Any {
30-
NSUnimplemented()
19+
return self
3120
}
3221

33-
// keys may be key paths
34-
public init(key: String?, ascending: Bool) { NSUnimplemented() }
22+
@available(*, unavailable, message: "Key-value coding is not available in swift-corelibs-foundation. Use Swift key paths instead with init(keyPath:ascending:) or init(keyPath:ascending:comparator:)", renamed: "init(keyPath:ascending:)")
23+
public init(key: String?, ascending: Bool) { NSUnsupported() }
3524

36-
open var key: String? { NSUnimplemented() }
37-
open var ascending: Bool { NSUnimplemented() }
38-
public var keyPath: AnyKeyPath? { NSUnimplemented() }
25+
@available(*, unavailable, message: "Key-value coding is not available in swift-corelibs-foundation. Use Swift key paths instead with init(keyPath:ascending:) or init(keyPath:ascending:comparator:)", renamed: "init(keyPath:ascending:)")
26+
public init(key: String?, ascending: Bool, comparator cmptr: Comparator) { NSUnsupported() }
3927

40-
open func allowEvaluation() { NSUnimplemented() } // Force a sort descriptor which was securely decoded to allow evaluation
4128

42-
public init(key: String?, ascending: Bool, comparator cmptr: Comparator) { NSUnimplemented() }
43-
public convenience init<Root, Value>(keyPath: KeyPath<Root, Value>, ascending: Bool) { NSUnimplemented() }
44-
public convenience init<Root, Value>(keyPath: KeyPath<Root, Value>, ascending: Bool, comparator cmptr: @escaping Comparator) { NSUnimplemented() }
29+
@available(*, unavailable, message: "Key-value coding is not available in swift-corelibs-foundation. Use .keyPath instead.", renamed: "keyPath")
30+
open var key: String? { NSUnsupported() }
4531

46-
open var comparator: Comparator { NSUnimplemented() }
32+
@available(*, unavailable, message: "Sort descriptors cannot be decoded from archives in swift-corelibs-foundation.")
33+
open func allowEvaluation() {}
4734

48-
open func compare(_ object1: Any, to object2: Any) -> ComparisonResult { NSUnimplemented() }// primitive - override this method if you want to perform comparisons differently (not key based for example)
49-
open var reversedSortDescriptor: Any { NSUnimplemented() } // primitive - override this method to return a sort descriptor instance with reversed sort order
50-
}
51-
52-
extension NSSet {
35+
// MARK: Available parts.
36+
37+
public init<Root, Value: Comparable>(keyPath: KeyPath<Root, Value>, ascending: Bool) {
38+
self.keyPath = keyPath
39+
self.ascending = ascending
40+
self.lens = { ($0 as! Root)[keyPath: keyPath] }
41+
self.comparator = { (a, b) -> ComparisonResult in
42+
let valueA = a as! Value
43+
let valueB = b as! Value
44+
45+
if valueA < valueB {
46+
return .orderedAscending
47+
} else if valueB < valueA {
48+
return .orderedDescending
49+
} else {
50+
return .orderedSame
51+
}
52+
}
53+
self.reversedSortDescriptorProducer = { return NSSortDescriptor(keyPath: keyPath, ascending: !ascending) }
54+
}
55+
56+
public init<Root, Value>(keyPath: KeyPath<Root, Value>, ascending: Bool, comparator cmptr: @escaping Comparator) {
57+
self.keyPath = keyPath
58+
self.ascending = ascending
59+
self.lens = { ($0 as! Root)[keyPath: keyPath] }
60+
self.comparator = cmptr
61+
self.reversedSortDescriptorProducer = { return NSSortDescriptor(keyPath: keyPath, ascending: !ascending, comparator: cmptr) }
62+
}
63+
64+
private let lens: (Any) -> Any
65+
private let reversedSortDescriptorProducer: () -> NSSortDescriptor
5366

54-
public func sortedArray(using sortDescriptors: [NSSortDescriptor]) -> [Any] { NSUnimplemented() }// returns a new array by sorting the objects of the receiver
67+
open private(set) var ascending: Bool
68+
open private(set) var keyPath: AnyKeyPath
69+
open private(set) var comparator: Comparator
70+
71+
// primitive - override this method if you want to perform comparisons differently (not key based for example)
72+
open func compare(_ object1: Any, to object2: Any) -> ComparisonResult {
73+
let lhs = lens(object1)
74+
let rhs = lens(object2)
75+
let result = comparator(lhs, rhs)
76+
77+
let actualResult: ComparisonResult
78+
79+
if ascending {
80+
actualResult = result
81+
} else {
82+
switch result {
83+
case .orderedAscending: actualResult = .orderedDescending
84+
case .orderedDescending: actualResult = .orderedAscending
85+
case .orderedSame: actualResult = .orderedSame
86+
}
87+
}
88+
89+
return actualResult
90+
}
91+
open var reversedSortDescriptor: Any {
92+
return reversedSortDescriptorProducer()
93+
}
5594
}
5695

57-
extension NSArray {
58-
59-
public func sortedArray(using sortDescriptors: [NSSortDescriptor]) -> [Any] { NSUnimplemented() }// returns a new array by sorting the objects of the receiver
96+
extension NSNumber: Comparable {
97+
public static func < (lhs: NSNumber, rhs: NSNumber) -> Bool {
98+
return lhs.compare(rhs) == .orderedAscending
99+
}
60100
}
61101

62-
extension NSMutableArray {
63-
64-
public func sort(using sortDescriptors: [NSSortDescriptor]) { NSUnimplemented() } // sorts the array itself
102+
extension NSString: Comparable {
103+
public static func < (lhs: NSString, rhs: NSString) -> Bool {
104+
return lhs.compare(rhs._swiftObject) == .orderedAscending
105+
}
65106
}
66107

108+
extension NSDateInterval: Comparable {
109+
public static func < (lhs: NSDateInterval, rhs: NSDateInterval) -> Bool {
110+
return lhs.compare(rhs._swiftObject) == .orderedAscending
111+
}
112+
}
67113

68-
extension NSOrderedSet {
69-
70-
// returns a new array by sorting the objects of the receiver
71-
public func sortedArray(using sortDescriptors: [NSSortDescriptor]) -> [Any] { NSUnimplemented() }
114+
extension NSDate: Comparable {
115+
public static func < (lhs: NSDate, rhs: NSDate) -> Bool {
116+
return lhs.compare(rhs._swiftObject) == .orderedAscending
117+
}
72118
}
73119

74-
extension NSMutableOrderedSet {
75-
76-
// sorts the ordered set itself
77-
public func sort(using sortDescriptors: [NSSortDescriptor]) { NSUnimplemented() }
120+
extension NSIndexPath: Comparable {
121+
public static func < (lhs: NSIndexPath, rhs: NSIndexPath) -> Bool {
122+
return lhs.compare(rhs._swiftObject) == .orderedAscending
123+
}
78124
}

Foundation/NSSwiftRuntime.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ internal func __CFInitializeSwift() {
199199
__CFSwiftBridge.NSArray.count = _CFSwiftArrayGetCount
200200
__CFSwiftBridge.NSArray.objectAtIndex = _CFSwiftArrayGetValueAtIndex
201201
__CFSwiftBridge.NSArray.getObjects = _CFSwiftArrayGetValues
202+
__CFSwiftBridge.NSArray.isSubclassOfNSMutableArray = _CFSwiftArrayIsSubclassOfNSMutableArray
202203

203204
__CFSwiftBridge.NSMutableArray.addObject = _CFSwiftArrayAppendValue
204205
__CFSwiftBridge.NSMutableArray.setObject = _CFSwiftArraySetValueAtIndex

0 commit comments

Comments
 (0)