Skip to content

nikolay-dementiev/DMAction

Repository files navigation

DMAction-SDK-logo

Swift Swift-tools-version

Platforms CocoaPods Compatible Swift Package Manager Unit Tests coverage CI_tests

DMAction Swift SDK

DMAction is a Swift SDK that provides a framework for defining and handling actions with retry and fallback mechanisms. It offers a flexible way to manage asynchronous actions and handle errors gracefully.

Features

  • Define actions with retry and fallback mechanisms
  • Handle asynchronous actions with completion handlers
  • Protocol-based design for easy integration
  • Simple and intuitive API

UML Schema

Protocol Overview

Uml-schema

Retry Mechanism

Uml-schema

Fallback Behavior

Uml-schema

Installation

CocoaPods

To integrate DMAction into your Xcode project using CocoaPods, specify it in your Podfile:

pod 'DMAction'

Then, run the following command:

pod install

Swift Package Manager

To integrate DMAction into your Xcode project using Swift Package Manager, add it to the dependencies array in your Package.swift file:

dependencies: [
    .package(url: "https://github.com/nikolay-dementiev/DMAction.git", branch: "main")
]

Usage

Basic (of course simple action closure works as expected).

But the main power was explained in the section below A More Advanced Example:

let buttonAction = DMButtonAction {
    print("Button action performed")
}

buttonAction { _ in
    ...
}

Using within UIKit

let buttonTest = UIButton(type: .system)

// If the result is completely uninteresting (muted)
buttonTest.addTarget(self,// `self` here is some UIKit object where the action's function exists
                     action: #selector(buttonTestActionWithMutedResult),
                     for: .touchUpInside)
// OR:
                     
// if you need to process the result
buttonTest.addTarget(self,// `self` here is some UIKit object where the action's function exists
                     action: #selector(buttonTestActionWithHandledResult),
                     for: .touchUpInside)
                     
@objc
func buttonTestActionWithMutedResult() {
    let primaryButtonAction = DMButtonAction(makeActionWithFailureResult)
    let fallbackButtonAction = DMButtonAction(makeActionWithSuccessResult)
    
    primaryButtonAction
        .retry(2)
        .fallbackTo(fallbackButtonAction)
        .simpleAction()
}

@objc
func buttonTestActionWithHandledResult() {
    let primaryButtonAction = DMButtonAction(makeActionWithFailureResult)
    let fallbackButtonAction = DMButtonAction(makeActionWithSuccessResult)
    
    primaryButtonAction
        .retry(2)
        .fallbackTo(fallbackButtonAction)() { result in
            // do something with result
        }
}

func makeActionWithFailureResult(completion: @escaping (DMButtonAction.ResultType) -> Void) {
    
    // ... do something
    
    completion(.failure(NSError(domain: "TestDomain",
                                code: 404,
                                userInfo: nil)))
}

func makeActionWithSuccessResult(completion: @escaping (DMButtonAction.ResultType) -> Void) {
    
    // ... do something
    
    let yourResultVaue: Copyable = "\(#function) succeded!"
    completion(.success(yourResultVaue))
}

Using within SWiftUI

...
var body: some View {
    // If the result is completely uninteresting (muted)
    Button("Test button with muted result", action: buttonTestActionWithMutedResult)
    // if you need to process the result
    Button("Test button with handled result", action: buttonTestActionWithHandledResult)
}
...

func buttonTestActionWithMutedResult() {
    let primaryButtonAction = DMButtonAction(makeActionWithFailureResult)
    let fallbackButtonAction = DMButtonAction(makeActionWithSuccessResult)
    
    primaryButtonAction
        .retry(2)
        .fallbackTo(fallbackButtonAction)
        .simpleAction()
}

func buttonTestActionWithHandledResult() {
    let primaryButtonAction = DMButtonAction(makeActionWithFailureResult)
    let fallbackButtonAction = DMButtonAction(makeActionWithSuccessResult)
    
    primaryButtonAction
        .retry(2)
        .fallbackTo(fallbackButtonAction)() { result in
            // do something with result
        }
}

func makeActionWithFailureResult(completion: @escaping (DMButtonAction.ResultType) -> Void) {
    
    // ... do something
    
    completion(.failure(NSError(domain: "TestDomain",
                                code: 404,
                                userInfo: nil)))
}

func makeActionWithSuccessResult(completion: @escaping (DMButtonAction.ResultType) -> Void) {
    
    // ... do something
    
    let yourResultVaue: Copyable = "\(#function) succeded!"
    completion(.success(yourResultVaue))
}

A More Advanced Example

  • An example with two different actions (primaryButtonAction and fallbackButtonAction) combined into a single execution chain using .retry(1):
    • 1 represents the maximum number of retry attempts for primaryButtonAction. It will be executed until either it succeeds or the retry limit is reached.
    • If primaryButtonAction fails after the maximum attempts,fallbackButtonAction will be triggered via fallbackTo(...).
// Primary action
let primaryButtonAction = DMButtonAction { completion in
    completion(.failure(NSError(domain: "TestError", code: 1, userInfo: nil)))
}

// Fallback action
let fallbackButtonAction = DMButtonAction { completion in
    completion(.success(MockCopyable(value: "Fallback Success")))
}

// Create execution chain
let actionWithFallback = primaryButtonAction
    .retry(1) //The number of retry action before fallback
    .fallbackTo(fallbackButtonAction)
    
var resultOfAction: DMAction.ResultType?
actionWithFallback { result in
    // `unwrapValue()`: get rid of the wrapper - return the original result value that 
    // was passed via DMButtonAction' completion closure
    print("the result value: \(result.unwrapValue())")
    // `attemptCount`: contains UInt number of action's attemps
    print("attemptCount: \(result.attemptCount)")
    
    resultOfAction = result
}

if case .success(let copyableValue) = resultOfAction {
    ...
} else {
    ...
}

License

This project is licensed under the MIT License - see the LICENSE file for details.

FOSSA Status

Additional Resources

About

Action with fallback action possibility

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •