diff --git a/Source/Views/DownView.swift b/Source/Views/DownView.swift index 4b95eef5..e570ab57 100644 --- a/Source/Views/DownView.swift +++ b/Source/Views/DownView.swift @@ -28,10 +28,12 @@ open class DownView: WKWebView { /// - configuration: Optional custom web view configuration. /// - options: `DownOptions` to modify parsing or rendering, defaulting to `.default` /// - didLoadSuccessfully: Optional callback for when the web content has loaded successfully + /// - writableBundle: Whether or not the bundle folder is writable. /// - Throws: `DownErrors` depending on the scenario - public init(frame: CGRect, markdownString: String, openLinksInBrowser: Bool = true, templateBundle: Bundle? = nil, configuration: WKWebViewConfiguration? = nil, options: DownOptions = .default, didLoadSuccessfully: DownViewClosure? = nil) throws { + public init(frame: CGRect, markdownString: String, openLinksInBrowser: Bool = true, templateBundle: Bundle? = nil, writableBundle: Bool = false, configuration: WKWebViewConfiguration? = nil, options: DownOptions = .default, didLoadSuccessfully: DownViewClosure? = nil) throws { self.options = options self.didLoadSuccessfully = didLoadSuccessfully + self.writableBundle = writableBundle if let templateBundle = templateBundle { self.bundle = templateBundle @@ -86,6 +88,7 @@ open class DownView: WKWebView { // MARK: - Private Properties let bundle: Bundle + let writableBundle: Bool var options: DownOptions private lazy var baseURL: URL = { @@ -113,7 +116,12 @@ private extension DownView { let pageHTMLString = try htmlFromTemplate(htmlString) #if os(iOS) - loadHTMLString(pageHTMLString, baseURL: baseURL) + if writableBundle { + let newIndexUrl = try writeTempIndexFile(pageHTMLString: pageHTMLString) + loadFileURL(newIndexUrl, allowingReadAccessTo: newIndexUrl.deletingLastPathComponent()) + } else { + loadHTMLString(pageHTMLString, baseURL: baseURL) + } #elseif os(macOS) let indexURL = try createTemporaryBundle(pageHTMLString: pageHTMLString) loadFileURL(indexURL, allowingReadAccessTo: indexURL.deletingLastPathComponent()) @@ -125,6 +133,14 @@ private extension DownView { return template.replacingOccurrences(of: "DOWN_HTML", with: htmlString) } + #if os(iOS) + func writeTempIndexFile(pageHTMLString: String) throws -> URL { + let newIndexUrl = bundle.resourceURL!.appendingPathComponent("tmp_index.html") + try pageHTMLString.write(to: newIndexUrl, atomically: true, encoding: .utf8) + return newIndexUrl + } + #endif + #if os(macOS) func createTemporaryBundle(pageHTMLString: String) throws -> URL { guard let bundleResourceURL = bundle.resourceURL diff --git a/Tests/DownViewTests.swift b/Tests/DownViewTests.swift index 69a4d469..3402ca43 100644 --- a/Tests/DownViewTests.swift +++ b/Tests/DownViewTests.swift @@ -97,6 +97,40 @@ class DownViewTests: XCTestCase { } } + func testInstantiationWithCustomWritableTemplateBundle() { + let expect1 = expectation(description: "DownView accepts and loads custom bundle files from a user writable location") + + guard + let bundle = Bundle(for: type(of: self)).url(forResource: "TestDownView", withExtension: "bundle"), + let templateBundle = Bundle(url: bundle) + else { + XCTFail("Test template bundle not found in test target!") + return + } + + let markdownString = """ +```swift +let x = 1 +``` +""" + var downView: DownView? + downView = try? DownView(frame: .zero, markdownString: markdownString, templateBundle: templateBundle, writableBundle: true, didLoadSuccessfully: { + self._pageContents(for: downView!) { htmlString in + XCTAssertTrue(htmlString!.contains("css/down.min.css")) + XCTAssertTrue(htmlString!.contains("hljs-keyword")) + XCTAssertTrue(htmlString!.contains("But also, custom HTML!")) + + expect1.fulfill() + } + }) + + waitForExpectations(timeout: 10) { (error: Error?) in + if let error = error { + XCTFail("waitForExpectationsWithTimeout errored: \(error)") + } + } + } + func testDownOptions() { let markdownString = "## [Down](https://github.com/iwasrobbed/Down)\n\nI'm strong!" let renderedHTML = "I'm strong!"