@capacitor/ios 4.5.0 → 4.5.1-dev-20221117T024619.0

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.
@@ -105,6 +105,7 @@ import Cordova
105
105
  */
106
106
  open func webViewConfiguration(for instanceConfiguration: InstanceConfiguration) -> WKWebViewConfiguration {
107
107
  let webViewConfiguration = WKWebViewConfiguration()
108
+ webViewConfiguration.websiteDataStore.httpCookieStore.add(CapacitorWKCookieObserver())
108
109
  webViewConfiguration.allowsInlineMediaPlayback = true
109
110
  webViewConfiguration.suppressesIncrementalRendering = false
110
111
  webViewConfiguration.allowsAirPlayForMediaPlayback = true
@@ -125,6 +125,7 @@ extension CAPWebView {
125
125
 
126
126
  public func webViewConfiguration(for instanceConfiguration: InstanceConfiguration) -> WKWebViewConfiguration {
127
127
  let webViewConfiguration = WKWebViewConfiguration()
128
+ webViewConfiguration.websiteDataStore.httpCookieStore.add(CapacitorWKCookieObserver())
128
129
  webViewConfiguration.allowsInlineMediaPlayback = true
129
130
  webViewConfiguration.suppressesIncrementalRendering = false
130
131
  webViewConfiguration.allowsAirPlayForMediaPlayback = true
@@ -1,5 +1,18 @@
1
1
  import Foundation
2
2
 
3
+ public class CapacitorWKCookieObserver: NSObject, WKHTTPCookieStoreObserver {
4
+ // Sync WKWebView Cookies to HTTPCookieStorage
5
+ public func cookiesDidChange(in cookieStore: WKHTTPCookieStore) {
6
+ DispatchQueue.main.async {
7
+ cookieStore.getAllCookies { cookies in
8
+ cookies.forEach { cookie in
9
+ HTTPCookieStorage.shared.setCookie(cookie)
10
+ }
11
+ }
12
+ }
13
+ }
14
+ }
15
+
3
16
  public class CapacitorCookieManager {
4
17
  var config: InstanceConfiguration?
5
18
 
@@ -11,12 +24,22 @@ public class CapacitorCookieManager {
11
24
  return self.config?.serverURL
12
25
  }
13
26
 
14
- public func getServerUrl(_ call: CAPPluginCall) -> URL? {
15
- guard let urlString = call.getString("url") else {
27
+ private func isUrlSanitized(_ urlString: String) -> Bool {
28
+ return urlString.isEmpty || urlString == getServerUrl()?.absoluteString || urlString.hasPrefix("http://") || urlString.hasPrefix("https://")
29
+ }
30
+
31
+ public func getServerUrl(_ urlString: String?) -> URL? {
32
+ guard let urlString = urlString else {
16
33
  return getServerUrl()
17
34
  }
18
35
 
19
- guard let url = URL(string: urlString) else {
36
+ if urlString.isEmpty {
37
+ return getServerUrl()
38
+ }
39
+
40
+ let validUrlString = (isUrlSanitized(urlString)) ? urlString : "http://\(urlString)"
41
+
42
+ guard let url = URL(string: validUrlString) else {
20
43
  return getServerUrl()
21
44
  }
22
45
 
@@ -31,22 +54,25 @@ public class CapacitorCookieManager {
31
54
  return value.removingPercentEncoding!
32
55
  }
33
56
 
34
- public func setCookie(_ key: String, _ value: String) {
35
- let url = getServerUrl()!
57
+ public func setCookie(_ domain: String, _ action: String) {
58
+ let url = getServerUrl(domain)!
36
59
  let jar = HTTPCookieStorage.shared
37
- let field = ["Set-Cookie": "\(key)=\(value)"]
60
+ let field = ["Set-Cookie": action]
38
61
  let cookies = HTTPCookie.cookies(withResponseHeaderFields: field, for: url)
39
- jar.setCookies(cookies, for: url, mainDocumentURL: url)
62
+ jar.setCookies(cookies, for: url, mainDocumentURL: nil)
63
+ syncCookiesToWebView()
40
64
  }
41
65
 
42
- public func setCookie(_ url: URL, _ key: String, _ value: String) {
66
+ public func setCookie(_ url: URL, _ key: String, _ value: String, _ expires: String?, _ path: String?) {
43
67
  let jar = HTTPCookieStorage.shared
44
- let field = ["Set-Cookie": "\(key)=\(value)"]
68
+ let field = ["Set-Cookie": "\(key)=\(value); expires=\(expires ?? ""); path=\(path ?? "/")"]
45
69
  let cookies = HTTPCookie.cookies(withResponseHeaderFields: field, for: url)
46
- jar.setCookies(cookies, for: url, mainDocumentURL: url)
70
+ jar.setCookies(cookies, for: url, mainDocumentURL: nil)
71
+ syncCookiesToWebView()
47
72
  }
48
73
 
49
74
  public func getCookiesAsMap(_ url: URL) -> [String: String] {
75
+ syncCookiesToWebView()
50
76
  var cookiesMap: [String: String] = [:]
51
77
  let jar = HTTPCookieStorage.shared
52
78
  if let cookies = jar.cookies(for: url) {
@@ -58,6 +84,7 @@ public class CapacitorCookieManager {
58
84
  }
59
85
 
60
86
  public func getCookies() -> String {
87
+ syncCookiesToWebView()
61
88
  let jar = HTTPCookieStorage.shared
62
89
  guard let url = self.getServerUrl() else { return "" }
63
90
  guard let cookies = jar.cookies(for: url) else { return "" }
@@ -66,20 +93,46 @@ public class CapacitorCookieManager {
66
93
 
67
94
  public func deleteCookie(_ url: URL, _ key: String) {
68
95
  let jar = HTTPCookieStorage.shared
69
- let cookie = jar.cookies(for: url)?.first(where: { (cookie) -> Bool in
96
+ if let cookie = jar.cookies(for: url)?.first(where: { (cookie) -> Bool in
70
97
  return cookie.name == key
71
- })
72
-
73
- if cookie != nil { jar.deleteCookie(cookie!) }
98
+ }) {
99
+ jar.deleteCookie(cookie)
100
+ DispatchQueue.main.async {
101
+ WKWebsiteDataStore.default().httpCookieStore.delete(cookie)
102
+ }
103
+ }
74
104
  }
75
105
 
76
106
  public func clearCookies(_ url: URL) {
77
107
  let jar = HTTPCookieStorage.shared
78
- jar.cookies(for: url)?.forEach({ (cookie) in jar.deleteCookie(cookie) })
108
+ jar.cookies(for: url)?.forEach({ (cookie) in
109
+ jar.deleteCookie(cookie)
110
+ DispatchQueue.main.async {
111
+ WKWebsiteDataStore.default().httpCookieStore.delete(cookie)
112
+ }
113
+ })
79
114
  }
80
115
 
81
116
  public func clearAllCookies() {
82
117
  let jar = HTTPCookieStorage.shared
83
- jar.cookies?.forEach({ (cookie) in jar.deleteCookie(cookie) })
118
+ jar.cookies?.forEach({ (cookie) in
119
+ jar.deleteCookie(cookie)
120
+ DispatchQueue.main.async {
121
+ WKWebsiteDataStore.default().httpCookieStore.delete(cookie)
122
+ }
123
+ })
124
+ }
125
+
126
+ public func syncCookiesToWebView() {
127
+ if let cookies = HTTPCookieStorage.shared.cookies {
128
+ for cookie in cookies {
129
+ DispatchQueue.main.async {
130
+ WKWebsiteDataStore.default()
131
+ .httpCookieStore
132
+ .setCookie(cookie, completionHandler: nil)
133
+ }
134
+
135
+ }
136
+ }
84
137
  }
85
138
  }
@@ -9,7 +9,7 @@ public class CAPCookiesPlugin: CAPPlugin {
9
9
  }
10
10
 
11
11
  @objc func getCookies(_ call: CAPPluginCall) {
12
- guard let url = cookieManager!.getServerUrl(call) else { return call.reject("Invalid URL / Server URL")}
12
+ guard let url = cookieManager!.getServerUrl(call.getString("url")) else { return call.reject("Invalid URL / Server URL")}
13
13
  call.resolve(cookieManager!.getCookiesAsMap(url))
14
14
  }
15
15
 
@@ -17,22 +17,23 @@ public class CAPCookiesPlugin: CAPPlugin {
17
17
  guard let key = call.getString("key") else { return call.reject("Must provide key") }
18
18
  guard let value = call.getString("value") else { return call.reject("Must provide value") }
19
19
 
20
- let url = cookieManager!.getServerUrl(call)
21
- if url != nil {
22
- cookieManager!.setCookie(url!, key, cookieManager!.encode(value))
23
- call.resolve()
24
- }
20
+ guard let url = cookieManager!.getServerUrl(call.getString("url")) else { return call.reject("Invalid domain") }
21
+
22
+ let expires = call.getString("expires", "")
23
+ let path = call.getString("path", "")
24
+ cookieManager!.setCookie(url, key, cookieManager!.encode(value), expires, path)
25
+ call.resolve()
25
26
  }
26
27
 
27
28
  @objc func deleteCookie(_ call: CAPPluginCall) {
28
29
  guard let key = call.getString("key") else { return call.reject("Must provide key") }
29
- guard let url = cookieManager!.getServerUrl(call) else { return call.reject("Invalid URL / Server URL")}
30
+ guard let url = cookieManager!.getServerUrl(call.getString("url")) else { return call.reject("Invalid URL / Server URL")}
30
31
  cookieManager!.deleteCookie(url, key)
31
32
  call.resolve()
32
33
  }
33
34
 
34
35
  @objc func clearCookies(_ call: CAPPluginCall) {
35
- let url = cookieManager!.getServerUrl(call)
36
+ let url = cookieManager!.getServerUrl(call.getString("url"))
36
37
  if url != nil {
37
38
  cookieManager!.clearCookies(url!)
38
39
  call.resolve()
@@ -8,7 +8,7 @@ public class CAPHttpPlugin: CAPPlugin {
8
8
  guard var _ = URL(string: url) else { return call.reject("Invalid URL"); }
9
9
 
10
10
  do {
11
- try HttpRequestHandler.request(call, httpMethod)
11
+ try HttpRequestHandler.request(call, httpMethod, self.bridge?.config)
12
12
  } catch let error {
13
13
  call.reject(error.localizedDescription)
14
14
  }
@@ -97,6 +97,24 @@ class HttpRequestHandler {
97
97
  }
98
98
  }
99
99
 
100
+ private static func setCookiesFromResponse(_ response: HTTPURLResponse, _ config: InstanceConfiguration?) {
101
+ let headers = response.allHeaderFields
102
+ if let cookies = headers["Set-Cookie"] as? String {
103
+ for cookie in cookies.components(separatedBy: ",") {
104
+ let domainComponents = cookie.lowercased().components(separatedBy: "domain=")
105
+ if domainComponents.count > 1 {
106
+ CapacitorCookieManager(config).setCookie(
107
+ domainComponents[1].components(separatedBy: ";")[0],
108
+ cookie
109
+ )
110
+ } else {
111
+ CapacitorCookieManager(config).setCookie("", cookie)
112
+ }
113
+ }
114
+ }
115
+ CapacitorCookieManager(config).syncCookiesToWebView()
116
+ }
117
+
100
118
  private static func buildResponse(_ data: Data?, _ response: HTTPURLResponse, responseType: ResponseType = .default) -> [String: Any] {
101
119
  var output = [:] as [String: Any]
102
120
 
@@ -122,7 +140,7 @@ class HttpRequestHandler {
122
140
  return output
123
141
  }
124
142
 
125
- public static func request(_ call: CAPPluginCall, _ httpMethod: String?) throws {
143
+ public static func request(_ call: CAPPluginCall, _ httpMethod: String?, _ config: InstanceConfiguration?) throws {
126
144
  guard let urlString = call.getString("url") else { throw URLError(.badURL) }
127
145
  let method = httpMethod ?? call.getString("method", "GET")
128
146
 
@@ -161,10 +179,13 @@ class HttpRequestHandler {
161
179
  let urlSession = request.getUrlSession(call)
162
180
  let task = urlSession.dataTask(with: urlRequest) { (data, response, error) in
163
181
  urlSession.invalidateAndCancel()
182
+
164
183
  if error != nil {
165
184
  return
166
185
  }
167
186
 
187
+ setCookiesFromResponse(response as! HTTPURLResponse, config)
188
+
168
189
  let type = ResponseType(rawValue: responseType) ?? .default
169
190
  call.resolve(self.buildResponse(data, response as! HTTPURLResponse, responseType: type))
170
191
  }
@@ -256,9 +256,9 @@ internal class WebViewDelegationHandler: NSObject, WKNavigationDelegate, WKUIDel
256
256
  return
257
257
  } else if type == "CapacitorCookies.set" {
258
258
  // swiftlint:disable force_cast
259
- let key = payload["key"] as! String
260
- let value = payload["value"] as! String
261
- CapacitorCookieManager(bridge!.config).setCookie(key, value)
259
+ let action = payload["action"] as! String
260
+ let domain = payload["domain"] as! String
261
+ CapacitorCookieManager(bridge!.config).setCookie(domain, action)
262
262
  completionHandler("")
263
263
  // swiftlint:enable force_cast
264
264
  // Don't present prompt
@@ -311,25 +311,24 @@ const nativeBridge = (function (exports) {
311
311
  },
312
312
  set: function (val) {
313
313
  const cookiePairs = val.split(';');
314
- for (const cookiePair of cookiePairs) {
315
- const cookieKey = cookiePair.split('=')[0];
316
- const cookieValue = cookiePair.split('=')[1];
317
- if (null == cookieValue) {
318
- continue;
319
- }
320
- if (platform === 'ios') {
321
- // Use prompt to synchronously set cookies.
322
- // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323
323
- const payload = {
324
- type: 'CapacitorCookies.set',
325
- key: cookieKey,
326
- value: cookieValue,
327
- };
328
- prompt(JSON.stringify(payload));
329
- }
330
- else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') {
331
- win.CapacitorCookiesAndroidInterface.setCookie(cookieKey, cookieValue);
332
- }
314
+ const domainSection = val.toLowerCase().split('domain=')[1];
315
+ const domain = cookiePairs.length > 1 &&
316
+ domainSection != null &&
317
+ domainSection.length > 0
318
+ ? domainSection.split(';')[0].trim()
319
+ : '';
320
+ if (platform === 'ios') {
321
+ // Use prompt to synchronously set cookies.
322
+ // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323
323
+ const payload = {
324
+ type: 'CapacitorCookies.set',
325
+ action: val,
326
+ domain,
327
+ };
328
+ prompt(JSON.stringify(payload));
329
+ }
330
+ else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') {
331
+ win.CapacitorCookiesAndroidInterface.setCookie(domain, val);
333
332
  }
334
333
  },
335
334
  });
@@ -426,7 +425,8 @@ const nativeBridge = (function (exports) {
426
425
  };
427
426
  // XHR patch abort
428
427
  window.XMLHttpRequest.prototype.abort = function () {
429
- if (this._url == null || !(this._url.startsWith('http:') || this._url.startsWith('https:'))) {
428
+ if (this._url == null ||
429
+ !(this._url.startsWith('http:') || this._url.startsWith('https:'))) {
430
430
  return win.CapacitorWebXMLHttpRequest.abort.call(this);
431
431
  }
432
432
  this.readyState = 0;
@@ -480,14 +480,16 @@ const nativeBridge = (function (exports) {
480
480
  };
481
481
  // XHR patch set request header
482
482
  window.XMLHttpRequest.prototype.setRequestHeader = function (header, value) {
483
- if (this._url == null || !(this._url.startsWith('http:') || this._url.startsWith('https:'))) {
483
+ if (this._url == null ||
484
+ !(this._url.startsWith('http:') || this._url.startsWith('https:'))) {
484
485
  return win.CapacitorWebXMLHttpRequest.setRequestHeader.call(this, header, value);
485
486
  }
486
487
  this._headers[header] = value;
487
488
  };
488
489
  // XHR patch send
489
490
  window.XMLHttpRequest.prototype.send = function (body) {
490
- if (this._url == null || !(this._url.startsWith('http:') || this._url.startsWith('https:'))) {
491
+ if (this._url == null ||
492
+ !(this._url.startsWith('http:') || this._url.startsWith('https:'))) {
491
493
  return win.CapacitorWebXMLHttpRequest.send.call(this, body);
492
494
  }
493
495
  try {
@@ -543,7 +545,8 @@ const nativeBridge = (function (exports) {
543
545
  };
544
546
  // XHR patch getAllResponseHeaders
545
547
  window.XMLHttpRequest.prototype.getAllResponseHeaders = function () {
546
- if (this._url == null || !(this._url.startsWith('http:') || this._url.startsWith('https:'))) {
548
+ if (this._url == null ||
549
+ !(this._url.startsWith('http:') || this._url.startsWith('https:'))) {
547
550
  return win.CapacitorWebXMLHttpRequest.getAllResponseHeaders.call(this);
548
551
  }
549
552
  let returnString = '';
@@ -556,7 +559,8 @@ const nativeBridge = (function (exports) {
556
559
  };
557
560
  // XHR patch getResponseHeader
558
561
  window.XMLHttpRequest.prototype.getResponseHeader = function (name) {
559
- if (this._url == null || !(this._url.startsWith('http:') || this._url.startsWith('https:'))) {
562
+ if (this._url == null ||
563
+ !(this._url.startsWith('http:') || this._url.startsWith('https:'))) {
560
564
  return win.CapacitorWebXMLHttpRequest.getResponseHeader.call(this, name);
561
565
  }
562
566
  return this._headers[name];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capacitor/ios",
3
- "version": "4.5.0",
3
+ "version": "4.5.1-dev-20221117T024619.0",
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)",
@@ -25,10 +25,10 @@
25
25
  "xc:build:CapacitorCordova": "cd CapacitorCordova && xcodebuild && cd .."
26
26
  },
27
27
  "peerDependencies": {
28
- "@capacitor/core": "^4.4.0"
28
+ "@capacitor/core": "^4.5.0"
29
29
  },
30
30
  "publishConfig": {
31
31
  "access": "public"
32
32
  },
33
- "gitHead": "c54a71f5a01e5228c648a1e9194849198a39cee6"
33
+ "gitHead": "6c509ade89ed4e09169bfd3d3a79fac2b9fe0c4d"
34
34
  }