<aside> ✅ You can package ScandiPWA as a native iOS application
</aside>
To deploy an iOS app, you need to have at least one native feature to be included in the app. We used a barcode scanner to search for items. You can add notifications management or anything else, but you have to add it as native code.
<aside> 🚨 To deploy an iOS application you must have a macOS-powered device.
</aside>
To display your site in iOS App, you first need to make sure it is deployed online. Then, you can use WebView to display the site inside of an iOS native app. To integrate native feature to the site, we suggest using the following approach:
ViewController.swift
import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate, WKScriptMessageHandler {
/// Assuming that the javascript sends message back, this function handles the message
///
/// - Parameters:
/// - userContentController: controller
/// - message: Message. Can be a String or [String:Any] to a single level.
func userContentController(
_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage
) {
print("something recived");
let messageBody = message.body as! [String: Any];
let action = messageBody["action"] as! String;
switch action {
case "barcodeClick":
print("barcode was clicked");
return;
default:
return;
}
}
lazy var webView: WKWebView = {
let webCfg:WKWebViewConfiguration = WKWebViewConfiguration()
// Setup WKUserContentController instance for injecting user script
var userController:WKUserContentController = WKUserContentController()
var script:String?
// Get the contents of the file `inject.js`
if let filePath:String = Bundle.main.path(forResource: "inject", ofType:"js") {
script = try! String(contentsOfFile: filePath, encoding: .utf8)
}
let userScript:WKUserScript = WKUserScript(source: script!, injectionTime: WKUserScriptInjectionTime.atDocumentStart, forMainFrameOnly: false)
userController.addUserScript(userScript)
// Add a script message handler for receiving "nativeProcess" event notifications posted from the JS document using window.webkit.messageHandlers.nativeProcess.postMessage script message
userController.add(self, name: "nativeProcess")
// Configure the WKWebViewConfiguration instance with the WKUserContentController
webCfg.userContentController = userController;
let webView = WKWebView(
frame: CGRect(
x: 0,
y: 0,
width: self.view.frame.width,
height: self.view.frame.height
),
configuration: webCfg
)
return webView
}();
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.navigationItem.title = "ScandiPWA"
self.view.addSubview(webView)
let urlToLoad = URL(string: "<https://tech-demo.scandipwa.com>")
// Do any additional setup after loading the view.
webView.load(URLRequest(url: urlToLoad!))
}
}
inject.js
file from empty templatefunction sendToNative(message) {
// Initiate the handle for Native process
const native = window.webkit.messageHandlers.nativeProcess
native.postMessage(message)
}
function onBarcodeClick() {
sendToNative({
action: 'barcodeClick',
});
}
const BARCODE_SCANNER_ID = 'barcode-scanner';
const SEARCH_FIELD_ID = 'search-field';
function tryRenderingElement() {
setTimeout(() => {
if (document.getElementById(BARCODE_SCANNER_ID)) {
return;
}
const searchElement = document.getElementById(SEARCH_FIELD_ID);
const barcodeButton = document.createElement('button');
barcodeButton.id = BARCODE_SCANNER_ID;
barcodeButton.style.width = '30px';
barcodeButton.style.height = '30px';
barcodeButton.style.marginLeft = '1.4rem';
barcodeButton.style.backgroundImage = `url("data:image/svg+xml,%3Csvg xmlns='<http://www.w3.org/2000/svg>' viewBox='0 0 480 480'%3E%3Cpath d='M80 48H16C7 48 0 55 0 64v64a16 16 0 0032 0V80h48a16 16 0 000-32zM464 336c-9 0-16 7-16 16v48h-48a16 16 0 000 32h64c9 0 16-7 16-16v-64c0-9-7-16-16-16zM464 48h-64a16 16 0 000 32h48v48a16 16 0 0032 0V64c0-9-7-16-16-16zM80 400H32v-48a16 16 0 00-32 0v64c0 9 7 16 16 16h64a16 16 0 000-32zM64 112h32v256H64zM128 112h32v192h-32zM192 112h32v192h-32zM256 112h32v256h-32zM320 112h32v192h-32zM384 112h32v256h-32zM128 336h32v32h-32zM192 336h32v32h-32zM320 336h32v32h-32z'/%3E%3C/svg%3E")`;
barcodeButton.style.backgroundSize = 'contain';
barcodeButton.onclick = onBarcodeClick;
const searchFieldWrapper = searchElement.parentNode.parentNode;
searchFieldWrapper.style.display = 'flex';
searchFieldWrapper.style.alignItems = 'center';
searchFieldWrapper.appendChild(barcodeButton);
const searchField = searchElement.parentNode;
searchField.style.flexGrow = '1';
}, 0);
}
const pushState = window.history.pushState;
window.history.pushState = function () {
// Track page changes in React
pushState.apply(window.history, arguments);
tryRenderingElement();
};
tryRenderingElement();
As you can see injecting scripts into WebView is not that hard. When clicking on the barcode icon, the XCode console logs the barcode was clicked
message. You can replace this logic with anything that matches your needs.
We will publish the full app code (including the barcode implementation) soon, so you can use it as a reference.
When the app is done, it's time to publish it!
To publish an iOS app, you must be signed up for the Apple Developer Program. You can do this on the official site, here.
For a more detailed guide, please see Chris's guide.
The process of review might take up to 10 days and result in refusal. ScandiPWA does not guarantee your app publishing. Please make sure the app you build complies with AppStore guidelines.