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

Replace completion handlers with async/await. #407

Merged
merged 1 commit into from
Jan 5, 2023
Merged
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
6 changes: 3 additions & 3 deletions ElementX/Sources/Application/AppCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ class AppCoordinator: AppCoordinatorProtocol {
.sink { [weak self] callback in
switch callback {
case .registeredNotifications(let deviceToken):
self?.notificationManager?.register(with: deviceToken, completion: nil)
Task { await self?.notificationManager?.register(with: deviceToken) }
case .failedToRegisteredNotifications(let error):
self?.notificationManager?.registrationFailed(with: error)
}
Expand Down Expand Up @@ -467,8 +467,8 @@ extension AppCoordinator: NotificationManagerDelegate {
break
default:
// error or no room proxy
service.showLocalNotification(with: "⚠️ " + ElementL10n.dialogTitleError,
subtitle: ElementL10n.a11yErrorSomeMessageNotSent)
await service.showLocalNotification(with: "⚠️ " + ElementL10n.dialogTitleError,
subtitle: ElementL10n.a11yErrorSomeMessageNotSent)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class MockNotificationManager: NotificationManagerProtocol {
delegate?.authorizationStatusUpdated(self, granted: false)
}

func register(with deviceToken: Data, completion: ((Bool) -> Void)? = nil) { }
func register(with deviceToken: Data) async -> Bool { false }

func registrationFailed(with error: Error) { }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,30 +46,26 @@ class NotificationManager: NSObject, NotificationManagerProtocol {
options: [])
notificationCenter.setNotificationCategories([replyCategory])
notificationCenter.delegate = self
notificationCenter.requestAuthorization(options: [.alert, .sound, .badge]) { [weak self] granted, error in
guard let self else {
return
}
if let error {
MXLog.debug("[NotificationManager] request authorization failed: \(error)")
} else {
Task {
do {
let granted = try await notificationCenter.requestAuthorization(options: [.alert, .sound, .badge])
MXLog.debug("[NotificationManager] permission granted: \(granted)")
DispatchQueue.main.async {
await MainActor.run {
self.delegate?.authorizationStatusUpdated(self, granted: granted)
}
} catch {
MXLog.debug("[NotificationManager] request authorization failed: \(error)")
}
}
}

func register(with deviceToken: Data, completion: ((Bool) -> Void)? = nil) {
setPusher(with: deviceToken,
clientProxy: clientProxy,
completion: completion)
func register(with deviceToken: Data) async -> Bool {
await setPusher(with: deviceToken, clientProxy: clientProxy)
}

func registrationFailed(with error: Error) { }

func showLocalNotification(with title: String, subtitle: String?) {
func showLocalNotification(with title: String, subtitle: String?) async {
let content = UNMutableNotificationContent()
content.title = title
if let subtitle {
Expand All @@ -78,44 +74,39 @@ class NotificationManager: NSObject, NotificationManagerProtocol {
let request = UNNotificationRequest(identifier: ProcessInfo.processInfo.globallyUniqueString,
content: content,
trigger: nil)
notificationCenter.add(request) { error in
if let error {
MXLog.debug("[NotificationManager] show local notification failed: \(error)")
} else {
MXLog.debug("[NotificationManager] show local notification succeeded")
}
do {
try await notificationCenter.add(request)
MXLog.debug("[NotificationManager] show local notification succeeded")
} catch {
MXLog.debug("[NotificationManager] show local notification failed: \(error)")
}
}

private func setPusher(with deviceToken: Data,
clientProxy: ClientProxyProtocol,
completion: ((Bool) -> Void)?) {
Task {
do {
try await clientProxy.setPusher(pushkey: deviceToken.base64EncodedString(),
kind: .http,
appId: ServiceLocator.shared.settings.pusherAppId,
appDisplayName: "\(InfoPlistReader.target.bundleDisplayName) (iOS)",
deviceDisplayName: UIDevice.current.name,
profileTag: pusherProfileTag(),
lang: Bundle.preferredLanguages.first ?? "en",
url: ServiceLocator.shared.settings.pushGatewayBaseURL.absoluteString,
format: .eventIdOnly,
defaultPayload: [
"aps": [
"mutable-content": 1,
"alert": [
"loc-key": "Notification",
"loc-args": []
]

private func setPusher(with deviceToken: Data, clientProxy: ClientProxyProtocol) async -> Bool {
do {
try await clientProxy.setPusher(pushkey: deviceToken.base64EncodedString(),
kind: .http,
appId: ServiceLocator.shared.settings.pusherAppId,
appDisplayName: "\(InfoPlistReader.target.bundleDisplayName) (iOS)",
deviceDisplayName: UIDevice.current.name,
profileTag: pusherProfileTag(),
lang: Bundle.preferredLanguages.first ?? "en",
url: ServiceLocator.shared.settings.pushGatewayBaseURL.absoluteString,
format: .eventIdOnly,
defaultPayload: [
"aps": [
"mutable-content": 1,
"alert": [
"loc-key": "Notification",
"loc-args": []
]
])
MXLog.debug("[NotificationManager] set pusher succeeded")
completion?(true)
} catch {
MXLog.debug("[NotificationManager] set pusher failed: \(error)")
completion?(false)
}
]
])
MXLog.debug("[NotificationManager] set pusher succeeded")
return true
} catch {
MXLog.debug("[NotificationManager] set pusher failed: \(error)")
return false
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ protocol NotificationManagerProtocol {
var delegate: NotificationManagerDelegate? { get set }

func start()
func register(with deviceToken: Data, completion: ((Bool) -> Void)?)
func register(with deviceToken: Data) async -> Bool
func registrationFailed(with error: Error)
func showLocalNotification(with title: String, subtitle: String?)
func showLocalNotification(with title: String, subtitle: String?) async
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import UserNotifications

protocol UserNotificationCenterProtocol: AnyObject {
var delegate: UNUserNotificationCenterDelegate? { get set }
func add(_ request: UNNotificationRequest, withCompletionHandler completionHandler: ((Error?) -> Void)?)
func requestAuthorization(options: UNAuthorizationOptions, completionHandler: @escaping (Bool, Error?) -> Void)
func add(_ request: UNNotificationRequest) async throws
func requestAuthorization(options: UNAuthorizationOptions) async throws -> Bool
func setNotificationCategories(_ categories: Set<UNNotificationCategory>)
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,48 +33,29 @@ final class NotificationManagerTests: XCTestCase {
sut = NotificationManager(clientProxy: clientProxySpy, notificationCenter: notificationCenter)
}

func test_whenRegistered_pusherIsCalled() throws {
let expectation = expectation(description: "Callback happened")
sut.register(with: Data(), completion: { _ in
expectation.fulfill()
})
waitForExpectations(timeout: 0.5)
func test_whenRegistered_pusherIsCalled() async {
_ = await sut.register(with: Data())
XCTAssertTrue(clientProxySpy.setPusherCalled)
}

func test_whenRegisteredSuccess_completionSuccessIsCalled() throws {
let expectation = expectation(description: "Callback happened")
var success = false
sut.register(with: Data(), completion: { s in
success = s
expectation.fulfill()
})
waitForExpectations(timeout: 0.5)
func test_whenRegisteredSuccess_completionSuccessIsCalled() async throws {
let success = await sut.register(with: Data())
XCTAssertTrue(success)
}

func test_whenRegisteredAndPusherThrowsError_completionFalseIsCalled() throws {
let expectation = expectation(description: "Callback happened")
var success = true
func test_whenRegisteredAndPusherThrowsError_completionFalseIsCalled() async throws {
enum TestError: Error {
case someError
}
clientProxySpy.setPusherErrorToThrow = TestError.someError
sut.register(with: Data(), completion: { s in
success = s
expectation.fulfill()
})
waitForExpectations(timeout: 0.5)
let success = await sut.register(with: Data())
XCTAssertFalse(success)
}

func test_whenRegistered_pusherIsCalledWithCorrectValues() throws {
let expectation = expectation(description: "Callback happened")
@MainActor
func test_whenRegistered_pusherIsCalledWithCorrectValues() async throws {
let pushkeyData = Data("1234".utf8)
sut.register(with: pushkeyData, completion: { _ in
expectation.fulfill()
})
waitForExpectations(timeout: 0.5)
_ = await sut.register(with: pushkeyData)
XCTAssertEqual(clientProxySpy.setPusherPushkey, pushkeyData.base64EncodedString())
XCTAssertEqual(clientProxySpy.setPusherAppId, settings?.pusherAppId)
XCTAssertEqual(clientProxySpy.setPusherKind, .http)
Expand All @@ -98,28 +79,20 @@ final class NotificationManagerTests: XCTestCase {
XCTAssertTrue(actualPayload.isEqual(to: defaultPayload))
}

func test_whenRegisteredAndPusherTagNotSetInSettings_tagGeneratedAndSavedInSettings() throws {
let expectation = expectation(description: "Callback happened")
func test_whenRegisteredAndPusherTagNotSetInSettings_tagGeneratedAndSavedInSettings() async throws {
settings?.pusherProfileTag = nil
sut.register(with: Data(), completion: { _ in
expectation.fulfill()
})
waitForExpectations(timeout: 0.5)
_ = await sut.register(with: Data())
XCTAssertNotNil(settings?.pusherProfileTag)
}

func test_whenRegisteredAndPusherTagIsSetInSettings_tagNotGenerated() throws {
let expectation = expectation(description: "Callback happened")
func test_whenRegisteredAndPusherTagIsSetInSettings_tagNotGenerated() async throws {
settings?.pusherProfileTag = "12345"
sut.register(with: Data(), completion: { _ in
expectation.fulfill()
})
waitForExpectations(timeout: 0.5)
_ = await sut.register(with: Data())
XCTAssertEqual(settings?.pusherProfileTag, "12345")
}

func test_whenShowLocalNotification_notificationRequestGetsAdded() throws {
sut.showLocalNotification(with: "Title", subtitle: "Subtitle")
func test_whenShowLocalNotification_notificationRequestGetsAdded() async throws {
await sut.showLocalNotification(with: "Title", subtitle: "Subtitle")
let request = try XCTUnwrap(notificationCenter.addRequest)
XCTAssertEqual(request.content.title, "Title")
XCTAssertEqual(request.content.subtitle, "Subtitle")
Expand All @@ -143,21 +116,18 @@ final class NotificationManagerTests: XCTestCase {
XCTAssertTrue(delegate.isEqual(sut))
}

func test_whenStart_requestAuthorizationCalledWithCorrectParams() throws {
func test_whenStart_requestAuthorizationCalledWithCorrectParams() async throws {
sut.start()
await Task.yield()
XCTAssertEqual(notificationCenter.requestAuthorizationOptions, [.alert, .sound, .badge])
}

func test_whenStartAndAuthorizationGranted_delegateCalled() throws {
func test_whenStartAndAuthorizationGranted_delegateCalled() async throws {
authorizationStatusWasGranted = false
notificationCenter.requestAuthorizationGrantedReturnValue = true
sut.delegate = self
sut.start()
let expectation = expectation(description: "Wait for main thread")
DispatchQueue.main.async {
expectation.fulfill()
}
waitForExpectations(timeout: 1, handler: nil)
try await Task.sleep(for: .milliseconds(100))
pixlwave marked this conversation as resolved.
Show resolved Hide resolved
XCTAssertTrue(authorizationStatusWasGranted)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,15 @@ class UserNotificationCenterSpy: UserNotificationCenterProtocol {
weak var delegate: UNUserNotificationCenterDelegate?

var addRequest: UNNotificationRequest?
func add(_ request: UNNotificationRequest, withCompletionHandler completionHandler: ((Error?) -> Void)?) {
func add(_ request: UNNotificationRequest) async throws {
addRequest = request
completionHandler?(nil)
}

var requestAuthorizationOptions: UNAuthorizationOptions?
var requestAuthorizationGrantedReturnValue = false
func requestAuthorization(options: UNAuthorizationOptions, completionHandler: @escaping (Bool, Error?) -> Void) {
func requestAuthorization(options: UNAuthorizationOptions) async throws -> Bool {
requestAuthorizationOptions = options
completionHandler(requestAuthorizationGrantedReturnValue, nil)
return requestAuthorizationGrantedReturnValue
}

var notificationCategoriesValue: Set<UNNotificationCategory>?
Expand Down
1 change: 1 addition & 0 deletions changelog.d/pr-407.change
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Notification Manager: Replace completion handlers with async/await.