bleam 0.0.7 → 0.0.9
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/dist/cli.cjs +23 -4
- package/dist/cli.d.cts +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +23 -4
- package/dist/{ui-234Plg7Z.d.cts → ui-Bg11tvlc.d.ts} +7 -7
- package/dist/ui.d.ts +1 -1
- package/dist/window.d.ts +1 -1
- package/package.json +4 -1
- package/templates/image-generation/app/index.tsx +1 -1
- package/templates/native/.gitattributes +1 -0
- package/templates/native/App.tsx +26 -0
- package/templates/native/app.json +12 -0
- package/templates/native/index.ts +8 -0
- package/templates/native/ios/.xcode.env +11 -0
- package/templates/native/ios/Bleam/AI/Flux2/Configuration/Flux2Config.swift +288 -0
- package/templates/native/ios/Bleam/AI/Flux2/Configuration/MemoryConfig.swift +233 -0
- package/templates/native/ios/Bleam/AI/Flux2/Configuration/MemoryOptimizationConfig.swift +157 -0
- package/templates/native/ios/Bleam/AI/Flux2/Configuration/ModelRegistry.swift +425 -0
- package/templates/native/ios/Bleam/AI/Flux2/Configuration/QuantizationConfig.swift +138 -0
- package/templates/native/ios/Bleam/AI/Flux2/Configuration/TransformerRepoProfile.swift +59 -0
- package/templates/native/ios/Bleam/AI/Flux2/Configuration/VAEConfig.swift +134 -0
- package/templates/native/ios/Bleam/AI/Flux2/Flux2Core.swift +55 -0
- package/templates/native/ios/Bleam/AI/Flux2/Loading/WeightLoader.swift +1192 -0
- package/templates/native/ios/Bleam/AI/Flux2/Pipeline/FluxImageRunner.swift +1282 -0
- package/templates/native/ios/Bleam/AI/Flux2/Pipeline/LatentUtils.swift +191 -0
- package/templates/native/ios/Bleam/AI/Flux2/Scheduler/FlowMatchEulerScheduler.swift +279 -0
- package/templates/native/ios/Bleam/AI/Flux2/Text/KleinTextEncoder.swift +380 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2Attention.swift +379 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2Embeddings.swift +159 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2FeedForward.swift +154 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2FusedKernels.swift +198 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2KVCache.swift +36 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2Modulation.swift +193 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2ParallelAttention.swift +389 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2RoPE.swift +304 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2SingleBlock.swift +290 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2Transformer.swift +530 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2TransformerBlock.swift +362 -0
- package/templates/native/ios/Bleam/AI/Flux2/Utils/Flux2Debug.swift +101 -0
- package/templates/native/ios/Bleam/AI/Flux2/Utils/MLXCheckpoint.swift +118 -0
- package/templates/native/ios/Bleam/AI/Flux2/Utils/MemoryManager.swift +201 -0
- package/templates/native/ios/Bleam/AI/Flux2/VAE/AutoencoderKL.swift +476 -0
- package/templates/native/ios/Bleam/AI/Flux2/VAE/ResnetBlock.swift +316 -0
- package/templates/native/ios/Bleam/AI/Flux2/VAE/VAEDecoder.swift +120 -0
- package/templates/native/ios/Bleam/AI/Flux2/VAE/VAEEncoder.swift +136 -0
- package/templates/native/ios/Bleam/AppDelegate.swift +373 -0
- package/templates/native/ios/Bleam/Appearance.swift +62 -0
- package/templates/native/ios/Bleam/Bridging-Header.h +3 -0
- package/templates/native/ios/Bleam/Images.xcassets/AppIcon.appiconset/Contents.json +13 -0
- package/templates/native/ios/Bleam/Images.xcassets/Contents.json +6 -0
- package/templates/native/ios/Bleam/Info.plist +76 -0
- package/templates/native/ios/Bleam/PrivacyInfo.xcprivacy +48 -0
- package/templates/native/ios/Bleam/SceneDelegate.swift +58 -0
- package/templates/native/ios/Bleam/SplashScreen.storyboard +47 -0
- package/templates/native/ios/Bleam/Supporting/Expo.plist +6 -0
- package/templates/native/ios/Bleam/bleam.entitlements +10 -0
- package/templates/native/ios/Bleam.xcodeproj/project.pbxproj +1146 -0
- package/templates/native/ios/Bleam.xcodeproj/xcshareddata/xcschemes/Bleam.xcscheme +88 -0
- package/templates/native/ios/GenerationService/GenerationService.entitlements +10 -0
- package/templates/native/ios/GenerationService/ImageGenerationRunner.swift +591 -0
- package/templates/native/ios/GenerationService/Info.plist +31 -0
- package/templates/native/ios/GenerationService/main.swift +165 -0
- package/templates/native/ios/PlatformHelper/Info.plist +29 -0
- package/templates/native/ios/PlatformHelper/main.swift +335 -0
- package/templates/native/ios/Podfile +198 -0
- package/templates/native/ios/Podfile.lock +2284 -0
- package/templates/native/ios/Podfile.properties.json +5 -0
- package/templates/native/ios/Shared/Generation/GenerationServiceProtocol.swift +12 -0
- package/templates/native/ios/Shared/Generation/GenerationWorkerProtocol.swift +124 -0
- package/templates/native/metro.config.js +6 -0
- package/templates/native/modules/bleam-runtime/BleamRuntime.podspec +14 -0
- package/templates/native/modules/bleam-runtime/expo-module.config.json +8 -0
- package/templates/native/modules/bleam-runtime/ios/AIModule.swift +1258 -0
- package/templates/native/modules/bleam-runtime/ios/GenerationContracts.swift +135 -0
- package/templates/native/modules/bleam-runtime/ios/PlatformModule.swift +313 -0
- package/templates/native/modules/bleam-runtime/package.json +5 -0
- package/templates/native/package.json +39 -0
- package/templates/native/scripts/build/mlx-frameworks.ts +421 -0
- package/templates/native/scripts/start/index.ts +474 -0
- package/templates/native/scripts/start/known-failures.ts +20 -0
- package/templates/native/scripts/start/loader.ts +76 -0
- package/templates/native/scripts/start/native-state.ts +242 -0
- package/templates/native/scripts/start/package.json +4 -0
- package/templates/native/scripts/start/swift-packages.ts +414 -0
- package/templates/native/scripts/start/xcode-formatter.ts +106 -0
- package/templates/native/tsconfig.json +14 -0
- package/templates/native/yarn.lock +3749 -0
- package/dist/ui-BJmXhz9Q.d.ts +0 -92
- package/dist/ui-Dd7SXdbg.d.cts +0 -92
- package/dist/ui-Ds88eETu.d.ts +0 -92
- package/dist/ui-TaqnB5SP.d.ts +0 -92
- package/dist/ui-WQvnXL0W.d.ts +0 -92
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
internal import Expo
|
|
2
|
+
import React
|
|
3
|
+
import ReactAppDependencyProvider
|
|
4
|
+
import UIKit
|
|
5
|
+
import CryptoKit
|
|
6
|
+
import Darwin
|
|
7
|
+
|
|
8
|
+
func launchArgument(_ name: String) -> String? {
|
|
9
|
+
let arguments = ProcessInfo.processInfo.arguments
|
|
10
|
+
guard let flagIndex = arguments.firstIndex(of: name) else {
|
|
11
|
+
return nil
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
let valueIndex = arguments.index(after: flagIndex)
|
|
15
|
+
guard arguments.indices.contains(valueIndex) else {
|
|
16
|
+
return nil
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return arguments[valueIndex]
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@main
|
|
23
|
+
class AppDelegate: ExpoAppDelegate {
|
|
24
|
+
public override func application(
|
|
25
|
+
_ application: UIApplication,
|
|
26
|
+
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
|
27
|
+
) -> Bool {
|
|
28
|
+
let launched = super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
|
29
|
+
if launched {
|
|
30
|
+
PlatformHelperInstaller.refreshIfInstalledApp()
|
|
31
|
+
}
|
|
32
|
+
return launched
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public func application(
|
|
36
|
+
_ application: UIApplication,
|
|
37
|
+
configurationForConnecting connectingSceneSession: UISceneSession,
|
|
38
|
+
options: UIScene.ConnectionOptions
|
|
39
|
+
) -> UISceneConfiguration {
|
|
40
|
+
let configuration = UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
|
|
41
|
+
configuration.delegateClass = SceneDelegate.self
|
|
42
|
+
return configuration
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Linking API
|
|
46
|
+
public override func application(
|
|
47
|
+
_ app: UIApplication,
|
|
48
|
+
open url: URL,
|
|
49
|
+
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
|
|
50
|
+
) -> Bool {
|
|
51
|
+
return super.application(app, open: url, options: options) || RCTLinkingManager.application(app, open: url, options: options)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Universal Links
|
|
55
|
+
public override func application(
|
|
56
|
+
_ application: UIApplication,
|
|
57
|
+
continue userActivity: NSUserActivity,
|
|
58
|
+
restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void
|
|
59
|
+
) -> Bool {
|
|
60
|
+
let result = RCTLinkingManager.application(application, continue: userActivity, restorationHandler: restorationHandler)
|
|
61
|
+
return super.application(application, continue: userActivity, restorationHandler: restorationHandler) || result
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private enum PlatformHelperInstaller {
|
|
66
|
+
static func refreshIfInstalledApp() {
|
|
67
|
+
#if targetEnvironment(macCatalyst)
|
|
68
|
+
let homeDirectory = URL(fileURLWithPath: NSHomeDirectory(), isDirectory: true)
|
|
69
|
+
let homeApplications = homeDirectory
|
|
70
|
+
.appendingPathComponent("Applications", isDirectory: true)
|
|
71
|
+
.standardizedFileURL.path + "/"
|
|
72
|
+
guard Bundle.main.bundleURL.standardizedFileURL.path.hasPrefix(homeApplications) else {
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
guard let bundleIdentifier = Bundle.main.bundleIdentifier else { return }
|
|
76
|
+
|
|
77
|
+
let serviceName = "PlatformHelper"
|
|
78
|
+
let bundledService = Bundle.main.bundleURL
|
|
79
|
+
.appendingPathComponent("Contents/XPCServices/\(serviceName).xpc", isDirectory: true)
|
|
80
|
+
guard FileManager.default.fileExists(atPath: bundledService.path) else {
|
|
81
|
+
NSLog("Bleam helper unavailable: bundled executable is missing")
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
let label = "\(bundleIdentifier).PlatformHelper"
|
|
86
|
+
let domain = "gui/\(getuid())"
|
|
87
|
+
_ = runLaunchctl(["bootout", "\(domain)/\(label)"])
|
|
88
|
+
|
|
89
|
+
let helperRoot = homeDirectory
|
|
90
|
+
.appendingPathComponent("Library/Application Support/Bleam/Helpers", isDirectory: true)
|
|
91
|
+
.appendingPathComponent(bundleIdentifier, isDirectory: true)
|
|
92
|
+
let installedService = helperRoot.appendingPathComponent("\(serviceName).xpc", isDirectory: true)
|
|
93
|
+
let executable = installedService
|
|
94
|
+
.appendingPathComponent("Contents/MacOS/\(serviceName)")
|
|
95
|
+
let launchAgents = homeDirectory
|
|
96
|
+
.appendingPathComponent("Library/LaunchAgents", isDirectory: true)
|
|
97
|
+
let plistURL = launchAgents.appendingPathComponent("\(label).plist")
|
|
98
|
+
let propertyList: [String: Any] = [
|
|
99
|
+
"Label": label,
|
|
100
|
+
"ProgramArguments": [executable.path],
|
|
101
|
+
"RunAtLoad": true,
|
|
102
|
+
"KeepAlive": true,
|
|
103
|
+
"ProcessType": "Background",
|
|
104
|
+
"MachServices": ["\(bundleIdentifier).PlatformHelper": true],
|
|
105
|
+
"EnvironmentVariables": ["XPC_SERVICE_NAME": "\(bundleIdentifier).PlatformHelper"],
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
do {
|
|
109
|
+
try FileManager.default.createDirectory(
|
|
110
|
+
at: helperRoot,
|
|
111
|
+
withIntermediateDirectories: true
|
|
112
|
+
)
|
|
113
|
+
try? FileManager.default.removeItem(at: installedService)
|
|
114
|
+
try FileManager.default.copyItem(at: bundledService, to: installedService)
|
|
115
|
+
try FileManager.default.createDirectory(
|
|
116
|
+
at: launchAgents,
|
|
117
|
+
withIntermediateDirectories: true
|
|
118
|
+
)
|
|
119
|
+
let data = try PropertyListSerialization.data(
|
|
120
|
+
fromPropertyList: propertyList,
|
|
121
|
+
format: .xml,
|
|
122
|
+
options: 0
|
|
123
|
+
)
|
|
124
|
+
try data.write(to: plistURL, options: .atomic)
|
|
125
|
+
} catch {
|
|
126
|
+
NSLog("Bleam helper install failed: %@", error.localizedDescription)
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if runLaunchctl(["bootstrap", domain, plistURL.path]) != 0 {
|
|
131
|
+
NSLog("Bleam helper registration failed")
|
|
132
|
+
}
|
|
133
|
+
#endif
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private static func runLaunchctl(_ arguments: [String]) -> Int32 {
|
|
137
|
+
let values = ["launchctl"] + arguments
|
|
138
|
+
var argv = values.map { strdup($0) }
|
|
139
|
+
argv.append(nil)
|
|
140
|
+
defer { argv.compactMap { $0 }.forEach { free($0) } }
|
|
141
|
+
|
|
142
|
+
var pid: pid_t = 0
|
|
143
|
+
let result = posix_spawn(&pid, "/bin/launchctl", nil, nil, &argv, environ)
|
|
144
|
+
guard result == 0 else { return Int32(result) }
|
|
145
|
+
var status: Int32 = 0
|
|
146
|
+
guard waitpid(pid, &status, 0) == pid else { return -1 }
|
|
147
|
+
return status
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
class ReactNativeDelegate: ExpoReactNativeFactoryDelegate {
|
|
152
|
+
private let bundleOverrideURL: URL?
|
|
153
|
+
|
|
154
|
+
init(bundleURL: URL? = nil) {
|
|
155
|
+
bundleOverrideURL = bundleURL
|
|
156
|
+
super.init()
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Extension point for config-plugins
|
|
160
|
+
|
|
161
|
+
override func sourceURL(for bridge: RCTBridge) -> URL? {
|
|
162
|
+
// needed to return the correct URL for expo-dev-client.
|
|
163
|
+
bridge.bundleURL ?? bundleURL()
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
override func customize(_ rootView: UIView) {
|
|
167
|
+
super.customize(rootView)
|
|
168
|
+
applyRootAppearance(to: rootView)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
override func bundleURL() -> URL? {
|
|
172
|
+
if let bundleOverrideURL {
|
|
173
|
+
return bundleOverrideURL
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if let bundleURL = launchArgument("--bleam-bundle-url") {
|
|
177
|
+
return URL(string: bundleURL)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
#if DEBUG
|
|
181
|
+
return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: ".expo/.virtual-metro-entry")
|
|
182
|
+
#else
|
|
183
|
+
return OTAUpdateManager.shared.launchBundleURL()
|
|
184
|
+
#endif
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private struct OTAArtifact: Decodable {
|
|
189
|
+
let url: URL
|
|
190
|
+
let sizeBytes: Int
|
|
191
|
+
let sha256: String
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private struct OTARelease: Decodable {
|
|
195
|
+
let schemaVersion: Int
|
|
196
|
+
let kind: String
|
|
197
|
+
let releaseId: String
|
|
198
|
+
let bundleIdentifier: String
|
|
199
|
+
let teamIdentifier: String
|
|
200
|
+
let runtimeVersion: String
|
|
201
|
+
let artifact: OTAArtifact
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
final class OTAUpdateManager {
|
|
205
|
+
static let shared = OTAUpdateManager()
|
|
206
|
+
|
|
207
|
+
private let fileManager = FileManager.default
|
|
208
|
+
private let defaults = UserDefaults.standard
|
|
209
|
+
private let activeKey = "BleamOTAActiveRelease"
|
|
210
|
+
private let attemptedKey = "BleamOTAAttemptedRelease"
|
|
211
|
+
private let rejectedKey = "BleamOTARejectedRelease"
|
|
212
|
+
private var launchingRelease: String?
|
|
213
|
+
|
|
214
|
+
private var cacheRoot: URL {
|
|
215
|
+
let caches = fileManager.urls(for: .cachesDirectory, in: .userDomainMask)[0]
|
|
216
|
+
let bundleIdentifier = Bundle.main.bundleIdentifier ?? "app"
|
|
217
|
+
return caches.appendingPathComponent("Bleam/OTA/\(bundleIdentifier)", isDirectory: true)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
private init() {
|
|
221
|
+
for name in ["RCTJavaScriptDidLoadNotification", "RCTContentDidAppearNotification"] {
|
|
222
|
+
NotificationCenter.default.addObserver(
|
|
223
|
+
forName: NSNotification.Name(rawValue: name),
|
|
224
|
+
object: nil,
|
|
225
|
+
queue: .main
|
|
226
|
+
) { [weak self] _ in
|
|
227
|
+
self?.markLaunchSuccessful()
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
func launchBundleURL() -> URL? {
|
|
233
|
+
guard let releaseId = defaults.string(forKey: activeKey) else {
|
|
234
|
+
return embeddedBundleURL()
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if defaults.string(forKey: attemptedKey) == releaseId {
|
|
238
|
+
defaults.set(releaseId, forKey: rejectedKey)
|
|
239
|
+
defaults.removeObject(forKey: activeKey)
|
|
240
|
+
defaults.removeObject(forKey: attemptedKey)
|
|
241
|
+
return embeddedBundleURL()
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
let bundle = cacheRoot
|
|
245
|
+
.appendingPathComponent(releaseId, isDirectory: true)
|
|
246
|
+
.appendingPathComponent("bundle/bundle.js")
|
|
247
|
+
guard fileManager.fileExists(atPath: bundle.path) else {
|
|
248
|
+
defaults.removeObject(forKey: activeKey)
|
|
249
|
+
return embeddedBundleURL()
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
launchingRelease = releaseId
|
|
253
|
+
defaults.set(releaseId, forKey: attemptedKey)
|
|
254
|
+
return bundle
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
func checkForUpdate() {
|
|
258
|
+
guard
|
|
259
|
+
let baseUrlString = Bundle.main.object(forInfoDictionaryKey: "BleamUpdateServiceURL") as? String,
|
|
260
|
+
let baseUrl = URL(string: baseUrlString),
|
|
261
|
+
let bundleIdentifier = Bundle.main.bundleIdentifier,
|
|
262
|
+
let runtimeVersion = Bundle.main.object(forInfoDictionaryKey: "BleamRuntimeVersion") as? String
|
|
263
|
+
else {
|
|
264
|
+
NSLog("Bleam OTA disabled: missing update URL, bundle identifier, or runtime version")
|
|
265
|
+
return
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
let path = "/v1/apps/\(bundleIdentifier)/ota/\(runtimeVersion)"
|
|
269
|
+
guard let url = URL(string: path, relativeTo: baseUrl)?.absoluteURL else {
|
|
270
|
+
NSLog("Bleam OTA disabled: invalid resolver URL")
|
|
271
|
+
return
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
NSLog("Bleam OTA checking %@", url.absoluteString)
|
|
275
|
+
URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
|
|
276
|
+
if let error {
|
|
277
|
+
NSLog("Bleam OTA resolver failed: %@", error.localizedDescription)
|
|
278
|
+
}
|
|
279
|
+
guard
|
|
280
|
+
let self,
|
|
281
|
+
let response = response as? HTTPURLResponse,
|
|
282
|
+
response.statusCode == 200,
|
|
283
|
+
let data,
|
|
284
|
+
let release = try? JSONDecoder().decode(OTARelease.self, from: data)
|
|
285
|
+
else {
|
|
286
|
+
let status = (response as? HTTPURLResponse)?.statusCode ?? 0
|
|
287
|
+
NSLog("Bleam OTA resolver returned an unusable response: %d", status)
|
|
288
|
+
return
|
|
289
|
+
}
|
|
290
|
+
self.download(release)
|
|
291
|
+
}.resume()
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
private func download(_ release: OTARelease) {
|
|
295
|
+
guard
|
|
296
|
+
release.schemaVersion == 1,
|
|
297
|
+
release.kind == "ota",
|
|
298
|
+
release.bundleIdentifier == Bundle.main.bundleIdentifier,
|
|
299
|
+
release.runtimeVersion == Bundle.main.object(forInfoDictionaryKey: "BleamRuntimeVersion") as? String,
|
|
300
|
+
release.teamIdentifier == Bundle.main.object(forInfoDictionaryKey: "BleamTeamIdentifier") as? String,
|
|
301
|
+
release.releaseId != defaults.string(forKey: activeKey),
|
|
302
|
+
release.releaseId != defaults.string(forKey: rejectedKey)
|
|
303
|
+
else {
|
|
304
|
+
NSLog("Bleam OTA rejected release %@", release.releaseId)
|
|
305
|
+
return
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
NSLog("Bleam OTA downloading %@", release.releaseId)
|
|
309
|
+
URLSession.shared.downloadTask(with: release.artifact.url) { [weak self] temporaryUrl, _, error in
|
|
310
|
+
if let error {
|
|
311
|
+
NSLog("Bleam OTA download failed: %@", error.localizedDescription)
|
|
312
|
+
}
|
|
313
|
+
guard let self, let temporaryUrl else { return }
|
|
314
|
+
guard
|
|
315
|
+
let attributes = try? self.fileManager.attributesOfItem(atPath: temporaryUrl.path),
|
|
316
|
+
attributes[.size] as? Int == release.artifact.sizeBytes,
|
|
317
|
+
let data = try? Data(contentsOf: temporaryUrl),
|
|
318
|
+
SHA256.hash(data: data).map({ String(format: "%02x", $0) }).joined() == release.artifact.sha256
|
|
319
|
+
else {
|
|
320
|
+
NSLog("Bleam OTA artifact verification failed for %@", release.releaseId)
|
|
321
|
+
return
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
let destination = self.cacheRoot.appendingPathComponent(release.releaseId, isDirectory: true)
|
|
325
|
+
try? self.fileManager.removeItem(at: destination)
|
|
326
|
+
do {
|
|
327
|
+
try self.fileManager.createDirectory(at: destination, withIntermediateDirectories: true)
|
|
328
|
+
try self.extract(temporaryUrl, to: destination)
|
|
329
|
+
guard self.fileManager.fileExists(
|
|
330
|
+
atPath: destination.appendingPathComponent("bundle/bundle.js").path
|
|
331
|
+
) else {
|
|
332
|
+
try? self.fileManager.removeItem(at: destination)
|
|
333
|
+
return
|
|
334
|
+
}
|
|
335
|
+
self.defaults.removeObject(forKey: self.attemptedKey)
|
|
336
|
+
self.defaults.set(release.releaseId, forKey: self.activeKey)
|
|
337
|
+
NSLog("Bleam OTA staged %@ for next launch", release.releaseId)
|
|
338
|
+
} catch {
|
|
339
|
+
NSLog("Bleam OTA extraction failed: %@", error.localizedDescription)
|
|
340
|
+
try? self.fileManager.removeItem(at: destination)
|
|
341
|
+
}
|
|
342
|
+
}.resume()
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
private func extract(_ archive: URL, to destination: URL) throws {
|
|
346
|
+
let arguments = ["ditto", "-x", "-k", archive.path, destination.path]
|
|
347
|
+
var argv = arguments.map { strdup($0) }
|
|
348
|
+
argv.append(nil)
|
|
349
|
+
defer { argv.compactMap { $0 }.forEach { free($0) } }
|
|
350
|
+
|
|
351
|
+
var pid: pid_t = 0
|
|
352
|
+
let spawnResult = posix_spawn(&pid, "/usr/bin/ditto", nil, nil, &argv, environ)
|
|
353
|
+
guard spawnResult == 0 else {
|
|
354
|
+
throw NSError(domain: NSPOSIXErrorDomain, code: Int(spawnResult))
|
|
355
|
+
}
|
|
356
|
+
var status: Int32 = 0
|
|
357
|
+
guard waitpid(pid, &status, 0) == pid, status == 0 else {
|
|
358
|
+
throw NSError(domain: NSPOSIXErrorDomain, code: Int(status))
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
private func markLaunchSuccessful() {
|
|
363
|
+
guard let releaseId = launchingRelease else { return }
|
|
364
|
+
defaults.removeObject(forKey: attemptedKey)
|
|
365
|
+
launchingRelease = nil
|
|
366
|
+
let releaseRoot = cacheRoot.appendingPathComponent(releaseId, isDirectory: true)
|
|
367
|
+
try? fileManager.createDirectory(at: releaseRoot, withIntermediateDirectories: true)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
private func embeddedBundleURL() -> URL? {
|
|
371
|
+
Bundle.main.url(forResource: "main", withExtension: "jsbundle")
|
|
372
|
+
}
|
|
373
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
|
|
3
|
+
struct Appearance {
|
|
4
|
+
let windowBackgroundColor: UIColor?
|
|
5
|
+
let userInterfaceStyle: UIUserInterfaceStyle
|
|
6
|
+
|
|
7
|
+
static let `default` = Appearance(
|
|
8
|
+
windowBackgroundColor: nil,
|
|
9
|
+
userInterfaceStyle: .unspecified
|
|
10
|
+
)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
func parseColor(from hex: String?) -> UIColor? {
|
|
14
|
+
guard let hex else {
|
|
15
|
+
return nil
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let value = hex.trimmingCharacters(in: .whitespacesAndNewlines).replacingOccurrences(of: "#", with: "")
|
|
19
|
+
guard value.count == 6, let number = Int(value, radix: 16) else {
|
|
20
|
+
return nil
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return UIColor(
|
|
24
|
+
red: CGFloat((number >> 16) & 0xff) / 255,
|
|
25
|
+
green: CGFloat((number >> 8) & 0xff) / 255,
|
|
26
|
+
blue: CGFloat(number & 0xff) / 255,
|
|
27
|
+
alpha: 1
|
|
28
|
+
)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
func parseAppearance(from value: Any?) -> Appearance {
|
|
32
|
+
guard let object = value as? [String: Any] else {
|
|
33
|
+
return .default
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let window = object["window"] as? [String: Any]
|
|
37
|
+
let style = object["userInterfaceStyle"] as? String
|
|
38
|
+
|
|
39
|
+
return Appearance(
|
|
40
|
+
windowBackgroundColor: parseColor(from: window?["backgroundColor"] as? String),
|
|
41
|
+
userInterfaceStyle: style == "light" ? .light : style == "dark" ? .dark : .unspecified
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
func projectAppearance() -> Appearance {
|
|
46
|
+
guard let configPath = launchArgument("--bleam-project-config") else {
|
|
47
|
+
return .default
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
do {
|
|
51
|
+
let data = try Data(contentsOf: URL(fileURLWithPath: configPath))
|
|
52
|
+
let value = try JSONSerialization.jsonObject(with: data) as? [String: Any]
|
|
53
|
+
return parseAppearance(from: value?["appearance"])
|
|
54
|
+
} catch {
|
|
55
|
+
return .default
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
func applyRootAppearance(to rootView: UIView) {
|
|
60
|
+
rootView.backgroundColor = .clear
|
|
61
|
+
rootView.isOpaque = false
|
|
62
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>CADisableMinimumFrameDurationOnPhone</key>
|
|
6
|
+
<true/>
|
|
7
|
+
<key>CFBundleDevelopmentRegion</key>
|
|
8
|
+
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
|
9
|
+
<key>CFBundleExecutable</key>
|
|
10
|
+
<string>$(EXECUTABLE_NAME)</string>
|
|
11
|
+
<key>CFBundleIdentifier</key>
|
|
12
|
+
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
|
13
|
+
<key>CFBundleInfoDictionaryVersion</key>
|
|
14
|
+
<string>6.0</string>
|
|
15
|
+
<key>CFBundleName</key>
|
|
16
|
+
<string>$(PRODUCT_NAME)</string>
|
|
17
|
+
<key>CFBundleDisplayName</key>
|
|
18
|
+
<string>Bleam</string>
|
|
19
|
+
<key>CFBundlePackageType</key>
|
|
20
|
+
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
|
21
|
+
<key>CFBundleShortVersionString</key>
|
|
22
|
+
<string>1.0</string>
|
|
23
|
+
<key>CFBundleSignature</key>
|
|
24
|
+
<string>????</string>
|
|
25
|
+
<key>CFBundleVersion</key>
|
|
26
|
+
<string>1</string>
|
|
27
|
+
<key>AppGroupIdentifier</key>
|
|
28
|
+
<string>group.dev.bleam.app</string>
|
|
29
|
+
<key>LSMinimumSystemVersion</key>
|
|
30
|
+
<string>12.0</string>
|
|
31
|
+
<key>LSRequiresIPhoneOS</key>
|
|
32
|
+
<true/>
|
|
33
|
+
<key>NSAppTransportSecurity</key>
|
|
34
|
+
<dict>
|
|
35
|
+
<key>NSAllowsArbitraryLoads</key>
|
|
36
|
+
<false/>
|
|
37
|
+
<key>NSAllowsLocalNetworking</key>
|
|
38
|
+
<true/>
|
|
39
|
+
</dict>
|
|
40
|
+
<key>RCTNewArchEnabled</key>
|
|
41
|
+
<true/>
|
|
42
|
+
<key>UILaunchStoryboardName</key>
|
|
43
|
+
<string>SplashScreen</string>
|
|
44
|
+
<key>UIRequiredDeviceCapabilities</key>
|
|
45
|
+
<array>
|
|
46
|
+
<string>arm64</string>
|
|
47
|
+
</array>
|
|
48
|
+
<key>UIStatusBarStyle</key>
|
|
49
|
+
<string>UIStatusBarStyleDefault</string>
|
|
50
|
+
<key>UIApplicationSceneManifest</key>
|
|
51
|
+
<dict>
|
|
52
|
+
<key>UIApplicationSupportsMultipleScenes</key>
|
|
53
|
+
<false/>
|
|
54
|
+
<key>UISceneConfigurations</key>
|
|
55
|
+
<dict>
|
|
56
|
+
<key>UIWindowSceneSessionRoleApplication</key>
|
|
57
|
+
<array>
|
|
58
|
+
<dict>
|
|
59
|
+
<key>UISceneConfigurationName</key>
|
|
60
|
+
<string>Default Configuration</string>
|
|
61
|
+
<key>UISceneDelegateClassName</key>
|
|
62
|
+
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
|
|
63
|
+
</dict>
|
|
64
|
+
</array>
|
|
65
|
+
</dict>
|
|
66
|
+
</dict>
|
|
67
|
+
<key>UISupportedInterfaceOrientations</key>
|
|
68
|
+
<array>
|
|
69
|
+
<string>UIInterfaceOrientationPortrait</string>
|
|
70
|
+
<string>UIInterfaceOrientationLandscapeLeft</string>
|
|
71
|
+
<string>UIInterfaceOrientationLandscapeRight</string>
|
|
72
|
+
</array>
|
|
73
|
+
<key>UIViewControllerBasedStatusBarAppearance</key>
|
|
74
|
+
<false/>
|
|
75
|
+
</dict>
|
|
76
|
+
</plist>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>NSPrivacyAccessedAPITypes</key>
|
|
6
|
+
<array>
|
|
7
|
+
<dict>
|
|
8
|
+
<key>NSPrivacyAccessedAPIType</key>
|
|
9
|
+
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
|
10
|
+
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
11
|
+
<array>
|
|
12
|
+
<string>CA92.1</string>
|
|
13
|
+
</array>
|
|
14
|
+
</dict>
|
|
15
|
+
<dict>
|
|
16
|
+
<key>NSPrivacyAccessedAPIType</key>
|
|
17
|
+
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
|
18
|
+
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
19
|
+
<array>
|
|
20
|
+
<string>0A2A.1</string>
|
|
21
|
+
<string>3B52.1</string>
|
|
22
|
+
<string>C617.1</string>
|
|
23
|
+
</array>
|
|
24
|
+
</dict>
|
|
25
|
+
<dict>
|
|
26
|
+
<key>NSPrivacyAccessedAPIType</key>
|
|
27
|
+
<string>NSPrivacyAccessedAPICategoryDiskSpace</string>
|
|
28
|
+
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
29
|
+
<array>
|
|
30
|
+
<string>E174.1</string>
|
|
31
|
+
<string>85F4.1</string>
|
|
32
|
+
</array>
|
|
33
|
+
</dict>
|
|
34
|
+
<dict>
|
|
35
|
+
<key>NSPrivacyAccessedAPIType</key>
|
|
36
|
+
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
|
|
37
|
+
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
38
|
+
<array>
|
|
39
|
+
<string>35F9.1</string>
|
|
40
|
+
</array>
|
|
41
|
+
</dict>
|
|
42
|
+
</array>
|
|
43
|
+
<key>NSPrivacyCollectedDataTypes</key>
|
|
44
|
+
<array/>
|
|
45
|
+
<key>NSPrivacyTracking</key>
|
|
46
|
+
<false/>
|
|
47
|
+
</dict>
|
|
48
|
+
</plist>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
internal import Expo
|
|
2
|
+
import React
|
|
3
|
+
import ReactAppDependencyProvider
|
|
4
|
+
import UIKit
|
|
5
|
+
|
|
6
|
+
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|
7
|
+
var window: UIWindow?
|
|
8
|
+
var reactNativeDelegate: ReactNativeDelegate?
|
|
9
|
+
var reactNativeFactory: ExpoReactNativeFactory?
|
|
10
|
+
|
|
11
|
+
func scene(
|
|
12
|
+
_ scene: UIScene,
|
|
13
|
+
willConnectTo _: UISceneSession,
|
|
14
|
+
options _: UIScene.ConnectionOptions
|
|
15
|
+
) {
|
|
16
|
+
guard let windowScene = scene as? UIWindowScene else {
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let bundleURL = SceneDelegate.bundleURL()
|
|
21
|
+
let title = launchArgument("--bleam-title")
|
|
22
|
+
let appearance = projectAppearance()
|
|
23
|
+
|
|
24
|
+
let delegate = ReactNativeDelegate(bundleURL: bundleURL)
|
|
25
|
+
let factory = ExpoReactNativeFactory(delegate: delegate)
|
|
26
|
+
delegate.dependencyProvider = RCTAppDependencyProvider()
|
|
27
|
+
|
|
28
|
+
let window = UIWindow(windowScene: windowScene)
|
|
29
|
+
if let windowBackgroundColor = appearance.windowBackgroundColor {
|
|
30
|
+
window.backgroundColor = windowBackgroundColor
|
|
31
|
+
}
|
|
32
|
+
window.overrideUserInterfaceStyle = appearance.userInterfaceStyle
|
|
33
|
+
factory.startReactNative(
|
|
34
|
+
withModuleName: "main",
|
|
35
|
+
in: window,
|
|
36
|
+
initialProperties: [:],
|
|
37
|
+
launchOptions: nil
|
|
38
|
+
)
|
|
39
|
+
window.rootViewController?.overrideUserInterfaceStyle = appearance.userInterfaceStyle
|
|
40
|
+
if let rootView = window.rootViewController?.view {
|
|
41
|
+
applyRootAppearance(to: rootView)
|
|
42
|
+
}
|
|
43
|
+
window.rootViewController?.title = title ?? "Bleam"
|
|
44
|
+
|
|
45
|
+
self.window = window
|
|
46
|
+
reactNativeDelegate = delegate
|
|
47
|
+
reactNativeFactory = factory
|
|
48
|
+
OTAUpdateManager.shared.checkForUpdate()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private static func bundleURL() -> URL? {
|
|
52
|
+
guard let bundleURLString = launchArgument("--bleam-bundle-url") else {
|
|
53
|
+
return nil
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return URL(string: bundleURLString)
|
|
57
|
+
}
|
|
58
|
+
}
|