Skip to content

Commit

Permalink
Swift 5.9 fixes (#197)
Browse files Browse the repository at this point in the history
* Swift 5.9 fixes

* Fix CI reference

* Consistent

* fix

* wip

* fix

* wip
  • Loading branch information
stephencelis authored Aug 12, 2024
1 parent 7ea5fcc commit dfe94f7
Show file tree
Hide file tree
Showing 16 changed files with 146 additions and 110 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ final class ErasedNavigationStackController: NavigationStackController, UIKitCas
let isPresentedInSheet = true
private var model: Model!

convenience init() {
@UIBindable var model = Model()
convenience init(model: Model) {
@UIBindable var model = model
self.init(path: $model.path) {
RootViewController(model: model)
}
Expand Down Expand Up @@ -256,5 +256,5 @@ private class BoolFeatureViewController: UIViewController {
}

#Preview {
ErasedNavigationStackController()
ErasedNavigationStackController(model: ErasedNavigationStackController.Model())
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ final class StaticNavigationStackController: NavigationStackController, UIKitCas
let isPresentedInSheet = true
private var model: Model!

@MainActor
convenience init() {
@UIBindable var model = Model()
self.init(path: $model.path) {
Expand Down
2 changes: 1 addition & 1 deletion Examples/CaseStudies/UIKit/UIKitCaseStudies.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ struct UIKitCaseStudiesView: View {
}
CaseStudyGroupView("Stack navigation") {
StaticNavigationStackController()
ErasedNavigationStackController()
ErasedNavigationStackController(model: ErasedNavigationStackController.Model())
// TODO: state restoration
}
CaseStudyGroupView("Advanced") {
Expand Down
13 changes: 6 additions & 7 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
{
"originHash" : "0f769ae8f778ebc2843f0541f2d8e85fd717f40bcbaee30a77a887a4882e74fd",
"pins" : [
{
"identity" : "swift-case-paths",
Expand All @@ -24,8 +23,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-custom-dump",
"state" : {
"revision" : "aec6a73f5c1dc1f1be4f61888094b95cf995d973",
"version" : "1.3.2"
"revision" : "82645ec760917961cfa08c9c0c7104a57a0fa4b1",
"version" : "1.3.3"
}
},
{
Expand All @@ -40,7 +39,7 @@
{
"identity" : "swift-docc-symbolkit",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftlang/swift-docc-symbolkit",
"location" : "https://github.com/apple/swift-docc-symbolkit",
"state" : {
"revision" : "b45d1f2ed151d057b54504d653e0da5552844e34",
"version" : "1.0.0"
Expand All @@ -60,8 +59,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftlang/swift-syntax",
"state" : {
"revision" : "82a453c2dfa335c7e778695762438dfe72b328d2",
"version" : "600.0.0-prerelease-2024-07-24"
"revision" : "06b5cdc432e93b60e3bdf53aff2857c6b312991a",
"version" : "600.0.0-prerelease-2024-07-30"
}
},
{
Expand All @@ -74,5 +73,5 @@
}
}
],
"version" : 3
"version" : 2
}
4 changes: 2 additions & 2 deletions Sources/UIKitNavigation/Bindings/UIControl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,12 @@
@UncheckedSendable var uncheckedKeyPath = keyPath
let observation = observe(keyPath) { [$uncheckedKeyPath] control, _ in
guard isSetting.withValue({ !$0 }) else { return }
MainActor.assumeIsolated {
MainActor._assumeIsolated {
binding.wrappedValue = control[keyPath: $uncheckedKeyPath.wrappedValue]
}
}
let observationToken = ObservationToken { [weak self] in
MainActor.assumeIsolated {
MainActor._assumeIsolated {
self?.removeAction(action, for: .allEvents)
}
token.cancel()
Expand Down
6 changes: 3 additions & 3 deletions Sources/UIKitNavigation/Bindings/UITextField.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,12 @@
textSelection = selection.wrappedValue
}
let observation = observe(\.selectedTextRange) { control, _ in
MainActor.assumeIsolated {
MainActor._assumeIsolated {
selection.wrappedValue = control.textSelection
}
}
let observationToken = ObservationToken { [weak self] in
MainActor.assumeIsolated {
MainActor._assumeIsolated {
self?.removeAction(editingChangedAction, for: [.editingChanged, .editingDidBegin])
self?.removeAction(editingDidEndAction, for: .editingDidEnd)
}
Expand Down Expand Up @@ -215,7 +215,7 @@
}
}
let outerToken = ObservationToken { [weak self] in
MainActor.assumeIsolated {
MainActor._assumeIsolated {
self?.removeAction(editingDidBeginAction, for: .editingDidBegin)
self?.removeAction(editingDidEndAction, for: [.editingDidEnd, .editingDidEndOnExit])
}
Expand Down
30 changes: 30 additions & 0 deletions Sources/UIKitNavigation/Internal/AssumeIsolated.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Foundation

extension MainActor {
// NB: This functionality was not back-deployed in Swift 5.9
static func _assumeIsolated<T : Sendable>(
_ operation: @MainActor () throws -> T,
file: StaticString = #fileID,
line: UInt = #line
) rethrows -> T {
#if swift(<5.10)
typealias YesActor = @MainActor () throws -> T
typealias NoActor = () throws -> T

guard Thread.isMainThread else {
fatalError(
"Incorrect actor executor assumption; Expected same executor as \(self).",
file: file,
line: line
)
}

return try withoutActuallyEscaping(operation) { (_ fn: @escaping YesActor) throws -> T in
let rawFn = unsafeBitCast(fn, to: NoActor.self)
return try rawFn()
}
#else
return try assumeIsolated(operation, file: file, line: line)
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@

override func responds(to aSelector: Selector!) -> Bool {
aSelector == #selector(navigationController(_:didShow:animated:))
|| MainActor.assumeIsolated { base?.responds(to: aSelector) }
|| MainActor._assumeIsolated { base?.responds(to: aSelector) }
?? false
}

Expand Down
2 changes: 1 addition & 1 deletion Sources/UIKitNavigation/Navigation/Presentation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@
// NB: This can only be assumed because it is held in a UIViewController and is guaranteed to
// deinit alongside it on the main thread. If we use this other places we should force it
// to be a UIViewController as well, to ensure this functionality.
MainActor.assumeIsolated {
MainActor._assumeIsolated {
self.controller?.dismiss(animated: false)
}
}
Expand Down
13 changes: 6 additions & 7 deletions Sources/UIKitNavigation/Observe.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
@_spi(Internals) import SwiftNavigation
import UIKit

@MainActor
extension NSObject {
/// Observe access to properties of an observable (or perceptible) object.
///
Expand Down Expand Up @@ -105,7 +106,6 @@
/// of a property changes.
/// - Returns: A cancellation token.
@discardableResult
@MainActor
public func observe(_ apply: @escaping @MainActor @Sendable () -> Void) -> ObservationToken {
observe { _ in apply() }
}
Expand All @@ -118,12 +118,11 @@
/// of a property changes.
/// - Returns: A cancellation token.
@discardableResult
@MainActor
public func observe(
_ apply: @escaping @MainActor @Sendable (_ transaction: UITransaction) -> Void
) -> ObservationToken {
let token = SwiftNavigation.observe { transaction in
MainActor.assumeIsolated {
MainActor._assumeIsolated {
withUITransaction(transaction) {
#if os(watchOS)
apply(transaction)
Expand Down Expand Up @@ -164,13 +163,13 @@

fileprivate var tokens: [Any] {
get {
objc_getAssociatedObject(self, tokensKey) as? [Any] ?? []
objc_getAssociatedObject(self, Self.tokensKey) as? [Any] ?? []
}
set {
objc_setAssociatedObject(self, tokensKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_setAssociatedObject(self, Self.tokensKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
}
}

private nonisolated(unsafe) let tokensKey = malloc(1)!
private static let tokensKey = malloc(1)!
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-custom-dump",
"state" : {
"revision" : "aec6a73f5c1dc1f1be4f61888094b95cf995d973",
"version" : "1.3.2"
"revision" : "82645ec760917961cfa08c9c0c7104a57a0fa4b1",
"version" : "1.3.3"
}
},
{
"identity" : "swift-dependencies",
"kind" : "remoteSourceControl",
"location" : "http://github.com/pointfreeco/swift-dependencies",
"state" : {
"revision" : "4b521568a7fd257e83ce9a8d77f9865c1d01a7ca",
"version" : "1.3.6"
"revision" : "d7472be6b3c89251ce4c0db07d32405b43426781",
"version" : "1.3.7"
}
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
reference = "container:Tests/UIKitNavigation.xctestplan">
</TestPlanReference>
<TestPlanReference
reference = "container:Tests/SwiftNavigationTests/SwiftNavigation.xctestplan">
reference = "container:Tests/SwiftNavigation.xctestplan">
</TestPlanReference>
</TestPlans>
</TestAction>
Expand Down
86 changes: 44 additions & 42 deletions Tests/SwiftNavigationTests/IsolationTests.swift
Original file line number Diff line number Diff line change
@@ -1,50 +1,52 @@
import SwiftNavigation
import XCTest
#if swift(>=6)
import SwiftNavigation
import XCTest

class IsolationTests: XCTestCase {
@MainActor
func testIsolationOnMinActor() async {
let model = MainActorModel()
let expectation = expectation(description: "observation")
expectation.expectedFulfillmentCount = 2
let token = SwiftNavigation.observe {
_ = model.count
MainActor.assertIsolated()
expectation.fulfill()
class IsolationTests: XCTestCase {
@MainActor
func testIsolationOnMinActor() async {
let model = MainActorModel()
let expectation = expectation(description: "observation")
expectation.expectedFulfillmentCount = 2
let token = SwiftNavigation.observe {
_ = model.count
MainActor.assertIsolated()
expectation.fulfill()
}
model.count += 1
await fulfillment(of: [expectation], timeout: 1)
_ = token
}
model.count += 1
await fulfillment(of: [expectation], timeout: 1)
_ = token
}

@GlobalActorIsolated
func testIsolationOnGlobalActor() async {
let model = GlobalActorModel()
let expectation = expectation(description: "observation")
expectation.expectedFulfillmentCount = 2
let token = SwiftNavigation.observe {
_ = model.count
GlobalActorIsolated.assertIsolated()
expectation.fulfill()
@GlobalActorIsolated
func testIsolationOnGlobalActor() async {
let model = GlobalActorModel()
let expectation = expectation(description: "observation")
expectation.expectedFulfillmentCount = 2
let token = SwiftNavigation.observe {
_ = model.count
GlobalActorIsolated.assertIsolated()
expectation.fulfill()
}
model.count += 1
await fulfillment(of: [expectation], timeout: 1)
_ = token
}
model.count += 1
await fulfillment(of: [expectation], timeout: 1)
_ = token
}
}

@globalActor private actor GlobalActorIsolated: GlobalActor {
static let shared = GlobalActorIsolated()
}
@globalActor private actor GlobalActorIsolated: GlobalActor {
static let shared = GlobalActorIsolated()
}

@Perceptible
@MainActor
class MainActorModel {
var count = 0
}
@Perceptible
@MainActor
class MainActorModel {
var count = 0
}

@Perceptible
@GlobalActorIsolated
private class GlobalActorModel {
var count = 0
}
@Perceptible
@GlobalActorIsolated
private class GlobalActorModel {
var count = 0
}
#endif
Loading

0 comments on commit dfe94f7

Please sign in to comment.