iOS开发-javaScript交互

金马争霸赛末段总结,再次抽奖,对自家吧证明了一些:幸运从来不是于天而降。

前言

目前混合开发模式迎来了划时代的上进,跨平台支付、热更新等优点决定了这种模式之主要地位。虽然前端界面在竞相、动效等多点相差原生应用还有区别,但得混合开发只会受进一步多的号接受。在iOS中,混合开发模式让分成两独时代,分别是iOS7事先的坑爹时代和之后的金一代,其分割代表为JavaScriptCore框架

但是证明了好几:我得以拉动被身边人幸运。

坑爹时代

作宏观避开iOS7之眼前版的福星,我只得从某位前辈的口中得知那悲惨的辰。作为老年代唯一能及眼前端界面交互的招就是是UIWebView,先不说她本身的内存泄露缺陷,下面是同段子前辈写了之代码:

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    NSString * address = request.URL.absoluteString;
    for (NSString * black in _blackList) {
        if ([address containsString: black]) {
            return NO;
        }
    }
    for (NSString * event in _eventList) {
        if ([address containsString: event]) {
            SEL callback = NSSelectorFromString(_callbacks[event]);
            [self performSelector: callback];
            return [event containsString: @"shouldOpen=1"];
        }
    }
    return YES;
}

当怪年代,前辈的小伙伴等把前端事件之接触条件设置为链接跳转,然后经过链接中之重要性字符来判断处理操作。为这个,需要定义好把个数据集合来囤这些主要字符的拍卖操作。如果遇到应用以及前端交换交互数据的时,那同样抬高串的参数字符全部拼接在伸手地址里,想想呢是醉了。另外的互方式就是通过stringByEvaluatingJavaScriptFromString措施来推行js代码。

恭贺区区独稍伙伴同时中奖(我们帅气的青年人又平等赖中奖哦)

JavaScriptCore

JavaScriptCore举凡平等法用来对JS代码进行剖析和提供履环境之开源框架,极大的简化了咱的彼此过程。下面从种类及JS代码相互调用的点滴单例外操作介绍其中相对应之点子

列调用JS代码

  • JSContext
    一个JSContext对象是JavaScript运作的大局环境目标,它提供了代码运行和登记方式接口的服务。下面的代码就创办了一个JSContext对象,并且定义了一样部分的JS代码加入到实践环境遭到
    let context = JSContext()
    context.evaluateScript(” var age = 22 “)
    context.evaluateScript(” var name = ‘SindriLin’ “)
    context.evaluateScript(” var birth = 1993-01-01 “)
    context.evaluateScript(” var createPerson =
    function(age, name, birth)
    {
    return {‘age’: age, ‘name’: name, ‘birth’: birth}
    } “)
    context.evaluateScript(” var codeDescription = ‘The code create
    three value and a function to create a dictionary stored person
    information’ “)
    此外,在JS代码执行过程中,可能会见起语法错误等多误,通过下的代码可以针对这些错误进行拍卖
    context?.exceptionHandler = { context, exception in
    print(“Java Script Run Error: (exception)”)
    }

  • JSValue
    JSValue是所有JSContext操作后归的值,包装了几乎有的数据类型,包括错误与IMP指针等。在JSValue恍如组织面临有多独toXXXX取名的章程换成为iOS数据类型以及call措施来调用方法。下面的代码从JSContext条件受到拿走已在的部分变量,并且实施创建一个囤积person消息之字典
    let age = context?.objectForKeyedSubscript(“age”)
    let name = context?.objectForKeyedSubscript(“name”)
    let birth = context?.objectForKeyedSubscript(“birth”)
    let createFunction =
    context?.objectForKeyedSubscript(“createPerson”)
    let codeDescription =
    context?.objectForKeyedSubscript(“codeDescription”)
    let person = createFunction.call(withArguments: [age.toInt32(),
    name.toString(), birth.toString()])

    let personInfo = "name: \(person["name"]) age: \(person["age"] and birth: \(person["birth"])"
    print("The javaScript code description: \(codeDescription.toString())")
    print("The created person \(personInfo) ")
    

透过地方的例证,我们可见见,只要了解及JS代码中我们要调用的法子信息,通过JSContext + JSValue的主意我们即便能轻轻松松的当类型受到调用前端界面的方,而不再用拼接长串参数字符通过链接地址传递给前端界面

JS调用项目代码

JavaScript顾我们代码中的对象与艺术发生半点种办法:BlocksJSExport

  • Blocks
    起定义的block代码可以由此JSContext转换成JS代码中之函数指针调用,这里存在一个坑就是Swift吃的闭包无法完成如此的类型转换,因此这种方式的操作流程在Swift中凡是这么的:Closure
    -> block ->
    function pointer。在闭包转成为block的这无异于历程遭到,需要动用一个要害的根本符@convention
    let stringConvert: @convention(block) (String)->String = {
    let pinyin = NSMutableString(string: $0) as CFMutableString
    CFStringTransform(pinyin, nil, kCFStringTransformToLatin, false)
    CFStringTransform(pinyin, nil,
    kCFStringTransformStripCombiningMarks, false)
    return pinyin as String
    }

    let convertObjc = unsafeBitCast(stringConvert, to: AnyObject.self)
    context?.setObject(convertObjc, forKeyedSubscript: "convertFunc")
    let convertFunc = context?.objectForKeyedSubscript("convertFunc")
    print("林欣达的拼音是\(convertFunc.call(withArguments: ["林欣达"]).toString())")
    

    这会儿,只要前端在JS的按钮点击代码中调用convertFunc()眼看句代码就会见履是closure面临的代码。使用这种办法如留意由于闭包的抓获特性,有或会见促成您的JSContext目标为引用而一筹莫展为保释,使用JSContext.current()落当前上生温柔来化解引用问题

  • JSExport
    JS中调用iOS措施的下,通过调用JSExport的派生协议章程来实现。所有派生协议的方法会自动提供给JavaScript代码应用,这个以底下的demo中好见到

背胶王薛总为有幸

实战

在本文demo中我形容了千篇一律段落JS代码,下面放这段代码和运行效果。其中要留意的凡按钮的onclik意味着按钮点击的应事件:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
        <div style="margin-top: 20px">
            <h2 align="center" style="color:#ff0000">JS与iOS交互</h2>
            <input type="button" value="点击后切换控制器的背景颜色" onclick="sindrilin.call()">
        </div>
        <div style="color:#7BBDE5">
            <br />
            <br />
            账户:
            <input id="account" type="text">
            <br />
            密码:
            <input id="password" type="password">
        </div>
        <div>
            <input type="button" value="登录" onclick="login()">
        </div>

        <script>

            var login = function()
            {
                account = document.getElementById("account")
                password = document.getElementById("password")
                var accountInfo = JSON.stringify({"account": account.value, "password": password.value});
                sindrilin.login(accountInfo);
            }

            var alertFromIOS = function(message)
            {
                    alert(message)
            }

        </script>
    </body>
</html>

首先我们需要加载是HTML文本,然后取代码运行的全局环境目标。基本上以享有的HTML格式文件中,获取环境目标的keyPath都是一样的:

let jsPath = Bundle.main().pathForResource("interaction", ofType: "html")
webView.loadRequest(URLRequest(url: URL(fileURLWithPath: jsPath!)))
interactionContext = webView.value(forKeyPath: "documentView.webView.mainFrame.javaScriptContext") as? JSContext
interactionContext?.exceptionHandler = {
    print("Interaction Error: \($1?.toString())")
}

对照HTML代码,最上面的按钮点击后会调用一个sindrilin.call()的办法,这个方法最终要出于咱们的控制器来拓展拍卖。我们可将此字符串分成类似Target-Action编制的有限片段,前者sindrilin代表响应者,后面call()意味着应事件。其中Target的装方法如下

interactionContext?.setObject(self, forKeyedSubscript: "sindrilin")

响应者已经发生了,那么响应事件也如我们实现代码,这里就是用动用JSExport磋商了。所有这种看似Target-Action的轩然大波触发都见面经过之协议得到方式实现,因此我们要打定义响应协议和响应事件。对于来参数的主意我们得为此@objc(name)的法门吃艺术从OC仪式的方名,才能够管会叫科学调用响应:

@objc protocol LXDInteractionExport: JSExport {
    func call()                                    ///响应sindrilin.call()
    @objc(login:) func login(accountInfo: String)  ///响应sindrilin.login(accountInfo)
}

extension ViewController: LXDInteractionExport {
    func call() {
        print("call from html button clicked")
        view.backgroundColor = UIColor(red: CGFloat(arc4random() % 256) / 255, green: CGFloat(arc4random() % 256) / 255, blue: CGFloat(arc4random() % 256) / 255, alpha: 1)
    }

    func login(accountInfo: String) {
        do {
            if let JSON: [String: String] = try JSONSerialization.jsonObject(with: accountInfo.data(using: String.Encoding.utf8)!, options: JSONSerialization.ReadingOptions()) as? [String: String] {
                print("JSON: \(JSON)")
                let alert = interactionContext?.objectForKeyedSubscript("alertFromIOS")
                let message = "The alert from javascript call\naccount: \(JSON["account"]) and password: \(JSON["password"])"
                _ = alert?.call(withArguments: [message])
            }
        } catch {
            print("Error: \(error)")
        }      
    }
}

用户以前者界面输入账户与密码信息之后点击登录就会调用login(accountInfo: String)方,将用户称和密码拼凑成JSON字符串传递过来。在应措施被本身分析获取对承诺字段的用户信息,并且组转成新的字符串调用JS的弹窗函数弹有响应。demo下载

关注iOS开发文集收看更多篇
转载请注明原文作者与地址

2017次之边境线金马争霸赛,历时半独月之成才学习;

幸运不经常于,唯有努力不靠光阴

既幸运不是从天而降,那么即便记住:越努力进一步幸运;

扣押罢了点,到这边大概分享下今天早起底一个小感悟:数据化运营、和相同连接百连。

如出一辙、数据化运营

   
在纵金马总会会长-何总分享年度目标的创制及执行,一切计划暨对象都有照可循,用多少称、懂得怎样分析数据;

    其中,有得到的凡:从不同之维度去分析数据制定目标、以及可执行量/度化。

第一要有一样栽数据化思维,可参考文:http://www.jianshu.com/p/cd50fe74d40e

亚、一接入百接入

    其实,这几个字只是以印证一个道理。很多人连一生中召开了森事情,却一样从业不管成;而有成千上万总人口办好了相同件事,却成功了许多事。

   
也许很多丁还是休理解,难道是真的只是做同样件事?一生只做一样码事?我力所能及让的答案:是吗非是。

   
再推个例子:一悠悠产品,通过各种渠道/方法去销,一慢性产品形成极致;当你还变一种产品晚,可能会见时有发生例外的销售措施,但是要记得本质不会见相差太大,也许只是一时的向上,社会模式、平台规则、新媒体于转移。

故,学习是重新的经过,为之凡持续强化清晰思路、为的是时时刻刻接到新东西安静对待社会模式的革命、为底是不断打思维格局。很多读书或者还,但空杯心态会受您发出例外的感受。