Skip to content
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

View for UIViewControllerRepresentable is absent #300

Open
quanshousio opened this issue Apr 27, 2024 · 0 comments
Open

View for UIViewControllerRepresentable is absent #300

quanshousio opened this issue Apr 27, 2024 · 0 comments

Comments

@quanshousio
Copy link

Hello @nalexn,

Awesome library by the way. I'm currently trying to use it to test one of the libraries that I'm maintaining. However, I've encountered an issue wherein .viewController() fails to locate the underlying UIViewController in a UIViewControllerRepresentable. I'm entirely not sure if I'm overlooking something in my approach. Hence, I'd greatly appreciate any help.

Here's a minimal reproducible example based on my library's implementation. I use background() modifier as a bridge for the UIKit implementation behind the scene.

Test was run using Xcode 15.3, iOS 17.4 simulator on macOS 14.4.1.

Let me know if you need additional information. Thank you!

import Combine
import SwiftUI
import ViewInspector
import XCTest

final class Test: XCTestCase {
  func testBridge() throws {
    let sut = ContentView()
    let exp = sut.inspection.inspect(after: 0.1) { view in
      XCTAssertTrue(try view.actualView().isPresented)

      let bridge = try view.find(UIViewControllerBridge<Text>.self).actualView()
      XCTAssertTrue(bridge.isPresented)

      let viewController = try bridge.viewController() // failed - View for UIViewControllerBridge<Text> is absent
      XCTAssertTrue(viewController.internalState)
    }

    ViewHosting.host(view: sut)
    wait(for: [exp], timeout: 1.0)
  }
}

struct ContentView: View {
  @State var isPresented = false
  internal let inspection = Inspection<Self>()

  var body: some View {
    Text("text")
      .onReceive(inspection.notice) {
        inspection.visit(self, $0)
      }
      .onAppear {
        isPresented = true
      }
      .background(
        UIViewControllerBridge(isPresented: $isPresented) {
          Text("pop up")
        }
      )
  }
}

struct UIViewControllerBridge<Content>: UIViewControllerRepresentable where Content: View {
  @Binding var isPresented: Bool
  let content: () -> Content

  func makeUIViewController(context _: Context) -> CustomUIViewController<Content> {
    return CustomUIViewController()
  }

  func updateUIViewController(
    _ viewController: CustomUIViewController<Content>,
    context: Context
  ) {
    viewController.update(
      $isPresented,
      content
    )
  }
}

class CustomUIViewController<Content>: UIViewController where Content: View {
  var internalState = false

  func update(
    _ isPresented: Binding<Bool>,
    _ content: () -> Content
  ) {
    internalState = isPresented.wrappedValue
    print("isPresented: \(isPresented.wrappedValue)")

    // present the content() in another UIWindow
  }
}

internal final class Inspection<V> {
  let notice = PassthroughSubject<UInt, Never>()
  var callbacks: [UInt: (V) -> Void] = [:]

  func visit(_ view: V, _ line: UInt) {
    if let callback = callbacks.removeValue(forKey: line) {
      callback(view)
    }
  }
}

extension Inspection: InspectionEmissary {}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant