@capacitor/ios 4.2.1-nightly-20220921T150520.0 → 4.3.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.
- package/CHANGELOG.md +16 -0
- package/Capacitor/Capacitor/Plugins/CapacitorCookieManager.swift +66 -0
- package/Capacitor/Capacitor/Plugins/CapacitorCookies.swift +52 -0
- package/Capacitor/Capacitor/Plugins/CapacitorHttp.swift +40 -0
- package/Capacitor/Capacitor/Plugins/CapacitorUrlRequest.swift +150 -0
- package/Capacitor/Capacitor/Plugins/DefaultPlugins.m +16 -0
- package/Capacitor/Capacitor/Plugins/HttpRequestHandler.swift +174 -0
- package/Capacitor/Capacitor/WebViewDelegationHandler.swift +28 -0
- package/Capacitor/Capacitor/assets/native-bridge.js +269 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,22 @@
|
|
|
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.3.0](https://github.com/ionic-team/capacitor/compare/4.2.0...4.3.0) (2022-09-21)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **core:** Exception object was not set on Cap ([#5917](https://github.com/ionic-team/capacitor/issues/5917)) ([9ca27a4](https://github.com/ionic-team/capacitor/commit/9ca27a4f8441b368f8bf9d97dda57b1a55ac0e4e))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
* Capacitor Cookies & Capacitor Http core plugins ([d4047cf](https://github.com/ionic-team/capacitor/commit/d4047cfa947676777f400389a8d65defae140b45))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
6
22
|
# [4.2.0](https://github.com/ionic-team/capacitor/compare/4.1.0...4.2.0) (2022-09-08)
|
|
7
23
|
|
|
8
24
|
**Note:** Version bump only for package @capacitor/ios
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
public class CapacitorCookieManager {
|
|
4
|
+
var config: InstanceConfiguration?
|
|
5
|
+
|
|
6
|
+
init(_ capConfig: InstanceConfiguration?) {
|
|
7
|
+
self.config = capConfig
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
public func getServerUrl() -> URL? {
|
|
11
|
+
return self.config?.serverURL
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public func getServerUrl(_ call: CAPPluginCall) -> URL? {
|
|
15
|
+
guard let urlString = call.getString("url") else {
|
|
16
|
+
return getServerUrl()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
guard let url = URL(string: urlString) else {
|
|
20
|
+
return getServerUrl()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return url
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public func encode(_ value: String) -> String {
|
|
27
|
+
return value.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public func decode(_ value: String) -> String {
|
|
31
|
+
return value.removingPercentEncoding!
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public func setCookie(_ url: URL, _ key: String, _ value: String) {
|
|
35
|
+
let jar = HTTPCookieStorage.shared
|
|
36
|
+
let field = ["Set-Cookie": "\(key)=\(value)"]
|
|
37
|
+
let cookies = HTTPCookie.cookies(withResponseHeaderFields: field, for: url)
|
|
38
|
+
jar.setCookies(cookies, for: url, mainDocumentURL: url)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public func getCookies() -> String {
|
|
42
|
+
let jar = HTTPCookieStorage.shared
|
|
43
|
+
guard let url = self.getServerUrl() else { return "" }
|
|
44
|
+
guard let cookies = jar.cookies(for: url) else { return "" }
|
|
45
|
+
return cookies.map({"\($0.name)=\($0.value)"}).joined(separator: ";")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public func deleteCookie(_ url: URL, _ key: String) {
|
|
49
|
+
let jar = HTTPCookieStorage.shared
|
|
50
|
+
let cookie = jar.cookies(for: url)?.first(where: { (cookie) -> Bool in
|
|
51
|
+
return cookie.name == key
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
if cookie != nil { jar.deleteCookie(cookie!) }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public func clearCookies(_ url: URL) {
|
|
58
|
+
let jar = HTTPCookieStorage.shared
|
|
59
|
+
jar.cookies(for: url)?.forEach({ (cookie) in jar.deleteCookie(cookie) })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public func clearAllCookies() {
|
|
63
|
+
let jar = HTTPCookieStorage.shared
|
|
64
|
+
jar.cookies?.forEach({ (cookie) in jar.deleteCookie(cookie) })
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
@objc(CAPCookiesPlugin)
|
|
4
|
+
public class CAPCookiesPlugin: CAPPlugin {
|
|
5
|
+
var cookieManager: CapacitorCookieManager?
|
|
6
|
+
|
|
7
|
+
@objc override public func load() {
|
|
8
|
+
cookieManager = CapacitorCookieManager(bridge?.config)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
@objc func setCookie(_ call: CAPPluginCall) {
|
|
12
|
+
guard let key = call.getString("key") else { return call.reject("Must provide key") }
|
|
13
|
+
guard let value = call.getString("value") else { return call.reject("Must provide value") }
|
|
14
|
+
|
|
15
|
+
let url = cookieManager!.getServerUrl(call)
|
|
16
|
+
if url != nil {
|
|
17
|
+
cookieManager!.setCookie(url!, key, cookieManager!.encode(value))
|
|
18
|
+
call.resolve()
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@objc func deleteCookie(_ call: CAPPluginCall) {
|
|
23
|
+
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
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@objc func clearCookies(_ call: CAPPluginCall) {
|
|
41
|
+
let url = cookieManager!.getServerUrl(call)
|
|
42
|
+
if url != nil {
|
|
43
|
+
cookieManager!.clearCookies(url!)
|
|
44
|
+
call.resolve()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@objc func clearAllCookies(_ call: CAPPluginCall) {
|
|
49
|
+
cookieManager!.clearAllCookies()
|
|
50
|
+
call.resolve()
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
@objc(CAPHttpPlugin)
|
|
4
|
+
public class CAPHttpPlugin: CAPPlugin {
|
|
5
|
+
@objc func http(_ call: CAPPluginCall, _ httpMethod: String?) {
|
|
6
|
+
// Protect against bad values from JS before calling request
|
|
7
|
+
guard let url = call.getString("url") else { return call.reject("Must provide a URL"); }
|
|
8
|
+
guard var _ = URL(string: url) else { return call.reject("Invalid URL"); }
|
|
9
|
+
|
|
10
|
+
do {
|
|
11
|
+
try HttpRequestHandler.request(call, httpMethod)
|
|
12
|
+
} catch let error {
|
|
13
|
+
call.reject(error.localizedDescription)
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@objc func request(_ call: CAPPluginCall) {
|
|
18
|
+
http(call, nil)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@objc func get(_ call: CAPPluginCall) {
|
|
22
|
+
http(call, "GET")
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@objc func post(_ call: CAPPluginCall) {
|
|
26
|
+
http(call, "POST")
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@objc func put(_ call: CAPPluginCall) {
|
|
30
|
+
http(call, "PUT")
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@objc func patch(_ call: CAPPluginCall) {
|
|
34
|
+
http(call, "PATCH")
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@objc func del(_ call: CAPPluginCall) {
|
|
38
|
+
http(call, "DELETE")
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
public class CapacitorUrlRequest: NSObject, URLSessionTaskDelegate {
|
|
4
|
+
private var request: URLRequest
|
|
5
|
+
private var headers: [String: String]
|
|
6
|
+
|
|
7
|
+
enum CapacitorUrlRequestError: Error {
|
|
8
|
+
case serializationError(String?)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
init(_ url: URL, method: String) {
|
|
12
|
+
request = URLRequest(url: url)
|
|
13
|
+
request.httpMethod = method
|
|
14
|
+
headers = [:]
|
|
15
|
+
if let lang = Locale.autoupdatingCurrent.languageCode {
|
|
16
|
+
if let country = Locale.autoupdatingCurrent.regionCode {
|
|
17
|
+
headers["Accept-Language"] = "\(lang)-\(country),\(lang);q=0.5"
|
|
18
|
+
} else {
|
|
19
|
+
headers["Accept-Language"] = "\(lang);q=0.5"
|
|
20
|
+
}
|
|
21
|
+
request.addValue(headers["Accept-Language"]!, forHTTPHeaderField: "Accept-Language")
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private func getRequestDataAsJson(_ data: JSValue) throws -> Data? {
|
|
26
|
+
// We need to check if the JSON is valid before attempting to serialize, as JSONSerialization.data will not throw an exception that can be caught, and will cause the application to crash if it fails.
|
|
27
|
+
if JSONSerialization.isValidJSONObject(data) {
|
|
28
|
+
return try JSONSerialization.data(withJSONObject: data)
|
|
29
|
+
} else {
|
|
30
|
+
throw CapacitorUrlRequest.CapacitorUrlRequestError.serializationError("[ data ] argument for request of content-type [ application/json ] must be serializable to JSON")
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
private func getRequestDataAsFormUrlEncoded(_ data: JSValue) throws -> Data? {
|
|
35
|
+
guard var components = URLComponents(url: request.url!, resolvingAgainstBaseURL: false) else { return nil }
|
|
36
|
+
components.queryItems = []
|
|
37
|
+
|
|
38
|
+
guard let obj = data as? JSObject else {
|
|
39
|
+
// Throw, other data types explicitly not supported
|
|
40
|
+
throw CapacitorUrlRequestError.serializationError("[ data ] argument for request with content-type [ multipart/form-data ] may only be a plain javascript object")
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
obj.keys.forEach { (key: String) in
|
|
44
|
+
components.queryItems?.append(URLQueryItem(name: key, value: "\(obj[key] ?? "")"))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if components.query != nil {
|
|
48
|
+
return Data(components.query!.utf8)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return nil
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private func getRequestDataAsMultipartFormData(_ data: JSValue) throws -> Data {
|
|
55
|
+
guard let obj = data as? JSObject else {
|
|
56
|
+
// Throw, other data types explicitly not supported.
|
|
57
|
+
throw CapacitorUrlRequestError.serializationError("[ data ] argument for request with content-type [ application/x-www-form-urlencoded ] may only be a plain javascript object")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let strings: [String: String] = obj.compactMapValues { any in
|
|
61
|
+
any as? String
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
var data = Data()
|
|
65
|
+
let boundary = UUID().uuidString
|
|
66
|
+
let contentType = "multipart/form-data; boundary=\(boundary)"
|
|
67
|
+
request.setValue(contentType, forHTTPHeaderField: "Content-Type")
|
|
68
|
+
headers["Content-Type"] = contentType
|
|
69
|
+
|
|
70
|
+
strings.forEach { key, value in
|
|
71
|
+
data.append("\r\n--\(boundary)\r\n".data(using: .utf8)!)
|
|
72
|
+
data.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data(using: .utf8)!)
|
|
73
|
+
data.append(value.data(using: .utf8)!)
|
|
74
|
+
}
|
|
75
|
+
data.append("\r\n--\(boundary)--\r\n".data(using: .utf8)!)
|
|
76
|
+
|
|
77
|
+
return data
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private func getRequestDataAsString(_ data: JSValue) throws -> Data {
|
|
81
|
+
guard let stringData = data as? String else {
|
|
82
|
+
throw CapacitorUrlRequestError.serializationError("[ data ] argument could not be parsed as string")
|
|
83
|
+
}
|
|
84
|
+
return Data(stringData.utf8)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
func getRequestHeader(_ index: String) -> Any? {
|
|
88
|
+
var normalized = [:] as [String: Any]
|
|
89
|
+
self.headers.keys.forEach { (key: String) in
|
|
90
|
+
normalized[key.lowercased()] = self.headers[key]
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return normalized[index.lowercased()]
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
func getRequestData(_ body: JSValue, _ contentType: String) throws -> Data? {
|
|
97
|
+
// If data can be parsed directly as a string, return that without processing.
|
|
98
|
+
if let strVal = try? getRequestDataAsString(body) {
|
|
99
|
+
return strVal
|
|
100
|
+
} else if contentType.contains("application/json") {
|
|
101
|
+
return try getRequestDataAsJson(body)
|
|
102
|
+
} else if contentType.contains("application/x-www-form-urlencoded") {
|
|
103
|
+
return try getRequestDataAsFormUrlEncoded(body)
|
|
104
|
+
} else if contentType.contains("multipart/form-data") {
|
|
105
|
+
return try getRequestDataAsMultipartFormData(body)
|
|
106
|
+
} else {
|
|
107
|
+
throw CapacitorUrlRequestError.serializationError("[ data ] argument could not be parsed for content type [ \(contentType) ]")
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
public func setRequestHeaders(_ headers: [String: String]) {
|
|
112
|
+
headers.keys.forEach { (key: String) in
|
|
113
|
+
let value = headers[key]
|
|
114
|
+
request.addValue(value!, forHTTPHeaderField: key)
|
|
115
|
+
self.headers[key] = value
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public func setRequestBody(_ body: JSValue) throws {
|
|
120
|
+
let contentType = self.getRequestHeader("Content-Type") as? String
|
|
121
|
+
|
|
122
|
+
if contentType != nil {
|
|
123
|
+
request.httpBody = try getRequestData(body, contentType!)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
public func setContentType(_ data: String?) {
|
|
128
|
+
request.setValue(data, forHTTPHeaderField: "Content-Type")
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
public func setTimeout(_ timeout: TimeInterval) {
|
|
132
|
+
request.timeoutInterval = timeout
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
public func getUrlRequest() -> URLRequest {
|
|
136
|
+
return request
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
public func urlSession(_ session: URLSession, task: URLSessionTask, willPerformHTTPRedirection response: HTTPURLResponse, newRequest request: URLRequest, completionHandler: @escaping (URLRequest?) -> Void) {
|
|
140
|
+
completionHandler(nil)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
public func getUrlSession(_ call: CAPPluginCall) -> URLSession {
|
|
144
|
+
let disableRedirects = call.getBool("disableRedirects") ?? false
|
|
145
|
+
if !disableRedirects {
|
|
146
|
+
return URLSession.shared
|
|
147
|
+
}
|
|
148
|
+
return URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
#import "CAPBridgedPlugin.h"
|
|
4
4
|
|
|
5
|
+
CAP_PLUGIN(CAPCookiesPlugin, "CapacitorCookies",
|
|
6
|
+
CAP_PLUGIN_METHOD(setCookie, CAPPluginReturnPromise);
|
|
7
|
+
CAP_PLUGIN_METHOD(deleteCookie, CAPPluginReturnPromise);
|
|
8
|
+
CAP_PLUGIN_METHOD(clearCookies, CAPPluginReturnPromise);
|
|
9
|
+
CAP_PLUGIN_METHOD(clearAllCookies, CAPPluginReturnPromise);
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
CAP_PLUGIN(CAPHttpPlugin, "CapacitorHttp",
|
|
13
|
+
CAP_PLUGIN_METHOD(request, CAPPluginReturnPromise);
|
|
14
|
+
CAP_PLUGIN_METHOD(get, CAPPluginReturnPromise);
|
|
15
|
+
CAP_PLUGIN_METHOD(post, CAPPluginReturnPromise);
|
|
16
|
+
CAP_PLUGIN_METHOD(put, CAPPluginReturnPromise);
|
|
17
|
+
CAP_PLUGIN_METHOD(patch, CAPPluginReturnPromise);
|
|
18
|
+
CAP_PLUGIN_METHOD(delete, CAPPluginReturnPromise);
|
|
19
|
+
)
|
|
20
|
+
|
|
5
21
|
CAP_PLUGIN(CAPConsolePlugin, "Console",
|
|
6
22
|
CAP_PLUGIN_METHOD(log, CAPPluginReturnNone);
|
|
7
23
|
)
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// See https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/responseType
|
|
4
|
+
private enum ResponseType: String {
|
|
5
|
+
case arrayBuffer = "arraybuffer"
|
|
6
|
+
case blob = "blob"
|
|
7
|
+
case document = "document"
|
|
8
|
+
case json = "json"
|
|
9
|
+
case text = "text"
|
|
10
|
+
|
|
11
|
+
static let `default`: ResponseType = .text
|
|
12
|
+
|
|
13
|
+
init(string: String?) {
|
|
14
|
+
guard let string = string else {
|
|
15
|
+
self = .default
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
guard let responseType = ResponseType(rawValue: string.lowercased()) else {
|
|
20
|
+
self = .default
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
self = responseType
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/// Helper that safely parses JSON Data. Otherwise returns an error (without throwing)
|
|
29
|
+
/// - Parameters:
|
|
30
|
+
/// - data: The JSON Data to parse
|
|
31
|
+
/// - Returns: The parsed value or an error
|
|
32
|
+
func tryParseJson(_ data: Data) -> Any {
|
|
33
|
+
do {
|
|
34
|
+
return try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
|
|
35
|
+
} catch {
|
|
36
|
+
return error.localizedDescription
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
class HttpRequestHandler {
|
|
41
|
+
private class CapacitorHttpRequestBuilder {
|
|
42
|
+
private var url: URL?
|
|
43
|
+
private var method: String?
|
|
44
|
+
private var params: [String: String]?
|
|
45
|
+
private var request: CapacitorUrlRequest?
|
|
46
|
+
|
|
47
|
+
/// Set the URL of the HttpRequest
|
|
48
|
+
/// - Throws: an error of URLError if the urlString cannot be parsed
|
|
49
|
+
/// - Parameters:
|
|
50
|
+
/// - urlString: The URL value to parse
|
|
51
|
+
/// - Returns: self to continue chaining functions
|
|
52
|
+
public func setUrl(_ urlString: String) throws -> CapacitorHttpRequestBuilder {
|
|
53
|
+
guard let url = URL(string: urlString) else {
|
|
54
|
+
throw URLError(.badURL)
|
|
55
|
+
}
|
|
56
|
+
self.url = url
|
|
57
|
+
return self
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public func setMethod(_ method: String) -> CapacitorHttpRequestBuilder {
|
|
61
|
+
self.method = method
|
|
62
|
+
return self
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public func setUrlParams(_ params: [String: Any]) -> CapacitorHttpRequestBuilder {
|
|
66
|
+
if params.count != 0 {
|
|
67
|
+
// swiftlint:disable force_cast
|
|
68
|
+
var cmps = URLComponents(url: url!, resolvingAgainstBaseURL: true)
|
|
69
|
+
if cmps?.queryItems == nil {
|
|
70
|
+
cmps?.queryItems = []
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
var urlSafeParams: [URLQueryItem] = []
|
|
74
|
+
for (key, value) in params {
|
|
75
|
+
if let arr = value as? [String] {
|
|
76
|
+
arr.forEach { str in
|
|
77
|
+
urlSafeParams.append(URLQueryItem(name: key, value: str))
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
urlSafeParams.append(URLQueryItem(name: key, value: (value as! String)))
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
cmps!.queryItems?.append(contentsOf: urlSafeParams)
|
|
85
|
+
url = cmps!.url!
|
|
86
|
+
}
|
|
87
|
+
return self
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public func openConnection() -> CapacitorHttpRequestBuilder {
|
|
91
|
+
request = CapacitorUrlRequest(url!, method: method!)
|
|
92
|
+
return self
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public func build() -> CapacitorUrlRequest {
|
|
96
|
+
return request!
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private static func buildResponse(_ data: Data?, _ response: HTTPURLResponse, responseType: ResponseType = .default) -> [String: Any] {
|
|
101
|
+
var output = [:] as [String: Any]
|
|
102
|
+
|
|
103
|
+
output["status"] = response.statusCode
|
|
104
|
+
output["headers"] = response.allHeaderFields
|
|
105
|
+
output["url"] = response.url?.absoluteString
|
|
106
|
+
|
|
107
|
+
guard let data = data else {
|
|
108
|
+
output["data"] = ""
|
|
109
|
+
return output
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let contentType = (response.allHeaderFields["Content-Type"] as? String ?? "application/default").lowercased()
|
|
113
|
+
|
|
114
|
+
if contentType.contains("application/json") || responseType == .json {
|
|
115
|
+
output["data"] = tryParseJson(data)
|
|
116
|
+
} else if responseType == .arrayBuffer || responseType == .blob {
|
|
117
|
+
output["data"] = data.base64EncodedString()
|
|
118
|
+
} else if responseType == .document || responseType == .text || responseType == .default {
|
|
119
|
+
output["data"] = String(data: data, encoding: .utf8)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return output
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public static func request(_ call: CAPPluginCall, _ httpMethod: String?) throws {
|
|
126
|
+
guard let urlString = call.getString("url") else { throw URLError(.badURL) }
|
|
127
|
+
let method = httpMethod ?? call.getString("method", "GET")
|
|
128
|
+
|
|
129
|
+
// swiftlint:disable force_cast
|
|
130
|
+
let headers = (call.getObject("headers") ?? [:]) as! [String: String]
|
|
131
|
+
let params = (call.getObject("params") ?? [:]) as [String: Any]
|
|
132
|
+
let responseType = call.getString("responseType") ?? "text"
|
|
133
|
+
let connectTimeout = call.getDouble("connectTimeout")
|
|
134
|
+
let readTimeout = call.getDouble("readTimeout")
|
|
135
|
+
|
|
136
|
+
let request = try CapacitorHttpRequestBuilder()
|
|
137
|
+
.setUrl(urlString)
|
|
138
|
+
.setMethod(method)
|
|
139
|
+
.setUrlParams(params)
|
|
140
|
+
.openConnection()
|
|
141
|
+
.build()
|
|
142
|
+
|
|
143
|
+
request.setRequestHeaders(headers)
|
|
144
|
+
|
|
145
|
+
// Timeouts in iOS are in seconds. So read the value in millis and divide by 1000
|
|
146
|
+
let timeout = (connectTimeout ?? readTimeout ?? 600000.0) / 1000.0
|
|
147
|
+
request.setTimeout(timeout)
|
|
148
|
+
|
|
149
|
+
if let data = call.options["data"] as? JSValue {
|
|
150
|
+
do {
|
|
151
|
+
try request.setRequestBody(data)
|
|
152
|
+
} catch {
|
|
153
|
+
// Explicitly reject if the http request body was not set successfully,
|
|
154
|
+
// so as to not send a known malformed request, and to provide the developer with additional context.
|
|
155
|
+
call.reject("Error", "REQUEST", error, [:])
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
let urlRequest = request.getUrlRequest()
|
|
161
|
+
let urlSession = request.getUrlSession(call)
|
|
162
|
+
let task = urlSession.dataTask(with: urlRequest) { (data, response, error) in
|
|
163
|
+
urlSession.invalidateAndCancel()
|
|
164
|
+
if error != nil {
|
|
165
|
+
return
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let type = ResponseType(rawValue: responseType) ?? .default
|
|
169
|
+
call.resolve(self.buildResponse(data, response as! HTTPURLResponse, responseType: type))
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
task.resume()
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -243,6 +243,34 @@ internal class WebViewDelegationHandler: NSObject, WKNavigationDelegate, WKUIDel
|
|
|
243
243
|
}
|
|
244
244
|
|
|
245
245
|
public func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
|
|
246
|
+
|
|
247
|
+
// Check if this is synchronous cookie or http call
|
|
248
|
+
do {
|
|
249
|
+
if let dataFromString = prompt.data(using: .utf8, allowLossyConversion: false) {
|
|
250
|
+
if let payload = try JSONSerialization.jsonObject(with: dataFromString, options: .fragmentsAllowed) as? [String: AnyObject] {
|
|
251
|
+
let type = payload["type"] as? String
|
|
252
|
+
|
|
253
|
+
if type == "CapacitorCookies" {
|
|
254
|
+
completionHandler(CapacitorCookieManager(bridge!.config).getCookies())
|
|
255
|
+
// Don't present prompt
|
|
256
|
+
return
|
|
257
|
+
} else if type == "CapacitorCookies.isEnabled" {
|
|
258
|
+
let pluginConfig = bridge!.config.getPluginConfig("CapacitorCookies")
|
|
259
|
+
completionHandler(String(pluginConfig.getBoolean("enabled", false)))
|
|
260
|
+
// Don't present prompt
|
|
261
|
+
return
|
|
262
|
+
} else if type == "CapacitorHttp" {
|
|
263
|
+
let pluginConfig = bridge!.config.getPluginConfig("CapacitorHttp")
|
|
264
|
+
completionHandler(String(pluginConfig.getBoolean("enabled", false)))
|
|
265
|
+
// Don't present prompt
|
|
266
|
+
return
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
} catch {
|
|
271
|
+
// Continue with regular prompt
|
|
272
|
+
}
|
|
273
|
+
|
|
246
274
|
guard let viewController = bridge?.viewController else {
|
|
247
275
|
return
|
|
248
276
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/*! Capacitor: https://capacitorjs.com/ - MIT License */
|
|
3
3
|
/* Generated File. Do not edit. */
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
const nativeBridge = (function (exports) {
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
8
|
var ExceptionCode;
|
|
@@ -268,6 +268,274 @@ var nativeBridge = (function (exports) {
|
|
|
268
268
|
}
|
|
269
269
|
return String(msg);
|
|
270
270
|
};
|
|
271
|
+
/**
|
|
272
|
+
* Safely web decode a string value (inspired by js-cookie)
|
|
273
|
+
* @param str The string value to decode
|
|
274
|
+
*/
|
|
275
|
+
const decode = (str) => str.replace(/(%[\dA-F]{2})+/gi, decodeURIComponent);
|
|
276
|
+
const platform = getPlatformId(win);
|
|
277
|
+
if (platform == 'android' || platform == 'ios') {
|
|
278
|
+
// patch document.cookie on Android/iOS
|
|
279
|
+
win.CapacitorCookiesDescriptor =
|
|
280
|
+
Object.getOwnPropertyDescriptor(Document.prototype, 'cookie') ||
|
|
281
|
+
Object.getOwnPropertyDescriptor(HTMLDocument.prototype, 'cookie');
|
|
282
|
+
let doPatchCookies = false;
|
|
283
|
+
// check if capacitor cookies is disabled before patching
|
|
284
|
+
if (platform === 'ios') {
|
|
285
|
+
// Use prompt to synchronously get capacitor cookies config.
|
|
286
|
+
// https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323
|
|
287
|
+
const payload = {
|
|
288
|
+
type: 'CapacitorCookies.isEnabled',
|
|
289
|
+
};
|
|
290
|
+
const isCookiesEnabled = prompt(JSON.stringify(payload));
|
|
291
|
+
if (isCookiesEnabled === 'true') {
|
|
292
|
+
doPatchCookies = true;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') {
|
|
296
|
+
const isCookiesEnabled = win.CapacitorCookiesAndroidInterface.isEnabled();
|
|
297
|
+
if (isCookiesEnabled === true) {
|
|
298
|
+
doPatchCookies = true;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (doPatchCookies) {
|
|
302
|
+
Object.defineProperty(document, 'cookie', {
|
|
303
|
+
get: function () {
|
|
304
|
+
if (platform === 'ios') {
|
|
305
|
+
// Use prompt to synchronously get cookies.
|
|
306
|
+
// https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323
|
|
307
|
+
const payload = {
|
|
308
|
+
type: 'CapacitorCookies',
|
|
309
|
+
};
|
|
310
|
+
const res = prompt(JSON.stringify(payload));
|
|
311
|
+
return res;
|
|
312
|
+
}
|
|
313
|
+
else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') {
|
|
314
|
+
return win.CapacitorCookiesAndroidInterface.getCookies();
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
set: function (val) {
|
|
318
|
+
const cookiePairs = val.split(';');
|
|
319
|
+
for (const cookiePair of cookiePairs) {
|
|
320
|
+
const cookieKey = cookiePair.split('=')[0];
|
|
321
|
+
const cookieValue = cookiePair.split('=')[1];
|
|
322
|
+
if (null == cookieValue) {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
cap.toNative('CapacitorCookies', 'setCookie', {
|
|
326
|
+
key: cookieKey,
|
|
327
|
+
value: decode(cookieValue),
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
// patch fetch / XHR on Android/iOS
|
|
334
|
+
// store original fetch & XHR functions
|
|
335
|
+
win.CapacitorWebFetch = window.fetch;
|
|
336
|
+
win.CapacitorWebXMLHttpRequest = {
|
|
337
|
+
abort: window.XMLHttpRequest.prototype.abort,
|
|
338
|
+
open: window.XMLHttpRequest.prototype.open,
|
|
339
|
+
send: window.XMLHttpRequest.prototype.send,
|
|
340
|
+
setRequestHeader: window.XMLHttpRequest.prototype.setRequestHeader,
|
|
341
|
+
};
|
|
342
|
+
let doPatchHttp = false;
|
|
343
|
+
// check if capacitor http is disabled before patching
|
|
344
|
+
if (platform === 'ios') {
|
|
345
|
+
// Use prompt to synchronously get capacitor http config.
|
|
346
|
+
// https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323
|
|
347
|
+
const payload = {
|
|
348
|
+
type: 'CapacitorHttp',
|
|
349
|
+
};
|
|
350
|
+
const isHttpEnabled = prompt(JSON.stringify(payload));
|
|
351
|
+
if (isHttpEnabled === 'true') {
|
|
352
|
+
doPatchHttp = true;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
else if (typeof win.CapacitorHttpAndroidInterface !== 'undefined') {
|
|
356
|
+
const isHttpEnabled = win.CapacitorHttpAndroidInterface.isEnabled();
|
|
357
|
+
if (isHttpEnabled === true) {
|
|
358
|
+
doPatchHttp = true;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
if (doPatchHttp) {
|
|
362
|
+
// fetch patch
|
|
363
|
+
window.fetch = async (resource, options) => {
|
|
364
|
+
if (resource.toString().startsWith('data:') ||
|
|
365
|
+
resource.toString().startsWith('blob:')) {
|
|
366
|
+
return win.CapacitorWebFetch(resource, options);
|
|
367
|
+
}
|
|
368
|
+
try {
|
|
369
|
+
// intercept request & pass to the bridge
|
|
370
|
+
const nativeResponse = await cap.nativePromise('CapacitorHttp', 'request', {
|
|
371
|
+
url: resource,
|
|
372
|
+
method: (options === null || options === void 0 ? void 0 : options.method) ? options.method : undefined,
|
|
373
|
+
data: (options === null || options === void 0 ? void 0 : options.body) ? options.body : undefined,
|
|
374
|
+
headers: (options === null || options === void 0 ? void 0 : options.headers) ? options.headers : undefined,
|
|
375
|
+
});
|
|
376
|
+
const data = typeof nativeResponse.data === 'string'
|
|
377
|
+
? nativeResponse.data
|
|
378
|
+
: JSON.stringify(nativeResponse.data);
|
|
379
|
+
// intercept & parse response before returning
|
|
380
|
+
const response = new Response(data, {
|
|
381
|
+
headers: nativeResponse.headers,
|
|
382
|
+
status: nativeResponse.status,
|
|
383
|
+
});
|
|
384
|
+
return response;
|
|
385
|
+
}
|
|
386
|
+
catch (error) {
|
|
387
|
+
return Promise.reject(error);
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
// XHR event listeners
|
|
391
|
+
const addEventListeners = function () {
|
|
392
|
+
this.addEventListener('abort', function () {
|
|
393
|
+
if (typeof this.onabort === 'function')
|
|
394
|
+
this.onabort();
|
|
395
|
+
});
|
|
396
|
+
this.addEventListener('error', function () {
|
|
397
|
+
if (typeof this.onerror === 'function')
|
|
398
|
+
this.onerror();
|
|
399
|
+
});
|
|
400
|
+
this.addEventListener('load', function () {
|
|
401
|
+
if (typeof this.onload === 'function')
|
|
402
|
+
this.onload();
|
|
403
|
+
});
|
|
404
|
+
this.addEventListener('loadend', function () {
|
|
405
|
+
if (typeof this.onloadend === 'function')
|
|
406
|
+
this.onloadend();
|
|
407
|
+
});
|
|
408
|
+
this.addEventListener('loadstart', function () {
|
|
409
|
+
if (typeof this.onloadstart === 'function')
|
|
410
|
+
this.onloadstart();
|
|
411
|
+
});
|
|
412
|
+
this.addEventListener('readystatechange', function () {
|
|
413
|
+
if (typeof this.onreadystatechange === 'function')
|
|
414
|
+
this.onreadystatechange();
|
|
415
|
+
});
|
|
416
|
+
this.addEventListener('timeout', function () {
|
|
417
|
+
if (typeof this.ontimeout === 'function')
|
|
418
|
+
this.ontimeout();
|
|
419
|
+
});
|
|
420
|
+
};
|
|
421
|
+
// XHR patch abort
|
|
422
|
+
window.XMLHttpRequest.prototype.abort = function () {
|
|
423
|
+
this.readyState = 0;
|
|
424
|
+
this.dispatchEvent(new Event('abort'));
|
|
425
|
+
this.dispatchEvent(new Event('loadend'));
|
|
426
|
+
};
|
|
427
|
+
// XHR patch open
|
|
428
|
+
window.XMLHttpRequest.prototype.open = function (method, url) {
|
|
429
|
+
Object.defineProperties(this, {
|
|
430
|
+
_headers: {
|
|
431
|
+
value: {},
|
|
432
|
+
writable: true,
|
|
433
|
+
},
|
|
434
|
+
readyState: {
|
|
435
|
+
get: function () {
|
|
436
|
+
var _a;
|
|
437
|
+
return (_a = this._readyState) !== null && _a !== void 0 ? _a : 0;
|
|
438
|
+
},
|
|
439
|
+
set: function (val) {
|
|
440
|
+
this._readyState = val;
|
|
441
|
+
this.dispatchEvent(new Event('readystatechange'));
|
|
442
|
+
},
|
|
443
|
+
},
|
|
444
|
+
response: {
|
|
445
|
+
value: '',
|
|
446
|
+
writable: true,
|
|
447
|
+
},
|
|
448
|
+
responseText: {
|
|
449
|
+
value: '',
|
|
450
|
+
writable: true,
|
|
451
|
+
},
|
|
452
|
+
responseURL: {
|
|
453
|
+
value: '',
|
|
454
|
+
writable: true,
|
|
455
|
+
},
|
|
456
|
+
status: {
|
|
457
|
+
value: 0,
|
|
458
|
+
writable: true,
|
|
459
|
+
},
|
|
460
|
+
});
|
|
461
|
+
addEventListeners.call(this);
|
|
462
|
+
this._method = method;
|
|
463
|
+
this._url = url;
|
|
464
|
+
this.readyState = 1;
|
|
465
|
+
};
|
|
466
|
+
// XHR patch set request header
|
|
467
|
+
window.XMLHttpRequest.prototype.setRequestHeader = function (header, value) {
|
|
468
|
+
this._headers[header] = value;
|
|
469
|
+
};
|
|
470
|
+
// XHR patch send
|
|
471
|
+
window.XMLHttpRequest.prototype.send = function (body) {
|
|
472
|
+
try {
|
|
473
|
+
this.readyState = 2;
|
|
474
|
+
// intercept request & pass to the bridge
|
|
475
|
+
cap
|
|
476
|
+
.nativePromise('CapacitorHttp', 'request', {
|
|
477
|
+
url: this._url,
|
|
478
|
+
method: this._method,
|
|
479
|
+
data: body !== null ? body : undefined,
|
|
480
|
+
headers: this._headers,
|
|
481
|
+
})
|
|
482
|
+
.then((nativeResponse) => {
|
|
483
|
+
// intercept & parse response before returning
|
|
484
|
+
if (this.readyState == 2) {
|
|
485
|
+
this.dispatchEvent(new Event('loadstart'));
|
|
486
|
+
this._headers = nativeResponse.headers;
|
|
487
|
+
this.status = nativeResponse.status;
|
|
488
|
+
this.response = nativeResponse.data;
|
|
489
|
+
this.responseText =
|
|
490
|
+
typeof nativeResponse.data === 'string'
|
|
491
|
+
? nativeResponse.data
|
|
492
|
+
: JSON.stringify(nativeResponse.data);
|
|
493
|
+
this.responseURL = nativeResponse.url;
|
|
494
|
+
this.readyState = 4;
|
|
495
|
+
this.dispatchEvent(new Event('load'));
|
|
496
|
+
this.dispatchEvent(new Event('loadend'));
|
|
497
|
+
}
|
|
498
|
+
})
|
|
499
|
+
.catch((error) => {
|
|
500
|
+
this.dispatchEvent(new Event('loadstart'));
|
|
501
|
+
this.status = error.status;
|
|
502
|
+
this._headers = error.headers;
|
|
503
|
+
this.response = error.data;
|
|
504
|
+
this.responseText = JSON.stringify(error.data);
|
|
505
|
+
this.responseURL = error.url;
|
|
506
|
+
this.readyState = 4;
|
|
507
|
+
this.dispatchEvent(new Event('error'));
|
|
508
|
+
this.dispatchEvent(new Event('loadend'));
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
catch (error) {
|
|
512
|
+
this.dispatchEvent(new Event('loadstart'));
|
|
513
|
+
this.status = 500;
|
|
514
|
+
this._headers = {};
|
|
515
|
+
this.response = error;
|
|
516
|
+
this.responseText = error.toString();
|
|
517
|
+
this.responseURL = this._url;
|
|
518
|
+
this.readyState = 4;
|
|
519
|
+
this.dispatchEvent(new Event('error'));
|
|
520
|
+
this.dispatchEvent(new Event('loadend'));
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
// XHR patch getAllResponseHeaders
|
|
524
|
+
window.XMLHttpRequest.prototype.getAllResponseHeaders = function () {
|
|
525
|
+
let returnString = '';
|
|
526
|
+
for (const key in this._headers) {
|
|
527
|
+
if (key != 'Set-Cookie') {
|
|
528
|
+
returnString += key + ': ' + this._headers[key] + '\r\n';
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return returnString;
|
|
532
|
+
};
|
|
533
|
+
// XHR patch getResponseHeader
|
|
534
|
+
window.XMLHttpRequest.prototype.getResponseHeader = function (name) {
|
|
535
|
+
return this._headers[name];
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
}
|
|
271
539
|
// patch window.console on iOS and store original console fns
|
|
272
540
|
const isIos = getPlatformId(win) === 'ios';
|
|
273
541
|
if (win.console && isIos) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capacitor/ios",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.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)",
|
|
@@ -30,5 +30,5 @@
|
|
|
30
30
|
"publishConfig": {
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
|
-
"gitHead": "
|
|
33
|
+
"gitHead": "3d4433b505252d263784ec0fafc72a9b7cd8591e"
|
|
34
34
|
}
|