@ionic/portals-react-native 0.5.1 → 0.6.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/ReactNativePortals.podspec +39 -14
- package/android/build.gradle +78 -108
- package/android/gradle.properties +5 -3
- package/android/src/main/java/io/ionic/portals/reactnative/PortalView.kt +11 -8
- package/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalManager.kt +37 -16
- package/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalsModule.kt +2 -3
- package/android/src/main/java/io/ionic/portals/reactnative/ReactNativeWebVitalsModule.kt +11 -19
- package/ios/IonicPortals+Codable.swift +77 -0
- package/ios/LiveUpdateManager+Async.swift +16 -46
- package/ios/Podfile +29 -4
- package/ios/Podfile.lock +478 -241
- package/ios/Portal.swift +50 -54
- package/ios/PortalView.swift +26 -8
- package/ios/PortalsConfig.swift +1 -88
- package/ios/PortalsReactNative-Bridging-Header.h +2 -0
- package/ios/PortalsReactNative.swift +41 -36
- package/ios/SyncResult+SyncError+Encodable.swift +55 -0
- package/ios/WebVitals.swift +19 -26
- package/package.json +62 -43
- package/src/{PortalView.android.tsx → BasePortalView.android.tsx} +2 -3
- package/src/{PortalView.tsx → BasePortalView.tsx} +2 -3
- package/src/{index.ts → index.tsx} +77 -39
- package/ios/LiveUpdate+Dict.swift +0 -30
- package/ios/LiveUpdateManagerError+Dict.swift +0 -19
- package/ios/ReactNativePortals.xcodeproj/project.pbxproj +0 -465
- package/ios/ReactNativePortals.xcodeproj/xcshareddata/xcschemes/ReactNativePortals.xcscheme +0 -67
- package/ios/ReactNativePortals.xcworkspace/contents.xcworkspacedata +0 -10
- package/ios/ReactNativePortals.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +0 -8
- package/ios/SyncResult+Dict.swift +0 -35
- package/lib/commonjs/PortalView.android.js +0 -28
- package/lib/commonjs/PortalView.android.js.map +0 -1
- package/lib/commonjs/PortalView.js +0 -15
- package/lib/commonjs/PortalView.js.map +0 -1
- package/lib/commonjs/index.js +0 -200
- package/lib/commonjs/index.js.map +0 -1
- package/lib/module/PortalView.android.js +0 -20
- package/lib/module/PortalView.android.js.map +0 -1
- package/lib/module/PortalView.js +0 -8
- package/lib/module/PortalView.js.map +0 -1
- package/lib/module/index.js +0 -176
- package/lib/module/index.js.map +0 -1
- package/lib/typescript/PortalView.android.d.ts +0 -4
- package/lib/typescript/PortalView.d.ts +0 -4
- package/lib/typescript/index.d.ts +0 -181
- /package/ios/{PortalManager.m → PortalManager.mm} +0 -0
- /package/ios/{PortalView.m → PortalView.mm} +0 -0
- /package/ios/{PortalWebVitals.m → PortalWebVitals.mm} +0 -0
- /package/ios/{PortalsPubSub.m → PortalsPubSub.mm} +0 -0
package/ios/Portal.swift
CHANGED
|
@@ -12,13 +12,26 @@ import IonicPortals
|
|
|
12
12
|
|
|
13
13
|
@dynamicMemberLookup
|
|
14
14
|
struct Portal {
|
|
15
|
-
struct Plugin {
|
|
15
|
+
struct Plugin: Codable {
|
|
16
16
|
var iosClassName: String
|
|
17
17
|
var androidClassPath: String
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
var _portal: IonicPortals.Portal
|
|
21
21
|
var plugins: [Plugin]
|
|
22
|
+
private var webVitals: [String]?
|
|
23
|
+
var usesWebVitals: Bool {
|
|
24
|
+
get {
|
|
25
|
+
webVitals?.contains("fcp") ?? false
|
|
26
|
+
}
|
|
27
|
+
set {
|
|
28
|
+
if newValue {
|
|
29
|
+
webVitals = ["fcp"]
|
|
30
|
+
} else {
|
|
31
|
+
webVitals = nil
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
22
35
|
|
|
23
36
|
subscript<T>(dynamicMember keypath: Swift.WritableKeyPath<IonicPortals.Portal, T>) -> T {
|
|
24
37
|
get { _portal[keyPath: keypath] }
|
|
@@ -31,66 +44,49 @@ struct Portal {
|
|
|
31
44
|
}
|
|
32
45
|
|
|
33
46
|
extension Portal {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if let capPlugins = dict["plugins"] as? Array<[String: String]> {
|
|
39
|
-
plugins = capPlugins.compactMap(Portal.Plugin.init)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
var assetMaps: [AssetMap] = []
|
|
43
|
-
|
|
44
|
-
if let maps = dict["assetMaps"] as? Array<[String: String]> {
|
|
45
|
-
assetMaps = maps.compactMap(AssetMap.init)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
self._portal = IonicPortals.Portal(
|
|
49
|
-
name: name,
|
|
50
|
-
startDir: dict["startDir"] as? String,
|
|
51
|
-
index: dict["index"] as? String ?? "index.html",
|
|
52
|
-
initialContext: JSTypes.coerceDictionaryToJSObject(dict["initialContext"] as? [String: Any]) ?? [:],
|
|
53
|
-
assetMaps: assetMaps,
|
|
54
|
-
plugins: plugins.toCapPlugin,
|
|
55
|
-
liveUpdateManager: liveUpdateManager,
|
|
56
|
-
liveUpdateConfig: (dict["liveUpdate"] as? [String: Any]).flatMap(LiveUpdate.init)
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
self.plugins = plugins
|
|
47
|
+
func encode(to encoder: JSValueEncoder) throws -> JSObject {
|
|
48
|
+
var object = try encoder.encodeJSObject(self)
|
|
49
|
+
object["initialContext"] = _portal.initialContext
|
|
50
|
+
return object
|
|
60
51
|
}
|
|
61
52
|
|
|
62
|
-
|
|
63
|
-
var
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
"index": self.index,
|
|
67
|
-
"initialContext": self.initialContext,
|
|
68
|
-
"liveUpdate": self.liveUpdateConfig?.dict as Any
|
|
69
|
-
]
|
|
70
|
-
|
|
71
|
-
if !plugins.isEmpty {
|
|
72
|
-
base["plugins"] = plugins.map(\.dict)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return base
|
|
53
|
+
static func decode(from jsObject: JSObject, with decoder: JSValueDecoder) throws -> Portal {
|
|
54
|
+
var portal = try decoder.decode(Portal.self, from: jsObject)
|
|
55
|
+
portal.initialContext = jsObject["initialContext"] as? JSObject ?? [:]
|
|
56
|
+
return portal
|
|
76
57
|
}
|
|
77
58
|
}
|
|
78
59
|
|
|
79
|
-
extension Portal
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
self.
|
|
60
|
+
extension Portal: Encodable {
|
|
61
|
+
enum CodingKeys: String, CodingKey {
|
|
62
|
+
case name, startDir, plugins, index, initialContext, assetMaps, liveUpdate, webVitals
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public func encode(to encoder: Encoder) throws {
|
|
66
|
+
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
67
|
+
try container.encode(self.name, forKey: .name)
|
|
68
|
+
try container.encode(self.startDir, forKey: .startDir)
|
|
69
|
+
try container.encode(self.plugins, forKey: .plugins)
|
|
70
|
+
try container.encode(self.index, forKey: .index)
|
|
71
|
+
try container.encode(self.assetMaps, forKey: .assetMaps)
|
|
72
|
+
try container.encode(self.liveUpdateConfig, forKey: .liveUpdate)
|
|
73
|
+
try container.encodeIfPresent(webVitals, forKey: .webVitals)
|
|
87
74
|
}
|
|
75
|
+
}
|
|
88
76
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
77
|
+
extension Portal: Decodable {
|
|
78
|
+
public init(from decoder: Decoder) throws {
|
|
79
|
+
let container = try decoder.container(keyedBy: CodingKeys.self)
|
|
80
|
+
let name = try container.decode(String.self, forKey: .name)
|
|
81
|
+
let startDir = try container.decodeIfPresent(String.self, forKey: .startDir) ?? ""
|
|
82
|
+
let plugins = try container.decodeIfPresent([Plugin].self, forKey: .plugins) ?? []
|
|
83
|
+
let index = try container.decodeIfPresent(String.self, forKey: .index) ?? "index.html"
|
|
84
|
+
let assetMaps = try container.decodeIfPresent([AssetMap].self, forKey: .assetMaps) ?? []
|
|
85
|
+
let liveUpdateConfig = try container.decodeIfPresent(LiveUpdate.self, forKey: .liveUpdate)
|
|
86
|
+
let webVitals = try container.decodeIfPresent([String].self, forKey: .webVitals)
|
|
87
|
+
|
|
88
|
+
let portal = IonicPortals.Portal(name: name, startDir: startDir, index: index, assetMaps: assetMaps, plugins: plugins.toCapPlugin, liveUpdateManager: PortalsReactNative.lum, liveUpdateConfig: liveUpdateConfig)
|
|
89
|
+
self.init(_portal: portal, plugins: plugins, webVitals: webVitals)
|
|
94
90
|
}
|
|
95
91
|
}
|
|
96
92
|
|
package/ios/PortalView.swift
CHANGED
|
@@ -23,10 +23,7 @@ class PortalView: UIView {
|
|
|
23
23
|
@objc var portal: [String: Any]? {
|
|
24
24
|
get {
|
|
25
25
|
guard let _portal = _portal else { return nil }
|
|
26
|
-
return
|
|
27
|
-
"name": _portal.name,
|
|
28
|
-
"initialContext": _portal.initialContext
|
|
29
|
-
]
|
|
26
|
+
return try? _portal.encode(to: JSValueEncoder(optionalEncodingStrategy: .undefined))
|
|
30
27
|
}
|
|
31
28
|
|
|
32
29
|
set {
|
|
@@ -34,12 +31,33 @@ class PortalView: UIView {
|
|
|
34
31
|
let name = portalDict["name"] as? String
|
|
35
32
|
else { return }
|
|
36
33
|
|
|
37
|
-
var portal
|
|
34
|
+
var portal: Portal
|
|
38
35
|
|
|
39
|
-
if
|
|
40
|
-
|
|
36
|
+
if var deprecatedPortal = PortalsReactNative.getPortal(named: name) {
|
|
37
|
+
if let initialContext = portalDict["initialContext"] as? [String: Any] {
|
|
38
|
+
deprecatedPortal.initialContext = JSTypes.coerceDictionaryToJSObject(initialContext) ?? [:]
|
|
39
|
+
}
|
|
40
|
+
portal = deprecatedPortal
|
|
41
|
+
} else {
|
|
42
|
+
let jsObject = JSTypes.coerceDictionaryToJSObject(portalDict) ?? [:]
|
|
43
|
+
do {
|
|
44
|
+
portal = try Portal.decode(from: jsObject, with: JSValueDecoder())
|
|
45
|
+
} catch {
|
|
46
|
+
print(error.localizedDescription)
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if portal.usesWebVitals {
|
|
51
|
+
var vitalsPlugin = WebVitalsPlugin { portalName, duration in
|
|
52
|
+
IonicPortals.PortalsPubSub
|
|
53
|
+
.shared
|
|
54
|
+
.publish(
|
|
55
|
+
["portalName": portalName, "duration": duration],
|
|
56
|
+
to: "webVitals:received"
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
portal._portal.plugins.append(.instance(vitalsPlugin))
|
|
41
60
|
}
|
|
42
|
-
|
|
43
61
|
_portal = portal
|
|
44
62
|
}
|
|
45
63
|
}
|
package/ios/PortalsConfig.swift
CHANGED
|
@@ -10,94 +10,7 @@ import Capacitor
|
|
|
10
10
|
import IonicLiveUpdates
|
|
11
11
|
import IonicPortals
|
|
12
12
|
|
|
13
|
-
struct PortalsConfig {
|
|
14
|
-
var portals: [Portal]
|
|
13
|
+
struct PortalsConfig: Decodable {
|
|
15
14
|
var registrationKey: String?
|
|
16
15
|
var secureLiveUpdatesPublicKey: String?
|
|
17
|
-
|
|
18
|
-
struct Portal {
|
|
19
|
-
var name: String
|
|
20
|
-
var startDir: String?
|
|
21
|
-
var index: String?
|
|
22
|
-
var initialContext: JSObject?
|
|
23
|
-
var assetMaps: [AssetMap]?
|
|
24
|
-
var plugins: [ReactNativePortals.Portal.Plugin]?
|
|
25
|
-
var liveUpdate: LiveUpdate?
|
|
26
|
-
|
|
27
|
-
func portal(with liveUpdateManager: LiveUpdateManager) -> ReactNativePortals.Portal {
|
|
28
|
-
return .init(
|
|
29
|
-
_portal: .init(
|
|
30
|
-
name: name,
|
|
31
|
-
startDir: startDir,
|
|
32
|
-
index: index ?? "index.html",
|
|
33
|
-
initialContext: initialContext ?? [:],
|
|
34
|
-
assetMaps: assetMaps ?? [],
|
|
35
|
-
plugins: plugins?.toCapPlugin ?? [],
|
|
36
|
-
liveUpdateManager: liveUpdateManager,
|
|
37
|
-
liveUpdateConfig: liveUpdate.map { .init(appId: $0.appId, channel: $0.channel, syncOnAdd: $0.syncOnAdd) }
|
|
38
|
-
),
|
|
39
|
-
plugins: plugins ?? []
|
|
40
|
-
)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
struct LiveUpdate {
|
|
44
|
-
var channel: String
|
|
45
|
-
var appId: String
|
|
46
|
-
var syncOnAdd: Bool
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
16
|
}
|
|
50
|
-
|
|
51
|
-
extension PortalsConfig {
|
|
52
|
-
init?(_ dict: [String: Any]) {
|
|
53
|
-
guard let rawPortals = dict["portals"] as? [[String: Any]] else {
|
|
54
|
-
print("Portals configuration must contain a 'portals' property.")
|
|
55
|
-
return nil
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
let portals = rawPortals.compactMap(Portal.init)
|
|
59
|
-
guard portals.count == rawPortals.count else {
|
|
60
|
-
print("Invalid portals configuration.")
|
|
61
|
-
return nil
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
self.portals = portals
|
|
65
|
-
registrationKey = dict["registrationKey"] as? String
|
|
66
|
-
secureLiveUpdatesPublicKey = dict["liveUpdatesKey"] as? String
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
extension PortalsConfig.Portal {
|
|
71
|
-
init?(_ dict: [String: Any]) {
|
|
72
|
-
guard let name = dict["name"] as? String else {
|
|
73
|
-
print("Portal confifguration must contain a 'name' property.")
|
|
74
|
-
return nil
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
self.name = name
|
|
78
|
-
startDir = dict["startDir"] as? String
|
|
79
|
-
index = dict["index"] as? String
|
|
80
|
-
initialContext = (dict["initialContext"] as? [String: Any])
|
|
81
|
-
.flatMap { JSTypes.coerceDictionaryToJSObject($0) }
|
|
82
|
-
plugins = (dict["plugins"] as? Array<[String: String]>)
|
|
83
|
-
.flatMap { $0.compactMap(ReactNativePortals.Portal.Plugin.init) }
|
|
84
|
-
assetMaps = (dict["assetMaps"] as? Array<[String: String]>)
|
|
85
|
-
.flatMap { $0.compactMap(AssetMap.init) }
|
|
86
|
-
liveUpdate = (dict["liveUpdate"] as? [String: Any])
|
|
87
|
-
.flatMap(LiveUpdate.init)
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
extension PortalsConfig.Portal.LiveUpdate {
|
|
92
|
-
init?(_ dict: [String: Any]) {
|
|
93
|
-
guard let appId = dict["appId"] as? String else {
|
|
94
|
-
print("LiveUpdate configuration must contain an 'appId' property.")
|
|
95
|
-
return nil
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
self.appId = appId
|
|
99
|
-
channel = dict["channel"] as? String ?? "production"
|
|
100
|
-
syncOnAdd = dict["syncOnAdd"] as? Bool ?? true
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
@@ -1,42 +1,33 @@
|
|
|
1
1
|
import Foundation
|
|
2
|
+
import Capacitor
|
|
2
3
|
import IonicPortals
|
|
3
4
|
import IonicLiveUpdates
|
|
4
5
|
import React
|
|
5
6
|
|
|
6
7
|
@objc(IONPortalsReactNative)
|
|
7
8
|
public class PortalsReactNative: NSObject {
|
|
8
|
-
private var lum: LiveUpdateManager
|
|
9
|
+
internal private(set) static var lum: LiveUpdateManager = .shared
|
|
10
|
+
@available(*, deprecated, message: "This will be removed in the next release")
|
|
9
11
|
internal static var portals = ConcurrentDictionary<String, Portal>(label: "com.portals.reactnative", dict: [:])
|
|
12
|
+
let encoder = JSValueEncoder(optionalEncodingStrategy: .undefined)
|
|
13
|
+
let decoder = JSValueDecoder()
|
|
10
14
|
|
|
11
15
|
public override init() {
|
|
12
16
|
guard let configUrl = Bundle.main.url(forResource: "portals.config.json", withExtension: nil) else {
|
|
13
|
-
lum = .shared
|
|
14
17
|
return
|
|
15
18
|
}
|
|
16
19
|
|
|
17
20
|
guard let configData = try? Data(contentsOf: configUrl),
|
|
18
|
-
let
|
|
19
|
-
let portalsConfig = PortalsConfig(jsonData)
|
|
21
|
+
let portalsConfig = try? JSONDecoder().decode(PortalsConfig.self, from: configData)
|
|
20
22
|
else { fatalError("Portals config data is malformed. Aborting.") }
|
|
21
23
|
|
|
22
24
|
if let registrationKey = portalsConfig.registrationKey {
|
|
23
25
|
PortalsRegistrationManager.shared.register(key: registrationKey)
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
let liveUpdateManager: LiveUpdateManager
|
|
27
28
|
if let publicKeyPath = portalsConfig.secureLiveUpdatesPublicKey {
|
|
28
29
|
guard let publicKeyUrl = Bundle.main.url(forResource: publicKeyPath, withExtension: nil) else { fatalError("Public key not found at \(publicKeyPath)") }
|
|
29
|
-
|
|
30
|
-
} else {
|
|
31
|
-
liveUpdateManager = .shared
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
lum = liveUpdateManager
|
|
35
|
-
|
|
36
|
-
let portals = portalsConfig.portals.map { $0.portal(with: liveUpdateManager) }
|
|
37
|
-
|
|
38
|
-
for portal in portals {
|
|
39
|
-
Self.portals[portal.name] = portal
|
|
30
|
+
Self.lum = SecureLiveUpdateManager(named: "secure-updates", publicKeyUrl: publicKeyUrl)
|
|
40
31
|
}
|
|
41
32
|
}
|
|
42
33
|
|
|
@@ -47,39 +38,53 @@ public class PortalsReactNative: NSObject {
|
|
|
47
38
|
|
|
48
39
|
@objc func enableSecureLiveUpdates(_ publicKeyPath: String, resolver: RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) {
|
|
49
40
|
guard let publicKeyUrl = Bundle.main.url(forResource: publicKeyPath, withExtension: nil) else { fatalError("Public key not found at \(publicKeyPath)") }
|
|
50
|
-
lum = SecureLiveUpdateManager(named: "secure-updates", publicKeyUrl: publicKeyUrl)
|
|
41
|
+
Self.lum = SecureLiveUpdateManager(named: "secure-updates", publicKeyUrl: publicKeyUrl)
|
|
51
42
|
resolver(())
|
|
52
43
|
}
|
|
53
44
|
|
|
45
|
+
@available(*, deprecated, message: "This will be removed in the next release")
|
|
54
46
|
@objc func addPortal(_ portalDict: [String: Any], resolver: RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
47
|
+
do {
|
|
48
|
+
let portal = try Portal.decode(from: JSTypes.coerceDictionaryToJSObject(portalDict) ?? [:], with: decoder)
|
|
49
|
+
Self.portals[portal.name] = portal
|
|
50
|
+
resolver(try encoder.encode(portal))
|
|
51
|
+
} catch {
|
|
52
|
+
rejector(nil, "Invalid Portal configuration", error)
|
|
53
|
+
}
|
|
58
54
|
}
|
|
59
55
|
|
|
56
|
+
@available(*, deprecated, message: "This will be removed in the next release")
|
|
60
57
|
@objc func addPortals(_ portalsArray: [[String: Any]], resolver: RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
58
|
+
do {
|
|
59
|
+
let portals = try decoder.decode([Portal].self, from: JSTypes.coerceArrayToJSArray(portalsArray) ?? [])
|
|
60
|
+
for portal in portals {
|
|
61
|
+
Self.portals[portal.name] = portal
|
|
62
|
+
}
|
|
63
|
+
resolver(try encoder.encode(portals))
|
|
64
|
+
} catch {
|
|
65
|
+
rejector(nil, "Invalid Portal configuration", error)
|
|
65
66
|
}
|
|
66
|
-
|
|
67
|
-
resolver(portals.map(\.dict))
|
|
68
67
|
}
|
|
69
68
|
|
|
69
|
+
@available(*, deprecated, message: "This will be removed in the next release")
|
|
70
70
|
static func getPortal(named name: String) -> Portal? { portals[name] }
|
|
71
71
|
|
|
72
|
+
@available(*, deprecated, message: "This will be removed in the next release")
|
|
72
73
|
@objc func getPortal(_ name: String, resolver: RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) {
|
|
73
74
|
guard let portal = Self.getPortal(named: name) else { return rejector(nil, "Portal named \(name) not registered", nil) }
|
|
74
|
-
|
|
75
|
+
do {
|
|
76
|
+
resolver(try encoder.encode(portal))
|
|
77
|
+
} catch {
|
|
78
|
+
rejector(nil, "Invalid Portal configuration", error)
|
|
79
|
+
}
|
|
75
80
|
}
|
|
76
81
|
|
|
77
82
|
@objc func syncOne(_ appId: String, resolver: @escaping RCTPromiseResolveBlock, rejector: @escaping RCTPromiseRejectBlock) {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
resolver(
|
|
82
|
-
|
|
83
|
+
Task {
|
|
84
|
+
do {
|
|
85
|
+
let result = try await Self.lum.sync(appId: appId)
|
|
86
|
+
resolver(try? JSValueEncoder(optionalEncodingStrategy: .undefined).encode(result))
|
|
87
|
+
} catch {
|
|
83
88
|
rejector(nil, nil, error)
|
|
84
89
|
}
|
|
85
90
|
}
|
|
@@ -87,15 +92,15 @@ public class PortalsReactNative: NSObject {
|
|
|
87
92
|
|
|
88
93
|
@objc func syncSome(_ appIds: [String], resolver: @escaping RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) {
|
|
89
94
|
Task {
|
|
90
|
-
let syncResult = await lum.syncSome(appIds)
|
|
91
|
-
resolver(syncResult.dict)
|
|
95
|
+
let syncResult = await Self.lum.syncSome(appIds)
|
|
96
|
+
resolver(try? syncResult.dict)
|
|
92
97
|
}
|
|
93
98
|
}
|
|
94
99
|
|
|
95
100
|
@objc func syncAll(_ resolver: @escaping RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) {
|
|
96
101
|
Task {
|
|
97
|
-
let syncResult = await lum.syncAll()
|
|
98
|
-
resolver(syncResult.dict)
|
|
102
|
+
let syncResult = await Self.lum.syncAll()
|
|
103
|
+
resolver(try? syncResult.dict)
|
|
99
104
|
}
|
|
100
105
|
}
|
|
101
106
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
//
|
|
2
|
+
// SyncResult+Dict.swift
|
|
3
|
+
// ReactNativePortals
|
|
4
|
+
//
|
|
5
|
+
// Created by Steven Sherry on 3/28/23.
|
|
6
|
+
// Copyright © 2023 Facebook. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import IonicLiveUpdates
|
|
10
|
+
|
|
11
|
+
extension LiveUpdateManager.SyncResult: Encodable {
|
|
12
|
+
enum TopLevelKeys: String, CodingKey { case liveUpdate, snapshot, source, activeApplicationPathChanged }
|
|
13
|
+
enum LiveUpdateKeys: String, CodingKey { case appId, channel }
|
|
14
|
+
enum SnapshotKeys: String, CodingKey { case id, buildId }
|
|
15
|
+
|
|
16
|
+
public func encode(to encoder: Encoder) throws {
|
|
17
|
+
var topLevelContainer = encoder.container(keyedBy: TopLevelKeys.self)
|
|
18
|
+
|
|
19
|
+
var liveUpdateContainer = topLevelContainer.nestedContainer(keyedBy: LiveUpdateKeys.self, forKey: .liveUpdate)
|
|
20
|
+
try liveUpdateContainer.encode(liveUpdate.appId, forKey: .appId)
|
|
21
|
+
try liveUpdateContainer.encode(liveUpdate.channel, forKey: .channel)
|
|
22
|
+
|
|
23
|
+
if let snapshot {
|
|
24
|
+
var snapshotContainer = topLevelContainer.nestedContainer(keyedBy: SnapshotKeys.self, forKey: .snapshot)
|
|
25
|
+
try snapshotContainer.encode(snapshot.id, forKey: .id)
|
|
26
|
+
try snapshotContainer.encode(snapshot.buildId, forKey: .buildId)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if case let .cache(pathsChanged) = source {
|
|
30
|
+
try topLevelContainer.encode("cache", forKey: .source)
|
|
31
|
+
try topLevelContainer.encode(pathsChanged, forKey: .activeApplicationPathChanged)
|
|
32
|
+
} else {
|
|
33
|
+
try topLevelContainer.encode("download", forKey: .source)
|
|
34
|
+
try topLevelContainer.encode(true, forKey: .activeApplicationPathChanged)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
extension LiveUpdateManager.SyncError: Encodable {
|
|
40
|
+
enum CodingKeys: String, CodingKey { case appId, failStep, message }
|
|
41
|
+
|
|
42
|
+
public func encode(to encoder: Encoder) throws {
|
|
43
|
+
var container = encoder.container(keyedBy: CodingKeys.self)
|
|
44
|
+
let failStep: String
|
|
45
|
+
if case .secureUpdateError = reason {
|
|
46
|
+
failStep = "VERIFY"
|
|
47
|
+
} else {
|
|
48
|
+
failStep = self.failStep.rawValue.uppercased()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try container.encode(appId, forKey: .appId)
|
|
52
|
+
try container.encode(failStep, forKey: .failStep)
|
|
53
|
+
try container.encode(errorDescription, forKey: .message)
|
|
54
|
+
}
|
|
55
|
+
}
|
package/ios/WebVitals.swift
CHANGED
|
@@ -8,10 +8,27 @@
|
|
|
8
8
|
|
|
9
9
|
import IonicPortals
|
|
10
10
|
import React
|
|
11
|
+
import Combine
|
|
11
12
|
|
|
12
13
|
@objc(IONPortalsWebVitals)
|
|
13
14
|
class WebVitals: RCTEventEmitter {
|
|
14
15
|
private let fcp = "vitals:fcp"
|
|
16
|
+
private var subscription: AnyCancellable?
|
|
17
|
+
|
|
18
|
+
override init() {
|
|
19
|
+
super.init()
|
|
20
|
+
subscription = IonicPortals.PortalsPubSub
|
|
21
|
+
.shared
|
|
22
|
+
.publisher(for: "webVitals:received")
|
|
23
|
+
.data()
|
|
24
|
+
.sink { [weak self] data in
|
|
25
|
+
guard let self = self else { return }
|
|
26
|
+
self.sendEvent(
|
|
27
|
+
withName: self.fcp,
|
|
28
|
+
body: data
|
|
29
|
+
)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
15
32
|
|
|
16
33
|
override func supportedEvents() -> [String] {
|
|
17
34
|
[fcp]
|
|
@@ -21,33 +38,9 @@ class WebVitals: RCTEventEmitter {
|
|
|
21
38
|
guard var portal = PortalsReactNative.portals[portalName] else {
|
|
22
39
|
return resolver(())
|
|
23
40
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
switch plugin {
|
|
27
|
-
case .instance(let plugin):
|
|
28
|
-
return type(of: plugin) != WebVitalsPlugin.self
|
|
29
|
-
case .type:
|
|
30
|
-
return true
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
var vitalsPlugin = WebVitalsPlugin { [weak self] _, duration in
|
|
36
|
-
guard let self = self else { return }
|
|
37
|
-
self.sendEvent(
|
|
38
|
-
withName: self.fcp,
|
|
39
|
-
body: [
|
|
40
|
-
"portalName": portalName,
|
|
41
|
-
"duration": duration
|
|
42
|
-
]
|
|
43
|
-
)
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
portalPlugins.append(.instance(vitalsPlugin))
|
|
47
|
-
portal._portal.plugins = portalPlugins
|
|
48
|
-
|
|
41
|
+
|
|
42
|
+
portal.usesWebVitals = true
|
|
49
43
|
PortalsReactNative.portals[portalName] = portal
|
|
50
|
-
|
|
51
44
|
resolver(())
|
|
52
45
|
}
|
|
53
46
|
|