In my new application—Demo Remote—I needed a markdown parser to convert the user’s input markdown file into HTML. Unfortunately, most Swift markdown parsers suffer from a set of different issues, namely being large frameworks, having odd external dependencies, or too many features for what I needed. So I wondered if I could just use a simple markdown javascript parser that I know works, and the answer is yes I can. And I did.
JavaScriptCore is a framework available on macOS iOS, and tvOS to run javascript without a web browser. This is really great for when you want to reuse some javascript code, have dynamic business logic, or maybe even build a simple plugin system.
JavaScriptCore requires a JSContext
to evaluate the script which returns a JSValue
. This value can then be converted to a native object or primitive type, NSHipster has a great table on all the types.
My super simple Markdown parser is below, it imports a script from the bundle, but could very easily be enhanced by pulling one from a server so I can enhance the script quickly.
import Cocoa
import JavaScriptCore
class MarkdownRenderer: NSObject {
// Fetch the script from file and parse it as a string
private static var source : String? = {
guard let url = Bundle.main.path(forResource: "marked.min", ofType: "js", inDirectory: nil, forLocalization: nil) else {
return nil
}
guard let newsource = try? NSString(contentsOfFile: url, encoding: String.Encoding.utf8.rawValue) else {
return nil
}
return newsource as String
}()
// The static function that is called from elsewhere in the app
// returns HTML in a string
static func render(_ message: String) -> String? {
if let jsSource = source {
// Create a context
let context = JSContext()
// Evaluate the script
context?.evaluateScript(jsSource)
// Select the function in the script I want to call
let testFunction = context?.objectForKeyedSubscript("marked")
// Call the function with the arguments (in this case the markdown)
let result = testFunction?.call(withArguments: [message])
// Convert the result to a string
return result?.toString()
} else {
return nil
}
}
}
JavaScriptCore is a really cool way to extend your native application and opens up your application to a greater range of options. Whatever your opinion of JS the langauge is, it’s hard to deny it’s a massive community with a package for everything.
I’ll be reaching for this again the future when native doesn’t make sense.