@@ -146,6 +146,29 @@ struct SymbolGraphLoader {
146
146
147
147
self . symbolGraphs = loadedGraphs. mapValues ( \. graph)
148
148
( self . unifiedGraphs, self . graphLocations) = graphLoader. finishLoading ( )
149
+
150
+ for var unifiedGraph in unifiedGraphs. values {
151
+ var defaultUnavailablePlatforms = [ PlatformName] ( )
152
+ var defaultAvailableInformation = [ DefaultAvailability . ModuleAvailability] ( )
153
+
154
+ if let defaultAvailabilities = bundle. info. defaultAvailability? . modules [ unifiedGraph. moduleName] {
155
+ let ( unavailablePlatforms, availablePlatforms) = defaultAvailabilities. categorize ( where: { $0. versionInformation == . unavailable } )
156
+ defaultUnavailablePlatforms = unavailablePlatforms. map ( \. platformName)
157
+ defaultAvailableInformation = availablePlatforms
158
+ }
159
+
160
+ let platformsFoundInSymbolGraphs : [ PlatformName ] = unifiedGraph. moduleData. compactMap {
161
+ guard let platformName = $0. value. platform. name else { return nil }
162
+ return PlatformName ( operatingSystemName: platformName)
163
+ }
164
+
165
+ addMissingAvailability (
166
+ unifiedGraph: & unifiedGraph,
167
+ unconditionallyUnavailablePlatformNames: defaultUnavailablePlatforms,
168
+ registeredPlatforms: platformsFoundInSymbolGraphs,
169
+ defaultAvailabilities: defaultAvailableInformation
170
+ )
171
+ }
149
172
}
150
173
151
174
// Alias to declutter code
@@ -176,76 +199,105 @@ struct SymbolGraphLoader {
176
199
177
200
return ( symbolGraph, isMainSymbolGraph)
178
201
}
202
+
203
+ /// Adds the missing fallback and default availability information to the unified symbol graph
204
+ /// in case it didn't exists in the loaded symbol graphs.
205
+ private func addMissingAvailability(
206
+ unifiedGraph: inout UnifiedSymbolGraph ,
207
+ unconditionallyUnavailablePlatformNames: [ PlatformName ] ,
208
+ registeredPlatforms: [ PlatformName ] ,
209
+ defaultAvailabilities: [ DefaultAvailability . ModuleAvailability ]
210
+ ) {
211
+ // The fallback platforms that are missing from the unified graph correspond to
212
+ // the fallback platforms that have not been registered yet,
213
+ // are not marked as unavailable,
214
+ // and the corresponding inheritance platform has a SGF (has been registered).
215
+ let missingFallbackPlatforms = DefaultAvailability . fallbackPlatforms. filter {
216
+ !registeredPlatforms. contains ( $0. key) &&
217
+ !unconditionallyUnavailablePlatformNames. contains ( $0. key) &&
218
+ registeredPlatforms. contains ( $0. value)
219
+ }
220
+ // Platforms that are defined in the Info.plist that had no corresponding SGF
221
+ // and are not being added as fallback of another platform.
222
+ let missingAvailabilities = defaultAvailabilities. filter {
223
+ !missingFallbackPlatforms. keys. contains ( $0. platformName) &&
224
+ !registeredPlatforms. contains ( $0. platformName)
225
+ }
226
+
227
+ unifiedGraph. symbols. values. forEach { symbol in
228
+ for (selector, _) in symbol. mixins {
229
+ if var symbolAvailability = ( symbol. mixins [ selector] ? [ " availability " ] as? SymbolGraph . Symbol. Availability) {
230
+ guard !symbolAvailability. availability. isEmpty else { continue }
231
+ // Add fallback availability.
232
+ for (fallbackPlatform, inheritedPlatform) in missingFallbackPlatforms {
233
+ if !symbolAvailability. contains ( fallbackPlatform) {
234
+ for var fallbackAvailability in symbolAvailability. availability {
235
+ // Add the platform fallback to the availability mixin the platform is inheriting from.
236
+ // The added availability copies the entire availability information,
237
+ // including deprecated and obsolete versions.
238
+ if fallbackAvailability. matches ( inheritedPlatform) {
239
+ fallbackAvailability. domain = SymbolGraph . Symbol. Availability. Domain ( rawValue: fallbackPlatform. rawValue)
240
+ symbolAvailability. availability. append ( fallbackAvailability)
241
+ }
242
+ }
243
+ }
244
+ }
245
+ // Add the missing default platform availability.
246
+ missingAvailabilities. forEach { missingAvailability in
247
+ if !symbolAvailability. contains ( missingAvailability. platformName) {
248
+ guard let defaultAvailability = AvailabilityItem ( missingAvailability) else { return }
249
+ symbolAvailability. availability. append ( defaultAvailability)
250
+ }
251
+ }
252
+ symbol. mixins [ selector] ![ SymbolGraph . Symbol. Availability. mixinKey] = symbolAvailability
253
+ }
254
+ }
255
+ }
256
+ }
179
257
180
258
/// If the bundle defines default availability for the symbols in the given symbol graph
181
259
/// this method adds them to each of the symbols in the graph.
182
260
private func addDefaultAvailability( to symbolGraph: inout SymbolGraph , moduleName: String ) {
261
+ let selector = UnifiedSymbolGraph . Selector ( forSymbolGraph: symbolGraph)
183
262
// Check if there are defined default availabilities for the current module
184
263
if let defaultAvailabilities = bundle. info. defaultAvailability? . modules [ moduleName] ,
185
264
let platformName = symbolGraph. module. platform. name. map ( PlatformName . init) {
186
-
187
- // Prepare a default availability lookup for this module.
188
- let defaultAvailabilityIndex = defaultAvailabilities
189
- . reduce ( into: [ DefaultAvailability . ModuleAvailability: AvailabilityItem] ( ) , { result, defaultAvailability in
190
- result [ defaultAvailability] = AvailabilityItem ( defaultAvailability)
191
- } )
192
-
265
+
193
266
// Prepare a default availability versions lookup for this module.
194
267
let defaultAvailabilityVersionByPlatform = defaultAvailabilities
195
268
. reduce ( into: [ PlatformName: SymbolGraph . SemanticVersion] ( ) , { result, defaultAvailability in
196
- if let version = SymbolGraph . SemanticVersion ( string: defaultAvailability . platformVersion ) {
269
+ if let introducedVersion = defaultAvailability . introducedVersion , let version = SymbolGraph . SemanticVersion ( string: introducedVersion ) {
197
270
result [ defaultAvailability. platformName] = version
198
271
}
199
272
} )
200
273
201
- // In the case of Mac Catalyst use default availability for the iOS platform if annotated
202
- let fallbackPlatform = ( platformName == . catalyst) ? PlatformName . iOS. displayName : nil
203
-
204
- // `true` if this module has Mac Catalyst availability.
205
- let isDefaultCatalystAvailabilitySet = defaultAvailabilities. contains ( where: { $0. platformName == . catalyst } )
206
-
207
274
// Map all symbols and add default availability for any missing platforms
208
275
let symbolsWithFilledIntroducedVersions = symbolGraph. symbols. mapValues { symbol -> SymbolGraph . Symbol in
209
276
var symbol = symbol
210
-
277
+ let defaultModuleVersion = defaultAvailabilityVersionByPlatform [ platformName]
278
+ // The availability item for each symbol of the given module.
279
+ let modulePlatformAvailabilityItem = AvailabilityItem ( domain: SymbolGraph . Symbol. Availability. Domain ( rawValue: platformName. rawValue) , introducedVersion: defaultModuleVersion, deprecatedVersion: nil , obsoletedVersion: nil , message: nil , renamed: nil , isUnconditionallyDeprecated: false , isUnconditionallyUnavailable: false , willEventuallyBeDeprecated: false )
211
280
// Check if the symbol has existing availabilities from source
212
281
if var availability = symbol. mixins [ SymbolGraph . Symbol. Availability. mixinKey] as? SymbolGraph . Symbol . Availability {
213
282
214
283
// Fill introduced versions when missing.
215
- var newAvailabilityItems = availability. availability. map {
216
- $0. fillingMissingIntroducedVersion ( from: defaultAvailabilityVersionByPlatform, fallbackPlatform: fallbackPlatform)
284
+ availability. availability = availability. availability. map {
285
+ $0. fillingMissingIntroducedVersion (
286
+ from: defaultAvailabilityVersionByPlatform,
287
+ fallbackPlatform: DefaultAvailability . fallbackPlatforms [ platformName] ? . rawValue
288
+ )
217
289
}
218
-
219
- // When Catalyst is missing, fall back on iOS availability.
220
-
221
- // First check if we're targeting the Mac Catalyst platform
222
- if isDefaultCatalystAvailabilitySet,
223
- // Then verify annotated availability from source for Mac Catalyst is missing
224
- !newAvailabilityItems. contains ( where: { $0. domain? . rawValue == SymbolGraph . Symbol. Availability. Domain. macCatalyst } ) ,
225
- // And finally fetch the symbol's iOS availability if there is one
226
- let iOSAvailability = newAvailabilityItems. first ( where: { $0. domain? . rawValue == SymbolGraph . Symbol. Availability. Domain. iOS } ) {
227
-
228
- var macCatalystAvailability = iOSAvailability
229
- macCatalystAvailability. domain = SymbolGraph . Symbol. Availability. Domain ( rawValue: SymbolGraph . Symbol. Availability. Domain. macCatalyst)
230
- newAvailabilityItems. append ( macCatalystAvailability)
231
- }
232
-
233
- // If a symbol doesn't have any availability annotation at all
234
- // for a given platform, create a new one just with the
235
- // introduced version so that it shows up in the sidebar.
236
- for defaultAvailability in defaultAvailabilities {
237
- let hasAvailabilityForThisPlatform = newAvailabilityItems. contains {
238
- guard let domain = $0. domain else { return false }
239
- return PlatformName ( operatingSystemName: domain. rawValue) == defaultAvailability. platformName
240
- }
241
- if !hasAvailabilityForThisPlatform {
242
- // Safe to force unwrap below, the index contains all the avaialbility keys.
243
- newAvailabilityItems. append ( defaultAvailabilityIndex [ defaultAvailability] !)
244
- }
290
+ // Add the module availability information to each of the symbols availability mixin.
291
+ if !availability. contains ( platformName) {
292
+ availability. availability. append ( modulePlatformAvailabilityItem)
245
293
}
246
-
247
- availability. availability = newAvailabilityItems
248
294
symbol. mixins [ SymbolGraph . Symbol. Availability. mixinKey] = availability
295
+ } else {
296
+ // ObjC doesn't propagate symbol availability to their children properties,
297
+ // so only add the default availability to the Swift variant of the symbols.
298
+ if !( selector? . interfaceLanguage == InterfaceLanguage . objc. name. lowercased ( ) ) {
299
+ symbol. mixins [ SymbolGraph . Symbol. Availability. mixinKey] = SymbolGraph . Symbol. Availability ( availability: [ modulePlatformAvailabilityItem] )
300
+ }
249
301
}
250
302
return symbol
251
303
}
@@ -330,7 +382,7 @@ extension SymbolGraph.Symbol.Availability.AvailabilityItem {
330
382
/// - Note: If the `defaultAvailability` argument doesn't have a valid
331
383
/// platform version that can be parsed as a `SemanticVersion`, returns `nil`.
332
384
init ? ( _ defaultAvailability: DefaultAvailability . ModuleAvailability ) {
333
- guard let platformVersion = SymbolGraph . SemanticVersion ( string: defaultAvailability . platformVersion ) else {
385
+ guard let introducedVersion = defaultAvailability . introducedVersion , let platformVersion = SymbolGraph . SemanticVersion ( string: introducedVersion ) else {
334
386
return nil
335
387
}
336
388
let domain = SymbolGraph . Symbol. Availability. Domain ( rawValue: defaultAvailability. platformName. rawValue)
@@ -354,7 +406,6 @@ extension SymbolGraph.Symbol.Availability.AvailabilityItem {
354
406
355
407
- parameter defaults: Default module availabilities for each platform mentioned in a documentation bundle's `Info.plist`
356
408
- parameter fallbackPlatform: An optional fallback platform name if this item's domain isn't found in the `defaults`.
357
- For example, `macCatalyst` should fall back to `iOS` because `macCatalyst` symbols are originally `iOS` symbols.
358
409
*/
359
410
func fillingMissingIntroducedVersion( from defaults: [ PlatformName : SymbolGraph . SemanticVersion ] ,
360
411
fallbackPlatform: String ? ) -> SymbolGraph . Symbol . Availability . AvailabilityItem {
@@ -395,3 +446,15 @@ extension SymbolGraph.Symbol.Availability.AvailabilityItem {
395
446
return newValue
396
447
}
397
448
}
449
+
450
+ private extension SymbolGraph . Symbol . Availability {
451
+ func contains( _ platform: PlatformName ) -> Bool {
452
+ availability. contains ( where: { $0. matches ( platform) } )
453
+ }
454
+ }
455
+
456
+ private extension SymbolGraph . Symbol . Availability . AvailabilityItem {
457
+ func matches( _ platform: PlatformName ) -> Bool {
458
+ domain? . rawValue. lowercased ( ) == platform. rawValue. lowercased ( )
459
+ }
460
+ }
0 commit comments