@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 +103 -0
- package/Capacitor/Capacitor/AppUUID.swift +53 -0
- package/Capacitor/Capacitor/CAPBridgeViewController.swift +7 -7
- package/Capacitor/Capacitor/CAPLog.swift +1 -1
- package/Capacitor/Capacitor/CAPPlugin.h +2 -1
- package/Capacitor/Capacitor/CAPPlugin.m +10 -1
- package/Capacitor/Capacitor/CAPWebView.swift +240 -0
- package/Capacitor/Capacitor/JSTypes.swift +9 -0
- package/Capacitor/Capacitor/Router.swift +29 -0
- package/Capacitor/Capacitor/TmpViewController.swift +1 -13
- package/Capacitor/Capacitor/UIColor.swift +2 -2
- package/Capacitor/Capacitor/WebViewAssetHandler.swift +44 -20
- package/Capacitor/Capacitor/WebViewDelegationHandler.swift +10 -5
- package/package.json +3 -3
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
|
-
|
|
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
|
|
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(
|
|
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
|
-
|
|
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:
|
|
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).
|
|
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
|
-
|
|
8
|
+
init(router: Router) {
|
|
9
|
+
self.router = router
|
|
10
|
+
super.init()
|
|
11
|
+
}
|
|
8
12
|
|
|
9
13
|
func setAssetPath(_ assetPath: String) {
|
|
10
|
-
|
|
14
|
+
router.basePath = assetPath
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
|
|
14
|
-
|
|
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.
|
|
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
|
-
|
|
39
|
-
let headers = [
|
|
34
|
+
var headers = [
|
|
40
35
|
"Content-Type": mimeType,
|
|
41
36
|
"Cache-Control": "no-cache"
|
|
42
37
|
]
|
|
43
|
-
let
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
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-
|
|
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": "
|
|
27
|
+
"@capacitor/core": "next"
|
|
28
28
|
},
|
|
29
29
|
"publishConfig": {
|
|
30
30
|
"access": "public"
|
|
31
31
|
},
|
|
32
|
-
"gitHead": "
|
|
32
|
+
"gitHead": "902e1f56af32143b817cf03233264203bcd5adab"
|
|
33
33
|
}
|