@craft-native/ios 0.0.153 → 0.0.154
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
CHANGED
|
@@ -602,8 +602,12 @@ await fetch('https://example.com/upload', { method: 'POST', body: form });
|
|
|
602
602
|
// A file for the share sheet (Save to Files, AirDrop, another app), fetched
|
|
603
603
|
// straight to disk with your headers. Not 2xx: { ok: false, status, body }.
|
|
604
604
|
const gpx = await craft.files.download('https://example.com/route.gpx', { headers: { Authorization: 'Bearer …' } });
|
|
605
|
-
if (gpx.ok) await craft.share({ files: [gpx
|
|
605
|
+
if (gpx.ok) await craft.share({ files: [gpx] }); // a file, or its path
|
|
606
606
|
await craft.files.remove(gpx.path); // or leave it: tmp, swept after a day
|
|
607
|
+
// Something the screen made itself (a CSV export): text crosses the bridge
|
|
608
|
+
// once, as it is; bytes go as base64 ({ encoding: 'base64' }).
|
|
609
|
+
const csv = await craft.files.write('health-data.csv', 'date,steps\n…', { type: 'text/csv' });
|
|
610
|
+
await craft.share({ files: [csv] });
|
|
607
611
|
|
|
608
612
|
// Speech: short spoken cues that duck the person's music instead of stopping
|
|
609
613
|
// it, and play with the silent switch on. Always available (no config flag).
|
package/package.json
CHANGED
|
@@ -171,6 +171,16 @@ enum CraftNativeActions {
|
|
|
171
171
|
case ("Files", "download"):
|
|
172
172
|
download(requestToken: requestToken, args: args, completion: completion)
|
|
173
173
|
return
|
|
174
|
+
case ("Files", "write"):
|
|
175
|
+
// Something the screen made (a CSV export) as a file for the
|
|
176
|
+
// share sheet; the disk work is off the main thread.
|
|
177
|
+
let name = args.first as? String ?? ""
|
|
178
|
+
let data = args.count > 1 ? args[1] : nil
|
|
179
|
+
let options = args.count > 2 ? args[2] as? [String: Any] ?? [:] : [:]
|
|
180
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
181
|
+
complete(requestToken, CraftNativeFiles.write(name: name, data: data, options: options), completion)
|
|
182
|
+
}
|
|
183
|
+
return
|
|
174
184
|
case ("Files", "remove"):
|
|
175
185
|
sync = .success(CraftNativeFiles.remove(args.first as? String ?? ""))
|
|
176
186
|
case ("Lifecycle", "getState"):
|
|
@@ -329,6 +329,40 @@ enum CraftNativeFiles {
|
|
|
329
329
|
}
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
+
/// What a screen made itself (a CSV export, a route it built) written as
|
|
333
|
+
/// a file for the share sheet: `data` is text (`encoding: 'utf8'`, the
|
|
334
|
+
/// default, so a CSV crosses the bridge once and no larger) or base64
|
|
335
|
+
/// (`encoding: 'base64'`, for bytes). What is already on a server is
|
|
336
|
+
/// `download`ed instead, straight to disk.
|
|
337
|
+
static func write(name: String, data: Any?, options: [String: Any]) -> Result<Any, CraftNativeActionError> {
|
|
338
|
+
guard !name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else {
|
|
339
|
+
return .failure(CraftNativeActionError(code: "INVALID_ARGUMENT", message: "files.write needs a file name"))
|
|
340
|
+
}
|
|
341
|
+
guard let text = data as? String else {
|
|
342
|
+
return .failure(CraftNativeActionError(code: "INVALID_ARGUMENT", message: "files.write needs its contents as a string"))
|
|
343
|
+
}
|
|
344
|
+
let bytes: Data
|
|
345
|
+
switch options["encoding"] as? String ?? "utf8" {
|
|
346
|
+
case "utf8":
|
|
347
|
+
bytes = Data(text.utf8)
|
|
348
|
+
case "base64":
|
|
349
|
+
guard let decoded = Data(base64Encoded: text.filter { !$0.isWhitespace }) else {
|
|
350
|
+
return .failure(CraftNativeActionError(code: "INVALID_ARGUMENT", message: "files.write was given base64 it could not read"))
|
|
351
|
+
}
|
|
352
|
+
bytes = decoded
|
|
353
|
+
default:
|
|
354
|
+
return .failure(CraftNativeActionError(code: "INVALID_ARGUMENT", message: "files.write encoding is 'utf8' or 'base64'"))
|
|
355
|
+
}
|
|
356
|
+
let type = (options["type"] as? String).flatMap { $0.isEmpty ? nil : $0 } ?? "application/octet-stream"
|
|
357
|
+
do {
|
|
358
|
+
let url = try newFile(named: name)
|
|
359
|
+
try bytes.write(to: url, options: .atomic)
|
|
360
|
+
return .success(describe(url, type: type))
|
|
361
|
+
} catch {
|
|
362
|
+
return .failure(CraftNativeActionError(code: "WRITE_ERROR", message: error.localizedDescription))
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
332
366
|
/// A refused download: its status and the start of its answer, so the
|
|
333
367
|
/// screen can say what the server said. No file is kept.
|
|
334
368
|
static func refusedDownload(at location: URL?, response: HTTPURLResponse) -> [String: Any] {
|
|
@@ -527,7 +527,11 @@ enum CraftNativeShare {
|
|
|
527
527
|
if let title = options["title"] as? String, !title.isEmpty { items.append(title) }
|
|
528
528
|
if let text = options["text"] as? String, !text.isEmpty { items.append(text) }
|
|
529
529
|
if let address = options["url"] as? String, let url = URL(string: address) { items.append(url) }
|
|
530
|
-
|
|
530
|
+
// A path or file URL, or a file craft.files made (`{ path, name }`).
|
|
531
|
+
let files = (options["files"] as? [Any] ?? []).compactMap { item -> String? in
|
|
532
|
+
item as? String ?? (item as? [String: Any])?["path"] as? String
|
|
533
|
+
}
|
|
534
|
+
for file in files {
|
|
531
535
|
if let url = URL(string: file), url.isFileURL { items.append(url) }
|
|
532
536
|
else if FileManager.default.fileExists(atPath: file) { items.append(URL(fileURLWithPath: file)) }
|
|
533
537
|
}
|