Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add dev mode #78

Merged
merged 7 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from 6 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
52 changes: 52 additions & 0 deletions Sources/IonicPortals/DevConfiguration.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import Foundation

struct ServerUrl {
var variableName: String
var url: URL? {
ProcessInfo.processInfo.environment[variableName].flatMap(URL.init)
}
}

struct ConfigUrl {
var variableName: String

var url: URL? {
guard let value = ProcessInfo.processInfo.environment[variableName],
let decodedData = Data(base64Encoded: value, options: [.ignoreUnknownCharacters]),
let decodedString = String(data: decodedData, encoding: .utf8)
else { return nil }

let targetTempFile = FileManager.default.temporaryDirectory
.appendingPathComponent(UUID().uuidString)
.appendingPathExtension("json")

do {
try decodedString.write(to: targetTempFile, atomically: true, encoding: .utf8)
} catch {
return nil
}

return targetTempFile
}
}

struct DevConfiguration {
var server: ServerUrl
var capacitorConfig: ConfigUrl

init(server: ServerUrl, capacitorConfig: ConfigUrl) {
self.server = server
self.capacitorConfig = capacitorConfig
}
}

extension DevConfiguration {
init(baseName: String) {
server = .init(variableName: "\(baseName.uppercased())_SERVER")
capacitorConfig = .init(variableName: "\(baseName.uppercased())_CONFIG")
}
}

extension DevConfiguration {
static var `default` = DevConfiguration(baseName: "PORTAL")
}
11 changes: 10 additions & 1 deletion 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:bundle:initialContext:assetMaps:plugins:liveUpdateManager:liveUpdateConfig:)``
- ``init(name:startDir:index:devModeEnabled:bundle:initialContext:assetMaps:plugins:liveUpdateManager:liveUpdateConfig:)``
- ``init(stringLiteral:)``

### Web App Location
Expand All @@ -15,6 +15,7 @@

### Plugin Management

- ``plugins``
- ``adding(_:)-72o29``
- ``adding(_:)-9sqqz``
- ``adding(_:)-868wl``
Expand All @@ -39,3 +40,11 @@
### Name

- ``name``

### DevMode

- ``devModeEnabled``

### Assets

- ``assetMaps``
9 changes: 7 additions & 2 deletions Sources/IonicPortals/Portal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import Foundation
import Capacitor
import IonicLiveUpdates


/// The configuration of a web application to be embedded in an iOS application
public struct Portal {
/// The name of the portal.
Expand All @@ -13,7 +12,10 @@ public struct Portal {

/// The root directory of the ``Portal`` web application relative to the root of ``bundle``
public let startDir: String


/// Enables web developers to override Portal content in debug builds.
public var devModeEnabled: Bool

/// The initial file to load in the Portal.
public let index: String

Expand Down Expand Up @@ -48,6 +50,7 @@ public struct Portal {
/// - startDir: The starting directory of the ``Portal`` relative to the root of ``bundle``.
/// If `nil`, the portal name is used as the starting directory. Defaults to `nil`.
/// - index: The initial file to load in the Portal. Defaults to `index.html`.
/// - devModeEnabled: Enables web developers to override the Portal content in debug builds. Defaults to `true`.
/// - bundle: The `Bundle` that contains the web application. Defaults to `Bundle.main`.
/// - plugins: Any ``Plugin``s to load. Defautls to `[]`.
/// - initialContext: Any initial state required by the web application. Defaults to `[:]`.
Expand All @@ -58,6 +61,7 @@ public struct Portal {
name: String,
startDir: String? = nil,
index: String = "index.html",
devModeEnabled: Bool = true,
bundle: Bundle = .main,
initialContext: JSObject = [:],
assetMaps: [AssetMap] = [],
Expand All @@ -67,6 +71,7 @@ public struct Portal {
) {
self.name = name
self.startDir = startDir ?? name
self.devModeEnabled = devModeEnabled
self.index = index
self.initialContext = initialContext
self.bundle = bundle
Expand Down
43 changes: 31 additions & 12 deletions Sources/IonicPortals/PortalUIView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class PortalUIView: UIView {
super.init(frame: .zero)
initView()
}

/// Creates an instance of ``PortalUIView``
/// - Parameter portal: The ``IONPortal`` to render.
@objc public convenience init(portal: IONPortal) {
Expand All @@ -32,7 +32,7 @@ public class PortalUIView: UIView {
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

func initView () {
if PortalsRegistrationManager.shared.isRegistered {
if let liveUpdateConfig = portal.liveUpdateConfig {
Expand Down Expand Up @@ -70,15 +70,19 @@ public class PortalUIView: UIView {
final class InternalCapWebView: CAPWebView {
private var portal: Portal
private var liveUpdatePath: URL?


#if DEBUG
private lazy var devConfiguration = DevConfiguration(baseName: portal.name)
#endif

override var router: Router { PortalRouter(portal: portal) }

init(portal: Portal, liveUpdatePath: URL?) {
self.portal = portal
self.liveUpdatePath = liveUpdatePath
super.init(autoRegisterPlugins: false)
}

override func capacitorDidLoad() {
bridge.registerPluginInstance(PortalsPlugin())

Expand All @@ -96,15 +100,15 @@ public class PortalUIView: UIView {
fatalError("init(coder:) has not been implemented")
}

override func instanceDescriptor() -> InstanceDescriptor {
let descriptor = createInstanceDescriptor()
portal.descriptorConfiguration.forEach { $0(descriptor) }
return descriptor
}

private func createInstanceDescriptor() -> InstanceDescriptor {
let bundleURL = portal.bundle.url(forResource: portal.startDir, withExtension: nil)


#if DEBUG
if portal.devModeEnabled, let debugConfigUrl = devConfiguration.capacitorConfig.url ?? DevConfiguration.default.capacitorConfig.url {
return InstanceDescriptor(at: Bundle.main.bundleURL, configuration: debugConfigUrl, cordovaConfiguration: nil)
}
#endif

guard let path = liveUpdatePath ?? bundleURL else {
// DCG this should throw or something else
return InstanceDescriptor()
Expand All @@ -123,12 +127,27 @@ public class PortalUIView: UIView {
cordovaConfigUrl = updatedCordovaConfig
}


let descriptor = InstanceDescriptor(at: path, configuration: capConfigUrl, cordovaConfiguration: cordovaConfigUrl)
descriptor.handleApplicationNotifications = false
return descriptor
}


override func instanceDescriptor() -> InstanceDescriptor {
let descriptor = createInstanceDescriptor()
portal.descriptorConfiguration.forEach { $0(descriptor) }
#if DEBUG
if portal.devModeEnabled, let serverUrl = devConfiguration.server.url ?? DevConfiguration.default.server.url {
descriptor.serverURL = serverUrl.absoluteString
// This allows for not having any files on disk during dev
if !FileManager.default.fileExists(atPath: descriptor.appLocation.path) {
descriptor.appLocation = Bundle.main.bundleURL
}
}
#endif
return descriptor
}

override func loadInitialContext(_ userContentViewController: WKUserContentController) {
let portalInitialContext: String

Expand Down
2 changes: 1 addition & 1 deletion Sources/IonicPortals/PortalView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Capacitor
public struct PortalView: UIViewRepresentable {
private let portal: Portal
private let onBridgeAvailable: (CAPBridgeProtocol) -> Void

/// Creates an instance of ``PortalView``
/// - Parameters:
/// - portal: The ``Portal`` to render.
Expand Down
Loading