@capacitor/ios 4.4.0 → 4.4.1-dev-20221114T215445.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
@@ -131,6 +131,9 @@ internal extension InstanceDescriptor {
131
131
  if let preferredMode = (config[keyPath: "ios.preferredContentMode"] as? String) {
132
132
  preferredContentMode = preferredMode
133
133
  }
134
+ if let handleNotifications = config[keyPath: "ios.handleApplicationNotifications"] as? Bool {
135
+ handleApplicationNotifications = handleNotifications
136
+ }
134
137
  }
135
138
  }
136
139
  // swiftlint:enable cyclomatic_complexity
@@ -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,18 @@ public class CapacitorCookieManager {
11
24
  return self.config?.serverURL
12
25
  }
13
26
 
27
+ private func isUrlSanitized(_ urlString: String) -> Bool {
28
+ return urlString.isEmpty || urlString == getServerUrl()?.absoluteString || urlString.hasPrefix("http://") || urlString.hasPrefix("https://")
29
+ }
30
+
14
31
  public func getServerUrl(_ call: CAPPluginCall) -> URL? {
15
32
  guard let urlString = call.getString("url") else {
16
33
  return getServerUrl()
17
34
  }
18
35
 
19
- guard let url = URL(string: urlString) else {
36
+ let validUrlString = (isUrlSanitized(urlString)) ? urlString : "http://\(urlString)"
37
+
38
+ guard let url = URL(string: validUrlString) else {
20
39
  return getServerUrl()
21
40
  }
22
41
 
@@ -31,22 +50,37 @@ public class CapacitorCookieManager {
31
50
  return value.removingPercentEncoding!
32
51
  }
33
52
 
34
- public func setCookie(_ key: String, _ value: String) {
53
+ public func setCookie(_ action: String) {
35
54
  let url = getServerUrl()!
36
55
  let jar = HTTPCookieStorage.shared
37
- let field = ["Set-Cookie": "\(key)=\(value)"]
56
+ let field = ["Set-Cookie": action]
38
57
  let cookies = HTTPCookie.cookies(withResponseHeaderFields: field, for: url)
39
- jar.setCookies(cookies, for: url, mainDocumentURL: url)
58
+ jar.setCookies(cookies, for: url, mainDocumentURL: nil)
59
+ syncCookiesToWebView()
40
60
  }
41
61
 
42
- public func setCookie(_ url: URL, _ key: String, _ value: String) {
62
+ public func setCookie(_ url: URL, _ key: String, _ value: String, _ expires: String?, _ path: String?) {
43
63
  let jar = HTTPCookieStorage.shared
44
- let field = ["Set-Cookie": "\(key)=\(value)"]
64
+ let field = ["Set-Cookie": "\(key)=\(value); expires=\(expires ?? ""); path=\(path ?? "/")"]
45
65
  let cookies = HTTPCookie.cookies(withResponseHeaderFields: field, for: url)
46
- jar.setCookies(cookies, for: url, mainDocumentURL: url)
66
+ jar.setCookies(cookies, for: url, mainDocumentURL: nil)
67
+ syncCookiesToWebView()
68
+ }
69
+
70
+ public func getCookiesAsMap(_ url: URL) -> [String: String] {
71
+ syncCookiesToWebView()
72
+ var cookiesMap: [String: String] = [:]
73
+ let jar = HTTPCookieStorage.shared
74
+ if let cookies = jar.cookies(for: url) {
75
+ for cookie in cookies {
76
+ cookiesMap[cookie.name] = cookie.value
77
+ }
78
+ }
79
+ return cookiesMap
47
80
  }
48
81
 
49
82
  public func getCookies() -> String {
83
+ syncCookiesToWebView()
50
84
  let jar = HTTPCookieStorage.shared
51
85
  guard let url = self.getServerUrl() else { return "" }
52
86
  guard let cookies = jar.cookies(for: url) else { return "" }
@@ -55,20 +89,46 @@ public class CapacitorCookieManager {
55
89
 
56
90
  public func deleteCookie(_ url: URL, _ key: String) {
57
91
  let jar = HTTPCookieStorage.shared
58
- let cookie = jar.cookies(for: url)?.first(where: { (cookie) -> Bool in
92
+ if let cookie = jar.cookies(for: url)?.first(where: { (cookie) -> Bool in
59
93
  return cookie.name == key
60
- })
61
-
62
- if cookie != nil { jar.deleteCookie(cookie!) }
94
+ }) {
95
+ jar.deleteCookie(cookie)
96
+ DispatchQueue.main.async {
97
+ WKWebsiteDataStore.default().httpCookieStore.delete(cookie)
98
+ }
99
+ }
63
100
  }
64
101
 
65
102
  public func clearCookies(_ url: URL) {
66
103
  let jar = HTTPCookieStorage.shared
67
- jar.cookies(for: url)?.forEach({ (cookie) in jar.deleteCookie(cookie) })
104
+ jar.cookies(for: url)?.forEach({ (cookie) in
105
+ jar.deleteCookie(cookie)
106
+ DispatchQueue.main.async {
107
+ WKWebsiteDataStore.default().httpCookieStore.delete(cookie)
108
+ }
109
+ })
68
110
  }
69
111
 
70
112
  public func clearAllCookies() {
71
113
  let jar = HTTPCookieStorage.shared
72
- jar.cookies?.forEach({ (cookie) in jar.deleteCookie(cookie) })
114
+ jar.cookies?.forEach({ (cookie) in
115
+ jar.deleteCookie(cookie)
116
+ DispatchQueue.main.async {
117
+ WKWebsiteDataStore.default().httpCookieStore.delete(cookie)
118
+ }
119
+ })
120
+ }
121
+
122
+ public func syncCookiesToWebView() {
123
+ if let cookies = HTTPCookieStorage.shared.cookies {
124
+ for cookie in cookies {
125
+ DispatchQueue.main.async {
126
+ WKWebsiteDataStore.default()
127
+ .httpCookieStore
128
+ .setCookie(cookie, completionHandler: nil)
129
+ }
130
+
131
+ }
132
+ }
73
133
  }
74
134
  }
@@ -8,33 +8,28 @@ public class CAPCookiesPlugin: CAPPlugin {
8
8
  cookieManager = CapacitorCookieManager(bridge?.config)
9
9
  }
10
10
 
11
+ @objc func getCookies(_ call: CAPPluginCall) {
12
+ guard let url = cookieManager!.getServerUrl(call) else { return call.reject("Invalid URL / Server URL")}
13
+ call.resolve(cookieManager!.getCookiesAsMap(url))
14
+ }
15
+
11
16
  @objc func setCookie(_ call: CAPPluginCall) {
12
17
  guard let key = call.getString("key") else { return call.reject("Must provide key") }
13
18
  guard let value = call.getString("value") else { return call.reject("Must provide value") }
14
19
 
15
- let url = cookieManager!.getServerUrl(call)
16
- if url != nil {
17
- cookieManager!.setCookie(url!, key, cookieManager!.encode(value))
18
- call.resolve()
19
- }
20
+ guard let url = cookieManager!.getServerUrl(call) 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()
20
26
  }
21
27
 
22
28
  @objc func deleteCookie(_ call: CAPPluginCall) {
23
29
  guard let key = call.getString("key") else { return call.reject("Must provide key") }
24
- let url = cookieManager!.getServerUrl(call)
25
- if url != nil {
26
- let jar = HTTPCookieStorage.shared
27
-
28
- let cookie = jar.cookies(for: url!)?.first(where: { (cookie) -> Bool in
29
- return cookie.name == key
30
- })
31
-
32
- if cookie != nil {
33
- jar.deleteCookie(cookie!)
34
- }
35
-
36
- call.resolve()
37
- }
30
+ guard let url = cookieManager!.getServerUrl(call) else { return call.reject("Invalid URL / Server URL")}
31
+ cookieManager!.deleteCookie(url, key)
32
+ call.resolve()
38
33
  }
39
34
 
40
35
  @objc func clearCookies(_ call: CAPPluginCall) {
@@ -3,6 +3,7 @@
3
3
  #import "CAPBridgedPlugin.h"
4
4
 
5
5
  CAP_PLUGIN(CAPCookiesPlugin, "CapacitorCookies",
6
+ CAP_PLUGIN_METHOD(getCookies, CAPPluginReturnPromise);
6
7
  CAP_PLUGIN_METHOD(setCookie, CAPPluginReturnPromise);
7
8
  CAP_PLUGIN_METHOD(deleteCookie, CAPPluginReturnPromise);
8
9
  CAP_PLUGIN_METHOD(clearCookies, CAPPluginReturnPromise);
@@ -161,6 +161,7 @@ class HttpRequestHandler {
161
161
  let urlSession = request.getUrlSession(call)
162
162
  let task = urlSession.dataTask(with: urlRequest) { (data, response, error) in
163
163
  urlSession.invalidateAndCancel()
164
+ CapacitorCookieManager(nil).syncCookiesToWebView()
164
165
  if error != nil {
165
166
  return
166
167
  }
@@ -256,9 +256,8 @@ 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
+ CapacitorCookieManager(bridge!.config).setCookie(action)
262
261
  completionHandler("")
263
262
  // swiftlint:enable force_cast
264
263
  // Don't present prompt
@@ -310,25 +310,25 @@ const nativeBridge = (function (exports) {
310
310
  }
311
311
  },
312
312
  set: function (val) {
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));
313
+ if (platform === 'ios') {
314
+ // Use prompt to synchronously set cookies.
315
+ // https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323
316
+ const payload = {
317
+ type: 'CapacitorCookies.set',
318
+ action: val
319
+ };
320
+ prompt(JSON.stringify(payload));
321
+ }
322
+ else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') {
323
+ const cookiePairs = val.split(';');
324
+ const domainSection = val.toLowerCase().split('domain=')[1];
325
+ // if key is not domain
326
+ if (cookiePairs.length > 1 && domainSection != null && domainSection.length > 0) {
327
+ const domain = domainSection.split(';')[0].trim();
328
+ win.CapacitorCookiesAndroidInterface.setCookies(domain, val);
329
329
  }
330
- else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') {
331
- win.CapacitorCookiesAndroidInterface.setCookie(cookieKey, cookieValue);
330
+ else {
331
+ win.CapacitorCookiesAndroidInterface.setCookie(val);
332
332
  }
333
333
  }
334
334
  },
@@ -426,7 +426,8 @@ const nativeBridge = (function (exports) {
426
426
  };
427
427
  // XHR patch abort
428
428
  window.XMLHttpRequest.prototype.abort = function () {
429
- if (this._url == null || !(this._url.startsWith('http:') || this._url.startsWith('https:'))) {
429
+ if (this._url == null ||
430
+ !(this._url.startsWith('http:') || this._url.startsWith('https:'))) {
430
431
  return win.CapacitorWebXMLHttpRequest.abort.call(this);
431
432
  }
432
433
  this.readyState = 0;
@@ -480,14 +481,16 @@ const nativeBridge = (function (exports) {
480
481
  };
481
482
  // XHR patch set request header
482
483
  window.XMLHttpRequest.prototype.setRequestHeader = function (header, value) {
483
- if (this._url == null || !(this._url.startsWith('http:') || this._url.startsWith('https:'))) {
484
+ if (this._url == null ||
485
+ !(this._url.startsWith('http:') || this._url.startsWith('https:'))) {
484
486
  return win.CapacitorWebXMLHttpRequest.setRequestHeader.call(this, header, value);
485
487
  }
486
488
  this._headers[header] = value;
487
489
  };
488
490
  // XHR patch send
489
491
  window.XMLHttpRequest.prototype.send = function (body) {
490
- if (this._url == null || !(this._url.startsWith('http:') || this._url.startsWith('https:'))) {
492
+ if (this._url == null ||
493
+ !(this._url.startsWith('http:') || this._url.startsWith('https:'))) {
491
494
  return win.CapacitorWebXMLHttpRequest.send.call(this, body);
492
495
  }
493
496
  try {
@@ -543,7 +546,8 @@ const nativeBridge = (function (exports) {
543
546
  };
544
547
  // XHR patch getAllResponseHeaders
545
548
  window.XMLHttpRequest.prototype.getAllResponseHeaders = function () {
546
- if (this._url == null || !(this._url.startsWith('http:') || this._url.startsWith('https:'))) {
549
+ if (this._url == null ||
550
+ !(this._url.startsWith('http:') || this._url.startsWith('https:'))) {
547
551
  return win.CapacitorWebXMLHttpRequest.getAllResponseHeaders.call(this);
548
552
  }
549
553
  let returnString = '';
@@ -556,7 +560,8 @@ const nativeBridge = (function (exports) {
556
560
  };
557
561
  // XHR patch getResponseHeader
558
562
  window.XMLHttpRequest.prototype.getResponseHeader = function (name) {
559
- if (this._url == null || !(this._url.startsWith('http:') || this._url.startsWith('https:'))) {
563
+ if (this._url == null ||
564
+ !(this._url.startsWith('http:') || this._url.startsWith('https:'))) {
560
565
  return win.CapacitorWebXMLHttpRequest.getResponseHeader.call(this, name);
561
566
  }
562
567
  return this._headers[name];
package/Capacitor.podspec CHANGED
@@ -1,5 +1,11 @@
1
- require "json"
2
- package = JSON.parse(File.read(File.join(__dir__, "package.json")))
1
+ require 'json'
2
+ package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
3
+ prefix = if ENV['NATIVE_PUBLISH'] == 'true'
4
+ 'ios/'
5
+ else
6
+ ''
7
+ end
8
+
3
9
  Pod::Spec.new do |s|
4
10
  s.name = 'Capacitor'
5
11
  s.version = package['version']
@@ -7,12 +13,13 @@ Pod::Spec.new do |s|
7
13
  s.social_media_url = 'https://twitter.com/capacitorjs'
8
14
  s.license = 'MIT'
9
15
  s.homepage = 'https://capacitorjs.com/'
10
- s.ios.deployment_target = '13.0'
16
+ s.ios.deployment_target = '13.0'
11
17
  s.authors = { 'Ionic Team' => 'hi@ionicframework.com' }
12
- s.source = { :git => 'https://github.com/ionic-team/capacitor.git', :branch => "portals-dev" }
13
- s.source_files = 'Capacitor/Capacitor/*.{swift,h,m}', 'Capacitor/Capacitor/Plugins/*.{swift,h,m}', 'Capacitor/Capacitor/Plugins/**/*.{swift,h,m}'
14
- s.module_map = 'Capacitor/Capacitor/Capacitor.modulemap'
15
- s.resources = ['Capacitor/Capacitor/assets/native-bridge.js']
18
+ s.source = { git: 'https://github.com/ionic-team/capacitor.git', tag: package['version'] }
19
+ s.source_files = "#{prefix}Capacitor/Capacitor/*.{swift,h,m}", "#{prefix}Capacitor/Capacitor/Plugins/*.{swift,h,m}",
20
+ "#{prefix}Capacitor/Capacitor/Plugins/**/*.{swift,h,m}"
21
+ s.module_map = "#{prefix}Capacitor/Capacitor/Capacitor.modulemap"
22
+ s.resources = ["#{prefix}Capacitor/Capacitor/assets/native-bridge.js"]
16
23
  s.dependency 'CapacitorCordova'
17
24
  s.swift_version = '5.1'
18
25
  end
@@ -1,18 +1,25 @@
1
- require "json"
2
- package = JSON.parse(File.read(File.join(__dir__, "package.json")))
1
+ require 'json'
2
+ package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
3
+ prefix = if ENV['NATIVE_PUBLISH'] == 'true'
4
+ 'ios/'
5
+ else
6
+ ''
7
+ end
8
+
3
9
  Pod::Spec.new do |s|
4
- s.name = "CapacitorCordova"
10
+ s.name = 'CapacitorCordova'
5
11
  s.module_name = 'Cordova'
6
12
  s.version = package['version']
7
- s.summary = "Capacitor Cordova Compatibility Layer"
8
- s.homepage = "https://capacitorjs.com"
13
+ s.summary = 'Capacitor Cordova Compatibility Layer'
14
+ s.homepage = 'https://capacitorjs.com'
9
15
  s.license = 'MIT'
10
16
  s.authors = { 'Ionic Team' => 'hi@ionicframework.com' }
11
- s.source = { :git => 'https://github.com/ionic-team/capacitor', :tag => s.version.to_s }
17
+ s.source = { git: 'https://github.com/ionic-team/capacitor', tag: s.version.to_s }
12
18
  s.platform = :ios, 13.0
13
- s.source_files = 'CapacitorCordova/CapacitorCordova/**/*.{h,m}'
14
- s.public_header_files = 'CapacitorCordova/CapacitorCordova/Classes/Public/*.h', 'CapacitorCordova/CapacitorCordova/CapacitorCordova.h'
15
- s.module_map = 'CapacitorCordova/CapacitorCordova/CapacitorCordova.modulemap'
19
+ s.source_files = "#{prefix}CapacitorCordova/CapacitorCordova/**/*.{h,m}"
20
+ s.public_header_files = "#{prefix}CapacitorCordova/CapacitorCordova/Classes/Public/*.h",
21
+ "#{prefix}CapacitorCordova/CapacitorCordova/CapacitorCordova.h"
22
+ s.module_map = "#{prefix}CapacitorCordova/CapacitorCordova/CapacitorCordova.modulemap"
16
23
  s.requires_arc = true
17
- s.framework = "WebKit"
24
+ s.framework = 'WebKit'
18
25
  end
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capacitor/ios",
3
- "version": "4.4.0",
3
+ "version": "4.4.1-dev-20221114T215445.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.3.0"
28
+ "@capacitor/core": "^4.4.0"
29
29
  },
30
30
  "publishConfig": {
31
31
  "access": "public"
32
32
  },
33
- "gitHead": "f9ec5a3059e109dbf2bd656f895243487eebeba5"
33
+ "gitHead": "ecbf81ebb1b01de0b544c3f171fe3b042253088d"
34
34
  }