-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Actions queue #30
Closed
Closed
Actions queue #30
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
39 changes: 39 additions & 0 deletions
39
Sources/FuturedArchitecture/Architecture/QueueAction.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
// | ||
// QueueAction.swift | ||
// | ||
// | ||
// Created by Simon Sestak on 11/10/2024. | ||
// | ||
|
||
import Foundation | ||
|
||
public protocol QueueAction: Identifiable { | ||
/// Priority, which defines the order of actions addition to the queue | ||
var priority: QueueActionPriority { get } | ||
} | ||
|
||
public enum QueueActionPriority { | ||
/// Appended to the end of the queue | ||
case normal | ||
/// Added to the start of the queue, but after `highest` | ||
/// If there is already a `high` priority action in the queue, new action will be added after it | ||
case high | ||
/// Added to the start of the queue | ||
/// If there is already a `highest` priority action in the queue, new action will be added after it | ||
case highest | ||
/// Queue will replace all its elements when receives an action with `replaceAll` priority | ||
case replaceAll | ||
|
||
public var higher: Self? { | ||
switch self { | ||
case .normal: | ||
return .high | ||
case .high: | ||
return .highest | ||
case .highest: | ||
return .replaceAll | ||
case .replaceAll: | ||
return nil | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
// | ||
// ActionsCache.swift | ||
// | ||
// | ||
// Created by Simon Sestak on 11/10/2024. | ||
// | ||
|
||
import FuturedArchitecture | ||
import Combine | ||
|
||
actor ActionsCache<A: QueueAction> { | ||
let subject: CurrentValueSubject<A?, Never> = CurrentValueSubject(nil) | ||
|
||
private var actions = [A]() { | ||
didSet { | ||
subject.send(actions.first) | ||
} | ||
} | ||
|
||
var currentAction: A? { | ||
actions.first | ||
} | ||
|
||
func add(action: A) { | ||
// If there is already an action with the same id, just replace it with new one | ||
if let index = actions.firstIndex(where: { $0.id == action.id }) { | ||
actions[index] = action | ||
|
||
// Else, append it regarding to its priority | ||
} else { | ||
switch action.priority { | ||
case .normal: | ||
actions.append(action) | ||
case .high, .highest: | ||
if let index = indexForAction(withPriority: action.priority) { | ||
actions.insert(action, at: index) | ||
} else { | ||
actions.insert(action, at: 0) | ||
} | ||
case .replaceAll: | ||
actions = [action] | ||
} | ||
} | ||
} | ||
|
||
func replaceFirstAction(where oldAction: (A) -> Bool, with newAction: A) { | ||
if let index = actions.firstIndex(where: oldAction) { | ||
actions[index] = newAction | ||
} else { | ||
add(action: newAction) | ||
} | ||
} | ||
|
||
func finishCurrent() { | ||
if !actions.isEmpty { | ||
actions.removeFirst() | ||
} | ||
} | ||
|
||
func finishAll(where condition: (A) -> Bool) { | ||
actions.removeAll(where: condition) | ||
} | ||
|
||
func finishAll() { | ||
actions.removeAll() | ||
} | ||
|
||
private func indexForAction(withPriority priority: QueueActionPriority) -> Int? { | ||
guard let lastIndex = actions.lastIndex(where: { $0.priority == priority }) else { | ||
if let higherPriority = priority.higher { | ||
return indexForAction(withPriority: higherPriority) | ||
} else { | ||
return nil | ||
} | ||
} | ||
|
||
return actions.index(after: lastIndex) | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
// | ||
// ActionsQueue.swift | ||
// | ||
// | ||
// Created by Simon Sestak on 11/10/2024. | ||
// | ||
|
||
import Combine | ||
import FuturedArchitecture | ||
import Foundation | ||
|
||
/// Actions queue which emits new first action using publisher property after current first action is removed | ||
public class ActionsQueue<A: QueueAction> { | ||
/** | ||
- Subscribe on change of the first element in queue | ||
- Will emit new action after the current first action in queue was removed | ||
- Ignores duplicates of the same element | ||
*/ | ||
public let publisher: AnyPublisher<A?, Never> | ||
|
||
private var actionsCache: ActionsCache<A> | ||
private var cancellable: AnyCancellable? | ||
|
||
/** | ||
Init the queue to assign its changes to @Published property, which triggers ui updates automatically | ||
|
||
- Parameters: | ||
- published: @Published.Publisher property which is assigned to action queue publisher. Typically @Published property, that is responsible for presenting alerts, sheets, covers, toasts etc. | ||
*/ | ||
public init(publisher: inout Published<A?>.Publisher) { | ||
self.actionsCache = ActionsCache() | ||
self.publisher = actionsCache.subject | ||
.removeDuplicates { $0?.id == $1?.id } | ||
.eraseToAnyPublisher() | ||
|
||
// Send actions to @Published property | ||
self.publisher | ||
.receive(on: DispatchQueue.main) | ||
.assign(to: &publisher) | ||
|
||
// If @Published property is set to nil, remove first action from queue if present | ||
self.cancellable = | ||
publisher | ||
.map { $0 == nil } | ||
.removeDuplicates() | ||
.sink(receiveValue: removeFirstIfNecessary) | ||
} | ||
|
||
/// Current action in queue (the first one in the array of actions) | ||
public func currentAction() async -> A? { | ||
await actionsCache.currentAction | ||
} | ||
|
||
/// Adds actions to queue | ||
/// If there is already an action with the same id, just replace it with new one | ||
public func add(action: A) async { | ||
await actionsCache.add(action: action) | ||
} | ||
|
||
/// Removes first action in queue | ||
public func finishCurrent() async { | ||
await actionsCache.finishCurrent() | ||
} | ||
|
||
/// Removes all actions, which matches given condition | ||
public func finishAll(where condition: (A) -> Bool) async { | ||
await actionsCache.finishAll(where: condition) | ||
} | ||
|
||
public func finishAll() async { | ||
await actionsCache.finishAll() | ||
} | ||
|
||
/// Replaces first found action in array, that matches given condition, with new one | ||
public func replaceFirstAction(where oldAction: (A) -> Bool, with newAction: A) async { | ||
await actionsCache.replaceFirstAction(where: oldAction, with: newAction) | ||
} | ||
|
||
private func removeFirstIfNecessary(isNil: Bool) { | ||
if isNil { | ||
Task { await finishCurrent() } | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
jinak mozna se da zbavit toho subjectu tim, ze se z toho udela @published property. Da se na to subscribovat, drzi to current value a bude to o par radku mensi
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Skúsil som to ale potom by musel byť
ActionsQueue.init
asynchrónny nie ?A to kvôli týmto pár riadkom (viz screenshot).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mrkneme se na to spolu dneska 👍