Build a Hybrid SwiftUI app for iOS with Phoenix LiveView
Tags: elixir, swift, phoenix, liveview
Published: 2019-10-31 20:25:45

I've succesfully published  hybrid mobile apps with [Turbolinks iOS](https://github.com/turbolinks/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](https://www.richardtaylor.dev/articles/flappy-phoenix-live-view) a LiveView game I created previously.

Create a new iOS project using `Single View App` in Xcode

![image](//images.ctfassets.net/qnx6g1z4xbnr/5Kbm2tdLAtrATdkpAvPh6y/7504b7f431df0bcc3235cfbf378ae3ab/image.png)

Make sure you've selected the `SwiftUI` user interface

![image](//images.ctfassets.net/qnx6g1z4xbnr/1FLl47U3qnVSJayS2cN49H/437f5abdb3205068ff37b265683edd91/image.png) 

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

```swift
// 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://plappy-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](//images.ctfassets.net/qnx6g1z4xbnr/3Khed9wJDJdNdaiyF4rzSN/97265bb99885b6b467e7bb58b8ed4fa9/image.png)

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.

```swift
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:

```swift
webView.customUserAgent = "FlappyPhoenix iOS"
```

If you have any tips or feedback please get in touch via [Twitter](https://twitter.com/moomerman/status/1190000843170426880).

## Resources

* [The Ultimate Guide to WKWebView](https://www.hackingwithswift.com/articles/112/the-ultimate-guide-to-wkwebview)
* [WebKit in SwiftUI with SegmentedControl, WebView, State, UIViewRepresentable](https://www.youtube.com/watch?v=5xvvfHNdB5c)

If you have any tips or feedback please get in touch via [Twitter](https://twitter.com/moomerman/status/1190000843170426880).