From 9f9c3c3faef89c5cebb9e002de7673972240d385 Mon Sep 17 00:00:00 2001 From: Bart Wesselink Date: Fri, 26 Jul 2019 15:18:19 +0200 Subject: [PATCH 1/6] feat(ios): Add possibility to configure webview origin for iOS --- ios/Capacitor/Capacitor/CAPAssetHandler.swift | 7 ++++++- ios/Capacitor/Capacitor/CAPBridge.swift | 10 ++++++++-- ios/Capacitor/Capacitor/CAPBridgeViewController.swift | 7 +++++-- site/docs-md/basics/configuring-your-app.md | 5 ++++- .../cordova/migrating-from-cordova-to-capacitor.md | 11 +++++++++++ 5 files changed, 34 insertions(+), 6 deletions(-) diff --git a/ios/Capacitor/Capacitor/CAPAssetHandler.swift b/ios/Capacitor/Capacitor/CAPAssetHandler.swift index 4a2eb0c53..eaeda77d7 100644 --- a/ios/Capacitor/Capacitor/CAPAssetHandler.swift +++ b/ios/Capacitor/Capacitor/CAPAssetHandler.swift @@ -4,10 +4,15 @@ import MobileCoreServices class CAPAssetHandler: NSObject, WKURLSchemeHandler { private var basePath: String = "" + private var config: CAPConfig! func setAssetPath(_ assetPath: String) { self.basePath = assetPath; } + + func setConfig(_ config: CAPConfig) { + self.config = config + } func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) { var startPath = self.basePath @@ -15,7 +20,7 @@ class CAPAssetHandler: NSObject, WKURLSchemeHandler { let stringToLoad = url.path let scheme = url.scheme - if scheme == CAPBridge.CAP_SCHEME { + if scheme == CAPBridge.getSchemeForConfig(self.config) { if stringToLoad.isEmpty || url.pathExtension.isEmpty { startPath.append("/index.html") } else if stringToLoad.starts(with: CAPBridge.CAP_FILE_START) { diff --git a/ios/Capacitor/Capacitor/CAPBridge.swift b/ios/Capacitor/Capacitor/CAPBridge.swift index 278c5b9a4..6a820073d 100644 --- a/ios/Capacitor/Capacitor/CAPBridge.swift +++ b/ios/Capacitor/Capacitor/CAPBridge.swift @@ -11,7 +11,6 @@ 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_" // The last URL that caused the app to open @@ -53,7 +52,7 @@ enum BridgeError: Error { super.init() self.notificationsDelegate.bridge = self; - localUrl = "\(CAPBridge.CAP_SCHEME)://\(config.getString("server.hostname") ?? "localhost")" + localUrl = "\(CAPBridge.getSchemeForConfig(self.config))://\(config.getString("server.hostname") ?? "localhost")" exportCoreJS(localUrl: localUrl!) registerPlugins() setupCordovaCompatibility() @@ -110,6 +109,13 @@ enum BridgeError: Error { CAPBridge.lastUrl = url return true } + + /** + * Determine which scheme to use when serving content + */ + public static func getSchemeForConfig(_ config: CAPConfig) -> String { + return config.getString("server.scheme") ?? "capacitor" + } /** * Handle continueUserActivity, for now this just provides universal link responding support. diff --git a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift index fab84d934..b359c2c3e 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift @@ -45,12 +45,15 @@ 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) + self.handler!.setConfig(capConfig) + webViewConfiguration.setURLSchemeHandler(self.handler, forURLScheme: CAPBridge.getSchemeForConfig(capConfig)) let o = WKUserContentController() o.add(self, name: "bridge") @@ -71,7 +74,7 @@ public class CAPBridgeViewController: UIViewController, CAPBridgeDelegate, WKScr setKeyboardRequiresUserInteraction(false) - bridge = CAPBridge(self, o, CAPConfig(self.config)) + bridge = CAPBridge(self, o, capConfig) if let scrollEnabled = bridge!.config.getValue("ios.scrollEnabled") as? Bool { webView?.scrollView.isScrollEnabled = scrollEnabled } diff --git a/site/docs-md/basics/configuring-your-app.md b/site/docs-md/basics/configuring-your-app.md index 1444dd580..74068d2a1 100644 --- a/site/docs-md/basics/configuring-your-app.md +++ b/site/docs-md/basics/configuring-your-app.md @@ -63,6 +63,9 @@ 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", + // On iOS, 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 is ionic. + "scheme": "ionic", // 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": [ @@ -111,4 +114,4 @@ iOS and Android each have configuration guides walking through making common cha [Configuring iOS ›](/docs/ios/configuration) -[Configuring Android ›](/docs/android/configuration) \ No newline at end of file +[Configuring Android ›](/docs/android/configuration) diff --git a/site/docs-md/cordova/migrating-from-cordova-to-capacitor.md b/site/docs-md/cordova/migrating-from-cordova-to-capacitor.md index f34368f6f..b8d44db07 100644 --- a/site/docs-md/cordova/migrating-from-cordova-to-capacitor.md +++ b/site/docs-md/cordova/migrating-from-cordova-to-capacitor.md @@ -115,6 +115,17 @@ 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. Capacitor uses its own webview, with its own origin. This means that using a origin-binded Web API like local storage, will result in a loss of data. This can be fixed by changing the scheme that is used for serving the content: + +```json +{ + "server": { + "scheme": "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. From 2028419d8e1edd1b4fb988971b30be6ba3c7d1d8 Mon Sep 17 00:00:00 2001 From: Bart Wesselink Date: Tue, 20 Aug 2019 11:06:54 +0200 Subject: [PATCH 2/6] Allow configurable origin on Android and add prefixes for iOS and Android --- .../main/java/com/getcapacitor/Bridge.java | 21 +++++++++++++-- .../com/getcapacitor/WebViewLocalServer.java | 10 ++++--- ios/Capacitor/Capacitor/CAPAssetHandler.swift | 9 +++++-- ios/Capacitor/Capacitor/CAPBridge.swift | 27 +++++++++++++------ .../Capacitor/CAPBridgeViewController.swift | 12 ++++----- site/docs-md/basics/configuring-your-app.md | 7 ++--- .../migrating-from-cordova-to-capacitor.md | 4 +-- 7 files changed, 64 insertions(+), 26 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index 7cf9717bb..bf8aa7f39 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -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_"; @@ -176,7 +177,15 @@ 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; + + // custom URL schemes requires path ending with / + if (!scheme.equals(Bridge.CAPACITOR_HTTP_SCHEME) && !scheme.equals(CAPACITOR_HTTPS_SCHEME)) { + localUrl += "/"; + } if (appUrlConfig != null) { try { @@ -294,6 +303,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"); diff --git a/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java b/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java index c0e0c406d..768de91d8 100755 --- a/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java +++ b/android/capacitor/src/main/java/com/getcapacitor/WebViewLocalServer.java @@ -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; @@ -433,8 +432,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); + } } } diff --git a/ios/Capacitor/Capacitor/CAPAssetHandler.swift b/ios/Capacitor/Capacitor/CAPAssetHandler.swift index eaeda77d7..55388937c 100644 --- a/ios/Capacitor/Capacitor/CAPAssetHandler.swift +++ b/ios/Capacitor/Capacitor/CAPAssetHandler.swift @@ -5,6 +5,7 @@ class CAPAssetHandler: NSObject, WKURLSchemeHandler { private var basePath: String = "" private var config: CAPConfig! + private var scheme: String = CAPBridge.CAP_DEFAULT_SCHEME func setAssetPath(_ assetPath: String) { self.basePath = assetPath; @@ -13,14 +14,18 @@ class CAPAssetHandler: NSObject, WKURLSchemeHandler { func setConfig(_ config: CAPConfig) { self.config = config } + + func setScheme(_ scheme: String) { + self.scheme = scheme + } func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) { var startPath = self.basePath let url = urlSchemeTask.request.url! let stringToLoad = url.path - let scheme = url.scheme + let urlScheme = url.scheme - if scheme == CAPBridge.getSchemeForConfig(self.config) { + if urlScheme == self.scheme { if stringToLoad.isEmpty || url.pathExtension.isEmpty { startPath.append("/index.html") } else if stringToLoad.starts(with: CAPBridge.CAP_FILE_START) { diff --git a/ios/Capacitor/Capacitor/CAPBridge.swift b/ios/Capacitor/Capacitor/CAPBridge.swift index 6a820073d..b4b9b3a67 100644 --- a/ios/Capacitor/Capacitor/CAPBridge.swift +++ b/ios/Capacitor/Capacitor/CAPBridge.swift @@ -12,6 +12,7 @@ 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_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? @@ -35,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 = CAPBridge.CAP_DEFAULT_SCHEME // Whether the app is active private var isActive = true @@ -51,8 +54,10 @@ enum BridgeError: Error { super.init() + determineScheme() + self.notificationsDelegate.bridge = self; - localUrl = "\(CAPBridge.getSchemeForConfig(self.config))://\(config.getString("server.hostname") ?? "localhost")" + localUrl = "\(self.scheme)://\(config.getString("server.hostname") ?? "localhost")" exportCoreJS(localUrl: localUrl!) registerPlugins() setupCordovaCompatibility() @@ -109,13 +114,6 @@ enum BridgeError: Error { CAPBridge.lastUrl = url return true } - - /** - * Determine which scheme to use when serving content - */ - public static func getSchemeForConfig(_ config: CAPConfig) -> String { - return config.getString("server.scheme") ?? "capacitor" - } /** * Handle continueUserActivity, for now this just provides universal link responding support. @@ -498,6 +496,19 @@ enum BridgeError: Error { } } } + + /** + * Determine which scheme to use and check if it is valid + */ + private func determineScheme() { + let configScheme = config.getString("server.iosScheme") + if let specifiedScheme = configScheme { + // check if WebKit handles scheme and if it is valid according to Apple's documentation + if !!WKWebView.handlesURLScheme(self.scheme) && specifiedScheme.range(of: "^[a-z][a-z0-9.+-]*$", options: [.regularExpression, .caseInsensitive], range: nil, locale: nil) != nil { + self.scheme = specifiedScheme + } + } + } /** * Eval JS for a specific plugin. diff --git a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift index b359c2c3e..0d1eb368d 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift @@ -47,16 +47,17 @@ public class CAPBridgeViewController: UIViewController, CAPBridgeDelegate, WKScr setScreenOrientationDefaults() let capConfig = CAPConfig(self.config) + + let o = WKUserContentController() + o.add(self, name: "bridge") + bridge = CAPBridge(self, o, capConfig) HTTPCookieStorage.shared.cookieAcceptPolicy = HTTPCookie.AcceptPolicy.always let webViewConfiguration = WKWebViewConfiguration() self.handler = CAPAssetHandler() self.handler!.setAssetPath(startPath) - self.handler!.setConfig(capConfig) - webViewConfiguration.setURLSchemeHandler(self.handler, forURLScheme: CAPBridge.getSchemeForConfig(capConfig)) - - let o = WKUserContentController() - o.add(self, name: "bridge") + self.handler!.setScheme(bridge.scheme) + webViewConfiguration.setURLSchemeHandler(self.handler, forURLScheme: bridge.scheme) webViewConfiguration.userContentController = o @@ -74,7 +75,6 @@ public class CAPBridgeViewController: UIViewController, CAPBridgeDelegate, WKScr setKeyboardRequiresUserInteraction(false) - bridge = CAPBridge(self, o, capConfig) if let scrollEnabled = bridge!.config.getValue("ios.scrollEnabled") as? Bool { webView?.scrollView.isScrollEnabled = scrollEnabled } diff --git a/site/docs-md/basics/configuring-your-app.md b/site/docs-md/basics/configuring-your-app.md index 74068d2a1..92e52f7d4 100644 --- a/site/docs-md/basics/configuring-your-app.md +++ b/site/docs-md/basics/configuring-your-app.md @@ -63,9 +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", - // On iOS, 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 is ionic. - "scheme": "ionic", + // 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": [ diff --git a/site/docs-md/cordova/migrating-from-cordova-to-capacitor.md b/site/docs-md/cordova/migrating-from-cordova-to-capacitor.md index b8d44db07..802c8e20f 100644 --- a/site/docs-md/cordova/migrating-from-cordova-to-capacitor.md +++ b/site/docs-md/cordova/migrating-from-cordova-to-capacitor.md @@ -116,12 +116,12 @@ 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. Capacitor uses its own webview, with its own origin. This means that using a origin-binded Web API like local storage, will result in a loss of data. This can be fixed by changing the scheme that is used for serving the content: +When using Ionic with Cordova, your app uses `cordova-plugin-ionic-webview` by default. Capacitor uses its own webview, with its own origin. This means that using a origin-binded Web API like local storage, will result in a loss of data. The origin on iOS for `cordova-plugin-ionic-webview` is different from the Capacitor one. This can be fixed by changing the scheme that is used for serving the content: ```json { "server": { - "scheme": "ionic" + "iosScheme": "ionic" } } ``` From 0bc68a745f45c5e24c2d757275d88973a954c2eb Mon Sep 17 00:00:00 2001 From: Isabel van Bodegom Date: Tue, 20 Aug 2019 12:12:26 +0200 Subject: [PATCH 3/6] Update iOS build errors --- ios/Capacitor/Capacitor/CAPBridge.swift | 2 +- ios/Capacitor/Capacitor/CAPBridgeViewController.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ios/Capacitor/Capacitor/CAPBridge.swift b/ios/Capacitor/Capacitor/CAPBridge.swift index b4b9b3a67..db5289b8e 100644 --- a/ios/Capacitor/Capacitor/CAPBridge.swift +++ b/ios/Capacitor/Capacitor/CAPBridge.swift @@ -504,7 +504,7 @@ enum BridgeError: Error { let configScheme = config.getString("server.iosScheme") if let specifiedScheme = configScheme { // check if WebKit handles scheme and if it is valid according to Apple's documentation - if !!WKWebView.handlesURLScheme(self.scheme) && specifiedScheme.range(of: "^[a-z][a-z0-9.+-]*$", options: [.regularExpression, .caseInsensitive], range: nil, locale: nil) != nil { + if !WKWebView.handlesURLScheme(self.scheme) && specifiedScheme.range(of: "^[a-z][a-z0-9.+-]*$", options: [.regularExpression, .caseInsensitive], range: nil, locale: nil) != nil { self.scheme = specifiedScheme } } diff --git a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift index 0d1eb368d..a4be9f3ea 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift @@ -56,8 +56,8 @@ public class CAPBridgeViewController: UIViewController, CAPBridgeDelegate, WKScr let webViewConfiguration = WKWebViewConfiguration() self.handler = CAPAssetHandler() self.handler!.setAssetPath(startPath) - self.handler!.setScheme(bridge.scheme) - webViewConfiguration.setURLSchemeHandler(self.handler, forURLScheme: bridge.scheme) + self.handler!.setScheme(bridge!.scheme) + webViewConfiguration.setURLSchemeHandler(self.handler, forURLScheme: bridge!.scheme) webViewConfiguration.userContentController = o From a4b3710aaf1da3b120dc3391755554e750c57e4c Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Fri, 23 Aug 2019 14:27:44 +0200 Subject: [PATCH 4/6] Append the / to appUrl, not to localUrl --- .../src/main/java/com/getcapacitor/Bridge.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java index 4e003f61d..3bd25fcb4 100644 --- a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java +++ b/android/capacitor/src/main/java/com/getcapacitor/Bridge.java @@ -182,11 +182,6 @@ private void loadWebView() { localUrl = scheme + "://" + authority; - // custom URL schemes requires path ending with / - if (!scheme.equals(Bridge.CAPACITOR_HTTP_SCHEME) && !scheme.equals(CAPACITOR_HTTPS_SCHEME)) { - localUrl += "/"; - } - if (appUrlConfig != null) { try { URL appUrlObject = new URL(appUrlConfig); @@ -205,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); From 2ac6196076ca80791d6f2ca58ca36a4411da7f6d Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Fri, 23 Aug 2019 16:26:39 +0200 Subject: [PATCH 5/6] Simplify iOS code --- ios/Capacitor/Capacitor/CAPAssetHandler.swift | 25 ++++------------ ios/Capacitor/Capacitor/CAPBridge.swift | 20 ++----------- .../Capacitor/CAPBridgeViewController.swift | 29 ++++++++++--------- 3 files changed, 24 insertions(+), 50 deletions(-) diff --git a/ios/Capacitor/Capacitor/CAPAssetHandler.swift b/ios/Capacitor/Capacitor/CAPAssetHandler.swift index 55388937c..4664f76d1 100644 --- a/ios/Capacitor/Capacitor/CAPAssetHandler.swift +++ b/ios/Capacitor/Capacitor/CAPAssetHandler.swift @@ -4,35 +4,22 @@ import MobileCoreServices class CAPAssetHandler: NSObject, WKURLSchemeHandler { private var basePath: String = "" - private var config: CAPConfig! - private var scheme: String = CAPBridge.CAP_DEFAULT_SCHEME func setAssetPath(_ assetPath: String) { self.basePath = assetPath; } - - func setConfig(_ config: CAPConfig) { - self.config = config - } - - func setScheme(_ scheme: String) { - self.scheme = scheme - } func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) { var startPath = self.basePath let url = urlSchemeTask.request.url! let stringToLoad = url.path - let urlScheme = url.scheme - if urlScheme == self.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) diff --git a/ios/Capacitor/Capacitor/CAPBridge.swift b/ios/Capacitor/Capacitor/CAPBridge.swift index db5289b8e..691fc2660 100644 --- a/ios/Capacitor/Capacitor/CAPBridge.swift +++ b/ios/Capacitor/Capacitor/CAPBridge.swift @@ -37,7 +37,7 @@ enum BridgeError: Error { // Calls we are storing to resolve later public var storedCalls = [String:CAPPluginCall]() // Scheme to use when serving content - public var scheme = CAPBridge.CAP_DEFAULT_SCHEME + public var scheme: String // Whether the app is active private var isActive = true @@ -46,16 +46,15 @@ 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() - determineScheme() - self.notificationsDelegate.bridge = self; localUrl = "\(self.scheme)://\(config.getString("server.hostname") ?? "localhost")" exportCoreJS(localUrl: localUrl!) @@ -496,19 +495,6 @@ enum BridgeError: Error { } } } - - /** - * Determine which scheme to use and check if it is valid - */ - private func determineScheme() { - let configScheme = config.getString("server.iosScheme") - if let specifiedScheme = configScheme { - // check if WebKit handles scheme and if it is valid according to Apple's documentation - if !WKWebView.handlesURLScheme(self.scheme) && specifiedScheme.range(of: "^[a-z][a-z0-9.+-]*$", options: [.regularExpression, .caseInsensitive], range: nil, locale: nil) != nil { - self.scheme = specifiedScheme - } - } - } /** * Eval JS for a specific plugin. diff --git a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift index a4be9f3ea..a2a8d9128 100644 --- a/ios/Capacitor/Capacitor/CAPBridgeViewController.swift +++ b/ios/Capacitor/Capacitor/CAPBridgeViewController.swift @@ -45,19 +45,22 @@ public class CAPBridgeViewController: UIViewController, CAPBridgeDelegate, WKScr setStatusBarDefaults() setScreenOrientationDefaults() - let capConfig = CAPConfig(self.config) - - let o = WKUserContentController() - o.add(self, name: "bridge") - bridge = CAPBridge(self, o, capConfig) HTTPCookieStorage.shared.cookieAcceptPolicy = HTTPCookie.AcceptPolicy.always let webViewConfiguration = WKWebViewConfiguration() self.handler = CAPAssetHandler() self.handler!.setAssetPath(startPath) - self.handler!.setScheme(bridge!.scheme) - webViewConfiguration.setURLSchemeHandler(self.handler, forURLScheme: bridge!.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") webViewConfiguration.userContentController = o @@ -75,16 +78,17 @@ public class CAPBridgeViewController: UIViewController, CAPBridgeDelegate, WKScr setKeyboardRequiresUserInteraction(false) + 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 { @@ -159,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 @@ -437,7 +440,6 @@ public class CAPBridgeViewController: UIViewController, CAPBridgeDelegate, WKScr return self.webView! } - public func getServerBasePath() -> String { return self.basePath } @@ -486,4 +488,3 @@ public class CAPBridgeViewController: UIViewController, CAPBridgeDelegate, WKScr */ } - From c83216cb181ce0cd98f170c8219dd99a4c6b032a Mon Sep 17 00:00:00 2001 From: jcesarmobile Date: Fri, 23 Aug 2019 16:38:20 +0200 Subject: [PATCH 6/6] reword cordova migration text --- site/docs-md/cordova/migrating-from-cordova-to-capacitor.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/site/docs-md/cordova/migrating-from-cordova-to-capacitor.md b/site/docs-md/cordova/migrating-from-cordova-to-capacitor.md index 802c8e20f..1240b281c 100644 --- a/site/docs-md/cordova/migrating-from-cordova-to-capacitor.md +++ b/site/docs-md/cordova/migrating-from-cordova-to-capacitor.md @@ -116,7 +116,8 @@ 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. Capacitor uses its own webview, with its own origin. This means that using a origin-binded Web API like local storage, will result in a loss of data. The origin on iOS for `cordova-plugin-ionic-webview` is different from the Capacitor one. This can be fixed by changing the scheme that is used for serving the content: + +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 {