Skip to content

Commit

Permalink
0.33.0
Browse files Browse the repository at this point in the history
  • Loading branch information
dankinsoid committed Mar 18, 2024
1 parent 2deb6fc commit b7e3752
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 14 deletions.
6 changes: 3 additions & 3 deletions Examples/SyncUps/SyncUps/SyncUpForm.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import SwiftUI
import VDStore
import VDFlow
import VDStore

struct SyncUpForm: Equatable {

Expand Down Expand Up @@ -60,7 +60,7 @@ struct SyncUpFormView: View {
Form {
Section {
TextField("Title", text: _state.syncUp.title)
.focused(_state.focus, equals: .title)
.focused(_state.focus, equals: .title)
HStack {
Slider(value: _state.syncUp.duration.minutes, in: 5 ... 30, step: 1) {
Text("Length")
Expand All @@ -75,7 +75,7 @@ struct SyncUpFormView: View {
Section {
ForEach(_state.syncUp.attendees) { attendee in
TextField("Name", text: attendee.name)
.focused(_state.focus, equals: .attendee(attendee.id))
.focused(_state.focus, equals: .attendee(attendee.id))
}
.onDelete { indices in
$state.deleteAttendees(atOffsets: indices)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ import PackageDescription
let package = Package(
name: "SomeProject",
dependencies: [
.package(url: "https://github.com/dankinsoid/VDStore.git", from: "0.32.0")
.package(url: "https://github.com/dankinsoid/VDStore.git", from: "0.33.0")
],
targets: [
.target(name: "SomeProject", dependencies: ["VDStore"])
Expand Down
2 changes: 1 addition & 1 deletion Sources/VDStore/Store.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ import SwiftUI
///
/// ### Thread safety
///
/// The `Store` class is isolated to main actor by @MainActor attribute.
/// The `Store` class is isolated to main actor by @MainActor attribute, but the thread safety is not guaranteed. All the state changes should be done on the main thread.
@propertyWrapper
@dynamicMemberLookup
@MainActor
Expand Down
71 changes: 71 additions & 0 deletions Sources/VDStore/Utils/RuntimeWarnings.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import Foundation

@_transparent
@usableFromInline
@inline(__always)
func runtimeWarn(
_ message: @autoclosure () -> String,
category: String? = "VDStore"
) {
#if DEBUG
let message = message()
let category = category ?? "Runtime Warning"
if _XCTIsTesting {
#if canImport(XCTest)
XCTFail(message)
#endif
} else {
#if canImport(os)
os_log(
.fault,
dso: dso,
log: OSLog(subsystem: "com.apple.runtime-issues", category: category),
"%@",
message
)
#else
fputs("\(formatter.string(from: Date())) [\(category)] \(message)\n", stderr)
#endif
}
#endif
}

#if DEBUG
#if canImport(XCTest)
import XCTest
#endif

#if canImport(os)
import os

// NB: Xcode runtime warnings offer a much better experience than traditional assertions and
// breakpoints, but Apple provides no means of creating custom runtime warnings ourselves.
// To work around this, we hook into SwiftUI's runtime issue delivery mechanism, instead.
//
// Feedback filed: https://gist.github.com/stephencelis/a8d06383ed6ccde3e5ef5d1b3ad52bbc
@usableFromInline
let dso = { () -> UnsafeMutableRawPointer in
let count = _dyld_image_count()
for i in 0 ..< count {
if let name = _dyld_get_image_name(i) {
let swiftString = String(cString: name)
if swiftString.hasSuffix("/SwiftUI") {
if let header = _dyld_get_image_header(i) {
return UnsafeMutableRawPointer(mutating: UnsafeRawPointer(header))
}
}
}
}
return UnsafeMutableRawPointer(mutating: #dsohandle)
}()
#else
import Foundation

@usableFromInline
let formatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:MM:SS.sssZ"
return formatter
}()
#endif
#endif
13 changes: 13 additions & 0 deletions Sources/VDStore/Utils/StoreBox.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ private final class StoreRootBox<State>: StoreRootBoxType, Publisher {
private var _state: State
var state: State {
get {
checkStateThread()
_$observationRegistrar.access(box: self)
return _state
}
Expand Down Expand Up @@ -206,3 +207,15 @@ private struct MockObservationRegistrar: ObservationRegistrarProtocol {
try mutation()
}
}

private func checkStateThread() {
threadCheck(message:
"""
Store state was accessed on a non-main thread. …
The "Store" struct is not thread-safe, and so all interactions with an instance of \
"Store" (including all of its scopes and derived view stores) must be done on the main \
thread.
"""
)
}
12 changes: 12 additions & 0 deletions Sources/VDStore/Utils/ThreadCheck.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Foundation

#if DEBUG
@inline(__always)
func threadCheck(message: @autoclosure () -> String) {
guard !Thread.isMainThread else { return }
runtimeWarn(message())
}
#else
@_transparent
func threadCheck(status: ThreadCheckStatus) {}
#endif
18 changes: 9 additions & 9 deletions Sources/VDStore/ViewStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ public struct ViewStore<State>: DynamicProperty {
nonmutating set { store.state = newValue }
}

public var projectedValue: Store<State> {
store
}
public var projectedValue: Store<State> {
store
}

public var store: Store<State> {
public var store: Store<State> {
let result: Store<State>
switch property {
case let .stateObject(observable):
Expand Down Expand Up @@ -61,11 +61,11 @@ public struct ViewStore<State>: DynamicProperty {
self.init(Store(wrappedValue: state))
}

public subscript<LocalValue>(
dynamicMember keyPath: WritableKeyPath<State, LocalValue>
) -> Binding<LocalValue> {
store.binding[dynamicMember: keyPath]
}
public subscript<LocalValue>(
dynamicMember keyPath: WritableKeyPath<State, LocalValue>
) -> Binding<LocalValue> {
store.binding[dynamicMember: keyPath]
}

@MainActor
private enum Property: DynamicProperty {
Expand Down

0 comments on commit b7e3752

Please sign in to comment.