diff --git a/Deploy/WultraMobileTokenSDK.podspec b/Deploy/WultraMobileTokenSDK.podspec index e5c076d..6466ecd 100644 --- a/Deploy/WultraMobileTokenSDK.podspec +++ b/Deploy/WultraMobileTokenSDK.podspec @@ -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 diff --git a/WultraMobileTokenSDK.podspec b/WultraMobileTokenSDK.podspec index 759d713..ad950ed 100644 --- a/WultraMobileTokenSDK.podspec +++ b/WultraMobileTokenSDK.podspec @@ -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 diff --git a/WultraMobileTokenSDK.xcodeproj/project.pbxproj b/WultraMobileTokenSDK.xcodeproj/project.pbxproj index d78a9f3..6aff83c 100644 --- a/WultraMobileTokenSDK.xcodeproj/project.pbxproj +++ b/WultraMobileTokenSDK.xcodeproj/project.pbxproj @@ -18,25 +18,22 @@ DC3D0B372480F3C7000DC4D9 /* WMTOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3D0B362480F3C7000DC4D9 /* WMTOperation.swift */; }; DC3D0B392480F886000DC4D9 /* WMTLocalOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC3D0B382480F886000DC4D9 /* WMTLocalOperation.swift */; }; DC488031292282C900DB844B /* WMTService.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC488030292282C900DB844B /* WMTService.swift */; }; - DC48803D292282FF00DB844B /* WMTInbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC488035292282FF00DB844B /* WMTInbox.swift */; }; DC48803E292282FF00DB844B /* WMTInboxMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC488037292282FF00DB844B /* WMTInboxMessage.swift */; }; DC48803F292282FF00DB844B /* WMTInboxMessageDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC488038292282FF00DB844B /* WMTInboxMessageDetail.swift */; }; DC488040292282FF00DB844B /* WMTInboxCount.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC488039292282FF00DB844B /* WMTInboxCount.swift */; }; DC488041292282FF00DB844B /* WMTInboxEndpoints.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC48803B292282FF00DB844B /* WMTInboxEndpoints.swift */; }; - DC488042292282FF00DB844B /* WMTInboxImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC48803C292282FF00DB844B /* WMTInboxImpl.swift */; }; + DC488042292282FF00DB844B /* WMTInbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC48803C292282FF00DB844B /* WMTInbox.swift */; }; DC5F6DE824E14FA100D351D3 /* Configs in Resources */ = {isa = PBXBuildFile; fileRef = DC5F6DE724E14FA100D351D3 /* Configs */; }; DC616236248508F8000DED17 /* QROperationParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC616235248508F8000DED17 /* QROperationParserTests.swift */; }; DC616238248508F8000DED17 /* WultraMobileTokenSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DCC5CC9A2449EE21004679AC /* WultraMobileTokenSDK.framework */; }; DC61624224852B6D000DED17 /* NetworkingObjectsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC61624124852B6D000DED17 /* NetworkingObjectsTests.swift */; }; + DC68CFD02D538FC500D7468C /* WMTLazy.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC68CFCF2D538FC500D7468C /* WMTLazy.swift */; }; DC6E52D6259C964600FC25BE /* WMTOperationExpirationWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC6E52D5259C964600FC25BE /* WMTOperationExpirationWatcher.swift */; }; DC6EDB7925A49ED900A229E4 /* OperationExpirationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC6EDB7825A49ED900A229E4 /* OperationExpirationTests.swift */; }; DC81D1C9244F38DB00F80CD6 /* WMTPushEndpoints.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC81D1C8244F38DB00F80CD6 /* WMTPushEndpoints.swift */; }; - DC81D1CB244F451E00F80CD6 /* WMTPushImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC81D1CA244F451E00F80CD6 /* WMTPushImpl.swift */; }; - DC81D1CD244F640600F80CD6 /* WMTPush.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC81D1CC244F640600F80CD6 /* WMTPush.swift */; }; DC848E2126B16C2400EBFA6C /* PowerAuth2.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC848E2026B16C2400EBFA6C /* PowerAuth2.xcframework */; }; DC848E2226B1782900EBFA6C /* PowerAuth2.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC848E2026B16C2400EBFA6C /* PowerAuth2.xcframework */; }; DC848E2426B1782900EBFA6C /* PowerAuthCore.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC848E2326B1782900EBFA6C /* PowerAuthCore.xcframework */; }; - DC8CB202244DCBE2009DDAA3 /* WMTOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC8CB201244DCBE2009DDAA3 /* WMTOperations.swift */; }; DC8CB206244DD007009DDAA3 /* WMTAllowedOperationSignature.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC8CB205244DD007009DDAA3 /* WMTAllowedOperationSignature.swift */; }; DC9511F926EA02C100FF40AD /* WPNIntegration.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC9511F826EA02C100FF40AD /* WPNIntegration.swift */; }; DC9511FB26EA02ED00FF40AD /* WultraPowerAuthNetworking.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC9511FA26EA02ED00FF40AD /* WultraPowerAuthNetworking.xcframework */; }; @@ -46,7 +43,7 @@ DCAB7BCA24580BAC0006989D /* WMTQROperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCAB7BC924580BAC0006989D /* WMTQROperation.swift */; }; DCC3420424E3DB310045D27D /* WMTPushParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC3420324E3DB310045D27D /* WMTPushParser.swift */; }; DCC5CC9F2449EE21004679AC /* MobileTokenSDK.h in Headers */ = {isa = PBXBuildFile; fileRef = DCC5CC9D2449EE21004679AC /* MobileTokenSDK.h */; settings = {ATTRIBUTES = (Public, ); }; }; - DCC5CCAC2449F765004679AC /* WMTOperationsImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC5CCAB2449F765004679AC /* WMTOperationsImpl.swift */; }; + DCC5CCAC2449F765004679AC /* WMTOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC5CCAB2449F765004679AC /* WMTOperations.swift */; }; DCC5CCAE2449F7AC004679AC /* WMTUserOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC5CCAD2449F7AC004679AC /* WMTUserOperation.swift */; }; DCC5CCB12449F81C004679AC /* WMTOperationFormData.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC5CCB02449F81C004679AC /* WMTOperationFormData.swift */; }; DCC5CCB32449F8CD004679AC /* WMTOperationAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = DCC5CCB22449F8CD004679AC /* WMTOperationAttribute.swift */; }; @@ -72,9 +69,14 @@ EA6DDF1C29F807230011E234 /* OperationUIDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA6DDF1B29F807230011E234 /* OperationUIDataTests.swift */; }; EA74F7B32C2561BB004340B9 /* WMTResultTexts.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA74F7B22C2561BB004340B9 /* WMTResultTexts.swift */; }; EA7A6E582B0E639800C1D4F4 /* WMTOperationDetailRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7A6E572B0E639800C1D4F4 /* WMTOperationDetailRequest.swift */; }; + EA7C94572D07111B000A3EFA /* WultraMobileToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA7C94562D07111B000A3EFA /* WultraMobileToken.swift */; }; EA9CE2BE2AEAA9FD00FE4E35 /* WMTProximityCheck.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9CE2BD2AEAA9FD00FE4E35 /* WMTProximityCheck.swift */; }; EA9CE2C22AEBDB0D00FE4E35 /* WMTPACUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA9CE2C12AEBDB0D00FE4E35 /* WMTPACUtils.swift */; }; + EAA3DD3B2D14667F00C06DC2 /* WMTPush.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA3DD3A2D14667F00C06DC2 /* WMTPush.swift */; }; EAB7054A2AF1161500756AC2 /* PACParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB705492AF1161500756AC2 /* PACParserTests.swift */; }; + EAB7AA7A2D2BFA2000C50FDC /* WMTHexadecimalString.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB7AA792D2BFA1900C50FDC /* WMTHexadecimalString.swift */; }; + EAB7AA7C2D2BFA8F00C50FDC /* WMTPushErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB7AA7B2D2BFA8900C50FDC /* WMTPushErrors.swift */; }; + EAB7AA7E2D2C00D100C50FDC /* WMTOperationsErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB7AA7D2D2C00CA00C50FDC /* WMTOperationsErrors.swift */; }; EACAF7B02A126B7D0021CA54 /* WMTJsonValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = EACAF7AF2A126B7D0021CA54 /* WMTJsonValue.swift */; }; /* End PBXBuildFile section */ @@ -100,25 +102,22 @@ DC3D0B362480F3C7000DC4D9 /* WMTOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTOperation.swift; sourceTree = ""; }; DC3D0B382480F886000DC4D9 /* WMTLocalOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTLocalOperation.swift; sourceTree = ""; }; DC488030292282C900DB844B /* WMTService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMTService.swift; sourceTree = ""; }; - DC488035292282FF00DB844B /* WMTInbox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMTInbox.swift; sourceTree = ""; }; DC488037292282FF00DB844B /* WMTInboxMessage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMTInboxMessage.swift; sourceTree = ""; }; DC488038292282FF00DB844B /* WMTInboxMessageDetail.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMTInboxMessageDetail.swift; sourceTree = ""; }; DC488039292282FF00DB844B /* WMTInboxCount.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMTInboxCount.swift; sourceTree = ""; }; DC48803B292282FF00DB844B /* WMTInboxEndpoints.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMTInboxEndpoints.swift; sourceTree = ""; }; - DC48803C292282FF00DB844B /* WMTInboxImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMTInboxImpl.swift; sourceTree = ""; }; + DC48803C292282FF00DB844B /* WMTInbox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMTInbox.swift; sourceTree = ""; }; DC5F6DE724E14FA100D351D3 /* Configs */ = {isa = PBXFileReference; lastKnownFileType = folder; path = Configs; sourceTree = ""; }; DC616233248508F8000DED17 /* WultraMobileTokenSDKTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WultraMobileTokenSDKTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DC616235248508F8000DED17 /* QROperationParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QROperationParserTests.swift; sourceTree = ""; }; DC616237248508F8000DED17 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; DC61624124852B6D000DED17 /* NetworkingObjectsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkingObjectsTests.swift; sourceTree = ""; }; + DC68CFCF2D538FC500D7468C /* WMTLazy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTLazy.swift; sourceTree = ""; }; DC6E52D5259C964600FC25BE /* WMTOperationExpirationWatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTOperationExpirationWatcher.swift; sourceTree = ""; }; DC6EDB7825A49ED900A229E4 /* OperationExpirationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationExpirationTests.swift; sourceTree = ""; }; DC81D1C8244F38DB00F80CD6 /* WMTPushEndpoints.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTPushEndpoints.swift; sourceTree = ""; }; - DC81D1CA244F451E00F80CD6 /* WMTPushImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTPushImpl.swift; sourceTree = ""; }; - DC81D1CC244F640600F80CD6 /* WMTPush.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTPush.swift; sourceTree = ""; }; DC848E2026B16C2400EBFA6C /* PowerAuth2.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = PowerAuth2.xcframework; path = Carthage/Build/PowerAuth2.xcframework; sourceTree = ""; }; DC848E2326B1782900EBFA6C /* PowerAuthCore.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = PowerAuthCore.xcframework; path = Carthage/Build/PowerAuthCore.xcframework; sourceTree = ""; }; - DC8CB201244DCBE2009DDAA3 /* WMTOperations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTOperations.swift; sourceTree = ""; }; DC8CB205244DD007009DDAA3 /* WMTAllowedOperationSignature.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTAllowedOperationSignature.swift; sourceTree = ""; }; DC9511F826EA02C100FF40AD /* WPNIntegration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WPNIntegration.swift; sourceTree = ""; }; DC9511FA26EA02ED00FF40AD /* WultraPowerAuthNetworking.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = WultraPowerAuthNetworking.xcframework; path = Carthage/Build/WultraPowerAuthNetworking.xcframework; sourceTree = ""; }; @@ -133,7 +132,7 @@ DCC5CCA82449F2B1004679AC /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; DCC5CCA92449F2E1004679AC /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; DCC5CCAA2449F2ED004679AC /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - DCC5CCAB2449F765004679AC /* WMTOperationsImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTOperationsImpl.swift; sourceTree = ""; }; + DCC5CCAB2449F765004679AC /* WMTOperations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTOperations.swift; sourceTree = ""; }; DCC5CCAD2449F7AC004679AC /* WMTUserOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTUserOperation.swift; sourceTree = ""; }; DCC5CCB02449F81C004679AC /* WMTOperationFormData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTOperationFormData.swift; sourceTree = ""; }; DCC5CCB22449F8CD004679AC /* WMTOperationAttribute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTOperationAttribute.swift; sourceTree = ""; }; @@ -159,9 +158,14 @@ EA6DDF1B29F807230011E234 /* OperationUIDataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationUIDataTests.swift; sourceTree = ""; }; EA74F7B22C2561BB004340B9 /* WMTResultTexts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTResultTexts.swift; sourceTree = ""; }; EA7A6E572B0E639800C1D4F4 /* WMTOperationDetailRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTOperationDetailRequest.swift; sourceTree = ""; }; + EA7C94562D07111B000A3EFA /* WultraMobileToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WultraMobileToken.swift; sourceTree = ""; }; EA9CE2BD2AEAA9FD00FE4E35 /* WMTProximityCheck.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMTProximityCheck.swift; sourceTree = ""; }; EA9CE2C12AEBDB0D00FE4E35 /* WMTPACUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WMTPACUtils.swift; sourceTree = ""; }; + EAA3DD3A2D14667F00C06DC2 /* WMTPush.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTPush.swift; sourceTree = ""; }; EAB705492AF1161500756AC2 /* PACParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PACParserTests.swift; sourceTree = ""; }; + EAB7AA792D2BFA1900C50FDC /* WMTHexadecimalString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTHexadecimalString.swift; sourceTree = ""; }; + EAB7AA7B2D2BFA8900C50FDC /* WMTPushErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTPushErrors.swift; sourceTree = ""; }; + EAB7AA7D2D2C00CA00C50FDC /* WMTOperationsErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTOperationsErrors.swift; sourceTree = ""; }; EACAF7AF2A126B7D0021CA54 /* WMTJsonValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WMTJsonValue.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -239,7 +243,6 @@ DC488034292282FF00DB844B /* Inbox */ = { isa = PBXGroup; children = ( - DC488035292282FF00DB844B /* WMTInbox.swift */, DC488036292282FF00DB844B /* Model */, DC48803A292282FF00DB844B /* Service */, ); @@ -262,7 +265,7 @@ isa = PBXGroup; children = ( DC48803B292282FF00DB844B /* WMTInboxEndpoints.swift */, - DC48803C292282FF00DB844B /* WMTInboxImpl.swift */, + DC48803C292282FF00DB844B /* WMTInbox.swift */, ); path = Service; sourceTree = ""; @@ -297,6 +300,7 @@ DC6E52D4259C959900FC25BE /* Utils */ = { isa = PBXGroup; children = ( + EAB7AA7D2D2C00CA00C50FDC /* WMTOperationsErrors.swift */, EA9CE2C12AEBDB0D00FE4E35 /* WMTPACUtils.swift */, DC6E52D5259C964600FC25BE /* WMTOperationExpirationWatcher.swift */, EACAF7AF2A126B7D0021CA54 /* WMTJsonValue.swift */, @@ -307,7 +311,7 @@ DC76D01A2452E92D009F2DFC /* Service */ = { isa = PBXGroup; children = ( - DCC5CCAB2449F765004679AC /* WMTOperationsImpl.swift */, + DCC5CCAB2449F765004679AC /* WMTOperations.swift */, DCC5CCD3244DBA1C004679AC /* WMTOperationEndpoints.swift */, ); path = Service; @@ -316,7 +320,7 @@ DC76D01B24531413009F2DFC /* Service */ = { isa = PBXGroup; children = ( - DC81D1CA244F451E00F80CD6 /* WMTPushImpl.swift */, + EAA3DD3A2D14667F00C06DC2 /* WMTPush.swift */, DC81D1C8244F38DB00F80CD6 /* WMTPushEndpoints.swift */, ); path = Service; @@ -325,7 +329,6 @@ DC81D1C5244F302300F80CD6 /* Operations */ = { isa = PBXGroup; children = ( - DC8CB201244DCBE2009DDAA3 /* WMTOperations.swift */, DCAB7BC624580B2B0006989D /* QR */, DC76D01A2452E92D009F2DFC /* Service */, DCC5CCAF2449F7FA004679AC /* Model */, @@ -337,7 +340,6 @@ DC81D1C6244F357900F80CD6 /* Push */ = { isa = PBXGroup; children = ( - DC81D1CC244F640600F80CD6 /* WMTPush.swift */, DCC3420324E3DB310045D27D /* WMTPushParser.swift */, DC76D01B24531413009F2DFC /* Service */, DC81D1C7244F382800F80CD6 /* Model */, @@ -349,6 +351,7 @@ isa = PBXGroup; children = ( DCC5CCD5244DBB7F004679AC /* WMTPushRegistrationData.swift */, + EAB7AA782D2BFA1100C50FDC /* Utils */, ); path = Model; sourceTree = ""; @@ -356,6 +359,7 @@ DC81D1CE24502E0300F80CD6 /* Common */ = { isa = PBXGroup; children = ( + DC68CFCF2D538FC500D7468C /* WMTLazy.swift */, BFEEB20A2937AD700047941D /* WMTCancellable.swift */, DC488030292282C900DB844B /* WMTService.swift */, DCC5CCCD244DB0AD004679AC /* WMTLogger.swift */, @@ -396,6 +400,7 @@ DCC5CC9C2449EE21004679AC /* WultraMobileTokenSDK */ = { isa = PBXGroup; children = ( + EA7C94562D07111B000A3EFA /* WultraMobileToken.swift */, DC81D1CE24502E0300F80CD6 /* Common */, DC81D1C5244F302300F80CD6 /* Operations */, DC81D1C6244F357900F80CD6 /* Push */, @@ -452,6 +457,15 @@ path = Screens; sourceTree = ""; }; + EAB7AA782D2BFA1100C50FDC /* Utils */ = { + isa = PBXGroup; + children = ( + EAB7AA7B2D2BFA8900C50FDC /* WMTPushErrors.swift */, + EAB7AA792D2BFA1900C50FDC /* WMTHexadecimalString.swift */, + ); + path = Utils; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -601,11 +615,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + DC68CFD02D538FC500D7468C /* WMTLazy.swift in Sources */, DC81D1C9244F38DB00F80CD6 /* WMTPushEndpoints.swift in Sources */, DC0268DF29965495000BB9FA /* WMTOperationListResponse.swift in Sources */, - DC8CB202244DCBE2009DDAA3 /* WMTOperations.swift in Sources */, DC48803E292282FF00DB844B /* WMTInboxMessage.swift in Sources */, DCC5CCB52449F8E9004679AC /* WMTOperationAttributeAmount.swift in Sources */, + EAB7AA7E2D2C00D100C50FDC /* WMTOperationsErrors.swift in Sources */, DCC5CCD6244DBB7F004679AC /* WMTPushRegistrationData.swift in Sources */, DC3D0B392480F886000DC4D9 /* WMTLocalOperation.swift in Sources */, DCD8B336246C1BAF00385F02 /* WMTRejectionReason.swift in Sources */, @@ -615,13 +630,13 @@ EA74F7B32C2561BB004340B9 /* WMTResultTexts.swift in Sources */, DC3D0B372480F3C7000DC4D9 /* WMTOperation.swift in Sources */, BFEEB20B2937AD700047941D /* WMTCancellable.swift in Sources */, - DC81D1CD244F640600F80CD6 /* WMTPush.swift in Sources */, DC6E52D6259C964600FC25BE /* WMTOperationExpirationWatcher.swift in Sources */, DCC5CCDA244DBBE2004679AC /* WMTRejectionData.swift in Sources */, DC48803F292282FF00DB844B /* WMTInboxMessageDetail.swift in Sources */, EA6DDF0F29F8036B0011E234 /* WMTPreApprovalScreen.swift in Sources */, DCAB7BC824580B4C0006989D /* WMTQROperationParser.swift in Sources */, EA44366C29F9297100DDEC1C /* WMTPostApprovaScreenRedirect.swift in Sources */, + EAB7AA7A2D2BFA2000C50FDC /* WMTHexadecimalString.swift in Sources */, DCC5CCB12449F81C004679AC /* WMTOperationFormData.swift in Sources */, DCC5CCD4244DBA1C004679AC /* WMTOperationEndpoints.swift in Sources */, DC488031292282C900DB844B /* WMTService.swift in Sources */, @@ -630,14 +645,17 @@ DCC5CCBB2449F952004679AC /* WMTOperationAttributeNote.swift in Sources */, BFEEB2092937A2680047941D /* WMTInboxGetList.swift in Sources */, BFEEB20729379F960047941D /* WMTInboxSetMessageRead.swift in Sources */, + EAA3DD3B2D14667F00C06DC2 /* WMTPush.swift in Sources */, EA44366A29F9294600DDEC1C /* WMTPostApprovaScreenReview.swift in Sources */, EA9CE2C22AEBDB0D00FE4E35 /* WMTPACUtils.swift in Sources */, EA294F3D29F6A07A00A0494E /* WMTOperationUIData.swift in Sources */, DCC5CCB32449F8CD004679AC /* WMTOperationAttribute.swift in Sources */, - DCC5CCAC2449F765004679AC /* WMTOperationsImpl.swift in Sources */, + DCC5CCAC2449F765004679AC /* WMTOperations.swift in Sources */, DC06D01F25AC74E400F2EA69 /* WMTLock.swift in Sources */, + EAB7AA7C2D2BFA8F00C50FDC /* WMTPushErrors.swift in Sources */, DCC5CCCE244DB0AD004679AC /* WMTLogger.swift in Sources */, DCC5CCAE2449F7AC004679AC /* WMTUserOperation.swift in Sources */, + EA7C94572D07111B000A3EFA /* WultraMobileToken.swift in Sources */, DC9511F926EA02C100FF40AD /* WPNIntegration.swift in Sources */, DCC5CCBD2449F965004679AC /* WMTOperationAttributeHeading.swift in Sources */, DC8CB206244DD007009DDAA3 /* WMTAllowedOperationSignature.swift in Sources */, @@ -646,14 +664,12 @@ EACAF7B02A126B7D0021CA54 /* WMTJsonValue.swift in Sources */, DCAB7BCA24580BAC0006989D /* WMTQROperation.swift in Sources */, DCC5CCBF2449F981004679AC /* WMTOperationAttributePartyInfo.swift in Sources */, - DC81D1CB244F451E00F80CD6 /* WMTPushImpl.swift in Sources */, EA9CE2BE2AEAA9FD00FE4E35 /* WMTProximityCheck.swift in Sources */, DC488041292282FF00DB844B /* WMTInboxEndpoints.swift in Sources */, BF53DFC82971905600829814 /* WMTInboxContentType.swift in Sources */, DCA43C6D2993F63E0059A163 /* WMTOperationAttributeImage.swift in Sources */, EA44366E29F9298100DDEC1C /* WMTPostApprovaScreenGeneric.swift in Sources */, - DC48803D292282FF00DB844B /* WMTInbox.swift in Sources */, - DC488042292282FF00DB844B /* WMTInboxImpl.swift in Sources */, + DC488042292282FF00DB844B /* WMTInbox.swift in Sources */, EA7A6E582B0E639800C1D4F4 /* WMTOperationDetailRequest.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/WultraMobileTokenSDK/Common/WMTLazy.swift b/WultraMobileTokenSDK/Common/WMTLazy.swift new file mode 100644 index 0000000..a89c2c4 --- /dev/null +++ b/WultraMobileTokenSDK/Common/WMTLazy.swift @@ -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 { + + 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 + } +} diff --git a/WultraMobileTokenSDK/Common/WMTService.swift b/WultraMobileTokenSDK/Common/WMTService.swift index 7164e01..4f934e8 100644 --- a/WultraMobileTokenSDK/Common/WMTService.swift +++ b/WultraMobileTokenSDK/Common/WMTService.swift @@ -15,11 +15,10 @@ // import Foundation -import PowerAuth2 import WultraPowerAuthNetworking protocol WMTService { - var powerAuth: PowerAuthSDK { get } + var networking: WPNNetworkingService { get } } extension WMTService { @@ -29,7 +28,7 @@ extension WMTService { /// - Parameter completion: Completion /// - Returns: True if the activation is valid func validateActivation(_ completion: @escaping (Result) -> Void) -> Bool { - guard powerAuth.hasValidActivation() else { + guard networking.powerAuth.hasValidActivation() else { DispatchQueue.main.async { completion(.failure(WMTError(reason: .missingActivation))) } diff --git a/WultraMobileTokenSDK/Inbox/WMTInbox.swift b/WultraMobileTokenSDK/Inbox/Service/WMTInbox.swift similarity index 68% rename from WultraMobileTokenSDK/Inbox/WMTInbox.swift rename to WultraMobileTokenSDK/Inbox/Service/WMTInbox.swift index e1995cc..fdf94e0 100644 --- a/WultraMobileTokenSDK/Inbox/WMTInbox.swift +++ b/WultraMobileTokenSDK/Inbox/Service/WMTInbox.swift @@ -15,17 +15,29 @@ // 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. /// @@ -33,7 +45,15 @@ 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 getUnreadCount(completion: @escaping(Result) -> Void) -> Operation? + public func getUnreadCount(completion: @escaping (Result) -> 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. /// @@ -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) -> 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) -> Operation? + public func getMessageDetail(messageId: String, completion: @escaping (Result) -> 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) -> Operation? -} - -public extension WMTInbox { + public func markRead(messageId: String, completion: @escaping (Result) -> 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) -> 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. @@ -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) diff --git a/WultraMobileTokenSDK/Inbox/Service/WMTInboxImpl.swift b/WultraMobileTokenSDK/Inbox/Service/WMTInboxImpl.swift deleted file mode 100644 index 506cd6e..0000000 --- a/WultraMobileTokenSDK/Inbox/Service/WMTInboxImpl.swift +++ /dev/null @@ -1,102 +0,0 @@ -// -// Copyright 2022 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. -// - -import Foundation -import PowerAuth2 -import WultraPowerAuthNetworking - -public extension PowerAuthSDK { - /// Creates instance of the `WMTInbox` on top of the PowerAuth instance. - /// - Parameters: - /// - networkingConfig: Networking service config - /// - Returns: Inbox service - func createWMTInbox(networkingConfig: WPNConfig) -> WMTInbox { - return WMTInboxImpl(networking: WPNNetworkingService(powerAuth: self, config: networkingConfig, serviceName: "WMTInbox")) - } -} - -public extension WPNNetworkingService { - /// Creates instance of the `WMTInbox` on top of the WPNNetworkingService instance. - /// - Returns: Inbox service - func createWMTInbox() -> WMTInbox { - return WMTInboxImpl(networking: self) - } -} - -class WMTInboxImpl: WMTInbox, WMTService { - - // Dependencies - lazy var powerAuth = networking.powerAuth - private let networking: WPNNetworkingService - - var acceptLanguage: String { - get { networking.acceptLanguage } - set { networking.acceptLanguage = newValue } - } - - init(networking: WPNNetworkingService) { - self.networking = networking - } - - func getUnreadCount(completion: @escaping (Result) -> Void) -> Operation? { - guard validateActivation(completion) else { - return nil - } - - return networking.post(data: .init(), signedWith: .possession(), to: WMTInboxEndpoints.Count.endpoint) { [weak self] response, error in - self?.processResult(response: response, error: error, completion: completion) - } - } - - 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) { [weak self] response, error in - self?.processResult(response: response, error: error, completion: completion) - } - } - - func getMessageDetail(messageId: String, completion: @escaping (Result) -> 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) { [weak self] response, error in - self?.processResult(response: response, error: error, completion: completion) - } - } - - func markRead(messageId: String, completion: @escaping (Result) -> 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) { [weak self] response, error in - self?.processResult(response: response, error: error, completion: completion) - } - } - - func markAllRead(completion: @escaping (Result) -> Void) -> Operation? { - guard validateActivation(completion) else { - return nil - } - return networking.post(data: .init(), signedWith: .possession(), to: WMTInboxEndpoints.MessageReadAll.endpoint) { [weak self] response, error in - self?.processResult(response: response, error: error, completion: completion) - } - } -} diff --git a/WultraMobileTokenSDK/Operations/Service/WMTOperationEndpoints.swift b/WultraMobileTokenSDK/Operations/Service/WMTOperationEndpoints.swift index e4c8073..3cc1620 100644 --- a/WultraMobileTokenSDK/Operations/Service/WMTOperationEndpoints.swift +++ b/WultraMobileTokenSDK/Operations/Service/WMTOperationEndpoints.swift @@ -19,9 +19,9 @@ import WultraPowerAuthNetworking enum WMTOperationEndpoints { - enum List { - typealias EndpointType = WPNEndpointSignedWithToken> - static var endpoint: EndpointType { WPNEndpointSignedWithToken(endpointURLPath: "/api/auth/token/app/operation/list", tokenName: "possession_universal") } + enum List { + typealias EndpointType = WPNEndpointSignedWithToken> + static let endpoint: EndpointType = WPNEndpointSignedWithToken(endpointURLPath: "/api/auth/token/app/operation/list", tokenName: "possession_universal") } enum History { diff --git a/WultraMobileTokenSDK/Operations/Service/WMTOperationsImpl.swift b/WultraMobileTokenSDK/Operations/Service/WMTOperations.swift similarity index 67% rename from WultraMobileTokenSDK/Operations/Service/WMTOperationsImpl.swift rename to WultraMobileTokenSDK/Operations/Service/WMTOperations.swift index c1bdd3e..722dd89 100644 --- a/WultraMobileTokenSDK/Operations/Service/WMTOperationsImpl.swift +++ b/WultraMobileTokenSDK/Operations/Service/WMTOperations.swift @@ -21,79 +21,34 @@ import WultraPowerAuthNetworking import UIKit #endif -public extension PowerAuthSDK { +/// Delegate for WMTOperations service +public protocol WMTOperationsDelegate: AnyObject { - /// Creates instance of the `WMTOperations` on top of the PowerAuth instance. + /// When operations has changed + /// /// - Parameters: - /// - networkingConfig: Networking service config - /// - pollingOptions: Polling feature configuration - /// - Returns: Operations service - func createWMTOperations(networkingConfig: WPNConfig, pollingOptions: WMTOperationsPollingOptions = []) -> WMTOperations { - return createWMTOperations(networkingConfig: networkingConfig, pollingOptions: pollingOptions, customUserOperationType: WMTUserOperation.self) - } - - /// Creates instance of the `WMTOperations` on top of the PowerAuth instance. - /// - Parameters: - /// - networkingConfig: Networking service config - /// - pollingOptions: Polling feature configuration - /// - customUserOperationType: All user operations fetched from the server will be decoded as the given type. Make sure such type properly conforms to the Codable protocol. - /// - Returns: Operations service - func createWMTOperations( - networkingConfig: WPNConfig, - pollingOptions: WMTOperationsPollingOptions = [], - customUserOperationType: T.Type - ) -> WMTOperations { - return WMTOperationsImpl(networking: WPNNetworkingService(powerAuth: self, config: networkingConfig, serviceName: "WMTOperations"), pollingOptions: pollingOptions) - } + /// - operations: current state of the operations + /// - removed: removed operation since the last call + /// - added: added operations since the last call + func operationsChanged(operations: [WMTUserOperation], removed: [WMTUserOperation], added: [WMTUserOperation]) + + /// When operations failed to load + /// + /// - Parameter error: error with more details + func operationsFailed(error: WMTError) + + /// Called when operation loading is started or stopped + /// + /// - Parameter loading: if the get operation request is in progress + func operationsLoading(loading: Bool) } -public extension WPNNetworkingService { - - /// Creates instance of the `WMTOperations` on top of the WPNNetworkingService/PowerAuth instance. - /// - Parameters: - /// - pollingOptions: Polling feature configuration - /// - Returns: Operations service - func createWMTOperations(pollingOptions: WMTOperationsPollingOptions = []) -> WMTOperations { - return createWMTOperations(pollingOptions: pollingOptions, customUserOperationType: WMTUserOperation.self) - } - - /// Creates instance of the `WMTOperations` on top of the WPNNetworkingService/PowerAuth instance. - /// - Parameters: - /// - pollingOptions: Polling feature configuration - /// - customUserOperationType: All user operations fetched from the server will be decoded as the given type. Make sure such type properly conforms to the Codable protocol. - /// - Returns: Operations service - func createWMTOperations(pollingOptions: WMTOperationsPollingOptions = [], customUserOperationType: T.Type) -> WMTOperations { - return WMTOperationsImpl(networking: self, pollingOptions: pollingOptions) - } -} - -public extension WMTErrorReason { - /// Request needs valid powerauth activation. - static let operations_invalidActivation = WMTErrorReason(rawValue: "operations_invalidActivation") - /// Operation is already in failed a state. - static let operations_alreadyFailed = WMTErrorReason(rawValue: "operations_alreadyFailed") - /// Operation is already in finished a state. - static let operations_alreadyFinished = WMTErrorReason(rawValue: "operations_alreadyFinished") - /// Operation is already in canceled a state. - static let operations_alreadyCanceled = WMTErrorReason(rawValue: "operations_alreadyCanceled") - /// Operation expired. - static let operations_alreadyRejected = WMTErrorReason(rawValue: "operations_expired") - /// Operation has expired when trying to approve the operation. - static let operations_authExpired = WMTErrorReason(rawValue: "operations_authExpired") - /// Operation has expired when trying to reject the operation. - static let operations_rejectExpired = WMTErrorReason(rawValue: "operations_rejectExpired") - /// Operation action failed. - static let operations_failed = WMTErrorReason(rawValue: "operations_failed") - - /// Couldn't sign QR operation. - static let operations_QROperationFailed = WMTErrorReason(rawValue: "operations_QRFailed") -} - -class WMTOperationsImpl: WMTOperations, WMTService { +/// Service, that communicates with Mobile Token API that handles operation approving +/// via powerauth protocol. +public class WMTOperations: WMTService { // Dependencies - lazy var powerAuth = networking.powerAuth - private let networking: WPNNetworkingService + let networking: WPNNetworkingService private let qrQueue: OperationQueue = { let q = OperationQueue() q.name = "WMTOperationsQRQueue" @@ -101,36 +56,35 @@ class WMTOperationsImpl: WMTOperations, WMTService { }() /// If operation loading is currently in progress - private(set) var isLoadingOperations = false { + public private(set) var isLoadingOperations = false { didSet { let val = isLoadingOperations delegate?.operationsLoading(loading: val) } } - var isPollingOperations: Bool { return pollingLock.synchronized { isPollingOperationsInternal } } - private var isPollingOperationsInternal: Bool { pollingTimer != nil } - - let pollingOptions: WMTOperationsPollingOptions + /// If the service is polling operations + public var isPollingOperations: Bool { return pollingLock.synchronized { isPollingOperationsInternal } } + private var isPollingOperationsInternal: Bool { + pollingTimer != nil + } - var acceptLanguage: String { + /// 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). + public var acceptLanguage: String { get { networking.acceptLanguage } set { networking.acceptLanguage = newValue } } - private var currentDate: Date { - let timeService = powerAuth.timeSynchronizationService - if timeService.isTimeSynchronized { - return Date(timeIntervalSince1970: timeService.currentTime()) - } - return Date() - } - private var tasks = [GetOperationsTask]() // Task that are waiting for operation fetch private var pollingTimer: Timer? // Timer that manages operations polling when requested private var isPollingPaused: Bool { return pollingTimer?.isValid == false } private let pollingLock = WMTLock() - private var notificationObservers = [NSObjectProtocol]() private let minimumTimePollingInterval = 5.0 /// Operation register holds operations in order @@ -138,55 +92,24 @@ class WMTOperationsImpl: WMTOperations, WMTService { self?.delegate?.operationsChanged(operations: ops, removed: removed, added: added) } - /// Last result of operation fetch. - private(set) var lastFetchResult: GetOperationsResult? + /// Last fetched operation result, not persisted. + public private(set) var lastFetchResult: GetOperationsResult? /// Delegate gets notified about changes in operations loading. /// Methods of the delegate are always called on the main thread. - weak var delegate: WMTOperationsDelegate? + public weak var delegate: WMTOperationsDelegate? - init(networking: WPNNetworkingService, pollingOptions: WMTOperationsPollingOptions = []) { + /// Initializes the instance with the given networking service. + public init(networking: WPNNetworkingService) { self.networking = networking - self.pollingOptions = pollingOptions - - #if os(iOS) - if pollingOptions.contains(.pauseWhenOnBackground) { - notificationObservers.append(NotificationCenter.default.addObserver(forName: UIApplication.willResignActiveNotification, object: nil, queue: nil) { [weak self] _ in - guard let self = self else { - return - } - self.pollingLock.synchronized { - if self.isPollingOperationsInternal { - self.pollingTimer?.invalidate() - } - } - }) - notificationObservers.append(NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: nil) { [weak self] _ in - guard let self = self else { - return - } - self.pollingLock.synchronized { - if self.isPollingPaused { - guard let timer = self.pollingTimer else { - D.error("This is a logical error, timer shouldn't be deallocated when paused") - return - } - self.pollingTimer = nil - self.startPollingOperationsInternal(interval: timer.timeInterval, delayStart: false) - } - } - }) - } - #endif - } - - deinit { - notificationObservers.forEach(NotificationCenter.default.removeObserver) } // MARK: - service API - func refreshOperations() { + /// Refreshes operations, but does not return any result. For the result, you can + /// add a delegate to `delegate` property. + /// If operations are already loading, the function does nothing. + public func refreshOperations() { DispatchQueue.main.async { // no need to start new operation loading if there is already one in progress if self.isLoadingOperations == false { @@ -195,8 +118,13 @@ class WMTOperationsImpl: WMTOperations, WMTService { } } + /// Retrieves user operations and calls task when finished. + /// + /// - Parameter completion: To be called when operations are loaded. + /// This completion is always called on the main thread. + /// - Returns: Control object in case the operations needs to be canceled. @discardableResult - func getOperations(completion: @escaping GetOperationsCompletion) -> WMTCancellable { + public func getOperations(completion: @escaping GetOperationsCompletion) -> WMTCancellable { let task = GetOperationsTask(completion: completion) @@ -226,7 +154,14 @@ class WMTOperationsImpl: WMTOperations, WMTService { return task } - func getHistory(authentication: PowerAuthAuthentication, completion: @escaping (Result<[WMTUserOperation], WMTError>) -> Void) -> Operation? { + /// Retrieves the history of user operations with its current status. + /// - Parameters: + /// - authentication: 2FA authentication object (password or biometrics) for signing. + /// - completion: Result completion. + /// This completion is always called on the main thread. + /// - Returns: Operation object for its state observation. + @discardableResult + public func getHistory(authentication: PowerAuthAuthentication, completion: @escaping (Result<[WMTUserOperation], WMTError>) -> Void) -> Operation? { guard validateActivation(completion) else { return nil @@ -237,7 +172,14 @@ class WMTOperationsImpl: WMTOperations, WMTService { } } - func getDetail(operationId: String, completion: @escaping (Result) -> Void) -> Operation? { + /// Retrieves operation detail based on operation ID + /// - Parameters: + /// - operationId: Operation ID to get + /// - completion: Result completion. + /// This completion is always called on the main thread. + /// - Returns: Operation object for its state observation. + @discardableResult + public func getDetail(operationId: String, completion: @escaping (Result) -> Void) -> Operation? { guard validateActivation(completion) else { return nil } @@ -256,7 +198,14 @@ class WMTOperationsImpl: WMTOperations, WMTService { } } - func claim(operationId: String, completion: @escaping(Result) -> Void) -> Operation? { + /// Assigns the 'non-personalized' operation to the user + /// - Parameters: + /// - operationId: Operation ID which will be claimed to belong to the user + /// - completion: Result completion. + /// This completion is always called on the main thread. + /// - Returns: Operation object for its state observation. + @discardableResult + public func claim(operationId: String, completion: @escaping(Result) -> Void) -> Operation? { guard validateActivation(completion) else { return nil @@ -277,11 +226,23 @@ class WMTOperationsImpl: WMTOperations, WMTService { } } - func authorize(operation: WMTOperation, with authentication: PowerAuthAuthentication, completion: @escaping (Result) -> Void) -> Operation? { + /// Authorize operation with given PowerAuth authentication object. + /// + /// - Parameters: + /// - operation: Operation that should be authorized. + /// - authentication: Authentication object for signing, which depends on the operation type but usually 2FA (password or biometrics) + /// - completion: Result callback. + /// This completion is always called on the main thread. + /// - Returns: Operation object for its state observation. + @discardableResult + public func authorize(operation: WMTOperation, with authentication: PowerAuthAuthentication, completion: @escaping (Result) -> Void) -> Operation? { guard validateActivation(completion) else { return nil } + + let timeService = networking.powerAuth.timeSynchronizationService + let currentDate = timeService.isTimeSynchronized ? Date(timeIntervalSince1970: timeService.currentTime()) : Date() let data = WMTAuthorizationData(operation: operation, timestampSent: currentDate) return networking.post(data: .init(data), signedWith: authentication, to: WMTOperationEndpoints.Authorize.endpoint) { response, error in @@ -297,7 +258,52 @@ class WMTOperationsImpl: WMTOperations, WMTService { } } - func reject(operation: WMTOperation, with reason: WMTRejectionReason, completion: @escaping(Result) -> Void) -> Operation? { + /// Will sign the given QR operation with URI ID and authentication object. + /// + /// Note that the operation will be signed even if the authentication object is + /// not valid as it cannot be verified on the server. + /// + /// - Parameters: + /// - qrOperation: QR operation data. + /// - uriId: Custom signature URI ID of the operation. Use URI ID under which the operation was + /// created on the server. Default value is `/operation/authorize/offline`. + /// - authentication: Authentication object for signing, which depends on the operation type but usually 2FA (password or biometrics) + /// - completion: Result completion. + /// This completion is always called on the main thread. + /// - Returns: Operation object for its state observation. + @discardableResult + public func authorize(qrOperation: WMTQROperation, uriId: String = "/operation/authorize/offline", authentication: PowerAuthAuthentication, completion: @escaping(Result) -> Void) -> Operation { + + let op = WPNAsyncBlockOperation { _, markFinished in + do { + let body = qrOperation.dataForOfflineSigning + let nonce = qrOperation.nonceForOfflineSigning + let signature = try self.networking.powerAuth.offlineSignature(with: authentication, uriId: uriId, body: body, nonce: nonce) + markFinished { + completion(.success(signature)) + } + + } catch let error { + markFinished { + completion(.failure(WMTError(reason: .operations_QROperationFailed, error: error))) + } + } + } + op.completionQueue = .main + qrQueue.addOperation(op) + return op + } + + /// Reject operation with a reason. + /// + /// - Parameters: + /// - operation: Operation that should be rejected. + /// - reason: Reason for the rejection. + /// - completion: Result callback. + /// This completion is always called on the main thread. + /// - Returns: Operation object for its state observation. + @discardableResult + public func reject(operation: WMTOperation, with reason: WMTRejectionReason, completion: @escaping(Result) -> Void) -> Operation? { guard validateActivation(completion) else { return nil @@ -320,29 +326,26 @@ class WMTOperationsImpl: WMTOperations, WMTService { } } - func authorize(qrOperation: WMTQROperation, uriId: String, authentication: PowerAuthAuthentication, completion: @escaping(Result) -> Void) -> Operation { - - let op = WPNAsyncBlockOperation { _, markFinished in - do { - let body = qrOperation.dataForOfflineSigning - let nonce = qrOperation.nonceForOfflineSigning - let signature = try self.powerAuth.offlineSignature(with: authentication, uriId: uriId, body: body, nonce: nonce) - markFinished { - completion(.success(signature)) - } - - } catch let error { - markFinished { - completion(.failure(WMTError(reason: .operations_QROperationFailed, error: error))) - } - } - } - op.completionQueue = .main - qrQueue.addOperation(op) - return op - } - - func startPollingOperations(interval: TimeInterval, delayStart: Bool) { + /// Starts the operations polling. + /// + /// Deafula implementation of startPollingOperations + /// The `interval` is set to 7 seconds, with a minimum value of 5 seconds. + /// + /// - Parameters: + /// - interval: Default is set to 7 seconds, with a minimum value of 5 seconds. + /// - delayStart: Default is set to false and polling starts immediately. + public func startPollingOperations() { + return startPollingOperations(interval: 7, delayStart: false) + } + + /// Starts the operations polling. + /// + /// If operations are already polling this call is ignored and + /// polling interval won't be changed. + /// - Parameter interval: Polling interval, minimum is 5s + /// - Parameter delayStart: When true, polling starts after + /// the first `interval` time passes + public func startPollingOperations(interval: TimeInterval, delayStart: Bool) { pollingLock.synchronized { self.startPollingOperationsInternal(interval: interval, delayStart: delayStart) } @@ -381,7 +384,7 @@ class WMTOperationsImpl: WMTOperations, WMTService { } /// Stops operations polling - func stopPollingOperations() { + public func stopPollingOperations() { pollingLock.synchronized { self.stopPollingOperationsInternal() } @@ -406,7 +409,7 @@ class WMTOperationsImpl: WMTOperations, WMTService { return } - networking.post(data: .init(), signedWith: .possession(), to: WMTOperationEndpoints.List.endpoint) { response, error in + networking.post(data: .init(), signedWith: .possession(), to: WMTOperationEndpoints.List.endpoint) { response, error in assert(Thread.isMainThread) @@ -585,3 +588,6 @@ public extension Result where Success == [WMTUserOperation], Failure == WMTError } } } + +public typealias GetOperationsResult = Result<[WMTUserOperation], WMTError> +public typealias GetOperationsCompletion = (GetOperationsResult) -> Void diff --git a/WultraMobileTokenSDK/Operations/Utils/WMTOperationsErrors.swift b/WultraMobileTokenSDK/Operations/Utils/WMTOperationsErrors.swift new file mode 100644 index 0000000..feabc12 --- /dev/null +++ b/WultraMobileTokenSDK/Operations/Utils/WMTOperationsErrors.swift @@ -0,0 +1,37 @@ +// +// 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. +// + +public extension WMTErrorReason { + /// Request needs valid powerauth activation. + static let operations_invalidActivation = WMTErrorReason(rawValue: "operations_invalidActivation") + /// Operation is already in failed a state. + static let operations_alreadyFailed = WMTErrorReason(rawValue: "operations_alreadyFailed") + /// Operation is already in finished a state. + static let operations_alreadyFinished = WMTErrorReason(rawValue: "operations_alreadyFinished") + /// Operation is already in canceled a state. + static let operations_alreadyCanceled = WMTErrorReason(rawValue: "operations_alreadyCanceled") + /// Operation expired. + static let operations_alreadyRejected = WMTErrorReason(rawValue: "operations_expired") + /// Operation has expired when trying to approve the operation. + static let operations_authExpired = WMTErrorReason(rawValue: "operations_authExpired") + /// Operation has expired when trying to reject the operation. + static let operations_rejectExpired = WMTErrorReason(rawValue: "operations_rejectExpired") + /// Operation action failed. + static let operations_failed = WMTErrorReason(rawValue: "operations_failed") + + /// Couldn't sign QR operation. + static let operations_QROperationFailed = WMTErrorReason(rawValue: "operations_QRFailed") +} diff --git a/WultraMobileTokenSDK/Operations/WMTOperations.swift b/WultraMobileTokenSDK/Operations/WMTOperations.swift deleted file mode 100644 index c57985d..0000000 --- a/WultraMobileTokenSDK/Operations/WMTOperations.swift +++ /dev/null @@ -1,211 +0,0 @@ -// -// Copyright 2020 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. -// - -import Foundation -import PowerAuth2 - -/// Protocol for service, that communicates with Mobile Token API that handles operation approving -/// via powerauth protocol. -public protocol WMTOperations: AnyObject { - - /// Delegate gets notified about changes in operations loading. - /// Methods of the delegate are always called on the main thread. - var delegate: WMTOperationsDelegate? { get set } - - /// Configuration of the polling feature - var pollingOptions: WMTOperationsPollingOptions { get } - - /// Accept language for the outgoing requests headers. - /// Default value is "en". - /// - /// 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 } - - /// Last cached operation result for easy access. - var lastFetchResult: GetOperationsResult? { get } - - /// If operation loading is currently in progress. - var isLoadingOperations: Bool { get } - - /// Refreshes operations, but does not return any result. For the result, you can - /// add a delegate to `delegate` property. - /// If operations are already loading, the function does nothing. - func refreshOperations() - - /// Retrieves user operations and calls task when finished. - /// - /// - Parameter completion: To be called when operations are loaded. - /// This completion is always called on the main thread. - /// - Returns: Control object in case the operations needs to be canceled. - @discardableResult - func getOperations(completion: @escaping GetOperationsCompletion) -> WMTCancellable - - /// Retrieves the history of user operations with its current status. - /// - Parameters: - /// - authentication: Authentication object for signing. - /// - completion: Result completion. - /// This completion is always called on the main thread. - /// - Returns: Operation object for its state observation. - @discardableResult - func getHistory(authentication: PowerAuthAuthentication, completion: @escaping(Result<[WMTUserOperation], WMTError>) -> Void) -> Operation? - - /// Retrieves operation detail based on operation ID - /// - Parameters: - /// - operationId: Operation ID to get - /// - completion: Result completion. - /// This completion is always called on the main thread. - /// - Returns: Operation object for its state observation. - @discardableResult - func getDetail(operationId: String, completion: @escaping(Result) -> Void) -> Operation? - - /// Assigns the 'non-personalized' operation to the user - /// - Parameters: - /// - operationId: Operation ID which will be claimed to belong to the user - /// - completion: Result completion. - /// This completion is always called on the main thread. - /// - Returns: Operation object for its state observation. - @discardableResult - func claim(operationId: String, completion: @escaping(Result) -> Void) -> Operation? - - /// Authorize operation with given PowerAuth authentication object. - /// - /// - Parameters: - /// - operation: Operation that should be authorized. - /// - authentication: Authentication object for signing. - /// - completion: Result callback. - /// This completion is always called on the main thread. - /// - Returns: Operation object for its state observation. - @discardableResult - func authorize(operation: WMTOperation, with: PowerAuthAuthentication, completion: @escaping(Result) -> Void) -> Operation? - - /// Will sign the given QR operation with URI ID and authentication object. - /// - /// Note that the operation will be signed even if the authentication object is - /// not valid as it cannot be verified on the server. - /// - /// - Parameters: - /// - qrOperation: QR operation data. - /// - uriId: Custom signature URI ID of the operation. Use URI ID under which the operation was - /// created on the server. Usually something like `/confirm/offline/operation`. - /// - authentication: Authentication object for signing. - /// - completion: Result completion. - /// This completion is always called on the main thread. - /// - Returns: Operation object for its state observation. - @discardableResult - func authorize(qrOperation: WMTQROperation, uriId: String, authentication: PowerAuthAuthentication, completion: @escaping(Result) -> Void) -> Operation - - /// Reject operation with a reason. - /// - /// - Parameters: - /// - operation: Operation that should be rejected. - /// - reason: Reason for the rejection. - /// - completion: Result callback. - /// This completion is always called on the main thread. - /// - Returns: Operation object for its state observation. - @discardableResult - func reject(operation: WMTOperation, with: WMTRejectionReason, completion: @escaping(Result) -> Void) -> Operation? - - /// If the service is polling operations - var isPollingOperations: Bool { get } - - /// Starts the operations polling. - /// - /// If operations are already polling this call is ignored and - /// polling interval won't be changed. - /// - Parameter interval: Polling interval, minimum is 5s - /// - Parameter delayStart: When true, polling starts after - /// the first `interval` time passes - func startPollingOperations(interval: TimeInterval, delayStart: Bool) - - /// Stops the operations polling. - func stopPollingOperations() -} - -public extension WMTOperations { - - /// Will sign the given QR operation with authentication object. - /// - /// Default operation URI ID `/operation/authorize/offline` is used. To customize this value, use - /// the method with `uriId` parameter. - /// - /// Note that the operation will be signed even if the authentication object is - /// not valid as it cannot be verified on the server. - /// - /// - Parameters: - /// - qrOperation: QR operation data - /// - authentication: Authentication object for signing. - /// - completion: Result completion. - /// This completion is always called on the main thread. - /// - Returns: Operation object for its state observation. - func authorize(qrOperation: WMTQROperation, authentication: PowerAuthAuthentication, completion: @escaping (Result) -> Void) -> Operation { - return authorize(qrOperation: qrOperation, uriId: "/operation/authorize/offline", authentication: authentication, completion: completion) - } - - /// Starts the operations polling. - /// - /// Deafula implementation of startPollingOperations - /// The `interval` is set to 7 seconds, with a minimum value of 5 seconds. - /// - /// - Parameters: - /// - interval: Default is set to 7 seconds, with a minimum value of 5 seconds. - /// - delayStart: Default is set to false and polling starts immediately. - func startPollingOperations() { - return startPollingOperations(interval: 7, delayStart: false) - } -} - -public typealias GetOperationsResult = Result<[WMTUserOperation], WMTError> -public typealias GetOperationsCompletion = (GetOperationsResult) -> Void - -/// Delegate for WMTOperations service -public protocol WMTOperationsDelegate: AnyObject { - - /// When operations has changed - /// - /// - Parameters: - /// - operations: current state of the operations - /// - removed: removed operation since the last call - /// - added: added operations since the last call - func operationsChanged(operations: [WMTUserOperation], removed: [WMTUserOperation], added: [WMTUserOperation]) - - /// When operations failed to load - /// - /// - Parameter error: error with more details - func operationsFailed(error: WMTError) - - /// Called when operation loading is started or stopped - /// - /// - Parameter loading: if the get operation request is in progress - func operationsLoading(loading: Bool) -} - -/// Configuration of the polling feature -public struct WMTOperationsPollingOptions: OptionSet { - - /// Pause polling when the app goes to the background. - /// - /// The polling is paused on `willResignActiveNotification`. - /// The polling is unpaused on `didBecomeActiveNotification`. - @available(iOS 10, *) // due to the dependency on UIKit (iOS 10 is minimum target). - public static let pauseWhenOnBackground = WMTOperationsPollingOptions(rawValue: 1 << 0) - - public let rawValue: Int - public init(rawValue: Int) { - self.rawValue = rawValue - } -} diff --git a/WultraMobileTokenSDK/Push/Model/Utils/WMTHexadecimalString.swift b/WultraMobileTokenSDK/Push/Model/Utils/WMTHexadecimalString.swift new file mode 100644 index 0000000..a9bd05c --- /dev/null +++ b/WultraMobileTokenSDK/Push/Model/Utils/WMTHexadecimalString.swift @@ -0,0 +1,32 @@ +// +// 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. +// +import Foundation + +class WMTHexadecimalString { + + static let toHexTable: [Character] = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" ] + + static func encodeData(_ data: Data) -> String { + var result = "" + result.reserveCapacity(data.count * 2) + for byte in data { + let byteAsUInt = Int(byte) + result.append(toHexTable[byteAsUInt >> 4]) + result.append(toHexTable[byteAsUInt & 15]) + } + return result + } +} diff --git a/WultraMobileTokenSDK/Push/Model/Utils/WMTPushErrors.swift b/WultraMobileTokenSDK/Push/Model/Utils/WMTPushErrors.swift new file mode 100644 index 0000000..714435b --- /dev/null +++ b/WultraMobileTokenSDK/Push/Model/Utils/WMTPushErrors.swift @@ -0,0 +1,20 @@ +// +// 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. +// + +public extension WMTErrorReason { + /// Push registration is already in progress. + static let push_alreadyRegistering = WMTErrorReason(rawValue: "push_alreadyRegistering") +} diff --git a/WultraMobileTokenSDK/Push/Service/WMTPushImpl.swift b/WultraMobileTokenSDK/Push/Service/WMTPush.swift similarity index 51% rename from WultraMobileTokenSDK/Push/Service/WMTPushImpl.swift rename to WultraMobileTokenSDK/Push/Service/WMTPush.swift index 3fe5086..b218841 100644 --- a/WultraMobileTokenSDK/Push/Service/WMTPushImpl.swift +++ b/WultraMobileTokenSDK/Push/Service/WMTPush.swift @@ -15,53 +15,42 @@ // import Foundation -import PowerAuth2 import WultraPowerAuthNetworking -public extension PowerAuthSDK { - - /// Creates instance of the `WMTPush` on top of the PowerAuth instance. - /// - Parameter networkingConfig: Networking service config - /// - Returns: Push service - func createWMTPush(networkingConfig: WPNConfig) -> WMTPush { - return WMTPushImpl(networking: WPNNetworkingService(powerAuth: self, config: networkingConfig, serviceName: "WMTPush")) - } -} - -public extension WPNNetworkingService { - - /// Creates instance of the `WMTPush` on top of the WPNNetworkingService instance. - /// - Returns: Push service - func createWMTPush() -> WMTPush { - return WMTPushImpl(networking: self) - } -} - -public extension WMTErrorReason { - /// Push registration is already in progress. - static let push_alreadyRegistering = WMTErrorReason(rawValue: "push_alreadyRegistering") -} - -class WMTPushImpl: WMTPush, WMTService { +public class WMTPush: WMTService { // Dependencies - lazy var powerAuth = networking.powerAuth let networking: WPNNetworkingService - private(set) var pushNotificationsRegisteredOnServer = false // Contains true if push notifications were already registered + /// If there was already made an successful request. + public private(set) var pushNotificationsRegisteredOnServer = false // Contains true if push notifications were already registered private var pendingRegistrationForRemotePushNotifications = false // Contains true if there's pending registration for push notifications - var acceptLanguage: String { + /// 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). + public var acceptLanguage: String { get { networking.acceptLanguage } set { networking.acceptLanguage = newValue } } - init(networking: WPNNetworkingService) { + public init(networking: WPNNetworkingService) { self.networking = networking } + /// Registers the current powerauth activation for push notifications. + /// + /// - Parameters: + /// - token: Push token. + /// - completion: Completion handler. + /// This completion is always called on the main thread. + /// - Returns: Operation object for its state observation. @discardableResult - func registerDeviceTokenForPushNotifications(token: Data, completion: @escaping (Result) -> Void) -> Operation? { + public func registerDeviceTokenForPushNotifications(token: Data, completion: @escaping (Result) -> Void) -> Operation? { guard validateActivation(completion) else { return nil @@ -77,7 +66,7 @@ class WMTPushImpl: WMTPush, WMTService { pendingRegistrationForRemotePushNotifications = true pushNotificationsRegisteredOnServer = false - let data = WMTPushRegistrationData(token: HexadecimalString.encodeData(token)) + let data = WMTPushRegistrationData(token: WMTHexadecimalString.encodeData(token)) return networking.post(data: .init(data), signedWith: .possession(), to: WMTPushEndpoints.RegisterDevice.endpoint) { _, error in self.pendingRegistrationForRemotePushNotifications = false @@ -91,20 +80,3 @@ class WMTPushImpl: WMTPush, WMTService { } } } - -private class HexadecimalString { - - static let toHexTable: [Character] = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" ] - - static func encodeData(_ data: Data) -> String { - var result = "" - result.reserveCapacity(data.count * 2) - for byte in data { - let byteAsUInt = Int(byte) - result.append(toHexTable[byteAsUInt >> 4]) - result.append(toHexTable[byteAsUInt & 15]) - } - return result - } - -} diff --git a/WultraMobileTokenSDK/Push/WMTPush.swift b/WultraMobileTokenSDK/Push/WMTPush.swift deleted file mode 100644 index 28bc229..0000000 --- a/WultraMobileTokenSDK/Push/WMTPush.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// Copyright 2020 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. -// - -import Foundation -import PowerAuth2 - -/// Protocol for service, that communicates with Mobile Token API that handles registration for -/// push notifications. -public protocol WMTPush: AnyObject { - /// If there was already made an successful request. - var pushNotificationsRegisteredOnServer: Bool { get } - - /// Accept language for the outgoing requests headers. - /// Default value is "en". - var acceptLanguage: String { get set } - - /// Registers the current powerauth activation for push notifications. - /// - /// - Parameters: - /// - token: Push token. - /// - completion: Completion handler. - /// This completion is always called on the main thread. - /// - Returns: Operation object for its state observation. - @discardableResult - func registerDeviceTokenForPushNotifications(token: Data, completion: @escaping (Result) -> Void) -> Operation? -} diff --git a/WultraMobileTokenSDK/WultraMobileToken.swift b/WultraMobileTokenSDK/WultraMobileToken.swift new file mode 100644 index 0000000..a0fb04c --- /dev/null +++ b/WultraMobileTokenSDK/WultraMobileToken.swift @@ -0,0 +1,150 @@ +// +// 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. +// + +import Foundation +import PowerAuth2 +import WultraPowerAuthNetworking + +// MARK: - PowerAuthSDK quick-access extension + +public extension PowerAuthSDK { + + /// Creates Wultra Mobile Token services from on top of the `PowerAuthSDK`. + /// URL from the `PowerAuthSDK` instance is used for services. + /// + /// `PowerAuthSDK` instance. Needs to be activated when calling any method of this class; otherwise, an error will be thrown. + /// - Parameters: + /// - acceptLanguage: The language code to set for the `Accept-Language` header. "en" when nil. + /// - userAgent: User agent that will be used in a HTTP header. Default library value when nil. + /// - Returns: Mobile Token SDK main wrapper. + /// - Throws: `InitError` when the object cannot be instantiated (incorrent URL). + func createWultraMobileToken(acceptLanguage: String? = nil, userAgent: WPNUserAgent? = nil) throws -> WultraMobileToken { + return try WultraMobileToken(powerAuth: self, acceptLanguage: acceptLanguage, userAgent: userAgent) + } +} + +// MARK: - Main Class + + /// `WultraMobileToken` provides lazy loaded core services of the SDK: + /// `operations`, `push` and `inbox`. +public class WultraMobileToken { + + // MARK: Private fields + + // PowerAuth instance + private let powerAuth: PowerAuthSDK + // Networking config + private let wpnConfig: WPNConfig + // Accept language backing field + private var acceptLanguage: String + + // Lazy-loaded backing fields + private lazy var operationsBacking = WMTLazy( + WMTOperations( + networking: WPNNetworkingService( + powerAuth: self.powerAuth, + config: self.wpnConfig, + serviceName: "WMTOperations", + acceptLanguage: self.acceptLanguage + ) + ) + ) + private lazy var pushBacking = WMTLazy( + WMTPush( + networking: WPNNetworkingService( + powerAuth: self.powerAuth, + config: self.wpnConfig, + serviceName: "WMTPush", + acceptLanguage: self.acceptLanguage + ) + ) + ) + private lazy var inboxBacking = WMTLazy( + WMTInbox( + networking: WPNNetworkingService( + powerAuth: self.powerAuth, + config: self.wpnConfig, + serviceName: "WMTInbox", + acceptLanguage: self.acceptLanguage + ) + ) + ) + + // MARK: Public API + + /// Initializes a new instance of `WultraMobileToken`. Which may fail if the PowerAuth `baseEndpointUrl` is invalid + /// - Parameters: + /// - powerAuth: `PowerAuthSDK` instance. Needs to be activated when calling any method of this class; otherwise, an error will be thrown. + /// - acceptLanguage: The language code to set for the `Accept-Language` header. "en" when nil. + /// - userAgent: User agent that will be used in a HTTP header. Default library value when nil. + /// - Throws: `InitError` when the object cannot be instantiated (incorrent URL). + public init( + powerAuth: PowerAuthSDK, + acceptLanguage: String? = nil, + userAgent: WPNUserAgent? = nil + ) throws { + self.powerAuth = powerAuth + self.acceptLanguage = acceptLanguage ?? "en" + guard let url = URL(string: powerAuth.configuration.baseEndpointUrl) else { + throw InitError.invalidBaseURL(url: powerAuth.configuration.baseEndpointUrl) + } + self.wpnConfig = WPNConfig(baseUrl: url, userAgent: userAgent ?? .libraryDefault) + + D.debug("Default Wultra Mobile Token object created with:") + D.debug(" - baseURL: \(powerAuth.configuration.baseEndpointUrl)") + } + + /// Operations manager. Use for fetching pending lists, approving operations, etc. + public var operations: WMTOperations { operationsBacking.lazy } + + /// Push manager for registering the device to receive PowerAuth push notifications for a given PowerAuth activation. + public var push: WMTPush { pushBacking.lazy } + + /// Inbox manager - receives messages to communicate with the user. + public var inbox: WMTInbox { inboxBacking.lazy } + + /** + Sets the accept language for the outgoing request headers for `operations`, `push`, and `inbox` objects. + + The value can be further modified in each object individually. + + **Standard RFC "Accept-Language"**: [RFC 7231, Section 5.3.5](https://tools.ietf.org/html/rfc7231#section-5.3.5) + + Response texts are based on this setting. For example, when `de` is set, the server + will return operation texts in German (if available). + + - Parameter lang: The language code to set for the `Accept-Language` header. + */ + public func setAcceptLanguage(_ lang: String) { + acceptLanguage = lang + operationsBacking.optional?.acceptLanguage = lang + pushBacking.optional?.acceptLanguage = lang + inboxBacking.optional?.acceptLanguage = lang + D.info("Accept language set to \(lang)") + } + + /// Initializer error + public enum InitError: LocalizedError { + /// Provided URL is invalid (see `url` associated value) + case invalidBaseURL(url: String) + + public var errorDescription: String? { + switch self { + case .invalidBaseURL(let url): return "invalidBaseURL: Provided URL is invalid: \(url)" + } + } + } +} diff --git a/WultraMobileTokenSDKTests/IntegrationProxy.swift b/WultraMobileTokenSDKTests/IntegrationProxy.swift index b0497df..ad8627f 100644 --- a/WultraMobileTokenSDKTests/IntegrationProxy.swift +++ b/WultraMobileTokenSDKTests/IntegrationProxy.swift @@ -21,7 +21,8 @@ import WultraPowerAuthNetworking class IntegrationProxy { private(set) var powerAuth: PowerAuthSDK? - private(set) var operations: WMTOperations? + private(set) var wmt: WultraMobileToken? + private(set) var ops: WMTOperations? private(set) var inbox: WMTInbox? private var config: IntegrationConfig! @@ -50,12 +51,16 @@ class IntegrationProxy { if let error = error { callback(error) } else { + self.powerAuth = pa + + // use in case you have only one enrollment server baseURL + //self.wmt = try! pa.createWultraMobileToken() + + // use if your operations and inbox urls are diffferent - set in config file `WultraMobileTokenSDKTests/Configs/Readme.md` let wpnOperationsConf = WPNConfig(baseUrl: URL(string: self.config.operationsServerUrl)!, sslValidation: .noValidation) let wpnInboxConf = WPNConfig(baseUrl: URL(string: self.config.inboxServerUrl)!, sslValidation: .noValidation) - self.powerAuth = pa - self.operations = pa.createWMTOperations(networkingConfig: wpnOperationsConf, pollingOptions: [.pauseWhenOnBackground]) - self.inbox = pa.createWMTInbox(networkingConfig: wpnInboxConf) - callback(nil) + self.ops = WMTOperations(networking: WPNNetworkingService(powerAuth: pa, config: wpnOperationsConf, serviceName: "WMTOperations")) + self.inbox = WMTInbox(networking: WPNNetworkingService(powerAuth: pa, config: wpnInboxConf, serviceName: "WMTInbox")) } } } diff --git a/WultraMobileTokenSDKTests/IntegrationTests.swift b/WultraMobileTokenSDKTests/IntegrationTests.swift index a38e71c..c227aee 100644 --- a/WultraMobileTokenSDKTests/IntegrationTests.swift +++ b/WultraMobileTokenSDKTests/IntegrationTests.swift @@ -28,8 +28,8 @@ class IntegrationTests: XCTestCase { private var proxy: IntegrationProxy! private var pa: PowerAuthSDK! { proxy.powerAuth } - private var ops: WMTOperations! { proxy.operations } - private var inbox: WMTInbox! { proxy.inbox } + private var ops: WMTOperations! { proxy.wmt?.operations ?? proxy.ops } + private var inbox: WMTInbox! { proxy.wmt?.inbox ?? proxy.inbox } private let pin = "1234" @@ -76,7 +76,7 @@ class IntegrationTests: XCTestCase { func testList() { let exp = expectation(description: "Empty list of operations") - _ = ops.getOperations { result in + _ = ops.getOperations() { result in switch result { case .success(let ops): @@ -490,90 +490,6 @@ class IntegrationTests: XCTestCase { waitForExpectations(timeout: 20, handler: nil) } - // Testing that operations polling pause works - func testOperationPollingPause() { - XCTAssertTrue(ops.pollingOptions.contains(.pauseWhenOnBackground), "Operation service is not set to pause on background") - let exp = expectation(description: "Timeout expectation") - XCTAssertFalse(ops.isPollingOperations, "Polling should be inactive") - let delegate = OpDelegate() - delegate.loadingCountCallback = { count in - if count == 1 { - // will resign active should stop polling as the app "is on background" - NotificationCenter.default.post(name: UIApplication.willResignActiveNotification, object: nil) - } - } - ops.delegate = delegate - ops.startPollingOperations(interval: 1, delayStart: false) - XCTAssertTrue(ops.isPollingOperations) - - if XCTWaiter.wait(for: [exp], timeout: 5) == XCTWaiter.Result.timedOut { - XCTAssertEqual(delegate.loadingCount, 1, "only one loading should be made") - XCTAssertTrue(ops.isPollingOperations, "Polling should be active") - exp.fulfill() - } else { - XCTFail("expectation should not have been met") - } - - // After the pause, reactive the app again and check if it was continued - - let exp2 = expectation(description: "Polling pause expectation") - let delegate2 = OpDelegate() - delegate2.loadingCountCallback = { count in - if count == 1 { - self.ops.stopPollingOperations() - exp2.fulfill() - } - } - ops.delegate = delegate2 - NotificationCenter.default.post(name: UIApplication.didBecomeActiveNotification, object: nil) - wait(for: [exp2], timeout: 5) - XCTAssertEqual(delegate2.loadingCount, 1, "Loading did continue after the active notification") - XCTAssertFalse(ops.isPollingOperations) - } - - // Testing that operations polling stop works when paused - func testOperationPollingPauseAndStop() { - XCTAssertTrue(ops.pollingOptions.contains(.pauseWhenOnBackground), "Operation service is not set to pause on background") - let exp = expectation(description: "Timeout expectation") - XCTAssertFalse(ops.isPollingOperations, "Polling should be inactive") - let delegate = OpDelegate() - delegate.loadingCountCallback = { count in - if count == 1 { - // will resign active should stop polling as the app "is on background" - NotificationCenter.default.post(name: UIApplication.willResignActiveNotification, object: nil) - } - } - ops.delegate = delegate - ops.startPollingOperations(interval: 1, delayStart: false) - XCTAssertTrue(ops.isPollingOperations) - - // The expectation should time out - if XCTWaiter.wait(for: [exp], timeout: 5) == XCTWaiter.Result.timedOut { - XCTAssertEqual(delegate.loadingCount, 1, "only one loading should be made") - XCTAssertTrue(ops.isPollingOperations, "Polling should be active") - exp.fulfill() - } else { - XCTFail("expectation should not have been met") - } - - // After the pause, we will stop the polling and "activate" the app again. - // In such case, the polling should not be started since it was stopped. - - let exp2 = expectation(description: "Polling pause expectation") - let delegate2 = OpDelegate() - ops.delegate = delegate2 - ops.stopPollingOperations() - XCTAssertFalse(ops.isPollingOperations) - NotificationCenter.default.post(name: UIApplication.didBecomeActiveNotification, object: nil) - if XCTWaiter.wait(for: [exp2], timeout: 5) == XCTWaiter.Result.timedOut { - XCTAssertEqual(delegate2.loadingCount, 0, "Loading continued after the active notification") - XCTAssertFalse(ops.isPollingOperations) - exp2.fulfill() - } else { - XCTFail("expectation should not have been met") - } - } - func testOperationChangedDelegate() { // overall process expectation diff --git a/docs/Example-Usage.md b/docs/Example-Usage.md new file mode 100644 index 0000000..85ee6b7 --- /dev/null +++ b/docs/Example-Usage.md @@ -0,0 +1,47 @@ +# Example Usage + +This is an example of the most common use case of this SDK - fetching operations and approving them. + +## SDK Integration + +Follow the [SDK Integration](./SDK-Integration.md) tutorial for SDK installation. + +## Example Code + +```swift +// PowerAuth instance needs to be configured and a user-activated instance. +// More about PowerAuth SDK can be found here: https://github.com/wultra/powerauth-mobile-sdk + +import PowerAuth2 +import WultraMobileTokenSDK + +func exampleUsage(powerAuth: PowerAuthSDK) { + do { + let mtoken = try powerauth.createWultraMobileToken(acceptLanguage: "de") // create the WultraMobileToken instance and set "requested content" to german language (default is english - "en") + + mtoken.operations.getOperations { result in + switch result { + case .success(let ops): + // we expect at least 1 operation in the list for the example purposes + let auth = PowerAuthAuthentication.possessionWithPassword(password: "1234") // simulate that user entered PIN 1234 + mtoken.operations.authorize(operation: ops.first!, with: auth) { result in + // handle success or failure of authorization + } + case .failure(let err): + //operation failed + } + } + } catch { + // PowerAuth baseUrl is not valid + } +} + +``` + +For more examples see [IntegrationTests](https://github.com/wultra/mtoken-sdk-ios/blob/develop/WultraMobileTokenSDKTests/IntegrationTests.swift) + +## Read Next + +- [Using Operations Service](./Using-Operations-Service.md) +- [Using Push Service](./Using-Push-Service.md) +- [Using Inbox Service](./Using-Inbox-Service.md) diff --git a/docs/Migration-2.0.md b/docs/Migration-2.0.md new file mode 100644 index 0000000..3e9f346 --- /dev/null +++ b/docs/Migration-2.0.md @@ -0,0 +1,53 @@ +# Migration from 1.12.x to 2.0.x + +This guide provides instructions for migrating from Wultra Mobile Token SDK for iOS version `1.12.x` to version `2.0.x`. + +Version `2.0.x` introduces a significant simplification of the SDK’s design and usage. + +--- + +### Added Functionality +**Preferred Instantiation Method** + The `WultraMobileToken` class is now the recommended way to instantiate the SDK. + PowerAuthSDK extension method is provided for this purpose. + +```kotlin +func createWultraTokenMobile(powerAuth: PowerAuthSDK) { + do { + let wmt = powerAuth.createWultraMobileToken() + + let operationsService = wmt.operations + let pushService = wmt.inbox + let inboxService = wmt.push + + } catch IntiError.invalidBaseURL(let url) { + // PowerAuth baseUrl is not valid + } +} +``` + +### Removed Functionality + +1. **Removed protocols** + +The protocols ‘WMTOperations’, ‘WMTInbox’, and ‘WMTPush’ have been removed and replaced by concrete class implementations. + +2. **Removed Extension Methods** + +The following extension methods of `PowerAuthSDK` and `WPNNetworkingService` have been removed: + - `createWMTOperations()` + - `createWMTInbox()` + - `createWMTPush` + +Services should now be instantiated using the recommended `WultraMobileToken` approach. For advanced service configuration, refer to the links below. + + + - [Using Operations Service: Creating an Instance](./Using-Operations-Service.md#creating-an-instance) + - [Using Push Service: Creating an Instance](./Using-Push-Service.md#creating-an-instance) + - [Using Inbox Service: Creating an Instance](./Using-Inbox-Service.md#creating-an-instance) + +3. **Removed Polling Options** + +The `WMTOperationsPollingOptions` class has been removed for simplification. If you require features such as polling pauses, this functionality is now outside the scope of the SDK. + +4. **`WMTOperations` no longer uses generics but only `WMTUserOperation`** diff --git a/docs/Readme.md b/docs/Readme.md index 8bd4b7b..eee2c37 100644 --- a/docs/Readme.md +++ b/docs/Readme.md @@ -21,6 +21,7 @@ If you need to upgrade the Wultra Mobile Token SDK for iOS to a newer version, y - [Migration from version `1.9.x` to `1.10.x`](Migration-1.10.md) - [Migration from version `1.12.x` to `1.13.x`](Migration-1.13.md) +- [Migration from version `1.13.x` to `2.0.x`](Migration-2.0.md) ## Integration Tutorials diff --git a/docs/SDK-Integration.md b/docs/SDK-Integration.md index 123c950..33be4b7 100644 --- a/docs/SDK-Integration.md +++ b/docs/SDK-Integration.md @@ -55,6 +55,7 @@ Note: If you want to use only operations, you can omit the Push dependency and i | WMT SDK | PowerAuth SDK | |-----------------------|---------------| +| `2.0.x` | `1.9.x` | | `1.12.x` | `1.9.x` | | `1.8.x` - `1.11.x` | `1.8.x` | | `1.6.x` - `1.7.x` | `1.7.x` | diff --git a/docs/Using-Inbox-Service.md b/docs/Using-Inbox-Service.md index ab6527b..a6f0045 100644 --- a/docs/Using-Inbox-Service.md +++ b/docs/Using-Inbox-Service.md @@ -23,7 +23,14 @@ Inbox Service communicates with the [Mobile Token API](https://developers.wultra ## Creating an Instance -### On Top of the `PowerAuthSDK` instance +The preferred way of instantiating Inbox Service is via `WultraMobileToken` class. +See: [Example Usage](./Example-Usage) + + +### Customized initialization + +If you need to create a more customized instance, such as when your Push Service uses a different enrollment server URL than other services in the SDK, you can use an initializer. Simply define the networking configuration and provide the PowerAuthSDK instance. + ```swift import WultraMobileTokenSDK import WultraPowerAuthNetworking @@ -32,16 +39,15 @@ let networkingConfig = WPNConfig( baseUrl: URL(string: "https://powerauth.myservice.com/enrollment-server")!, sslValidation: .default ) -// powerAuth is instance of PowerAuthSDK -let inboxService = powerAuth.createWMTInbox(networkingConfig: networkingConfig) -``` -### On Top of the `WPNNetworkingService` instance -```swift -import WultraMobileTokenSDK +let networkingService = WPNNetworkingService( + powerAuth: powerAuth, + config: networkingConfig, + serviceName: "InboxService", + acceptLanguage: "en" +) -// networkingService is instance of WPNNetworkingService -let inboxService = networkingService.createWMTInbox() +let opsService = WMTInbox(networking: networkingService) ``` ## Inbox Service Usage diff --git a/docs/Using-Operations-Service.md b/docs/Using-Operations-Service.md index 8c0ff64..4f0865a 100644 --- a/docs/Using-Operations-Service.md +++ b/docs/Using-Operations-Service.md @@ -33,7 +33,13 @@ Operations Service communicates with the [Mobile Token API](https://developers.w ## Creating an Instance -### On Top of the `PowerAuthSDK` instance +The preferred way of instantiating Operations Service is via `WultraMobileToken` class. +See: [Example Usage](./Example-Usage) + +### Customized initialization + +In case you need to create more customized instance. You can do so with an initializer. We will need to define networking configuration and provide PowerAuthSDK instance. + ```swift import WultraMobileTokenSDK import WultraPowerAuthNetworking @@ -42,33 +48,17 @@ let networkingConfig = WPNConfig( baseUrl: URL(string: "https://powerauth.myservice.com/enrollment-server")!, sslValidation: .default ) -// powerAuth is instance of PowerAuthSDK -let opsService = powerAuth.createWMTOperations(networkingConfig: networkingConfig, pollingOptions: [.pauseWhenOnBackground]) -``` - -### On Top of the `WPNNetworkingService` instance -```swift -import WultraMobileTokenSDK -import WultraPowerAuthNetworking -// networkingService is instance of WPNNetworkingService -let opsService = networkingService.createWMTOperations(pollingOptions: [.pauseWhenOnBackground]) -``` - -The `pollingOptions` parameter is used for polling feature configuration. The default value is empty `[]`. Possible options are: - -- `WMTOperationsPollingOptions.pauseWhenOnBackground` - -### With custom WMTUserOperation objects - -To retrieve custom user operations, both `createWMTOperations` methods offer the optional parameter `customUserOperationType` where you can set up the requested type. +let networkingService = WPNNetworkingService( + powerAuth: powerAuth, + config: networkingConfig, + serviceName: "OperationsService", + acceptLanguage: "en" +) -```swift -// networkingService is instance of WPNNetworkingService -let opsService = networkingService.createWMTOperations(customUserOperationType: CustomUserOperation.self). +let opsService = WMTOperations(networking: networkingService) ``` -When [custom operation type](#subclassing-WMTUserOperation) is set, all `WMTUserOperation` objects from such service can be explicitly unboxed to this type. ## Retrieve Pending Operations diff --git a/docs/Using-Push-Service.md b/docs/Using-Push-Service.md index 1ae7610..3b4f198 100644 --- a/docs/Using-Push-Service.md +++ b/docs/Using-Push-Service.md @@ -21,7 +21,14 @@ Push Service communicates with the [Mobile Token API](https://developers.wultra. ## Creating an Instance -### On Top of the `PowerAuthSDK` instance +The preferred way of instantiating Push Service is via `WultraMobileToken` class. +See: [Example Usage](./Example-Usage) + + +### Customized initialization + +If you need to create a more customized instance, such as when your Push Service uses a different enrollment server URL than other services in the SDK, you can use an initializer. Simply define the networking configuration and provide the PowerAuthSDK instance. + ```swift import WultraMobileTokenSDK import WultraPowerAuthNetworking @@ -30,16 +37,15 @@ let networkingConfig = WPNConfig( baseUrl: URL(string: "https://powerauth.myservice.com/enrollment-server")!, sslValidation: .default ) -// powerAuth is instance of PowerAuthSDK -let pushService = powerAuth.createWMTPush(networkingConfig: networkingConfig) -``` -### On Top of the `WPNNetworkingService` instance -```swift -import WultraMobileTokenSDK +let networkingService = WPNNetworkingService( + powerAuth: powerAuth, + config: networkingConfig, + serviceName: "PushService", + acceptLanguage: "en" +) -// networkingService is instance of WPNNetworkingService -let pushService = networkingService.createWMTPush() +let opsService = WMTPush(networking: networkingService) ``` ## Push Service API Reference diff --git a/docs/_Sidebar.md b/docs/_Sidebar.md index c3a3463..076f7f3 100644 --- a/docs/_Sidebar.md +++ b/docs/_Sidebar.md @@ -1,6 +1,7 @@ **Tutorials** - [SDK Integration](./SDK-Integration.md) +- [Example Usage](./Example-Usage.md) - [Using Operations Service](./Using-Operations-Service.md) - [Using Push Service](./Using-Push-Service.md) - [Using Inbox Service](./Using-Inbox-Service.md) @@ -11,4 +12,4 @@ **Other** - [Changelog](./Changelog.md) -- [Migration Guides](./Readme.md#migration-guides) \ No newline at end of file +- [Migration Guides](./Readme.md#migration-guides)