@@ -27,22 +27,85 @@ extension NSAttributedString {
27
27
}
28
28
}
29
29
30
+ extension NSAttributedString . Key : _ObjectiveCBridgeable {
31
+ public func _bridgeToObjectiveC( ) -> NSString {
32
+ return rawValue as NSString
33
+ }
34
+
35
+ public static func _forceBridgeFromObjectiveC( _ source: NSString , result: inout NSAttributedString . Key ? ) {
36
+ result = NSAttributedString . Key ( source as String )
37
+ }
38
+
39
+ public static func _conditionallyBridgeFromObjectiveC( _ source: NSString , result: inout NSAttributedString . Key ? ) -> Bool {
40
+ result = NSAttributedString . Key ( source as String )
41
+ return true
42
+ }
43
+
44
+ public static func _unconditionallyBridgeFromObjectiveC( _ source: NSString ? ) -> NSAttributedString . Key {
45
+ guard let source = source else { return NSAttributedString . Key ( " " ) }
46
+ return NSAttributedString . Key ( source as String )
47
+ }
48
+ }
49
+
30
50
@available ( * , unavailable, renamed: " NSAttributedString.Key " )
31
51
public typealias NSAttributedStringKey = NSAttributedString . Key
32
52
33
-
34
53
open class NSAttributedString : NSObject , NSCopying , NSMutableCopying , NSSecureCoding {
35
54
36
55
private let _cfinfo = _CFInfo ( typeID: CFAttributedStringGetTypeID ( ) )
37
56
fileprivate var _string : NSString
38
57
fileprivate var _attributeArray : CFRunArrayRef
39
58
40
59
public required init ? ( coder aDecoder: NSCoder ) {
41
- NSUnimplemented ( )
60
+ let mutableAttributedString = NSMutableAttributedString ( string: " " )
61
+ guard _NSReadMutableAttributedStringWithCoder ( aDecoder, mutableAttributedString: mutableAttributedString) else {
62
+ return nil
63
+ }
64
+
65
+ // use the resulting _string and _attributeArray to initialize a new instance, just like init
66
+ _string = mutableAttributedString. _string
67
+ _attributeArray = mutableAttributedString. _attributeArray
42
68
}
43
69
44
70
open func encode( with aCoder: NSCoder ) {
45
- NSUnimplemented ( )
71
+ guard aCoder. allowsKeyedCoding else { fatalError ( " We do not support saving to a non-keyed coder. " ) }
72
+
73
+ aCoder. encode ( string, forKey: " NSString " )
74
+ let length = self . length
75
+
76
+ if length > 0 {
77
+ var range = NSMakeRange ( NSNotFound, NSNotFound)
78
+ var loc = 0
79
+ var dict = attributes ( at: loc, effectiveRange: & range) as NSDictionary
80
+ if range. length == length {
81
+ // Special single-attribute run case
82
+ // If NSAttributeInfo is not written, then NSAttributes is a dictionary
83
+ aCoder. encode ( dict, forKey: " NSAttributes " )
84
+ } else {
85
+ let attrsArray = NSMutableArray ( capacity: 20 )
86
+ let data = NSMutableData ( capacity: 100 ) ?? NSMutableData ( )
87
+ let attrsTable = NSMutableDictionary ( )
88
+ while true {
89
+ var arraySlot = 0
90
+ if let cachedSlot = attrsTable. object ( forKey: dict) as? Int {
91
+ arraySlot = cachedSlot
92
+ } else {
93
+ arraySlot = attrsArray. count
94
+ attrsTable. setObject ( arraySlot, forKey: dict)
95
+ attrsArray. add ( dict)
96
+ }
97
+
98
+ _NSWriteIntToMutableAttributedStringCoding ( range. length, data)
99
+ _NSWriteIntToMutableAttributedStringCoding ( arraySlot, data)
100
+
101
+ loc += range. length
102
+ guard loc < length else { break }
103
+ dict = attributes ( at: loc, effectiveRange: & range) as NSDictionary
104
+ }
105
+ aCoder. encode ( attrsArray, forKey: " NSAttributes " )
106
+ aCoder. encode ( data, forKey: " NSAttributeInfo " )
107
+ }
108
+ }
46
109
}
47
110
48
111
static public var supportsSecureCoding : Bool {
@@ -117,6 +180,11 @@ open class NSAttributedString: NSObject, NSCopying, NSMutableCopying, NSSecureCo
117
180
return _attribute ( attrName, atIndex: location, rangeInfo: rangeInfo)
118
181
}
119
182
183
+ open override func isEqual( _ object: Any ? ) -> Bool {
184
+ guard let other = object as? NSAttributedString else { return false }
185
+ return isEqual ( to: other)
186
+ }
187
+
120
188
/// Returns a Boolean value that indicates whether the receiver is equal to another given attributed string.
121
189
open func isEqual( to other: NSAttributedString ) -> Bool {
122
190
guard let runtimeClass = _CFRuntimeGetClassWithTypeID ( CFAttributedStringGetTypeID ( ) ) else {
@@ -412,7 +480,13 @@ open class NSMutableAttributedString : NSAttributedString {
412
480
}
413
481
414
482
public required init ? ( coder aDecoder: NSCoder ) {
415
- NSUnimplemented ( )
483
+ let mutableAttributedString = NSMutableAttributedString ( string: " " )
484
+ guard _NSReadMutableAttributedStringWithCoder ( aDecoder, mutableAttributedString: mutableAttributedString) else {
485
+ return nil
486
+ }
487
+
488
+ super. init ( attributedString: mutableAttributedString)
489
+ _string = NSMutableString ( string: mutableAttributedString. string)
416
490
}
417
491
418
492
}
@@ -431,3 +505,117 @@ private extension NSMutableAttributedString {
431
505
return attributesDictionary. _cfObject
432
506
}
433
507
}
508
+
509
+ // MARK: Coding
510
+
511
+ fileprivate let _allowedCodingClasses : [ AnyClass ] = [
512
+ NSNumber . self,
513
+ NSArray . self,
514
+ NSDictionary . self,
515
+ NSURL . self,
516
+ NSString . self,
517
+ ]
518
+
519
+ internal func _NSReadIntFromMutableAttributedStringCoding( _ data: NSData , _ startingOffset: Int ) -> ( value: Int , newOffset: Int ) ? {
520
+ var multiplier = 1
521
+ var offset = startingOffset
522
+ let length = data. length
523
+
524
+ var value = 0
525
+
526
+ while offset < length {
527
+ let i = Int ( data. bytes. load ( fromByteOffset: offset, as: UInt8 . self) )
528
+
529
+ offset += 1
530
+
531
+ let isLast = i < 128
532
+
533
+ let intermediateValue = multiplier. multipliedReportingOverflow ( by: isLast ? i : ( i - 128 ) )
534
+ guard !intermediateValue. overflow else { return nil }
535
+
536
+ let newValue = value. addingReportingOverflow ( intermediateValue. partialValue)
537
+ guard !newValue. overflow else { return nil }
538
+
539
+ value = newValue. partialValue
540
+
541
+ if isLast {
542
+ return ( value: value, newOffset: offset)
543
+ }
544
+
545
+ multiplier *= 128
546
+ }
547
+
548
+ return nil // Getting to the end of the stream indicates error, since we were still expecting more bytes
549
+ }
550
+
551
+ internal func _NSWriteIntToMutableAttributedStringCoding( _ i: Int , _ data: NSMutableData ) {
552
+ if i > 127 {
553
+ let byte = UInt8 ( 128 + i % 128 ) ;
554
+ data. append ( Data ( [ byte] ) )
555
+ _NSWriteIntToMutableAttributedStringCoding ( i / 128 , data)
556
+ } else {
557
+ data. append ( Data ( [ UInt8 ( i) ] ) )
558
+ }
559
+ }
560
+
561
+ internal func _NSReadMutableAttributedStringWithCoder( _ decoder: NSCoder , mutableAttributedString: NSMutableAttributedString ) -> Bool {
562
+
563
+ // NSAttributedString.Key is not currently bridging correctly every time we'd like it to.
564
+ // Ensure we manually go through String in the meanwhile. SR-XXXX.
565
+ func toAttributesDictionary( _ ns: NSDictionary ) -> [ NSAttributedString . Key : Any ] ? {
566
+ if let bridged = __SwiftValue. fetch ( ns) as? [ String : Any ] {
567
+ return Dictionary ( bridged. map { ( NSAttributedString . Key ( $0. key) , $0. value) } , uniquingKeysWith: { $1 } )
568
+ } else {
569
+ return nil
570
+ }
571
+ }
572
+
573
+ guard decoder. allowsKeyedCoding else { /* Unkeyed unarchiving is not supported. */ return false }
574
+
575
+ let string = decoder. decodeObject ( of: NSString . self, forKey: " NSString " ) ?? " "
576
+
577
+ mutableAttributedString. replaceCharacters ( in: NSMakeRange ( 0 , 0 ) , with: string as String )
578
+
579
+ guard string. length > 0 else { return true }
580
+
581
+ var allowed = _allowedCodingClasses
582
+ for aClass in decoder. allowedClasses ?? [ ] {
583
+ if !allowed. contains ( where: { $0 === aClass } ) {
584
+ allowed. append ( aClass)
585
+ }
586
+ }
587
+
588
+ let attributes = decoder. decodeObject ( of: allowed, forKey: " NSAttributes " )
589
+ // If this is present, 'attributes' should be an array; otherwise, a dictionary:
590
+ let attrData = decoder. decodeObject ( of: NSData . self, forKey: " NSAttributeInfo " )
591
+ if attrData == nil , let attributes = attributes as? [ NSAttributedString . Key : Any ] {
592
+ mutableAttributedString. setAttributes ( attributes, range: NSMakeRange ( 0 , string. length) )
593
+ return true
594
+ } else if let attrData = attrData, let attributesNS = attributes as? [ NSDictionary ] {
595
+ let attributes = attributesNS. compactMap { toAttributesDictionary ( $0) }
596
+ guard attributes. count == attributesNS. count else { return false }
597
+
598
+ var loc = 0
599
+ var offset = 0
600
+ let length = string. length
601
+ while loc < length {
602
+ var rangeLen = 0 , arraySlot = 0
603
+ guard let intResult1 = _NSReadIntFromMutableAttributedStringCoding ( attrData, offset) else { return false }
604
+ rangeLen = intResult1. value
605
+ offset = intResult1. newOffset
606
+
607
+ guard let intResult2 = _NSReadIntFromMutableAttributedStringCoding ( attrData, offset) else { return false }
608
+ arraySlot = intResult2. value
609
+ offset = intResult2. newOffset
610
+
611
+ guard arraySlot < attributes. count else { return false }
612
+ mutableAttributedString. setAttributes ( attributes [ arraySlot] , range: NSMakeRange ( loc, rangeLen) )
613
+
614
+ loc += rangeLen
615
+ }
616
+
617
+ return true
618
+ }
619
+
620
+ return false
621
+ }
0 commit comments