1
+ import Foundation
2
+
1
3
/// A structure that wraps the underlying data for a ``SelectionSet``.
2
4
public struct DataDict : Hashable {
3
5
@usableFromInline var _storage : _Storage
@@ -51,11 +53,16 @@ public struct DataDict: Hashable {
51
53
52
54
@inlinable public subscript< T: AnyScalarType & Hashable > ( _ key: String ) -> T {
53
55
get {
54
- #if swift(>=5.4)
55
- _data [ key] as! T
56
- #else
57
- _data [ key] ? . base as! T
58
- #endif
56
+ if DataDict . _AnyHashableCanBeCoerced {
57
+ return _data [ key] as! T
58
+ } else {
59
+ let value = _data [ key]
60
+ if value == DataDict . _NullValue {
61
+ return ( Optional < T > . none as Any ) as! T
62
+ } else {
63
+ return ( value? . base as? T ) ?? ( value. _asAnyHashable as! T )
64
+ }
65
+ }
59
66
}
60
67
set {
61
68
_data [ key] = newValue
@@ -126,6 +133,34 @@ public struct DataDict: Hashable {
126
133
}
127
134
}
128
135
136
+ // MARK: - Null Value Definition
137
+ extension DataDict {
138
+ /// A common value used to represent a null value in a `DataDict`.
139
+ ///
140
+ /// This value can be cast to `NSNull` and will bridge automatically.
141
+ public static let _NullValue = {
142
+ if DataDict . _AnyHashableCanBeCoerced {
143
+ return AnyHashable ( Optional< AnyHashable> . none)
144
+ } else {
145
+ return NSNull ( )
146
+ }
147
+ } ( )
148
+
149
+ /// Indicates if `AnyHashable` can be coerced via casting into its underlying type.
150
+ ///
151
+ /// In iOS versions 14.4 and lower, `AnyHashable` coercion does not work. On these platforms,
152
+ /// we need to do some additional unwrapping and casting of the values to avoid crashes and other
153
+ /// run time bugs.
154
+ public static var _AnyHashableCanBeCoerced : Bool {
155
+ if #available( iOS 14 . 5 , * ) {
156
+ return true
157
+ } else {
158
+ return false
159
+ }
160
+ }
161
+
162
+ }
163
+
129
164
// MARK: - Value Conversion Helpers
130
165
131
166
public protocol SelectionSetEntityValue {
@@ -160,11 +195,13 @@ extension Optional: SelectionSetEntityValue where Wrapped: SelectionSetEntityVal
160
195
case . none:
161
196
self = . none
162
197
case . some( let hashable) :
163
- if let optional = hashable. base as? Optional < AnyHashable > , optional == nil {
164
- self = . none
165
- return
166
- }
198
+ if DataDict . _AnyHashableCanBeCoerced && hashable == DataDict . _NullValue {
199
+ self = . none
200
+ } else if let optional = hashable. base as? Optional < AnyHashable > , optional == nil {
201
+ self = . none
202
+ } else {
167
203
self = . some( Wrapped . init ( _fieldData: data) )
204
+ }
168
205
}
169
206
}
170
207
@@ -179,11 +216,11 @@ extension Array: SelectionSetEntityValue where Element: SelectionSetEntityValue
179
216
fatalError ( " \( Self . self) expected list of data for entity. " )
180
217
}
181
218
self = data. map {
182
- # if swift(>=5.4)
183
- Element . init ( _fieldData: $0)
184
- # else
185
- Element . init ( _fieldData: $0? . base as? AnyHashable )
186
- #endif
219
+ if DataDict . _AnyHashableCanBeCoerced {
220
+ return Element . init ( _fieldData: $0)
221
+ } else {
222
+ return Element . init ( _fieldData: $0? . base as? AnyHashable )
223
+ }
187
224
}
188
225
}
189
226
0 commit comments