@ionic/portals-react-native 0.1.0 → 0.2.0-1
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/README.md +11 -159
- package/ReactNativePortals.podspec +2 -1
- package/android/build.gradle +1 -1
- package/android/src/main/java/io/ionic/portals/reactnative/PortalView.kt +132 -0
- package/android/src/main/java/io/ionic/portals/reactnative/ReactNativeLiveUpdatesModule.kt +10 -28
- package/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalManager.kt +149 -0
- package/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalsModule.kt +73 -232
- package/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalsPackage.kt +0 -1
- package/android/src/main/java/io/ionic/portals/reactnative/ReactNativePortalsPubSub.kt +45 -0
- package/ios/LiveUpdate+Dict.swift +30 -0
- package/ios/LiveUpdateManager+Async.swift +69 -0
- package/ios/LiveUpdateManagerError+Dict.swift +19 -0
- package/ios/Podfile +2 -1
- package/ios/Podfile.lock +8 -7
- package/ios/Portal+Dict.swift +35 -0
- package/ios/PortalManager.m +10 -4
- package/ios/PortalView.m +1 -1
- package/ios/PortalView.swift +67 -0
- package/ios/PortalsConfig.swift +92 -0
- package/ios/PortalsPubSub.m +3 -3
- package/ios/PortalsPubSub.swift +47 -0
- package/ios/PortalsReactNative.swift +104 -0
- package/ios/ReactNativePortals.xcodeproj/project.pbxproj +32 -8
- package/lib/commonjs/index.js +55 -13
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/index.js +45 -12
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/index.d.ts +27 -2
- package/package.json +3 -1
- package/src/index.ts +47 -12
- package/ios/LiveUpdatesManager.m +0 -16
- package/ios/ReactNativePortals.swift +0 -262
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
//
|
|
2
|
+
// PortalView.swift
|
|
3
|
+
// ReactNativePortals
|
|
4
|
+
//
|
|
5
|
+
// Created by Steven Sherry on 10/5/22.
|
|
6
|
+
// Copyright © 2022 Ionic. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import UIKit
|
|
10
|
+
import Capacitor
|
|
11
|
+
import IonicPortals
|
|
12
|
+
import React
|
|
13
|
+
|
|
14
|
+
@objc(IONPortalViewManager)
|
|
15
|
+
class PortalViewManager: RCTViewManager {
|
|
16
|
+
override class func requiresMainQueueSetup() -> Bool { true }
|
|
17
|
+
override func view() -> UIView! { PortalView() }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
class PortalView: UIView {
|
|
21
|
+
private var webView: PortalUIView?
|
|
22
|
+
|
|
23
|
+
@objc var portal: [String: Any]? {
|
|
24
|
+
get {
|
|
25
|
+
guard let _portal = _portal else { return nil }
|
|
26
|
+
return [
|
|
27
|
+
"name": _portal.name,
|
|
28
|
+
"initialContext": _portal.initialContext
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
set {
|
|
33
|
+
guard let portalDict = newValue,
|
|
34
|
+
let name = portalDict["name"] as? String
|
|
35
|
+
else { return }
|
|
36
|
+
|
|
37
|
+
var portal = PortalsReactNative.getPortal(named: name)
|
|
38
|
+
|
|
39
|
+
if let initialContext = portalDict["initialContext"] as? [String: Any] {
|
|
40
|
+
portal?.initialContext = JSTypes.coerceDictionaryToJSObject(initialContext) ?? [:]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
_portal = portal
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private var _portal: Portal? {
|
|
48
|
+
didSet {
|
|
49
|
+
guard let portal = _portal else { return }
|
|
50
|
+
|
|
51
|
+
DispatchQueue.main.async { [weak self] in
|
|
52
|
+
guard let self = self else { return }
|
|
53
|
+
self.webView?.removeFromSuperview()
|
|
54
|
+
let webView = PortalUIView(portal: portal)
|
|
55
|
+
webView.translatesAutoresizingMaskIntoConstraints = false
|
|
56
|
+
self.addSubview(webView)
|
|
57
|
+
NSLayoutConstraint.activate([
|
|
58
|
+
webView.topAnchor.constraint(equalTo: self.topAnchor),
|
|
59
|
+
webView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
|
|
60
|
+
webView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
|
|
61
|
+
webView.trailingAnchor.constraint(equalTo: self.trailingAnchor)
|
|
62
|
+
])
|
|
63
|
+
self.webView = webView
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
//
|
|
2
|
+
// PortalsConfig.swift
|
|
3
|
+
// ReactNativePortals
|
|
4
|
+
//
|
|
5
|
+
// Created by Steven Sherry on 10/5/22.
|
|
6
|
+
// Copyright © 2022 Ionic. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import Capacitor
|
|
10
|
+
import IonicLiveUpdates
|
|
11
|
+
import IonicPortals
|
|
12
|
+
|
|
13
|
+
struct PortalsConfig {
|
|
14
|
+
var portals: [Portal]
|
|
15
|
+
var registrationKey: String?
|
|
16
|
+
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 liveUpdate: LiveUpdate?
|
|
24
|
+
|
|
25
|
+
func portal(with liveUpdateManager: LiveUpdateManager) -> IonicPortals.Portal {
|
|
26
|
+
return .init(
|
|
27
|
+
name: name,
|
|
28
|
+
startDir: startDir,
|
|
29
|
+
index: index ?? "index.html",
|
|
30
|
+
initialContext: initialContext ?? [:],
|
|
31
|
+
liveUpdateManager: liveUpdateManager,
|
|
32
|
+
liveUpdateConfig: liveUpdate.map { .init(appId: $0.appId, channel: $0.channel, syncOnAdd: $0.syncOnAdd) }
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
struct LiveUpdate {
|
|
37
|
+
var channel: String
|
|
38
|
+
var appId: String
|
|
39
|
+
var syncOnAdd: Bool
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
extension PortalsConfig {
|
|
45
|
+
init?(_ dict: [String: Any]) {
|
|
46
|
+
guard let rawPortals = dict["portals"] as? [[String: Any]] else {
|
|
47
|
+
print("Portals configuration must contain a 'portals' property.")
|
|
48
|
+
return nil
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let portals = rawPortals.compactMap(Portal.init)
|
|
52
|
+
guard portals.count == rawPortals.count else {
|
|
53
|
+
print("Invalid portals configuration.")
|
|
54
|
+
return nil
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
self.portals = portals
|
|
58
|
+
registrationKey = dict["registrationKey"] as? String
|
|
59
|
+
secureLiveUpdatesPublicKey = dict["liveUpdatesKey"] as? String
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
extension PortalsConfig.Portal {
|
|
64
|
+
init?(_ dict: [String: Any]) {
|
|
65
|
+
guard let name = dict["name"] as? String else {
|
|
66
|
+
print("Portal confifguration must contain a 'name' property.")
|
|
67
|
+
return nil
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
self.name = name
|
|
71
|
+
startDir = dict["startDir"] as? String
|
|
72
|
+
index = dict["index"] as? String
|
|
73
|
+
initialContext = (dict["initialContext"] as? [String: Any])
|
|
74
|
+
.flatMap { JSTypes.coerceDictionaryToJSObject($0) }
|
|
75
|
+
liveUpdate = (dict["liveUpdate"] as? [String: Any])
|
|
76
|
+
.flatMap(LiveUpdate.init)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
extension PortalsConfig.Portal.LiveUpdate {
|
|
81
|
+
init?(_ dict: [String: Any]) {
|
|
82
|
+
guard let appId = dict["appId"] as? String else {
|
|
83
|
+
print("LiveUpdate configuration must contain an 'appId' property.")
|
|
84
|
+
return nil
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
self.appId = appId
|
|
88
|
+
channel = dict["channel"] as? String ?? "production"
|
|
89
|
+
syncOnAdd = dict["syncOnAdd"] as? Bool ?? true
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
package/ios/PortalsPubSub.m
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// ReactNativePortals
|
|
4
4
|
//
|
|
5
5
|
// Created by Steven Sherry on 4/1/22.
|
|
6
|
-
// Copyright © 2022
|
|
6
|
+
// Copyright © 2022 Ionic. All rights reserved.
|
|
7
7
|
//
|
|
8
8
|
|
|
9
9
|
#import <React/RCTBridgeModule.h>
|
|
@@ -11,6 +11,6 @@
|
|
|
11
11
|
|
|
12
12
|
@interface RCT_EXTERN_MODULE(IONPortalPubSub, RCTEventEmitter)
|
|
13
13
|
RCT_EXTERN_METHOD(subscribe: (NSString *) topic resolver: (RCTPromiseResolveBlock) resolver rejector: (RCTPromiseRejectBlock) rejector)
|
|
14
|
-
RCT_EXTERN_METHOD(unsubscribe: (NSString *) topic subscriptionRef: (NSNumber _Nonnull) subscriptionRef)
|
|
15
|
-
RCT_EXTERN_METHOD(publish: (NSString *) topic data: (id) data)
|
|
14
|
+
RCT_EXTERN_METHOD(unsubscribe: (NSString *) topic subscriptionRef: (NSNumber _Nonnull) subscriptionRef resolver: (RCTPromiseResolveBlock) resolver rejector: (RCTPromiseRejectBlock) rejector)
|
|
15
|
+
RCT_EXTERN_METHOD(publish: (NSString *) topic data: (id) data resolver: (RCTPromiseResolveBlock) resolver rejector: (RCTPromiseRejectBlock) rejector)
|
|
16
16
|
@end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
//
|
|
2
|
+
// PortalsPubSub.swift
|
|
3
|
+
// ReactNativePortals
|
|
4
|
+
//
|
|
5
|
+
// Created by Steven Sherry on 10/5/22.
|
|
6
|
+
// Copyright © 2022 Ionic. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import IonicPortals
|
|
10
|
+
import React
|
|
11
|
+
|
|
12
|
+
@objc(IONPortalPubSub)
|
|
13
|
+
class PortalsPubSub: RCTEventEmitter {
|
|
14
|
+
private let eventName = "PortalsSubscription"
|
|
15
|
+
|
|
16
|
+
override func supportedEvents() -> [String]! {
|
|
17
|
+
[eventName]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@objc func subscribe(_ topic: String, resolver: RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) {
|
|
21
|
+
let subRef = IonicPortals.PortalsPubSub.subscribe(topic) { [weak self] result in
|
|
22
|
+
guard let self = self else { return }
|
|
23
|
+
self.sendEvent(
|
|
24
|
+
withName: self.eventName,
|
|
25
|
+
body: [
|
|
26
|
+
"subscriptionRef": result.subscriptionRef,
|
|
27
|
+
"topic": result.topic,
|
|
28
|
+
"data": result.data
|
|
29
|
+
]
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
resolver(subRef)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@objc func unsubscribe(_ topic: String, subscriptionRef: NSNumber, resolver: RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) {
|
|
37
|
+
IonicPortals.PortalsPubSub.unsubscribe(from: topic, subscriptionRef: subscriptionRef.intValue)
|
|
38
|
+
resolver(())
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@objc func publish(_ topic: String, data: Any, resolver: RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) {
|
|
42
|
+
IONPortalsPubSub.publish(message: data, topic: topic)
|
|
43
|
+
resolver(())
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
override class func requiresMainQueueSetup() -> Bool { true }
|
|
47
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import IonicPortals
|
|
3
|
+
import IonicLiveUpdates
|
|
4
|
+
import React
|
|
5
|
+
|
|
6
|
+
@objc(IONPortalsReactNative)
|
|
7
|
+
public class PortalsReactNative: NSObject {
|
|
8
|
+
private var lum: LiveUpdateManager
|
|
9
|
+
private static var portals: [String: Portal] = [:]
|
|
10
|
+
|
|
11
|
+
public override init() {
|
|
12
|
+
guard let configUrl = Bundle.main.url(forResource: "portals.config.json", withExtension: nil) else {
|
|
13
|
+
lum = .shared
|
|
14
|
+
return
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
guard let configData = try? Data(contentsOf: configUrl),
|
|
18
|
+
let jsonData = try? JSONSerialization.jsonObject(with: configData) as? [String: Any],
|
|
19
|
+
let portalsConfig = PortalsConfig(jsonData)
|
|
20
|
+
else { fatalError("Portals config data is malformed. Aborting.") }
|
|
21
|
+
|
|
22
|
+
if let registrationKey = portalsConfig.registrationKey {
|
|
23
|
+
PortalsRegistrationManager.shared.register(key: registrationKey)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let liveUpdateManager: LiveUpdateManager
|
|
27
|
+
if let publicKeyPath = portalsConfig.secureLiveUpdatesPublicKey {
|
|
28
|
+
guard let publicKeyUrl = Bundle.main.url(forResource: publicKeyPath, withExtension: nil) else { fatalError("Public key not found at \(publicKeyPath)") }
|
|
29
|
+
liveUpdateManager = SecureLiveUpdateManager(named: "secure-updates", publicKeyUrl: publicKeyUrl)
|
|
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
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@objc func register(_ key: String, resolver: RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) {
|
|
44
|
+
PortalsRegistrationManager.shared.register(key: key)
|
|
45
|
+
resolver(())
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@objc func enableSecureLiveUpdates(_ publicKeyPath: String, resolver: RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) {
|
|
49
|
+
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)
|
|
51
|
+
resolver(())
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@objc func addPortal(_ portalDict: [String: Any], resolver: RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) {
|
|
55
|
+
guard let portal = Portal(portalDict, lum) else { return rejector(nil, "Invalid Portal configuration", nil) }
|
|
56
|
+
Self.portals[portal.name] = portal
|
|
57
|
+
resolver(portal.dict)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@objc func addPortals(_ portalsArray: [[String: Any]], resolver: RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) {
|
|
61
|
+
let portals = portalsArray.compactMap { Portal($0, lum) }
|
|
62
|
+
|
|
63
|
+
for portal in portals {
|
|
64
|
+
Self.portals[portal.name] = portal
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
resolver(portals.map(\.dict))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
static func getPortal(named name: String) -> Portal? { portals[name] }
|
|
71
|
+
|
|
72
|
+
@objc func getPortal(_ name: String, resolver: RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) {
|
|
73
|
+
guard let portal = Self.getPortal(named: name) else { return rejector(nil, "Portal named \(name) not registered", nil) }
|
|
74
|
+
resolver(portal.dict)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@objc func syncOne(_ appId: String, resolver: @escaping RCTPromiseResolveBlock, rejector: @escaping RCTPromiseRejectBlock) {
|
|
78
|
+
lum.sync(appId: appId, isParallel: true) { result in
|
|
79
|
+
switch result {
|
|
80
|
+
case .success(let update):
|
|
81
|
+
resolver(update.dict)
|
|
82
|
+
case .failure(let error):
|
|
83
|
+
rejector(nil, nil, error)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@objc func syncSome(_ appIds: [String], resolver: @escaping RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) {
|
|
89
|
+
Task {
|
|
90
|
+
let syncResult = await lum.syncSome(appIds)
|
|
91
|
+
resolver(syncResult.dict)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
@objc func syncAll(_ resolver: @escaping RCTPromiseResolveBlock, rejector: RCTPromiseRejectBlock) {
|
|
96
|
+
Task {
|
|
97
|
+
let syncResult = await lum.syncAll()
|
|
98
|
+
resolver(syncResult.dict)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@objc static func requiresMainQueueSetup() -> Bool { true }
|
|
104
|
+
}
|
|
@@ -8,12 +8,18 @@
|
|
|
8
8
|
|
|
9
9
|
/* Begin PBXBuildFile section */
|
|
10
10
|
24F0245B69703967E2679F6A /* Pods_ReactNativePortals.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0529BADF230AAE24E9A25425 /* Pods_ReactNativePortals.framework */; };
|
|
11
|
-
A7128A0627F7A16200DADDF3 /*
|
|
11
|
+
A7128A0627F7A16200DADDF3 /* PortalsReactNative.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4FF95D6245B92E800C19C63 /* PortalsReactNative.swift */; };
|
|
12
12
|
A7128A0727F7A16200DADDF3 /* PortalManager.m in Sources */ = {isa = PBXBuildFile; fileRef = A71289F927F79EB200DADDF3 /* PortalManager.m */; };
|
|
13
13
|
A7128A0827F7A16200DADDF3 /* ReactNativePortals-Bridging-Header.h in Headers */ = {isa = PBXBuildFile; fileRef = F4FF95D5245B92E700C19C63 /* ReactNativePortals-Bridging-Header.h */; };
|
|
14
14
|
A7128A0927F7A16200DADDF3 /* PortalsPubSub.m in Sources */ = {isa = PBXBuildFile; fileRef = A71289F827F79D4000DADDF3 /* PortalsPubSub.m */; };
|
|
15
15
|
A7128A0A27F7A16200DADDF3 /* PortalView.m in Sources */ = {isa = PBXBuildFile; fileRef = A71289F727F79CDC00DADDF3 /* PortalView.m */; };
|
|
16
|
-
|
|
16
|
+
A795AE1928EDEB6D0092B4B1 /* PortalsConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A795AE1828EDEB6D0092B4B1 /* PortalsConfig.swift */; };
|
|
17
|
+
A795AE1B28EDEBF20092B4B1 /* PortalsPubSub.swift in Sources */ = {isa = PBXBuildFile; fileRef = A795AE1A28EDEBF20092B4B1 /* PortalsPubSub.swift */; };
|
|
18
|
+
A795AE1D28EDEC7A0092B4B1 /* PortalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A795AE1C28EDEC7A0092B4B1 /* PortalView.swift */; };
|
|
19
|
+
A795AE1F28EDED0F0092B4B1 /* Portal+Dict.swift in Sources */ = {isa = PBXBuildFile; fileRef = A795AE1E28EDED0F0092B4B1 /* Portal+Dict.swift */; };
|
|
20
|
+
A795AE2128EDED710092B4B1 /* LiveUpdate+Dict.swift in Sources */ = {isa = PBXBuildFile; fileRef = A795AE2028EDED710092B4B1 /* LiveUpdate+Dict.swift */; };
|
|
21
|
+
A795AE2328EDEDCC0092B4B1 /* LiveUpdateManagerError+Dict.swift in Sources */ = {isa = PBXBuildFile; fileRef = A795AE2228EDEDCC0092B4B1 /* LiveUpdateManagerError+Dict.swift */; };
|
|
22
|
+
A795AE2528EDEE4E0092B4B1 /* LiveUpdateManager+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = A795AE2428EDEE4E0092B4B1 /* LiveUpdateManager+Async.swift */; };
|
|
17
23
|
/* End PBXBuildFile section */
|
|
18
24
|
|
|
19
25
|
/* Begin PBXFileReference section */
|
|
@@ -22,10 +28,16 @@
|
|
|
22
28
|
A71289F827F79D4000DADDF3 /* PortalsPubSub.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PortalsPubSub.m; sourceTree = "<group>"; };
|
|
23
29
|
A71289F927F79EB200DADDF3 /* PortalManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PortalManager.m; sourceTree = "<group>"; };
|
|
24
30
|
A71289FF27F7A14900DADDF3 /* ReactNativePortals.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ReactNativePortals.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
25
|
-
|
|
31
|
+
A795AE1828EDEB6D0092B4B1 /* PortalsConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PortalsConfig.swift; sourceTree = "<group>"; };
|
|
32
|
+
A795AE1A28EDEBF20092B4B1 /* PortalsPubSub.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PortalsPubSub.swift; sourceTree = "<group>"; };
|
|
33
|
+
A795AE1C28EDEC7A0092B4B1 /* PortalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PortalView.swift; sourceTree = "<group>"; };
|
|
34
|
+
A795AE1E28EDED0F0092B4B1 /* Portal+Dict.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Portal+Dict.swift"; sourceTree = "<group>"; };
|
|
35
|
+
A795AE2028EDED710092B4B1 /* LiveUpdate+Dict.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LiveUpdate+Dict.swift"; sourceTree = "<group>"; };
|
|
36
|
+
A795AE2228EDEDCC0092B4B1 /* LiveUpdateManagerError+Dict.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LiveUpdateManagerError+Dict.swift"; sourceTree = "<group>"; };
|
|
37
|
+
A795AE2428EDEE4E0092B4B1 /* LiveUpdateManager+Async.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "LiveUpdateManager+Async.swift"; sourceTree = "<group>"; };
|
|
26
38
|
CB9439A9444D2E97DA3B8149 /* Pods-ReactNativePortals.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativePortals.debug.xcconfig"; path = "Target Support Files/Pods-ReactNativePortals/Pods-ReactNativePortals.debug.xcconfig"; sourceTree = "<group>"; };
|
|
27
39
|
F4FF95D5245B92E700C19C63 /* ReactNativePortals-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ReactNativePortals-Bridging-Header.h"; sourceTree = "<group>"; };
|
|
28
|
-
F4FF95D6245B92E800C19C63 /*
|
|
40
|
+
F4FF95D6245B92E800C19C63 /* PortalsReactNative.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PortalsReactNative.swift; sourceTree = "<group>"; };
|
|
29
41
|
F8836E0DEEAAB66F82FCC5C9 /* Pods-ReactNativePortals.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ReactNativePortals.release.xcconfig"; path = "Target Support Files/Pods-ReactNativePortals/Pods-ReactNativePortals.release.xcconfig"; sourceTree = "<group>"; };
|
|
30
42
|
/* End PBXFileReference section */
|
|
31
43
|
|
|
@@ -59,8 +71,14 @@
|
|
|
59
71
|
58B511D21A9E6C8500147676 = {
|
|
60
72
|
isa = PBXGroup;
|
|
61
73
|
children = (
|
|
62
|
-
|
|
63
|
-
|
|
74
|
+
F4FF95D6245B92E800C19C63 /* PortalsReactNative.swift */,
|
|
75
|
+
A795AE2428EDEE4E0092B4B1 /* LiveUpdateManager+Async.swift */,
|
|
76
|
+
A795AE2228EDEDCC0092B4B1 /* LiveUpdateManagerError+Dict.swift */,
|
|
77
|
+
A795AE2028EDED710092B4B1 /* LiveUpdate+Dict.swift */,
|
|
78
|
+
A795AE1E28EDED0F0092B4B1 /* Portal+Dict.swift */,
|
|
79
|
+
A795AE1C28EDEC7A0092B4B1 /* PortalView.swift */,
|
|
80
|
+
A795AE1A28EDEBF20092B4B1 /* PortalsPubSub.swift */,
|
|
81
|
+
A795AE1828EDEB6D0092B4B1 /* PortalsConfig.swift */,
|
|
64
82
|
A71289F727F79CDC00DADDF3 /* PortalView.m */,
|
|
65
83
|
A71289F927F79EB200DADDF3 /* PortalManager.m */,
|
|
66
84
|
A71289F827F79D4000DADDF3 /* PortalsPubSub.m */,
|
|
@@ -186,11 +204,17 @@
|
|
|
186
204
|
isa = PBXSourcesBuildPhase;
|
|
187
205
|
buildActionMask = 2147483647;
|
|
188
206
|
files = (
|
|
189
|
-
A748ABFC28626EC300F26852 /* LiveUpdatesManager.m in Sources */,
|
|
190
207
|
A7128A0A27F7A16200DADDF3 /* PortalView.m in Sources */,
|
|
191
208
|
A7128A0927F7A16200DADDF3 /* PortalsPubSub.m in Sources */,
|
|
192
|
-
|
|
209
|
+
A795AE1D28EDEC7A0092B4B1 /* PortalView.swift in Sources */,
|
|
210
|
+
A795AE2128EDED710092B4B1 /* LiveUpdate+Dict.swift in Sources */,
|
|
211
|
+
A795AE1B28EDEBF20092B4B1 /* PortalsPubSub.swift in Sources */,
|
|
212
|
+
A7128A0627F7A16200DADDF3 /* PortalsReactNative.swift in Sources */,
|
|
213
|
+
A795AE1F28EDED0F0092B4B1 /* Portal+Dict.swift in Sources */,
|
|
214
|
+
A795AE2328EDEDCC0092B4B1 /* LiveUpdateManagerError+Dict.swift in Sources */,
|
|
193
215
|
A7128A0727F7A16200DADDF3 /* PortalManager.m in Sources */,
|
|
216
|
+
A795AE1928EDEB6D0092B4B1 /* PortalsConfig.swift in Sources */,
|
|
217
|
+
A795AE2528EDEE4E0092B4B1 /* LiveUpdateManager+Async.swift in Sources */,
|
|
194
218
|
);
|
|
195
219
|
runOnlyForDeploymentPostprocessing = 0;
|
|
196
220
|
};
|
package/lib/commonjs/index.js
CHANGED
|
@@ -9,7 +9,7 @@ Object.defineProperty(exports, "PortalView", {
|
|
|
9
9
|
return _PortalView.default;
|
|
10
10
|
}
|
|
11
11
|
});
|
|
12
|
-
exports.unsubscribe = exports.syncSome = exports.syncOne = exports.syncAll = exports.subscribe = exports.register = exports.publish = exports.addPortal = void 0;
|
|
12
|
+
exports.unsubscribe = exports.syncSome = exports.syncOne = exports.syncAll = exports.subscribe = exports.register = exports.publish = exports.getPortal = exports.enableSecureLiveUpdates = exports.addPortals = exports.addPortal = void 0;
|
|
13
13
|
|
|
14
14
|
var _reactNative = require("react-native");
|
|
15
15
|
|
|
@@ -19,8 +19,7 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
|
|
19
19
|
|
|
20
20
|
const {
|
|
21
21
|
IONPortalPubSub,
|
|
22
|
-
|
|
23
|
-
IONLiveUpdatesManager
|
|
22
|
+
IONPortalsReactNative
|
|
24
23
|
} = _reactNative.NativeModules;
|
|
25
24
|
const PortalsPubSub = new _reactNative.NativeEventEmitter(IONPortalPubSub);
|
|
26
25
|
const subscriptionMap = new Map();
|
|
@@ -80,13 +79,14 @@ const publish = (topic, data) => {
|
|
|
80
79
|
/**
|
|
81
80
|
* Validates that a valid registration key has been procured from http://ionic.io/register-portals
|
|
82
81
|
* @param key The registration key
|
|
82
|
+
* @returns Promise<void>
|
|
83
83
|
*/
|
|
84
84
|
|
|
85
85
|
|
|
86
86
|
exports.publish = publish;
|
|
87
87
|
|
|
88
|
-
const register = key => {
|
|
89
|
-
|
|
88
|
+
const register = async key => {
|
|
89
|
+
return IONPortalsReactNative.register(key);
|
|
90
90
|
};
|
|
91
91
|
/**
|
|
92
92
|
* The configuration of a web application to be embedded in a React Native application.
|
|
@@ -99,21 +99,63 @@ exports.register = register;
|
|
|
99
99
|
* Adds a Portal to an internal registry. Must be called before attempting to render a {@link PortalView}.
|
|
100
100
|
*
|
|
101
101
|
* @param portal The portal to add to the internal registry.
|
|
102
|
+
* @returns Promise containing the Portal that was added to the registry.
|
|
102
103
|
*/
|
|
103
|
-
const addPortal = portal => {
|
|
104
|
-
|
|
104
|
+
const addPortal = async portal => {
|
|
105
|
+
return IONPortalsReactNative.addPortal(portal);
|
|
105
106
|
};
|
|
107
|
+
/**
|
|
108
|
+
* Adds all portals to an internal registry. This or {@link addPortal} must be called before attempting to render a {@link PortalView}
|
|
109
|
+
*
|
|
110
|
+
* @param portals The portals to add to the internal registry.
|
|
111
|
+
* @returns Promise containing the Portals that were added to the registry.
|
|
112
|
+
*/
|
|
113
|
+
|
|
106
114
|
|
|
107
115
|
exports.addPortal = addPortal;
|
|
108
116
|
|
|
117
|
+
const addPortals = async portals => {
|
|
118
|
+
return IONPortalsReactNative.addPortals(portals);
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Gets a {@link Portal} previously registered via {@link addPortal} or {@link addPortals}.
|
|
122
|
+
*
|
|
123
|
+
* @param name The portal name to retrieve from the internal registry.
|
|
124
|
+
* @returns Promise containing the registered {@link Portal}. If the {@link Portal} was not registered, the Promise will fail.
|
|
125
|
+
*/
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
exports.addPortals = addPortals;
|
|
129
|
+
|
|
130
|
+
const getPortal = async name => {
|
|
131
|
+
return IONPortalsReactNative.getPortal(name);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
exports.getPortal = getPortal;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Configures LiveUpdates to cyrptographically verify the contents of the downloaded bundles.
|
|
138
|
+
* This method must be called before any LiveUpdates are registered otherwise they will no longer be tracked.
|
|
139
|
+
*
|
|
140
|
+
* @param pathToKey The *relative* path to the public key for verification.
|
|
141
|
+
* This path should be the same relatibe to the main application bundle on iOS and the assets directory on Android.
|
|
142
|
+
* @returns Promise<void>
|
|
143
|
+
*/
|
|
144
|
+
const enableSecureLiveUpdates = async pathToKey => {
|
|
145
|
+
return IONPortalsReactNative.enableSecureLiveUpdates(pathToKey);
|
|
146
|
+
};
|
|
109
147
|
/**
|
|
110
148
|
* Syncs a single live update.
|
|
111
149
|
*
|
|
112
150
|
* @param appId The AppFlow application ID to sync.
|
|
113
151
|
* @returns A Promise<LiveUpdate>. A failure should result in a {@link LiveUpdateError}.
|
|
114
152
|
*/
|
|
115
|
-
|
|
116
|
-
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
exports.enableSecureLiveUpdates = enableSecureLiveUpdates;
|
|
156
|
+
|
|
157
|
+
const syncOne = async appId => {
|
|
158
|
+
return IONPortalsReactNative.syncOne(appId);
|
|
117
159
|
};
|
|
118
160
|
/**
|
|
119
161
|
* Syncs many live updates.
|
|
@@ -125,8 +167,8 @@ const syncOne = appId => {
|
|
|
125
167
|
|
|
126
168
|
exports.syncOne = syncOne;
|
|
127
169
|
|
|
128
|
-
const syncSome = appIds => {
|
|
129
|
-
return
|
|
170
|
+
const syncSome = async appIds => {
|
|
171
|
+
return IONPortalsReactNative.syncSome(appIds);
|
|
130
172
|
};
|
|
131
173
|
/**
|
|
132
174
|
* Syncs all registered LiveUpdates
|
|
@@ -136,8 +178,8 @@ const syncSome = appIds => {
|
|
|
136
178
|
|
|
137
179
|
exports.syncSome = syncSome;
|
|
138
180
|
|
|
139
|
-
const syncAll = () => {
|
|
140
|
-
return
|
|
181
|
+
const syncAll = async () => {
|
|
182
|
+
return IONPortalsReactNative.syncAll();
|
|
141
183
|
};
|
|
142
184
|
|
|
143
185
|
exports.syncAll = syncAll;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["IONPortalPubSub","
|
|
1
|
+
{"version":3,"names":["IONPortalPubSub","IONPortalsReactNative","NativeModules","PortalsPubSub","NativeEventEmitter","subscriptionMap","Map","subscribe","topic","onMessageReceived","subscriptionRef","subscriber","addListener","message","set","unsubscribe","subRef","subscription","get","undefined","remove","delete","publish","data","msg","register","key","addPortal","portal","addPortals","portals","getPortal","name","enableSecureLiveUpdates","pathToKey","syncOne","appId","syncSome","appIds","syncAll"],"sources":["index.ts"],"sourcesContent":["import {\n EmitterSubscription,\n NativeEventEmitter,\n NativeModules,\n ViewProps,\n} from 'react-native';\n\nconst { IONPortalPubSub, IONPortalsReactNative } = NativeModules;\n\nexport { default as PortalView } from './PortalView';\n\n/**\n * The data that is received from a subscription event.\n */\nexport interface Message {\n /** The unique subscription reference received from {@link subscribe}*/\n subscriptionRef: number;\n data: any;\n /** The topic the message was sent from */\n topic: string;\n}\n\nconst PortalsPubSub = new NativeEventEmitter(IONPortalPubSub);\n\nconst subscriptionMap = new Map<number, EmitterSubscription>();\n\n/**\n * Subscribes to messages for a topic\n *\n * @param topic The topic to subscribe to\n * @param onMessageReceived The callback to invoke when a message is received\n * @returns A Promise<number> containing the unique subscription reference. This will need to be stored for calling {@link unsubscribe}.\n */\nexport const subscribe = async (\n topic: string,\n onMessageReceived: (message: Message) => void\n): Promise<number> => {\n const subscriptionRef = await IONPortalPubSub.subscribe(topic);\n\n const subscriber = PortalsPubSub.addListener(\n 'PortalsSubscription',\n (message: Message) => {\n if (message.subscriptionRef === subscriptionRef) {\n onMessageReceived(message);\n }\n }\n );\n\n subscriptionMap.set(subscriptionRef, subscriber);\n\n return subscriptionRef;\n};\n\n/**\n * Unsubscribes from events for the provided topic and subscription reference\n *\n * @param topic The topic to unsubscribe from\n * @param subRef The unique subscription reference received when initially calling {@link subscribe}\n */\nexport const unsubscribe = (topic: string, subRef: number) => {\n IONPortalPubSub.unsubscribe(topic, subRef);\n\n const subscription = subscriptionMap.get(subRef);\n if (subscription !== undefined) {\n subscription.remove();\n subscriptionMap.delete(subRef);\n }\n};\n\n/**\n * Publishes a message to the provided topic\n *\n * @param topic The topic to publish the message to\n * @param data The data to publish to subscribers\n */\nexport const publish = (topic: string, data: any) => {\n const msg = { message: data };\n IONPortalPubSub.publish(topic, msg);\n};\n\n/**\n * Validates that a valid registration key has been procured from http://ionic.io/register-portals\n * @param key The registration key\n * @returns Promise<void>\n */\nexport const register = async (key: string): Promise<void> => {\n return IONPortalsReactNative.register(key);\n};\n\n/**\n * The configuration of a web application to be embedded in a React Native application.\n */\nexport interface Portal {\n /** The name of the Portal to be referenced. Must be **unique** */\n name: string;\n /** The classpath of all Capacitor plugins used in Android. (e.g. com.capacitorjs.plugins.camera.CameraPlugin) */\n androidPlugins?: string[];\n /**\n * The root directory of the web application relative to Bundle.main on iOS\n * and src/main/assets on Android. If omitted, `name` is used.\n */\n startDir?: string;\n /** The name of the initial file to load. If omitted, 'index.html' is used. */\n index?: string;\n /** Any data needed at initial render when a portal is loaded. */\n initialContext?: {\n [key: string]: any;\n };\n liveUpdate?: LiveUpdateConfig;\n}\n\n/**\n * A subset of {@link Portal} properties needed for rendering a Portal. `initialContext` can be used to override\n * any initialContext defined in the original {@link Portal} definition.\n */\nexport type PortalProp = {\n portal: Pick<Portal, 'name' | 'initialContext'>;\n};\n\n/**\n * Props needed for rendering a {@link Portal}\n */\nexport type PortalProps = PortalProp & ViewProps;\n\n/**\n * Adds a Portal to an internal registry. Must be called before attempting to render a {@link PortalView}.\n *\n * @param portal The portal to add to the internal registry.\n * @returns Promise containing the Portal that was added to the registry.\n */\nexport const addPortal = async (portal: Portal): Promise<Portal> => {\n return IONPortalsReactNative.addPortal(portal);\n};\n\n/**\n * Adds all portals to an internal registry. This or {@link addPortal} must be called before attempting to render a {@link PortalView}\n *\n * @param portals The portals to add to the internal registry.\n * @returns Promise containing the Portals that were added to the registry.\n */\nexport const addPortals = async (portals: Portal[]): Promise<Portal[]> => {\n return IONPortalsReactNative.addPortals(portals);\n};\n\n/**\n * Gets a {@link Portal} previously registered via {@link addPortal} or {@link addPortals}.\n *\n * @param name The portal name to retrieve from the internal registry.\n * @returns Promise containing the registered {@link Portal}. If the {@link Portal} was not registered, the Promise will fail.\n */\nexport const getPortal = async (name: string): Promise<Portal> => {\n return IONPortalsReactNative.getPortal(name);\n};\n\nexport interface LiveUpdate {\n /** The AppFlow application ID */\n appId: string;\n /** The AppFlow distribution channel */\n channel: string;\n}\n\n/** Data needed to register a live update to be managed */\nexport type LiveUpdateConfig = LiveUpdate & { syncOnAdd: boolean };\n\nexport interface LiveUpdateError {\n /** The AppFlow application ID relating to the failure */\n appId: string;\n /** The step in the sync process the LiveUpdate failed on. (e.g. CHECK, UNPACK)*/\n failStep: string;\n /** A human readable error message */\n message: string;\n}\n\n/** Used for communicating sync results of multiple live updates */\nexport interface SyncResults {\n liveUpdates: LiveUpdate[];\n errors: LiveUpdateError[];\n}\n\n/**\n * Configures LiveUpdates to cyrptographically verify the contents of the downloaded bundles.\n * This method must be called before any LiveUpdates are registered otherwise they will no longer be tracked.\n *\n * @param pathToKey The *relative* path to the public key for verification.\n * This path should be the same relatibe to the main application bundle on iOS and the assets directory on Android.\n * @returns Promise<void>\n */\nexport const enableSecureLiveUpdates = async (\n pathToKey: string\n): Promise<void> => {\n return IONPortalsReactNative.enableSecureLiveUpdates(pathToKey);\n};\n\n/**\n * Syncs a single live update.\n *\n * @param appId The AppFlow application ID to sync.\n * @returns A Promise<LiveUpdate>. A failure should result in a {@link LiveUpdateError}.\n */\nexport const syncOne = async (appId: string): Promise<LiveUpdate> => {\n return IONPortalsReactNative.syncOne(appId);\n};\n\n/**\n * Syncs many live updates.\n *\n * @param appIds The AppFlow application IDs to sync.\n * @returns Promise<SyncResults>\n */\nexport const syncSome = async (appIds: string[]): Promise<SyncResults> => {\n return IONPortalsReactNative.syncSome(appIds);\n};\n\n/**\n * Syncs all registered LiveUpdates\n * @returns Promise<SyncResults>\n */\nexport const syncAll = async (): Promise<SyncResults> => {\n return IONPortalsReactNative.syncAll();\n};\n"],"mappings":"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"}
|