Hello,
I recently implemented a conditional debounce publisher using Swift's Combine.
If a string with a length less than 2 is passed, the event is sent downstream immediately without delay. If a string with a length of 2 or more is passed, the event is emitted downstream with a 0.2-second delay.
While writing test logic related to this, I noticed a strange phenomenon: sometimes the publisher, which should emit events with a 0.2-second delay, does not emit an event.
The test code below should have all indices from 1 to 100 in the array, but sometimes some indices are missing, causing the assertion to fail. Even after observing completion, cancel, and output events through handleEvents, I couldn't find any cause. Am I using Combine incorrectly, or is there a bug in Combine?
I would appreciate it if you could let me know.
import Foundation
import Combine
var cancellables: Set<AnyCancellable> = []
@MainActor func text(index: Int, completion: @escaping () -> Void) {
let subject = PassthroughSubject<String, Never>()
let textToSent = "textToSent"
subject
.map { text in
if text.count >= 2 {
return Just<String>(text)
.delay(for: .seconds(0.2), scheduler: RunLoop.main)
.eraseToAnyPublisher()
} else {
return Just<String>(text)
.eraseToAnyPublisher()
}
}
.switchToLatest()
.sink {
if $0.count >= 2 {
completion()
}
}.store(in: &cancellables)
for i in 0..<textToSent.count {
let stringIndex = textToSent.index(textToSent.startIndex, offsetBy: i)
let stringToSent = String(textToSent[textToSent.startIndex...stringIndex])
subject.send(stringToSent)
}
}
var array = [Int]()
for i in 1...100 {
text(index: i) {
array.append(i)
}
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
for i in 1...100 {
assert(array.contains(i))
}
}
RunLoop.main.run(until: .now + 10)
Post
Replies
Boosts
Views
Activity
Hello. Recently, while studying alignmentGuide, I had questions about it behaving differently from the documentation when setting multiple alignment guides.
For example, the document states that only the alignmentGuide modifier with a first parameter matching the container's alignment will function.
Therefore, I thought that writing the Swift code below would result in the yellow color's center alignment being aligned with the HStack's bottom alignment.
struct TestView: View {
var body: some View {
HStack(alignment: .bottom) {
Color.yellow
.frame(height: 50)
.alignmentGuide(VerticalAlignment.center) { dim in
dim[.top]
}
.alignmentGuide(VerticalAlignment.top) { dim in
dim[.bottom]
}
.alignmentGuide(VerticalAlignment.bottom) { dim in
dim[VerticalAlignment.center]
}
Text("Hello, world")
}
.border(.green)
}
}
Expect
However, in reality, I observed that the top of the yellow color aligns with the HStack's bottom alignment. From this, I inferred that the 3rd alignmentGuide is applied first, and this also causes the first alignmentGuide to work, which makes me curious about how this is possible. If I leave only the 3rd alignmentGuide, it behaves as I expected.
Real Behavior
Could anybody help me to figure it out this behavior? Thank you
Hello. I am developing an application using Swift 6 and SwiftUI.
I have custom implemented a BottomSheet that animates from bottom to top, and I attempted to achieve this animation by changing the alignmentGuide like this.
ZStack(alignment: .bottom) {
dimView
.opacity(isVisible ? 1 : 0)
.transaction { transaction in
transaction.animation = .easeInOut(duration: 0.35)
}
bottomSheetView
.alignmentGuide(VerticalAlignment.bottom) { dimension in
// compile error occur because isVisible property is state of MainActor isolated View!
isVisible ? dimension[.bottom] : dimension[.top]
}
}
There were no issues in Swift 5, but now I am encountering compile errors because the computeValue closure of the alignmentGuide is not isolated to the MainActor, preventing me from calling view state values or functions.
So I am curious if there are any plans to isolate this closure to the MainActor. From my observation, this closure is always called on the main thread.
Thank you.