-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
💲[Native Checkout] Stepper haptics #711
Changes from all commits
d567707
51518b5
34c14f9
1427503
efce1e6
fa57f81
a879bf3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,38 @@ | ||
import Library | ||
import UIKit | ||
|
||
// MARK: - Haptic feedback | ||
|
||
func generateImpactFeedback( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Convenience functions should now be unified place to trigger with the following improvements:
|
||
feedbackGenerator: UIImpactFeedbackGeneratorType = UIImpactFeedbackGenerator(style: .light) | ||
) { | ||
feedbackGenerator.prepare() | ||
feedbackGenerator.impactOccurred() | ||
} | ||
|
||
func generateNotificationSuccessFeedback( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any concern with these not being prefixed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since those are not extension functions or anything like that I went with simplicity |
||
feedbackGenerator: UINotificationFeedbackGeneratorType = UINotificationFeedbackGenerator() | ||
) { | ||
feedbackGenerator.prepare() | ||
feedbackGenerator.notificationOccurred(.success) | ||
} | ||
|
||
func generateNotificationWarningFeedback( | ||
feedbackGenerator: UINotificationFeedbackGeneratorType = UINotificationFeedbackGenerator() | ||
) { | ||
feedbackGenerator.prepare() | ||
feedbackGenerator.notificationOccurred(.warning) | ||
} | ||
|
||
func generateSelectionFeedback( | ||
feedbackGenerator: UISelectionFeedbackGeneratorType = UISelectionFeedbackGenerator() | ||
) { | ||
feedbackGenerator.prepare() | ||
feedbackGenerator.selectionChanged() | ||
} | ||
|
||
// MARK: - Login workflow | ||
|
||
public func logoutAndDismiss( | ||
viewController: UIViewController, | ||
appEnvironment: AppEnvironmentType.Type = AppEnvironment.self, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,42 @@ | |
import XCTest | ||
|
||
internal final class SharedFunctionsTests: XCTestCase { | ||
func testGenerateImpactFeedback() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tests to make sure that we call both |
||
let mockFeedbackGenerator = MockImpactFeedbackGenerator() | ||
|
||
generateImpactFeedback(feedbackGenerator: mockFeedbackGenerator) | ||
|
||
XCTAssertTrue(mockFeedbackGenerator.prepareWasCalled) | ||
XCTAssertTrue(mockFeedbackGenerator.impactOccurredWasCalled) | ||
} | ||
|
||
func testGenerateNotificationSuccessFeedback() { | ||
let mockFeedbackGenerator = MockNotificationFeedbackGenerator() | ||
|
||
generateNotificationSuccessFeedback(feedbackGenerator: mockFeedbackGenerator) | ||
|
||
XCTAssertTrue(mockFeedbackGenerator.prepareWasCalled) | ||
XCTAssertTrue(mockFeedbackGenerator.notificationOccurredWasCalled) | ||
} | ||
|
||
func testGenerateNotificationWarningFeedback() { | ||
let mockFeedbackGenerator = MockNotificationFeedbackGenerator() | ||
|
||
generateNotificationWarningFeedback(feedbackGenerator: mockFeedbackGenerator) | ||
|
||
XCTAssertTrue(mockFeedbackGenerator.prepareWasCalled) | ||
XCTAssertTrue(mockFeedbackGenerator.notificationOccurredWasCalled) | ||
} | ||
|
||
func testGenerateSelectionFeedback() { | ||
let mockFeedbackGenerator = MockSelectionFeedbackGenerator() | ||
|
||
generateSelectionFeedback(feedbackGenerator: mockFeedbackGenerator) | ||
|
||
XCTAssertTrue(mockFeedbackGenerator.prepareWasCalled) | ||
XCTAssertTrue(mockFeedbackGenerator.selectionChangedWasCalled) | ||
} | ||
|
||
func testLogoutAndDismiss() { | ||
let mockAppEnvironment = MockAppEnvironment.self | ||
let mockPushNotificationDialog = MockPushNotificationDialog.self | ||
|
@@ -20,27 +56,3 @@ internal final class SharedFunctionsTests: XCTestCase { | |
XCTAssertTrue(mockViewController.dismissAnimatedWasCalled) | ||
} | ||
} | ||
|
||
private struct MockAppEnvironment: AppEnvironmentType { | ||
static var logoutWasCalled = false | ||
|
||
static func logout() { | ||
self.logoutWasCalled = true | ||
} | ||
} | ||
|
||
private struct MockPushNotificationDialog: PushNotificationDialogType { | ||
static var resetAllContextsWasCalled = false | ||
|
||
static func resetAllContexts() { | ||
self.resetAllContextsWasCalled = true | ||
} | ||
} | ||
|
||
private class MockViewController: UIViewController { | ||
var dismissAnimatedWasCalled = false | ||
|
||
override func dismiss(animated _: Bool, completion _: (() -> Void)? = nil) { | ||
self.dismissAnimatedWasCalled = true | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import UIKit | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Protocol that allows us to mock and test our haptic feedback |
||
|
||
// MARK: - UIFeedbackGenerator | ||
|
||
protocol UIFeedbackGeneratorType { | ||
func prepare() | ||
} | ||
|
||
// MARK: - UIImpactFeedbackGeneratorType | ||
|
||
protocol UIImpactFeedbackGeneratorType: UIFeedbackGeneratorType { | ||
func impactOccurred() | ||
} | ||
|
||
extension UIImpactFeedbackGenerator: UIImpactFeedbackGeneratorType {} | ||
|
||
// MARK: - UINotificationFeedbackGeneratorType | ||
|
||
protocol UINotificationFeedbackGeneratorType: UIFeedbackGeneratorType { | ||
func notificationOccurred(_ notificationType: UINotificationFeedbackGenerator.FeedbackType) | ||
} | ||
|
||
extension UINotificationFeedbackGenerator: UINotificationFeedbackGeneratorType {} | ||
|
||
// MARK: - UISelectionFeedbackGeneratorType | ||
|
||
protocol UISelectionFeedbackGeneratorType: UIFeedbackGeneratorType { | ||
func selectionChanged() | ||
} | ||
|
||
extension UISelectionFeedbackGenerator: UISelectionFeedbackGeneratorType {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import Foundation | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Previously this was part of the test file that I touched, so I've extracted this since I was doing the same work for |
||
@testable import Library | ||
import UIKit | ||
|
||
struct MockAppEnvironment: AppEnvironmentType { | ||
static var logoutWasCalled = false | ||
|
||
static func logout() { | ||
self.logoutWasCalled = true | ||
} | ||
} | ||
|
||
struct MockPushNotificationDialog: PushNotificationDialogType { | ||
static var resetAllContextsWasCalled = false | ||
|
||
static func resetAllContexts() { | ||
self.resetAllContextsWasCalled = true | ||
} | ||
} | ||
|
||
class MockViewController: UIViewController { | ||
var dismissAnimatedWasCalled = false | ||
|
||
override func dismiss(animated _: Bool, completion _: (() -> Void)? = nil) { | ||
self.dismissAnimatedWasCalled = true | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import Foundation | ||
@testable import Kickstarter_Framework | ||
import UIKit | ||
|
||
class MockImpactFeedbackGenerator: UIImpactFeedbackGeneratorType { | ||
var prepareWasCalled = false | ||
var impactOccurredWasCalled = false | ||
|
||
func prepare() { | ||
self.prepareWasCalled = true | ||
} | ||
|
||
func impactOccurred() { | ||
self.impactOccurredWasCalled = true | ||
} | ||
} | ||
|
||
class MockNotificationFeedbackGenerator: UINotificationFeedbackGeneratorType { | ||
var prepareWasCalled = false | ||
var notificationOccurredWasCalled = false | ||
|
||
func prepare() { | ||
self.prepareWasCalled = true | ||
} | ||
|
||
func notificationOccurred(_: UINotificationFeedbackGenerator.FeedbackType) { | ||
self.notificationOccurredWasCalled = true | ||
} | ||
} | ||
|
||
class MockSelectionFeedbackGenerator: UISelectionFeedbackGeneratorType { | ||
var prepareWasCalled = false | ||
var selectionChangedWasCalled = false | ||
|
||
func prepare() { | ||
self.prepareWasCalled = true | ||
} | ||
|
||
func selectionChanged() { | ||
self.selectionChangedWasCalled = true | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -257,21 +257,15 @@ internal final class DiscoveryPostcardCell: UITableViewCell, ValueCell { | |
|
||
self.watchProjectViewModel.outputs.generateImpactFeedback | ||
.observeForUI() | ||
.observeValues { [weak self] in | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We now no longer need |
||
self?.saveButton.generateImpactFeedback(style: .light) | ||
} | ||
.observeValues { _ in generateImpactFeedback() } | ||
|
||
self.watchProjectViewModel.outputs.generateSuccessFeedback | ||
self.watchProjectViewModel.outputs.generateNotificationSuccessFeedback | ||
.observeForUI() | ||
.observeValues { [weak self] in | ||
self?.saveButton.generateSuccessFeedback() | ||
} | ||
.observeValues { generateNotificationSuccessFeedback() } | ||
|
||
self.watchProjectViewModel.outputs.generateSelectionFeedback | ||
.observeForUI() | ||
.observeValues { [weak self] in | ||
self?.saveButton.generateSelectionFeedback() | ||
} | ||
.observeValues { generateSelectionFeedback() } | ||
|
||
self.viewModel.outputs.projectCategoryName | ||
.signal | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -122,21 +122,15 @@ public final class ProjectNavBarViewController: UIViewController { | |
|
||
self.watchProjectViewModel.outputs.generateImpactFeedback | ||
.observeForUI() | ||
.observeValues { [weak self] in | ||
self?.saveButton.generateImpactFeedback(style: .light) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Team notice that this is a thing of past (these proxies were just duplicating things and made a lot of reasoning about our code harder than need be). So previously the flow would be button.generateImpactFeedback
-> UIFeedbackGenerator.ksr_impact()
-> UIImpactFeedbackGenerator(style: style).impactOccurred()` now we're removing one layer and doing UIFeedbackGenerator.ksr_generateImpactFeedback()
-> AppEnvironment.current.impactFeedbackGenerator.impactOccured (please not that the naming is now more consistent) & (this also allows us to test things) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After some feedback from Justin it's now even simpler...we only have one publicly available function for each haptic type. So what was previously this button.generateImpactFeedback
-> UIFeedbackGenerator.ksr_impact()
-> UIImpactFeedbackGenerator(style: style).impactOccurred() is now generateImpactFeedback() |
||
} | ||
.observeValues { generateImpactFeedback() } | ||
|
||
self.watchProjectViewModel.outputs.generateSuccessFeedback | ||
self.watchProjectViewModel.outputs.generateNotificationSuccessFeedback | ||
.observeForUI() | ||
.observeValues { [weak self] in | ||
self?.saveButton.generateSuccessFeedback() | ||
} | ||
.observeValues { generateNotificationSuccessFeedback() } | ||
|
||
self.watchProjectViewModel.outputs.generateSelectionFeedback | ||
.observeForUI() | ||
.observeValues { [weak self] in | ||
self?.saveButton.generateSelectionFeedback() | ||
} | ||
.observeValues { generateSelectionFeedback() } | ||
|
||
self.watchProjectViewModel.outputs.showProjectSavedAlert | ||
.observeForControllerAction() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please also review
ReactiveExtensions
updates