Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions IonicPortals.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ Pod::Spec.new do |s|
s.source_files = 'Sources/IonicPortals/**/*.swift'
s.dependency 'Capacitor', '~> 8.0.0'
s.dependency 'IonicLiveUpdates', '>= 0.5.0', '< 0.6.0'
s.dependency 'LiveUpdateProvider', '~> 0.1.0-alpha.1'
s.swift_version = '5.7'
end
8 changes: 5 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.5
// swift-tools-version:5.6

import PackageDescription

Expand All @@ -14,15 +14,17 @@ let package = Package(
dependencies: [
.package(url: "https://github.com/ionic-team/capacitor-swift-pm", .upToNextMajor(from: "8.0.0")),
.package(url: "https://github.com/ionic-team/ionic-live-updates-releases", "0.5.0"..<"0.6.0"),
.package(url: "https://github.com/pointfreeco/swift-clocks", .upToNextMajor(from: "1.0.2"))
.package(url: "https://github.com/pointfreeco/swift-clocks", .upToNextMajor(from: "1.0.2")),
.package(url: "https://github.com/ionic-team/live-update-provider-sdk", exact: "0.1.0-alpha.1")
],
targets: [
.target(
name: "IonicPortals",
dependencies: [
.product(name: "Capacitor", package: "capacitor-swift-pm"),
.product(name: "Cordova", package: "capacitor-swift-pm"),
.product(name: "IonicLiveUpdates", package: "ionic-live-updates-releases")
.product(name: "IonicLiveUpdates", package: "ionic-live-updates-releases"),
.product(name: "LiveUpdateProvider", package: "live-update-provider-sdk")
]
),
.testTarget(
Expand Down
1 change: 0 additions & 1 deletion Sources/IonicPortals/AssetMap.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
//

import Foundation
import IonicLiveUpdates

public struct AssetMap {
/// The name to index the asset map by.
Expand Down
5 changes: 2 additions & 3 deletions Sources/IonicPortals/IonicPortals.docc/Portal.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### Create a Portal

- ``init(name:startDir:index:devModeEnabled:bundle:initialContext:assetMaps:plugins:liveUpdateManager:liveUpdateConfig:)``
- ``init(name:startDir:index:devModeEnabled:bundle:initialContext:assetMaps:plugins:liveUpdateProvider:)``
- ``init(stringLiteral:)``

### Web App Location
Expand All @@ -26,8 +26,7 @@

### Live Updates

- ``liveUpdateConfig``
- ``liveUpdateManager``
- ``liveUpdateProvider``

### Initial Application State

Expand Down
47 changes: 35 additions & 12 deletions Sources/IonicPortals/Portal+LiveUpdates.swift
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
import Foundation
import IonicLiveUpdates
import LiveUpdateProvider

extension Portal {
/// Error thrown if a ``liveUpdateConfig`` is not present on a ``Portal`` when ``sync()`` is called.
public enum PortalSyncResult {
case appflow(LiveUpdateManager.SyncResult)
case custom(any SyncResult)
}

/// Error thrown if a ``liveUpdateProvider`` is not present on a ``Portal`` when ``sync()`` is called.
public struct LiveUpdateNotConfigured: Error {}

/// Syncs the ``liveUpdateConfig`` if present
/// - Returns: The result of the synchronization operation
/// - Throws: If the portal has no ``liveUpdateConfig``, a ``LiveUpdateNotConfigured`` error will be thrown.
/// Any errors thrown from ``liveUpdateManager`` will be propogated.
public func sync() async throws -> LiveUpdateManager.SyncResult {
if let liveUpdateConfig {
return try await liveUpdateManager.sync(appId: liveUpdateConfig.appId)
} else {
/// Syncs the live update provider if configured.
/// - Returns: The result of the synchronization operation.
/// - Throws: ``LiveUpdateNotConfigured`` if no live update provider is configured.
/// Any errors thrown from the live update provider will be propagated.
public func sync() async throws -> PortalSyncResult {
switch liveUpdateProvider {
case .appflow(let manager, let config):
return .appflow(try await manager.sync(appId: config.appId))
case .custom(let manager):
return .custom(try await manager.sync())
case .none:
throw LiveUpdateNotConfigured()
}
}

/// Synchronizes the ``liveUpdateConfig``s of the provided ``Portal``s in parallel
/// Synchronizes the ``liveUpdateProvider`` of the provided ``Portal``s in parallel.
/// - Parameter portals: The ``Portal``s to ``sync()``
/// - Returns: A ``ParallelLiveUpdateSyncGroup`` of the results of each call to ``Portal/sync()``
///
Expand All @@ -30,10 +40,23 @@ extension Portal {
public static func sync(_ portals: [Portal]) -> ParallelLiveUpdateSyncGroup {
.init(portals)
}

/// The directory of the latest synced web application assets for this portal.
/// Returns `nil` if no live update provider is configured or no sync has occurred.
public var latestAppDirectory: URL? {
switch liveUpdateProvider {
case .appflow(let manager, let config):
return manager.latestAppDirectory(for: config.appId)
case .custom(let manager):
return manager.latestAppDirectory
case .none:
return nil
}
}
}

extension Array where Element == Portal {
/// Synchronizes the ``Portal/liveUpdateConfig`` for the elements in the array
/// Synchronizes the ``Portal/liveUpdateProvider`` for the elements in the array
/// - Returns: A ``ParallelLiveUpdateSyncGroup`` of the results of each call to ``Portal/sync()``
///
/// Usage
Expand All @@ -49,7 +72,7 @@ extension Array where Element == Portal {
}

/// Alias for a parallel sequence of Live Update synchronization results
public typealias ParallelLiveUpdateSyncGroup = ParallelAsyncSequence<Result<LiveUpdateManager.SyncResult, any Error>>
public typealias ParallelLiveUpdateSyncGroup = ParallelAsyncSequence<Result<Portal.PortalSyncResult, any Error>>

extension ParallelLiveUpdateSyncGroup {
init(_ portals: [Portal]) {
Expand Down
63 changes: 39 additions & 24 deletions Sources/IonicPortals/Portal.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,21 @@
import Foundation
import Capacitor
import IonicLiveUpdates
import LiveUpdateProvider

/// The configuration of a web application to be embedded in an iOS application
public struct Portal {
/// The live update provider for a ``Portal``.
public enum LiveUpdateProvider {
/// Uses IonicLiveUpdates to sync and locate the latest web application assets.
case appflow(
/// The `LiveUpdateManager` responsible for locating the latest source for the web application.
liveUpdateManager: LiveUpdateManager = .shared,
/// The `LiveUpdate` configuration used to determine the location of updated application assets.
liveUpdateConfig: LiveUpdate)
/// Uses a custom live update provider to sync and locate the latest web application assets.
case custom(liveUpdateManager: any LiveUpdateManaging)
}
/// The name of the portal.
///
/// This is always provided to the web application
Expand All @@ -28,17 +40,15 @@ public struct Portal {
/// Any Capacitor plugins to load on the ``Portal``
public var plugins: [Plugin]

/// The `LiveUpdateManager` responsible for locating the latest source for the web application
public var liveUpdateManager: LiveUpdateManager

/// The `LiveUpdate` configuration used to determine the location of updated application assets.
public var liveUpdateConfig: LiveUpdate? = nil {
/// The live update provider responsible for locating and syncing the latest web application assets
public var liveUpdateProvider: LiveUpdateProvider? {
didSet {
guard let liveUpdateConfig = liveUpdateConfig else { return }
try? liveUpdateManager.add(
liveUpdateConfig,
existingCacheUrl: bundle.url(forResource: startDir, withExtension: nil)
)
if case .appflow(let manager, let config) = liveUpdateProvider {
try? manager.add(
config,
existingCacheUrl: bundle.url(forResource: startDir, withExtension: nil)
)
}
}
}

Expand All @@ -58,8 +68,7 @@ public struct Portal {
/// - plugins: Any ``Plugin``s to load. Defautls to `[]`.
/// - initialContext: Any initial state required by the web application. Defaults to `[:]`.
/// - assetMaps: Any ``AssetMap``s needed to share assets with the ``Portal``. Defaults to `[]`.
/// - liveUpdateManager: The `LiveUpdateManager` responsible for locating the source source for the web application. Defaults to `LiveUpdateManager.shared`.
/// - liveUpdateConfig: The `LiveUpdate` configuration used to determine to location of updated application assets. Defaults to `nil`.
/// - liveUpdateProvider: The live update provider responsible for locating and syncing the latest web application assets. Defaults to `nil`.
public init(
name: String,
startDir: String? = nil,
Expand All @@ -69,25 +78,24 @@ public struct Portal {
initialContext: JSObject = [:],
assetMaps: [AssetMap] = [],
plugins: [Plugin] = [],
liveUpdateManager: LiveUpdateManager = .shared,
liveUpdateConfig: LiveUpdate? = nil
liveUpdateProvider: LiveUpdateProvider? = nil
) {
self.name = name
self.startDir = startDir ?? name
self.devModeEnabled = devModeEnabled
self.index = index
self.initialContext = initialContext
self.bundle = bundle
self.assetMaps = assetMaps
self.plugins = plugins
self.liveUpdateManager = liveUpdateManager
self.liveUpdateConfig = liveUpdateConfig
if let liveUpdateConfig = liveUpdateConfig {
try? liveUpdateManager.add(
liveUpdateConfig,
self.liveUpdateProvider = liveUpdateProvider

if case .appflow(let manager, let config) = liveUpdateProvider {
try? manager.add(
config,
existingCacheUrl: bundle.url(forResource: self.startDir, withExtension: nil)
)
}
self.assetMaps = assetMaps
}
}

Expand Down Expand Up @@ -233,13 +241,21 @@ extension Portal {
self.portal = portal
}

/// Configures the `LiveUpdate` configuration
/// Configures the Ionic live update provider for this portal.
/// - Parameters:
/// - appId: The AppFlow id of the web application associated with the ``IONPortal``
/// - channel: The AppFlow channel to check for updates from.
/// - syncImmediately: Whether to immediately sync with AppFlow to check for updates.
/// - Note: This method has no effect if a custom live update provider is already configured.
@objc public func setLiveUpdateConfiguration(appId: String, channel: String, syncImmediately: Bool) {
portal.liveUpdateConfig = LiveUpdate(appId: appId, channel: channel, syncOnAdd: syncImmediately)
if case .custom = portal.liveUpdateProvider { return }

let config = LiveUpdate(appId: appId, channel: channel, syncOnAdd: syncImmediately)
if case .appflow(let manager, _) = portal.liveUpdateProvider {
portal.liveUpdateProvider = .appflow(liveUpdateManager: manager, liveUpdateConfig: config)
} else {
portal.liveUpdateProvider = .appflow(liveUpdateConfig: config)
}
}
}

Expand All @@ -254,8 +270,7 @@ extension IONPortal {
let portal = Portal(
name: name,
startDir: startDir,
initialContext: initialContext.flatMap { JSTypes.coerceDictionaryToJSObject($0) } ?? [:],
liveUpdateConfig: nil
initialContext: initialContext.flatMap { JSTypes.coerceDictionaryToJSObject($0) } ?? [:]
)

self.init(portal: portal)
Expand Down
10 changes: 3 additions & 7 deletions Sources/IonicPortals/PortalView/PortalUIView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,7 @@ public class PortalUIView: UIView {

private func initView () {
if PortalsRegistrationManager.shared.isRegistered {
if let liveUpdateConfig = portal.liveUpdateConfig {
self.liveUpdatePath = portal.liveUpdateManager.latestAppDirectory(for: liveUpdateConfig.appId)
}

self.liveUpdatePath = portal.latestAppDirectory
addPinnedSubview(webView)
} else {
let showRegistrationError = PortalsRegistrationManager.shared.registrationState == .error
Expand Down Expand Up @@ -310,9 +307,8 @@ extension PortalUIView {
}
/// Reloads the underlying `WKWebView`
@objc public func reload() {
if let liveUpdate = portal.liveUpdateConfig,
let latestAppPath = portal.liveUpdateManager.latestAppDirectory(for: liveUpdate.appId),
liveUpdatePath == nil || liveUpdatePath?.path != latestAppPath.path {
if let latestAppPath = portal.latestAppDirectory,
liveUpdatePath != latestAppPath {
liveUpdatePath = latestAppPath
return setServerBasePath(path: latestAppPath.path)
}
Expand Down
Loading