Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 105 additions & 58 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,62 +4,109 @@
import PackageDescription

let package = Package(
name: "SnapshotPreviews",
platforms: [.iOS(.v15), .macOS(.v12), .watchOS(.v9)],
products: [
// Products define the executables and libraries a package produces, making them visible to other packages.
.library(
name: "PreviewGallery",
type: .static, // Replace this to build dynamic
targets: ["PreviewGallery"]),
// Test library to import in your XCTest target.
// This is the only library that depends on XCTest.framework
.library(
name: "SnapshottingTests",
type: .static, // Replace this to build dynamic
targets: ["SnapshottingTests"]),
// Link the main app to this target to use custom snapshot settings
// This lib does not get inserted when running tests to avoid
// duplicate symbols.
.library(
name: "SnapshotPreferences",
targets: ["SnapshotPreferences"]),
// Core functionality for snapshotting exported from the internal package
.library(
name: "SnapshotPreviewsCore",
targets: ["SnapshotPreviewsCore"]),
// Dynamic library that your main app will have inserted to generate previews
.library(
name: "Snapshotting",
type: .dynamic,
targets: ["Snapshotting"]),
],
dependencies: [
.package(url: "https://github.com/swhitty/FlyingFox.git", exact: "0.16.0"),
.package(url: "https://github.com/EmergeTools/AccessibilitySnapshot.git", exact: "1.0.2"),
.package(url: "https://github.com/EmergeTools/SimpleDebugger.git", exact: "1.0.0"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
// Target that provides the XCTest
.target(name: "SnapshottingTestsObjc", dependencies: [.product(name: "SimpleDebugger", package: "SimpleDebugger", condition: .when(platforms: [.iOS, .macOS, .macCatalyst]))]),
.target(name: "SnapshottingTests", dependencies: ["SnapshotPreviewsCore", "SnapshottingTestsObjc"]),
.target(name: "SnapshotSharedModels"),
// Core functionality
.target(name: "SnapshotPreviewsCore", dependencies: ["PreviewsSupport", "SnapshotSharedModels", .product(name: "AccessibilitySnapshotCore", package: "AccessibilitySnapshot", condition: .when(platforms: [.iOS, .macCatalyst]))]),
.target(name: "SnapshotPreferences", dependencies: ["SnapshotSharedModels"]),
// Inserted dylib
.target(name: "Snapshotting", dependencies: ["SnapshottingSwift"]),
// Swift code in the inserted dylib
.target(name: "SnapshottingSwift", dependencies: ["SnapshotPreviewsCore", .product(name: "FlyingFox", package: "FlyingFox")]),
.target(name: "PreviewGallery", dependencies: ["SnapshotPreviewsCore", "SnapshotPreferences"]),
.binaryTarget(
name: "PreviewsSupport",
path: "PreviewsSupport/PreviewsSupport.xcframework"),
.testTarget(
name: "SnapshotPreviewsTests",
dependencies: ["SnapshotPreviewsCore"]),
],
cxxLanguageStandard: .cxx11
name: "SnapshotPreviews",
platforms: [.iOS(.v15), .macOS(.v12), .watchOS(.v9)],
products: [
// Products define the executables and libraries a package produces, making them visible to other packages.
.library(
name: "PreviewGallery",
type: .static, // Replace this to build dynamic
targets: ["PreviewGallery"]
),
// Test library to import in your XCTest target.
// This is the only library that depends on XCTest.framework
.library(
name: "SnapshottingTests",
type: .static, // Replace this to build dynamic
targets: ["SnapshottingTests"]
),
// Link the main app to this target to use custom snapshot settings
// This lib does not get inserted when running tests to avoid
// duplicate symbols.
.library(
name: "SnapshotPreferences",
targets: ["SnapshotPreferences"]
),
// Core functionality for snapshotting exported from the internal package
.library(
name: "SnapshotPreviewsCore",
targets: ["SnapshotPreviewsCore"]
),
// Dynamic library that your main app will have inserted to generate previews
.library(
name: "Snapshotting",
type: .dynamic,
targets: ["Snapshotting"]
),
],
dependencies: [
.package(url: "https://github.com/swhitty/FlyingFox.git", exact: "0.16.0"),
.package(
url: "https://github.com/EmergeTools/AccessibilitySnapshot.git",
exact: "1.0.2"
),
.package(
url: "https://github.com/EmergeTools/SimpleDebugger.git",
exact: "1.0.0"
),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
// Targets can depend on other targets in this package and products from dependencies.
// Target that provides the XCTest
.target(
name: "SnapshottingTestsObjc",
dependencies: [
.product(
name: "SimpleDebugger",
package: "SimpleDebugger",
condition: .when(platforms: [.iOS, .macOS, .macCatalyst])
)
]
),
.target(
name: "SnapshottingTests",
dependencies: ["SnapshotPreviewsCore", "SnapshottingTestsObjc"]
),
.target(name: "SnapshotSharedModels"),
// Core functionality
.target(
name: "SnapshotPreviewsCore",
dependencies: [
"PreviewsSupport", "SnapshotSharedModels",
.product(
name: "AccessibilitySnapshotCore",
package: "AccessibilitySnapshot",
condition: .when(platforms: [.iOS, .macCatalyst])
),
]
),
.target(
name: "SnapshotPreferences",
dependencies: ["SnapshotSharedModels"]
),
// Inserted dylib
.target(name: "Snapshotting", dependencies: ["SnapshottingSwift"]),
// Swift code in the inserted dylib
.target(
name: "SnapshottingSwift",
dependencies: [
"SnapshotPreviewsCore",
.product(name: "FlyingFox", package: "FlyingFox"),
]
),
.target(
name: "PreviewGallery",
dependencies: ["SnapshotPreviewsCore", "SnapshotPreferences"]
),
.binaryTarget(
name: "PreviewsSupport",
path: "PreviewsSupport/PreviewsSupport.xcframework"
),
.testTarget(
name: "SnapshotPreviewsTests",
dependencies: ["SnapshotPreviewsCore", "SnapshotPreferences"]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reformatted this file via swiftlint (can revert if you want) and added some dependencies for our testTarget.

),
],
cxxLanguageStandard: .cxx11
)
64 changes: 64 additions & 0 deletions Sources/SnapshotPreviewsCore/EmergeModifierView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// ModifierFinder.swift
//
//
// Created by Noah Martin on 7/8/24.
//

import Foundation
import SnapshotSharedModels
import SwiftUI

public struct EmergeModifierView: View {

private static let modifierFinderClass =
(NSClassFromString("EmergeModifierFinder") as? NSObject.Type)?.init()
private static let finder =
modifierFinderClass != nil
? Mirror(reflecting: modifierFinderClass!).descendant("finder")
as? (any View) -> any View : nil
private static let modifierState =
NSClassFromString("EmergeModifierState") as? NSObject.Type
private static let stateMirror =
modifierState != nil
? Mirror(
reflecting: modifierState!
.perform(NSSelectorFromString("shared"))
.takeUnretainedValue()
) : nil

private let internalView: AnyView

init(wrapped: some View) {
let rootView = Self.finder?(wrapped)
internalView = rootView != nil ? AnyView(rootView!) : AnyView(wrapped)
}

public var body: some View {
internalView
}

var emergeRenderingMode: EmergeRenderingMode? {
let renderingMode =
Self.stateMirror?.descendant("renderingMode")
as? EmergeRenderingMode.RawValue
return renderingMode != nil
? EmergeRenderingMode(rawValue: renderingMode!) : nil
}

var accessibilityEnabled: Bool? {
Self.stateMirror?.descendant("accessibilityEnabled") as? Bool
}

var appStoreSnapshot: Bool? {
Self.stateMirror?.descendant("appStoreSnapshot") as? Bool
}

var precision: Float? {
Self.stateMirror?.descendant("precision") as? Float
}

var supportsExpansion: Bool {
Self.stateMirror?.descendant("expansionPreference") as? Bool ?? true
}
}
53 changes: 0 additions & 53 deletions Sources/SnapshotPreviewsCore/ModifierFinder.swift

This file was deleted.

17 changes: 17 additions & 0 deletions Tests/SnapshotPreviewsTests/ConformanceLookupTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import XCTest

@testable import SnapshotPreviewsCore

final class ConformanceLookupTests: XCTestCase {
func test_getPreviewTypes() throws {
let types = getPreviewTypes()
XCTAssertEqual(types.count, 1)

let firstType = types.first!
XCTAssertEqual(
"SnapshotPreviewsTests.$s21SnapshotPreviewsTests0019TestViewswift_DJEEdfMX15_0_33_B5E96601318DE1EC85533DD88EB53190Ll7PreviewfMf_15PreviewRegistryfMu_",
firstType.name
)
XCTAssertEqual("PreviewRegistry", firstType.proto)
}
}
12 changes: 0 additions & 12 deletions Tests/SnapshotPreviewsTests/ETBrowserTests.swift

This file was deleted.

72 changes: 72 additions & 0 deletions Tests/SnapshotPreviewsTests/EmergeModifierStateTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//
// EmergeModifierViewTests.swift
// SnapshotPreviews
//
// Created by Trevor Elkins on 4/30/25.
//

import SwiftUI
import XCTest

@testable import SnapshotPreferences
@testable import SnapshotSharedModels
@testable import SnapshotPreviewsCore

final class EmergeModifierStateTests: XCTestCase {

override func setUp() {
super.setUp()
EmergeModifierState.shared.reset()
}

func test_storesRenderingMode() {
let view = makeBaseText().emergeRenderingMode(.uiView)
let state = state(for: view)
XCTAssertEqual(state.renderingMode, EmergeRenderingMode.uiView.rawValue)
}

func test_storesPrecision() throws {
let view = makeBaseText().emergeSnapshotPrecision(0.95)
let state = state(for: view)
let precision = try XCTUnwrap(state.precision)
XCTAssertEqual(precision, 0.95, accuracy: .ulpOfOne)
}

func test_storesExpansionPreference() {
let view = makeBaseText().emergeExpansion(false)
let state = state(for: view)
XCTAssertEqual(state.expansionPreference, false)
}

func test_storesAccessibilityFlag() {
let view = makeBaseText().emergeAccessibility(true)
let state = state(for: view)
XCTAssertEqual(state.accessibilityEnabled, true)
}

func test_storesAppStoreSnapshotFlag() {
let view = makeBaseText().emergeAppStoreSnapshot(true)
let state = state(for: view)
XCTAssertEqual(state.appStoreSnapshot, true)
}

private func makeBaseText() -> Text { Text("Hello") }

private func state(
for view: some View,
file: StaticString = #file,
line: UInt = #line
) -> EmergeModifierState {
let wrapped = EmergeModifierView(wrapped: view)
let hosting = UIHostingController(rootView: wrapped)

let window = UIWindow(frame: UIScreen.main.bounds)
window.rootViewController = hosting
window.makeKeyAndVisible()

// Give SwiftUI one tick to propagate preferences
RunLoop.main.run(until: Date(timeIntervalSinceNow: 0.01))

return EmergeModifierState.shared
}
}
Loading