Skip to content

1.3.0 : Infinite loop (caused by checking tabBarController.viewControllers) #254

Description

@SebouChu

I updated to 1.3.0 and now, my WebViews are stuck in infinite loops.

I managed to identify this commit as the source of the issue: 34d97ae (linked to this PR #233)

Here's a few code snippets, but I don't think I have any fancy stuff in it. I'm open to send more data if necessary!

Snippets

Path Config

{
  "settings": {

  },
  "rules": [
    {
      "patterns": [
        "/days/.*"
      ],
      "properties": {
        "presentation": "replace"
      }
    },
    {
      "patterns": [
        "/calendar$"
      ],
      "properties": {
        "context": "modal",
        "view_controller": "calendar"
      }
    },
    {
      "patterns": [
        "/new$",
        "/edit$"
      ],
      "properties": {
        "context": "modal",
        "pull_to_refresh_enabled": false,
        "modal_dismiss_gesture_enabled": false
      }
    },
    {
      "patterns": [
        "/session/new$",
        "/registration/new$"
      ],
      "properties": {
        "pull_to_refresh_enabled": false
      }
    }
  ]
}

AppDelegate

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        Hotwire.registerBridgeComponents(BridgeComponent.allTypes)
        
        let remotePathConfigURL = Endpoint.pathConfigurationURL
        Hotwire.loadPathConfiguration(from: [
            .server(remotePathConfigURL)
        ])
        Hotwire.config.pathConfiguration.matchQueryStrings = false
        Hotwire.config.debugLoggingEnabled = true

        return true
    }

    // ...
}

SceneDelegate

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?
    
    private lazy var tabBarController = HotwireTabBarController(navigatorDelegate: self)
    
    private let tabs = [
        HotwireTab(
            title: "Journal",
            image: .init(systemName: "book")!,
            url: Endpoint.rootURL.appendingPathComponent("days")
        ),
        HotwireTab(
            title: "Objectifs",
            image: .init(systemName: "target")!,
            url: Endpoint.rootURL.appendingPathComponent("objectives")
        ),
        HotwireTab(
            title: "Amis",
            image: .init(systemName: "person.2")!,
            url: Endpoint.rootURL.appendingPathComponent("friends")
        ),
        HotwireTab(
            title: "Compte",
            image: .init(systemName: "person.fill")!,
            url: Endpoint.rootURL.appendingPathComponent("settings")
        )
    ]
    
    private let homeTab = HotwireTab(
        title: "Accueil",
        image: .init(systemName: "house")!,
        url: Endpoint.rootURL
    )

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        UINavigationBar.configureWithOpaqueBackground()
        
        tabBarController.setTabBarHidden(true, animated: false)
        window?.rootViewController = tabBarController
        tabBarController.load([homeTab])
    }
    
    func signIn() {
        if (tabBarController.viewControllers?.count == tabs.count &&
            !tabBarController.isTabBarHidden) { return }
        tabBarController.setTabBarHidden(false, animated: true)
        tabBarController.load(tabs)
    }
    
    func signOut() {
        if tabBarController.viewControllers?.count == 1 { return }
        tabBarController.setTabBarHidden(true, animated: true)
        tabBarController.load([homeTab])
    }
}

extension SceneDelegate: NavigatorDelegate {
    func handle(proposal: VisitProposal, from navigator: Navigator) -> ProposalResult {
        switch proposal.viewController {
        case CalendarViewController.pathConfigurationIdentifier:
            let urlComponents = URLComponents(url: proposal.url, resolvingAgainstBaseURL: false)
            let selectedDateValue = urlComponents?.queryItems?.first { item in item.name == "selected_date" }?.value
            var selectedDate: Date?
            
            if selectedDateValue != nil {
                let dateFormatter = DateFormatter()
                dateFormatter.dateFormat = "yyyy-MM-dd"
                selectedDate = dateFormatter.date(from: selectedDateValue!)
            }
            let calendarViewController = CalendarViewController(navigator: navigator, selectedDate: selectedDate)
            return .acceptCustom(calendarViewController)
        default:
            return .accept
        }
    }
}

Logs

[Router] handler match found handler: app-navigation location: http://localhost:3000
[Session] visit ["options": HotwireNative.VisitOptions(action: HotwireNative.VisitAction.advance, response: nil), "location": http://localhost:3000, "reload": false]
[ColdBootVisit] startVisit http://localhost:3000
Failed to resolve host network app id to config: bundleID: com.apple.WebKit.Networking instance ID: Optional([_EXExtensionInstanceIdentifier: 09E5FA27-E177-44EE-B26A-74FB137834A7])
Start can only be run when there are no view controllers on the stack.
[Bridge] bridgeDestinationViewDidLoad: http://localhost:3000/
[Bridge] bridgeDestinationViewWillAppear: http://localhost:3000/
[Bridge] bridgeDestinationViewDidAppear: http://localhost:3000/
WebContent[11935] 0x11104c100 - [sessionID=1] WebProcess::updateFreezerStatus: isFreezable=1, error=-1
[ColdBootVisit] Same-origin redirect detected: http://localhost:3000 -> http://localhost:3000/. http://localhost:3000
WebContent[11933] Unable to hide query parameters from script (missing data)
[Bridge] bridgeDidReceiveMessage Message(id: "1", component: "authentication", event: "signOut", metadata: Optional(HotwireNative.Message.Metadata(url: "http://localhost:3000/")), jsonData: "{\"metadata\":{\"url\":\"http:\\/\\/localhost:3000\\/\"}}")
[Bridge] bridgeDestinationViewWillDisappear: http://localhost:3000/
[Router] handler match found handler: app-navigation location: http://localhost:3000
[Session] visit ["reload": false, "location": http://localhost:3000, "options": HotwireNative.VisitOptions(action: HotwireNative.VisitAction.advance, response: nil)]
[ColdBootVisit] startVisit http://localhost:3000
Error acquiring assertion: <Error Domain=RBSServiceErrorDomain Code=1 "((target is not running or doesn't have entitlement com.apple.developer.web-browser-engine.rendering AND target is not running or doesn't have entitlement com.apple.developer.web-browser-engine.networking AND target is not running or doesn't have entitlement com.apple.developer.web-browser-engine.webcontent))" UserInfo={NSLocalizedFailureReason=((target is not running or doesn't have entitlement com.apple.developer.web-browser-engine.rendering AND target is not running or doesn't have entitlement com.apple.developer.web-browser-engine.networking AND target is not running or doesn't have entitlement com.apple.developer.web-browser-engine.webcontent))}>
0x110170480 - ProcessAssertion::acquireSync Failed to acquire RBS assertion 'XPCConnectionTerminationWatchdog' for process with PID=11935, error: (null)
[Bridge] bridgeDestinationViewDidLoad: http://localhost:3000/
[Bridge] bridgeDestinationViewWillAppear: http://localhost:3000/
[Bridge] bridgeDestinationViewDidDisappear: http://localhost:3000/
[Bridge] bridgeDestinationViewDidAppear: http://localhost:3000/
[ColdBootVisit] Same-origin redirect detected: http://localhost:3000 -> http://localhost:3000/. http://localhost:3000
WebContent[11941] 0x11104c100 - [sessionID=1] WebProcess::updateFreezerStatus: isFreezable=1, error=-1
[Bridge] bridgeDidReceiveMessage Message(id: "1", component: "authentication", event: "signOut", metadata: Optional(HotwireNative.Message.Metadata(url: "http://localhost:3000/")), jsonData: "{\"metadata\":{\"url\":\"http:\\/\\/localhost:3000\\/\"}}")
[Bridge] bridgeDestinationViewWillDisappear: http://localhost:3000/

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions