rensbreur / SwiftTUI

SwiftUI for terminal applications
MIT License
1.23k stars 47 forks source link

Fatal error: Range requires lowerBound <= upperBound #4

Closed hassila closed 1 year ago

hassila commented 1 year ago

Trying to build small sample project (attached) which fails due to the rect being zero (so upperBound becomes negative -1).

2022-12-01 17:01:32.059857+0100 repro-swifttui[38382:1822751] Swift/arm64e-apple-macos.swiftinterface:5817:

Also tried with the official 0.1.0 - but got the same issue

image

repro-swifttui.zip

hassila commented 1 year ago

repro-swifttui.zip

hassila commented 1 year ago

All the code is simply:

import SwiftTUI

struct MyTerminalView: View {
    var body: some View {
        Text("Hello, world!")
    }
}

@main
struct MyApp {
    static func main() {
        Application(rootView: MyTerminalView()).start()
    }
}
rensbreur commented 1 year ago

Hi @hassila , thank you for sharing. You cannot run SwiftTUI apps in the Xcode console. Can you try to run the app in your Terminal application? You can do that by opening the package directory in a Terminal application and running

swift run
hassila commented 1 year ago

Oh, same thing from terminal for the larger project which I tried it with - I just brought it up in Xcode for getting the debugger screenshot. You can't reproduce with the attached reproducer?

hassila commented 1 year ago

(And kudos for a super cool project!)

rensbreur commented 1 year ago

Are you sure it's the same failure when you run from the Terminal? Running the project in the Xcode console like this will always fail with this error. The issue is that the console lacks many terminal features, such as reporting the window size. The renderer than fails drawing in a window of size 0x0.

hassila commented 1 year ago

I’ll give it another spin when in the office tomorrow - but fairly sure - I could get some sample code to give some proper output, but I’d inevitably crash (even with just a Text()). Will revert, thanks.

rensbreur commented 1 year ago

Thanks, that would be great! You can debug without Xcode. (I personally haven't been able to get Xcode to connect to an app running inside the terminal.) Run this inside your package folder:

lldb .build/debug/repro-swifttui

Then use this to launch the terminal app in a new window:

(lldb) process launch --tty

When the app crashes, you can print a stack trace like this:

(lldb) thread backtrace

That should give us more information on why it's crashing in the Terminal as well.

hassila commented 1 year ago

Ok, this seems to be an issue when I try to integrate it into a slightly larger tool which also uses SwiftArgumentParser etc;

  thread #1, queue = 'com.apple.main-thread'
    frame #0: 0x00000001a63aedf0 libsystem_kernel.dylib`mach_msg2_trap + 8
    frame #1: 0x00000001a63c08d8 libsystem_kernel.dylib`mach_msg2_internal + 80
    frame #2: 0x00000001a63b7638 libsystem_kernel.dylib`mach_msg_overwrite + 540
    frame #3: 0x00000001a63af16c libsystem_kernel.dylib`mach_msg + 24
    frame #4: 0x00000001a64cdbdc CoreFoundation`__CFRunLoopServiceMachPort + 160
    frame #5: 0x00000001a64cc4c8 CoreFoundation`__CFRunLoopRun + 1232
    frame #6: 0x00000001a64cb8a4 CoreFoundation`CFRunLoopRunSpecific + 612
    frame #7: 0x00000001a6552818 CoreFoundation`CFRunLoopRun + 64
    frame #8: 0x000000022e485f34 libswift_Concurrency.dylib`swift_task_asyncMainDrainQueueImpl() + 40
    frame #9: 0x000000022e485f0c libswift_Concurrency.dylib`swift_task_asyncMainDrainQueue + 100
    frame #10: 0x0000000100131d48 ordo-storage-tool`ordo_storage_tool_main at Dump.swift:0 [opt]
    frame #11: 0x00000001a60c3e50 dyld`start + 2544
* thread #2, queue = 'com.apple.root.default-qos.cooperative', stop reason = EXC_BREAKPOINT (code=1, subcode=0x1a6276050)
  * frame #0: 0x00000001a6276050 libdispatch.dylib`dispatch_main + 68
    frame #1: 0x00000001000e5e14 ordo-storage-tool`Application.start(self=0x0000600002104d20) at Application.swift:47:9 [opt]
    frame #2: 0x0000000100133ab0 ordo-storage-tool`StorageTool.run() [inlined] static ordo_storage_tool.Interactive.tailLastTransactions() async throws -> () at Interactive.swift:26:49 [opt]
    frame #3: 0x0000000100133a70 ordo-storage-tool`StorageTool.run(self=ordo_storage_tool.StorageTool @ 0x00006000017044d0) at Tool.swift:35:35 [opt]
    frame #4: 0x0000000100133e60 ordo-storage-tool`protocol witness for AsyncParsableCommand.run() in conformance StorageTool at <compiler-generated>:0 [opt]
    frame #5: 0x0000000100022108 ordo-storage-tool`static AsyncParsableCommand.main(self=0x0000000100172230) at AsyncParsableCommand.swift:37 [opt]
    frame #6: 0x0000000100131dc4 ordo-storage-tool`specialized thunk for @escaping @convention(thin) @async () -> () at <compiler-generated>:0 [opt]
(lldb) 

from the documentation:

This function "parks" the main thread and waits for blocks to be submitted to the main queue. 
Applications that call UIApplicationMain(_:_:_:_:) (iOS), NSApplicationMain(_:_:) (macOS), 
or CFRunLoopRun() on the main thread must not call dispatchMain().

So it seems I get an implicit CFRunLoop from using Concurrency (async main entry point through SwiftArgumentParser) and thus aren't allowed to call dispatchMain().

rensbreur commented 1 year ago

Right, I don't expect that to work, SwiftTUI apps need to be started from the main thread and start their own runloop.

hassila commented 1 year ago

Ok, will see if I can refactor this and get it to work - I'm looking to adding an 'interactive' mode to a CLI utility and it'd be cool to leverage SwiftTUI for that. Thanks for the reply.

hassila commented 1 year ago

Ok, for future readers - it is possible to integrate with Swift Argument Parser, just use 'Parsable Command' (can't have async entry point to the program unfortunately).