nathantannar4 / Transmission

Bridges UIKit presentation APIs to a SwiftUI API so you can use presentation controllers, interactive transitions and more.
BSD 2-Clause "Simplified" License
423 stars 17 forks source link

Crashes when trying to define a custom transition in xCode 16, iOS 18 #58

Closed coratype closed 1 month ago

coratype commented 1 month ago
== DATE:

    Tuesday, September 17, 2024 at 3:25:42 AM Eastern Daylight Time

    2024-09-17T07:25:42Z

== PREVIEW UPDATE ERROR:

    [Remote] JITError

    ==================================

    |  [Remote] LLVMError
    |  
    |  LLVMError: LLVMError(description: "Duplicate definition of symbol \'_$s12Transmission27SlidePresentationControllerC5edges7SwiftUI4EdgeO3SetVvg\'")

== ERROR:

    [Remote] JITError

    ==================================

    |  [Remote] LLVMError
    |  
    |  LLVMError: LLVMError(description: "Duplicate definition of symbol \'_$s12Transmission27SlidePresentationControllerC5edges7SwiftUI4EdgeO3SetVvg\'")

== VERSION INFO:

    Tools: 16A242d
    OS:    24A335
    PID:   31613
    Model: MacBook Pro
    Arch:  arm64e

== ENVIRONMENT:

    openFiles = [
        /Users/decoherence/repos/acusia_two/acusia/Transmission/CustomSheetTransition.swift, 
        /Users/decoherence/repos/acusia_two/acusia/Transmission/Prev.swift
    ]
    wantsNewBuildSystem = true
    newBuildSystemAvailable = true
    activeScheme = acusia
    activeRunDestination = iphone variant iphoneos arm64
    workspaceArena = [x]
    buildArena = [x]
    buildableEntries = [
        acusia.app
    ]
    runMode = JIT Executor

== SELECTED RUN DESTINATION:

    iOS 18.0 | iphoneos | arm64 | iPhone 15 Pro Max | no proxy

== EXECUTION MODE OVERRIDES:

    Workspace JIT mode user setting: true
    Falling back to Dynamic Replacement: false

== PACKAGE RESOLUTION ERRORS:

== REFERENCED SOURCE PACKAGES:

    <IDESwiftPackageCore.IDESwiftPackageDependency:0x34cdd7ec0 path:'/Users/decoherence/Library/Developer/Xcode/DerivedData/acusia-hjccgltqclnwfmdyjmimhikrzvmw/SourcePackages/checkouts/swift-syntax'>
    <IDESwiftPackageCore.IDESwiftPackageDependency:0x355f411c0 path:'/Users/decoherence/Library/Developer/Xcode/DerivedData/acusia-hjccgltqclnwfmdyjmimhikrzvmw/SourcePackages/checkouts/Transmission'>
    <IDESwiftPackageCore.IDESwiftPackageDependency:0x34a3eef90 path:'/Users/decoherence/Library/Developer/Xcode/DerivedData/acusia-hjccgltqclnwfmdyjmimhikrzvmw/SourcePackages/checkouts/Engine'>
    <IDESwiftPackageCore.IDESwiftPackageDependency:0x3112fae70 path:'/Users/decoherence/Library/Developer/Xcode/DerivedData/acusia-hjccgltqclnwfmdyjmimhikrzvmw/SourcePackages/checkouts/Wave'>
    <IDESwiftPackageCore.IDESwiftPackageDependency:0x3112c9ac0 path:'/Users/decoherence/Library/Developer/Xcode/DerivedData/acusia-hjccgltqclnwfmdyjmimhikrzvmw/SourcePackages/checkouts/BigUIPaging'>
    <IDESwiftPackageCore.IDESwiftPackageDependency:0x3480d0c90 path:'/Users/decoherence/Library/Developer/Xcode/DerivedData/acusia-hjccgltqclnwfmdyjmimhikrzvmw/SourcePackages/checkouts/Turbocharger'>

Relevant files:

import SwiftUI
import Transmission

struct Prev: View {
    @State private var isPresented = false

    var body: some View {
        Button {
            withAnimation {
                isPresented = true
            }
        } label: {
            HStack {
                RoundedRectangle(cornerRadius: 10)
                    .fill(Color.blue)
                    .aspectRatio(1, contentMode: .fit)
                    .frame(width: 44, height: 44)
                    .presentation(
                        transition: .custom,
                        isPresented: $isPresented
                    ) {
                        RoundedRectangle(cornerRadius: 10)
                            .fill(Color.red)
                            .aspectRatio(1, contentMode: .fit)
                            .frame(width: 44, height: 44)
                    }
            }
        }
    }
}

#Preview {
    Prev()
}
import UIKit
import SwiftUI
import Transmission

extension PresentationLinkTransition {
    static let custom: PresentationLinkTransition = .custom(
        options: .init(),
        CustomTransition()
    )
}

struct CustomTransition: PresentationLinkTransitionRepresentable {

    func makeUIPresentationController(
        presented: UIViewController,
        presenting: UIViewController?,
        context: Context
    ) -> UIPresentationController {
        let presentationController = UISheetPresentationController(
            presentedViewController: presented,
            presenting: presenting
        )
        presented.view.layer.cornerRadius = 14
        return presentationController
    }

    func updateUIPresentationController(
        presentationController: UIPresentationController,
        context: Context
    ) {

    }

    func animationController(
        forPresented presented: UIViewController,
        presenting: UIViewController,
        context: Context
    ) -> UIViewControllerAnimatedTransitioning? {
        MatchedGeometryTransition(
            sourceView: context.sourceView,
            isPresenting: true,
            animation: nil
        )
    }

    func animationController(
        forDismissed dismissed: UIViewController,
        context: Context
    ) -> UIViewControllerAnimatedTransitioning? {
        MatchedGeometryTransition(
            sourceView: context.sourceView,
            isPresenting: false,
            animation: nil
        )
    }
}

class MatchedGeometryTransition: PresentationControllerTransition {

    weak var sourceView: UIView?

    init(
        sourceView: UIView,
        isPresenting: Bool,
        animation: Animation?
    ) {
        super.init(isPresenting: isPresenting, animation: animation)
        self.sourceView = sourceView
    }

    override func transitionAnimator(
        using transitionContext: UIViewControllerContextTransitioning
    ) -> UIViewPropertyAnimator {

        let animator = UIViewPropertyAnimator(animation: animation) ?? UIViewPropertyAnimator(duration: duration, curve: completionCurve)

        guard
            let presented = transitionContext.viewController(forKey: isPresenting ? .to : .from)
        else {
            transitionContext.completeTransition(false)
            return animator
        }

        let isPresenting = isPresenting
        let hostingController = presented as? AnyHostingController

        let oldValue = hostingController?.disableSafeArea ?? false
        hostingController?.disableSafeArea = true

        var sourceFrame = sourceView.map {
            $0.convert($0.frame, to: transitionContext.containerView)
        } ?? transitionContext.containerView.frame
        let presentedFrame = isPresenting
            ? transitionContext.finalFrame(for: presented)
            : transitionContext.initialFrame(for: presented)
        if isPresenting {
            transitionContext.containerView.addSubview(presented.view)
            presented.view.frame = sourceFrame
            presented.view.layoutIfNeeded()
            hostingController?.render()
        }

        animator.addAnimations {
            if isPresenting {
                hostingController?.disableSafeArea = oldValue
            }
            presented.view.frame = isPresenting ? presentedFrame : sourceFrame
            presented.view.layoutIfNeeded()
        }
        animator.addCompletion { animatingPosition in
            hostingController?.disableSafeArea = oldValue
            switch animatingPosition {
            case .end:
                transitionContext.completeTransition(true)
            default:
                transitionContext.completeTransition(false)
            }
        }
        return animator
    }
}
nathantannar4 commented 1 month ago

Try a clean build. Xcode Menu > Product > Clean Build Folder. Then re build

coratype commented 1 month ago

that’s actually what i have been doing to get it to work but it inevitably always crashes

coratype commented 1 month ago

for further context it only seems to happen when i dive into the transmission source code and open it in xcode

coratype commented 1 month ago

okay it randomly stopped occurring

but package works great for my use case, thanks!

nathantannar4 commented 1 month ago

@coratype Let me know if you run into any other issues!