Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add a test displaying a system used as a feedback of another system #15

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 55 additions & 1 deletion Example/SignIn/SignIn.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ final class SignInViewModel: ViewModel<SignInViewModel.State, SignInViewModel.Ev
SignInViewModel.whenChangingUserName(api: GithubAPI()),
SignInViewModel.whenSubmiting(api: GithubAPI())
],
scheduler: RunLoop.main,
scheduler: DispatchQueue.main,
reducer: SignInViewModel.reduce
)
}
Expand Down Expand Up @@ -141,6 +141,8 @@ struct SignInView: View {
self.context = context
}

@SwiftUI.State var userName: String = ""

var body: some View {
return Form {
Section {
Expand Down Expand Up @@ -179,9 +181,21 @@ struct SignInView: View {
.textContentType(.newPassword)
}
Section {
// Seems like a bug in switch view amimation
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Seems like a bug in switch view amimation
// Seems like a bug in switch view animation

// ¯\_(ツ)_/¯
Toggle(isOn: context.binding(for: \.termsAccepted)) {
Text("Accept Terms and Conditions")
}
// When wrapping `UISwitch` into UIViewRepresentable
// everything works
HStack(
alignment: .center,
spacing: 8
) {
Text("Accept Terms and Conditions")
Spacer()
Switch(isOn: context.binding(for: \.termsAccepted))
}
}
Section {
ZStack {
Expand Down Expand Up @@ -264,3 +278,43 @@ extension String {
return self.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? ""
}
}

struct Switch: UIViewRepresentable {
let isOn: Binding<Bool>
let animated = true

func makeUIView(context: UIViewRepresentableContext<Switch>) -> UISwitch {
let view = UISwitch(frame: .zero)

view.addTarget(
context.coordinator,
action: #selector(Target.action(_:)),
for: .valueChanged
)

return view
}

func updateUIView(_ uiView: UISwitch, context: UIViewRepresentableContext<Switch>) {
uiView.setOn(isOn.wrappedValue, animated: animated)
}

func makeCoordinator() -> Target {
return Target { (view) in
self.isOn.wrappedValue = view.isOn
}
}

class Target: NSObject {
init(action: @escaping (UISwitch) -> Void) {
self._action = action
}

private let _action: (UISwitch) -> Void

@objc
func action(_ sender: UISwitch) {
_action(sender)
}
}
}
54 changes: 54 additions & 0 deletions Tests/CombineFeedbackTests/CombineFeedbackTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,58 @@ class CombineFeedbackTests: XCTestCase {

XCTAssertEqual("initial_a", value)
}

func test_system_in_a_system() {
let scheduler = TestScheduler()

let feedback1 = Feedback<String, String>(effects: { state -> AnyPublisher<String, Never> in
if state.count % 3 == 1 {
return Just("a").eraseToAnyPublisher()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest to add _a so it's easier to understand what's happening in the assertion

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the best example I could write at the time that uses simple string manipulation to display a system within a system. The outer system sends the inner system a string, the inner system accepts the string and returns another string back to the outer system as an event. The Outer system then appends the string to it's state and then because we've reached a point in the inner systems where it will no longer send a new event. (state.count % 3 == 2) Is that state. It isn't obvious because it is not written.

Looking at it 20 days later I've got to ask myself if it is a good choice for an example, not really? But it certainly is okay for testing that things should work.

However if I tried to keep your system of using _ to operate the events my simple use of '%' will no longer work since state.count will increase in a different way.

Any other recommendations? I can go farther, not use a string as the state but and make the internal state model something else, but I'd like your approval on that stylistically first since it will no longer be a file of test that always uses a simple string as it's state

} else {
return Empty<String,Never>().eraseToAnyPublisher()
}
})

let feedback2 = Feedback<String, String>(effects: { state -> AnyPublisher<String, Never> in
if state.count % 3 == 0 {
return Just("d").eraseToAnyPublisher()
} else {
return Empty<String,Never>().eraseToAnyPublisher()
}
})

let innerSystem = Feedback<String, String>{ (stringPublisher: AnyPublisher<String, Never>) -> AnyPublisher<String, Never> in
return stringPublisher.flatMap { (string) -> AnyPublisher<String, Never> in
return Publishers.system(initial: string,
feedbacks: [feedback1, feedback2],
scheduler: scheduler) { (state: String, event: String) -> String in
return state + event
}.filter({ (state) -> Bool in
state.count % 2 == 0
}).map({ (state) -> String in
return state + "c"
}).eraseToAnyPublisher()
}.eraseToAnyPublisher()
}

let outerSystem = Publishers.system(
initial: "initial",
feedbacks: [innerSystem],
scheduler: scheduler,
reduce: { (state: String, event: String) -> String in
return event
}
)

var value: String?

_ = outerSystem.sink(
receiveValue: {
value = $0
}
)

scheduler.advance()
XCTAssertEqual("initialacdc", value)
}
}