IOS development: using JavaScript in Swift

Source: Internet
Author: User

IOS development: using JavaScript in Swift

In RedMonk's January 2015 programming language ranking, the Swift adoption rate rapidly soared, from 68 to 22 at the time of its launch, while Objective-C still ranked top 10, javaScript has become the hottest programming language of the year thanks to its native experience advantage on the iOS platform.

As early as 2013, Apple added the JavaScriptCore framework to OS X Mavericks and iOS 7, allowing developers to easily, quickly, and securely write applications using JavaScript. Whatever the call, the dominant JavaScript status has become a fact. Developers are eager to find the JS tool resources, and high-speed virtual machines such as OSX and iOS are also booming.

  JSContext/JSValue

JSContext is the runtime environment of JavaScript code. A Context is an environment for JavaScript code execution, also called a scope. When JavaScript code is run in a browser, JSContext is equivalent to a window, which allows you to easily execute JavaScript code such as creating variables, operations, and even defining functions:

// Objective-C

JSContext * context = [[JSContext alloc] init];

[Context evaluateScript: @ "var num = 5 + 5"];

[Context evaluateScript: @ "var names = ['grace ', 'ada', 'Margaret ']"];

[Context evaluateScript: @ "var triple = function (value) {return value * 3}"];

JSValue * tripleNum = [context evaluateScript: @ "triple (num)"];

// Swift

Let context = JSContext ()

Context. evaluateScript ("var num = 5 + 5 ")

Context. evaluateScript ("var names = ['grace ', 'ada', 'Margaret ']")

Context. evaluateScript ("var triple = function (value) {return value * 3 }")

Let tripleNum: JSValue = context. evaluateScript ("triple (num )")

Dynamic languages such as JavaScript require a Dynamic Type. Therefore, as shown in the last line of the Code, different values in JSContext are encapsulated in JSValue objects, including strings, values, arrays, functions, and even errors, null, and undefined.

JSValue contains a series of methods for obtaining Underlying values, as shown in the following table:

To retrieve the value of tripleNum in the preceding example, you only need to use the corresponding method:

// Objective-C

NSLog (@ "Tripled: % d", [tripleNum toInt32]);

// Tripled: 30

// Swift

Println ("Tripled: \ (tripleNum. toInt32 ())")

// Tripled: 30

  Tag Value(Subscripting Values)

By using the subscript symbol in the JSContext and JSValue instances, you can easily obtain the existing values in the context. Among them, JSContext can only be a string subscript to put objects and arrays, while JSValue can be a string or an integer subscript.

// Objective-C

JSValue * names = context [@ "names"];

JSValue * initialName = names [0];

NSLog (@ "The first name: % @", [initialName toString]);

// The first name: Grace

// Swift

Let names = context. objectForKeyedSubscript ("names ")

Let initialName = names. objectAtIndexedSubscript (0)

Println ("The first name: \ (initialName. toString ())")

// The first name: Grace

The Swift language was born shortly after all, so it cannot use subscript symbols as easily as Objective-C. Currently, the Swift method can only implement subscripts such as objectAtKeyedSubscript () and objectAtIndexedSubscript.

  Function call(Calling Functions)

We can use the Foundation class as a parameter to directly call JavaScript Functions encapsulated in JSValue from Objective-C/Swift code. Here, JavaScriptCore plays a cohesive role again.

// Objective-C

JSValue * tripleFunction = context [@ "triple"];

JSValue * result = [tripleFunction callWithArguments: @ [@ 5];

NSLog (@ "Five tripled: % d", [result toInt32]);

// Swift

Let tripleFunction = context. objectForKeyedSubscript ("triple ")

Let result = tripleFunction. callWithArguments ([5])

Println ("Five tripled: \ (result. toInt32 ())")

  Exception Handling(Exception Handling)

JSContext also has an exclusive stunt. By setting the exceptionHandler attribute in the Context Environment, you can check and record the syntax, type, and running errors. ExceptionHandler is a callback handler that receives the reference of JSContext for exception handling.

// Objective-C

Context. exceptionHandler = ^ (JSContext * context, JSValue * exception ){

NSLog (@ "JS Error: % @", exception );

};

[Context evaluateScript: @ "function multiply (value1, value2) {return value1 * value2"];

// JS Error: SyntaxError: Unexpected end of script

// Swift

Context. exceptionHandler = {context, exception in

Println ("JS Error: \ (exception )")

}

Context. evaluateScript ("function multiply (value1, value2) {return value1 * value2 ")

// JS Error: SyntaxError: Unexpected end of script

  JavaScript function call

I learned how to get different values and call functions from the JavaScript environment. In turn, how can I get Objective-C or Swift-defined custom objects and methods in the JavaScript environment? To obtain the local client code from JSContext, there are two main ways: Blocks and JSExport.

Blocks (Block)

In JSContext, if the Objective-C code block is assigned an identifier, JavaScriptCore automatically encapsulates it in JavaScript Functions, therefore, it is more convenient to use the Foundation and Cocoa classes on JavaScript-This proves the strong cohesion of JavaScriptCore. Currently, CFStringTransform can also be used in JavaScript, as shown below:

// Objective-C

Context [@ "simplifyString"] = ^ (NSString * input ){

NSMutableString * mutableString = [input mutableCopy];

CFStringTransform (_ bridge CFMutableStringRef) mutableString, NULL, kCFStringTransformToLatin, NO );

CFStringTransform (_ bridge CFMutableStringRef) mutableString, NULL, kCFStringTransformStripCombiningMarks, NO );

Return mutableString;

};

NSLog (@ "% @", [context evaluateScript: @ "simplifyString ('?????! ') "]);

// Swift

Let simplifyString: @ objc_block String-> String = {input in

Var mutableString = NSMutableString (string: input) as CFMutableStringRef

CFStringTransform (mutableString, nil, kCFStringTransformToLatin, Boolean (0 ))

CFStringTransform (mutableString, nil, kCFStringTransformStripCombiningMarks, Boolean (0 ))

Return mutableString

}

Context. setObject (unsafeBitCast (simplifyString, AnyObject. self), forKeyedSubscript: "simplifyString ")

Println (context. evaluateScript ("simplifyString ('?????! ')"))

// Annyeonghasaeyo!

It should be noted that Swift's speedbump is only applicable to Objective-C blocks and is useless to Swift closures. To use a closure in a JSContext, there are two steps: one is to declare with @ objc_block, and the other is to convert Swift's knuckle-whitening unsafeBitCast () function to AnyObject.

  Memory Management(Memory Management)

The code block can capture variable references, while the strong references of all JSContext variables are kept in JSContext. Therefore, pay attention to avoid the issue of strong circular references. In addition, do not capture JSContext or any JSValues in the code block. We recommend that you use [JSContext currentContext] to obtain the current Context object and pass the value as a parameter to the block as needed.

  JSExport Protocol

You can also use custom objects on JavaScript using the JSExport protocol. The instance methods and class methods declared in the JSExport protocol can automatically interact with JavaScrip regardless of attributes. The article will introduce the specific practice process later.

  JavaScriptCore practices

We can use some examples to better understand how to use the above skills. First define a Person model that complies with the JSExport sub-Protocol PersonJSExport, and then use JavaScript to create and enter instances in JSON. What Should NSJSONSerialization do if there is an entire JVM?

  PersonJSExports and Person

The PersonJSExports protocol executed by the Person class specifies the available JavaScript attributes ., Class methods are indispensable during creation, because JavaScriptCore is not suitable for initialization and conversion. We cannot use var person = new Person () as we do with native JavaScript types ().

// Objective-C

// In Person. h -----------------

@ Class Person;

@ Protocol PersonJSExports @ property (nonatomic, copy) NSString * firstName;

@ Property (nonatomic, copy) NSString * lastName;

@ Property NSInteger ageToday;

-(NSString *) getFullName;

// Create and return a new Person instance with 'firstname' and 'lastname'

+ (Instancetype) createWithFirstName :( NSString *) firstName lastName :( NSString *) lastName;

@ End

@ Interface Person: NSObject @ property (nonatomic, copy) NSString * firstName;

@ Property (nonatomic, copy) NSString * lastName;

@ Property NSInteger ageToday;

@ End

// In Person. m -----------------

@ Implementation Person

-(NSString *) getFullName {

Return [NSString stringWithFormat: @ "% @", self. firstName, self. lastName];

}

+ (Instancetype) createWithFirstName :( NSString *) firstName lastName :( NSString *) lastName {

Person * person = [[Person alloc] init];

Person. firstName = firstName;

Person. lastName = lastName;

Return person;

}

@ End

// Swift

// Custom protocol must be declared with '@ objc'

@ Objc protocol PersonJSExports: JSExport {

Var firstName: String {get set}

Var lastName: String {get set}

Var birthYear: NSNumber? {Get set}

Func getFullName ()-> String

// Create and return a new Person instance with 'firstname' and 'lastname'

Class func createWithFirstName (firstName: String, lastName: String)-> Person

}

// Custom class must inherit from 'nsobject'

@ Objc class Person: NSObject, PersonJSExports {

// Properties must be declared as 'dynamic'

Dynamic var firstName: String

Dynamic var lastName: String

Dynamic var birthYear: NSNumber?

Init (firstName: String, lastName: String ){

Self. firstName = firstName

Self. lastName = lastName

}

Class func createWithFirstName (firstName: String, lastName: String)-> Person {

Return Person (firstName: firstName, lastName: lastName)

}

Func getFullName ()-> String {

Return "\ (firstName) \ (lastName )"

}

}

  Configure JSContext

After creating the Person class, you must first export it to the JavaScript environment, and also import the Mustache JS library to apply the template to the Person object.

// Objective-C

// Export Person class

Context [@ "Person"] = [Person class];

// Load Mustache. js

NSString * mustacheJSString = [NSString stringWithContentsOfFile:... encoding: NSUTF8StringEncoding error: nil];

[Context evaluateScript: mustacheJSString];

// Swift

// Export Person class

Context. setObject (Person. self, forKeyedSubscript: "Person ")

// Load Mustache. js

If let mustacheJSString = String (contentsOfFile:..., encoding: NSUTF8StringEncoding, error: nil ){

Context. evaluateScript (mustacheJSString)

}

JavaScript Data & Processing

The following is a simple JSON example, and a new Person instance is created using JSON.

Note: JavaScriptCore implements Objective-C/Swift method name and JavaScript code interaction. Because JavaScript does not have a named parameter, any additional parameter names are appended to the function name by Camel-Case. In this example, the Objective-C method createWithFirstName: lastName: is changed to createWithFirstNameLastName () in JavaScript ().

// JSON

[

{"First": "Grace", "last": "Hopper", "year": 1906 },

{"First": "Ada", "last": "Lovelace", "year": 1815 },

{& Quot; first & quot;: & quot; Margaret & quot;, & quot; last & quot;: & quot; Hamilton & quot;, & quot; year & quot;: 1936}

]

// JavaScript

Var loadPeopleFromJSON = function (jsonString ){

Var data = JSON. parse (jsonString );

Var people = [];

For (I = 0; I <data. length; I ++ ){

Var person = Person. createWithFirstNameLastName (data [I]. first, data [I]. last );

Person. birthYear = data [I]. year;

People. push (person );

}

Return people;

}

  Try it

Now you only need to load JSON data and call it in JSContext, parse it into the Person object array, and then use the Mustache template for rendering:

// Objective-C

// Get JSON string

NSString * peopleJSON = [NSString stringWithContentsOfFile:... encoding: NSUTF8StringEncoding error: nil];

// Get load function

JSValue * load = context [@ "loadPeopleFromJSON"];

// Call with JSON and convert to an NSArray

JSValue * loadResult = [load callWithArguments: @ [peopleJSON];

NSArray * people = [loadResult toArray];

// Get rendering function and create template

JSValue * mustacheRender = context [@ "Mustache"] [@ "render"];

NSString * template = @ "{getFullName }}, born {birthYear }}";

// Loop through people and render Person object as string

For (Person * person in people ){

NSLog (@ "% @", [mustacheRender callWithArguments: @ [template, person]);

}

// Output:

// Grace Hopper, born 1906

// Ada Lovelace, born 1815

// Margaret Hamilton, born 1936

// Swift

// Get JSON string

If let peopleJSON = NSString (contentsOfFile:..., encoding: NSUTF8StringEncoding, error: nil ){

// Get load function

Let load = context. objectForKeyedSubscript ("loadPeopleFromJSON ")

// Call with JSON and convert to an array of 'person'

If let people = load. callWithArguments ([peopleJSON]). toArray ()? [Person] {

// Get rendering function and create template

Let mustacheRender = context. objectForKeyedSubscript ("Mustache"). objectForKeyedSubscript ("render ")

Let template = "{getFullName }}, born {birthYear }}"

// Loop through people and render Person object as string

For person in people {

Println (mustacheRender. callWithArguments ([template, person])

}

}

}

// Output:

// Grace Hopper, born 1906

// Ada Lovelace, born 1815

// Margaret Hamilton, born 1936

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.