Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ios+android): Add possibility to configure loading scheme #1810

Merged
merged 7 commits into from
Aug 23, 2019
Merged
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
27 changes: 23 additions & 4 deletions android/capacitor/src/main/java/com/getcapacitor/Bridge.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,8 @@ public class Bridge {

// The name of the directory we use to look for index.html and the rest of our web assets
public static final String DEFAULT_WEB_ASSET_DIR = "public";
public static final String CAPACITOR_SCHEME_NAME = "http";
public static final String CAPACITOR_HTTP_SCHEME = "http";
public static final String CAPACITOR_HTTPS_SCHEME = "https";
public static final String CAPACITOR_FILE_START = "/_capacitor_file_";
public static final String CAPACITOR_CONTENT_START = "/_capacitor_content_";

Expand Down Expand Up @@ -176,7 +177,10 @@ private void loadWebView() {

String authority = Config.getString("server.hostname", "localhost");
authorities.add(authority);
localUrl = CAPACITOR_SCHEME_NAME + "://" + authority;

String scheme = this.getScheme();

localUrl = scheme + "://" + authority;

if (appUrlConfig != null) {
try {
Expand All @@ -196,8 +200,15 @@ private void loadWebView() {
localServer = new WebViewLocalServer(context, this, getJSInjector(), authorities, html5mode);
localServer.hostAssets(DEFAULT_WEB_ASSET_DIR);


appUrl = appUrlConfig == null ? localUrl : appUrlConfig;
if (appUrlConfig == null) {
appUrl = localUrl;
// custom URL schemes requires path ending with /
if (!scheme.equals(Bridge.CAPACITOR_HTTP_SCHEME) && !scheme.equals(CAPACITOR_HTTPS_SCHEME)) {
appUrl += "/";
}
} else {
appUrl = appUrlConfig;
}

Log.d(LOG_TAG, "Loading app at " + appUrl);

Expand Down Expand Up @@ -294,6 +305,14 @@ public Uri getIntentUri() {
return intentUri;
}

/**
* Get scheme that is used to serve content
* @return
*/
public String getScheme() {
return Config.getString("server.androidScheme", CAPACITOR_HTTP_SCHEME);
}

/*
public void registerPlugins() {
Log.d(LOG_TAG, "Finding plugins");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
*/
public class WebViewLocalServer {

private final static String capacitorScheme = Bridge.CAPACITOR_SCHEME_NAME;
private final static String capacitorFileStart = Bridge.CAPACITOR_FILE_START;
private final static String capacitorContentStart = Bridge.CAPACITOR_CONTENT_START;
private String basePath;
Expand Down Expand Up @@ -435,8 +434,13 @@ public InputStream handle(Uri url) {
};

for (String authority: authorities) {
registerUriForScheme(capacitorScheme, handler, authority);
registerUriForScheme("https", handler, authority);
registerUriForScheme(Bridge.CAPACITOR_HTTP_SCHEME, handler, authority);
registerUriForScheme(Bridge.CAPACITOR_HTTPS_SCHEME, handler, authority);

String customScheme = this.bridge.getScheme();
if (!customScheme.equals(Bridge.CAPACITOR_HTTP_SCHEME) && !customScheme.equals(Bridge.CAPACITOR_HTTPS_SCHEME)) {
registerUriForScheme(customScheme, handler, authority);
}
}

}
Expand Down
15 changes: 6 additions & 9 deletions ios/Capacitor/Capacitor/CAPAssetHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,13 @@ class CAPAssetHandler: NSObject, WKURLSchemeHandler {
var startPath = self.basePath
let url = urlSchemeTask.request.url!
let stringToLoad = url.path
let scheme = url.scheme

if scheme == CAPBridge.CAP_SCHEME {
if stringToLoad.isEmpty || url.pathExtension.isEmpty {
startPath.append("/index.html")
} else if stringToLoad.starts(with: CAPBridge.CAP_FILE_START) {
startPath = stringToLoad.replacingOccurrences(of: CAPBridge.CAP_FILE_START, with: "")
} else {
startPath.append(stringToLoad)
}
if stringToLoad.isEmpty || url.pathExtension.isEmpty {
startPath.append("/index.html")
} else if stringToLoad.starts(with: CAPBridge.CAP_FILE_START) {
startPath = stringToLoad.replacingOccurrences(of: CAPBridge.CAP_FILE_START, with: "")
} else {
startPath.append(stringToLoad)
}
let localUrl = URL.init(string: url.absoluteString)!
let fileUrl = URL.init(fileURLWithPath: startPath)
Expand Down
9 changes: 6 additions & 3 deletions ios/Capacitor/Capacitor/CAPBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ enum BridgeError: Error {

public static let statusBarTappedNotification = Notification(name: Notification.Name(rawValue: "statusBarTappedNotification"))
public static var CAP_SITE = "https://capacitor.ionicframework.com/"
public static var CAP_SCHEME = "capacitor"
public static var CAP_FILE_START = "/_capacitor_file_"
public static let CAP_DEFAULT_SCHEME = "capacitor"

// The last URL that caused the app to open
private static var lastUrl: URL?
Expand All @@ -36,6 +36,8 @@ enum BridgeError: Error {
public var cordovaPluginManager: CDVPluginManager?
// Calls we are storing to resolve later
public var storedCalls = [String:CAPPluginCall]()
// Scheme to use when serving content
public var scheme: String
// Whether the app is active
private var isActive = true

Expand All @@ -44,16 +46,17 @@ enum BridgeError: Error {

public var notificationsDelegate : CAPUNUserNotificationCenterDelegate

public init(_ bridgeDelegate: CAPBridgeDelegate, _ userContentController: WKUserContentController, _ config: CAPConfig) {
public init(_ bridgeDelegate: CAPBridgeDelegate, _ userContentController: WKUserContentController, _ config: CAPConfig, _ scheme: String) {
self.bridgeDelegate = bridgeDelegate
self.userContentController = userContentController
self.notificationsDelegate = CAPUNUserNotificationCenterDelegate()
self.config = config
self.scheme = scheme

super.init()

self.notificationsDelegate.bridge = self;
localUrl = "\(CAPBridge.CAP_SCHEME)://\(config.getString("server.hostname") ?? "localhost")"
localUrl = "\(self.scheme)://\(config.getString("server.hostname") ?? "localhost")"
exportCoreJS(localUrl: localUrl!)
registerPlugins()
setupCordovaCompatibility()
Expand Down
24 changes: 14 additions & 10 deletions ios/Capacitor/Capacitor/CAPBridgeViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,20 @@ public class CAPBridgeViewController: UIViewController, CAPBridgeDelegate, WKScr

setStatusBarDefaults()
setScreenOrientationDefaults()
let capConfig = CAPConfig(self.config)

HTTPCookieStorage.shared.cookieAcceptPolicy = HTTPCookie.AcceptPolicy.always
let webViewConfiguration = WKWebViewConfiguration()
self.handler = CAPAssetHandler()
self.handler!.setAssetPath(startPath)
webViewConfiguration.setURLSchemeHandler(self.handler, forURLScheme: CAPBridge.CAP_SCHEME)

var specifiedScheme = CAPBridge.CAP_DEFAULT_SCHEME
let configScheme = capConfig.getString("server.iosScheme") ?? CAPBridge.CAP_DEFAULT_SCHEME
// check if WebKit handles scheme and if it is valid according to Apple's documentation
if !WKWebView.handlesURLScheme(configScheme) && configScheme.range(of: "^[a-z][a-z0-9.+-]*$", options: [.regularExpression, .caseInsensitive], range: nil, locale: nil) != nil {
specifiedScheme = configScheme.lowercased()
}
webViewConfiguration.setURLSchemeHandler(self.handler, forURLScheme: specifiedScheme)

let o = WKUserContentController()
o.add(self, name: "bridge")

Expand All @@ -71,17 +78,17 @@ public class CAPBridgeViewController: UIViewController, CAPBridgeDelegate, WKScr

setKeyboardRequiresUserInteraction(false)

bridge = CAPBridge(self, o, CAPConfig(self.config))
bridge = CAPBridge(self, o, capConfig, specifiedScheme)
if let scrollEnabled = bridge!.config.getValue("ios.scrollEnabled") as? Bool {
webView?.scrollView.isScrollEnabled = scrollEnabled
webView?.scrollView.isScrollEnabled = scrollEnabled
}

if let backgroundColor = (bridge!.config.getValue("ios.backgroundColor") as? String) ?? (bridge!.config.getValue("backgroundColor") as? String) {
webView?.backgroundColor = UIColor(fromHex: backgroundColor)
webView?.scrollView.backgroundColor = UIColor(fromHex: backgroundColor)
webView?.backgroundColor = UIColor(fromHex: backgroundColor)
webView?.scrollView.backgroundColor = UIColor(fromHex: backgroundColor)
}
}

private func getStartPath() -> String? {
let fullStartPath = URL(fileURLWithPath: "public").appendingPathComponent(startDir)
guard var startPath = Bundle.main.path(forResource: fullStartPath.relativePath, ofType: nil) else {
Expand Down Expand Up @@ -156,7 +163,6 @@ public class CAPBridgeViewController: UIViewController, CAPBridgeDelegate, WKScr
self.isStatusBarVisible = false
}
}

if let statusBarStyle = plist["UIStatusBarStyle"] as? String {
if (statusBarStyle != "UIStatusBarStyleDefault") {
self.statusBarStyle = .lightContent
Expand Down Expand Up @@ -434,7 +440,6 @@ public class CAPBridgeViewController: UIViewController, CAPBridgeDelegate, WKScr
return self.webView!
}


public func getServerBasePath() -> String {
return self.basePath
}
Expand Down Expand Up @@ -483,4 +488,3 @@ public class CAPBridgeViewController: UIViewController, CAPBridgeDelegate, WKScr
*/

}

6 changes: 5 additions & 1 deletion site/docs-md/basics/configuring-your-app.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ The current ones you might configure are:
// as it allows to run web APIs that require a secure context such as
// navigator.geolocation and MediaDevices.getUserMedia.
"hostname": "app",
// It is possible to configure the local scheme that is used. This can be useful
// when migrating from cordova-plugin-ionic-webview, where the default scheme on iOS is ionic.
"iosScheme": "ionic",
"androidScheme": "http",
// Normally all external URLs are opened in the browser. By setting this option, you tell
// Capacitor to open URLs belonging to these hosts inside its WebView.
"allowNavigation": [
Expand Down Expand Up @@ -111,4 +115,4 @@ iOS and Android each have configuration guides walking through making common cha

[Configuring iOS ›](/docs/ios/configuration)

[Configuring Android ›](/docs/android/configuration)
[Configuring Android ›](/docs/android/configuration)
12 changes: 12 additions & 0 deletions site/docs-md/cordova/migrating-from-cordova-to-capacitor.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,18 @@ iOS `edit-config` elements need to be [configured in Info.plist](/docs/ios/confi

It's impossible to cover every `config.xml` element available. However, most questions relating to "How do I configure X in Capacitor?" should be thought of as "How do I configure X in [platform] (iOS/Android)?" when searching online for answers.

## Setting Scheme

When using Ionic with Cordova, your app uses `cordova-plugin-ionic-webview` by default, which on iOS uses `ionic://` scheme for serving the content. Capacitor apps use `capacitor://` as default scheme on iOS. This means that using a origin-binded Web API like LocalStorage, will result in a loss of data as the origin is different. This can be fixed by changing the scheme that is used for serving the content:

```json
{
"server": {
"iosScheme": "ionic"
}
}
```

## Removing Cordova

Once you've tested that all migration changes have been applied and the app is working well, Cordova can be removed from the project. Delete `config.xml` as well as the `platforms` and `plugins` folders. Note that you don't technically have to remove Cordova, since Capacitor works alongside it. In fact, if you plan to continue using Cordova plugins or think you may in the future, you can leave the Cordova assets where they are.
Expand Down