Skip to content

Commit

Permalink
doesnt work
Browse files Browse the repository at this point in the history
  • Loading branch information
mbrandonw committed Aug 22, 2024
1 parent 18d5db4 commit 6d0d1d0
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 33 deletions.
28 changes: 28 additions & 0 deletions Examples/CaseStudies/CaseStudiesApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,34 @@ struct CaseStudiesApp: App {
var body: some Scene {
WindowGroup {
RootView()
// ContentView()
}
}
}

import UIKitNavigation
struct ContentView: View {
@State var isHidden = false
var body: some View {
Form {
Text("Hello!")
.opacity(isHidden ? 0 : 1 )

// animations: [PlatformKey: ]
// perform.uiKit
// perform.wasm
// perform.uiKit

Button("Tap") {
withUIKitAnimation(.linear) {
withAnimation(.linear(duration: 0.3)) {
isHidden.toggle()
}
}
}
}
}
}
#Preview {

}
19 changes: 18 additions & 1 deletion Sources/SwiftNavigation/Observe.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,24 @@ private func onChange(
apply(.current)
} onChange: {
task(.current) {
onChange(apply, task: task)
onChange(apply) { transaction, operation in
var perform: @Sendable () -> Void = {
task(transaction, operation)
}
for value in transaction.storage.keys {
@Sendable func open<K: PerformKey>(_: K.Type, operation: @escaping @Sendable () -> Void) {
K.perform(transaction: transaction) {
operation()
}
}
if let type = value.keyType as? any PerformKey.Type {
perform = { [perform] in
open(type, operation: perform)
}
}
}
perform()
}
}
}
}
Expand Down
11 changes: 9 additions & 2 deletions Sources/SwiftNavigation/UITransaction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public func withUITransaction<R, V>(
public struct UITransaction: Sendable {
@TaskLocal package static var current = Self()

private var storage: [Key: any Sendable] = [:]
var storage: [Key: any Sendable] = [:]

/// Creates a transaction.
public init() {}
Expand Down Expand Up @@ -68,7 +68,7 @@ public struct UITransaction: Sendable {
storage.isEmpty
}

private struct Key: Hashable {
struct Key: Hashable {
let keyType: Any.Type
init<K: UITransactionKey>(_ keyType: K.Type) {
self.keyType = keyType
Expand All @@ -92,3 +92,10 @@ public protocol UITransactionKey {
/// The default value for the transaction key.
static var defaultValue: Value { get }
}

public protocol PerformKey: UITransactionKey, Sendable {
static func perform(
transaction: UITransaction,
operation: @escaping @Sendable () -> Void
)
}
29 changes: 1 addition & 28 deletions Sources/UIKitNavigation/Observe.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,34 +123,7 @@
) -> ObserveToken {
let token = SwiftNavigation.observe { transaction in
MainActor._assumeIsolated {
withUITransaction(transaction) {
#if os(watchOS)
apply(transaction)
#else
if transaction.uiKit.disablesAnimations {
UIView.performWithoutAnimation { apply(transaction) }
for completion in transaction.uiKit.animationCompletions {
completion(true)
}
} else if let animation = transaction.uiKit.animation {
return animation.perform(
{ apply(transaction) },
completion: transaction.uiKit.animationCompletions.isEmpty
? nil
: {
for completion in transaction.uiKit.animationCompletions {
completion($0)
}
}
)
} else {
apply(transaction)
for completion in transaction.uiKit.animationCompletions {
completion(true)
}
}
#endif
}
apply(transaction)
}
} task: { transaction, work in
DispatchQueue.main.async {
Expand Down
47 changes: 45 additions & 2 deletions Sources/UIKitNavigation/UITransaction.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
#if canImport(UIKit) && !os(watchOS)

import UIKit
import SwiftNavigation

extension UITransaction {
/// Creates a transaction and assigns its animation property.
///
Expand All @@ -11,11 +15,50 @@
/// UIKit-specific data associated with the current state change.
public var uiKit: UIKit {
get { self[UIKitKey.self] }
set { self[UIKitKey.self] = newValue }
set {
self[UIKitKey.self] = newValue
// self.perform = { transaction, work in
//
// }
}
}

private enum UIKitKey: UITransactionKey {
private enum UIKitKey: UITransactionKey, PerformKey {
static let defaultValue = UIKit()

public static func perform(
transaction: UITransaction,
operation: @escaping @Sendable () -> Void
) {
MainActor._assumeIsolated {
#if os(watchOS)
//apply(transaction)
#else
if transaction.uiKit.disablesAnimations {
UIView.performWithoutAnimation { operation() }
for completion in transaction.uiKit.animationCompletions {
completion(true)
}
} else if let animation = transaction.uiKit.animation {
return animation.perform(
{ operation() },
completion: transaction.uiKit.animationCompletions.isEmpty
? nil
: {
for completion in transaction.uiKit.animationCompletions {
completion($0)
}
}
)
} else {
operation()
for completion in transaction.uiKit.animationCompletions {
completion(true)
}
}
#endif
}
}
}

/// UIKit-specific data associated with a ``UITransaction``.
Expand Down
130 changes: 130 additions & 0 deletions Tests/UIKitNavigationTests/ObserveTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,135 @@
}
XCTAssertEqual(count, 1)
}

@MainActor
func testTransactionKeyPropagates() async {
let expectation = expectation(description: "onChange")
expectation.expectedFulfillmentCount = 2

let model = Model()
XCTAssertEqual(UITransaction.current.isSet, false)

observe {
if model.count == 0 {
XCTAssertEqual(UITransaction.current.isSet, false)
} else if model.count == 1 {
XCTAssertEqual(UITransaction.current.isSet, true)
} else {
XCTFail()
}
expectation.fulfill()
}

withUITransaction(\.isSet, true) {
model.count += 1
}
await fulfillment(of: [expectation], timeout: 1)
XCTAssertEqual(model.count, 1)
XCTAssertEqual(UITransaction.current.isSet, false)
}

@MainActor
func testTransactionKeyPropagatesWithAnimation() async {
let expectation = expectation(description: "onChange")
expectation.expectedFulfillmentCount = 2

let model = Model()
XCTAssertEqual(UITransaction.current.isSet, false)

observe {
if model.count == 0 {
XCTAssertEqual(UITransaction.current.isSet, false)
} else if model.count == 1 {
XCTAssertEqual(UITransaction.current.isSet, true)
} else {
XCTFail()
}
expectation.fulfill()
}

withUITransaction(\.isSet, true) {
withUIKitAnimation {
model.count += 1
}
}
await fulfillment(of: [expectation], timeout: 1)
XCTAssertEqual(model.count, 1)
XCTAssertEqual(UITransaction.current.isSet, false)
}

// TODO: write test for a transaction helper and make sure merging is happening under the hood

@MainActor
func testSynchronousTransactionKey() async {
let expectation = expectation(description: "onChange")

let model = Model()
XCTAssertEqual(UITransaction.current.isSet, false)

_ = withUITransaction(\.isSet, true) {
observe {
XCTAssertEqual(model.count, 0)
XCTAssertEqual(UITransaction.current.isSet, true)
expectation.fulfill()
}
}

await fulfillment(of: [expectation], timeout: 1)
XCTAssertEqual(UITransaction.current.isSet, false)
}

@MainActor
func testOverrideTransactionKey() async {
XCTAssertEqual(UITransaction.current.isSet, false)
withUITransaction(\.isSet, true) {
XCTAssertEqual(UITransaction.current.isSet, true)
withUITransaction(\.isSet, false) {
XCTAssertEqual(UITransaction.current.isSet, false)
}
}
}

@MainActor
func testBindingTransactionKey() async {
let expectation = expectation(description: "onChange")
expectation.expectedFulfillmentCount = 2

@UIBinding var count = 0
var transaction = UITransaction()
transaction.isSet = true

observe {
if count == 0 {
XCTAssertEqual(UITransaction.current.isSet, false)
} else if count == 1 {
XCTAssertEqual(UITransaction.current.isSet, true)
} else {
XCTFail()
}
expectation.fulfill()
}

let bindingWithTransaction = $count.transaction(transaction)
bindingWithTransaction.wrappedValue = 1

await fulfillment(of: [expectation], timeout: 1)
}
}

@Perceptible
private class Model {
var count = 0
}

private enum IsSetKey: UITransactionKey {
static let defaultValue = false
}
extension UITransaction {
var isSet: Bool {
get { self[IsSetKey.self] }
set { self[IsSetKey.self] = newValue }
}
}

#endif

0 comments on commit 6d0d1d0

Please sign in to comment.