diff --git a/CHANGELOG.md b/CHANGELOG.md index b85ab24f8..62dd68e20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ ##### Enhancements -- None. +- SwiftUI previews (`PreviewProvider`) are no longer retained by default. Retain them with `--retain-swift-ui-previews`. ##### Bug Fixes diff --git a/Sources/Frontend/Commands/ScanCommand.swift b/Sources/Frontend/Commands/ScanCommand.swift index 8d1c47be5..3835a6811 100644 --- a/Sources/Frontend/Commands/ScanCommand.swift +++ b/Sources/Frontend/Commands/ScanCommand.swift @@ -75,6 +75,9 @@ struct ScanCommand: FrontendCommand { @Flag(help: "Retain unused protocol function parameters, even if the parameter is unused in all conforming functions") var retainUnusedProtocolFuncParams: Bool = defaultConfiguration.$retainUnusedProtocolFuncParams.defaultValue + @Flag(help: "Retain SwiftUI previews") + var retainSwiftUIPreviews: Bool = defaultConfiguration.$retainSwiftUIPreviews.defaultValue + @Flag(help: "Clean existing build artifacts before building") var cleanBuild: Bool = defaultConfiguration.$cleanBuild.defaultValue @@ -119,6 +122,7 @@ struct ScanCommand: FrontendCommand { configuration.apply(\.$retainObjcAccessible, retainObjcAccessible) configuration.apply(\.$retainObjcAnnotated, retainObjcAnnotated) configuration.apply(\.$retainUnusedProtocolFuncParams, retainUnusedProtocolFuncParams) + configuration.apply(\.$retainSwiftUIPreviews, retainSwiftUIPreviews) configuration.apply(\.$disableRedundantPublicAnalysis, disableRedundantPublicAnalysis) configuration.apply(\.$externalEncodableProtocols, externalEncodableProtocols) configuration.apply(\.$externalTestCaseClasses, externalTestCaseClasses) diff --git a/Sources/PeripheryKit/SourceGraph/Mutators/SwiftUIRetainer.swift b/Sources/PeripheryKit/SourceGraph/Mutators/SwiftUIRetainer.swift index 53629ccbe..41a5f7f3a 100644 --- a/Sources/PeripheryKit/SourceGraph/Mutators/SwiftUIRetainer.swift +++ b/Sources/PeripheryKit/SourceGraph/Mutators/SwiftUIRetainer.swift @@ -3,11 +3,13 @@ import Shared final class SwiftUIRetainer: SourceGraphMutator { private let graph: SourceGraph - private static let specialProtocolNames = ["PreviewProvider", "LibraryContentProvider"] + private let configuration: Configuration + private static let specialProtocolNames = ["LibraryContentProvider"] private static let applicationDelegateAdaptorStructNames = ["UIApplicationDelegateAdaptor", "NSApplicationDelegateAdaptor"] required init(graph: SourceGraph, configuration: Configuration) { self.graph = graph + self.configuration = configuration } func mutate() { @@ -18,12 +20,18 @@ final class SwiftUIRetainer: SourceGraphMutator { // MARK: - Private private func retainSpecialProtocolConformances() { + var names = Self.specialProtocolNames + + if configuration.retainSwiftUIPreviews { + names.append("PreviewProvider") + } + graph .declarations(ofKinds: [.class, .struct, .enum]) .lazy .filter { $0.related.contains { - self.graph.isExternal($0) && $0.kind == .protocol && Self.specialProtocolNames.contains($0.name ?? "") + self.graph.isExternal($0) && $0.kind == .protocol && names.contains($0.name ?? "") } } .forEach { graph.markRetained($0) } diff --git a/Sources/Shared/Configuration.swift b/Sources/Shared/Configuration.swift index bbec02ce1..0b8470a7c 100644 --- a/Sources/Shared/Configuration.swift +++ b/Sources/Shared/Configuration.swift @@ -65,6 +65,9 @@ public final class Configuration { @Setting(key: "retain_unused_protocol_func_params", defaultValue: false) public var retainUnusedProtocolFuncParams: Bool + @Setting(key: "retain_swift_ui_previews", defaultValue: false) + public var retainSwiftUIPreviews: Bool + @Setting(key: "disable_redundant_public_analysis", defaultValue: false) public var disableRedundantPublicAnalysis: Bool @@ -166,6 +169,10 @@ public final class Configuration { config[$retainUnusedProtocolFuncParams.key] = retainUnusedProtocolFuncParams } + if $retainSwiftUIPreviews.hasNonDefaultValue { + config[$retainSwiftUIPreviews.key] = retainSwiftUIPreviews + } + if $disableRedundantPublicAnalysis.hasNonDefaultValue { config[$disableRedundantPublicAnalysis.key] = disableRedundantPublicAnalysis } @@ -252,6 +259,8 @@ public final class Configuration { $retainObjcAnnotated.assign(value) case $retainUnusedProtocolFuncParams.key: $retainUnusedProtocolFuncParams.assign(value) + case $retainSwiftUIPreviews.key: + $retainSwiftUIPreviews.assign(value) case $disableRedundantPublicAnalysis.key: $disableRedundantPublicAnalysis.assign(value) case $verbose.key: @@ -292,6 +301,7 @@ public final class Configuration { $retainObjcAccessible.reset() $retainObjcAnnotated.reset() $retainUnusedProtocolFuncParams.reset() + $retainSwiftUIPreviews.reset() $disableRedundantPublicAnalysis.reset() $externalEncodableProtocols.reset() $externalTestCaseClasses.reset() diff --git a/Tests/XcodeTests/SwiftUIProjectTest.swift b/Tests/XcodeTests/SwiftUIProjectTest.swift index 01660821b..0390db3f9 100644 --- a/Tests/XcodeTests/SwiftUIProjectTest.swift +++ b/Tests/XcodeTests/SwiftUIProjectTest.swift @@ -20,8 +20,8 @@ class SwiftUIProjectTest: SourceGraphTestCase { assertReferenced(.struct("SwiftUIProjectApp")) } - func testRetainsPreviewProvider() { - assertReferenced(.struct("ContentView_Previews")) + func testDoesNotRetainPreviewProvider() { + assertNotReferenced(.struct("ContentView_Previews")) } func testRetainsLibraryContentProvider() {