@capacitor/ios 4.0.0-alpha.2 → 4.0.0-beta.2

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,6 +3,74 @@
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.0-beta.2](https://github.com/ionic-team/capacitor/compare/4.0.0-beta.1...4.0.0-beta.2) (2022-07-08)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **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))
12
+ * **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))
13
+
14
+
15
+ ### Features
16
+
17
+ * **ios:** Add `setServerBasePath(path:)` to CAPWebView ([#5742](https://github.com/ionic-team/capacitor/issues/5742)) ([1afbf8a](https://github.com/ionic-team/capacitor/commit/1afbf8a9dd0b8f7b1ac439d24e5d8ba26f786318))
18
+ * Add CapWebView ([#5715](https://github.com/ionic-team/capacitor/issues/5715)) ([143d266](https://github.com/ionic-team/capacitor/commit/143d266ef0a818bac59dbbdaeda3b5c382ebfa1d))
19
+
20
+
21
+
22
+
23
+
24
+ # [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)
25
+
26
+
27
+ ### Bug Fixes
28
+
29
+ * **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))
30
+
31
+
32
+ ### Features
33
+
34
+ * **ios:** Allow to configure popover size ([#5717](https://github.com/ionic-team/capacitor/issues/5717)) ([ca1a125](https://github.com/ionic-team/capacitor/commit/ca1a125e5ab05d6066dd303bc75e99dfe21f210a))
35
+
36
+
37
+
38
+
39
+
40
+ # [4.0.0-beta.0](https://github.com/ionic-team/capacitor/compare/3.6.0...4.0.0-beta.0) (2022-06-17)
41
+
42
+
43
+ ### Bug Fixes
44
+
45
+ * **ios:** make removeAllListeners return a promise ([#5526](https://github.com/ionic-team/capacitor/issues/5526)) ([815f71b](https://github.com/ionic-team/capacitor/commit/815f71b6b62f6c4d5f66e6a36c190bb00a96fdcc))
46
+
47
+
48
+ ### Features
49
+
50
+ * **ios:** add getConfig to CAPPlugin ([#5495](https://github.com/ionic-team/capacitor/issues/5495)) ([224a9d0](https://github.com/ionic-team/capacitor/commit/224a9d075629d9c9da9ddc658eb282617fc46d09))
51
+ * **ios:** Add preferredContentMode configuration option ([#5583](https://github.com/ionic-team/capacitor/issues/5583)) ([5b6dfa3](https://github.com/ionic-team/capacitor/commit/5b6dfa3fe29c85632546b299f03cc04a77cf7475))
52
+ * **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))
53
+
54
+
55
+
56
+
57
+
58
+ # [3.6.0](https://github.com/ionic-team/capacitor/compare/3.5.1...3.6.0) (2022-06-17)
59
+
60
+
61
+ ### Bug Fixes
62
+
63
+ * **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))
64
+
65
+
66
+ ### Features
67
+
68
+ * **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))
69
+
70
+
71
+
72
+
73
+
6
74
  # [4.0.0-alpha.2](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.2) (2022-05-12)
7
75
 
8
76
 
@@ -20,6 +88,26 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
20
88
 
21
89
 
22
90
 
91
+
92
+ ## [3.5.1](https://github.com/ionic-team/capacitor/compare/3.5.0...3.5.1) (2022-05-04)
93
+
94
+ **Note:** Version bump only for package @capacitor/ios
95
+
96
+
97
+
98
+
99
+
100
+ # [3.5.0](https://github.com/ionic-team/capacitor/compare/3.4.3...3.5.0) (2022-04-22)
101
+
102
+
103
+ ### Features
104
+
105
+ * **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))
106
+
107
+
108
+
109
+
110
+
23
111
  # [4.0.0-alpha.1](https://github.com/ionic-team/capacitor/compare/3.4.1...4.0.0-alpha.1) (2022-03-25)
24
112
 
25
113
 
@@ -31,6 +119,21 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
31
119
 
32
120
 
33
121
 
122
+ ## [3.4.3](https://github.com/ionic-team/capacitor/compare/3.4.2...3.4.3) (2022-03-04)
123
+
124
+ **Note:** Version bump only for package @capacitor/ios
125
+
126
+
127
+
128
+
129
+
130
+ ## [3.4.2](https://github.com/ionic-team/capacitor/compare/3.4.1...3.4.2) (2022-03-03)
131
+
132
+ **Note:** Version bump only for package @capacitor/ios
133
+
134
+
135
+
136
+
34
137
  ## [3.4.1](https://github.com/ionic-team/capacitor/compare/3.4.0...3.4.1) (2022-02-09)
35
138
 
36
139
 
@@ -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
 
@@ -171,11 +175,7 @@ import Cordova
171
175
  }
172
176
  if let statusBarStyle = plist["UIStatusBarStyle"] as? String {
173
177
  if statusBarStyle == "UIStatusBarStyleDarkContent" {
174
- if #available(iOS 13.0, *) {
175
- self.statusBarStyle = .darkContent
176
- } else {
177
- self.statusBarStyle = .default
178
- }
178
+ self.statusBarStyle = .darkContent
179
179
  } else if statusBarStyle != "UIStatusBarStyleDefault" {
180
180
  self.statusBarStyle = .lightContent
181
181
  }
@@ -300,7 +300,7 @@ extension CAPBridgeViewController {
300
300
  if let backgroundColor = configuration.backgroundColor {
301
301
  aWebView.backgroundColor = backgroundColor
302
302
  aWebView.scrollView.backgroundColor = backgroundColor
303
- } else if #available(iOS 13, *) {
303
+ } else {
304
304
  // Use the system background colors if background is not set by user
305
305
  aWebView.backgroundColor = UIColor.systemBackground
306
306
  aWebView.scrollView.backgroundColor = UIColor.systemBackground
@@ -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;
51
+ -(void)setCenteredPopover:(UIViewController* _Nonnull) vc;
52
+ -(void)setCenteredPopover:(UIViewController* _Nonnull) vc size:(CGSize) size;
52
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
 
@@ -145,6 +145,15 @@
145
145
  }
146
146
  }
147
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
+
148
157
  -(BOOL)supportsPopover {
149
158
  return YES;
150
159
  }
@@ -0,0 +1,240 @@
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
+ public required init?(coder: NSCoder) {
36
+ super.init(coder: coder)
37
+ setup()
38
+ }
39
+
40
+ public init() {
41
+ super.init(frame: .zero)
42
+ setup()
43
+ }
44
+
45
+ private func setup() {
46
+ CAPLog.enableLogging = configuration.loggingEnabled
47
+ logWarnings(for: configDescriptor)
48
+
49
+ if configDescriptor.instanceType == .fixed { updateBinaryVersion() }
50
+
51
+ addSubview(webView)
52
+ webView.translatesAutoresizingMaskIntoConstraints = false
53
+ NSLayoutConstraint.activate([
54
+ webView.topAnchor.constraint(equalTo: topAnchor),
55
+ webView.bottomAnchor.constraint(equalTo: bottomAnchor),
56
+ webView.leadingAnchor.constraint(equalTo: leadingAnchor),
57
+ webView.trailingAnchor.constraint(equalTo: trailingAnchor)
58
+ ])
59
+
60
+ guard FileManager.default.fileExists(atPath: bridge.config.appStartFileURL.path) else { fatalLoadError() }
61
+ capacitorDidLoad()
62
+
63
+ let url = bridge.config.appStartServerURL
64
+ CAPLog.print("⚡️ Loading app at \(url.absoluteString)")
65
+ capacitorBridge.webViewDelegationHandler.willLoadWebview(webView)
66
+ _ = webView.load(URLRequest(url: url))
67
+ }
68
+
69
+ public lazy final var isNewBinary: Bool = {
70
+ if let curVersionCode = Bundle.main.infoDictionary?["CFBundleVersion"] as? String,
71
+ let curVersionName = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
72
+ if let lastVersionCode = UserDefaults.standard.string(forKey: "lastBinaryVersionCode"),
73
+ let lastVersionName = UserDefaults.standard.string(forKey: "lastBinaryVersionName") {
74
+ return (curVersionCode.isEqual(lastVersionCode) == false || curVersionName.isEqual(lastVersionName) == false)
75
+ }
76
+ }
77
+ return false
78
+ }()
79
+
80
+ open func instanceDescriptor() -> InstanceDescriptor {
81
+ let descriptor = InstanceDescriptor.init()
82
+ if !isNewBinary && !descriptor.cordovaDeployDisabled {
83
+ if let persistedPath = UserDefaults.standard.string(forKey: "serverBasePath"), !persistedPath.isEmpty {
84
+ if let libPath = NSSearchPathForDirectoriesInDomains(.libraryDirectory, .userDomainMask, true).first {
85
+ descriptor.appLocation = URL(fileURLWithPath: libPath, isDirectory: true)
86
+ .appendingPathComponent("NoCloud")
87
+ .appendingPathComponent("ionic_built_snapshots")
88
+ .appendingPathComponent(URL(fileURLWithPath: persistedPath, isDirectory: true).lastPathComponent)
89
+ }
90
+ }
91
+ }
92
+ return descriptor
93
+ }
94
+
95
+ /**
96
+ Allows any additional configuration to be performed. The `webView` and `bridge` properties will be set by this point.
97
+
98
+ - Note: This is called before the webview has been added to the view hierarchy. Not all operations may be possible at
99
+ this time.
100
+ */
101
+ open func capacitorDidLoad() {
102
+ }
103
+
104
+ open func loadInitialContext(_ userContentController: WKUserContentController) {
105
+ CAPLog.print("in loadInitialContext base")
106
+ }
107
+
108
+ public func setServerBasePath(path: String) {
109
+ let url = URL(fileURLWithPath: path, isDirectory: true)
110
+ guard FileManager.default.fileExists(atPath: url.path) else { return }
111
+
112
+ capacitorBridge.config = capacitorBridge.config.updatingAppLocation(url)
113
+ capacitorBridge.webViewAssetHandler.setAssetPath(url.path)
114
+
115
+ DispatchQueue.main.async { [weak self] in
116
+ guard let self = self else { return }
117
+ _ = self.webView.load(URLRequest(url: self.capacitorBridge.config.serverURL))
118
+ }
119
+ }
120
+ }
121
+
122
+ extension CAPWebView {
123
+
124
+ open func webViewConfiguration(for instanceConfiguration: InstanceConfiguration) -> WKWebViewConfiguration {
125
+ let webViewConfiguration = WKWebViewConfiguration()
126
+ webViewConfiguration.allowsInlineMediaPlayback = true
127
+ webViewConfiguration.suppressesIncrementalRendering = false
128
+ webViewConfiguration.allowsAirPlayForMediaPlayback = true
129
+ webViewConfiguration.mediaTypesRequiringUserActionForPlayback = []
130
+ if let appendUserAgent = instanceConfiguration.appendedUserAgentString {
131
+ if let appName = webViewConfiguration.applicationNameForUserAgent {
132
+ webViewConfiguration.applicationNameForUserAgent = "\(appName) \(appendUserAgent)"
133
+ } else {
134
+ webViewConfiguration.applicationNameForUserAgent = appendUserAgent
135
+ }
136
+ }
137
+ return webViewConfiguration
138
+ }
139
+
140
+ private func createWebView(with configuration: InstanceConfiguration, assetHandler: WebViewAssetHandler, delegationHandler: WebViewDelegationHandler) -> WKWebView {
141
+ // set the cookie policy
142
+ HTTPCookieStorage.shared.cookieAcceptPolicy = HTTPCookie.AcceptPolicy.always
143
+ // setup the web view configuration
144
+ let webViewConfig = webViewConfiguration(for: configuration)
145
+ webViewConfig.setURLSchemeHandler(assetHandler, forURLScheme: configuration.localURL.scheme ?? InstanceDescriptorDefaults.scheme)
146
+ webViewConfig.userContentController = delegationHandler.contentController
147
+ // create the web view and set its properties
148
+ loadInitialContext(webViewConfig.userContentController)
149
+ let webView = WKWebView(frame: .zero, configuration: webViewConfig)
150
+ webView.scrollView.bounces = false
151
+ webView.scrollView.contentInsetAdjustmentBehavior = configuration.contentInsetAdjustmentBehavior
152
+ webView.allowsLinkPreview = configuration.allowLinkPreviews
153
+ webView.configuration.preferences.setValue(true, forKey: "allowFileAccessFromFileURLs")
154
+ webView.scrollView.isScrollEnabled = configuration.scrollingEnabled
155
+
156
+ if let overrideUserAgent = configuration.overridenUserAgentString {
157
+ webView.customUserAgent = overrideUserAgent
158
+ }
159
+
160
+ if let backgroundColor = configuration.backgroundColor {
161
+ self.backgroundColor = backgroundColor
162
+ webView.backgroundColor = backgroundColor
163
+ webView.scrollView.backgroundColor = backgroundColor
164
+ } else if #available(iOS 13, *) {
165
+ // Use the system background colors if background is not set by user
166
+ self.backgroundColor = UIColor.systemBackground
167
+ webView.backgroundColor = UIColor.systemBackground
168
+ webView.scrollView.backgroundColor = UIColor.systemBackground
169
+ }
170
+
171
+ // set our delegates
172
+ webView.uiDelegate = delegationHandler
173
+ webView.navigationDelegate = delegationHandler
174
+ return webView
175
+ }
176
+
177
+ private func logWarnings(for descriptor: InstanceDescriptor) {
178
+ if descriptor.warnings.contains(.missingAppDir) {
179
+ CAPLog.print("⚡️ ERROR: Unable to find application directory at: \"\(descriptor.appLocation.absoluteString)\"!")
180
+ }
181
+ if descriptor.instanceType == .fixed {
182
+ if descriptor.warnings.contains(.missingFile) {
183
+ CAPLog.print("Unable to find capacitor.config.json, make sure it exists and run npx cap copy.")
184
+ }
185
+ if descriptor.warnings.contains(.invalidFile) {
186
+ CAPLog.print("Unable to parse capacitor.config.json. Make sure it's valid JSON.")
187
+ }
188
+ if descriptor.warnings.contains(.missingCordovaFile) {
189
+ CAPLog.print("Unable to find config.xml, make sure it exists and run npx cap copy.")
190
+ }
191
+ if descriptor.warnings.contains(.invalidCordovaFile) {
192
+ CAPLog.print("Unable to parse config.xml. Make sure it's valid XML.")
193
+ }
194
+ }
195
+ }
196
+
197
+ private func updateBinaryVersion() {
198
+ guard isNewBinary else {
199
+ return
200
+ }
201
+ guard let versionCode = Bundle.main.infoDictionary?["CFBundleVersion"] as? String,
202
+ let versionName = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else {
203
+ return
204
+ }
205
+ let prefs = UserDefaults.standard
206
+ prefs.set(versionCode, forKey: "lastBinaryVersionCode")
207
+ prefs.set(versionName, forKey: "lastBinaryVersionName")
208
+ prefs.set("", forKey: "serverBasePath")
209
+ prefs.synchronize()
210
+ }
211
+
212
+ private func fatalLoadError() -> Never {
213
+ printLoadError()
214
+ exit(1)
215
+ }
216
+
217
+ private func printLoadError() {
218
+ let fullStartPath = capacitorBridge.config.appStartFileURL.path
219
+
220
+ CAPLog.print("⚡️ ERROR: Unable to load \(fullStartPath)")
221
+ CAPLog.print("⚡️ This file is the root of your web app and must exist before")
222
+ CAPLog.print("⚡️ Capacitor can run. Ensure you've run capacitor copy at least")
223
+ CAPLog.print("⚡️ or, if embedding, that this directory exists as a resource directory.")
224
+ }
225
+ }
226
+
227
+ extension CAPWebView: CAPBridgeDelegate {
228
+ internal var bridgedWebView: WKWebView? {
229
+ return webView
230
+ }
231
+
232
+ internal var bridgedViewController: UIViewController? {
233
+ // search for the parent view controller
234
+ var object = self.next
235
+ while !(object is UIViewController) && object != nil {
236
+ object = object?.next
237
+ }
238
+ return object as? UIViewController
239
+ }
240
+ }
@@ -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
  }
@@ -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,7 +46,7 @@ internal class WebViewDelegationHandler: NSObject, WKNavigationDelegate, WKUIDel
46
46
  // Reset the bridge on each navigation
47
47
  bridge?.reset()
48
48
  }
49
-
49
+
50
50
  @available(iOS 15, *)
51
51
  func webView(
52
52
  _ webView: WKWebView,
@@ -60,9 +60,9 @@ internal class WebViewDelegationHandler: NSObject, WKNavigationDelegate, WKUIDel
60
60
 
61
61
  @available(iOS 15, *)
62
62
  func webView(_ webView: WKWebView,
63
- requestDeviceOrientationAndMotionPermissionFor origin: WKSecurityOrigin,
64
- initiatedByFrame frame: WKFrameInfo,
65
- decisionHandler: @escaping (WKPermissionDecision) -> Void) {
63
+ requestDeviceOrientationAndMotionPermissionFor origin: WKSecurityOrigin,
64
+ initiatedByFrame frame: WKFrameInfo,
65
+ decisionHandler: @escaping (WKPermissionDecision) -> Void) {
66
66
  decisionHandler(.grant)
67
67
  }
68
68
 
@@ -102,7 +102,12 @@ internal class WebViewDelegationHandler: NSObject, WKNavigationDelegate, WKUIDel
102
102
 
103
103
  // otherwise, is this a new window or a main frame navigation but to an outside source
104
104
  let toplevelNavigation = (navigationAction.targetFrame == nil || navigationAction.targetFrame?.isMainFrame == true)
105
- 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 {
106
111
  // disallow and let the system handle it
107
112
  if UIApplication.shared.applicationState == .active {
108
113
  UIApplication.shared.open(navURL, options: [:], completionHandler: nil)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capacitor/ios",
3
- "version": "4.0.0-alpha.2",
3
+ "version": "4.0.0-beta.2",
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)",
@@ -24,10 +24,10 @@
24
24
  "xc:build:CapacitorCordova": "cd CapacitorCordova && xcodebuild && cd .."
25
25
  },
26
26
  "peerDependencies": {
27
- "@capacitor/core": "^3.4.0"
27
+ "@capacitor/core": "next"
28
28
  },
29
29
  "publishConfig": {
30
30
  "access": "public"
31
31
  },
32
- "gitHead": "5c588d5bd15b2b939c6efc25b7db9d6af29afae0"
32
+ "gitHead": "902e1f56af32143b817cf03233264203bcd5adab"
33
33
  }