@capacitor/ios 4.0.1-alpha.0 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,13 +3,164 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
- ## [4.0.1-alpha.0](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.1-alpha.0) (2022-03-25)
6
+ ## [4.0.1](https://github.com/ionic-team/capacitor/compare/4.0.0...4.0.1) (2022-07-28)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **ios:** publish Podfile script ([#5799](https://github.com/ionic-team/capacitor/issues/5799)) ([604f03a](https://github.com/ionic-team/capacitor/commit/604f03a29bc500d2841987d0a0f1b20d34fba7d6))
12
+
13
+
14
+
15
+
16
+
17
+ # [4.0.0](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.2...4.0.0) (2022-07-27)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * **ios:** error data is optional ([#5782](https://github.com/ionic-team/capacitor/issues/5782)) ([da48d79](https://github.com/ionic-team/capacitor/commit/da48d798c3463de9de188ae6a6475fd6afba6091))
23
+
24
+
25
+ ### Features
26
+
27
+ * **android:** Add Optional Data Param for Error Object ([#5719](https://github.com/ionic-team/capacitor/issues/5719)) ([174172b](https://github.com/ionic-team/capacitor/commit/174172b6c64dc9117c48ed0e20c25e0b6c2fb625))
28
+ * **android:** Use addWebMessageListener where available ([#5427](https://github.com/ionic-team/capacitor/issues/5427)) ([c2dfe80](https://github.com/ionic-team/capacitor/commit/c2dfe808446717412b35e82713d123b7a052f264))
29
+ * **ios:** Add overrideable router var for CAPWebView. ([#5743](https://github.com/ionic-team/capacitor/issues/5743)) ([c1de1c0](https://github.com/ionic-team/capacitor/commit/c1de1c0138aad188a760118e35983d10d257f8e7))
30
+ * **iOS:** post install script for deployment target ([#5783](https://github.com/ionic-team/capacitor/issues/5783)) ([f5afa94](https://github.com/ionic-team/capacitor/commit/f5afa94b3b9c246d87b2af03359840f503bace90))
31
+ * Add option for custom error page ([#5723](https://github.com/ionic-team/capacitor/issues/5723)) ([e8bdef3](https://github.com/ionic-team/capacitor/commit/e8bdef3b4634e4ad45fa8fc34c7c0ab8dfa383f3))
32
+
33
+
34
+
35
+
36
+
37
+ # [4.0.0-beta.2](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.1...4.0.0-beta.2) (2022-07-08)
38
+
39
+
40
+ ### Bug Fixes
41
+
42
+ * **ios:** Add check for both serverURL and localURL in navigation ([#5736](https://github.com/ionic-team/capacitor/issues/5736)) ([8e824f3](https://github.com/ionic-team/capacitor/commit/8e824f33ad4df898fb8c0936a8f5e9041832a5c5))
43
+ * **ios:** properly deliver retained events after listener re-add [#5732](https://github.com/ionic-team/capacitor/issues/5732) ([c5d6328](https://github.com/ionic-team/capacitor/commit/c5d632831924a1bcc868bc46b42f7ff619408752))
44
+
45
+
46
+ ### Features
47
+
48
+ * **ios:** Add `setServerBasePath(path:)` to CAPWebView ([#5742](https://github.com/ionic-team/capacitor/issues/5742)) ([1afbf8a](https://github.com/ionic-team/capacitor/commit/1afbf8a9dd0b8f7b1ac439d24e5d8ba26f786318))
49
+ * Add CapWebView ([#5715](https://github.com/ionic-team/capacitor/issues/5715)) ([143d266](https://github.com/ionic-team/capacitor/commit/143d266ef0a818bac59dbbdaeda3b5c382ebfa1d))
50
+
51
+
52
+
53
+
54
+
55
+ # [4.0.0-beta.1](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.0...4.0.0-beta.1) (2022-06-27)
56
+
57
+
58
+ ### Bug Fixes
59
+
60
+ * **ios:** Remove Cordova as an embedded framework ([#5709](https://github.com/ionic-team/capacitor/issues/5709)) ([bbf6d24](https://github.com/ionic-team/capacitor/commit/bbf6d248bf9217a5c5c6c15c7bcfeda209aba5b1))
61
+
62
+
63
+ ### Features
64
+
65
+ * **ios:** Allow to configure popover size ([#5717](https://github.com/ionic-team/capacitor/issues/5717)) ([ca1a125](https://github.com/ionic-team/capacitor/commit/ca1a125e5ab05d6066dd303bc75e99dfe21f210a))
66
+
67
+
68
+
69
+
70
+
71
+ # [4.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/3.6.0...4.0.0-beta.0) (2022-06-17)
72
+
73
+
74
+ ### Bug Fixes
75
+
76
+ * **ios:** make removeAllListeners return a promise ([#5526](https://github.com/ionic-team/capacitor/issues/5526)) ([815f71b](https://github.com/ionic-team/capacitor/commit/815f71b6b62f6c4d5f66e6a36c190bb00a96fdcc))
7
77
 
8
78
 
9
79
  ### Features
10
80
 
11
81
  * **ios:** add getConfig to CAPPlugin ([#5495](https://github.com/ionic-team/capacitor/issues/5495)) ([224a9d0](https://github.com/ionic-team/capacitor/commit/224a9d075629d9c9da9ddc658eb282617fc46d09))
82
+ * **ios:** Add preferredContentMode configuration option ([#5583](https://github.com/ionic-team/capacitor/issues/5583)) ([5b6dfa3](https://github.com/ionic-team/capacitor/commit/5b6dfa3fe29c85632546b299f03cc04a77cf7475))
83
+ * **ios:** Support of range requests on WebViewAssetHandler ([#5659](https://github.com/ionic-team/capacitor/issues/5659)) ([348c08d](https://github.com/ionic-team/capacitor/commit/348c08d511e9d57a1b2ecedc3290c65fa9ba3924))
84
+
85
+
86
+
87
+
88
+
89
+ # [3.6.0](https://github.com/ionic-team/capacitor/compare/3.5.1...3.6.0) (2022-06-17)
90
+
91
+
92
+ ### Bug Fixes
12
93
 
94
+ * **ios:** Use `URL(fileURLWithPath:)` instead of `URL(string:)` ([#5603](https://github.com/ionic-team/capacitor/issues/5603)) ([5fac1b2](https://github.com/ionic-team/capacitor/commit/5fac1b2da5aa5882087716cb2aa862d89173f4a1))
95
+
96
+
97
+ ### Features
98
+
99
+ * **iOS, Android:** add AppUUID Lib for plugins ([#5690](https://github.com/ionic-team/capacitor/issues/5690)) ([05e76cf](https://github.com/ionic-team/capacitor/commit/05e76cf526a44e07fa75f9482fa2223a13918638))
100
+
101
+
102
+
103
+
104
+
105
+ # [4.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.2) (2022-05-12)
106
+
107
+
108
+ ### Bug Fixes
109
+
110
+ * **ios:** make removeAllListeners return a promise ([#5526](https://github.com/ionic-team/capacitor/issues/5526)) ([815f71b](https://github.com/ionic-team/capacitor/commit/815f71b6b62f6c4d5f66e6a36c190bb00a96fdcc))
111
+
112
+
113
+ ### Features
114
+
115
+ * **ios:** add getConfig to CAPPlugin ([#5495](https://github.com/ionic-team/capacitor/issues/5495)) ([224a9d0](https://github.com/ionic-team/capacitor/commit/224a9d075629d9c9da9ddc658eb282617fc46d09))
116
+ * **ios:** Add preferredContentMode configuration option ([#5583](https://github.com/ionic-team/capacitor/issues/5583)) ([5b6dfa3](https://github.com/ionic-team/capacitor/commit/5b6dfa3fe29c85632546b299f03cc04a77cf7475))
117
+
118
+
119
+
120
+
121
+
122
+
123
+ ## [3.5.1](https://github.com/ionic-team/capacitor/compare/3.5.0...3.5.1) (2022-05-04)
124
+
125
+ **Note:** Version bump only for package @capacitor/ios
126
+
127
+
128
+
129
+
130
+
131
+ # [3.5.0](https://github.com/ionic-team/capacitor/compare/3.4.3...3.5.0) (2022-04-22)
132
+
133
+
134
+ ### Features
135
+
136
+ * **ios:** Add overrideable routing for CAPBridgeViewController subclasses ([#5546](https://github.com/ionic-team/capacitor/issues/5546)) ([8875d5e](https://github.com/ionic-team/capacitor/commit/8875d5e2721e8a8ee763ce70cb672db383f36efa))
137
+
138
+
139
+
140
+
141
+
142
+ # [4.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.1) (2022-03-25)
143
+
144
+
145
+ ### Features
146
+
147
+ * **ios:** add getConfig to CAPPlugin ([#5495](https://github.com/ionic-team/capacitor/issues/5495)) ([224a9d0](https://github.com/ionic-team/capacitor/commit/224a9d075629d9c9da9ddc658eb282617fc46d09))
148
+
149
+
150
+
151
+
152
+
153
+ ## [3.4.3](https://github.com/ionic-team/capacitor/compare/3.4.2...3.4.3) (2022-03-04)
154
+
155
+ **Note:** Version bump only for package @capacitor/ios
156
+
157
+
158
+
159
+
160
+
161
+ ## [3.4.2](https://github.com/ionic-team/capacitor/compare/3.4.1...3.4.2) (2022-03-03)
162
+
163
+ **Note:** Version bump only for package @capacitor/ios
13
164
 
14
165
 
15
166
 
@@ -0,0 +1,53 @@
1
+ import CommonCrypto
2
+ import Foundation
3
+
4
+ private func hexString(_ iterator: Array<UInt8>.Iterator) -> String {
5
+ return iterator.map { String(format: "%02x", $0) }.joined()
6
+ }
7
+
8
+ extension Data {
9
+ public var sha256: String {
10
+ var digest = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
11
+ self.withUnsafeBytes { bytes in
12
+ _ = CC_SHA256(bytes.baseAddress, CC_LONG(self.count), &digest)
13
+ }
14
+ return hexString(digest.makeIterator())
15
+ }
16
+ }
17
+
18
+ public class AppUUID {
19
+ private static let key: String = "CapacitorAppUUID"
20
+
21
+ public static func getAppUUID() -> String {
22
+ assertAppUUID()
23
+ return readUUID()
24
+ }
25
+
26
+ public static func regenerateAppUUID() {
27
+ let uuid = generateUUID()
28
+ writeUUID(uuid)
29
+ }
30
+
31
+ private static func assertAppUUID() {
32
+ let uuid = readUUID()
33
+ if uuid == "" {
34
+ regenerateAppUUID()
35
+ }
36
+ }
37
+
38
+ private static func generateUUID() -> String {
39
+ let uuid: String = UUID.init().uuidString
40
+ return uuid.data(using: .utf8)!.sha256
41
+ }
42
+
43
+ private static func readUUID() -> String {
44
+ let defaults = UserDefaults.standard
45
+ return defaults.string(forKey: key) ?? ""
46
+ }
47
+
48
+ private static func writeUUID(_ uuid: String) {
49
+ let defaults = UserDefaults.standard
50
+ defaults.set(uuid, forKey: key)
51
+ }
52
+
53
+ }
@@ -42,7 +42,7 @@ import Cordova
42
42
  setScreenOrientationDefaults()
43
43
 
44
44
  // get the web view
45
- let assetHandler = WebViewAssetHandler()
45
+ let assetHandler = WebViewAssetHandler(router: router())
46
46
  assetHandler.setAssetPath(configuration.appLocation.path)
47
47
  let delegationHandler = WebViewDelegationHandler()
48
48
  prepareWebView(with: configuration, assetHandler: assetHandler, delegationHandler: delegationHandler)
@@ -90,6 +90,10 @@ import Cordova
90
90
  return descriptor
91
91
  }
92
92
 
93
+ open func router() -> Router {
94
+ return _Router()
95
+ }
96
+
93
97
  /**
94
98
  The WKWebViewConfiguration to use for the webview.
95
99
 
@@ -115,6 +119,15 @@ import Cordova
115
119
  webViewConfiguration.applicationNameForUserAgent = appendUserAgent
116
120
  }
117
121
  }
122
+ if let preferredContentMode = instanceConfiguration.preferredContentMode {
123
+ var mode = WKWebpagePreferences.ContentMode.recommended
124
+ if preferredContentMode == "mobile" {
125
+ mode = WKWebpagePreferences.ContentMode.mobile
126
+ } else if preferredContentMode == "desktop" {
127
+ mode = WKWebpagePreferences.ContentMode.desktop
128
+ }
129
+ webViewConfiguration.defaultWebpagePreferences.preferredContentMode = mode
130
+ }
118
131
  return webViewConfiguration
119
132
  }
120
133
 
@@ -162,11 +175,7 @@ import Cordova
162
175
  }
163
176
  if let statusBarStyle = plist["UIStatusBarStyle"] as? String {
164
177
  if statusBarStyle == "UIStatusBarStyleDarkContent" {
165
- if #available(iOS 13.0, *) {
166
- self.statusBarStyle = .darkContent
167
- } else {
168
- self.statusBarStyle = .default
169
- }
178
+ self.statusBarStyle = .darkContent
170
179
  } else if statusBarStyle != "UIStatusBarStyleDefault" {
171
180
  self.statusBarStyle = .lightContent
172
181
  }
@@ -291,7 +300,7 @@ extension CAPBridgeViewController {
291
300
  if let backgroundColor = configuration.backgroundColor {
292
301
  aWebView.backgroundColor = backgroundColor
293
302
  aWebView.scrollView.backgroundColor = backgroundColor
294
- } else if #available(iOS 13, *) {
303
+ } else {
295
304
  // Use the system background colors if background is not set by user
296
305
  aWebView.backgroundColor = UIColor.systemBackground
297
306
  aWebView.scrollView.backgroundColor = UIColor.systemBackground
@@ -13,6 +13,7 @@ NS_SWIFT_NAME(InstanceConfiguration)
13
13
  @property (nonatomic, readonly, nonnull) NSArray<NSString*> *allowedNavigationHostnames;
14
14
  @property (nonatomic, readonly, nonnull) NSURL *localURL;
15
15
  @property (nonatomic, readonly, nonnull) NSURL *serverURL;
16
+ @property (nonatomic, readonly, nullable) NSString *errorPath;
16
17
  @property (nonatomic, readonly, nonnull) NSDictionary *pluginConfigurations;
17
18
  @property (nonatomic, readonly) BOOL loggingEnabled;
18
19
  @property (nonatomic, readonly) BOOL scrollingEnabled;
@@ -23,6 +24,7 @@ NS_SWIFT_NAME(InstanceConfiguration)
23
24
  @property (nonatomic, readonly, nonnull) NSURL *appLocation;
24
25
  @property (nonatomic, readonly, nullable) NSString *appStartPath;
25
26
  @property (nonatomic, readonly) BOOL limitsNavigationsToAppBoundDomains;
27
+ @property (nonatomic, readonly, nullable) NSString *preferredContentMode;
26
28
 
27
29
  @property (nonatomic, readonly, nonnull) NSDictionary *legacyConfig DEPRECATED_MSG_ATTRIBUTE("Use direct properties instead");
28
30
 
@@ -35,6 +35,7 @@
35
35
  _appLocation = descriptor.appLocation;
36
36
  _appStartPath = descriptor.appStartPath;
37
37
  _limitsNavigationsToAppBoundDomains = descriptor.limitsNavigationsToAppBoundDomains;
38
+ _preferredContentMode = descriptor.preferredContentMode;
38
39
  _pluginConfigurations = descriptor.pluginConfigurations;
39
40
  _legacyConfig = descriptor.legacyConfig;
40
41
  // construct the necessary URLs
@@ -45,6 +46,7 @@
45
46
  else {
46
47
  _serverURL = _localURL;
47
48
  }
49
+ _errorPath = descriptor.errorPath;
48
50
  // extract the one value we care about from the cordova configuration
49
51
  _cordovaDeployDisabled = [descriptor cordovaDeployDisabled];
50
52
  }
@@ -59,6 +61,7 @@
59
61
  _allowedNavigationHostnames = [[configuration allowedNavigationHostnames] copy];
60
62
  _localURL = [[configuration localURL] copy];
61
63
  _serverURL = [[configuration serverURL] copy];
64
+ _errorPath = [[configuration errorPath] copy];
62
65
  _pluginConfigurations = [[configuration pluginConfigurations] copy];
63
66
  _loggingEnabled = configuration.loggingEnabled;
64
67
  _scrollingEnabled = configuration.scrollingEnabled;
@@ -15,6 +15,14 @@ extension InstanceConfiguration {
15
15
  return serverURL
16
16
  }
17
17
 
18
+ @objc var errorPathURL: URL? {
19
+ guard let errorPath = errorPath else {
20
+ return nil
21
+ }
22
+
23
+ return localURL.appendingPathComponent(errorPath)
24
+ }
25
+
18
26
  @available(*, deprecated, message: "Use getPluginConfig")
19
27
  @objc public func getPluginConfigValue(_ pluginId: String, _ configKey: String) -> Any? {
20
28
  return (pluginConfigurations as? JSObject)?[keyPath: KeyPath("\(pluginId).\(configKey)")]
@@ -53,6 +53,11 @@ NS_SWIFT_NAME(InstanceDescriptor)
53
53
  @discussion Defaults to @c capacitor. Set by @c server.iosScheme in the configuration file.
54
54
  */
55
55
  @property (nonatomic, copy, nullable) NSString *urlScheme;
56
+ /**
57
+ @brief The path to a local html page to display in case of errors.
58
+ @discussion Defaults to nil.
59
+ */
60
+ @property (nonatomic, copy, nullable) NSString *errorPath;
56
61
  /**
57
62
  @brief The hostname that will be used for the server URL.
58
63
  @discussion Defaults to @c localhost. Set by @c server.hostname in the configuration file.
@@ -108,6 +113,11 @@ NS_SWIFT_NAME(InstanceDescriptor)
108
113
  @discussion Defaults to @c false. Set by @c ios.limitsNavigationsToAppBoundDomains in the configuration file. Required to be @c true for plugins to work if the app includes @c WKAppBoundDomains in the Info.plist.
109
114
  */
110
115
  @property (nonatomic, assign) BOOL limitsNavigationsToAppBoundDomains;
116
+ /**
117
+ @brief The content mode for the web view to use when it loads and renders web content.
118
+ @discussion Defaults to @c recommended. Set by @c ios.preferredContentMode in the configuration file.
119
+ */
120
+ @property (nonatomic, copy, nullable) NSString *preferredContentMode;
111
121
  /**
112
122
  @brief The parser used to load the cofiguration for Cordova plugins.
113
123
  */
@@ -96,6 +96,9 @@ internal extension InstanceDescriptor {
96
96
  if let urlString = config[keyPath: "server.url"] as? String {
97
97
  serverURL = urlString
98
98
  }
99
+ if let errorPathString = (config[keyPath: "server.errorPath"] as? String) {
100
+ errorPath = errorPathString
101
+ }
99
102
  if let insetBehavior = config[keyPath: "ios.contentInset"] as? String {
100
103
  let availableInsets: [String: UIScrollView.ContentInsetAdjustmentBehavior] = ["automatic": .automatic,
101
104
  "scrollableAxes": .scrollableAxes,
@@ -125,6 +128,9 @@ internal extension InstanceDescriptor {
125
128
  if let limitsNavigations = config[keyPath: "ios.limitsNavigationsToAppBoundDomains"] as? Bool {
126
129
  limitsNavigationsToAppBoundDomains = limitsNavigations
127
130
  }
131
+ if let preferredMode = (config[keyPath: "ios.preferredContentMode"] as? String) {
132
+ preferredContentMode = preferredMode
133
+ }
128
134
  }
129
135
  }
130
136
  // swiftlint:enable cyclomatic_complexity
@@ -4,7 +4,7 @@ public class CAPLog {
4
4
  public static func print(_ items: Any..., separator: String = " ", terminator: String = "\n") {
5
5
  if enableLogging {
6
6
  for (itemIndex, item) in items.enumerated() {
7
- Swift.print(item, terminator: itemIndex == items.count - 1 ? terminator : separator)
7
+ Swift.print("\(item)".prefix(4068), terminator: itemIndex == items.count - 1 ? terminator : separator)
8
8
  }
9
9
  }
10
10
  }
@@ -48,7 +48,8 @@
48
48
  -(NSString* _Nullable)getString:(CAPPluginCall* _Nonnull)call field:(NSString* _Nonnull)field defaultValue:(NSString* _Nonnull)defaultValue DEPRECATED_MSG_ATTRIBUTE("Use accessors on CAPPluginCall instead. See CAPBridgedJSTypes.h for Obj-C implementations.");
49
49
  -(id _Nullable)getConfigValue:(NSString* _Nonnull)key __deprecated_msg("use getConfig() and access config values using the methods available depending on the type.");
50
50
  -(PluginConfig* _Nonnull)getConfig;
51
- -(void)setCenteredPopover:(UIViewController* _Nonnull)vc;
52
- -(BOOL)supportsPopover;
51
+ -(void)setCenteredPopover:(UIViewController* _Nonnull) vc;
52
+ -(void)setCenteredPopover:(UIViewController* _Nonnull) vc size:(CGSize) size;
53
+ -(BOOL)supportsPopover DEPRECATED_MSG_ATTRIBUTE("All iOS 13+ devices support popover");
53
54
 
54
55
  @end
@@ -41,7 +41,7 @@
41
41
 
42
42
  - (void)addEventListener:(NSString *)eventName listener:(CAPPluginCall *)listener {
43
43
  NSMutableArray *listenersForEvent = [self.eventListeners objectForKey:eventName];
44
- if(!listenersForEvent) {
44
+ if(listenersForEvent == nil || [listenersForEvent count] == 0) {
45
45
  listenersForEvent = [[NSMutableArray alloc] initWithObjects:listener, nil];
46
46
  [self.eventListeners setValue:listenersForEvent forKey:eventName];
47
47
 
@@ -109,6 +109,7 @@
109
109
 
110
110
  - (void)removeAllListeners:(CAPPluginCall *)call {
111
111
  [self.eventListeners removeAllObjects];
112
+ [call resolve];
112
113
  }
113
114
 
114
115
  - (NSArray<CAPPluginCall *>*)getListeners:(NSString *)eventName {
@@ -144,12 +145,17 @@
144
145
  }
145
146
  }
146
147
 
148
+ -(void)setCenteredPopover:(UIViewController* _Nonnull) vc size:(CGSize) size {
149
+ if (self.bridge.viewController != nil) {
150
+ vc.popoverPresentationController.sourceRect = CGRectMake(self.bridge.viewController.view.center.x, self.bridge.viewController.view.center.y, 0, 0);
151
+ vc.preferredContentSize = size;
152
+ vc.popoverPresentationController.sourceView = self.bridge.viewController.view;
153
+ vc.popoverPresentationController.permittedArrowDirections = 0;
154
+ }
155
+ }
156
+
147
157
  -(BOOL)supportsPopover {
148
- if (@available(iOS 13, *)) {
149
158
  return YES;
150
- } else {
151
- return UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad;
152
- }
153
159
  }
154
160
 
155
161
  - (NSNumber*)shouldOverrideLoad:(WKNavigationAction*)navigationAction {
@@ -57,7 +57,7 @@ extension CAPPluginCall: JSValueContainer {
57
57
  errorHandler(CAPPluginCallError(message: message, code: nil, error: error, data: data))
58
58
  }
59
59
 
60
- func reject(_ message: String, _ code: String? = nil, _ error: Error? = nil, _ data: PluginCallResultData = [:]) {
60
+ func reject(_ message: String, _ code: String? = nil, _ error: Error? = nil, _ data: PluginCallResultData? = nil) {
61
61
  errorHandler(CAPPluginCallError(message: message, code: code, error: error, data: data))
62
62
  }
63
63
 
@@ -0,0 +1,242 @@
1
+ import Foundation
2
+ import WebKit
3
+ import UIKit
4
+
5
+ open class CAPWebView: UIView {
6
+ lazy var webView: WKWebView = createWebView(
7
+ with: configuration,
8
+ assetHandler: assetHandler,
9
+ delegationHandler: delegationHandler
10
+ )
11
+
12
+ private lazy var capacitorBridge = CapacitorBridge(
13
+ with: configuration,
14
+ delegate: self,
15
+ cordovaConfiguration: configDescriptor.cordovaConfiguration,
16
+ assetHandler: assetHandler,
17
+ delegationHandler: delegationHandler
18
+ )
19
+
20
+ public final var bridge: CAPBridgeProtocol {
21
+ return capacitorBridge
22
+ }
23
+
24
+ private lazy var configDescriptor = instanceDescriptor()
25
+ private lazy var configuration = InstanceConfiguration(with: configDescriptor, isDebug: CapacitorBridge.isDevEnvironment)
26
+
27
+ private lazy var assetHandler: WebViewAssetHandler = {
28
+ let handler = WebViewAssetHandler(router: router)
29
+ handler.setAssetPath(configuration.appLocation.path)
30
+ return handler
31
+ }()
32
+
33
+ private lazy var delegationHandler = WebViewDelegationHandler()
34
+
35
+ open var router: Router { _Router() }
36
+
37
+ public required init?(coder: NSCoder) {
38
+ super.init(coder: coder)
39
+ setup()
40
+ }
41
+
42
+ public init() {
43
+ super.init(frame: .zero)
44
+ setup()
45
+ }
46
+
47
+ private func setup() {
48
+ CAPLog.enableLogging = configuration.loggingEnabled
49
+ logWarnings(for: configDescriptor)
50
+
51
+ if configDescriptor.instanceType == .fixed { updateBinaryVersion() }
52
+
53
+ addSubview(webView)
54
+ webView.translatesAutoresizingMaskIntoConstraints = false
55
+ NSLayoutConstraint.activate([
56
+ webView.topAnchor.constraint(equalTo: topAnchor),
57
+ webView.bottomAnchor.constraint(equalTo: bottomAnchor),
58
+ webView.leadingAnchor.constraint(equalTo: leadingAnchor),
59
+ webView.trailingAnchor.constraint(equalTo: trailingAnchor)
60
+ ])
61
+
62
+ guard FileManager.default.fileExists(atPath: bridge.config.appStartFileURL.path) else { fatalLoadError() }
63
+ capacitorDidLoad()
64
+
65
+ let url = bridge.config.appStartServerURL
66
+ CAPLog.print("⚡️ Loading app at \(url.absoluteString)")
67
+ capacitorBridge.webViewDelegationHandler.willLoadWebview(webView)
68
+ _ = webView.load(URLRequest(url: url))
69
+ }
70
+
71
+ public lazy final var isNewBinary: Bool = {
72
+ if let curVersionCode = Bundle.main.infoDictionary?["CFBundleVersion"] as? String,
73
+ let curVersionName = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
74
+ if let lastVersionCode = UserDefaults.standard.string(forKey: "lastBinaryVersionCode"),
75
+ let lastVersionName = UserDefaults.standard.string(forKey: "lastBinaryVersionName") {
76
+ return (curVersionCode.isEqual(lastVersionCode) == false || curVersionName.isEqual(lastVersionName) == false)
77
+ }
78
+ }
79
+ return false
80
+ }()
81
+
82
+ open func instanceDescriptor() -> InstanceDescriptor {
83
+ let descriptor = InstanceDescriptor.init()
84
+ if !isNewBinary && !descriptor.cordovaDeployDisabled {
85
+ if let persistedPath = UserDefaults.standard.string(forKey: "serverBasePath"), !persistedPath.isEmpty {
86
+ if let libPath = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true).first {
87
+ descriptor.appLocation = URL(fileURLWithPath: libPath, isDirectory: true)
88
+ .appendingPathComponent("NoCloud")
89
+ .appendingPathComponent("ionic_built_snapshots")
90
+ .appendingPathComponent(URL(fileURLWithPath: persistedPath, isDirectory: true).lastPathComponent)
91
+ }
92
+ }
93
+ }
94
+ return descriptor
95
+ }
96
+
97
+ /**
98
+ Allows any additional configuration to be performed. The `webView` and `bridge` properties will be set by this point.
99
+
100
+ - Note: This is called before the webview has been added to the view hierarchy. Not all operations may be possible at
101
+ this time.
102
+ */
103
+ open func capacitorDidLoad() {
104
+ }
105
+
106
+ open func loadInitialContext(_ userContentController: WKUserContentController) {
107
+ CAPLog.print("in loadInitialContext base")
108
+ }
109
+
110
+ public func setServerBasePath(path: String) {
111
+ let url = URL(fileURLWithPath: path, isDirectory: true)
112
+ guard FileManager.default.fileExists(atPath: url.path) else { return }
113
+
114
+ capacitorBridge.config = capacitorBridge.config.updatingAppLocation(url)
115
+ capacitorBridge.webViewAssetHandler.setAssetPath(url.path)
116
+
117
+ DispatchQueue.main.async { [weak self] in
118
+ guard let self = self else { return }
119
+ _ = self.webView.load(URLRequest(url: self.capacitorBridge.config.serverURL))
120
+ }
121
+ }
122
+ }
123
+
124
+ extension CAPWebView {
125
+
126
+ open func webViewConfiguration(for instanceConfiguration: InstanceConfiguration) -> WKWebViewConfiguration {
127
+ let webViewConfiguration = WKWebViewConfiguration()
128
+ webViewConfiguration.allowsInlineMediaPlayback = true
129
+ webViewConfiguration.suppressesIncrementalRendering = false
130
+ webViewConfiguration.allowsAirPlayForMediaPlayback = true
131
+ webViewConfiguration.mediaTypesRequiringUserActionForPlayback = []
132
+ if let appendUserAgent = instanceConfiguration.appendedUserAgentString {
133
+ if let appName = webViewConfiguration.applicationNameForUserAgent {
134
+ webViewConfiguration.applicationNameForUserAgent = "\(appName) \(appendUserAgent)"
135
+ } else {
136
+ webViewConfiguration.applicationNameForUserAgent = appendUserAgent
137
+ }
138
+ }
139
+ return webViewConfiguration
140
+ }
141
+
142
+ private func createWebView(with configuration: InstanceConfiguration, assetHandler: WebViewAssetHandler, delegationHandler: WebViewDelegationHandler) -> WKWebView {
143
+ // set the cookie policy
144
+ HTTPCookieStorage.shared.cookieAcceptPolicy = HTTPCookie.AcceptPolicy.always
145
+ // setup the web view configuration
146
+ let webViewConfig = webViewConfiguration(for: configuration)
147
+ webViewConfig.setURLSchemeHandler(assetHandler, forURLScheme: configuration.localURL.scheme ?? InstanceDescriptorDefaults.scheme)
148
+ webViewConfig.userContentController = delegationHandler.contentController
149
+ // create the web view and set its properties
150
+ loadInitialContext(webViewConfig.userContentController)
151
+ let webView = WKWebView(frame: .zero, configuration: webViewConfig)
152
+ webView.scrollView.bounces = false
153
+ webView.scrollView.contentInsetAdjustmentBehavior = configuration.contentInsetAdjustmentBehavior
154
+ webView.allowsLinkPreview = configuration.allowLinkPreviews
155
+ webView.configuration.preferences.setValue(true, forKey: "allowFileAccessFromFileURLs")
156
+ webView.scrollView.isScrollEnabled = configuration.scrollingEnabled
157
+
158
+ if let overrideUserAgent = configuration.overridenUserAgentString {
159
+ webView.customUserAgent = overrideUserAgent
160
+ }
161
+
162
+ if let backgroundColor = configuration.backgroundColor {
163
+ self.backgroundColor = backgroundColor
164
+ webView.backgroundColor = backgroundColor
165
+ webView.scrollView.backgroundColor = backgroundColor
166
+ } else {
167
+ // Use the system background colors if background is not set by user
168
+ self.backgroundColor = UIColor.systemBackground
169
+ webView.backgroundColor = UIColor.systemBackground
170
+ webView.scrollView.backgroundColor = UIColor.systemBackground
171
+ }
172
+
173
+ // set our delegates
174
+ webView.uiDelegate = delegationHandler
175
+ webView.navigationDelegate = delegationHandler
176
+ return webView
177
+ }
178
+
179
+ private func logWarnings(for descriptor: InstanceDescriptor) {
180
+ if descriptor.warnings.contains(.missingAppDir) {
181
+ CAPLog.print("⚡️ ERROR: Unable to find application directory at: \"\(descriptor.appLocation.absoluteString)\"!")
182
+ }
183
+ if descriptor.instanceType == .fixed {
184
+ if descriptor.warnings.contains(.missingFile) {
185
+ CAPLog.print("Unable to find capacitor.config.json, make sure it exists and run npx cap copy.")
186
+ }
187
+ if descriptor.warnings.contains(.invalidFile) {
188
+ CAPLog.print("Unable to parse capacitor.config.json. Make sure it's valid JSON.")
189
+ }
190
+ if descriptor.warnings.contains(.missingCordovaFile) {
191
+ CAPLog.print("Unable to find config.xml, make sure it exists and run npx cap copy.")
192
+ }
193
+ if descriptor.warnings.contains(.invalidCordovaFile) {
194
+ CAPLog.print("Unable to parse config.xml. Make sure it's valid XML.")
195
+ }
196
+ }
197
+ }
198
+
199
+ private func updateBinaryVersion() {
200
+ guard isNewBinary else {
201
+ return
202
+ }
203
+ guard let versionCode = Bundle.main.infoDictionary?["CFBundleVersion"] as? String,
204
+ let versionName = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else {
205
+ return
206
+ }
207
+ let prefs = UserDefaults.standard
208
+ prefs.set(versionCode, forKey: "lastBinaryVersionCode")
209
+ prefs.set(versionName, forKey: "lastBinaryVersionName")
210
+ prefs.set("", forKey: "serverBasePath")
211
+ prefs.synchronize()
212
+ }
213
+
214
+ private func fatalLoadError() -> Never {
215
+ printLoadError()
216
+ exit(1)
217
+ }
218
+
219
+ private func printLoadError() {
220
+ let fullStartPath = capacitorBridge.config.appStartFileURL.path
221
+
222
+ CAPLog.print("⚡️ ERROR: Unable to load \(fullStartPath)")
223
+ CAPLog.print("⚡️ This file is the root of your web app and must exist before")
224
+ CAPLog.print("⚡️ Capacitor can run. Ensure you've run capacitor copy at least")
225
+ CAPLog.print("⚡️ or, if embedding, that this directory exists as a resource directory.")
226
+ }
227
+ }
228
+
229
+ extension CAPWebView: CAPBridgeDelegate {
230
+ internal var bridgedWebView: WKWebView? {
231
+ return webView
232
+ }
233
+
234
+ internal var bridgedViewController: UIViewController? {
235
+ // search for the parent view controller
236
+ var object = self.next
237
+ while !(object is UIViewController) && object != nil {
238
+ object = object?.next
239
+ }
240
+ return object as? UIViewController
241
+ }
242
+ }
@@ -97,7 +97,7 @@ internal class JSExport {
97
97
  let methods = [
98
98
  PluginHeaderMethod(name: "addListener", rtype: nil),
99
99
  PluginHeaderMethod(name: "removeListener", rtype: nil),
100
- PluginHeaderMethod(name: "removeAllListeners", rtype: nil),
100
+ PluginHeaderMethod(name: "removeAllListeners", rtype: "promise"),
101
101
  PluginHeaderMethod(name: "checkPermissions", rtype: "promise"),
102
102
  PluginHeaderMethod(name: "requestPermissions", rtype: "promise")
103
103
  ]
@@ -125,6 +125,15 @@ public protocol JSValueContainer: JSStringContainer, JSBoolContainer, JSIntConta
125
125
  }
126
126
 
127
127
  extension JSValueContainer {
128
+ public func getValue(_ key: String) -> JSValue? {
129
+ return jsObjectRepresentation[key]
130
+ }
131
+
132
+ @available(*, message: "All values returned conform to JSValue, use getValue(_:) instead.", renamed: "getValue(_:)")
133
+ public func getAny(_ key: String) -> Any? {
134
+ return getValue(key)
135
+ }
136
+
128
137
  public func getString(_ key: String) -> String? {
129
138
  return jsObjectRepresentation[key] as? String
130
139
  }
@@ -98,7 +98,7 @@ public enum PluginCallResult {
98
98
  self.code = code
99
99
  self.error = error
100
100
  if let data = data {
101
- resultData = .dictionary(data)
101
+ resultData = .dictionary(["data": data])
102
102
  } else {
103
103
  resultData = nil
104
104
  }
@@ -9,39 +9,39 @@ import Foundation
9
9
  self.config = config
10
10
  }
11
11
 
12
- public func getString(configKey: String, defaultValue: String? = nil) -> String? {
12
+ @objc public func getString(_ configKey: String, _ defaultValue: String? = nil) -> String? {
13
13
  if let val = (self.config)[keyPath: KeyPath(configKey)] as? String {
14
14
  return val
15
15
  }
16
16
  return defaultValue
17
17
  }
18
18
 
19
- public func getBoolean(configKey: String, defaultValue: Bool) -> Bool {
19
+ @objc public func getBoolean(_ configKey: String, _ defaultValue: Bool) -> Bool {
20
20
  if let val = (self.config)[keyPath: KeyPath(configKey)] as? Bool {
21
21
  return val
22
22
  }
23
23
  return defaultValue
24
24
  }
25
25
 
26
- public func getInt(configKey: String, defaultValue: Int) -> Int {
26
+ @objc public func getInt(_ configKey: String, _ defaultValue: Int) -> Int {
27
27
  if let val = (self.config)[keyPath: KeyPath(configKey)] as? Int {
28
28
  return val
29
29
  }
30
30
  return defaultValue
31
31
  }
32
32
 
33
- public func getArray(configKey: String, defaultValue: JSArray? = nil) -> JSArray? {
33
+ public func getArray(_ configKey: String, _ defaultValue: JSArray? = nil) -> JSArray? {
34
34
  if let val = (self.config)[keyPath: KeyPath(configKey)] as? JSArray {
35
35
  return val
36
36
  }
37
37
  return defaultValue
38
38
  }
39
39
 
40
- public func getObject(configKey: String) -> JSObject? {
40
+ public func getObject(_ configKey: String) -> JSObject? {
41
41
  return (self.config)[keyPath: KeyPath(configKey)] as? JSObject
42
42
  }
43
43
 
44
- public func isEmpty() -> Bool {
44
+ @objc public func isEmpty() -> Bool {
45
45
  return self.config.isEmpty
46
46
  }
47
47
 
@@ -0,0 +1,29 @@
1
+ //
2
+ // Router.swift
3
+ // Capacitor
4
+ //
5
+ // Created by Steven Sherry on 3/29/22.
6
+ // Copyright © 2022 Drifty Co. All rights reserved.
7
+ //
8
+
9
+ import Foundation
10
+
11
+ public protocol Router {
12
+ func route(for path: String) -> String
13
+ var basePath: String { get set }
14
+ }
15
+
16
+ // swiftlint:disable:next type_name
17
+ internal struct _Router: Router {
18
+ var basePath: String = ""
19
+ func route(for path: String) -> String {
20
+ let pathUrl = URL(fileURLWithPath: path)
21
+
22
+ // If there's no path extension it also means the path is empty or a SPA route
23
+ if pathUrl.pathExtension.isEmpty {
24
+ return basePath + "/index.html"
25
+ }
26
+
27
+ return basePath + path
28
+ }
29
+ }
@@ -1,20 +1,8 @@
1
1
  import UIKit
2
2
 
3
3
  internal class TmpViewController: UIViewController {
4
- var count = 0
5
4
  override func viewDidAppear(_ animated: Bool) {
6
5
  super.viewDidAppear(animated)
7
- // On iOS 12 viewDidAppear is called when TmpViewController is created and
8
- // when the presented VC gets dismissed.
9
- // On iOS 13 only when the presented VC gets dismissed.
10
- // We only want to send the notification when the presented VC gets dismissed.
11
- if #available(iOS 13, *) {
12
- count += 2
13
- } else {
14
- count += 1
15
- }
16
- if count > 1 {
17
- NotificationCenter.default.post(CapacitorBridge.tmpVCAppeared)
18
- }
6
+ NotificationCenter.default.post(CapacitorBridge.tmpVCAppeared)
19
7
  }
20
8
  }
@@ -26,14 +26,14 @@ public extension CapacitorExtensionTypeWrapper where T: UIColor {
26
26
  with: ""
27
27
  )
28
28
 
29
- var argb: UInt32 = 0
29
+ var argb: UInt64 = 0
30
30
 
31
31
  var red: CGFloat = 0.0
32
32
  var green: CGFloat = 0.0
33
33
  var blue: CGFloat = 0.0
34
34
  var alpha: CGFloat = 1.0
35
35
 
36
- guard Scanner(string: hexString).scanHexInt32(&argb) else { return nil }
36
+ guard Scanner(string: hexString).scanHexInt64(&argb) else { return nil }
37
37
 
38
38
  if hexString.count == 6 {
39
39
  red = CGFloat((argb & 0xFF0000) >> 16) / 255.0
@@ -3,49 +3,73 @@ import MobileCoreServices
3
3
 
4
4
  @objc(CAPWebViewAssetHandler)
5
5
  internal class WebViewAssetHandler: NSObject, WKURLSchemeHandler {
6
+ private var router: Router
6
7
 
7
- private var basePath: String = ""
8
+ init(router: Router) {
9
+ self.router = router
10
+ super.init()
11
+ }
8
12
 
9
13
  func setAssetPath(_ assetPath: String) {
10
- self.basePath = assetPath
14
+ router.basePath = assetPath
11
15
  }
12
16
 
13
17
  func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
14
- var startPath = self.basePath
18
+ let startPath: String
15
19
  let url = urlSchemeTask.request.url!
16
20
  let stringToLoad = url.path
17
21
 
18
22
  if stringToLoad.starts(with: CapacitorBridge.fileStartIdentifier) {
19
23
  startPath = stringToLoad.replacingOccurrences(of: CapacitorBridge.fileStartIdentifier, with: "")
20
- } else if stringToLoad.isEmpty || url.pathExtension.isEmpty {
21
- startPath.append("/index.html")
22
24
  } else {
23
- startPath.append(stringToLoad)
25
+ startPath = router.route(for: stringToLoad)
24
26
  }
27
+
25
28
  let localUrl = URL.init(string: url.absoluteString)!
26
29
  let fileUrl = URL.init(fileURLWithPath: startPath)
27
30
 
28
31
  do {
29
32
  var data = Data()
30
- if !stringToLoad.contains("cordova.js") {
31
- if isMediaExtension(pathExtension: url.pathExtension) {
32
- data = try Data(contentsOf: fileUrl, options: Data.ReadingOptions.mappedIfSafe)
33
- } else {
34
- data = try Data(contentsOf: fileUrl)
35
- }
36
- }
37
33
  let mimeType = mimeTypeForExtension(pathExtension: url.pathExtension)
38
- let expectedContentLength = data.count
39
- let headers = [
34
+ var headers = [
40
35
  "Content-Type": mimeType,
41
36
  "Cache-Control": "no-cache"
42
37
  ]
43
- let urlResponse = URLResponse(url: localUrl, mimeType: mimeType, expectedContentLength: expectedContentLength, textEncodingName: nil)
44
- let httpResponse = HTTPURLResponse(url: localUrl, statusCode: 200, httpVersion: nil, headerFields: headers)
45
- if isMediaExtension(pathExtension: url.pathExtension) {
46
- urlSchemeTask.didReceive(urlResponse)
38
+ if let rangeString = urlSchemeTask.request.value(forHTTPHeaderField: "Range"),
39
+ let totalSize = try fileUrl.resourceValues(forKeys: [.fileSizeKey]).fileSize,
40
+ isMediaExtension(pathExtension: url.pathExtension) {
41
+ let fileHandle = try FileHandle(forReadingFrom: fileUrl)
42
+ let parts = rangeString.components(separatedBy: "=")
43
+ let streamParts = parts[1].components(separatedBy: "-")
44
+ let fromRange = Int(streamParts[0]) ?? 0
45
+ var toRange = totalSize - 1
46
+ if streamParts.count > 1 {
47
+ toRange = Int(streamParts[1]) ?? toRange
48
+ }
49
+ let rangeLength = toRange - fromRange + 1
50
+ try fileHandle.seek(toOffset: UInt64(fromRange))
51
+ data = fileHandle.readData(ofLength: rangeLength)
52
+ headers["Accept-Ranges"] = "bytes"
53
+ headers["Content-Range"] = "bytes \(fromRange)-\(toRange)/\(totalSize)"
54
+ headers["Content-Length"] = String(data.count)
55
+ let response = HTTPURLResponse(url: localUrl, statusCode: 206, httpVersion: nil, headerFields: headers)
56
+ urlSchemeTask.didReceive(response!)
57
+ try fileHandle.close()
47
58
  } else {
48
- urlSchemeTask.didReceive(httpResponse!)
59
+ if !stringToLoad.contains("cordova.js") {
60
+ if isMediaExtension(pathExtension: url.pathExtension) {
61
+ data = try Data(contentsOf: fileUrl, options: Data.ReadingOptions.mappedIfSafe)
62
+ } else {
63
+ data = try Data(contentsOf: fileUrl)
64
+ }
65
+ }
66
+ let urlResponse = URLResponse(url: localUrl, mimeType: mimeType, expectedContentLength: data.count, textEncodingName: nil)
67
+ let httpResponse = HTTPURLResponse(url: localUrl, statusCode: 200, httpVersion: nil, headerFields: headers)
68
+ if isMediaExtension(pathExtension: url.pathExtension) {
69
+ urlSchemeTask.didReceive(urlResponse)
70
+ } else {
71
+ urlSchemeTask.didReceive(httpResponse!)
72
+ }
49
73
  }
50
74
  urlSchemeTask.didReceive(data)
51
75
  } catch let error as NSError {
@@ -46,9 +46,7 @@ internal class WebViewDelegationHandler: NSObject, WKNavigationDelegate, WKUIDel
46
46
  // Reset the bridge on each navigation
47
47
  bridge?.reset()
48
48
  }
49
-
50
- // TODO: remove once Xcode 12 support is dropped
51
- #if compiler(>=5.5)
49
+
52
50
  @available(iOS 15, *)
53
51
  func webView(
54
52
  _ webView: WKWebView,
@@ -62,12 +60,11 @@ internal class WebViewDelegationHandler: NSObject, WKNavigationDelegate, WKUIDel
62
60
 
63
61
  @available(iOS 15, *)
64
62
  func webView(_ webView: WKWebView,
65
- requestDeviceOrientationAndMotionPermissionFor origin: WKSecurityOrigin,
66
- initiatedByFrame frame: WKFrameInfo,
67
- decisionHandler: @escaping (WKPermissionDecision) -> Void) {
63
+ requestDeviceOrientationAndMotionPermissionFor origin: WKSecurityOrigin,
64
+ initiatedByFrame frame: WKFrameInfo,
65
+ decisionHandler: @escaping (WKPermissionDecision) -> Void) {
68
66
  decisionHandler(.grant)
69
67
  }
70
- #endif
71
68
 
72
69
  public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
73
70
  // post a notification for any listeners
@@ -105,7 +102,12 @@ internal class WebViewDelegationHandler: NSObject, WKNavigationDelegate, WKUIDel
105
102
 
106
103
  // otherwise, is this a new window or a main frame navigation but to an outside source
107
104
  let toplevelNavigation = (navigationAction.targetFrame == nil || navigationAction.targetFrame?.isMainFrame == true)
108
- if navURL.absoluteString.contains(bridge.config.serverURL.absoluteString) == false, toplevelNavigation {
105
+
106
+ // Check if the url being navigated to is configured as an application url (whether local or remote)
107
+ let isApplicationNavigation = navURL.absoluteString.starts(with: bridge.config.serverURL.absoluteString) ||
108
+ navURL.absoluteString.starts(with: bridge.config.localURL.absoluteString)
109
+
110
+ if !isApplicationNavigation, toplevelNavigation {
109
111
  // disallow and let the system handle it
110
112
  if UIApplication.shared.applicationState == .active {
111
113
  UIApplication.shared.open(navURL, options: [:], completionHandler: nil)
@@ -135,6 +137,11 @@ internal class WebViewDelegationHandler: NSObject, WKNavigationDelegate, WKUIDel
135
137
  webView.isOpaque = isOpaque
136
138
  webViewLoadingState = .subsequentLoad
137
139
  }
140
+
141
+ if let errorURL = bridge?.config.errorPathURL {
142
+ webView.load(URLRequest(url: errorURL))
143
+ }
144
+
138
145
  CAPLog.print("⚡️ WebView failed to load")
139
146
  CAPLog.print("⚡️ Error: " + error.localizedDescription)
140
147
  }
@@ -142,6 +149,10 @@ internal class WebViewDelegationHandler: NSObject, WKNavigationDelegate, WKUIDel
142
149
  // The force unwrap is part of the protocol declaration, so we should keep it.
143
150
  // swiftlint:disable:next implicitly_unwrapped_optional
144
151
  public func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
152
+ if let errorURL = bridge?.config.errorPathURL {
153
+ webView.load(URLRequest(url: errorURL))
154
+ }
155
+
145
156
  CAPLog.print("⚡️ WebView failed provisional navigation")
146
157
  CAPLog.print("⚡️ Error: " + error.localizedDescription)
147
158
  }
@@ -384,10 +384,18 @@ const nativeBridge = (function (exports) {
384
384
  }
385
385
  return null;
386
386
  };
387
+ if (win === null || win === void 0 ? void 0 : win.androidBridge) {
388
+ win.androidBridge.onmessage = function (event) {
389
+ returnResult(JSON.parse(event.data));
390
+ };
391
+ }
387
392
  /**
388
393
  * Process a response from the native layer.
389
394
  */
390
395
  cap.fromNative = result => {
396
+ returnResult(result);
397
+ };
398
+ const returnResult = (result) => {
391
399
  var _a, _b;
392
400
  if (cap.isLoggingEnabled && result.pluginId !== 'Console') {
393
401
  cap.logFromNative(result);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capacitor/ios",
3
- "version": "4.0.1-alpha.0",
3
+ "version": "4.0.1",
4
4
  "description": "Capacitor: Cross-platform apps with JavaScript and the web",
5
5
  "homepage": "https://capacitorjs.com",
6
6
  "author": "Ionic Team <hi@ionic.io> (https://ionic.io)",
@@ -16,7 +16,8 @@
16
16
  "Capacitor/Capacitor/",
17
17
  "CapacitorCordova/CapacitorCordova/",
18
18
  "Capacitor.podspec",
19
- "CapacitorCordova.podspec"
19
+ "CapacitorCordova.podspec",
20
+ "scripts/pods_helpers.rb"
20
21
  ],
21
22
  "scripts": {
22
23
  "verify": "npm run xc:build:Capacitor && npm run xc:build:CapacitorCordova",
@@ -24,10 +25,10 @@
24
25
  "xc:build:CapacitorCordova": "cd CapacitorCordova && xcodebuild && cd .."
25
26
  },
26
27
  "peerDependencies": {
27
- "@capacitor/core": "^3.4.0"
28
+ "@capacitor/core": "^4.0.0"
28
29
  },
29
30
  "publishConfig": {
30
31
  "access": "public"
31
32
  },
32
- "gitHead": "7faec09fd724a0af7436f3d30c83856b7bb5ce51"
33
+ "gitHead": "a14824b660ccd39b7ed150a26debb15e9a161303"
33
34
  }
@@ -0,0 +1,12 @@
1
+ def assertDeploymentTarget(installer)
2
+ installer.pods_project.targets.each do |target|
3
+ target.build_configurations.each do |config|
4
+ # ensure IPHONEOS_DEPLOYMENT_TARGET is at least 13.0
5
+ deployment_target = config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'].to_f
6
+ should_upgrade = deployment_target < 13.0 && deployment_target != 0.0
7
+ if should_upgrade
8
+ config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
9
+ end
10
+ end
11
+ end
12
+ end