Skip to content

Commit

Permalink
Simplification + Implement OIDC (#185)
Browse files Browse the repository at this point in the history
* Introduce `WultraMobileToken` object which directly instantiates services

* Replace `WMT` throwing init for failable init

* Simplify WMT class, revert init? to throwing init, code cleanup

* WIP: docs

* Fix access modifiers

* Draft WMTOperations generics

* Improve Operations service

* Update docs

* Add migration guide to readme

* Revert WMTOperations generics

* Change file copyright header for the SDK correct one

* Remove unnecessary debug print()s

* Remove unused generics in operation expoints

* Implement WMTOidcService

* Add OidcTests

* Move WultraMobileToken.swift to Common and update podspec to include all files

* Fix circular dependency of sub.dependencies in .podspec

* Add Oidc to subspec

* Fix Push `pushNotificationsRegisteredOnServer` visibility

* Change Oidc service name to be consistent with other services and other refactoring

* Remove incorrectly commited files

* Fix refactor

* Remove config dependent part of the test

* Update after mtoken implementation testing

* Improved WultraMobileToken class

* Implement remarks

* Remarks/rename to capital OIDC

* Rename files

* Rename prepareOIDCAuthorizationData method to omit redundant OIDC

---------

Co-authored-by: Jan Kobersky <kober32@gmail.com>
  • Loading branch information
Hopsaheysa and kober32 authored Feb 14, 2025
1 parent 5b0da64 commit 31adc45
Show file tree
Hide file tree
Showing 40 changed files with 2,153 additions and 799 deletions.
3 changes: 0 additions & 3 deletions Cartfile.resolved

This file was deleted.

31 changes: 5 additions & 26 deletions Deploy/WultraMobileTokenSDK.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,11 @@ Pod::Spec.new do |s|
s.swift_version = '5.9'
s.ios.deployment_target = '12.0'

# Sources
s.default_subspec = 'Operations'
# Source files
s.source_files = 'WultraMobileTokenSDK/**/*.{swift}'

# 'Common' subspec
s.subspec 'Common' do |sub|
sub.source_files = 'WultraMobileTokenSDK/Common/**/*.swift'
sub.dependency 'PowerAuth2', '~> 1.9.0'
sub.dependency 'WultraPowerAuthNetworking', '~> 1.5.0'
end

# 'Operations' subspec
s.subspec 'Operations' do |sub|
sub.source_files = 'WultraMobileTokenSDK/Operations/**/*.swift'
sub.dependency 'WultraMobileTokenSDK/Common'
end

# 'Push' subspec
s.subspec 'Push' do |sub|
sub.source_files = 'WultraMobileTokenSDK/Push/**/*.swift'
sub.dependency 'WultraMobileTokenSDK/Common'
end

# 'Inbox' subspec
s.subspec 'Inbox' do |sub|
sub.source_files = 'WultraMobileTokenSDK/Inbox/**/*.swift'
sub.dependency 'WultraMobileTokenSDK/Common'
end
# Dependencies
s.dependency 'PowerAuth2', '~> 1.9.3'
s.dependency 'WultraPowerAuthNetworking', '~> 1.5.0'

end
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ let package = Package(
.library(name: "WultraMobileTokenSDK", targets: ["WultraMobileTokenSDK"])
],
dependencies: [
.package(url: "https://github.com/wultra/powerauth-mobile-sdk-spm.git", .upToNextMinor(from: "1.9.0")),
.package(url: "https://github.com/wultra/powerauth-mobile-sdk-spm.git", .upToNextMinor(from: "1.9.2")),
.package(url: "https://github.com/wultra/networking-apple.git", .upToNextMinor(from: "1.5.0"))
],
targets: [
Expand Down
31 changes: 5 additions & 26 deletions WultraMobileTokenSDK.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,11 @@ Pod::Spec.new do |s|
s.swift_version = '5.9'
s.ios.deployment_target = '12.0'

# Sources
s.default_subspec = 'Operations'
# Source files
s.source_files = 'WultraMobileTokenSDK/**/*.{swift}'

# 'Common' subspec
s.subspec 'Common' do |sub|
sub.source_files = 'WultraMobileTokenSDK/Common/**/*.swift'
sub.dependency 'PowerAuth2', '~> 1.9.0'
sub.dependency 'WultraPowerAuthNetworking', '~> 1.5.0'
end

# 'Operations' subspec
s.subspec 'Operations' do |sub|
sub.source_files = 'WultraMobileTokenSDK/Operations/**/*.swift'
sub.dependency 'WultraMobileTokenSDK/Common'
end

# 'Push' subspec
s.subspec 'Push' do |sub|
sub.source_files = 'WultraMobileTokenSDK/Push/**/*.swift'
sub.dependency 'WultraMobileTokenSDK/Common'
end

# 'Inbox' subspec
s.subspec 'Inbox' do |sub|
sub.source_files = 'WultraMobileTokenSDK/Inbox/**/*.swift'
sub.dependency 'WultraMobileTokenSDK/Common'
end
# Dependencies
s.dependency 'PowerAuth2', '~> 1.9.3'
s.dependency 'WultraPowerAuthNetworking', '~> 1.5.0'

end
141 changes: 117 additions & 24 deletions WultraMobileTokenSDK.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

41 changes: 41 additions & 0 deletions WultraMobileTokenSDK/Common/WMTLazy.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// Copyright 2025 Wultra s.r.o.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions
// and limitations under the License.
//

/// Lazy loaded instance with possibility of "peek".
class WMTLazy<T> {

private var instance: T?
private let factory: () -> T
private let lock = WMTLock()

var lazy: T {
return instance ?? lock.synchronized {
if let instance = instance {
return instance
}
self.instance = factory()
return self.instance!
}
}

var optional: T? {
return instance
}

init(_ factory: @autoclosure @escaping () -> T) {
self.factory = factory
}
}
5 changes: 2 additions & 3 deletions WultraMobileTokenSDK/Common/WMTService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@
//

import Foundation
import PowerAuth2
import WultraPowerAuthNetworking

protocol WMTService {
var powerAuth: PowerAuthSDK { get }
var networking: WPNNetworkingService { get }
}

extension WMTService {
Expand All @@ -29,7 +28,7 @@ extension WMTService {
/// - Parameter completion: Completion
/// - Returns: True if the activation is valid
func validateActivation<T>(_ completion: @escaping (Result<T, WMTError>) -> Void) -> Bool {
guard powerAuth.hasValidActivation() else {
guard networking.powerAuth.hasValidActivation() else {
DispatchQueue.main.async {
completion(.failure(WMTError(reason: .missingActivation)))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,45 @@
//

import Foundation
import WultraPowerAuthNetworking

/// Protocol for service that communicates with Inbox API that is managing user's inbox.
public protocol WMTInbox: AnyObject {
/// Service that communicates with Inbox API that is managing user's inbox.
public class WMTInbox: WMTService {

// Dependencies
let networking: WPNNetworkingService

/// Accept language for the outgoing requests headers.
/// Default value is "en".
/// Changing this value updates the accept language of the underlying networking service.
///
/// Standard RFC "Accept-Language" https://tools.ietf.org/html/rfc7231#section-5.3.5
/// Response texts are based on this setting. For example when "de" is set, server
/// will return operation texts in german (if available).
var acceptLanguage: String { get set }
public var acceptLanguage: String {
get { networking.acceptLanguage }
set { networking.acceptLanguage = newValue }
}

public init(networking: WPNNetworkingService) {
self.networking = networking
}

/// Get number of unread messages in the inbox.
///
/// - Parameters:
/// - completion: Result callback. This completion is always called on the main thread.
/// - Returns: Operation object for its state observation.
@discardableResult
func getUnreadCount(completion: @escaping(Result<WMTInboxCount, WMTError>) -> Void) -> Operation?
public func getUnreadCount(completion: @escaping (Result<WMTInboxCount, WMTError>) -> Void) -> Operation? {
guard validateActivation(completion) else {
return nil
}

return networking.post(data: .init(), signedWith: .possession(), to: WMTInboxEndpoints.Count.endpoint) { response, error in
self.processResult(response: response, error: error, completion: completion)
}
}

/// Paged list of messages in the inbox. You can use also `getAllMessages()` method to fetch all messages.
///
Expand All @@ -44,49 +64,77 @@ public protocol WMTInbox: AnyObject {
/// - completion: Result callback. This completion is always called on the main thread.
/// - Returns: Operation object for its state observation.
@discardableResult
func getMessageList(pageNumber: Int, pageSize: Int, onlyUnread: Bool, completion: @escaping(Result<[WMTInboxMessage], WMTError>) -> Void) -> Operation?
public func getMessageList(pageNumber: Int, pageSize: Int, onlyUnread: Bool, completion: @escaping (Result<[WMTInboxMessage], WMTError>) -> Void) -> Operation? {
guard validateActivation(completion) else {
return nil
}
let data = WMTInboxGetList(page: pageNumber, size: pageSize, onlyUnread: onlyUnread)
return networking.post(data: .init(data), signedWith: .possession(), to: WMTInboxEndpoints.MessageList.endpoint) { response, error in
self.processResult(response: response, error: error, completion: completion)
}
}

/// Get message detail in the inbox.
/// Get all messages in the inbox. The function will issue multiple HTTP requests until the list is not complete.
///
/// - Parameters:
/// - messageId: Message ID.
/// - pageSize: How many messages should be fetched at once. The default value is 100.
/// - messageLimit: Maximum number of messages to be retrieved. Use 0 to set no limit. The default value is 1000.
/// - onlyUnread: If `true` then only unread messages will be returned. The default value is `false`.
/// - completion: Result callback. This completion is always called on the main thread.
/// - Returns: Operation object for its state observation.
@discardableResult
func getMessageDetail(messageId: String, completion: @escaping(Result<WMTInboxMessageDetail, WMTError>) -> Void) -> Operation?
public func getAllMessages(pageSize: Int = 100, messageLimit: Int = 1000, onlyUnread: Bool = false, completion: @escaping(Result<[WMTInboxMessage], WMTError>) -> Void) -> WMTCancellable? {
let operation = FetchOperation(pageSize: pageSize, onlyUnread: onlyUnread, messageLimit: messageLimit, completion: completion)
return fetchPartialList(fetchOperation: operation) == nil ? nil : operation
}

/// Mark the message with the given identifier as read.
/// Get message detail in the inbox.
///
/// - Parameters:
/// - messageId: Message identifier.
/// - messageId: Message ID.
/// - completion: Result callback. This completion is always called on the main thread.
/// - Returns: Operation object for its state observation.
@discardableResult
func markRead(messageId: String, completion: @escaping(Result<Void, WMTError>) -> Void) -> Operation?
public func getMessageDetail(messageId: String, completion: @escaping (Result<WMTInboxMessageDetail, WMTError>) -> Void) -> Operation? {
guard validateActivation(completion) else {
return nil
}
let data = WMTInboxGetMessageDetail(id: messageId)
return networking.post(data: .init(data), signedWith: .possession(), to: WMTInboxEndpoints.MessageDetail.endpoint) { response, error in
self.processResult(response: response, error: error, completion: completion)
}
}

/// Marks all unread messages in the inbox as read.
/// Mark the message with the given identifier as read.
///
/// - Parameters:
/// - messageId: Message identifier.
/// - completion: Result callback. This completion is always called on the main thread.
/// - Returns: Operation object for its state observation.
@discardableResult
func markAllRead(completion: @escaping(Result<Void, WMTError>) -> Void) -> Operation?
}

public extension WMTInbox {
public func markRead(messageId: String, completion: @escaping (Result<Void, WMTError>) -> Void) -> Operation? {
guard validateActivation(completion) else {
return nil
}
let data = WMTInboxSetMessageRead(id: messageId)
return networking.post(data: .init(data), signedWith: .possession(), to: WMTInboxEndpoints.MessageRead.endpoint) { response, error in
self.processResult(response: response, error: error, completion: completion)
}
}

/// Get all messages in the inbox. The function will issue multiple HTTP requests until the list is not complete.
/// Marks all unread messages in the inbox as read.
///
/// - Parameters:
/// - pageSize: How many messages should be fetched at once. The default value is 100.
/// - messageLimit: Maximum number of messages to be retrieved. Use 0 to set no limit. The default value is 1000.
/// - onlyUnread: If `true` then only unread messages will be returned. The default value is `false`.
/// - completion: Result callback. This completion is always called on the main thread.
/// - Returns: Operation object for its state observation.
@discardableResult
func getAllMessages(pageSize: Int = 100, messageLimit: Int = 1000, onlyUnread: Bool = false, completion: @escaping(Result<[WMTInboxMessage], WMTError>) -> Void) -> WMTCancellable? {
let operation = FetchOperation(pageSize: pageSize, onlyUnread: onlyUnread, messageLimit: messageLimit, completion: completion)
return fetchPartialList(fetchOperation: operation) == nil ? nil : operation
public func markAllRead(completion: @escaping (Result<Void, WMTError>) -> Void) -> Operation? {
guard validateActivation(completion) else {
return nil
}
return networking.post(data: .init(), signedWith: .possession(), to: WMTInboxEndpoints.MessageReadAll.endpoint) { response, error in
self.processResult(response: response, error: error, completion: completion)
}
}

/// Fetch partial list from the server.
Expand All @@ -109,7 +157,6 @@ public extension WMTInbox {
} else {
// We should fetch the next batch of messages.
fetchOperation.nestedOperation = self.fetchPartialList(fetchOperation: fetchOperation)

}
case .failure:
fetchOperation.complete(result)
Expand Down
Loading

0 comments on commit 31adc45

Please sign in to comment.