mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-09-10 03:41:53 +02:00
cleanup
This commit is contained in:
+16
-10
@@ -48,7 +48,7 @@ extension Classic.ColorsDemo {
|
||||
/**
|
||||
⭐️ Sample 3: We can end monitoring updates anytime. `removeObserver()` was called here for illustration purposes only. `ObjectMonitor`s safely remove deallocated observers automatically.
|
||||
*/
|
||||
deinit {
|
||||
isolated deinit {
|
||||
|
||||
self.palette.removeObserver(self)
|
||||
}
|
||||
@@ -87,13 +87,16 @@ extension Classic.ColorsDemo {
|
||||
|
||||
// MARK: ObjectObserver
|
||||
|
||||
func objectMonitor(
|
||||
nonisolated func objectMonitor(
|
||||
_ monitor: ObjectMonitor<Classic.ColorsDemo.Palette>,
|
||||
didUpdateObject object: Classic.ColorsDemo.Palette,
|
||||
didUpdateObject object: sending Classic.ColorsDemo.Palette,
|
||||
changedPersistentKeys: Set<KeyPathString>
|
||||
) {
|
||||
|
||||
self.reloadPaletteInfo(object, changedKeys: changedPersistentKeys)
|
||||
withMainActorImmediate {
|
||||
|
||||
self.reloadPaletteInfo(object, changedKeys: changedPersistentKeys)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -248,10 +251,11 @@ extension Classic.ColorsDemo {
|
||||
private dynamic func hueSliderValueDidChange(_ sender: UISlider) {
|
||||
|
||||
let value = sender.value
|
||||
let persistentID = self.palette.object?.persistentID()
|
||||
Classic.ColorsDemo.dataStack.perform(
|
||||
asynchronous: { [weak self] (transaction) in
|
||||
asynchronous: { (transaction) in
|
||||
|
||||
let palette = transaction.edit(self?.palette.object)
|
||||
let palette = transaction.edit(persistentID)
|
||||
palette?.hue = value
|
||||
},
|
||||
completion: { _ in }
|
||||
@@ -262,10 +266,11 @@ extension Classic.ColorsDemo {
|
||||
private dynamic func saturationSliderValueDidChange(_ sender: UISlider) {
|
||||
|
||||
let value = sender.value
|
||||
let persistentID = self.palette.object?.persistentID()
|
||||
Classic.ColorsDemo.dataStack.perform(
|
||||
asynchronous: { [weak self] (transaction) in
|
||||
asynchronous: { (transaction) in
|
||||
|
||||
let palette = transaction.edit(self?.palette.object)
|
||||
let palette = transaction.edit(persistentID)
|
||||
palette?.saturation = value
|
||||
},
|
||||
completion: { _ in }
|
||||
@@ -276,10 +281,11 @@ extension Classic.ColorsDemo {
|
||||
private dynamic func brightnessSliderValueDidChange(_ sender: UISlider) {
|
||||
|
||||
let value = sender.value
|
||||
let persistentID = self.palette.object?.persistentID()
|
||||
Classic.ColorsDemo.dataStack.perform(
|
||||
asynchronous: { [weak self] (transaction) in
|
||||
asynchronous: { (transaction) in
|
||||
|
||||
let palette = transaction.edit(self?.palette.object)
|
||||
let palette = transaction.edit(persistentID)
|
||||
palette?.brightness = value
|
||||
},
|
||||
completion: { _ in }
|
||||
|
||||
@@ -11,6 +11,7 @@ extension Modern.ColorsDemo {
|
||||
|
||||
// MARK: - Modern.ColorsDemo.MainView
|
||||
|
||||
@MainActor
|
||||
struct MainView<ListView: View, DetailView: View>: View {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
@@ -11,6 +11,7 @@ extension Modern.ColorsDemo.SwiftUI {
|
||||
|
||||
// MARK: - Modern.ColorsDemo.SwiftUI.DetailView
|
||||
|
||||
@MainActor
|
||||
struct DetailView: View {
|
||||
|
||||
/**
|
||||
@@ -33,6 +34,7 @@ extension Modern.ColorsDemo.SwiftUI {
|
||||
|
||||
init(_ palette: ObjectPublisher<Modern.ColorsDemo.Palette>) {
|
||||
|
||||
let persistentID = palette.persistentID()
|
||||
self._palette = .init(palette)
|
||||
self._hue = Binding(
|
||||
get: { palette.hue ?? 0 },
|
||||
@@ -41,7 +43,7 @@ extension Modern.ColorsDemo.SwiftUI {
|
||||
Modern.ColorsDemo.dataStack.perform(
|
||||
asynchronous: { (transaction) in
|
||||
|
||||
let palette = palette.asEditable(in: transaction)
|
||||
let palette = persistentID.asEditable(in: transaction)
|
||||
palette?.hue = percentage
|
||||
},
|
||||
completion: { _ in }
|
||||
@@ -55,7 +57,7 @@ extension Modern.ColorsDemo.SwiftUI {
|
||||
Modern.ColorsDemo.dataStack.perform(
|
||||
asynchronous: { (transaction) in
|
||||
|
||||
let palette = palette.asEditable(in: transaction)
|
||||
let palette = persistentID.asEditable(in: transaction)
|
||||
palette?.saturation = percentage
|
||||
},
|
||||
completion: { _ in }
|
||||
@@ -69,7 +71,7 @@ extension Modern.ColorsDemo.SwiftUI {
|
||||
Modern.ColorsDemo.dataStack.perform(
|
||||
asynchronous: { (transaction) in
|
||||
|
||||
let palette = palette.asEditable(in: transaction)
|
||||
let palette = persistentID.asEditable(in: transaction)
|
||||
palette?.brightness = percentage
|
||||
},
|
||||
completion: { _ in }
|
||||
|
||||
@@ -11,6 +11,7 @@ extension Modern.ColorsDemo.SwiftUI {
|
||||
|
||||
// MARK: - Modern.ColorsDemo.SwiftUI.ItemView
|
||||
|
||||
@MainActor
|
||||
struct ItemView: View {
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,6 +11,7 @@ extension Modern.ColorsDemo.SwiftUI {
|
||||
|
||||
// MARK: - Modern.ColorsDemo.SwiftUI.ListView
|
||||
|
||||
@MainActor
|
||||
struct ListView: View {
|
||||
|
||||
/**
|
||||
@@ -81,7 +82,7 @@ extension Modern.ColorsDemo.SwiftUI {
|
||||
Modern.ColorsDemo.dataStack.perform(
|
||||
asynchronous: { transaction in
|
||||
|
||||
transaction.delete(objectIDs: objectIDsToDelete)
|
||||
transaction.delete(persistentIDs: objectIDsToDelete)
|
||||
},
|
||||
completion: { _ in }
|
||||
)
|
||||
|
||||
+10
-9
@@ -92,7 +92,7 @@ extension Modern.ColorsDemo.UIKit {
|
||||
_ monitor: ObjectMonitor<Modern.ColorsDemo.Palette>,
|
||||
didUpdateObject object: sending Modern.ColorsDemo.Palette,
|
||||
changedPersistentKeys: Set<KeyPathString>,
|
||||
sourceIdentifier: Any?
|
||||
sourceIdentifier: (any Sendable)?
|
||||
) {
|
||||
|
||||
MainActor.assumeIsolated {
|
||||
@@ -253,10 +253,11 @@ extension Modern.ColorsDemo.UIKit {
|
||||
private dynamic func hueSliderValueDidChange(_ sender: UISlider) {
|
||||
|
||||
let value = sender.value
|
||||
let paletteID = self.palette.object?.persistentID()
|
||||
Modern.ColorsDemo.dataStack.perform(
|
||||
asynchronous: { [weak self] (transaction) in
|
||||
asynchronous: { transaction in
|
||||
|
||||
let palette = transaction.edit(self?.palette.object)
|
||||
let palette = transaction.edit(paletteID)
|
||||
palette?.hue = value
|
||||
},
|
||||
completion: { _ in }
|
||||
@@ -268,10 +269,11 @@ extension Modern.ColorsDemo.UIKit {
|
||||
private dynamic func saturationSliderValueDidChange(_ sender: UISlider) {
|
||||
|
||||
let value = sender.value
|
||||
let paletteID = self.palette.object?.persistentID()
|
||||
Modern.ColorsDemo.dataStack.perform(
|
||||
asynchronous: { [weak self] (transaction) in
|
||||
asynchronous: { transaction in
|
||||
|
||||
let palette = transaction.edit(self?.palette.object)
|
||||
let palette = transaction.edit(paletteID)
|
||||
palette?.saturation = value
|
||||
},
|
||||
completion: { _ in }
|
||||
@@ -283,10 +285,11 @@ extension Modern.ColorsDemo.UIKit {
|
||||
private dynamic func brightnessSliderValueDidChange(_ sender: UISlider) {
|
||||
|
||||
let value = sender.value
|
||||
let paletteID = self.palette.object?.persistentID()
|
||||
Modern.ColorsDemo.dataStack.perform(
|
||||
asynchronous: { [weak self] (transaction) in
|
||||
asynchronous: { transaction in
|
||||
|
||||
let palette = transaction.edit(self?.palette.object)
|
||||
let palette = transaction.edit(paletteID)
|
||||
palette?.brightness = value
|
||||
},
|
||||
completion: { _ in }
|
||||
@@ -294,5 +297,3 @@ extension Modern.ColorsDemo.UIKit {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -81,7 +81,7 @@ extension Modern.ColorsDemo.UIKit {
|
||||
self.dataStack.perform(
|
||||
asynchronous: { (transaction) in
|
||||
|
||||
transaction.delete(objectIDs: [itemID])
|
||||
transaction.delete(itemID)
|
||||
},
|
||||
sourceIdentifier: Modern.ColorsDemo.TransactionSource.delete,
|
||||
completion: { _ in }
|
||||
|
||||
@@ -14,6 +14,7 @@ extension Modern.PlacemarksDemo {
|
||||
|
||||
// MARK: - Modern.PlacemarksDemo.MainView
|
||||
|
||||
@MainActor
|
||||
struct MainView: View {
|
||||
|
||||
/**
|
||||
@@ -21,10 +22,11 @@ extension Modern.PlacemarksDemo {
|
||||
*/
|
||||
private func demoAsynchronousTransaction(coordinate: CLLocationCoordinate2D) {
|
||||
|
||||
let persistentID = self.$place?.persistentID()
|
||||
Modern.PlacemarksDemo.dataStack.perform(
|
||||
asynchronous: { (transaction) in
|
||||
|
||||
let place = self.$place?.asEditable(in: transaction)
|
||||
let place = persistentID?.asEditable(in: transaction)
|
||||
place?.annotation = .init(coordinate: coordinate)
|
||||
},
|
||||
completion: { _ in }
|
||||
@@ -107,7 +109,7 @@ extension Modern.PlacemarksDemo {
|
||||
return
|
||||
}
|
||||
let geocoded = await self.geocoder.geocode(place: place)
|
||||
guard self.place?.objectID() == place.objectID() else {
|
||||
guard self.place?.persistentID() == place.persistentID() else {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ extension Modern.PokedexDemo {
|
||||
|
||||
// MARK: ImportableObject
|
||||
|
||||
typealias ImportSource = Dictionary<String, Any>
|
||||
typealias ImportSource = Dictionary<String, any Sendable>
|
||||
|
||||
|
||||
// MARK: ImportableUniqueObject
|
||||
|
||||
@@ -11,6 +11,7 @@ extension Modern.PokedexDemo {
|
||||
|
||||
// MARK: - Modern.PokedexDemo.MainView
|
||||
|
||||
@MainActor
|
||||
struct MainView<ListView: View>: View {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
@@ -36,7 +36,7 @@ extension Modern.PokedexDemo {
|
||||
|
||||
// MARK: ImportableObject
|
||||
|
||||
typealias ImportSource = (index: Int, json: Dictionary<String, Any>)
|
||||
typealias ImportSource = (index: Int, json: Dictionary<String, any Sendable>)
|
||||
|
||||
func didInsert(from source: ImportSource, in transaction: BaseDataTransaction) throws {
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ extension Modern.PokedexDemo {
|
||||
/**
|
||||
⭐️ Sample 1: Importing a list of JSON data into `ImportableUniqueObject`s whose `ImportSource` are tuples
|
||||
*/
|
||||
private static func importPokedexEntries(from data: Data) async throws {
|
||||
private static func importPokedexEntries(from data: Data) async throws(Modern.PokedexDemo.Service.Error) {
|
||||
|
||||
do {
|
||||
|
||||
@@ -43,7 +43,7 @@ extension Modern.PokedexDemo {
|
||||
}
|
||||
catch {
|
||||
|
||||
throw self.mapError(error)
|
||||
throw self.mapError(.init(error))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,31 +51,31 @@ extension Modern.PokedexDemo {
|
||||
⭐️ Sample 2: Importing a single JSON data into an `ImportableUniqueObject` whose `ImportSource` is a JSON `Dictionary`
|
||||
*/
|
||||
private static func importSpecies(
|
||||
for detailsObjectID: NSManagedObjectID,
|
||||
for detailsPersistentID: DynamicObjectID<Modern.PokedexDemo.Details>,
|
||||
from data: Data
|
||||
) async throws -> ObjectSnapshot<Modern.PokedexDemo.Species> {
|
||||
|
||||
let speciesObjectID = try await Modern.PokedexDemo.dataStack.async.perform { transaction -> NSManagedObjectID in
|
||||
let speciesPersistentID = try await Modern.PokedexDemo.dataStack.async.perform { transaction in
|
||||
|
||||
let json: Dictionary<String, Any> = try self.parseJSON(
|
||||
try JSONSerialization.jsonObject(with: data, options: [])
|
||||
)
|
||||
guard
|
||||
let species = try transaction.importUniqueObject(
|
||||
Into<Modern.PokedexDemo.Species>(),
|
||||
source: json
|
||||
let json: Dictionary<String, Any> = try self.parseJSON(
|
||||
try JSONSerialization.jsonObject(with: data, options: [])
|
||||
)
|
||||
else {
|
||||
guard
|
||||
let species = try transaction.importUniqueObject(
|
||||
Into<Modern.PokedexDemo.Species>(),
|
||||
source: .init(json: json)
|
||||
)
|
||||
else {
|
||||
|
||||
throw Modern.PokedexDemo.Service.Error.unexpected
|
||||
}
|
||||
transaction
|
||||
.edit(Into<Modern.PokedexDemo.Details>(), detailsObjectID)?
|
||||
.edit(Into<Modern.PokedexDemo.Details>(), detailsPersistentID)?
|
||||
.species = species
|
||||
return species.objectID()
|
||||
return species.persistentID()
|
||||
}
|
||||
guard
|
||||
let species: Modern.PokedexDemo.Species = Modern.PokedexDemo.dataStack.fetchExisting(speciesObjectID),
|
||||
let species: Modern.PokedexDemo.Species = Modern.PokedexDemo.dataStack.fetchExisting(speciesPersistentID),
|
||||
let snapshot = species.asSnapshot()
|
||||
else {
|
||||
|
||||
@@ -88,7 +88,7 @@ extension Modern.PokedexDemo {
|
||||
⭐️ Sample 3: Importing a list of JSON data into `ImportableUniqueObject`s whose `ImportSource` are JSON `Dictionary`s
|
||||
*/
|
||||
private static func importForms(
|
||||
for detailsObjectID: NSManagedObjectID,
|
||||
for detailsPersistentID: DynamicObjectID<Modern.PokedexDemo.Details>,
|
||||
from dataArray: [Data]
|
||||
) async throws {
|
||||
|
||||
@@ -110,13 +110,13 @@ extension Modern.PokedexDemo {
|
||||
throw Modern.PokedexDemo.Service.Error.unexpected
|
||||
}
|
||||
transaction
|
||||
.edit(Into<Modern.PokedexDemo.Details>(), detailsObjectID)?
|
||||
.edit(Into<Modern.PokedexDemo.Details>(), detailsPersistentID)?
|
||||
.forms = forms
|
||||
}
|
||||
}
|
||||
catch {
|
||||
|
||||
throw self.mapError(error)
|
||||
throw self.mapError(.init(error))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,8 +195,8 @@ extension Modern.PokedexDemo {
|
||||
if let species = details.$species?.snapshot {
|
||||
|
||||
self.fetchFormsIfNeeded(
|
||||
key: species.$id,
|
||||
detailsObjectID: details.objectID(),
|
||||
key: String(species.$id),
|
||||
detailsPersistentID: details.persistentID(),
|
||||
species: species
|
||||
)
|
||||
return
|
||||
@@ -207,7 +207,7 @@ extension Modern.PokedexDemo {
|
||||
return
|
||||
}
|
||||
let speciesURL = pokedexEntry.$speciesURL
|
||||
let detailsObjectID = details.objectID()
|
||||
let detailsPersistentID = details.persistentID()
|
||||
self.detailTasks[key] = Task { [weak self] in
|
||||
|
||||
guard let self else {
|
||||
@@ -220,7 +220,7 @@ extension Modern.PokedexDemo {
|
||||
}
|
||||
await self.fetchSpecies(
|
||||
key: key,
|
||||
detailsObjectID: detailsObjectID,
|
||||
detailsPersistentID: detailsPersistentID,
|
||||
speciesURL: speciesURL
|
||||
)
|
||||
}
|
||||
@@ -289,7 +289,7 @@ extension Modern.PokedexDemo {
|
||||
|
||||
private func fetchSpecies(
|
||||
key: String,
|
||||
detailsObjectID: NSManagedObjectID,
|
||||
detailsPersistentID: DynamicObjectID<Modern.PokedexDemo.Details>,
|
||||
speciesURL: URL
|
||||
) async {
|
||||
|
||||
@@ -299,7 +299,7 @@ extension Modern.PokedexDemo {
|
||||
try Task.checkCancellation()
|
||||
|
||||
let species = try await Self.importSpecies(
|
||||
for: detailsObjectID,
|
||||
for: detailsPersistentID,
|
||||
from: data
|
||||
)
|
||||
guard species.$details?.snapshot?.$forms.isEmpty == true else {
|
||||
@@ -307,7 +307,7 @@ extension Modern.PokedexDemo {
|
||||
return
|
||||
}
|
||||
await self.fetchForms(
|
||||
detailsObjectID: detailsObjectID,
|
||||
detailsPersistentID: detailsPersistentID,
|
||||
formsURLs: species.$formsURLs
|
||||
)
|
||||
}
|
||||
@@ -331,7 +331,7 @@ extension Modern.PokedexDemo {
|
||||
|
||||
private func fetchFormsIfNeeded(
|
||||
key: String,
|
||||
detailsObjectID: NSManagedObjectID,
|
||||
detailsPersistentID: DynamicObjectID<Modern.PokedexDemo.Details>,
|
||||
species: ObjectSnapshot<Modern.PokedexDemo.Species>
|
||||
) {
|
||||
|
||||
@@ -356,14 +356,14 @@ extension Modern.PokedexDemo {
|
||||
self.detailTasks.removeValue(forKey: key)
|
||||
}
|
||||
await self.fetchForms(
|
||||
detailsObjectID: detailsObjectID,
|
||||
detailsPersistentID: detailsPersistentID,
|
||||
formsURLs: formsURLs
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func fetchForms(
|
||||
detailsObjectID: NSManagedObjectID,
|
||||
detailsPersistentID: DynamicObjectID<Modern.PokedexDemo.Details>,
|
||||
formsURLs: [URL]
|
||||
) async {
|
||||
|
||||
@@ -378,7 +378,7 @@ extension Modern.PokedexDemo {
|
||||
dataArray.append(data)
|
||||
}
|
||||
try await Self.importForms(
|
||||
for: detailsObjectID,
|
||||
for: detailsPersistentID,
|
||||
from: dataArray
|
||||
)
|
||||
}
|
||||
@@ -408,7 +408,7 @@ extension Modern.PokedexDemo {
|
||||
case networkError(URLError)
|
||||
case parseError(expected: Any.Type, actual: Any.Type, file: String)
|
||||
case saveError(CoreStoreError)
|
||||
case otherError(Swift.Error)
|
||||
case otherError(Swift::Error)
|
||||
case unexpected
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,10 @@ extension Modern.PokedexDemo {
|
||||
|
||||
// MARK: ImportableObject
|
||||
|
||||
typealias ImportSource = Dictionary<String, Any>
|
||||
struct ImportSource: @unchecked Sendable {
|
||||
|
||||
let json: Dictionary<String, Any>
|
||||
}
|
||||
|
||||
|
||||
// MARK: ImportableUniqueObject
|
||||
@@ -82,14 +85,14 @@ extension Modern.PokedexDemo {
|
||||
|
||||
static func uniqueID(from source: ImportSource, in transaction: BaseDataTransaction) throws -> UniqueIDType? {
|
||||
|
||||
let json = source
|
||||
let json = source.json
|
||||
return try Modern.PokedexDemo.Service.parseJSON(json["id"])
|
||||
}
|
||||
|
||||
func update(from source: ImportSource, in transaction: BaseDataTransaction) throws {
|
||||
|
||||
typealias Service = Modern.PokedexDemo.Service
|
||||
let json = source
|
||||
let json = source.json
|
||||
|
||||
self.name = try Service.parseJSON(json["name"])
|
||||
self.weight = try Service.parseJSON(json["weight"])
|
||||
|
||||
+20
-8
@@ -50,7 +50,7 @@ extension Modern.PokedexDemo.UIKit {
|
||||
fatalError()
|
||||
}
|
||||
|
||||
deinit {
|
||||
isolated deinit {
|
||||
|
||||
self.listPublisher.removeObserver(self)
|
||||
}
|
||||
@@ -94,13 +94,25 @@ extension Modern.PokedexDemo.UIKit {
|
||||
|
||||
private func startObservingList() {
|
||||
|
||||
self.listPublisher.addObserver(self) { (listPublisher) in
|
||||
|
||||
self.dataSource.apply(
|
||||
listPublisher.snapshot,
|
||||
animatingDifferences: true
|
||||
)
|
||||
}
|
||||
self.listPublisher.addObserver(
|
||||
self,
|
||||
notifyInitial: false,
|
||||
{ [weak self] (listPublisher) in
|
||||
|
||||
let snapshot = listPublisher.snapshot
|
||||
withMainActorImmediate {
|
||||
|
||||
guard let self else {
|
||||
|
||||
return
|
||||
}
|
||||
self.dataSource.apply(
|
||||
snapshot,
|
||||
animatingDifferences: true
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
self.dataSource.apply(
|
||||
self.listPublisher.snapshot,
|
||||
animatingDifferences: false
|
||||
|
||||
@@ -13,52 +13,70 @@ extension Menu {
|
||||
// MARK: - Menu.MainView
|
||||
|
||||
struct MainView: View {
|
||||
|
||||
@State
|
||||
private var selection: Menu.Route?
|
||||
|
||||
|
||||
@Environment(\.horizontalSizeClass)
|
||||
private var horizontalSizeClass
|
||||
|
||||
// MARK: View
|
||||
|
||||
@ViewBuilder
|
||||
var body: some View {
|
||||
|
||||
NavigationSplitView(
|
||||
sidebar: {
|
||||
|
||||
List(selection: self.$selection) {
|
||||
|
||||
ForEach(Menu.Section.allCases, id: \.self) { section in
|
||||
|
||||
Section(section.rawValue) {
|
||||
|
||||
ForEach(section.routes) { route in
|
||||
|
||||
Menu.ItemView(
|
||||
title: route.title,
|
||||
subtitle: route.subtitle,
|
||||
isEnabled: route.isEnabled
|
||||
)
|
||||
.tag(route as Menu.Route?)
|
||||
.disabled(!route.isEnabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("CoreStore Demos")
|
||||
.listStyle(.sidebar)
|
||||
},
|
||||
detail: {
|
||||
|
||||
if let selection = self.selection {
|
||||
|
||||
selection.destination
|
||||
}
|
||||
else {
|
||||
|
||||
|
||||
if self.horizontalSizeClass == .compact {
|
||||
NavigationStack {
|
||||
self.menuList
|
||||
}
|
||||
}
|
||||
else {
|
||||
NavigationSplitView(
|
||||
sidebar: {
|
||||
self.menuList
|
||||
},
|
||||
detail: {
|
||||
Menu.PlaceholderView()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
@ViewBuilder
|
||||
private var menuList: some View {
|
||||
List {
|
||||
|
||||
ForEach(Menu.Section.allCases, id: \.self) { section in
|
||||
|
||||
SwiftUI.Section(
|
||||
content: {
|
||||
|
||||
ForEach(section.routes) { route in
|
||||
|
||||
NavigationLink(
|
||||
destination: {
|
||||
route.destination
|
||||
},
|
||||
label: {
|
||||
Menu.ItemView(
|
||||
title: route.title,
|
||||
subtitle: route.subtitle,
|
||||
isEnabled: route.isEnabled
|
||||
)
|
||||
}
|
||||
)
|
||||
.disabled(!route.isEnabled)
|
||||
}
|
||||
},
|
||||
header: {
|
||||
|
||||
Text(section.rawValue)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
.navigationTitle("CoreStore Demos")
|
||||
.listStyle(.sidebar)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// WithMainActorImmediate.swift
|
||||
// Demo
|
||||
//
|
||||
// Created by John Estropia on 2026/07/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
// MARK: - withMainActorImmediate
|
||||
|
||||
func withMainActorImmediate(
|
||||
_ task: @MainActor @Sendable @escaping () -> Void
|
||||
) {
|
||||
|
||||
if #available(iOS 26.0, *) {
|
||||
Task.immediate(operation: task)
|
||||
}
|
||||
else if Thread.isMainThread {
|
||||
|
||||
MainActor.assumeIsolated {
|
||||
|
||||
task()
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
Task.init(operation: task)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user