@capacitor/ios 4.1.0 → 4.1.1-dev-6756a744-20220907T2202.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/Capacitor/Capacitor/Plugins/CapacitorCookieManager.swift +66 -0
- package/Capacitor/Capacitor/Plugins/CapacitorCookies.swift +52 -0
- package/Capacitor/Capacitor/Plugins/CapacitorHttp.swift +41 -0
- package/Capacitor/Capacitor/Plugins/CapacitorUrlRequest.swift +150 -0
- package/Capacitor/Capacitor/Plugins/DefaultPlugins.m +16 -0
- package/Capacitor/Capacitor/Plugins/HttpRequestHandler.swift +172 -0
- package/Capacitor/Capacitor/WebViewDelegationHandler.swift +23 -0
- package/Capacitor/Capacitor/assets/native-bridge.js +224 -0
- package/LICENSE +17 -19
- package/package.json +3 -3
|
@@ -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,41 @@
|
|
|
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 u = call.getString("url") else { return call.reject("Must provide a URL"); }
|
|
8
|
+
guard let _ = httpMethod ?? call.getString("method") else { return call.reject("Must provide an HTTP Method"); }
|
|
9
|
+
guard var _ = URL(string: u) else { return call.reject("Invalid URL"); }
|
|
10
|
+
|
|
11
|
+
do {
|
|
12
|
+
try HttpRequestHandler.request(call, httpMethod)
|
|
13
|
+
} catch let e {
|
|
14
|
+
call.reject(e.localizedDescription)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@objc func request(_ call: CAPPluginCall) {
|
|
19
|
+
http(call, nil)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@objc func get(_ call: CAPPluginCall) {
|
|
23
|
+
http(call, "GET")
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@objc func post(_ call: CAPPluginCall) {
|
|
27
|
+
http(call, "POST")
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@objc func put(_ call: CAPPluginCall) {
|
|
31
|
+
http(call, "PUT")
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@objc func patch(_ call: CAPPluginCall) {
|
|
35
|
+
http(call, "PATCH")
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@objc func del(_ call: CAPPluginCall) {
|
|
39
|
+
http(call, "DELETE")
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -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,10 +2,26 @@
|
|
|
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
|
+
|
|
5
12
|
CAP_PLUGIN(CAPConsolePlugin, "Console",
|
|
6
13
|
CAP_PLUGIN_METHOD(log, CAPPluginReturnNone);
|
|
7
14
|
)
|
|
8
15
|
|
|
16
|
+
CAP_PLUGIN(CAPHttpPlugin, "CapacitorHttp",
|
|
17
|
+
CAP_PLUGIN_METHOD(request, CAPPluginReturnPromise);
|
|
18
|
+
CAP_PLUGIN_METHOD(get, CAPPluginReturnPromise);
|
|
19
|
+
CAP_PLUGIN_METHOD(post, CAPPluginReturnPromise);
|
|
20
|
+
CAP_PLUGIN_METHOD(put, CAPPluginReturnPromise);
|
|
21
|
+
CAP_PLUGIN_METHOD(patch, CAPPluginReturnPromise);
|
|
22
|
+
CAP_PLUGIN_METHOD(delete, CAPPluginReturnPromise);
|
|
23
|
+
)
|
|
24
|
+
|
|
9
25
|
CAP_PLUGIN(CAPWebViewPlugin, "WebView",
|
|
10
26
|
CAP_PLUGIN_METHOD(setServerBasePath, CAPPluginReturnPromise);
|
|
11
27
|
CAP_PLUGIN_METHOD(getServerBasePath, CAPPluginReturnPromise);
|
|
@@ -0,0 +1,172 @@
|
|
|
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 u = URL(string: urlString) else {
|
|
54
|
+
throw URLError(.badURL)
|
|
55
|
+
}
|
|
56
|
+
url = u
|
|
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
|
+
var cmps = URLComponents(url: url!, resolvingAgainstBaseURL: true)
|
|
68
|
+
if cmps?.queryItems == nil {
|
|
69
|
+
cmps?.queryItems = []
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
var urlSafeParams: [URLQueryItem] = []
|
|
73
|
+
for (key, value) in params {
|
|
74
|
+
if let arr = value as? [String] {
|
|
75
|
+
arr.forEach { str in
|
|
76
|
+
urlSafeParams.append(URLQueryItem(name: key, value: str))
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
urlSafeParams.append(URLQueryItem(name: key, value: (value as! String)))
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
cmps!.queryItems?.append(contentsOf: urlSafeParams)
|
|
84
|
+
url = cmps!.url!
|
|
85
|
+
}
|
|
86
|
+
return self
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public func openConnection() -> CapacitorHttpRequestBuilder {
|
|
90
|
+
request = CapacitorUrlRequest(url!, method: method!)
|
|
91
|
+
return self
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
public func build() -> CapacitorUrlRequest {
|
|
95
|
+
return request!
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private static func buildResponse(_ data: Data?, _ response: HTTPURLResponse, responseType: ResponseType = .default) -> [String: Any] {
|
|
100
|
+
var output = [:] as [String: Any]
|
|
101
|
+
|
|
102
|
+
output["status"] = response.statusCode
|
|
103
|
+
output["headers"] = response.allHeaderFields
|
|
104
|
+
output["url"] = response.url?.absoluteString
|
|
105
|
+
|
|
106
|
+
guard let data = data else {
|
|
107
|
+
output["data"] = ""
|
|
108
|
+
return output
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
let contentType = (response.allHeaderFields["Content-Type"] as? String ?? "application/default").lowercased()
|
|
112
|
+
|
|
113
|
+
if contentType.contains("application/json") || responseType == .json {
|
|
114
|
+
output["data"] = tryParseJson(data)
|
|
115
|
+
} else if responseType == .arrayBuffer || responseType == .blob {
|
|
116
|
+
output["data"] = data.base64EncodedString()
|
|
117
|
+
} else if responseType == .document || responseType == .text || responseType == .default {
|
|
118
|
+
output["data"] = String(data: data, encoding: .utf8)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return output
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
public static func request(_ call: CAPPluginCall, _ httpMethod: String?) throws {
|
|
125
|
+
guard let urlString = call.getString("url") else { throw URLError(.badURL) }
|
|
126
|
+
guard let method = httpMethod ?? call.getString("method") else { throw URLError(.dataNotAllowed) }
|
|
127
|
+
|
|
128
|
+
let headers = (call.getObject("headers") ?? [:]) as! [String: String]
|
|
129
|
+
let params = (call.getObject("params") ?? [:]) as! [String: Any]
|
|
130
|
+
let responseType = call.getString("responseType") ?? "text"
|
|
131
|
+
let connectTimeout = call.getDouble("connectTimeout")
|
|
132
|
+
let readTimeout = call.getDouble("readTimeout")
|
|
133
|
+
|
|
134
|
+
let request = try! CapacitorHttpRequestBuilder()
|
|
135
|
+
.setUrl(urlString)
|
|
136
|
+
.setMethod(method)
|
|
137
|
+
.setUrlParams(params)
|
|
138
|
+
.openConnection()
|
|
139
|
+
.build()
|
|
140
|
+
|
|
141
|
+
request.setRequestHeaders(headers)
|
|
142
|
+
|
|
143
|
+
// Timeouts in iOS are in seconds. So read the value in millis and divide by 1000
|
|
144
|
+
let timeout = (connectTimeout ?? readTimeout ?? 600000.0) / 1000.0
|
|
145
|
+
request.setTimeout(timeout)
|
|
146
|
+
|
|
147
|
+
if let data = call.options["data"] as? JSValue {
|
|
148
|
+
do {
|
|
149
|
+
try request.setRequestBody(data)
|
|
150
|
+
} catch {
|
|
151
|
+
// Explicitly reject if the http request body was not set successfully,
|
|
152
|
+
// so as to not send a known malformed request, and to provide the developer with additional context.
|
|
153
|
+
call.reject("Error", "REQUEST", error, [:])
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
let urlRequest = request.getUrlRequest()
|
|
159
|
+
let urlSession = request.getUrlSession(call)
|
|
160
|
+
let task = urlSession.dataTask(with: urlRequest) { (data, response, error) in
|
|
161
|
+
urlSession.invalidateAndCancel()
|
|
162
|
+
if error != nil {
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
let type = ResponseType(rawValue: responseType) ?? .default
|
|
167
|
+
call.resolve(self.buildResponse(data, response as! HTTPURLResponse, responseType: type))
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
task.resume()
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -243,6 +243,29 @@ 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 == "CapacitorHttp" {
|
|
258
|
+
let pluginConfig = bridge!.config.getPluginConfig("CapacitorHttp")
|
|
259
|
+
completionHandler(String(pluginConfig.getBoolean("disabled", false)))
|
|
260
|
+
// Don't present prompt
|
|
261
|
+
return
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
} catch {
|
|
266
|
+
// Continue with regular prompt
|
|
267
|
+
}
|
|
268
|
+
|
|
246
269
|
guard let viewController = bridge?.viewController else {
|
|
247
270
|
return
|
|
248
271
|
}
|
|
@@ -241,6 +241,230 @@ const nativeBridge = (function (exports) {
|
|
|
241
241
|
}
|
|
242
242
|
return String(msg);
|
|
243
243
|
};
|
|
244
|
+
/**
|
|
245
|
+
* Safely web decode a string value (inspired by js-cookie)
|
|
246
|
+
* @param str The string value to decode
|
|
247
|
+
*/
|
|
248
|
+
const decode = (str) => str.replace(/(%[\dA-F]{2})+/gi, decodeURIComponent);
|
|
249
|
+
const platform = getPlatformId(win);
|
|
250
|
+
if (platform == 'android' || platform == 'ios') {
|
|
251
|
+
// patch document.cookie on Android/iOS
|
|
252
|
+
win.CapacitorCookiesDescriptor =
|
|
253
|
+
Object.getOwnPropertyDescriptor(Document.prototype, 'cookie') ||
|
|
254
|
+
Object.getOwnPropertyDescriptor(HTMLDocument.prototype, 'cookie');
|
|
255
|
+
Object.defineProperty(document, 'cookie', {
|
|
256
|
+
get: function () {
|
|
257
|
+
if (platform === 'ios') {
|
|
258
|
+
// Use prompt to synchronously get cookies.
|
|
259
|
+
// https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323
|
|
260
|
+
const payload = {
|
|
261
|
+
type: 'CapacitorCookies',
|
|
262
|
+
};
|
|
263
|
+
const res = prompt(JSON.stringify(payload));
|
|
264
|
+
return res;
|
|
265
|
+
}
|
|
266
|
+
else if (typeof win.CapacitorCookiesAndroidInterface !== 'undefined') {
|
|
267
|
+
return win.CapacitorCookiesAndroidInterface.getCookies();
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
set: function (val) {
|
|
271
|
+
const cookiePairs = val.split(';');
|
|
272
|
+
for (const cookiePair of cookiePairs) {
|
|
273
|
+
const cookieKey = cookiePair.split('=')[0];
|
|
274
|
+
const cookieValue = cookiePair.split('=')[1];
|
|
275
|
+
if (null == cookieValue) {
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
cap.toNative('CapacitorCookies', 'setCookie', {
|
|
279
|
+
key: cookieKey,
|
|
280
|
+
value: decode(cookieValue),
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
});
|
|
285
|
+
// patch fetch / XHR on Android/iOS
|
|
286
|
+
// store original fetch & XHR functions
|
|
287
|
+
win.CapacitorWebFetch = window.fetch;
|
|
288
|
+
win.CapacitorWebXMLHttpRequest = {
|
|
289
|
+
abort: window.XMLHttpRequest.prototype.abort,
|
|
290
|
+
open: window.XMLHttpRequest.prototype.open,
|
|
291
|
+
send: window.XMLHttpRequest.prototype.send,
|
|
292
|
+
setRequestHeader: window.XMLHttpRequest.prototype.setRequestHeader,
|
|
293
|
+
};
|
|
294
|
+
let doPatchHttp = true;
|
|
295
|
+
// check if capacitor http is disabled before patching
|
|
296
|
+
if (platform === 'ios') {
|
|
297
|
+
// Use prompt to synchronously get capacitor http config.
|
|
298
|
+
// https://stackoverflow.com/questions/29249132/wkwebview-complex-communication-between-javascript-native-code/49474323#49474323
|
|
299
|
+
const payload = {
|
|
300
|
+
type: 'CapacitorHttp',
|
|
301
|
+
};
|
|
302
|
+
const isDisabled = prompt(JSON.stringify(payload));
|
|
303
|
+
if (isDisabled === 'true') {
|
|
304
|
+
doPatchHttp = false;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
else if (typeof win.CapacitorHttpAndroidInterface !== 'undefined') {
|
|
308
|
+
const isDisabled = win.CapacitorHttpAndroidInterface.isDisabled();
|
|
309
|
+
if (isDisabled === true) {
|
|
310
|
+
doPatchHttp = false;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (doPatchHttp) {
|
|
314
|
+
// fetch patch
|
|
315
|
+
window.fetch = async (resource, options) => {
|
|
316
|
+
if (resource.toString().startsWith('data:')) {
|
|
317
|
+
return win.CapacitorWebFetch(resource, options);
|
|
318
|
+
}
|
|
319
|
+
try {
|
|
320
|
+
// intercept request & pass to the bridge
|
|
321
|
+
const nativeResponse = await cap.nativePromise('CapacitorHttp', 'request', {
|
|
322
|
+
url: resource,
|
|
323
|
+
method: (options === null || options === void 0 ? void 0 : options.method) ? options.method : undefined,
|
|
324
|
+
data: (options === null || options === void 0 ? void 0 : options.body) ? options.body : undefined,
|
|
325
|
+
headers: (options === null || options === void 0 ? void 0 : options.headers) ? options.headers : undefined,
|
|
326
|
+
});
|
|
327
|
+
// intercept & parse response before returning
|
|
328
|
+
const response = new Response(JSON.stringify(nativeResponse.data), {
|
|
329
|
+
headers: nativeResponse.headers,
|
|
330
|
+
status: nativeResponse.status,
|
|
331
|
+
});
|
|
332
|
+
return response;
|
|
333
|
+
}
|
|
334
|
+
catch (error) {
|
|
335
|
+
return Promise.reject(error);
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
// XHR event listeners
|
|
339
|
+
const addEventListeners = function () {
|
|
340
|
+
this.addEventListener('abort', function () {
|
|
341
|
+
if (typeof this.onabort === 'function')
|
|
342
|
+
this.onabort();
|
|
343
|
+
});
|
|
344
|
+
this.addEventListener('error', function () {
|
|
345
|
+
if (typeof this.onerror === 'function')
|
|
346
|
+
this.onerror();
|
|
347
|
+
});
|
|
348
|
+
this.addEventListener('load', function () {
|
|
349
|
+
if (typeof this.onload === 'function')
|
|
350
|
+
this.onload();
|
|
351
|
+
});
|
|
352
|
+
this.addEventListener('loadend', function () {
|
|
353
|
+
if (typeof this.onloadend === 'function')
|
|
354
|
+
this.onloadend();
|
|
355
|
+
});
|
|
356
|
+
this.addEventListener('loadstart', function () {
|
|
357
|
+
if (typeof this.onloadstart === 'function')
|
|
358
|
+
this.onloadstart();
|
|
359
|
+
});
|
|
360
|
+
this.addEventListener('readystatechange', function () {
|
|
361
|
+
if (typeof this.onreadystatechange === 'function')
|
|
362
|
+
this.onreadystatechange();
|
|
363
|
+
});
|
|
364
|
+
this.addEventListener('timeout', function () {
|
|
365
|
+
if (typeof this.ontimeout === 'function')
|
|
366
|
+
this.ontimeout();
|
|
367
|
+
});
|
|
368
|
+
};
|
|
369
|
+
// XHR patch abort
|
|
370
|
+
window.XMLHttpRequest.prototype.abort = function () {
|
|
371
|
+
Object.defineProperties(this, {
|
|
372
|
+
_headers: {
|
|
373
|
+
value: {},
|
|
374
|
+
writable: true,
|
|
375
|
+
},
|
|
376
|
+
readyState: {
|
|
377
|
+
get: function () {
|
|
378
|
+
var _a;
|
|
379
|
+
return (_a = this._readyState) !== null && _a !== void 0 ? _a : 0;
|
|
380
|
+
},
|
|
381
|
+
set: function (val) {
|
|
382
|
+
this._readyState = val;
|
|
383
|
+
this.dispatchEvent(new Event('readystatechange'));
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
response: {
|
|
387
|
+
value: '',
|
|
388
|
+
writable: true,
|
|
389
|
+
},
|
|
390
|
+
responseText: {
|
|
391
|
+
value: '',
|
|
392
|
+
writable: true,
|
|
393
|
+
},
|
|
394
|
+
responseURL: {
|
|
395
|
+
value: '',
|
|
396
|
+
writable: true,
|
|
397
|
+
},
|
|
398
|
+
status: {
|
|
399
|
+
value: 0,
|
|
400
|
+
writable: true,
|
|
401
|
+
},
|
|
402
|
+
});
|
|
403
|
+
this.readyState = 0;
|
|
404
|
+
this.dispatchEvent(new Event('abort'));
|
|
405
|
+
this.dispatchEvent(new Event('loadend'));
|
|
406
|
+
};
|
|
407
|
+
// XHR patch open
|
|
408
|
+
window.XMLHttpRequest.prototype.open = function (method, url) {
|
|
409
|
+
this.abort();
|
|
410
|
+
addEventListeners.call(this);
|
|
411
|
+
this._method = method;
|
|
412
|
+
this._url = url;
|
|
413
|
+
this.readyState = 1;
|
|
414
|
+
};
|
|
415
|
+
// XHR patch set request header
|
|
416
|
+
window.XMLHttpRequest.prototype.setRequestHeader = function (header, value) {
|
|
417
|
+
this._headers[header] = value;
|
|
418
|
+
};
|
|
419
|
+
// XHR patch send
|
|
420
|
+
window.XMLHttpRequest.prototype.send = function (body) {
|
|
421
|
+
try {
|
|
422
|
+
this.readyState = 2;
|
|
423
|
+
// intercept request & pass to the bridge
|
|
424
|
+
cap
|
|
425
|
+
.nativePromise('CapacitorHttp', 'request', {
|
|
426
|
+
url: this._url,
|
|
427
|
+
method: this._method,
|
|
428
|
+
data: body !== null ? body : undefined,
|
|
429
|
+
headers: this._headers,
|
|
430
|
+
})
|
|
431
|
+
.then((nativeResponse) => {
|
|
432
|
+
// intercept & parse response before returning
|
|
433
|
+
if (this.readyState == 2) {
|
|
434
|
+
this.dispatchEvent(new Event('loadstart'));
|
|
435
|
+
this.status = nativeResponse.status;
|
|
436
|
+
this.response = nativeResponse.data;
|
|
437
|
+
this.responseText = JSON.stringify(nativeResponse.data);
|
|
438
|
+
this.responseURL = nativeResponse.url;
|
|
439
|
+
this.readyState = 4;
|
|
440
|
+
this.dispatchEvent(new Event('load'));
|
|
441
|
+
this.dispatchEvent(new Event('loadend'));
|
|
442
|
+
}
|
|
443
|
+
})
|
|
444
|
+
.catch((error) => {
|
|
445
|
+
this.dispatchEvent(new Event('loadstart'));
|
|
446
|
+
this.status = error.status;
|
|
447
|
+
this.response = error.data;
|
|
448
|
+
this.responseText = JSON.stringify(error.data);
|
|
449
|
+
this.responseURL = error.url;
|
|
450
|
+
this.readyState = 4;
|
|
451
|
+
this.dispatchEvent(new Event('error'));
|
|
452
|
+
this.dispatchEvent(new Event('loadend'));
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
catch (error) {
|
|
456
|
+
this.dispatchEvent(new Event('loadstart'));
|
|
457
|
+
this.status = 500;
|
|
458
|
+
this.response = error;
|
|
459
|
+
this.responseText = error.toString();
|
|
460
|
+
this.responseURL = this._url;
|
|
461
|
+
this.readyState = 4;
|
|
462
|
+
this.dispatchEvent(new Event('error'));
|
|
463
|
+
this.dispatchEvent(new Event('loadend'));
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
}
|
|
244
468
|
// patch window.console on iOS and store original console fns
|
|
245
469
|
const isIos = getPlatformId(win) === 'ios';
|
|
246
470
|
if (win.console && isIos) {
|
package/LICENSE
CHANGED
|
@@ -1,23 +1,21 @@
|
|
|
1
|
-
Copyright 2017-present Ionic
|
|
2
|
-
https://ionic.io
|
|
3
|
-
|
|
4
1
|
MIT License
|
|
5
2
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
the
|
|
3
|
+
Copyright (c) 2017-present Drifty Co.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
13
11
|
|
|
14
|
-
The above copyright notice and this permission notice shall be
|
|
15
|
-
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
16
14
|
|
|
17
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
OF
|
|
23
|
-
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capacitor/ios",
|
|
3
|
-
"version": "4.1.0",
|
|
3
|
+
"version": "4.1.1-dev-6756a744-20220907T2202.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.
|
|
28
|
+
"@capacitor/core": "^4.1.0"
|
|
29
29
|
},
|
|
30
30
|
"publishConfig": {
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
|
-
"gitHead": "
|
|
33
|
+
"gitHead": "6756a744466b66243d5ef7211f4842e6d4ab2caa"
|
|
34
34
|
}
|