refactor: improve alert error context and copy action

This commit is contained in:
世界
2026-02-08 18:25:55 +08:00
parent a86dfc26d1
commit 8897e6deeb
32 changed files with 125 additions and 82 deletions
@@ -7,24 +7,24 @@ open class BaseViewModel: ObservableObject {
public init() {} public init() {}
public func showError(_ error: Error) { public func showError(_ error: Error, action: String) {
alert = AlertState(error: error) alert = AlertState(action: action, error: error)
} }
public func execute(_ operation: () async throws -> Void) async { public func execute(_ operation: () async throws -> Void, action: String) async {
do { do {
try await operation() try await operation()
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: action, error: error)
} }
} }
public func executeOnBackground(_ operation: @escaping @Sendable () async throws -> Void) async { public func executeOnBackground(_ operation: @escaping @Sendable () async throws -> Void, action: String) async {
do { do {
try await operation() try await operation()
} catch { } catch {
await MainActor.run { await MainActor.run {
alert = AlertState(error: error) alert = AlertState(action: action, error: error)
} }
} }
} }
@@ -105,7 +105,7 @@ public struct GlobalChecksModifier: ViewModifier {
try await profile.importProfile() try await profile.importProfile()
} catch { } catch {
await MainActor.run { await MainActor.run {
alert = AlertState(error: error) alert = AlertState(action: "import profile", error: error)
} }
return return
} }
@@ -174,7 +174,7 @@ public struct GlobalChecksModifier: ViewModifier {
} }
} catch { } catch {
await MainActor.run { await MainActor.run {
alert = AlertState(error: error) alert = AlertState(action: "check deprecated notes", error: error)
} }
} }
} }
@@ -86,7 +86,7 @@ public struct ShareButtonCompat<Label: View>: View {
} }
} catch { } catch {
await MainActor.run { await MainActor.run {
alert = AlertState(error: error) alert = AlertState(action: "prepare share file", error: error)
} }
} }
} }
@@ -117,7 +117,7 @@ public struct ShareButtonCompat<Label: View>: View {
} }
} catch { } catch {
await MainActor.run { await MainActor.run {
alert = AlertState(error: error) alert = AlertState(action: "prepare share file", error: error)
} }
} }
} }
@@ -90,7 +90,7 @@ public class ConnectionListViewModel: BaseViewModel {
do { do {
try LibboxNewStandaloneCommandClient()!.closeConnections() try LibboxNewStandaloneCommandClient()!.closeConnections()
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "close all connections", error: error)
} }
} }
@@ -118,7 +118,7 @@ public struct ConnectionView: View {
try await LibboxNewStandaloneCommandClient()!.closeConnection(connection.id) try await LibboxNewStandaloneCommandClient()!.closeConnection(connection.id)
} catch { } catch {
await MainActor.run { await MainActor.run {
alert = AlertState(error: error) alert = AlertState(action: "close connection", error: error)
} }
} }
} }
@@ -195,7 +195,7 @@ public struct ClashModeCard: View {
try LibboxNewStandaloneCommandClient()!.setClashMode(newMode) try LibboxNewStandaloneCommandClient()!.setClashMode(newMode)
} catch { } catch {
await MainActor.run { await MainActor.run {
alert = AlertState(error: error) alert = AlertState(action: "set clash mode", error: error)
} }
} }
} }
@@ -134,7 +134,7 @@ public struct ProfileCard: View {
) { result in ) { result in
viewModel.exportDocument = nil viewModel.exportDocument = nil
if case let .failure(error) = result { if case let .failure(error) = result {
viewModel.alert = AlertState(error: error) viewModel.alert = AlertState(action: "export profile", error: error)
} }
} }
#endif #endif
@@ -371,7 +371,7 @@ public struct ProfileCard: View {
) )
#endif #endif
} catch { } catch {
viewModel.alert = AlertState(error: error) viewModel.alert = AlertState(action: "share profile", error: error)
} }
} }
} }
@@ -394,7 +394,7 @@ public struct ProfileCard: View {
} }
} catch { } catch {
await MainActor.run { await MainActor.run {
viewModel.alert = AlertState(error: error) viewModel.alert = AlertState(action: "export profile", error: error)
} }
} }
} }
@@ -427,7 +427,7 @@ public struct ProfileCard: View {
do { do {
viewModel.qrsShareData = try await profile.origin.encodedContentDataAsync() viewModel.qrsShareData = try await profile.origin.encodedContentDataAsync()
} catch { } catch {
viewModel.alert = AlertState(error: error) viewModel.alert = AlertState(action: "prepare QRS share", error: error)
viewModel.showQRSShare = false viewModel.showQRSShare = false
} }
} }
@@ -536,7 +536,7 @@ extension ProfileCard {
try await profile.updateRemoteProfile() try await profile.updateRemoteProfile()
environments.profileUpdate.send() environments.profileUpdate.send()
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "update remote profile", error: error)
} }
} }
} }
@@ -402,7 +402,7 @@ struct ProfilePickerSheet: View {
try await profile.origin.updateRemoteProfile() try await profile.origin.updateRemoteProfile()
environments.profileUpdate.send() environments.profileUpdate.send()
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "update remote profile", error: error)
} }
} }
@@ -742,7 +742,7 @@ private struct ProfilePickerRow: View {
) { result in ) { result in
exportDocument = nil exportDocument = nil
if case let .failure(error) = result { if case let .failure(error) = result {
alert = AlertState(error: error) alert = AlertState(action: "export profile", error: error)
} }
} }
} }
@@ -789,7 +789,7 @@ private struct ProfilePickerRow: View {
) { result in ) { result in
exportDocument = nil exportDocument = nil
if case let .failure(error) = result { if case let .failure(error) = result {
alert = AlertState(error: error) alert = AlertState(action: "export profile", error: error)
} }
} }
} }
@@ -919,7 +919,7 @@ private struct ProfilePickerRow: View {
} }
} catch { } catch {
await MainActor.run { await MainActor.run {
alert = AlertState(error: error) alert = AlertState(action: "prepare QRS share", error: error)
showQRSShare = false showQRSShare = false
} }
} }
@@ -1015,7 +1015,7 @@ private struct ProfilePickerRow: View {
} }
} catch { } catch {
await MainActor.run { await MainActor.run {
alert = AlertState(error: error) alert = AlertState(action: "export profile", error: error)
} }
} }
} }
@@ -1067,7 +1067,7 @@ private struct ProfilePickerRow: View {
} }
} catch { } catch {
await MainActor.run { await MainActor.run {
alert = AlertState(error: error) alert = AlertState(action: "share profile", error: error)
} }
} }
} }
@@ -1091,7 +1091,7 @@ private struct ProfilePickerRow: View {
} }
} catch { } catch {
await MainActor.run { await MainActor.run {
alert = AlertState(error: error) alert = AlertState(action: "export profile", error: error)
} }
} }
} }
@@ -1282,7 +1282,7 @@ private struct ProfilePickerRow: View {
) { result in ) { result in
exportDocument = nil exportDocument = nil
if case let .failure(error) = result { if case let .failure(error) = result {
alert = AlertState(error: error) alert = AlertState(action: "export profile", error: error)
} }
} }
} }
@@ -1336,7 +1336,7 @@ private struct ProfilePickerRow: View {
} }
} catch { } catch {
await MainActor.run { await MainActor.run {
alert = AlertState(error: error) alert = AlertState(action: "prepare QRS share", error: error)
showQRSShare = false showQRSShare = false
} }
} }
@@ -1405,7 +1405,7 @@ private struct ProfilePickerRow: View {
} }
} catch { } catch {
await MainActor.run { await MainActor.run {
alert = AlertState(error: error) alert = AlertState(action: "export profile", error: error)
} }
} }
} }
@@ -26,7 +26,7 @@ public struct InstallProfileButton: View {
try await ExtensionProfile.install() try await ExtensionProfile.install()
await callback() await callback()
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "install network extension", error: error)
} }
} }
} }
@@ -31,7 +31,7 @@
} }
await callback() await callback()
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "install system extension", error: error)
} }
} }
} }
@@ -165,7 +165,8 @@ public struct StartStopButton: View {
} catch { } catch {
await MainActor.run { await MainActor.run {
isStarting = false isStarting = false
alert = AlertState(error: error) let action = isEnabled ? "start service" : "stop service"
alert = AlertState(action: action, error: error)
} }
} }
} }
@@ -69,7 +69,7 @@ public final class DashboardViewModel: BaseViewModel {
await SharedPreferences.selectedProfileID.set(selectedProfileID) await SharedPreferences.selectedProfileID.set(selectedProfileID)
} }
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "load profile list", error: error)
return return
} }
} }
@@ -109,7 +109,7 @@ extension ExtensionProfile {
) )
} }
#endif #endif
return AlertState(error: nsError) return AlertState(action: "fetch last disconnect error", error: nsError)
} }
} }
@@ -15,7 +15,7 @@ public final class OverviewViewModel: BaseViewModel {
do { do {
try await profile.reloadService() try await profile.reloadService()
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "reload service", error: error)
} }
} }
reasserting = false reasserting = false
@@ -32,7 +32,7 @@ public final class OverviewViewModel: BaseViewModel {
await MainActor.run { reasserting = false } await MainActor.run { reasserting = false }
} }
} catch { } catch {
await MainActor.run { alert = AlertState(error: error) } await MainActor.run { alert = AlertState(action: "update system proxy settings", error: error) }
} }
} }
} }
@@ -90,7 +90,7 @@ public class GroupListViewModel: BaseViewModel {
try await LibboxNewStandaloneCommandClient()!.selectOutbound(groupTag, outboundTag: outboundTag) try await LibboxNewStandaloneCommandClient()!.selectOutbound(groupTag, outboundTag: outboundTag)
} catch { } catch {
await MainActor.run { await MainActor.run {
alert = AlertState(error: error) alert = AlertState(action: "select outbound", error: error)
} }
} }
} }
@@ -109,7 +109,7 @@ public class GroupListViewModel: BaseViewModel {
try await LibboxNewStandaloneCommandClient()!.setGroupExpand(tag, isExpand: isExpand) try await LibboxNewStandaloneCommandClient()!.setGroupExpand(tag, isExpand: isExpand)
} catch { } catch {
await MainActor.run { await MainActor.run {
alert = AlertState(error: error) alert = AlertState(action: "update group expansion", error: error)
} }
} }
} }
@@ -125,7 +125,7 @@ public class GroupListViewModel: BaseViewModel {
try await LibboxNewStandaloneCommandClient()!.urlTest(tag) try await LibboxNewStandaloneCommandClient()!.urlTest(tag)
} catch { } catch {
await MainActor.run { await MainActor.run {
alert = AlertState(error: error) alert = AlertState(action: "run URL test", error: error)
} }
} }
} }
+1 -1
View File
@@ -399,7 +399,7 @@ private struct LogContentInnerView: View {
cleanup() cleanup()
logFileURL = nil logFileURL = nil
if case let .failure(error) = result { if case let .failure(error) = result {
alert = AlertState(error: error) alert = AlertState(action: "export log file", error: error)
} }
} }
.sheet(isPresented: $showShareSheet) { .sheet(isPresented: $showShareSheet) {
@@ -135,7 +135,7 @@ public class LogDataModel: ObservableObject {
try text.write(to: fileURL, atomically: true, encoding: .utf8) try text.write(to: fileURL, atomically: true, encoding: .utf8)
logFileURL = fileURL logFileURL = fileURL
} catch { } catch {
viewModel?.alert = AlertState(error: error) viewModel?.alert = AlertState(action: "prepare log file", error: error)
} }
} }
#endif #endif
@@ -85,7 +85,7 @@ public final class EditProfileContentViewModel: BaseViewModel {
do { do {
try await loadContentBackground() try await loadContentBackground()
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "load profile content", error: error)
} }
isLoading = false isLoading = false
} }
@@ -111,7 +111,7 @@ public final class EditProfileContentViewModel: BaseViewModel {
do { do {
try await saveContentBackground(profile) try await saveContentBackground(profile)
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "save profile content", error: error)
return return
} }
isChanged = false isChanged = false
@@ -21,7 +21,7 @@ public final class EditProfileViewModel: BaseViewModel {
try await profile.updateRemoteProfile() try await profile.updateRemoteProfile()
environments.profileUpdate.send() environments.profileUpdate.send()
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "update remote profile", error: error)
} }
} }
@@ -29,7 +29,7 @@ public final class EditProfileViewModel: BaseViewModel {
do { do {
try await ProfileManager.delete(profile) try await ProfileManager.delete(profile)
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "delete profile", error: error)
return return
} }
environments.profileUpdate.send() environments.profileUpdate.send()
@@ -46,7 +46,7 @@ public final class EditProfileViewModel: BaseViewModel {
#endif #endif
try await profile.onProfileUpdated() try await profile.onProfileUpdated()
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "save profile", error: error)
return return
} }
isChanged = false isChanged = false
@@ -38,7 +38,7 @@
case let .failed(error): case let .failed(error):
DispatchQueue.main.async { [self] in DispatchQueue.main.async { [self] in
reset() reset()
alert = AlertState(error: error) alert = AlertState(action: "connect to import source", error: error)
} }
default: break default: break
} }
@@ -47,7 +47,7 @@
do { do {
try await loopMessages(environments: environments) try await loopMessages(environments: environments)
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "import profile from device", error: error)
reset() reset()
} }
} }
@@ -121,7 +121,7 @@
try await socket.write(request.encode()) try await socket.write(request.encode())
} catch { } catch {
isImporting = false isImporting = false
alert = AlertState(error: error) alert = AlertState(action: "request profile content from device", error: error)
reset() reset()
} }
} }
@@ -223,14 +223,14 @@ public struct NewProfileMenuView: View {
environments.profileUpdate.send() environments.profileUpdate.send()
dismiss() dismiss()
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "import profile", error: error)
} }
} }
}, },
secondaryButton: .cancel() secondaryButton: .cancel()
) )
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "read imported profile file", error: error)
} }
} }
} }
@@ -250,7 +250,7 @@ public struct NewProfileMenuView: View {
var error: NSError? var error: NSError?
let remoteProfile = LibboxParseRemoteProfileImportLink(string, &error) let remoteProfile = LibboxParseRemoteProfileImportLink(string, &error)
if let error { if let error {
alert = AlertState(error: error) alert = AlertState(action: "parse QR code profile link", error: error)
return return
} }
guard let remoteProfile else { guard let remoteProfile else {
@@ -274,14 +274,14 @@ public struct NewProfileMenuView: View {
environments.profileUpdate.send() environments.profileUpdate.send()
dismiss() dismiss()
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "import profile", error: error)
} }
} }
}, },
secondaryButton: .cancel() secondaryButton: .cancel()
) )
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "decode QRS profile data", error: error)
} }
} }
#endif #endif
@@ -185,7 +185,7 @@ public struct NewProfileView: View {
viewModel.fileURL = urls[0] viewModel.fileURL = urls[0]
} }
} catch { } catch {
viewModel.alert = AlertState(error: error) viewModel.alert = AlertState(action: "read imported profile file", error: error)
return return
} }
} }
@@ -213,7 +213,7 @@ public struct NewProfileView: View {
viewModel.fileURL = urls[0] viewModel.fileURL = urls[0]
} }
} catch { } catch {
viewModel.alert = AlertState(error: error) viewModel.alert = AlertState(action: "read imported profile file", error: error)
return return
} }
} }
@@ -72,7 +72,7 @@ public final class NewProfileViewModel: BaseViewModel {
do { do {
createdProfile = try await createProfileBackground() createdProfile = try await createProfileBackground()
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "create profile", error: error)
return return
} }
@@ -160,7 +160,7 @@
) )
#endif #endif
default: default:
alert = AlertState(error: error) alert = AlertState(action: "scan QR code", error: error)
} }
} }
} }
@@ -204,7 +204,7 @@ public struct CoreView: View {
try await environments.extensionProfile!.stop() try await environments.extensionProfile!.stop()
await destroyWorkingDirectory() await destroyWorkingDirectory()
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "stop service before destroying working directory", error: error)
} }
} }
@@ -233,7 +233,7 @@ public struct CoreView: View {
#endif #endif
isLoading = true isLoading = true
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "destroy working directory", error: error)
} }
} }
@@ -281,7 +281,7 @@ public struct CoreView: View {
try await manager.signalEnumerator(for: .workingSet) try await manager.signalEnumerator(for: .workingSet)
} catch { } catch {
await MainActor.run { await MainActor.run {
alert = AlertState(error: error) alert = AlertState(action: "notify Files app about working directory changes", error: error)
} }
} }
} }
@@ -300,7 +300,7 @@ public struct CoreView: View {
} }
} catch { } catch {
await MainActor.run { await MainActor.run {
alert = AlertState(error: error) alert = AlertState(action: "open working directory in Files", error: error)
} }
} }
} }
@@ -109,14 +109,14 @@ public struct AppView: View {
try HelperServiceManager.registerRootHelper() try HelperServiceManager.registerRootHelper()
refreshHelperStatus() refreshHelperStatus()
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "update helper service", error: error)
} }
} }
} label: { } label: {
Label("Update", systemImage: "arrow.down.doc.fill") Label("Update", systemImage: "arrow.down.doc.fill")
} }
FormButton(role: .destructive) { FormButton(role: .destructive) {
performHelperAction { performHelperAction(actionName: "uninstall helper service") {
try HelperServiceManager.unregisterRootHelper() try HelperServiceManager.unregisterRootHelper()
} }
} label: { } label: {
@@ -130,7 +130,7 @@ public struct AppView: View {
} }
} else { } else {
FormButton { FormButton {
performHelperAction { performHelperAction(actionName: "install helper service") {
try HelperServiceManager.registerRootHelper() try HelperServiceManager.registerRootHelper()
} }
} label: { } label: {
@@ -210,7 +210,7 @@ public struct AppView: View {
try SMAppService.mainApp.unregister() try SMAppService.mainApp.unregister()
} }
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "update login items", error: error)
} }
} }
@@ -231,7 +231,7 @@ public struct AppView: View {
} }
} }
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "update system extension", error: error)
} }
} }
@@ -252,16 +252,16 @@ public struct AppView: View {
} }
} }
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "uninstall system extension", error: error)
} }
} }
private func performHelperAction(_ action: () throws -> Void) { private func performHelperAction(actionName: String, _ action: () throws -> Void) {
do { do {
try action() try action()
refreshHelperStatus() refreshHelperStatus()
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: actionName, error: error)
} }
} }
@@ -264,7 +264,7 @@ public struct OnDemandRulesView: View {
await updateService() await updateService()
isLoading = true isLoading = true
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "reset on-demand rules", error: error)
} }
} }
} label: { } label: {
@@ -289,7 +289,7 @@ public struct OnDemandRulesView: View {
let enabled = mode != .disabled let enabled = mode != .disabled
try await profile.updateOnDemand(enabled: enabled, useDefaultRules: mode == .alwaysOn) try await profile.updateOnDemand(enabled: enabled, useDefaultRules: mode == .alwaysOn)
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "update on-demand rules", error: error)
} }
} }
@@ -111,7 +111,7 @@ struct PacketTunnelView: View {
do { do {
try await profile.restart() try await profile.restart()
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "restart service", error: error)
} }
} }
@@ -65,7 +65,7 @@ public struct ProfileOverrideView: View {
do { do {
try await profile.reloadService() try await profile.reloadService()
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "reload service", error: error)
} }
} }
+38 -3
View File
@@ -1,5 +1,10 @@
import Foundation import Foundation
import SwiftUI import SwiftUI
#if canImport(UIKit)
import UIKit
#elseif canImport(AppKit)
import AppKit
#endif
public struct AlertState: Equatable { public struct AlertState: Equatable {
public var title: String public var title: String
@@ -36,15 +41,45 @@ public struct AlertState: Equatable {
} }
} }
public init(error: Error, dismiss: (() -> Void)? = nil) { private static func formatErrorMessage(action: String, error: Error) -> String {
self.init(errorMessage: error.localizedDescription, dismiss: dismiss) let normalizedAction = action.trimmingCharacters(in: .whitespacesAndNewlines)
let normalizedDescription = error.localizedDescription.trimmingCharacters(in: .whitespacesAndNewlines)
let actionText = normalizedAction.isEmpty ? "complete operation" : normalizedAction
if normalizedDescription.isEmpty {
return "Failed to \(actionText)"
}
return "Failed to \(actionText)\n\(normalizedDescription)"
} }
public init(errorMessage: String, dismiss: (() -> Void)? = nil) { private static func copyErrorMessage(_ text: String) {
#if canImport(UIKit)
UIPasteboard.general.string = text
#elseif canImport(AppKit)
NSPasteboard.general.clearContents()
NSPasteboard.general.setString(text, forType: .string)
#endif
}
public init(action: String, error: Error, dismiss: (() -> Void)? = nil) {
self.init(
errorMessage: Self.formatErrorMessage(action: action, error: error),
dismiss: dismiss,
allowsCopy: true
)
}
public init(errorMessage: String, dismiss: (() -> Void)? = nil, allowsCopy: Bool = false) {
title = String(localized: "Error") title = String(localized: "Error")
message = errorMessage message = errorMessage
if allowsCopy {
primaryButton = .default(String(localized: "Copy")) {
Self.copyErrorMessage(errorMessage)
}
secondaryButton = .default(String(localized: "Ok"), action: dismiss)
} else {
primaryButton = .default(String(localized: "Ok"), action: dismiss) primaryButton = .default(String(localized: "Ok"), action: dismiss)
secondaryButton = nil secondaryButton = nil
}
onDismiss = nil onDismiss = nil
} }
+7
View File
@@ -148,6 +148,13 @@ open class ExtensionProvider: NEPacketTunnelProvider {
throw ExtensionStartupError("(packet-tunnel) error: setup service: \(setupError.localizedDescription)") throw ExtensionStartupError("(packet-tunnel) error: setup service: \(setupError.localizedDescription)")
} }
let stderrPath = URL(fileURLWithPath: tempPath, isDirectory: true).appendingPathComponent("stderr.log").path
var stderrError: NSError?
LibboxRedirectStderr(stderrPath, &stderrError)
if let stderrError {
throw ExtensionStartupError("(packet-tunnel) redirect stderr error: \(stderrError.localizedDescription)")
}
let ignoreMemoryLimit = (effectiveOptions["ignoreMemoryLimit"] as? NSNumber)?.boolValue ?? false let ignoreMemoryLimit = (effectiveOptions["ignoreMemoryLimit"] as? NSNumber)?.boolValue ?? false
LibboxSetMemoryLimit(!ignoreMemoryLimit) LibboxSetMemoryLimit(!ignoreMemoryLimit)
+2 -2
View File
@@ -45,7 +45,7 @@ public class MainViewModel: BaseViewModel {
var error: NSError? var error: NSError?
importRemoteProfile = LibboxParseRemoteProfileImportLink(url.absoluteString, &error) importRemoteProfile = LibboxParseRemoteProfileImportLink(url.absoluteString, &error)
if let error { if let error {
alert = AlertState(error: error) alert = AlertState(action: "parse remote profile import link", error: error)
} }
} else if url.pathExtension == "bpf" { } else if url.pathExtension == "bpf" {
Task { Task {
@@ -62,7 +62,7 @@ public class MainViewModel: BaseViewModel {
try await .from(readURL(url)) try await .from(readURL(url))
} }
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "import profile from URL", error: error)
} }
} }
+2 -2
View File
@@ -215,7 +215,7 @@ struct MainView: View {
var error: NSError? var error: NSError?
importRemoteProfile = LibboxParseRemoteProfileImportLink(url.absoluteString, &error) importRemoteProfile = LibboxParseRemoteProfileImportLink(url.absoluteString, &error)
if let error { if let error {
alert = AlertState(error: error) alert = AlertState(action: "parse remote profile import link", error: error)
} }
} else if url.pathExtension == "bpf" { } else if url.pathExtension == "bpf" {
do { do {
@@ -223,7 +223,7 @@ struct MainView: View {
try .from(Data(contentsOf: url)) try .from(Data(contentsOf: url))
} }
} catch { } catch {
alert = AlertState(error: error) alert = AlertState(action: "import profile from URL", error: error)
} }
} else { } else {
alert = AlertState(errorMessage: String(localized: "Handled unknown URL \(url.absoluteString)")) alert = AlertState(errorMessage: String(localized: "Handled unknown URL \(url.absoluteString)"))