Giter Club home page Giter Club logo

billp / terminetwork Goto Github PK

View Code? Open in Web Editor NEW
98.0 98.0 8.0 54.89 MB

๐ŸŒ A zero-dependency networking solution for building modern and secure iOS, watchOS, macOS and tvOS applications.

Home Page: https://billp.github.io/TermiNetwork

License: MIT License

Ruby 0.19% Swift 99.37% Shell 0.05% Objective-C 0.38%
apple carthage cocoapods deserialization interceptors ios library macos mock-data networking routers swift swift-packages swiftui transformers tvos watchos xcode

terminetwork's People

Contributors

billp avatar voynovia avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

terminetwork's Issues

Unable to upload files. Wrong body type and exception is thrown

Hi, I am trying to upload a simple png file without success. The request is throwing thread exception because of wrong body type I think.

The endpoint configuration in my repo:

case .register(let verificationId, let verificationCode, let mobilePhone, let deviceName, let userModel, let avatar):
                return .init(method: .post,
                             path: .path(["auth", "register"]),
                             params: ["mobile_phone": mobilePhone,
                                      "verification_id": verificationId,
                                      "verification_code": verificationCode,
                                      "device_id": deviceName,
                                      "first_name": userModel.firstName,
                                      "last_name": userModel.lastName,
                                      "username": userModel.username,
                                      "avatar": avatar != nil ? MultipartFormDataPartType.url(avatar!) : nil
                                     ]
                             )

This is the caller

func register(mobilePhoneE164: String, verificationId: String, verificationCode: String, user: User, avatar: URL?) async throws {
        let deviceUuid = await UIDevice.current.identifierForVendor!.uuidString
        
        let result = try await Client<AuthRepository>()
            .request(for: .register(verificationId: verificationId, verificationCode: verificationCode, mobilePhone: mobilePhoneE164, deviceName: deviceUuid, user, avatar: avatar))
            .asyncUpload(as: ApiResponse<AccessToken>.self, progressUpdate: nil)
        
        saveToken(accessToken: result.data.accessToken)
    }

And finally the exception thrown

2023-08-14 18:34:00.764788+0300 MYPROJECT[43099:16605822] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (__SwiftValue)'
*** First throw call stack:
(
	0   CoreFoundation                      0x0000000180437330 __exceptionPreprocess + 172
	1   libobjc.A.dylib                     0x0000000180051274 objc_exception_throw + 56
	2   Foundation                          0x0000000180c0ed40 _writeJSONValue + 704
	3   Foundation                          0x0000000180c12bfc ___writeJSONObject_block_invoke + 384
	4   libswiftCore.dylib                  0x000000018bf3e434 $ss26_SwiftDeferredNSDictionaryC23enumerateKeysAndObjects7options5usingySi_ys9UnmanagedVyyXlG_AHSpys5UInt8VGtXBtFTf4dnn_n + 332
	5   libswiftCore.dylib                  0x000000018bd43584 $ss26_SwiftDeferredNSDictionaryC23enumerateKeysAndObjects7options5usingySi_ys9UnmanagedVyyXlG_AHSpys5UInt8VGtXBtFTo + 44
	6   Foundation                          0x0000000180c121a0 _writeJSONObject + 440
	7   Foundation                          0x0000000180c0ea40 -[_NSJSONWriter dataWithRootObject:options:] + 84
	8   Foundation                          0x0000000180c1170c +[NSJSONSerialization dataWithJSONObject:options:error:] + 108
	9   MYPROJECT                            0x0000000104e4303c $sSD12TermiNetworkE10toJSONData10Foundation4DataVSgyKF + 168
	10  MYPROJECT                            0x0000000104e68938 $s12TermiNetwork20RequestBodyGeneratorC20generateJSONBodyData4with10Foundation0H0VSDySSypSgG_tKFZ + 96
	11  MYPROJECT                            0x0000000104e87fe8 $s12TermiNetwork7RequestC21addBodyParamsIfNeeded33_C06940D9FEEDCA294CA875886000989FLL04withC06paramsy10Foundation10URLRequestVz_SDySSypSgGSgtKF + 2016
	12  MYPROJECT                            0x0000000104e87058 $s12TermiNetwork7RequestC02asC010Foundation10URLRequestVyKF + 3176
	13  MYPROJECT                            0x0000000104e8fc74 $s12TermiNetwork18SessionTaskFactoryC08makeDataD04with17completionHandler9onFailureSo012NSURLSessiongD0CSgAA7RequestC_y10Foundation0G0VSg_So13NSURLResponseCSgtcSgyAP_AA7TNErrorOtcSgtFZ + 344
	14  MYPROJECT                            0x0000000104e88ab8 $s12TermiNetwork7RequestC011executeDataC8IfNeededyyF + 416
	15  MYPROJECT                            0x0000000104e43974 $s12TermiNetwork7RequestC7failure15responseHandlerACyAA7TNErrorOc_tF + 156
	16  MYPROJECT                            0x0000000104e45498 $s12TermiNetwork7RequestC11asyncUpload2as14progressUpdatexxm_ySi_SiSftcSgtYaKSeRzlFxyYaKXEfU_yScCyxs5Error_pGXEfU_ + 524
	17  libswift_Concurrency.dylib          0x00000001b1632a3c $ss23withCheckedContinuation8function_xSS_yScCyxs5NeverOGXEtYalFySccyxADGXEfU_Tm + 108
	18  libswift_Concurrency.dylib          0x00000001b1632960 $ss31withCheckedThrowingContinuation8function_xSS_yScCyxs5Error_pGXEtYaKlF + 196
	19  MYPROJECT                            0x0000000104e44e9d $s12TermiNetwork7RequestC11asyncUpload2as14progressUpdatexxm_ySi_SiSftcSgtYaKSeRzlFxyYaKXEfU_TQ1_ + 1
	20  MYPROJECT                            0x0000000104e451e9 $s12TermiNetwork7RequestC11asyncUpload2as14progressUpdatexxm_ySi_SiSftcSgtYaKSeRzlFxyYaKXEfU_TATQ0_ + 1
	21  libswift_Concurrency.dylib          0x00000001b16407a5 $ss27withTaskCancellationHandler9operation8onCancelxxyYaKXE_yyYbXEtYaKlFTQ0_ + 1
	22  MYPROJECT                            0x0000000104e458a9 $ss27withTaskCancellationHandler9operation8onCancelxxyYaKXE_yyYbXEtYaKlFTwbTQ1_ + 1
	23  MYPROJECT                            0x0000000104e448f1 $s12TermiNetwork7RequestC11asyncUpload2as14progressUpdatexxm_ySi_SiSftcSgtYaKSeRzlFTQ1_ + 1
	24  MYPROJECT                            0x0000000104d5e9c9 $s8MYPROJECT11AuthServiceC8register15mobilePhoneE16414verificationId0H4Code4user6avatarySS_S2SAA4UserV10Foundation3URLVSgtYaKFTQ5_ + 1
	25  MYPROJECT                            0x0000000104d93ac9 $s8MYPROJECT15SignUpViewModelC22validateFormWithServerSbyYaFTQ1_ + 1
	26  MYPROJECT                            0x0000000104d92f9d $s8MYPROJECT15SignUpViewModelC4saveyyYaFTQ1_ + 1
	27  MYPROJECT                            0x0000000104d6e351 $s8MYPROJECT10SignUpViewV4bodyQrvg7SwiftUI05TupleD0VyAE0D0PAEE15ignoresSafeArea_5edgesQrAE0jK7RegionsV_AE4EdgeO3SetVtFQOyAE5ColorV_Qo__AiEE7paddingyQrAQ_12CoreGraphics7CGFloatVSgtFQOyAiEE21navigationDestination11isPresented11destinationQrAE7BindingVySbG_qd__yXEtAeHRd__lFQOyAiEE5sheetA_9onDismiss7contentQrA3__yycSgqd__yctAeHRd__lFQOyAE6VStackVyAGyAiEE5frame5width6height9alignmentQrAY_AyE9AlignmentVtFQOyAiEE010foregroundP0yQrASSgFQOyAE5ImageV_Qo__Qo__AiEEAUyQrAQ_AYtFQOyAE4TextV_Qo_A9_yAGyAE6ButtonVyAiEEAUyQrAQ_AYtFQOyAiEEA10_A11_A12_A13_QrAY_AYA15_tFQOyAiEE7overlay_A13_Qrqd___A15_tAeHRd__lFQOyAE5ShapePAEE4fill_5styleQrqd___AE9FillStyleVtAE10ShapeStyleRd__lFQOyAE6CircleV_ASQo__AE5GroupVyAE19_ConditionalContentVyAE6ZStackVyAGyAiEE9clipShape_A31_Qrqd___A33_tAEA28_Rd__lFQOyAA021CompleteProfileAvatarD0V_A36_Qo__AiEE6offset1x1yQrAX_AXtFQOyA26_yAiEEA16_yQrA17_FQOyAiEE4fontyQrAE4FontVSgFQOyA19__Qo__Qo_G_Qo_tGGA19_GGQo__Qo__Qo_G_AiEE8onSubmit2of_QrAE14SubmitTriggersV_yyctFQOyAiEEAUyQrAQ_AYtFQOyAI13FormValidatorE10validationyQrA71_19ValidationContainerVSgFQOyAiEE11submitLabelyQrAE11SubmitLabelVFQOyAiEE12cornerRadius_11antialiasedQrAX_SbtFQOyAiEE10background_0ijK5EdgesQrqd___AQtAEA34_Rd__lFQOyAiEEAUyQrAQ_AYtFQOyAiEEAUyQrAQ_AYtFQOyAiEE27textInputAutocapitalizationyQrAE27TextInputAutocapitalizationVSgFQOyAiEE22autocorrectionDisabledyQrSbFQOyAiEE15textContentTypeyQrSo17UITextContentTypeaSgFQOyAiEE12keyboardTypeyQrSo14UIKeyboardTypeVFQOyAiEE7focused_6equalsQrAE10FocusStateVA1_Vyqd___G_qd__tSHRd__lFQOyAiEEA16_yQrA17_FQOyAE9TextFieldVyA23_G_Qo__AA24CompleteProfileFormFocusOSgQo__Qo__Qo__Qo__Qo__Qo__Qo__ASQo__Qo__Qo__Qo__Qo__Qo_AiEEAUyQrAQ_AYtFQOyAIA71_EA72_yQrA75_FQOyAiEEA76_yQrA78_FQOyAiEEA79__A80_QrAX_SbtFQOyAiEEA81__A82_Qrqd___AQtAEA34_Rd__lFQOyAiEEAUyQrAQ_AYtFQOyAiEEAUyQrAQ_AYtFQOyAiEEA88_yQrA91_FQOyAiEEA83_yQrA86_FQOyAiEEA87_yQrSbFQOyA109__Qo__Qo__Qo__Qo__Qo__ASQo__Qo__Qo__Qo__Qo_A130_tGGAE6SpacerVAA06ButtonD0VtGG_16ExyteMediaPicker11MediaPickerVyAE05EmptyD0VA143_GQo__AA015OtpVerificationD0VQo__Qo_tGyXEfU_A137_yXEfU_yycfU0_yyYaYbcfU_TQ1_ + 1
	28  MYPROJECT                            0x0000000104d75c9d $s8MYPROJECT10SignUpViewV4bodyQrvg7SwiftUI05TupleD0VyAE0D0PAEE15ignoresSafeArea_5edgesQrAE0jK7RegionsV_AE4EdgeO3SetVtFQOyAE5ColorV_Qo__AiEE7paddingyQrAQ_12CoreGraphics7CGFloatVSgtFQOyAiEE21navigationDestination11isPresented11destinationQrAE7BindingVySbG_qd__yXEtAeHRd__lFQOyAiEE5sheetA_9onDismiss7contentQrA3__yycSgqd__yctAeHRd__lFQOyAE6VStackVyAGyAiEE5frame5width6height9alignmentQrAY_AyE9AlignmentVtFQOyAiEE010foregroundP0yQrASSgFQOyAE5ImageV_Qo__Qo__AiEEAUyQrAQ_AYtFQOyAE4TextV_Qo_A9_yAGyAE6ButtonVyAiEEAUyQrAQ_AYtFQOyAiEEA10_A11_A12_A13_QrAY_AYA15_tFQOyAiEE7overlay_A13_Qrqd___A15_tAeHRd__lFQOyAE5ShapePAEE4fill_5styleQrqd___AE9FillStyleVtAE10ShapeStyleRd__lFQOyAE6CircleV_ASQo__AE5GroupVyAE19_ConditionalContentVyAE6ZStackVyAGyAiEE9clipShape_A31_Qrqd___A33_tAEA28_Rd__lFQOyAA021CompleteProfileAvatarD0V_A36_Qo__AiEE6offset1x1yQrAX_AXtFQOyA26_yAiEEA16_yQrA17_FQOyAiEE4fontyQrAE4FontVSgFQOyA19__Qo__Qo_G_Qo_tGGA19_GGQo__Qo__Qo_G_AiEE8onSubmit2of_QrAE14SubmitTriggersV_yyctFQOyAiEEAUyQrAQ_AYtFQOyAI13FormValidatorE10validationyQrA71_19ValidationContainerVSgFQOyAiEE11submitLabelyQrAE11SubmitLabelVFQOyAiEE12cornerRadius_11antialiasedQrAX_SbtFQOyAiEE10background_0ijK5EdgesQrqd___AQtAEA34_Rd__lFQOyAiEEAUyQrAQ_AYtFQOyAiEEAUyQrAQ_AYtFQOyAiEE27textInputAutocapitalizationyQrAE27TextInputAutocapitalizationVSgFQOyAiEE22autocorrectionDisabledyQrSbFQOyAiEE15textContentTypeyQrSo17UITextContentTypeaSgFQOyAiEE12keyboardTypeyQrSo14UIKeyboardTypeVFQOyAiEE7focused_6equalsQrAE10FocusStateVA1_Vyqd___G_qd__tSHRd__lFQOyAiEEA16_yQrA17_FQOyAE9TextFieldVyA23_G_Qo__AA24CompleteProfileFormFocusOSgQo__Qo__Qo__Qo__Qo__Qo__Qo__ASQo__Qo__Qo__Qo__Qo__Qo_AiEEAUyQrAQ_AYtFQOyAIA71_EA72_yQrA75_FQOyAiEEA76_yQrA78_FQOyAiEEA79__A80_QrAX_SbtFQOyAiEEA81__A82_Qrqd___AQtAEA34_Rd__lFQOyAiEEAUyQrAQ_AYtFQOyAiEEAUyQrAQ_AYtFQOyAiEEA88_yQrA91_FQOyAiEEA83_yQrA86_FQOyAiEEA87_yQrSbFQOyA109__Qo__Qo__Qo__Qo__Qo__ASQo__Qo__Qo__Qo__Qo_A130_tGGAE6SpacerVAA06ButtonD0VtGG_16ExyteMediaPicker11MediaPickerVyAE05EmptyD0VA143_GQo__AA015OtpVerificationD0VQo__Qo_tGyXEfU_A137_yXEfU_yycfU0_yyYaYbcfU_TATQ0_ + 1
	29  MYPROJECT                            0x0000000104d56e01 $sxIeghHr_xs5Error_pIegHrzo_s8SendableRzs5NeverORs_r0_lTRTQ0_ + 1
	30  MYPROJECT                            0x0000000104d56f41 $sxIeghHr_xs5Error_pIegHrzo_s8SendableRzs5NeverORs_r0_lTRTATQ0_ + 1
	31  libswift_Concurrency.dylib          0x00000001b1660445 _ZL23completeTaskWithClosurePN5swift12AsyncContextEPNS_10SwiftErrorE + 1
)
libc++abi: terminating due to uncaught exception of type NSException
(Recorded stack frame) 
#0	0x0000000180437324 in __exceptionPreprocess ()
#1	0x0000000180051274 in objc_exception_throw ()
#2	0x0000000180c0ed40 in _writeJSONValue ()
#3	0x0000000180c12bfc in ___writeJSONObject_block_invoke ()
#4	0x000000018bf3e434 in specialized _SwiftDeferredNSDictionary.enumerateKeysAndObjects(options:using:) ()
#5	0x000000018bd43584 in @objc _SwiftDeferredNSDictionary.enumerateKeysAndObjects(options:using:) ()
#6	0x0000000180c121a0 in _writeJSONObject ()
#7	0x0000000180c0ea40 in -[_NSJSONWriter dataWithRootObject:options:] ()
#8	0x0000000180c1170c in +[NSJSONSerialization dataWithJSONObject:options:error:] ()
#9	0x0000000104e4303c in Dictionary.toJSONData() at /myproject/-aorjesgwpwvgdfbmtmzrwsqletef/SourcePackages/checkouts/TermiNetwork/Source/Extensions/Dictionary+Extensions.swift:24
#10	0x0000000104e68938 in static RequestBodyGenerator.generateJSONBodyData(with:) at /myproject/-aorjesgwpwvgdfbmtmzrwsqletef/SourcePackages/checkouts/TermiNetwork/Source/Helpers/RequestBodyGenerators.swift:60
#11	0x0000000104e87fe8 in Request.addBodyParamsIfNeeded(withRequest:params:) at /myproject/-aorjesgwpwvgdfbmtmzrwsqletef/SourcePackages/checkouts/TermiNetwork/Source/Request.swift:291
#12	0x0000000104e87058 in Request.asRequest() at /myproject/-aorjesgwpwvgdfbmtmzrwsqletef/SourcePackages/checkouts/TermiNetwork/Source/Request.swift:240
#13	0x0000000104e8fc74 in static SessionTaskFactory.makeDataTask(with:completionHandler:onFailure:) at /myproject/-aorjesgwpwvgdfbmtmzrwsqletef/SourcePackages/checkouts/TermiNetwork/Source/SessionTaskFactory.swift:35
#14	0x0000000104e88ab8 in Request.executeDataRequestIfNeeded() at /myproject/-aorjesgwpwvgdfbmtmzrwsqletef/SourcePackages/checkouts/TermiNetwork/Source/Request.swift:352
#15	0x0000000104e43974 in Request.failure(responseHandler:) at /myproject/-aorjesgwpwvgdfbmtmzrwsqletef/SourcePackages/checkouts/TermiNetwork/Source/Extensions/Operations/Request+DataOperations.swift:37
#16	0x0000000104e45498 in closure #1 in closure #1 in Request.asyncUpload<ฯ„_0_0>(as:progressUpdate:) at /myproject/-aorjesgwpwvgdfbmtmzrwsqletef/SourcePackages/checkouts/TermiNetwork/Source/Extensions/Operations/Request+FileOperationsAsync.swift:43
#17	0x00000001b1632a3c in closure #1 in withCheckedContinuation<ฯ„_0_0>(function:_:) ()
#18	0x00000001b1632960 in withCheckedThrowingContinuation<ฯ„_0_0>(function:_:) ()
#19	0x0000000104e44e9d in closure #1 in Request.asyncUpload<ฯ„_0_0>(as:progressUpdate:) at /myproject/-aorjesgwpwvgdfbmtmzrwsqletef/SourcePackages/checkouts/TermiNetwork/Source/Extensions/Operations/Request+FileOperationsAsync.swift:37
#20	0x0000000104e451e9 in partial apply for closure #1 in Request.asyncUpload<ฯ„_0_0>(as:progressUpdate:) ()
#21	0x00000001b16407a5 in withTaskCancellationHandler<ฯ„_0_0>(operation:onCancel:) ()
#22	0x0000000104e458a9 in withTaskCancellationHandler<ฯ„_0_0>(operation:onCancel:) ()
#23	0x0000000104e448f1 in Request.asyncUpload<ฯ„_0_0>(as:progressUpdate:) at /myproject/-aorjesgwpwvgdfbmtmzrwsqletef/SourcePackages/checkouts/TermiNetwork/Source/Extensions/Operations/Request+FileOperationsAsync.swift:35
#24	0x0000000104d5e9c9 in AuthService.register(mobilePhoneE164:verificationId:verificationCode:user:avatar:) at /Users/myuser/Developer/MYPROJECT/Code/apps/MYPROJECT/MYPROJECT/Services/AuthService.swift:94
#25	0x0000000104d93ac9 in SignUpViewModel.validateFormWithServer() at /Users/myuser/Developer/MYPROJECT/Code/apps/MYPROJECT/MYPROJECT/ViewModels/Authentication/SignUpViewModel.swift:78
#26	0x0000000104d92f9d in SignUpViewModel.save() at /Users/myuser/Developer/MYPROJECT/Code/apps/MYPROJECT/MYPROJECT/ViewModels/Authentication/SignUpViewModel.swift:61
#27	0x0000000104d6e351 in closure #1 in closure #2 in closure #1 in closure #1 in SignUpView.body.getter at /Users/myuser/Developer/MYPROJECT/Code/apps/MYPROJECT/MYPROJECT/Views/Screens/Authentication/SignUpView.swift:166
#28	0x0000000104d75c9d in partial apply for closure #1 in closure #2 in closure #1 in closure #1 in SignUpView.body.getter ()
#29	0x0000000104d56e01 in thunk for @escaping @callee_guaranteed @Sendable @async () -> (@out ฯ„_0_0) ()
#30	0x0000000104d56f41 in partial apply for thunk for @escaping @callee_guaranteed @Sendable @async () -> (@out ฯ„_0_0) ()
#31	0x00000001b1660445 in completeTaskWithClosure(swift::AsyncContext*, swift::SwiftError*) ()

I am thinking it's because of wrong body type since the request is trying to write json output while the avatar param is MultiPart

Once this is resolved I will try to find time to create PR with documentation on uploading

EDIT:

case .register(let verificationId, let verificationCode, let mobilePhone, let deviceName, let userModel, let avatar):
                let config = Configuration()
                config.requestBodyType = .multipartFormData(boundary: UUID().uuidString)
                return .init(method: .post,
                             path: .path(["auth", "register"]),
                             params: ["mobile_phone": MultipartFormDataPartType.value(value: mobilePhone),
                                      "verification_id": MultipartFormDataPartType.value(value: verificationId),
                                      "verification_code": MultipartFormDataPartType.value(value: verificationCode),
                                      "device_id": MultipartFormDataPartType.value(value: deviceName),
                                      "first_name": MultipartFormDataPartType.value(value: userModel.firstName),
                                      "last_name": MultipartFormDataPartType.value(value: userModel.lastName),
                                      "username": MultipartFormDataPartType.value(value: userModel.username),
                                      "avatar": avatar != nil ? MultipartFormDataPartType.url(avatar!) : nil
                                     ],
                             configuration: config
                             )

By explicitly configuring the body as multipart its working fine. But if verbose is enabled it does not work in either way so I think the issue is in asyncUpload() not setting the appropriate body type.

EDIT 1:

When no file is passed even though the body is same as with avatar the request is empty.

Change the way of getting the response

Description

Change the request execution method calls to a simpler approach.

Steps

  1. Split the start() method call to two deifferent methods:
    • response(as:[TYPE], completionHandler([TYPE])
    • error(as:[TYPE], completionHandler([TYPE], [ERROR])
  2. Remove the queue parameter from start() methods and add it as separate method queue([QUEUE])
  3. The transformer calls will be overloaded with the new methods as defined in 1
  4. All the new methods are optional
  5. To start the request without any additional method call for handling responses, implement the exec() function

Example

Router<CityRoute>()
    .request(for: .cities)
    .queue(myQueue)
    .success(transformer: CitiesTransformer.self,
             responseHandler: { model in
    })
    .failure(responseType: MyErrorModel.self,
             responseHandler: { errorModel, error in
    })

Issue with interceptors and catching errors.

Hi, It seems there is an issue for which I can't find any workaround. I have UnauthorizedInterceptor which refreshes the access token when needed which is working fine.

import Foundation
import TermiNetwork

final class UnauthorizedInterceptor: InterceptorProtocol {
    
    private var authService = TokenService.shared
    
    func requestFinished(responseData data: Data?,
                         error: TNError?,
                         request: Request,
                         proceed: @escaping (InterceptionAction) -> Void) {
        switch error {
            case .notSuccess(let statusCode, _):
                if statusCode == 401 {
                    // Login and get a new token.
                    Client<AuthRepository>()
                        .request(for: .refreshToken(refreshToken: authService.token!.refreshToken, expiredAccessToken: authService.token!.accessToken))
                        .success(responseType: AccessToken.self) { response in
                            try? self.authService.saveToken(accessToken: response)
                            
                            let authorizationValue = String(format: "Bearer %@", response.accessToken)
                            
                            // Update the global header in configuration which is inherited by all requests.
                            Environment.current.configuration?.headers?["Authorization"] = authorizationValue
                            
                            // Update current request's header.
                            request.headers?["Authorization"] = authorizationValue
                            
                            // Finally retry the original request.
                            proceed(.retry(delay: 0.2))
                        }
                } else {
                    // Continue if the retry limit is reached
                    proceed(.continue)
                }
            default:
                proceed(.continue)
        }
    }
}

BUT when the request which caused the token to be refresh also fails let's say with 422 in my case the Data is empty this is only happening when we are trying to catch the error in the request where 422 is happening

func save() async {
        let valid = formManager.triggerValidation()
        if !valid {
            return 
        }
        
        isLoading = true
        defer {
            isLoading = false
        }
        
        if var user = TokenService.shared.currentUser {
            do {
                user.firstName = firstName
                user.lastName = lastName
                user.username = username
                
                let _ = try await Client<UserRepository>()
                    .request(for: .updateUser(user: user))
                    .async(as: String.self)
            } catch let error {
                switch error as? TNError {
                    case .notSuccess(let statusCode, let data):
                        if statusCode == 401 {
                            let errorModel2 = try? data.deserializeJSONData() as [String: String]
                            print(String(describing: errorModel2))
                        }
                        if statusCode == 422 {
                            do {
                                let errorModel = try data.deserializeJSONData() as ApiErrorResponse
                                print(String(describing: errorModel))
                            } catch {
                                print(String(describing: data))
                                print(String(describing: error))
                            }
                        }
                    default:
                        print("Error \(error.localizedDescription)")
                }
            }
        }
    }

So when the interceptor is not involved everything is ok the errorModel is populated but when the interceptor kicks in the data is empty. Overall I think the request is throwing the error instead of passing it thru the interceptor that's why we can't catch the next error after the first(401) from the interceptor

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.