Using Javascript in the Swift case: Editing an editor that transforms Markdown into HTML

Source: Internet
Author: User


Original: Using JavaScript in Swift projects:building a Markdown to HTML Editor

GABRIEL Theodoropoulos

Translated by: Kmyhy


Always wanted to write an essay. about how Swift and Javascript are combined to build powerful apps that support rich text. This is not the first time we've heard that we're going to embed the Javacript code in the IOS case. But when you've finished reading this article, you'll feel that this process will become as simple as ever, as if it were magic. You just need to do very little work. The best of these is a framework called the JavaScriptCore framework.



You might think, why is it always someone who loves to use JavaScript, why not use Swift to fully function? That's actually what I want to ask. Here we have a few reasons:


    • IOS developers who have written web apps and have forgotten how to write Javascript have passed through the JavaScriptCore framework. You will have the opportunity to use the language they have loved again.



    • For some tasks, it is very likely that there are existing JavaScript repositories, and that they and the functionality that you are using with Swift are actually not.


      Why not use it now?

    • It is very likely that some tasks will be easier with JavaScript.



    • You may want to control the APP's line remotely. The ability to put JavaScript code in the server instead of the App bundle.


      You need to be careful when doing this, because it is very likely to cause a catastrophe.

    • Make your App more resilient and powerful.
    • You have a strong curiosity to use JavaScript in your IOS case.




Of course, in addition to that, you might think of a better reason to use JavaScript in IOS.



Now, you're not too busy, so let's see what we need to know about the background. First, JavaScript has its own environment, or, more specifically, it needs to run in a virtual machine. In the JavaScriptCore framework. Use Jsvirtualmachine to represent virtual machines. Of course you don't usually deal with it.



You can run multiple virtual machines in one App, and there's no way to exchange the numbers directly.






Second, the most you can use is actuallyJSContext. This is true for the actual environment (context) of the JavaScript foot. In a virtual machine (jsvirtualmachine) can have multiple context, you can in the context of the passing of the numbers. As you can see in your continued internal content.JSContextexposes the SWIFT code to JavaScript and exposes the JavaScript code to Swift. We're going to use it a lot, but most of the ways we use it are the same.






JSContextAll of the values in the Jsvalue are the same as thoseJSValueused to represent random types of JavaScript values. Suppose you want to visit JavaScript variables or functions from Swift, you can use theJSValueimage. Of course there are ways to convert jsvalue into specific types. For example, converttoString()to a string and convert it into a dictionary using thetoDictionary()method (which you'll see later).



Here is a complete list of methods.



I recommend that you read the official JavaScriptCore framework documents.



What you said earlier may have a general idea of the tools you will use. It also helps you to understand the contents of the back.



Right now. Let's officially start.



Let's take a look at what today's recipe is all about.


Demo Case Overview


We're going through a simple demonstration of the JavaScriptCore framework's characteristics. This case demonstrates how to use JavaScript in Swift. We will use the classic "Hello World" Demo sample (the example I like most). It saves a string value into the JavaScript variable. Our first concern is how to ask this change from Swift. We'd better use the Xcode console to print it out. We'll continue to do a few simple examples to step through a lot of other features. Of course, we're not just going to learn how to value Swift from JavaScript, we have to study the reverse direction.



Therefore, we need both to write a Swift code and to write JavaScript codes. But don't worry. The fact that JavaScript is not so difficult to deal with.



It's not difficult!



Attention. All the output from here is in the console, so we can focus on what really pays attention.






We have learned enough of the basics. We can look into how to use one language in another language.
To be more realistic, let's use a third-party JavaScript Library to try it out. The second part of the case. We're going to compile a markdown/html, or we'll go through a "converter library" to do this for us, "said the manager." Our work is simply to collect the user input MarkDown text from the edit box (a simple one), and then pass it on to theUITextViewJavaScript environment and display the HTML returned in the JavaScript environment to one of themUIWebView. Use a button to make a switch, and adjust the code. Take a look at the map:






In the third and final part, we will demonstrate how to pass on the customization of the components and methods to JavaScript Context.



In addition, we will create a pair of images and their own values in JavaScript in accordance with this definition.



We'll end up showing an iPhone's list of types (model names), and their earliest and latest OS version numbers. " and their pictures. The numbers are stored in a CSV file, and we'll use a third-party repository for parsing. To get the parsed numbers. We will use our custom Swift in JavaScript to render the statistics for the custom image, and then return the results to Swift. ". We'll use a TableView to show this list.



For example, the following illustration shows:






The above describe in general the three distinct tasks, that would let us get to know the JavaScriptCore framework. As there is a lot of things wrapped up together in the package of one, we'll have a initial menu screens that we'll use T O Navigate to the proper part of the project:






To steal lazy, we offer a starting case. You can download it here.



When you're done downloading, you'll be able to start your javascriptcore journey. In this article, we'll do a few things, but eventually we'll be clear that most of them are actually standard routines, and for the ultimate goal, we have to repeat these routines.



Let's start out.


Call JavaScript from Swift


As described in the introduction, the most basic role in JavaScriptCore is theJSContextcategory.



AnJSContextimage is a bridge between the JavaScript environment and the native JavaScript foot. So at first we need toBasicsViewControllerdeclare this in the middle. In theBasicsViewController.swiftarchives. Find the head of the category. Add variables such as the following:


var jscontext:jscontext!


jsContextThe image must be of a category. Suppose you initialize it as a local variable in the method, and you can't get to it when the method is finished.



Now we have to enter the JavaScriptCore framework and add this sentence to the file head:


Import JavaScriptCore


Next you want to initializejsContextthe image and then use it. But before we do that, we'll write a basic JavaScript code. We will compile them in a jssource.js file. You will be able to find this file in the Expert Navigator that started the case. We're going to announce a string of "Hello world" in there, and then there are a few simple functions that we'll be interviewing through IOS. Let's say you don't have to learn JavaScript, it's really simple. You can understand it at a glance.






Openjssource.jsThe file and add this change in the beginning:


var HelloWorld = "Hello world!"


Printing this change in the console is the first goal we've come up with!



Go back toBasicsViewController.swiftthe archives and create a way to complete 2 tasks:


    1. To initialize the nature of what we declared earlierjsContext.



    2. Download the Jssource.js file and upload the file to JavaScript, which is the code that it can write to in the file.




BasicsViewControllerCreate a new method in, initialize thejsContextvariables.



The method is very simple:


func initializeJS() {
    self.jsContext = JSContext()    

}


The second task above is divided into several steps, but it is also very simple.



Let's take a look at the source code, and then we'll discuss it:


Func initializeJS() {
     ...

     / / Specify the jssource.js file path
     If let jsSourcePath = Bundle.main.path(forResource: "jssource", ofType: "js") {
         Do {
             / / Load the contents of the file into a String
             Let jsSourceContents = try String(contentsOfFile: jsSourcePath)

             // Add the scripts included in jsSourceContents to the Javascript runtime via the jsContext object
             self.jsContext.evaluateScript(jsSourceContents)
         }
         Catch {
             Print(error.localizedDescription)
         }
     }

} 


The notes in the source code explain exactly what they mean.



First of all. We specified the Jssource.js file path, and then loaded the file into ajsSourceContentsstring (now, these are the content you previously wrote in the Jssource.js file). Assume success. The next sentence is important: we usejsContext"calculate" The JavaScript code, in such a way we can immediately transfer our JS code to JAVASCRIPT environment.



Then add a new approach:


func helloWorld() {
    if let variableHelloWorld = self.jsContext.objectForKeyedSubscript("helloWorld") {
        print(variableHelloWorld.toString())
    }
}


This method is very simple, but it does not work very small. The core part of this approach isobjectForKeyedSubscript(_:)a sentence that we pass through to ask about the changes in JavasscripthellowWorl.



The first sentence returns a jsvalue (assuming no value is returned as nil), and then puts itvariableHelloWorldin the save. In simple words, this is the end of our first goal, because we wrote some JavaScript in Swift. We can handle it in whatever way!



How do we deal with this variable that holds the "Hello World" string? To get it out to the console.



Now, we'reviewDidAppear(_:)calling these two new methods in:


override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    self.initializeJS()
    self.helloWorld()
}


To run the APP, click the first title for the Basics button. Open Xcode's console, and our "Hello World" word is javascriptcore to the console!









When using Swift and JavaScript in a mix, it's definitely not just about defining a few variables, but then printing their values. So let's create the first JavaScript function, let's see how we can use it.



I can't find any other simple examples. So use the following function. Used to synthesize the full name of the first and last names. In the Jssource.js file, add:


function getFullname(firstname, lastname) {
    return firstname + " " + lastname;
}


The surname and the name in a person's name are used as two parameters of a function. Save the file. ReturnBasicsViewController.swift.



There are two steps to calling a JavaScript function in Swift:



First, askjsContextfor the calling function name, which will return a jsvalue image. This is the same as the amount of helloWorld we visit. And then. Call this function through the method name. Pass in the parameters it requires.



You'll make it clear that you're going to have a new approach now:


func jsDemo1() {
    let firstname = "Mickey"
    let lastname = "Mouse"

    if let functionFullname = self.jsContext.objectForKeyedSubscript("getFullname") {

    }
}


Right now. SwiftfunctionFullnamehas quoted agetFullnameJS function.



Then the second step is to call this JS function:


func jsDemo1() {
    let firstname = "Mickey"
    let lastname = "Mouse"

    if let functionFullname = self.jsContext.objectForKeyedSubscript("getFullname") {
        // Call the function that composes the fullname.
        if let fullname = functionFullname.call(withArguments: [firstname, lastname]) {
            print(fullname.toString())
        }
    }
}


call(withArguments:)method is used to call the Getfullname function, and to cause it to perform.



callMethod simply receives a parameter, which is a random array of types, assuming that the function has no parameters, you can pass a nil. In our example, we pass FirstName and LastName.



The return value of this method is also a jsvalue, and we'll print it to the console. In the back, you'll see that the return value of the method is not necessarily intentional to us. So we will not use it.






Now, let's call thejsDemo1()method:


override func viewDidAppear(_ animated: Bool) {
    ...

    self.jsDemo1()
}


Running items, you will see in the console such as the following outputs:






This is not interesting, but you need to make it clear that you are showing the results of calling JS functions in Swift. At the same time, we have been able to conclude this process through this part:


    1. Construct a jscontext image.



    2. Download the JavaScript code and Calculate (evaluate) its value (or pass it to JavaScript environment).
    3. ThroughJSContexttheobjectForKeyedSubscript(_:)method to visit the JS function.



    4. Call the JS function and handle the return value (optional).
Handle JavaScript in a common


In the beginning. It is always unavoidable to encode, but it is necessary for the developers to see the bug. That's how they're going to resolve it. Assuming that you are mixing JS and Swift, how do you know where to try? Is Swift still JS? It's easy to make mistakes in Swift, but can we see the mistakes that happen on the JS side?



Fortunately, the JavaScriptCore framework provides an abnormal way to capture the JS environment in Swift. Observation is often a standard procedure, and we will understand it in the back, but how to deal with it is obviously a very subjective thing.



Back to the code we just programmed. Let's change theinitializeJS()method. To catch JS to run the time.



In this method, after the initialization of the Jscontext. Add for example the following sentence:


func initializeJS() {
    self.jsContext = JSContext()

    // Add an exception handler.
    self.jsContext.exceptionHandler = { context, exception in
        if let exc = exception {
            print("JS Exception:", exc.toString())
        }
    }

    ...
}


See, Exceptionhandler is a closed bag. Every time a jscontext happens, it calls this closure.



It has two parameters: the context in which the Chang is located (that isJSContext), and the difference itself. This exception is a jsvalue.



Here we are simply going to print out the usual messages to the console.



Let's try to make a difference to test whether this method is going to work.



For this, we have to write in jssource.js and a JS function. This function uses an integer array as the parameter (integer and negative numbers). Returns a dictionary that includes the maximum, minimum, and average values in this array.



Open the Jssource.js file and join the function:


function maxMinAverage(values) {
    var max = Math.max.apply(null, values);
    var min = Math.min.apply(null, values);
    var average = Math.average(values);

    return {
        "max": max,
        "min": min,
        "average": average
    };
}


The mistake in the code is that there is noMathaverage function in the image, so this sentence is completely wrong:


var average = Math.average (values);


Pretending we don't know about this situation. BackBasicsViewController.swift, add a new method:


func jsDemo2() {
    let values = [10, -5, 22, 14, -35, 101, -55, 16, 14]

    if let functionMaxMinAverage = self.jsContext.objectForKeyedSubscript("maxMinAverage") {
        if let results = functionMaxMinAverage.call(withArguments: [values]) {
            if let resultsDict = results.toDictionary() {
                for (key, value) in resultsDict {
                    print(key, value)
                }
            }
        }
    }
}


First of all. We've created an array of random numbers. We use it asmaxMinAveragea parameter to adjust the method. This method is quoted in Swift through thefunctionMaxMinAverageimage.



When calling the call method, we pass this array as the only parameter. Assuming everything is OK, we'll follow the Dictionary (notetoDictionary()method) to return the result, print the value one by one to the console (the Maxminaverage method returns a dictionary, so we print both key and value)



It's time to test it, but we have to call thisjsDemo2()method first:


override func viewDidAppear(_ animated: Bool) {
    ...

    self.jsDemo2()
}


Running APP, we expect to print out the maximum, minimum, and average of the array.




However, we get the last ugly, very straightforward, from the JS running environment:


JS Exception: TypeError: Math.average is not a function. (In ‘Math.average(values)‘, ‘Math.average‘ is undefined)





Before we resolve this intentional mistake, let's think about the meaning of this. Try to. If you can't catch the JS, you can't find out where the bug really is. In order to save our time, especially for large, complex apps, it's not something that we intentionally design, so it's really a pain to look at it in a bad way to find a bug.



So, after teaching, we should solve the problem.



In the Jssource.js file, change the Code>minmaxaverage function to:


function maxMinAverage(values) {
    var max = Math.max.apply(null, values);
    var min = Math.min.apply(null, values);

    var average = null;
    if (values.length > 0) {
        var sum = 0;
        for (var i=0; i 


Using Javascript in the Swift case: Editing an editor that transforms Markdown into HTML


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.