iOS (Swift)
Show the map
Let's start by using the WKWebView class to set up our web map.
import UIKit
import WebKit
class ViewController: UIViewController {
var webView: WKWebView!
override func loadView() {
webView = WKWebView()
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://pcmap-website-demo.netlify.app")!
webView.load(URLRequest(url: url))
}
}
Handle messages from the map
The application could receive messages from the web map. To make it possible we must implement the WKScriptMessageHandler protocol.
The message from the web map is in JSON format, it always contains the messageType field and some optional fields depending on the message type. For example, if some errors occur the message could look like this:
{
"messageType": "error",
"error": "Error details will be here..."
}
Here we handle the mapReady and error events. Both of them cause print a message to the output.
import UIKit
import WebKit
struct WebMapMessage: Decodable {
enum MessageType: String, Decodable {
case mapReady, error
}
let messageType: MessageType
let error: String?
}
class ViewController: UIViewController, WKScriptMessageHandler {
var webView: WKWebView!
override func loadView() {
webView = WKWebView()
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://pcmap-website-demo.netlify.app")!
webView.load(URLRequest(url: url))
webView.configuration.userContentController.add(self, name: "mainMessageHandler")
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
guard let jsonString = message.body as? String,
let jsonData = jsonString.data(using: .utf8)
else {
return
}
let webMapMessage: WebMapMessage? = try? JSONDecoder().decode(WebMapMessage.self, from: jsonData)
guard let messageType = webMapMessage?.messageType else {
return
}
switch messageType {
case .mapReady:
print("Map is ready!")
return
case .error:
if let error = webMapMessage?.error {
print(error)
}
}
}
}
Interaction
To access the map Events and Methods the application could evaluate a JavaScript code in the WebView as shown in example below:
func showOrdinal(ordinal: Int) {
let script = "app.map.showOrdinal(\(ordinal))"
webView.evaluateJavaScript(script) { (result, error) in
if let result = result {
print("Executed successfully: \(result)")
} else if let error = error {
print("An error occurred: \(error)")
}
}
}
Apply scenarios
The application can ask the web map to apply some scenarios.
The "One POI" scenario
The web map supports the onePOI scenario that, after applying, selects the required POI (Point Of Interest).
Other scenarios could be implemented by request.
In the example, we prepare the structure WebMapOnePOIScenario that will be serialized into JSON and sent to the web map once the mapReady message is received.
import UIKit
import WebKit
struct WebMapMessage: Decodable {
enum MessageType: String, Decodable {
case mapReady, error
}
let messageType: MessageType
let error: String?
}
struct WebMapOnePOIScenario: Encodable {
// required
let scenarioType: String = "onePOI"
let poi: String
// optional
var showSearch: Bool? = nil
var showLevelChoice: Bool? = nil
var showCallout: Bool? = nil
var showZoomControl: Bool? = nil
var zoomLevel: Int? = nil
}
class ViewController: UIViewController, WKScriptMessageHandler {
var webView: WKWebView!
override func loadView() {
webView = WKWebView()
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://pcmap-website-demo.netlify.app")!
webView.load(URLRequest(url: url))
webView.configuration.userContentController.add(self, name: "mainMessageHandler")
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
guard let jsonString = message.body as? String,
let jsonData = jsonString.data(using: .utf8)
else {
return
}
let webMapMessage: WebMapMessage? = try? JSONDecoder().decode(WebMapMessage.self, from: jsonData)
guard let messageType = webMapMessage?.messageType else {
return
}
switch messageType {
case .mapReady:
showOnePOI(poi: "150")
return
case .error:
if let error = webMapMessage?.error {
print(error)
}
}
}
func showOnePOI(poi: String) {
var scenario = WebMapOnePOIScenario(poi: poi)
scenario.showSearch = true
scenario.showLevelChoice = true
scenario.showCallout = true
scenario.showZoomControl = true
scenario.zoomLevel = 20
applyScenario(scenario: scenario)
}
func applyScenario<T: Encodable>(scenario: T) {
let jsonData = try! JSONEncoder().encode(scenario)
let jsonString = String(data: jsonData, encoding: .utf8)!
let script = "app.scenarios.apply(\(jsonString))"
webView.evaluateJavaScript(script) { (result, error) in
if let result = result {
print("Executed successfully: \(result)")
} else if let error = error {
print("An error occurred: \(error)")
}
}
}
}
User location
It is possible to tell the map where the user is right now. Below you can see an example of doing that.
import UIKit
import WebKit
struct WebMapMessage: Decodable {
enum MessageType: String, Decodable {
case mapReady, error
}
let messageType: MessageType
let error: String?
}
struct WebMapUserPosition: Encodable {
let latitude: Double
let longitude: Double
var ordinal: Int? = nil
var heading: Double? = nil
}
class ViewController: UIViewController, WKScriptMessageHandler {
var webView: WKWebView!
override func loadView() {
webView = WKWebView()
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://pcmap-website-demo.netlify.app/")!
webView.load(URLRequest(url: url))
webView.configuration.userContentController.add(self, name: "mainMessageHandler")
}
// Handle messages from the map
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
guard let jsonString = message.body as? String,
let jsonData = jsonString.data(using: .utf8)
else {
return
}
let webMapMessage: WebMapMessage? = try? JSONDecoder().decode(WebMapMessage.self, from: jsonData)
guard let messageType = webMapMessage?.messageType else {
return
}
switch messageType {
case .mapReady:
setUserPosition(latitude: 47.45232728284989, longitude: 8.56221512953916, ordinal: 2, heading: 91.5)
return
case .error:
if let error = webMapMessage?.error {
print(error)
}
}
}
// Show the "blue dot" on the map
func setUserPosition(latitude: Double, longitude: Double, ordinal: Int?, heading: Double?) {
let userPosition = WebMapUserPosition(latitude: latitude, longitude: longitude, ordinal: ordinal, heading: heading)
let jsonData = try! JSONEncoder().encode(userPosition)
let jsonString = String(data: jsonData, encoding: .utf8)!
let script = "app.map.setUserPosition(\(jsonString))"
webView.evaluateJavaScript(script) { (result, error) in
if let result = result {
print("Executed successfully: \(result)")
} else if let error = error {
print("An error occurred: \(error)")
}
}
}
}