stevengharris / MarkupEditor

WYSIWYG editing for SwiftUI and UIKit apps
MIT License
342 stars 28 forks source link

Unable to build in Swift Playgrounds #164

Open EduGonO opened 10 months ago

EduGonO commented 10 months ago

hello @stevengharris, loved your project and trying to use it in swift playgrounds on my iPad, I'm constantly getting an error message "Could not find markup.html, css, and js for this bundle." from MarkupWKWebView. Since I imported the package, nothing is modifiable so I'm a bit stuck on how to proceed. Thanks!

stevengharris commented 10 months ago

Thanks, I don't use playgrounds much, but I'll look into it. The logic for doing the bundle lookup (which is where that string shown in the error is) must require different handling for playgrounds.

stevengharris commented 10 months ago

Before I spend too much time on this, you are only seeing the "Could not find markup.html, css, and js for this bundle." problem? I tried and failed with an error:

ld: Undefined symbols:
  ___isPlatformVersionAtLeast, referenced from:
      Swift._stdlib_isOSVersionAtLeast_AEIC(Builtin.Word, Builtin.Word, Builtin.Word) -> Builtin.Int1 in MarkupEditorUIView.o
clang: error: linker command failed with exit code 1 (use -v to see invocation)

This seems to be some kind of toolchain issue per https://github.com/apple/swift/issues/62626. Maybe it has only appeared since the last update, I am not sure.

stevengharris commented 10 months ago

Well, I learned a lot about Playgrounds, but in the end, Playgrounds do not seem to support the basic WKWebView.loadFileURL method which MarkupWKWebView uses to load its initial markup.html file which in turn loads markup.css and markup.js. Since these don't load properly, basically nothing works. If you google around a bit for "playground" wkwebview "loadFileURL", you can see people complaining, but I couldn't find any solutions.

Here is some playground code to illustrate that only uses WKWebView. It references a test.html file you would have to create with contents like <h1>This is a test file.</h1>. The commented-out sections show that:

  1. Loading a URL works properly.
  2. Loading an HTML string works properly.
  3. Loading markup.html from the MarkupEditor's bundle fails, altho the file is found properly.
  4. Loading from a test.html file created locally in the Playground's resources fails.

The "failure" in the Playground is just that web page appears blank. Presumably this is because the file just doesn't load, but the failure is silent.

import SwiftUI
import MarkupEditor   // Just to make sure the bundle access is okay from within the Playground
import PlaygroundSupport
import WebKit

class WKWebViewController: UIViewController, WKUIDelegate {

    var webView: WKWebView!

    override func loadView() {
        let webConfiguration = WKWebViewConfiguration()
        webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.uiDelegate = self
        view = webView
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        /* Works: Load a web page

            let myURL = URL(string:"https://www.apple.com")
            let myRequest = URLRequest(url: myURL!)
            webView.load(myRequest)

        */

        /* Works: Load an HTML string

            webView.loadHTMLString("<h1>Hello World</h1>", baseURL: nil)

        */

        /* Fails: Load a file from the MarkupEditor bundle

            if let url = bundle().url(forResource: "markup", withExtension: "html") {
                if let markupHtmlContents = try? String(contentsOf: url, encoding: .utf8) {
                    print("Contents of test.html:")
                    print(markupHtmlContents)
                    webView.loadFileURL(url, allowingReadAccessTo: url)
                } else {
                    webView.loadHTMLString("<h1>Could not load contents of markup.html</h1>", baseURL: nil)
                }
            } else {
                webView.loadHTMLString("<h1>Failed to load markup.html</h1>", baseURL: nil)
            }

        */

        /* Fails: Load a file from the playground resources */

        if let url = Bundle.main.url(forResource: "test", withExtension: "html") {
            if let markupHtmlContents = try? String(contentsOf: url, encoding: .utf8) {
                print("Contents of test.html:")
                print(markupHtmlContents)
                webView.loadFileURL(url, allowingReadAccessTo: url)
            } else {
                webView.loadHTMLString("<h1>Could not load contents of test.html</h1>", baseURL: nil)
            }
        } else {
            webView.loadHTMLString("<h1>Failed to load test.html</h1>", baseURL: nil)
        }

    }

    /// Return the bundle that is appropriate for the packaging.
    // Include if testing the MarkupEditor bundle resources for markup.html
    //func bundle() -> Bundle {
    //    #if SWIFT_PACKAGE
    //    return Bundle.module
    //    #else
    //    return Bundle(for: MarkupWKWebView.self)
    //    #endif
    //}

}

PlaygroundPage.current.needsIndefiniteExecution
PlaygroundPage.current.setLiveView(WKWebViewController())

I'm going to leave this issue open, since MarkupEditor basically doesn't work in a Playground. I didn't have any trouble building when I included the Playground in an Xcode workspace that included the MarkupEditor.xcodeproj and never saw the "Could not find markup.html, css, and js for this bundle" error from initRootFiles. Still, if you could get past that problem (presumably running in an Xcode workspace does that), then it still won't work properly in a Playground.

EduGonO commented 10 months ago

hello, thanks a lot for this!! seems then like I might be out of luck? I'm running the SimplestContentView code from the example found in the package in ContentView.swift, then running it on the playground. the "Could not find markup.html, css, and js for this bundle." error is the only thing i get, which opens the MarkupWKWebView file that i can't edit.

here's my screen:

Screenshot 2023-12-22 at 11 36 02
stevengharris commented 10 months ago

Yes, I think you will ultimately be out of luck until Playgrounds support loadFileURL properly. In the meantime, if you are Xcode-enabled and can make a change to the code, the following would probably fix the bundle loading in MarkupWKWebView:

    func url(forResource name: String, withExtension ext: String?) -> URL? {
        let url = bundle().url(forResource: name, withExtension: ext)
        return url ?? Bundle.main.url(forResource: name, withExtension: ext)
    }

    /// Initialize the directory at cacheUrl with a clean copy of the root resource files.
    ///
    /// Any failure to find or copy the root resource files results in an assertion failure, since no editing is possible.
    private func initRootFiles() {
        guard
            let rootHtml = url(forResource: "markup", withExtension: "html"),
            let rootCss = url(forResource: "markup", withExtension: "css"),
            let rootJs = url(forResource: "markup", withExtension: "js") else {
            assertionFailure("Could not find markup.html, css, and js for this bundle.")
            return
        }
        ...

I will make this change at some point and will update this issue when it gets pushed to a tagged package properly, which is probably what you are trying to use with Playgrounds. It's something I am going to do to support user-supplied CSS anyway, which would come in the bundle of the app consuming MarkupEditor, rather than in the MarkupEditor bundle.