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 installSwift 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