Build a Hybrid SwiftUI app for iOS with Phoenix LiveView

Screenshot of Flappy Phoenix app on an iPhone

I’ve succesfully published hybrid mobile apps with Turbolinks iOS in the past but Phoenix LiveViews are better suited due to the websocket event handling there is little (or no) HTTP navigation happening.

To demonstrate this we’re going to build an iOS app for Flappy Phoenix a LiveView game I created previously.

Create a new iOS project using Single View App in Xcode

xcode new app dialog

Make sure you’ve selected the SwiftUI user interface

xcode new app dialog

Using SwiftUI we need very little code to get the WebView onto the screen. Update the generated ContentView with

// ContentView.swift

import SwiftUI
import WebKit

struct ContentView: View {
  var body: some View {
    WebView().edgesIgnoringSafeArea(.all)
  }
}

struct WebView: UIViewRepresentable {
  func makeUIView(context: Context) -> WKWebView {
    let webView = WKWebView()
    webView.scrollView.isScrollEnabled = false
    return webView
  }

  func updateUIView(_ webView: WKWebView, context: Context) {
    let liveView = "https://flappy-phoenix.herokuapp.com/game"
    if let url = URL(string: liveView) {
       let request = URLRequest(url: url)
       webView.load(request)
    }
  }
}

#if DEBUG
struct ContentView_Previews : PreviewProvider {
  static var previews: some View {
    ContentView()
  }
}
#endif

This results in a playable iOS app that wraps the LiveView!

Flappy Phoenix

It is now possible to extend the interface with SwiftUI components to make it feel more native, while keeping the main body of the app served via the LiveView.

struct ContentView: View {
  var body: some View{
    NavigationView {
      VStack{
        WebView()
      }
      .navigationBarTitle(Text("Flappy Phoenix"))
      .edgesIgnoringSafeArea(.all)
    }
  }
}

This is a simplified example, but hopefully it sparks your imagination of what is possible with LiveView hybrid apps.

In this case I haven’t had to make any modifications to the web application but often you might want to make some changes on the server-side eg. hiding the web navigation if you’re replacing it with native navigation.

To achieve this we can set a custom user agent in the WebView and then respond to that in the web application:

webView.customUserAgent = "FlappyPhoenix iOS"

If you have any tips or feedback please get in touch via Twitter.

Resources