-
Notifications
You must be signed in to change notification settings - Fork 17
Add uniqueName property and update attachment naming in SnapshotTest
#234
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
itaybre
wants to merge
11
commits into
main
Choose a base branch
from
feature/previews_unique_name
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 5 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
89f9003
Add `uniqueName` property and update attachment naming in SnapshotTest
itaybre de00e21
Extract preview count calculation into a separate method
itaybre 82bc044
Add uniqueName parameter to `Preview` and refactor PreviewType initia…
itaybre cd6a6a0
Code cleanup
itaybre 60c4f53
Rename `idForPreview` to `possibleUniqueIdForPreview`
itaybre c03808e
Bump swift-tools-version
itaybre 359f759
Remove conditional compilation checks
itaybre 2957aa8
Rename `PreviewType` to `PreviewTypeInfo`
itaybre 2bf9ed2
Add `DeveloperPreview` protocol and refactor code to use it instead o…
itaybre 7d8facc
Remove `nonisolated`
itaybre 8edbd5c
Use nonisolated on variables instead of protocol
itaybre File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,7 @@ import SwiftUI | |
| import PreviewsSupport | ||
|
|
||
| public struct Preview: Identifiable { | ||
| init<P: SwiftUI.PreviewProvider>(preview: _Preview, type: P.Type) { | ||
| init<P: SwiftUI.PreviewProvider>(preview: _Preview, type: P.Type, uniqueName: String) { | ||
| previewId = "\(preview.id)" | ||
| index = preview.id | ||
| orientation = preview.interfaceOrientation | ||
|
|
@@ -14,11 +14,12 @@ public struct Preview: Identifiable { | |
| P.previews | ||
| } | ||
| } | ||
| self.uniqueName = uniqueName | ||
| } | ||
|
|
||
| #if compiler(>=5.9) | ||
| @available(iOS 17.0, macOS 14.0, watchOS 10.0, tvOS 17.0, *) | ||
| init?(preview: DeveloperToolsSupport.Preview) { | ||
| init?(preview: DeveloperToolsSupport.Preview, uniqueName: String) { | ||
| previewId = "0" | ||
| var orientation: InterfaceOrientation = .portrait | ||
| device = nil | ||
|
|
@@ -67,6 +68,7 @@ public struct Preview: Identifiable { | |
| } | ||
|
|
||
| self._view = _view | ||
| self.uniqueName = uniqueName | ||
| } | ||
| #endif | ||
|
|
||
|
|
@@ -77,37 +79,22 @@ public struct Preview: Identifiable { | |
| public let index: Int | ||
| public let device: PreviewDevice? | ||
| public let layout: PreviewLayout | ||
| public let uniqueName: String | ||
| private let _view: @MainActor () -> any View | ||
| @MainActor public func view() -> any View { | ||
| _view() | ||
| } | ||
| } | ||
|
|
||
| // Wraps PreviewProvider or PreviewRegistry | ||
| public struct PreviewType: Hashable, Identifiable { | ||
| init<A: PreviewProvider>(typeName: String, previewProvider: A.Type) { | ||
| self.typeName = typeName | ||
| self.fileID = nil | ||
| self.line = nil | ||
| self.previews = A._allPreviews.map { Preview(preview: $0, type: A.self) } | ||
| self.platform = A.platform | ||
| fileprivate init(previewTypeInfo: PreviewTypeInfo, previews: [Preview]) { | ||
| self.typeName = previewTypeInfo.name | ||
| self.fileID = previewTypeInfo.fileID | ||
| self.line = previewTypeInfo.line | ||
| self.previews = previews | ||
| self.platform = previewTypeInfo.platform | ||
| } | ||
|
|
||
| #if compiler(>=5.9) | ||
| @available(iOS 17.0, macOS 14.0, watchOS 10.0, tvOS 17.0, *) | ||
| @MainActor | ||
| init?<A: PreviewRegistry>(typeName: String, registry: A.Type) { | ||
| self.typeName = typeName | ||
| self.fileID = A.fileID | ||
| self.line = A.line | ||
| guard let internalPreview = try? A.makePreview(), let preview = Preview(preview: internalPreview) else { | ||
| return nil | ||
| } | ||
| self.previews = [preview] | ||
| self.platform = nil | ||
| } | ||
| #endif | ||
|
|
||
| public var module: String { | ||
| String(typeName.split(separator: ".").first!) | ||
| } | ||
|
|
@@ -143,6 +130,38 @@ public struct PreviewType: Hashable, Identifiable { | |
| public let platform: PreviewPlatform? | ||
| } | ||
|
|
||
| private struct PreviewTypeInfo { | ||
| let name: String | ||
| let fileID: String? | ||
| let line: Int? | ||
| let previews: [InternalPreview] | ||
| let platform: PreviewPlatform? | ||
| } | ||
|
|
||
| private enum InternalPreview { | ||
| case previewProvider(_Preview, any SwiftUI.PreviewProvider.Type) | ||
| // Can't use DeveloperToolsSupport.Preview here because it's not available before iOS 17 | ||
|
||
| case previewRegistry(Any) | ||
|
|
||
| func getPreviewId() -> String { | ||
| switch self { | ||
| case .previewProvider(let internalPreview, _): | ||
| "\(internalPreview.id)" | ||
| case .previewRegistry(_): | ||
| "0" | ||
| } | ||
| } | ||
|
|
||
| func getDisplayName() -> String? { | ||
| switch self { | ||
| case .previewProvider(let internalPreview, _): | ||
| internalPreview.displayName | ||
| case .previewRegistry(let internalPreview): | ||
| Mirror(reflecting: internalPreview).descendant("displayName") as? String | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // The enum provides a namespace | ||
| public enum FindPreviews { | ||
| @available(iOS 16.0, macOS 13.0, tvOS 16.0, *) | ||
|
|
@@ -205,26 +224,100 @@ public enum FindPreviews { | |
| shouldInclude: (String, String) -> Bool = { _, _ in true }, | ||
| willAccess: (String) -> Void = { _ in }) -> [PreviewType] | ||
| { | ||
| return getPreviewTypes() | ||
| let rawPreviewTypes = getPreviewTypes() | ||
| .filter { shouldInclude($0.name, $0.proto) } | ||
| .compactMap { conformance -> PreviewType? in | ||
| let (name, accessor, proto) = conformance | ||
| willAccess(name) | ||
| switch proto { | ||
| case "PreviewProvider": | ||
| let previewProvider = unsafeBitCast(accessor(), to: Any.Type.self) as! any PreviewProvider.Type | ||
| return PreviewType(typeName: name, previewProvider: previewProvider) | ||
| case "PreviewRegistry": | ||
| #if compiler(>=5.9) | ||
| if #available(iOS 17.0, macOS 14.0, watchOS 10.0, tvOS 17.0, *) { | ||
| let previewRegistry = unsafeBitCast(accessor(), to: Any.Type.self) as! any PreviewRegistry.Type | ||
| return PreviewType(typeName: name, registry: previewRegistry) | ||
|
|
||
| let previewTypeInfos = rawPreviewTypes.compactMap { rawType -> PreviewTypeInfo? in | ||
| willAccess(rawType.name) | ||
| switch rawType.proto { | ||
| case "PreviewProvider": | ||
| let previewProvider = unsafeBitCast(rawType.accessor(), to: Any.Type.self) as! any PreviewProvider.Type | ||
| return PreviewTypeInfo( | ||
| name: rawType.name, | ||
| fileID: nil, | ||
| line: nil, | ||
| previews: previewProvider._allPreviews.map { .previewProvider($0, previewProvider.self) }, | ||
| platform: previewProvider.platform | ||
| ) | ||
| case "PreviewRegistry": | ||
| #if compiler(>=5.9) | ||
| if #available(iOS 17.0, macOS 14.0, watchOS 10.0, tvOS 17.0, *) { | ||
| let previewRegistry = unsafeBitCast(rawType.accessor(), to: Any.Type.self) as! any PreviewRegistry.Type | ||
| guard let internalPreview = try? previewRegistry.makePreview() else { | ||
| return nil | ||
| } | ||
| #endif | ||
| return nil | ||
| default: | ||
| return PreviewTypeInfo( | ||
| name: rawType.name, | ||
| fileID: previewRegistry.fileID, | ||
| line: previewRegistry.line, | ||
| previews: [ .previewRegistry(internalPreview) ], | ||
| platform: nil | ||
| ) | ||
| } | ||
| #endif | ||
| return nil | ||
| default: | ||
| return nil | ||
| } | ||
| } | ||
|
|
||
| let previewCountForId = calculateIdToPreviewCount(previewTypeInfos) | ||
|
|
||
| return generateFinalPreviewTypes(previewTypeInfos: previewTypeInfos, previewCountForId: previewCountForId) | ||
| } | ||
|
|
||
| private static func calculateIdToPreviewCount(_ previewTypeInfos: [PreviewTypeInfo]) -> [String: Int] { | ||
| var previewCountForId: [String: Int] = [:] | ||
| for previewTypeInfo in previewTypeInfos { | ||
| for preview in previewTypeInfo.previews { | ||
| let possibleId = possibleUniqueIdForPreview(preview, previewTypeInfo) | ||
| previewCountForId[possibleId, default: 0] += 1 | ||
| } | ||
| } | ||
| return previewCountForId | ||
| } | ||
|
|
||
| private static func generateFinalPreviewTypes(previewTypeInfos: [PreviewTypeInfo], previewCountForId: [String: Int]) -> [PreviewType] { | ||
| previewTypeInfos.map { previewTypeInfo in | ||
| let previews = previewTypeInfo.previews.compactMap { preview in | ||
| let possibleId = possibleUniqueIdForPreview(preview, previewTypeInfo) | ||
| let previewId = preview.getPreviewId() | ||
| let previewCount = previewCountForId[possibleId] ?? 1 | ||
| let uniqueName = generateUniqueName(possibleId: possibleId, previewCount: previewCount, previewTypeInfo: previewTypeInfo, previewId: previewId) | ||
|
|
||
| switch preview { | ||
| case .previewProvider(let internalPreview, let previewType): | ||
| return Preview(preview: internalPreview, type: previewType, uniqueName: uniqueName) | ||
| case .previewRegistry(let anyValue): | ||
| #if compiler(>=5.9) | ||
|
||
| if #available(iOS 17.0, macOS 14.0, watchOS 10.0, tvOS 17.0, *), | ||
| let realPreview = anyValue as? DeveloperToolsSupport.Preview { | ||
| return Preview(preview: realPreview, uniqueName: uniqueName) | ||
| } | ||
| #endif | ||
| return nil | ||
| } | ||
| } | ||
| return PreviewType(previewTypeInfo: previewTypeInfo, previews: previews) | ||
| } | ||
| } | ||
|
|
||
| private static func possibleUniqueIdForPreview(_ preview: InternalPreview, _ previewTypeInfo: PreviewTypeInfo) -> String { | ||
| var id = previewTypeInfo.fileID ?? previewTypeInfo.name | ||
| if let displayName = preview.getDisplayName() { | ||
| id += "_\(displayName)" | ||
| } | ||
| return id | ||
| } | ||
|
|
||
| private static func generateUniqueName(possibleId: String, previewCount: Int, previewTypeInfo: PreviewTypeInfo, previewId: String) -> String { | ||
| if previewCount == 1 { | ||
| return possibleId | ||
| } else if let fileId = previewTypeInfo.fileID, let line = previewTypeInfo.line { | ||
| return "\(fileId)_\(line)" | ||
| } else { | ||
| return "\(previewTypeInfo.name)_\(previewId)" | ||
| } | ||
| } | ||
| } | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having a
PreviewTypeand alsoPreviewTypeInfois a bit confusing, can we clean this up?