Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# SwiftUIWebView(with messaging)
This project shows how to implement WebView in SwiftUI using WKWebView and UIViewRepresentable. It also shows how to deal with WebView in different purposes.

**Make a WebView using WKWebView and UIViewRepresentable:**

Just for ease of use first create a ViewModel, a enum class named _WebViewNavigation_ and another enum named _WebUrlType_. _WebViewNavigation_ is used to navigate WebView forward and backward. _WebUrlType_ defines what type of url should load e.g. `.localUrl` or `.publicUrl`.

We can use any [UIKit](https://developer.apple.com/documentation/uikit) View into SwiftUI using UIViewRepresentable which is a view wrapper. Since we are going to make a WebView for SwiftUI, first we will create a structure named `WebView` that inherits `UIViewRepresentable` and implements necessary protocols. Then we will create a `Coordinator` inside `WebView` to implement necessary protocols of WKWebView.

**Load a local** `**.html**` **file or a public website or web app in WebView:**

In the above `WebView`, inside `Coordinator` class, function `updateUIView` loads the url of local or private website into WebView depending on `WebUrlType`.

if let url = Bundle.main.url(forResource: “LocalWebsite”, withExtension: “html”, subdirectory: “www”) {
webView.loadFileURL(url, allowingReadAccessTo: url.deletingLastPathComponent())
}

Above lines load a website named `LocalWebsite.html` located inside our iOS project’s “www” directory. I mean it local webpage because it is inside the project file.

These lines load a public website or web app

if let url = URL(string: "https://www.example.com") {
webView.load(URLRequest(url: url))
}

Get the title of the loaded website

In `ContentView` use `WebView` as follows

WebView(url: .localUrl, viewModel: viewModel)

**Interact between iOS native app and web app through JavaScript’s function**

**Receive value from web app:**

Inside `WebView`’s `makeUIView` function we passed a `iOSNative` as the name to the loaded web app so that web app can find our native iOS app through that name to pass value to our native iOS app. See below line:

configuration.userContentController.add(self.makeCoordinator(), name: “iOSNative”)

Make an extension to Coordinator to catch value sent from web app like:

Here [WKScriptMessageHandler](https://developer.apple.com/documentation/webkit/wkscriptmessagehandler) receives all values sent from web app. Here is our web app that is sending those values. This `LocalWebsite.html` is located inside our project’s `www` directory. I created this `.html` file for test purpose, obviously you will work with server hosted web app.

**Pass value to web app:**

Since this is a static .html file it can not receive any value from iOS app but your real web app can receive. Just call a JavaScript function from your iOS as follows:

In my case web app’s JavaScript function’s name is `valueGotFromIOS`.

**Intercepting web app navigation:**

To intercept every navigation of web app, just implement following function inside `Coordinator class`.

**Navigating web app backward, forward and reloading:**

For backward navigation use

if webView.canGoBack {
webView.goBack()
}

For forward navigation use

if webView.canGoForward {
webView.goForward()
}

To reload WebView use

webView.reload()

Finally build the app in [Xcode](https://developer.apple.com/xcode/)!

Reference [Md Yamin](https://medium.com/@mdyamin/swiftui-mastering-webview-5790e686833e)
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>SwiftUIWebView.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
</dict>
</dict>
</dict>
</plist>
5 changes: 4 additions & 1 deletion SwiftUIWebView/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ struct ContentView: View {
@State var showLoader = false
@State var message = ""
@State var webTitle = ""
@State var isLocal: Bool = true

// For WebView's forward and backward navigation
var webViewNavigationBar: some View {
Expand Down Expand Up @@ -71,6 +72,8 @@ struct ContentView: View {
at runtime where dynamic web app can */

HStack {
Toggle(isOn: $isLocal) {}
.labelsHidden()
TextField("Write message", text: $message).textFieldStyle(RoundedBorderTextFieldStyle())
Button(action: {
self.viewModel.valuePublisher.send(self.message)
Expand All @@ -94,7 +97,7 @@ struct ContentView: View {
/* This is our WebView. Here if you pass .localUrl it will load LocalWebsite.html file
into the WebView and if you pass .publicUrl it will load the public website depending on
your url provided. See WebView implementation for more info. */
WebView(url: .publicUrl, viewModel: viewModel).overlay (
WebView(url: isLocal ? .localUrl : .publicUrl, viewModel: viewModel).overlay (
RoundedRectangle(cornerRadius: 4, style: .circular)
.stroke(Color.gray, lineWidth: 0.5)
).padding(.leading, 20).padding(.trailing, 20)
Expand Down
3 changes: 2 additions & 1 deletion SwiftUIWebView/WebView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,11 @@ struct WebView: UIViewRepresentable, WebViewHandlerDelegate {
/* An observer that observes 'viewModel.valuePublisher' to get value from TextField and
pass that value to web app by calling JavaScript function */
valueSubscriber = parent.viewModel.valuePublisher.receive(on: RunLoop.main).sink(receiveValue: { value in
let javascriptFunction = "valueGotFromIOS(\(value));"
let javascriptFunction = "valueGotFromIOS('\(value)')"
webView.evaluateJavaScript(javascriptFunction) { (response, error) in
if let error = error {
print("Error calling javascript:valueGotFromIOS()")
print(error)
print(error.localizedDescription)
} else {
print("Called javascript:valueGotFromIOS()")
Expand Down
3 changes: 2 additions & 1 deletion SwiftUIWebView/www/LocalWebsite.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ <h1>SwiftUI JavaScript Interaction</h1>
window.webkit.messageHandlers.iOSNative.postMessage("Bla Bla Bla");
}

function valueGotFromIOS(value) {
function valueGotFromIOS(value = "Default response if nothing received") {
document.getElementById("output").innerHTML = value;
window.webkit.messageHandlers.iOSNative.postMessage(value);
}


Expand Down