I recently published a video that demonstrates why I think saying that Ionic applications are not native is inaccurate and confusing (even though it is perhaps the most common way to describe Ionic applications). I make the case for this by building a standard native application with Xcode/Swift and then adding Ionic to it:
This article is going to recap the same process of creating a native iOS application with Xcode and then getting an Ionic application running inside of it. I will also be expanding much more on my thoughts around this topic in this article. There are a few goals for this article:
- To help give a sense of how Capacitor works behind the scenes
- To demystify iOS/Swift and demonstrate how Ionic/Xcode/Swift work together
- To teach a little bit of Swift syntax which can be utilised with Capacitor
But perhaps the key goal is to:
- Demonstrate why I think referring to Ionic applications as “not native” is inaccurate and confusing terminology
To be clear, there is significant differences in the approach that Ionic takes and the approach a standard native application built entirely with Swift/Objective-C takes. There are also differences between the approach React Native takes and the approach a standard native application takes. Depending on the circumstance, this may have real implications that need to be considered and one approach may be far more suitable than the other. As such, we should distinguish between these different approaches with terminology that accurately defines these differences.
The point I will be making is that all of these approaches are native applications. They all result in native packages that can be submitted to the App Store. The differences between the approaches happen inside of the native applications, and that is the way that I think we should describe them.
1. Create a new Project in Xcode
Let’s quickly recap how to go about getting Ionic running inside of a native application by just building a standard native application.
IMPORTANT: You should absolutely not build Ionic applications this way. Capacitor already does this, and it does a much better job. What we are doing is pretty much creating a really basic version of Capacitor. The point of us doing this manually is so that we can explicitly see what is happening, rather than Capacitor doing it automatically behind the scenes.
- Open Xcode
- Go to
File > New > Project
- Use the iOS
Single View App
template
2. Add Webkit to the Projects Build Phases
- In the Projects configuration go to the
Build Phases
tab - Expand
Link Binary with Libraries
- Click the
+
button - Search for
Webkit
- Select
WebKit.framework
and then clickAdd
3. Set up Webkit
- Add the following code to your
ViewController.swift
file:
import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {
private var webView: WKWebView?
override public func loadView() {
let webViewConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webViewConfiguration)
webView?.scrollView.bounces = false
webView?.uiDelegate = self
webView?.navigationDelegate = self
webView?.configuration.preferences.setValue(true, forKey: "allowFileAccessFromFileURLs")
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}
This imports WebKit
and configures a new WKWebView
which we then assign to the view. We have used the same basic configuration options that Capacitor uses when created the web view (allowFileAccessFromFileURLs
is important here as it will allow the index.html
file that we load in the web view to load the other JavaScript and CSS files it depends on).
4. Add a Built Ionic Application to the Xcode Project
- Drag a built Ionic application (i.e. the
www
folder) into your Xcode application as shown below (you can just add it underneathInfo.plist
) - Make sure that
Copy items if needed
is checked and thatAdded folders
is set toCreate folder references
Although you would not typically do this in a standard Ionic/Capacitor application, you will also need to modify the index.html
file inside of the www
folder such that this line:
<base href="/" />
is changed to this:
<base href="./" />
If you do not do this, the JavaScript/CSS files will not load in correctly.
5. Load the Ionic code into the Web View
- Modify your
ViewController.swift
file to reflect the following:
import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {
private var webView: WKWebView?
override public func loadView() {
let webViewConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webViewConfiguration)
webView?.scrollView.bounces = false
webView?.uiDelegate = self
webView?.navigationDelegate = self
webView?.configuration.preferences.setValue(true, forKey: "allowFileAccessFromFileURLs")
view = webView
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let path = Bundle.main.path(forResource: "index", ofType: "html", inDirectory: "www")
let url = URL(fileURLWithPath: path!)
let request = URLRequest(url: url)
webView?.load(request)
}
}
Now inside of the viewDidLoad
hook we are creating a URL request for our index.html
file inside of the www
directory and then loading that into our web view. That’s it! We now have Ionic running inside of our native application:
Why is this Ionic application not native?
At what point in this process did the native application I created suddenly become not native? When I added the web view? When I loaded an Ionic application into that web view?
The application we have built has access to everything any standard native application does (because it is just a native application). It can access any native feature it wants, including using additional native controls on top of the web view. The only condition here is that if we want to make use of data retrieved natively inside of our user interface (e.g. a photo from the camera or data from storage) we would need to communicate that data to the web view so that the Ionic code can make use of it (this is something that Capacitor allows us to do).
The most noticeable difference here is that Ionic is primarily using the web view to display its user interface, whereas a standard native application uses the default native controls. It is common for people to draw the line here, and say that this is why Ionic is not native: because it doesn’t use the default native controls.
First, a WebKit View is one of the standard native user interface controls provided by iOS. But even if we exclude that, why would not making use of a specific subset of features that native applications offer suddenly make the application not native? When I first created the Xcode project, before I added the WebKit View, there were no native controls being used - was it not native at that point in time? We could drop other native controls on top of the Ionic application and make use of them (like modals, action sheets, alerts, and more), is there a certain number of native controls that need to be added? Why is usage of the user interface library the defining factor?
Some people will say that if it includes the WebKit View control it makes the application not native. This creates a very weird situation which where by adding a native control to your native application you make it less… “native”. But if we do take that definition, then many “typical” native applications wouldn’t be considered native, as many use web views to display at least parts of the user interface - I have not researched this myself to verify but I believe this includes big name applications like Instagram and Uber based on findings by others.
I realise I am being a bit silly here with my hypotheticals, and I don’t mean to come across as inflammatory. I just want to highlight the kinds of weird/confusing/absurd situations we have to discuss if we use “native” as the descriptor to distinguish between various approaches. Where do you draw the line?
What SHOULD we call these applications?
I don’t know. I’m just going to be the annoying person who points out issues without offering solutions. We do have the term “hybrid” for Ionic which is commonly used, which is better than simply labelling Ionic applications “not native”, but I still think it is a vague and confusing definition.
One commenter on my YouTube video said that it would be misleading to say to a client that Ionic is native. I agree that simply saying it is native is misleading, which gets to my point that the terminology as it is used today is inherently vague/confusing/misleading. It would be just as misleading to simply say that Ionic is not native.
If a client or a developer asks if Ionic is “native” the answer that would be useful and clarifying to them would depend on what exactly they mean. So saying Ionic is native and Ionic is not native here is misleading both ways.
- If they want to know if the built output is the same as a standard native application, then the answer they are seeking would be: yes
- If they want to know whether they can submit this to the app store, then the answer they are seeking would be: yes
- If they want to know whether they can access native functionality like the camera or native file storage then the answer they are seeking would be: yes
- If they wanted to know whether the user interface is implemented using native user interface controls then the answer would be (mostly): no
I can offer a few thoughts on how I would describe the nature of Ionic and alternatives, without using a blanket native/not-native term. This is how I would differentiate between the approaches I’ve mentioned in this article:
Applications built with Xcode/Swift/Objective-C, Ionic/Capacitor, and React Native are all native applications. Applications built with Xcode/Swift/Objective-C could be referred to as standard native applications. Applications built with Ionic/Capacitor could be referred to as native applications that primarily use Webkit/JavaScript to display the user interface and run business logic. Applications built with React Native could be referred to as native applications that use WebKit/JavaScript to run business logic and have their user interface interpreted into native controls at runtime.
Do these differences matter?
They might, but in a majority of cases the skills of the people building the applications are going to far outweigh any potential performance drawbacks of a particular approach.
There is this common perception that a “standard” native application will always outperform alternatives that have some level of abstraction going on inside (like Ionic and React Native). In theory this might be true, but the reality is very different. A standard native application might have more power available, but it is susceptible to the same coding/design flaws that any programming language is. I would take higher skilled developers building with Ionic over lesser skilled developers building standard native applications every day (even if we wipe out every other benefit of using Ionic like being able to share the exact same codebase over multiple platforms).
Try putting me in a V8 Supercar racing against Craig Lowndes in a stock standard Toyota Corolla. I don’t need to know much about cars to know that I am going to lose that race every time, despite the extra power I might have access to. If all we are interested in is city driving, then the Supercar gives me little to no benefit (and will likely be a hindrance).
What are your thoughts?
Although I’ve been a bit cheeky in this article, I do genuinely want to hear from, and have a discussion, with people who disagree with my view here. I think the majority of people do disagree with the view I have outlined.
Further resources
- Max Lynch (Ionic CEO) published an article a few years ago that talks about this topic: Capacitor/Cordova (and Ionic) Apps are Native Apps
- An overview of how React Native utilises WebKit/JavaScript: Performance Overview