Prepare airdrop support

This commit is contained in:
世界
2023-07-26 20:38:51 +08:00
parent 486a685bb5
commit 2bd462c789
52 changed files with 488 additions and 455 deletions
@@ -0,0 +1,47 @@
import Foundation
import SwiftUI
public extension Alert {
init(_ error: Error, _ dismissAction: (() -> Void)? = nil) {
self.init(
errorMessage: error.localizedDescription,
dismissAction
)
}
init(errorMessage: String, _ dismissAction: (() -> Void)? = nil) {
self.init(
title: Text("Error"),
message: Text(errorMessage),
dismissButton: .default(Text("Ok")) {
dismissAction?()
}
)
}
}
public extension View {
func alertBinding(_ binding: Binding<Alert?>) -> some View {
alert(isPresented: Binding(get: {
binding.wrappedValue != nil
}, set: { newValue, _ in
if !newValue {
binding.wrappedValue = nil
}
})) {
binding.wrappedValue!
}
}
func alertBinding(_ binding: Binding<Alert?>, _ isLoading: Binding<Bool>) -> some View {
alert(isPresented: Binding(get: {
binding.wrappedValue != nil
}, set: { newValue, _ in
if !newValue, !isLoading.wrappedValue {
binding.wrappedValue = nil
}
})) {
binding.wrappedValue!
}
}
}
@@ -16,9 +16,7 @@ public struct ActiveDashboardView: View {
@State private var selectedProfileID: Int64!
@State private var reasserting = false
@State private var observer: Any?
@State private var errorPresented = false
@State private var errorMessage = ""
@State private var alert: Alert?
public init() {}
@@ -78,43 +76,37 @@ public struct ActiveDashboardView: View {
}
}
}
.alert(isPresented: $errorPresented) {
Alert(
title: Text("Error"),
message: Text(errorMessage),
dismissButton: .default(Text("Ok"))
)
}
.alertBinding($alert)
#if os(iOS)
.onChange(of: scenePhase, perform: { newValue in
if newValue == .active {
Task.detached {
await doReload()
}
}
})
.onChange(of: selection.wrappedValue, perform: { newValue in
if newValue == .dashboard {
Task.detached {
await doReload()
}
}
})
#elseif os(macOS)
.onAppear {
if observer == nil {
observer = NotificationCenter.default.addObserver(forName: ActiveDashboardView.NotificationUpdateSelectedProfile, object: nil, queue: nil, using: { _ in
.onChange(of: scenePhase, perform: { newValue in
if newValue == .active {
Task.detached {
await doReload()
}
})
}
})
.onChange(of: selection.wrappedValue, perform: { newValue in
if newValue == .dashboard {
Task.detached {
await doReload()
}
}
})
#elseif os(macOS)
.onAppear {
if observer == nil {
observer = NotificationCenter.default.addObserver(forName: ActiveDashboardView.NotificationUpdateSelectedProfile, object: nil, queue: nil, using: { _ in
Task.detached {
await doReload()
}
})
}
}
}
.onDisappear {
if let observer {
NotificationCenter.default.removeObserver(observer)
.onDisappear {
if let observer {
NotificationCenter.default.removeObserver(observer)
}
}
}
#endif
}
@@ -132,8 +124,7 @@ public struct ActiveDashboardView: View {
do {
profileList = try ProfileManager.list()
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
return
}
if profileList.isEmpty {
@@ -158,8 +149,7 @@ public struct ActiveDashboardView: View {
do {
try LibboxNewStandaloneCommandClient(FilePath.sharedDirectory.relativePath)?.serviceReload()
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
}
}
reasserting = false
@@ -7,8 +7,7 @@ public struct ExtensionStatusView: View {
@State private var message: LibboxStatusMessage?
@State private var connectTask: Task<Void, Error>?
@State private var columnCount: Int = 4
@State private var errorPresented = false
@State private var errorMessage = ""
@State private var alert: Alert?
private let infoFont = Font.system(.caption, design: .monospaced)
@@ -67,13 +66,7 @@ public struct ExtensionStatusView: View {
}
commandClient = nil
}
.alert(isPresented: $errorPresented) {
Alert(
title: Text("Error"),
message: Text(errorMessage),
dismissButton: .default(Text("Ok"))
)
}
.alertBinding($alert)
}
private func doReload() {
@@ -125,8 +118,7 @@ public struct ExtensionStatusView: View {
do {
try LibboxNewStandaloneCommandClient(FilePath.sharedDirectory.relativePath)?.closeConnections()
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
}
}
@@ -4,8 +4,7 @@ import SwiftUI
public struct InstallProfileButton: View {
@Environment(\.extensionProfile) private var extensionProfile
@State private var errorPresented = false
@State private var errorMessage = ""
@State private var alert: Alert?
public init() {}
@@ -15,21 +14,14 @@ public struct InstallProfileButton: View {
await installProfile()
}
}
.alert(isPresented: $errorPresented) {
Alert(
title: Text("Error"),
message: Text(errorMessage),
dismissButton: .default(Text("Ok"))
)
}
.alertBinding($alert)
}
private func installProfile() async {
do {
try await ExtensionProfile.install()
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
}
}
}
@@ -4,8 +4,7 @@
import SwiftUI
public struct InstallSystemExtensionButton: View {
@State private var errorPresented = false
@State private var errorMessage = ""
@State private var alert: Alert?
private let callback: () -> Void
public init(_ callback: @escaping () -> Void) {
self.callback = callback
@@ -17,27 +16,19 @@
await installSystemExtension()
}
}
.alert(isPresented: $errorPresented) {
Alert(
title: Text("Error"),
message: Text(errorMessage),
dismissButton: .default(Text("Ok"))
)
}
.alertBinding($alert)
}
private func installSystemExtension() async {
do {
if let result = try await SystemExtension.install() {
if result == .willCompleteAfterReboot {
errorMessage = "Need reboot"
errorPresented = true
alert = Alert(errorMessage: "Need Reboot")
}
}
callback()
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
}
}
}
@@ -41,8 +41,7 @@ public struct StartStopButton: View {
private struct Button0: View {
@Environment(\.logClient) private var logClient
@ObservedObject private var profile: ExtensionProfile
@State private var errorPresented = false
@State private var errorMessage = ""
@State private var alert: Alert?
init(_ profile: ExtensionProfile) {
self.profile = profile
@@ -75,13 +74,7 @@ public struct StartStopButton: View {
#endif
}
.disabled(!profile.status.isEnabled)
.alert(isPresented: $errorPresented) {
Alert(
title: Text("Error"),
message: Text(errorMessage),
dismissButton: .default(Text("Ok"))
)
}
.alertBinding($alert)
}
private func switchProfile(_ isEnabled: Bool) async {
@@ -93,8 +86,7 @@ public struct StartStopButton: View {
profile.stop()
}
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
return
}
}
@@ -1,4 +1,5 @@
import Foundation
import Libbox
import Library
import SwiftUI
@@ -54,4 +55,17 @@ public extension EnvironmentValues {
self[logClientKey.self] = newValue
}
}
private struct importRemoteProfileKey: EnvironmentKey {
static var defaultValue: Binding<LibboxImportRemoteProfile?> = .constant(nil)
}
var importRemoteProfile: Binding<LibboxImportRemoteProfile?> {
get {
self[importRemoteProfileKey.self]
}
set {
self[importRemoteProfileKey.self] = newValue
}
}
}
@@ -3,16 +3,13 @@ import Library
import SwiftUI
public struct GroupView: View {
private var expland: Binding<Bool>
@Binding private var expland: Bool
@State private var group: OutboundGroup
@State private var geometryWidth: CGFloat = 300
@State private var errorPresented = false
@State private var errorMessage = ""
public init(_ group: OutboundGroup, _ expland: Binding<Bool>) {
self.group = group
self.expland = expland
_expland = expland
}
private var title: some View {
@@ -28,9 +25,9 @@ public struct GroupView: View {
.background(Color.gray.opacity(0.5))
.cornerRadius(4)
Button {
expland.wrappedValue = !expland.wrappedValue
expland = !expland
} label: {
if expland.wrappedValue {
if expland {
Image(systemName: "arrow.down.to.line")
} else {
Image(systemName: "arrow.up.to.line")
@@ -51,18 +48,11 @@ public struct GroupView: View {
#endif
Spacer(minLength: 6)
}
.alert(isPresented: $errorPresented) {
Alert(
title: Text("Error"),
message: Text(errorMessage),
dismissButton: .default(Text("Ok"))
)
}
}
public var body: some View {
Section {
if expland.wrappedValue {
if expland {
LazyVGrid(columns: Array(repeating: GridItem(.flexible()),
count: explandColumnCount()))
{
@@ -123,12 +113,7 @@ public struct GroupView: View {
}
private func doURLTest() {
do {
try LibboxNewStandaloneCommandClient(FilePath.sharedDirectory.relativePath)!.urlTest(group.tag)
} catch {
errorMessage = error.localizedDescription
errorPresented = true
}
try? LibboxNewStandaloneCommandClient(FilePath.sharedDirectory.relativePath)!.urlTest(group.tag)
}
}
@@ -26,10 +26,7 @@ public struct EditProfileContentView: View {
@State private var profile: Profile!
@State private var profileContent: String = ""
@State private var isChanged = false
@State private var errorPresented = false
@State private var errorMessage = ""
@State private var fatalError = false
@State private var alert: Alert?
public var body: some View {
viewBuilder {
@@ -60,17 +57,7 @@ public struct EditProfileContentView: View {
}
}
}
.alert(isPresented: $errorPresented) {
Alert(
title: Text("Error"),
message: Text(errorMessage),
dismissButton: .default(Text("Ok"), action: {
if fatalError {
dismiss()
}
})
)
}
.alertBinding($alert)
.navigationTitle(navigationTitle)
#if os(macOS)
.toolbar {
@@ -115,9 +102,7 @@ public struct EditProfileContentView: View {
do {
try loadContent0()
} catch {
errorMessage = error.localizedDescription
fatalError = true
errorPresented = true
alert = Alert(error, dismiss.callAsFunction)
}
}
@@ -140,8 +125,7 @@ public struct EditProfileContentView: View {
do {
try profile.write(profileContent)
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
return
}
isChanged = false
@@ -10,8 +10,7 @@ public struct EditProfileView: View {
@State private var isLoading = false
@State private var isChanged = false
@State private var errorPresented = false
@State private var errorMessage = ""
@State private var alert: Alert?
public init() {}
@@ -69,6 +68,17 @@ public struct EditProfileView: View {
}
.disabled(isLoading)
}
if #available(iOS 16.0, *) {
ShareLink(item: profile.shareLink) {
Text("Share")
}
} else {
Button("Share") {
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
windowScene.keyWindow?.rootViewController?.present(UIActivityViewController(activityItems: [profile.shareLink], applicationActivities: nil), animated: true, completion: nil)
}
}
}
}
#endif
}
@@ -132,13 +142,7 @@ public struct EditProfileView: View {
}
}
#endif
.alert(isPresented: $errorPresented) {
Alert(
title: Text("Error"),
message: Text(errorMessage),
dismissButton: .default(Text("Ok"))
)
}
.alertBinding($alert)
.navigationTitle("Edit Profile")
}
@@ -150,8 +154,7 @@ public struct EditProfileView: View {
try await Task.sleep(nanoseconds: UInt64(100 * Double(NSEC_PER_MSEC)))
try profile.updateRemoteProfile()
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
}
}
@@ -159,8 +162,7 @@ public struct EditProfileView: View {
do {
_ = try ProfileManager.update(profile)
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
return
}
isChanged = false
@@ -1,4 +1,3 @@
import Library
import SwiftUI
@@ -16,8 +15,7 @@ import SwiftUI
@State private var isLoading = true
@State private var profile: Profile!
@State private var errorPresented = false
@State private var errorMessage = ""
@State private var alert: Alert?
public var body: some View {
viewBuilder {
@@ -27,19 +25,11 @@ import SwiftUI
await doReload()
}
}
.alert(isPresented: $errorPresented) {
Alert(
title: Text("Error"),
message: Text(errorMessage),
dismissButton: .default(Text("Ok"), action: {
dismiss()
})
)
}
} else {
EditProfileView().environmentObject(profile!)
}
}
.alertBinding($alert)
.onExitCommand {
dismiss()
}
@@ -47,20 +37,17 @@ import SwiftUI
private func doReload() async {
guard let profileID else {
errorMessage = "Context destroyed"
errorPresented = true
alert = Alert(errorMessage: "Context destroyed")
return
}
do {
profile = try ProfileManager.get(profileID)
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
return
}
if profile == nil {
errorMessage = "Profile deleted"
errorPresented = true
alert = Alert(errorMessage: "Profile deleted")
return
}
isLoading = false
@@ -17,12 +17,21 @@ public struct NewProfileView: View {
@State private var fileURL: URL!
@State private var remotePath = ""
@State private var pickerPresented = false
@State private var errorPresented = false
@State private var errorMessage = ""
@State private var alert: Alert?
public struct ImportRequest: Codable, Hashable {
public let name: String
public let url: String
}
private let callback: (() -> Void)?
public init(_ callback: (() -> Void)? = nil) {
public init(_ importRequest: ImportRequest? = nil, _ callback: (() -> Void)? = nil) {
self.callback = callback
if let importRequest {
_profileName = .init(initialValue: importRequest.name)
_profileType = .init(initialValue: .remote)
_remotePath = .init(initialValue: importRequest.url)
}
}
public var body: some View {
@@ -88,13 +97,7 @@ public struct NewProfileView: View {
}
}
.navigationTitle("New Profile")
.alert(isPresented: $errorPresented) {
Alert(
title: Text("Error"),
message: Text(errorMessage),
dismissButton: .default(Text("Ok"))
)
}
.alertBinding($alert)
.fileImporter(
isPresented: $pickerPresented,
allowedContentTypes: [.json],
@@ -106,8 +109,7 @@ public struct NewProfileView: View {
fileURL = urls[0]
}
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
return
}
}
@@ -118,26 +120,22 @@ public struct NewProfileView: View {
isSaving = false
}
if profileName.isEmpty {
errorMessage = "Missing profile name"
errorPresented = true
alert = Alert(errorMessage: "Missing profile name")
return
}
if remotePath.isEmpty {
if profileType == .icloud {
errorMessage = "Missing path"
errorPresented = true
alert = Alert(errorMessage: "Missing path")
return
} else if profileType == .remote {
errorMessage = "Missing URL"
errorPresented = true
alert = Alert(errorMessage: "Missing URL")
return
}
}
do {
try createProfile0()
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
return
}
await MainActor.run {
@@ -173,13 +171,11 @@ public struct NewProfileView: View {
let profileConfig = profileConfigDirectory.appendingPathComponent("config_\(nextProfileID).json")
if fileImport {
guard let fileURL else {
errorMessage = "Missing file"
errorPresented = true
alert = Alert(errorMessage: "Missing file")
return
}
if !fileURL.startAccessingSecurityScopedResource() {
errorMessage = "Missing access to selected file"
errorPresented = true
alert = Alert(errorMessage: "Missing access to selected file")
return
}
defer {
@@ -1,16 +1,19 @@
import Foundation
import Libbox
import Library
import SwiftUI
public struct ProfileView: View {
public static let notificationName = Notification.Name("\(FilePath.packageName).update-profile")
@Environment(\.importRemoteProfile) private var importRemoteProfile
@State private var importRemoteProfileRequest: NewProfileView.ImportRequest?
@State private var importRemoteProfilePresented = false
@State private var isLoading = true
@State private var isUpdating = false
@State private var errorPresented = false
@State private var errorMessage = ""
@State private var alert: Alert?
@State private var profileList: [Profile] = []
#if os(iOS)
@@ -33,36 +36,51 @@ public struct ProfileView: View {
}
} else {
#if os(iOS)
FormView {
NavigationLink {
NewProfileView {
Task.detached {
doReload()
ZStack {
if let importRemoteProfileRequest {
NavigationLink(
destination: NewProfileView(importRemoteProfileRequest) {
Task.detached {
doReload()
}
},
isActive: $importRemoteProfilePresented,
label: {
EmptyView()
}
}
} label: {
Text("New Profile").foregroundColor(.accentColor)
)
}
.disabled(editMode.isEditing)
if profileList.isEmpty {
Text("Empty Profiles")
} else {
List {
ForEach(profileList, id: \.mustID) { profile in
viewBuilder {
if editMode.isEditing == true {
Text(profile.name)
} else {
NavigationLink {
EditProfileView().environmentObject(profile)
} label: {
FormView {
NavigationLink {
NewProfileView {
Task.detached {
doReload()
}
}
} label: {
Text("New Profile").foregroundColor(.accentColor)
}
.disabled(editMode.isEditing)
if profileList.isEmpty {
Text("Empty Profiles")
} else {
List {
ForEach(profileList, id: \.mustID) { profile in
viewBuilder {
if editMode.isEditing == true {
Text(profile.name)
} else {
NavigationLink {
EditProfileView().environmentObject(profile)
} label: {
Text(profile.name)
}
}
}
}
.onMove(perform: moveProfile)
.onDelete(perform: deleteProfile)
}
.onMove(perform: moveProfile)
.onDelete(perform: deleteProfile)
}
}
}
@@ -92,6 +110,9 @@ public struct ProfileView: View {
}, label: {
Image(systemName: "arrow.clockwise")
})
ShareLink(item: profile.shareLink) {
Image(systemName: "square.and.arrow.up.fill")
}
}
Button(action: {
openWindow(id: EditProfileWindowView.windowID, value: profile.mustID)
@@ -119,8 +140,13 @@ public struct ProfileView: View {
}
.disabled(isUpdating)
.navigationTitle("Profiles")
#if os(macOS)
.onAppear {
.alertBinding($alert, $isLoading)
.onAppear {
if let remoteProfile = importRemoteProfile.wrappedValue {
importRemoteProfile.wrappedValue = nil
createImportRemoteProfileDialog(remoteProfile)
}
#if os(macOS)
if observer == nil {
observer = NotificationCenter.default.addObserver(forName: ProfileView.notificationName, object: nil, queue: .main) { _ in
Task.detached {
@@ -128,40 +154,63 @@ public struct ProfileView: View {
}
}
}
#endif
}
.onChange(of: importRemoteProfile.wrappedValue) { newValue in
if let newValue {
importRemoteProfile.wrappedValue = nil
createImportRemoteProfileDialog(newValue)
}
.onDisappear {
if let observer {
NotificationCenter.default.removeObserver(observer)
}
observer = nil
}
#if os(macOS)
.onDisappear {
if let observer {
NotificationCenter.default.removeObserver(observer)
}
.toolbar {
ToolbarItem {
Button(action: {
openWindow(id: NewProfileView.windowID)
}, label: {
Label("New Profile", systemImage: "plus.square.fill")
})
}
observer = nil
}
.toolbar {
ToolbarItem {
Button(action: {
openWindow(id: NewProfileView.windowID)
}, label: {
Label("New Profile", systemImage: "plus.square.fill")
})
}
}
#elseif os(iOS)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
EditButton().disabled(profileList.isEmpty)
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
EditButton().disabled(profileList.isEmpty)
}
.environment(\.editMode, $editMode)
}
.environment(\.editMode, $editMode)
#endif
}
private func createImportRemoteProfileDialog(_ newValue: LibboxImportRemoteProfile) {
importRemoteProfileRequest = .init(name: newValue.name, url: newValue.url)
alert = Alert(
title: Text("Import Remote Profile"),
message: Text("Are you sure to import remote configuration \(newValue.name)? You will connect to \(newValue.host) to download the configuration."),
primaryButton: .default(Text("Import")) {
#if os(iOS)
importRemoteProfilePresented = true
#elseif os(macOS)
openWindow(id: NewProfileView.windowID, value: importRemoteProfileRequest!)
#endif
},
secondaryButton: .cancel()
)
}
private func deleteSelectedProfiles(_ profileID: [Int64]) {
do {
if try ProfileManager.delete(by: profileID) > 0 {
isLoading = true
}
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
}
}
@@ -178,8 +227,7 @@ public struct ProfileView: View {
do {
profileList = try ProfileManager.list()
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
return
}
}
@@ -189,8 +237,7 @@ public struct ProfileView: View {
do {
_ = try profile.updateRemoteProfile()
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
}
isUpdating = false
}
@@ -200,8 +247,7 @@ public struct ProfileView: View {
do {
_ = try ProfileManager.delete(profile)
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
return
}
isLoading = true
@@ -216,8 +262,7 @@ public struct ProfileView: View {
do {
try ProfileManager.update(profileList)
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
return
}
}
@@ -231,8 +276,7 @@ public struct ProfileView: View {
do {
_ = try ProfileManager.delete(profileToDelete)
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
}
}
}
@@ -24,9 +24,7 @@ public struct SettingView: View {
@State private var version = ""
@State private var dataSize = ""
@State private var taiwanFlagAvailable = false
@State private var errorPresented = false
@State private var errorMessage = ""
@State private var alert: Alert?
public init() {}
@@ -82,13 +80,11 @@ public struct SettingView: View {
do {
if let result = try await SystemExtension.install(forceUpdate: true) {
if result == .willCompleteAfterReboot {
errorMessage = "Need reboot"
errorPresented = true
alert = Alert(errorMessage: "Need reboot")
}
}
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
}
}
}
@@ -134,13 +130,7 @@ public struct SettingView: View {
}
}
.navigationTitle("Settings")
.alert(isPresented: $errorPresented) {
Alert(
title: Text("Error"),
message: Text(errorMessage),
dismissButton: .default(Text("Ok"))
)
}
.alertBinding($alert)
}
#if os(macOS)
@@ -156,8 +146,7 @@ public struct SettingView: View {
try SMAppService.mainApp.unregister()
}
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
}
}
#endif
+8
View File
@@ -0,0 +1,8 @@
import Foundation
import Libbox
public extension Profile {
var shareLink: URL {
URL(string: LibboxGenerateRemoteProfileImportLink(name, remoteURL!))!
}
}

Before

Width:  |  Height:  |  Size: 278 KiB

After

Width:  |  Height:  |  Size: 278 KiB

Before

Width:  |  Height:  |  Size: 286 KiB

After

Width:  |  Height:  |  Size: 286 KiB

Before

Width:  |  Height:  |  Size: 273 KiB

After

Width:  |  Height:  |  Size: 273 KiB

Before

Width:  |  Height:  |  Size: 273 KiB

After

Width:  |  Height:  |  Size: 273 KiB

Before

Width:  |  Height:  |  Size: 286 KiB

After

Width:  |  Height:  |  Size: 286 KiB

Before

Width:  |  Height:  |  Size: 306 KiB

After

Width:  |  Height:  |  Size: 306 KiB

Before

Width:  |  Height:  |  Size: 272 KiB

After

Width:  |  Height:  |  Size: 272 KiB

Before

Width:  |  Height:  |  Size: 274 KiB

After

Width:  |  Height:  |  Size: 274 KiB

Before

Width:  |  Height:  |  Size: 306 KiB

After

Width:  |  Height:  |  Size: 306 KiB

Before

Width:  |  Height:  |  Size: 340 KiB

After

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.
+4 -4
View File
@@ -12,7 +12,7 @@ public struct MacApplication: Scene {
MainView()
.onAppear {
Task.detached {
await initialize()
initialize()
}
}
.environment(\.showMenuBarExtra, $showMenuBarExtra)
@@ -35,9 +35,9 @@ public struct MacApplication: Scene {
SidebarCommands()
}
Window("New Profile", id: NewProfileView.windowID) {
NewProfileView()
}
WindowGroup("New Profile", id: NewProfileView.windowID, for: NewProfileView.ImportRequest.self) { importRequest in
NewProfileView(importRequest.wrappedValue)
}.commandsRemoved()
WindowGroup("Edit Profile", id: EditProfileWindowView.windowID, for: Int64.self) { profileID in
EditProfileWindowView(profileID.wrappedValue)
+29 -23
View File
@@ -1,4 +1,5 @@
import ApplicationLibrary
import Libbox
import Library
import SwiftUI
@@ -9,11 +10,8 @@ public struct MainView: View {
@State private var extensionProfile: ExtensionProfile?
@State private var profileLoading = true
@State private var logClient: LogClient!
@State private var dialogTitle = ""
@State private var dialogContent = ""
@State private var dialogAction: (() -> Void)?
@State private var dialogPresented = false
@State private var alert: Alert?
@State private var importRemoteProfile: LibboxImportRemoteProfile?
public init() {}
public var body: some View {
@@ -44,19 +42,10 @@ public struct MainView: View {
}
}
#endif
.alert(isPresented: $dialogPresented, content: {
Alert(
title: Text(dialogTitle),
message: Text(dialogContent),
dismissButton: .default(Text("Ok"), action: dialogAction)
)
})
.alertBinding($alert)
.onAppear {
ServiceNotification.setServiceNotificationListener { notification in
dialogTitle = notification.title
dialogContent = notification.body
dialogAction = nil
dialogPresented = true
alert = Alert(title: Text(notification.title), message: Text(notification.body))
}
}
.onDisappear {
@@ -84,6 +73,22 @@ public struct MainView: View {
.environment(\.selection, $selection)
.environment(\.extensionProfile, $extensionProfile)
.environment(\.logClient, $logClient)
.environment(\.importRemoteProfile, $importRemoteProfile)
.handlesExternalEvents(preferring: [], allowing: ["*"])
.onOpenURL(perform: openURL)
}
private func openURL(url: URL) {
if url.host == "import-remote-profile" {
var error: NSError?
importRemoteProfile = LibboxParseRemoteProfileImportLink(url.absoluteString, &error)
if error != nil {
return
}
if selection != .profiles {
selection = .profiles
}
}
}
private func loadProfile() async {
@@ -115,13 +120,14 @@ public struct MainView: View {
private func checkApplicationPath() {
let directoryName = URL(filePath: Bundle.main.bundlePath).deletingLastPathComponent().pathComponents.last
if directoryName != "Applications" {
dialogTitle = "Wrong application location"
dialogContent = "This app needs to be placed under ~/Applications to work."
dialogAction = {
NSWorkspace.shared.selectFile(Bundle.main.bundlePath, inFileViewerRootedAtPath: "")
NSApp.terminate(nil)
}
dialogPresented = true
alert = Alert(
title: Text("Wrong application location"),
message: Text("This app needs to be placed under ~/Applications to work."),
dismissButton: .default(Text("Ok")) {
NSWorkspace.shared.selectFile(Bundle.main.bundlePath, inFileViewerRootedAtPath: "")
NSApp.terminate(nil)
}
)
}
}
}
+7 -25
View File
@@ -71,8 +71,7 @@ public struct MenuView: View {
private struct StatusSwitch: View {
@ObservedObject private var profile: ExtensionProfile
@State private var errorPresented = false
@State private var errorMessage = ""
@State private var alert: Alert?
init(_ profile: ExtensionProfile) {
self.profile = profile
@@ -88,13 +87,7 @@ public struct MenuView: View {
})) {}
.toggleStyle(.switch)
.disabled(!profile.status.isEnabled)
.alert(isPresented: $errorPresented) {
Alert(
title: Text("Error"),
message: Text(errorMessage),
dismissButton: .default(Text("Ok"))
)
}
.alertBinding($alert)
}
private func switchProfile(_ isEnabled: Bool) async {
@@ -105,8 +98,7 @@ public struct MenuView: View {
profile.stop()
}
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
return
}
}
@@ -123,10 +115,8 @@ public struct MenuView: View {
@State private var profileList: [Profile] = []
@State private var selectedProfileID: Int64!
@State private var reasserting = false
@State private var errorPresented = false
@State private var errorMessage = ""
@State private var observer: Any?
@State private var alert: Alert?
var body: some View {
viewBuilder {
@@ -169,13 +159,7 @@ public struct MenuView: View {
NotificationCenter.default.removeObserver(observer)
}
}
.alert(isPresented: $errorPresented) {
Alert(
title: Text("Error"),
message: Text(errorMessage),
dismissButton: .default(Text("Ok"))
)
}
.alertBinding($alert)
}
private func doReload() {
@@ -185,8 +169,7 @@ public struct MenuView: View {
do {
profileList = try ProfileManager.list()
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
return
}
if profileList.isEmpty {
@@ -210,8 +193,7 @@ public struct MenuView: View {
do {
try LibboxNewStandaloneCommandClient(FilePath.sharedDirectory.relativePath)?.serviceReload()
} catch {
errorMessage = error.localizedDescription
errorPresented = true
alert = Alert(error)
}
}
reasserting = false
+15
View File
@@ -6,6 +6,21 @@
<array>
<string>io.nekohasekai.sfa.update_profiles</string>
</array>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLIconFile</key>
<string>AppIcon.icns</string>
<key>CFBundleURLName</key>
<string>sing-box</string>
<key>CFBundleURLSchemes</key>
<array>
<string>sing-box</string>
</array>
</dict>
</array>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>NSUbiquitousContainers</key>
+19 -1
View File
@@ -1,4 +1,5 @@
import ApplicationLibrary
import Libbox
import Library
import SwiftUI
@@ -14,6 +15,8 @@ struct MainView: View {
@State private var serviceNotificationContent = ""
@State private var serviceNotificationPresented = false
@State private var importRemoteProfile: LibboxImportRemoteProfile?
var body: some View {
viewBuilder {
if profileLoading {
@@ -54,7 +57,22 @@ struct MainView: View {
.environment(\.selection, $selection)
.environment(\.extensionProfile, $extensionProfile)
.environment(\.logClient, $logClient)
.preferredColorScheme(.dark)
.environment(\.importRemoteProfile, $importRemoteProfile)
.handlesExternalEvents(preferring: [], allowing: ["*"])
.onOpenURL(perform: openURL)
}
private func openURL(url: URL) {
if url.host == "import-remote-profile" {
var error: NSError?
importRemoteProfile = LibboxParseRemoteProfileImportLink(url.absoluteString, &error)
if error != nil {
return
}
if selection != .profiles {
selection = .profiles
}
}
}
private func loadProfile() async {
+17 -2
View File
@@ -2,6 +2,23 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLIconFile</key>
<string>AppIcon.icns</string>
<key>CFBundleURLName</key>
<string>sing-box</string>
<key>CFBundleURLSchemes</key>
<array>
<string>sing-box</string>
</array>
</dict>
</array>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>NSUbiquitousContainers</key>
<dict>
<key>iCloud.io.nekohasekai.sfa</key>
@@ -14,7 +31,5 @@
<string>Any</string>
</dict>
</dict>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
</dict>
</plist>
@@ -1,11 +0,0 @@
{
"colors": [
{
"idiom": "universal"
}
],
"info": {
"author": "xcode",
"version": 1
}
}
@@ -1,68 +0,0 @@
{
"images" : [
{
"filename" : "apple-16 1x.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
"filename" : "apple-16 2x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
"filename" : "apple-32 1x.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
"filename" : "apple-32 2x 1.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
"filename" : "apple-128 1x.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
"filename" : "apple-128 2x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
"filename" : "apple-256 1x.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
"filename" : "apple-256 2x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
"filename" : "apple-512 1x.png",
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
"filename" : "apple-512 2x.png",
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 278 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 273 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 340 KiB

-6
View File
@@ -1,6 +0,0 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
+15
View File
@@ -2,6 +2,21 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLIconFile</key>
<string>AppIcon</string>
<key>CFBundleURLName</key>
<string>sing-box</string>
<key>CFBundleURLSchemes</key>
<array>
<string>sing-box</string>
</array>
</dict>
</array>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>NSUbiquitousContainers</key>
+66 -21
View File
@@ -14,7 +14,6 @@
3A1CF2F62A50EE9C000A8289 /* GroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1CF2F52A50EE9C000A8289 /* GroupView.swift */; };
3A1CF2F82A50F0A5000A8289 /* GroupItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1CF2F72A50F0A5000A8289 /* GroupItemView.swift */; };
3A1CF2FA2A50F0BD000A8289 /* OutboundGroupItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1CF2F92A50F0BD000A8289 /* OutboundGroupItem.swift */; };
3A2223542A6E1B6700C50B23 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3A2223532A6E1B6700C50B23 /* Info.plist */; };
3A2223562A6E1BDE00C50B23 /* Variant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A2223552A6E1BDE00C50B23 /* Variant.swift */; };
3A2223582A6E1CC700C50B23 /* MacApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A2223572A6E1CC700C50B23 /* MacApplication.swift */; };
3A22235A2A6E212A00C50B23 /* SystemExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A2223592A6E212A00C50B23 /* SystemExtension.swift */; };
@@ -55,6 +54,12 @@
3A5F26C92A503D4A00C27EDF /* Library.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
3A648D2D2A4EEAA600D95A12 /* Library.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A648D2C2A4EEAA600D95A12 /* Library.swift */; };
3A648D542A4EF4C700D95A12 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AF342B12A4AA520002B34AC /* NetworkExtension.framework */; };
3A6CA5A02A71317A0027933B /* MarkdownUI in Frameworks */ = {isa = PBXBuildFile; productRef = 3A6CA59F2A71317A0027933B /* MarkdownUI */; };
3A6CA5A32A713A580027933B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3AEECC0A2A6DF9CA006A0E0C /* Assets.xcassets */; };
3A6CA5A62A713AA10027933B /* AppIcon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 3A6CA5A52A713AA10027933B /* AppIcon.icns */; };
3A6CA5A72A713ABA0027933B /* AppIcon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 3A6CA5A52A713AA10027933B /* AppIcon.icns */; };
3A6CA5A82A713B340027933B /* AppIcon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 3A6CA5A52A713AA10027933B /* AppIcon.icns */; };
3A6CA5A92A713C420027933B /* AppIcon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 3A6CA5A52A713AA10027933B /* AppIcon.icns */; };
3A76504C2A4F08BA003945C5 /* Libbox.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC20DB2A4599D000A63465 /* Libbox.xcframework */; };
3A7701702A4E6B34008F031F /* IntentsExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A77016F2A4E6B34008F031F /* IntentsExtension.swift */; };
3A7701722A4E6B34008F031F /* Intents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7701712A4E6B34008F031F /* Intents.swift */; };
@@ -65,10 +70,12 @@
3A9144D92A46AE370036E9AD /* ShadredPreferences+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A9144D82A46AE370036E9AD /* ShadredPreferences+Database.swift */; };
3A9759202A4EB69C00E4404B /* Library.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; };
3A9759212A4EB69C00E4404B /* Library.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3AEC211D2A459B4700A63465 /* Library.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
3AB1220B2A70FD500087CD55 /* Alert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AB1220A2A70FD500087CD55 /* Alert.swift */; };
3AC194492A50013F00BD8CB9 /* IntentsExtension.appex in Embed ExtensionKit Extensions */ = {isa = PBXBuildFile; fileRef = 3A77016D2A4E6B34008F031F /* IntentsExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
3AC1944F2A50247300BD8CB9 /* ApplicationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AC1944E2A50247300BD8CB9 /* ApplicationDelegate.swift */; };
3AC194502A502DFE00BD8CB9 /* ServiceNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AC1944C2A50206C00BD8CB9 /* ServiceNotification.swift */; };
3AC5EC082A6417470077AF34 /* DeviceCensorship.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AC5EC072A6417470077AF34 /* DeviceCensorship.swift */; };
3AD0953D2A70EB310052764E /* Profile+Share.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AD0953C2A70EB310052764E /* Profile+Share.swift */; };
3AE4D0B22A6E2B6A009FEA9E /* ExtensionPlatformInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF342A62A4AA0FF002B34AC /* ExtensionPlatformInterface.swift */; };
3AE4D0B32A6E2B94009FEA9E /* Extension+RunBlocking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF342A82A4AA155002B34AC /* Extension+RunBlocking.swift */; };
3AE4D0B42A6E2BA3009FEA9E /* Extension+Iterator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF342AA2A4AA173002B34AC /* Extension+Iterator.swift */; };
@@ -80,7 +87,6 @@
3AEAEE992A4F16430059612D /* Extension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 3A096F862A4ED3DE00D4A2ED /* Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
3AEC20F62A459AB400A63465 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEC20F52A459AB400A63465 /* Application.swift */; };
3AEC20FA2A459AB500A63465 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3AEC20F92A459AB500A63465 /* Assets.xcassets */; };
3AEC21102A459B1A00A63465 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3AEC210F2A459B1A00A63465 /* Assets.xcassets */; };
3AEC212F2A459D5600A63465 /* Profile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEC212E2A459D5600A63465 /* Profile.swift */; };
3AEC213C2A459FDF00A63465 /* Databse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEC213B2A459FDF00A63465 /* Databse.swift */; };
3AEC21402A45A28F00A63465 /* ProfileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AEC213F2A45A28F00A63465 /* ProfileManager.swift */; };
@@ -373,6 +379,7 @@
3A57DF3F2A4D70B600690BC5 /* MenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuView.swift; sourceTree = "<group>"; };
3A57DF412A4D927A00690BC5 /* Profile+Hashable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Profile+Hashable.swift"; sourceTree = "<group>"; };
3A648D2C2A4EEAA600D95A12 /* Library.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Library.swift; sourceTree = "<group>"; };
3A6CA5A52A713AA10027933B /* AppIcon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = AppIcon.icns; sourceTree = "<group>"; };
3A77016D2A4E6B34008F031F /* IntentsExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.extensionkit-extension"; includeInIndex = 0; path = IntentsExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
3A77016F2A4E6B34008F031F /* IntentsExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntentsExtension.swift; sourceTree = "<group>"; };
3A7701712A4E6B34008F031F /* Intents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Intents.swift; sourceTree = "<group>"; };
@@ -388,11 +395,13 @@
3AAB5E732A4BF90B009757F1 /* ServiceLogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceLogView.swift; sourceTree = "<group>"; };
3AAB5E752A4BFB0B009757F1 /* EditProfileContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditProfileContentView.swift; sourceTree = "<group>"; };
3AAB5E7A2A4C1446009757F1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
3AB1220A2A70FD500087CD55 /* Alert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Alert.swift; sourceTree = "<group>"; };
3ABA46D22A6A32A100D8366B /* Messages.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Messages.framework; path = Library/Frameworks/Messages.framework; sourceTree = DEVELOPER_DIR; };
3AC1944C2A50206C00BD8CB9 /* ServiceNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceNotification.swift; sourceTree = "<group>"; };
3AC1944E2A50247300BD8CB9 /* ApplicationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationDelegate.swift; sourceTree = "<group>"; };
3AC194512A50303300BD8CB9 /* ApplicationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationDelegate.swift; sourceTree = "<group>"; };
3AC5EC072A6417470077AF34 /* DeviceCensorship.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceCensorship.swift; sourceTree = "<group>"; };
3AD0953C2A70EB310052764E /* Profile+Share.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Profile+Share.swift"; sourceTree = "<group>"; };
3ADF8DF12A4AF59900900CC8 /* ActiveDashboardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveDashboardView.swift; sourceTree = "<group>"; };
3ADF8DF32A4AF9B500900CC8 /* DashboardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardView.swift; sourceTree = "<group>"; };
3ADF8DF62A4AFB2C00900CC8 /* ProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileView.swift; sourceTree = "<group>"; };
@@ -410,7 +419,6 @@
3AEC21092A459B1900A63465 /* sing-box.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "sing-box.app"; sourceTree = BUILT_PRODUCTS_DIR; };
3AEC210B2A459B1900A63465 /* Application.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Application.swift; sourceTree = "<group>"; };
3AEC210D2A459B1900A63465 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
3AEC210F2A459B1A00A63465 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
3AEC21142A459B1A00A63465 /* SFM.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = SFM.entitlements; sourceTree = "<group>"; };
3AEC211D2A459B4700A63465 /* Library.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Library.framework; sourceTree = BUILT_PRODUCTS_DIR; };
3AEC212E2A459D5600A63465 /* Profile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Profile.swift; sourceTree = "<group>"; };
@@ -460,6 +468,7 @@
buildActionMask = 2147483647;
files = (
3A4EAD1B2A4FEB02005435B3 /* Library.framework in Frameworks */,
3A6CA5A02A71317A0027933B /* MarkdownUI in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -573,6 +582,14 @@
path = Service;
sourceTree = "<group>";
};
3A6CA5A42A713A6C0027933B /* Icons */ = {
isa = PBXGroup;
children = (
3A6CA5A52A713AA10027933B /* AppIcon.icns */,
);
path = Icons;
sourceTree = "<group>";
};
3A77016E2A4E6B34008F031F /* IntentsExtension */ = {
isa = PBXGroup;
children = (
@@ -677,7 +694,6 @@
isa = PBXGroup;
children = (
3AEC210B2A459B1900A63465 /* Application.swift */,
3AEC210F2A459B1A00A63465 /* Assets.xcassets */,
3AEC21142A459B1A00A63465 /* SFM.entitlements */,
3AEAEE9C2A4F1A9D0059612D /* Info.plist */,
);
@@ -707,6 +723,7 @@
3A9144D82A46AE370036E9AD /* ShadredPreferences+Database.swift */,
3A57DF362A4D5D2600690BC5 /* Profile+Date.swift */,
3A57DF412A4D927A00690BC5 /* Profile+Hashable.swift */,
3AD0953C2A70EB310052764E /* Profile+Share.swift */,
);
path = Database;
sourceTree = "<group>";
@@ -767,7 +784,6 @@
isa = PBXGroup;
children = (
3AEECC062A6DF9CA006A0E0C /* Application.swift */,
3AEECC0A2A6DF9CA006A0E0C /* Assets.xcassets */,
3AEECC502A6E0074006A0E0C /* SFM.entitlements */,
3A2223532A6E1B6700C50B23 /* Info.plist */,
3A2EAEEC2A6F4CBB00D00DE3 /* IndependentApplicationDelegate.swift */,
@@ -778,6 +794,8 @@
3AEECC302A6DFDAD006A0E0C /* MacLibrary */ = {
isa = PBXGroup;
children = (
3A6CA5A42A713A6C0027933B /* Icons */,
3AEECC0A2A6DF9CA006A0E0C /* Assets.xcassets */,
3AEC210D2A459B1900A63465 /* MainView.swift */,
3A57DF3F2A4D70B600690BC5 /* MenuView.swift */,
3A1CF2F32A50E937000A8289 /* SidebarView.swift */,
@@ -808,6 +826,7 @@
3AF342D32A4AADB2002B34AC /* Formtem.swift */,
3ADF8E022A4B118700900CC8 /* Binding+Unwrap.swift */,
3AC5EC072A6417470077AF34 /* DeviceCensorship.swift */,
3AB1220A2A70FD500087CD55 /* Alert.swift */,
);
path = Abstract;
sourceTree = "<group>";
@@ -871,6 +890,9 @@
3A4EAD1E2A4FEB02005435B3 /* PBXTargetDependency */,
);
name = ApplicationLibrary;
packageProductDependencies = (
3A6CA59F2A71317A0027933B /* MarkdownUI */,
);
productName = ApplicationLibrary;
productReference = 3A4EAD102A4FEAE6005435B3 /* ApplicationLibrary.framework */;
productType = "com.apple.product-type.framework";
@@ -1090,6 +1112,7 @@
3A7E90362A46778E00D53052 /* XCRemoteSwiftPackageReference "BinaryCodable" */,
3A017F902A4AB2E4009149FA /* XCRemoteSwiftPackageReference "GRDB" */,
3A57DF3A2A4D705000690BC5 /* XCRemoteSwiftPackageReference "MacControlCenterUI" */,
3ADB2D832A71266E00A6517D /* XCRemoteSwiftPackageReference "swift-markdown-ui" */,
);
productRefGroup = 3AEC20C72A45991900A63465 /* Products */;
projectDirPath = "";
@@ -1120,6 +1143,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3A6CA5A92A713C420027933B /* AppIcon.icns in Resources */,
3AEC20FA2A459AB500A63465 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1128,8 +1152,9 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3A6CA5A72A713ABA0027933B /* AppIcon.icns in Resources */,
3A6CA5A32A713A580027933B /* Assets.xcassets in Resources */,
3A251C122A52D09700651082 /* Assets.xcassets in Resources */,
3AEC21102A459B1A00A63465 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1151,8 +1176,8 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3A6CA5A82A713B340027933B /* AppIcon.icns in Resources */,
3AEC8A4F2A6E5E18003702E1 /* Assets.xcassets in Resources */,
3A2223542A6E1B6700C50B23 /* Info.plist in Resources */,
3AEECC0B2A6DF9CA006A0E0C /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1161,6 +1186,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3A6CA5A62A713AA10027933B /* AppIcon.icns in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1197,6 +1223,7 @@
3A4EAD2F2A4FEB77005435B3 /* EditProfileContentView.swift in Sources */,
3A4EAD272A4FEB65005435B3 /* DashboardView.swift in Sources */,
3A4EAD342A4FEB7F005435B3 /* LogView.swift in Sources */,
3AB1220B2A70FD500087CD55 /* Alert.swift in Sources */,
3A4EAD362A4FEB9C005435B3 /* ProfileUpdateTask.swift in Sources */,
3A1CF2F62A50EE9C000A8289 /* GroupView.swift in Sources */,
3A4EAD212A4FEB3C005435B3 /* ApplicationLibrary.swift in Sources */,
@@ -1248,6 +1275,7 @@
3AEC214A2A45AA5600A63465 /* Profile+RW.swift in Sources */,
3A57DF422A4D927A00690BC5 /* Profile+Hashable.swift in Sources */,
3A7E90352A46756300D53052 /* SharedPreferences.swift in Sources */,
3AD0953D2A70EB310052764E /* Profile+Share.swift in Sources */,
3AE4D0B32A6E2B94009FEA9E /* Extension+RunBlocking.swift in Sources */,
3AEC21482A45A9DE00A63465 /* Bundle+Version.swift in Sources */,
3A57DF372A4D5D2600690BC5 /* Profile+Date.swift in Sources */,
@@ -1418,6 +1446,7 @@
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.0;
OTHER_CODE_SIGN_FLAGS = "";
OTHER_LDFLAGS = "-ld64";
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.extension;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1457,6 +1486,7 @@
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.0;
OTHER_CODE_SIGN_FLAGS = "";
OTHER_LDFLAGS = "-ld64";
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.extension;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1574,6 +1604,7 @@
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.0;
OTHER_CODE_SIGN_FLAGS = "";
OTHER_LDFLAGS = "-ld64";
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.intents;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1613,6 +1644,7 @@
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.0;
OTHER_CODE_SIGN_FLAGS = "";
OTHER_LDFLAGS = "-ld64";
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.intents;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1764,7 +1796,7 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
CODE_SIGN_ENTITLEMENTS = SFI/SFI.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
@@ -1786,9 +1818,9 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.3.4;
MARKETING_VERSION = 1.3.5;
OTHER_CODE_SIGN_FLAGS = "--deep";
OTHER_LDFLAGS = "";
OTHER_LDFLAGS = "-ld64";
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
PRODUCT_NAME = "sing-box";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1805,7 +1837,7 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = YES;
CODE_SIGN_ENTITLEMENTS = SFI/SFI.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
@@ -1827,9 +1859,9 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.3.4;
MARKETING_VERSION = 1.3.5;
OTHER_CODE_SIGN_FLAGS = "--deep";
OTHER_LDFLAGS = "";
OTHER_LDFLAGS = "-ld64";
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
PRODUCT_NAME = "sing-box";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1851,7 +1883,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 12;
CURRENT_PROJECT_VERSION = 14;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = Z56Z6NYZN2;
ENABLE_HARDENED_RUNTIME = YES;
@@ -1867,7 +1899,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.3.4;
MARKETING_VERSION = 1.3.5;
OTHER_CODE_SIGN_FLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
PRODUCT_NAME = "sing-box";
@@ -1889,7 +1921,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 12;
CURRENT_PROJECT_VERSION = 14;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = Z56Z6NYZN2;
ENABLE_HARDENED_RUNTIME = YES;
@@ -1905,7 +1937,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.3.4;
MARKETING_VERSION = 1.3.5;
OTHER_CODE_SIGN_FLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa;
PRODUCT_NAME = "sing-box";
@@ -1952,7 +1984,7 @@
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
OTHER_CODE_SIGN_FLAGS = "--deep";
OTHER_LDFLAGS = "";
OTHER_LDFLAGS = "-ld64";
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.library;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -2004,7 +2036,7 @@
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
OTHER_CODE_SIGN_FLAGS = "--deep";
OTHER_LDFLAGS = "";
OTHER_LDFLAGS = "-ld64";
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.library;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -2117,7 +2149,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.3.4;
MARKETING_VERSION = 1.3.5;
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.independent;
PRODUCT_NAME = SFM;
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -2156,7 +2188,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.3.4;
MARKETING_VERSION = 1.3.5;
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.independent;
PRODUCT_NAME = SFM;
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -2367,6 +2399,14 @@
minimumVersion = 2.0.0;
};
};
3ADB2D832A71266E00A6517D /* XCRemoteSwiftPackageReference "swift-markdown-ui" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/gonzalezreal/swift-markdown-ui";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 2.1.0;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
@@ -2375,6 +2415,11 @@
package = 3A017F902A4AB2E4009149FA /* XCRemoteSwiftPackageReference "GRDB" */;
productName = GRDB;
};
3A6CA59F2A71317A0027933B /* MarkdownUI */ = {
isa = XCSwiftPackageProductDependency;
package = 3ADB2D832A71266E00A6517D /* XCRemoteSwiftPackageReference "swift-markdown-ui" */;
productName = MarkdownUI;
};
3A7E90372A46778E00D53052 /* BinaryCodable */ = {
isa = XCSwiftPackageProductDependency;
package = 3A7E90362A46778E00D53052 /* XCRemoteSwiftPackageReference "BinaryCodable" */;
@@ -35,6 +35,15 @@
"revision" : "8757eb7c2cd708320df92e6ad6572efe90e58f16",
"version" : "1.0.4"
}
},
{
"identity" : "swift-markdown-ui",
"kind" : "remoteSourceControl",
"location" : "https://github.com/gonzalezreal/swift-markdown-ui",
"state" : {
"revision" : "12b351a75201a8124c2f2e1f9fc6ef5cd812c0b9",
"version" : "2.1.0"
}
}
],
"version" : 2
@@ -14,52 +14,52 @@
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>15</integer>
<integer>18</integer>
</dict>
<key>Associations (Playground) 2.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>16</integer>
<integer>19</integer>
</dict>
<key>Associations (Playground) 3.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>27</integer>
<integer>30</integer>
</dict>
<key>Associations (Playground) 4.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>28</integer>
<integer>31</integer>
</dict>
<key>Associations (Playground) 5.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>29</integer>
<integer>32</integer>
</dict>
<key>Associations (Playground).xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>14</integer>
<integer>17</integer>
</dict>
<key>ExtensionMac.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>22</integer>
<integer>25</integer>
</dict>
<key>Launcher.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>18</integer>
<integer>21</integer>
</dict>
<key>MacExtension.xcscheme_^#shared#^_</key>
<dict>
@@ -69,59 +69,59 @@
<key>MacLibrary.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>4</integer>
<integer>5</integer>
</dict>
<key>MessageExtension.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>16</integer>
<integer>20</integer>
</dict>
<key>MyPlayground (Playground) 1.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>9</integer>
<integer>11</integer>
</dict>
<key>MyPlayground (Playground) 2.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>10</integer>
<integer>12</integer>
</dict>
<key>MyPlayground (Playground) 3.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>23</integer>
<integer>26</integer>
</dict>
<key>MyPlayground (Playground) 4.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>24</integer>
<integer>27</integer>
</dict>
<key>MyPlayground (Playground) 5.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>26</integer>
<integer>29</integer>
</dict>
<key>MyPlayground (Playground).xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>8</integer>
<integer>10</integer>
</dict>
<key>SFA.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>21</integer>
<integer>24</integer>
</dict>
<key>SFI.xcscheme</key>
<dict>
@@ -131,121 +131,121 @@
<key>SFM.System.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>5</integer>
<integer>4</integer>
</dict>
<key>SFM.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>2</integer>
</dict>
<key>SystemExtension.xcscheme_^#shared#^_</key>
<key>SFM.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
</dict>
<key>SystemExtension.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>2</integer>
</dict>
<key>Test.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>10</integer>
<integer>13</integer>
</dict>
<key>Tour (Playground) 1.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>6</integer>
<integer>8</integer>
</dict>
<key>Tour (Playground) 2.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>7</integer>
<integer>9</integer>
</dict>
<key>Tour (Playground) 3.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>33</integer>
<integer>36</integer>
</dict>
<key>Tour (Playground) 4.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>34</integer>
<integer>37</integer>
</dict>
<key>Tour (Playground) 5.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>35</integer>
<integer>38</integer>
</dict>
<key>Tour (Playground).xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>5</integer>
<integer>7</integer>
</dict>
<key>TransactionObserver (Playground) 1.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>12</integer>
<integer>15</integer>
</dict>
<key>TransactionObserver (Playground) 2.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>13</integer>
<integer>16</integer>
</dict>
<key>TransactionObserver (Playground) 3.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>30</integer>
<integer>33</integer>
</dict>
<key>TransactionObserver (Playground) 4.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>31</integer>
<integer>34</integer>
</dict>
<key>TransactionObserver (Playground) 5.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>32</integer>
<integer>35</integer>
</dict>
<key>TransactionObserver (Playground).xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>11</integer>
<integer>14</integer>
</dict>
<key>mactest.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>25</integer>
<integer>28</integer>
</dict>
<key>sing-box.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>20</integer>
<integer>23</integer>
</dict>
<key>test.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>19</integer>
<integer>22</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>