Contents

nikolay-dementiev/dmaction

- [DMAction Swift SDK](#dm-action-swift-sdk)

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

<p align="center"> <img src="https://github.com/nikolay-dementiev/DMAction/blob/main/Resources/Uml-schema.svg?raw=true" alt="Uml-schema" height="300"> </p>

Retry Mechanism

<p align="center"> <img src="https://github.com/nikolay-dementiev/DMAction/blob/main/Resources/Retry-Mechanism.svg?raw=true" alt="Uml-schema" height="300"> </p>

Fallback Behavior

<p align="center"> <img src="https://github.com/nikolay-dementiev/DMAction/blob/main/Resources/Fallback-Behavior.svg?raw=true" alt="Uml-schema" height="300"> </p>

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]](https://app.fossa.io/projects/git%2Bgithub.com%2Fnikolay-dementiev%2FDMAction?ref=badge_large)

Additional Resources

Package Metadata

Repository: nikolay-dementiev/dmaction

Default branch: main

README: README.md