Test HTTP Requests Tools Blog Learn Quizzes Smile API Log In / Sign Up
Test HTTP Requests Tools Blog Learn Quizzes Smile API Log In / Sign Up
« Return to the tutorials list
We have updated the website and our policies to make sure your privacy rights and security are respected.
Click here to learn more about the way our website handles your data.

Remove this message.

You can read this article in: English :: Español :: русский

Comunicar de ida y vuelta entre el código JavaScript y código Swift 3.0

Daniel Gheorghe Difficulty: 20 / 50 Tweet
swift-js-wkwebview-poker-game

Hola,


En este tutorial quiero compartir mi experiencia relacionada con WebViews en Swift 3.0 y explicar cómo pude comunicar de ida y vuelta entre el código JavaScript y código nativo.
En mi proyecto necesitaba ser capaz de almacenar contenido en el almacenamiento persistente UserDefaults y posteriormente hacerlo disponible para el código JavaScript para el uso fuera de línea.


En concreto, necesitaba presentar al usuario con un video introductorio la primera vez que la aplicación se cargara, a continuación, almacenar algún tipo de variables persistentes que cambiarían a la página principal de ViewController cuando la aplicación se abriera la segunda vez.
Más tarde, el requisito era mostrar los datos relacionados con el usuario en la webView

Paso 1: Vamos a empezar con el archivo AppDelegate.swift


La primera acción para mí es copiar el contenido de la carpeta completa del WebView en el .documentDirectory de mi aplicación para poder tener acceso a los archivos. En la función aplicación en AppDelegate puedo comprobar si mi carpeta ya existe en el .documentDirectory y si no lo hace lo copio allí.


Mi aplicación es un juego de Video de Póker, por lo que verás un montón de referencias a eso en el código fuente.

    
        let sharedpath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        let gamefolder = sharedpath.path + "/videopoker";
        if( FileManager.default.fileExists( atPath: gamefolder ) == false ){
            do {
                try FileManager.default.copyItem(
                    atPath: Bundle.main.url(forResource: "videopoker", withExtension:"")!.path,
                    toPath: gamefolder
                )
            } catch {
                print("Couldn't copy videopoker folder");
                print(error);
            }
        }
    

Paso 2: Vamos a ejecutar el archivo de vídeo introductorio ahora


Cambiando el ajuste en el fichero de ViewController, importa las librerías necesarias para poder reproducir el video en el método viewDidAppear.
Además, vamos a hacer uso de NotificationCenter para añadir una función de observador que será llamada cuando el vídeo haya terminado de reproducir.
En el interior del observador, vamos a almacenar un valor persistente - "appInitializedOnce" y más tarde utilizaremos eso para verificar si el vídeo se ha reproducido una vez, o incluso mejor si la aplicación se ha cargado por primera vez.

	
        import AVKit;
        import AVFoundation;

        // ...

        self.moviePlayerViewController = AVPlayerViewController()
        let path = Bundle.main.path(forResource: "1", ofType: "mp4"); // the video file (don't forget to import it in xcode)
        let url = NSURL.fileURL(withPath: path!)
        self.moviePlayer = AVPlayer(url: url);

        self.moviePlayerViewController!.showsPlaybackControls = false;
        self.moviePlayerViewController!.player = moviePlayer;
        self.present(self.moviePlayerViewController!, animated: true) {
            self.moviePlayerViewController!.player!.play()
        }
        
        // Añadir el observador que llamará al método "playerDidFinishPlaying"      
        NotificationCenter.default.addObserver(self, selector: #selector(ViewController.playerDidFinishPlaying), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: self.moviePlayer?.currentItem)

        // ...
        func playerDidFinishPlaying() {
            NotificationCenter.default.removeObserver(self); //don't forget to remove the observer now
            let userDefaults = UserDefaults.standard
            userDefaults.set(true, forKey: "appInitializedOnce"); //store the value for "appInitializedOnce"
            
            // Volver al hilo principal
            OperationQueue.main.addOperation {
                let gameViewController = self.storyboard?.instantiateViewController(withIdentifier: "Game") as! GameViewController
                // Complicado -- no utilices uno mismo aquí debido a que el "controlador visible" es ahora el "moviePlayerViewController"
                self.moviePlayerViewController?.present(gameViewController, animated: true, completion: nil)
            }
        }
	

Paso 3: Now let's show the GameViewController which in turn loads the html page with our game

Ahora vamos a demostrar el GameViewController que a su vez carga la página html con nuestro juego
El pequeño juego que he escrito está escrito en JavaScript, así que necesita un WebView para cargar el código HTML en la carpeta que he copiado en el paso 1
Debido a que el contenido de WebView no es relevante para nuestra discusión, nos centramos más en la comunicación de ida y vuelta entre nativos y JavaScript.


El GameViewController necesita varias opciones de configuración para estar activa con el fin de permitir la comunicación entre el código nativo y JavaScript.


El uso de WKUserContentController y WKScriptMessageHandler permitirán inyectar el código JavaScript en el DOM y escuchar los publicados" mensajes de la WKWebView.


En unos pocos pasos, esto es lo que sucede en el código de abajo:

  1. crea el WKUserContentController
  2. crea el WKUserScript
  3. Pon la WKUserScript en el WKUserContentController
  4. crea devolución de llamada del manejador del objeto nativo JS
  5. poner todo lo anterior en un objeto WKWebViewConfiguration
  6. carga la WKWebView con la configuración creada anteriormente
  7. carga el URL del archivo que permite el acceso a la ".documentDirectory" (las demás características como etiquetas HTML 5 vídeo no funcionará)
  8. presente la WKWebView

	
        import UIKit
        import WebKit;
        import JavaScriptCore;

        class GameViewController: UIViewController, WKScriptMessageHandler {
            var webview: WKWebView?;
            // ...
            override func viewDidAppear(_ animated: Bool) {
                let contentController = WKUserContentController();
                let userScript: WKUserScript;
                // El código JavaScript inyectado
                userScript = WKUserScript(
                    source: "console.log('test')",
                    injectionTime: WKUserScriptInjectionTime.atDocumentEnd,
                    forMainFrameOnly: true
                )

                contentController.addUserScript(userScript);
                // Establecer los manejadores nativos para el postback de los métodos JS
                contentController.add(
                    self,
                    name: "nativeCallbackHandler"
                );

                // Poner la configuración anterior en un nuevo wkwebview
                let config = WKWebViewConfiguration()
                config.userContentController = contentController
                self.webview = WKWebView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: self.view.frame.height), configuration: config);
                
                // Pon el wkwebview en la vista y carga la url html pero primero cierra cualquier webviews previamente abiertos
                for subview in self.view.subviews {
                    if(subview is WKWebView) {
                        print("Found the previous wkwebview");
                        subview.removeFromSuperview();
                    }
                }
                self.view.addSubview(self.webview!)
                
                // Carga el código HTML de la carpeta que hemos copiado en el paso 1 
                // permitiendo el acceso a la carpeta raíz del ".documentDirectory "
                let folderPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!;
                let url = URL(fileURLWithPath: folderPath.path + "/videopoker/index.html");
                self.webview!.loadFileURL(url, allowingReadAccessTo: folderPath);
            }
        }
	

Paso 4: Manejar el "mensaje" que viene de JS


En la misma GameViewController crea una función userContentController para manejar cualquier dato de regrese de la WKWebView


Desde JavaScript, se muestra como esto: window.webkit.messageHandlers.nativeCallbackHandler.postMessage(JSON.stringify({"token": token, "user_id": "" + user_id}));

		
        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
            if (message.name == "nativeCallbackHandler") {
                // Lo que quieres hacer aquí

                // Esto es un ejemplo que se relaciona a la llamada de JavaScript mencionada
                let userDefaults = UserDefaults.standard
                userDefaults.set(message.body, forKey: "userData")
            }
        }
	

Paso 5: No mostrar el vídeo si ya se ha mostrado una vez


Como se ve en el paso 2, se ha almacenado la clave llamada "appInitializedOnce" como verdadera cuando el video termine de reproducirse. Por lo tanto, lo que tenemos que hacer ahora es tomar en cuenta que, al cargar la aplicación, para que cuando volvamos a AppDelegate.swift y añadamos el siguiente código en el método de aplicación - Muestre la GameViewController directamente si la aplicación ha sido abierta por lo menos una vez en el pasado.

	
        //...
        // Si los datos de usuario ya están establecidos (el usuario ya ha visto el video) no muestra más el video de bienvenida
        let userDefaults = UserDefaults.standard
        let appInitializedOnce = userDefaults.bool(forKey: "appInitializedOnce");
        if ( appInitializedOnce == true ) {
            self.window = UIWindow(frame: UIScreen.main.bounds)
            let storyboard = UIStoryboard(name: "Main", bundle: nil)
            let initialViewController = storyboard.instantiateViewController(withIdentifier: "Game")
            self.window?.rootViewController = initialViewController
            self.window?.makeKeyAndVisible()
        }
        //...
	

Este pequeño ejemplo está destinado a mostrar al programado cómo funciona el WebViews en Swift 3.0 y para familiarizarse con Swift de principiante con conceptos tales como ViewControllers, reproductores de vídeo y el desarrollo de aplicaciones para iOS híbrido.


Puedes ver y clonar todo el código fuente de la aplicación en GitHub. Gracias por leer, espero que esto sea de utilidad.

comments powered by Disqus