Skip to content

Commit

Permalink
SwiftUI SideEffects implementation (#31)
Browse files Browse the repository at this point in the history
* view effects

* added view effect modifier

* view side effects

* store view imlementation

* minor fix
  • Loading branch information
KazaiMazai authored Aug 23, 2024
1 parent b3ec776 commit 41bfbf8
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 0 deletions.
1 change: 1 addition & 0 deletions Sources/Puredux/SideEffects/SideEffects.swift
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ extension Store {
}
)
}

func effect(_ observer: AnyObject,
withDelay timeInterval: TimeInterval,
removeStateDuplicates: Equating<State>?,
Expand Down
23 changes: 23 additions & 0 deletions Sources/Puredux/SwiftUI/ViewsWIthStore/EffectModifier.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//
// File.swift
//
//
// Created by Sergey Kazakov on 22/08/2024.
//

import SwiftUI

@available(iOS 13.0, *)
struct EffectModifier<ViewState, Action>: ViewModifier {
let store: Store<ViewState, Action>
let createEffect: (AnyObject, Store<ViewState, Action>) -> Void

private class ViewIsAlive { }

@State private var viewIsAlive = ViewIsAlive()

func body(content: Content) -> some View {
content
.onAppear { createEffect(viewIsAlive, store) }
}
}
86 changes: 86 additions & 0 deletions Sources/Puredux/SwiftUI/ViewsWIthStore/StoreView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//
// File.swift
//
//
// Created by Sergey Kazakov on 21/08/2024.
//

import SwiftUI
import Dispatch
import Combine

@available(iOS 13.0, *)
public struct StoreView<ViewState, Action, Props, Content: View>: View {
let store: Store<ViewState, Action>
let props: (ViewState, @escaping Dispatch<Action>) -> Props
let content: (_ props: Props) -> Content

private(set) var removeStateDuplicates: Equating<ViewState>?
private(set) var presentationQueue: DispatchQueue = PresentationQueue.sharedPresentationQueue.dispatchQueue

@State private var currentProps: Props?

public var body: some View {
makeContent()
.effect(on: store,
withDelay: .uiDebounce,
removeStateDuplicates: removeStateDuplicates,
on: presentationQueue) { state in

Effect {
let props = props(state, store.dispatch)
DispatchQueue.main.async { currentProps = props }
}
}
}
}

@available(iOS 13.0, *)
extension StoreView {
@ViewBuilder
func makeContent() -> some View {
if let props = currentProps {
content(props)
} else {
Color.clear
}
}
}

@available(iOS 13.0, *)
public extension StoreView {

func removeStateDuplicates(_ equating: Equating<ViewState>) -> Self {
var selfCopy = self
selfCopy.removeStateDuplicates = equating
return selfCopy
}

func usePresentationQueue(_ queue: PresentationQueue) -> Self {
var selfCopy = self
selfCopy.presentationQueue = queue.dispatchQueue
return selfCopy
}
}

@available(iOS 13.0, *)
public extension StoreView {
init(_ store: Store<ViewState, Action>,
props: @escaping (ViewState, @escaping Dispatch<Action>) -> Props,
content: @escaping (Props) -> Content) {
self.store = store
self.props = props
self.content = content
}
}

@available(iOS 13.0, *)
public extension StoreView where Props == (ViewState, Dispatch<Action>) {

init(_ store: Store<ViewState, Action>,
content: @escaping (Props) -> Content) {
self.store = store
self.props = { state, store in (state, store) }
self.content = content
}
}
119 changes: 119 additions & 0 deletions Sources/Puredux/SwiftUI/ViewsWIthStore/ViewSideEffects.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
//
// File.swift
//
//
// Created by Sergey Kazakov on 21/08/2024.
//

import SwiftUI

@available(iOS 13.0, *)
extension View {


func forEachEffect<ViewState, Action, Effects>(
on store: Store<ViewState, Action>,
_ keyPath: KeyPath<ViewState, Effects>,
on queue: DispatchQueue = .main,
create: @escaping (ViewState, Effect.State) -> Effect) -> some View

where Effects: Collection & Hashable, Effects.Element == Effect.State {

effect(store) { anyObject, store in

store.forEachEffect(
anyObject,
keyPath,
on: queue,
create: create
)
}
}

func effect<ViewState, Action>(on store: Store<ViewState, Action>,
_ keyPath: KeyPath<ViewState, Effect.State>,
on queue: DispatchQueue = .main,
create: @escaping (ViewState) -> Effect) -> some View {

effect(store) { anyObject, store in

store.effect(
anyObject,
keyPath,
on: queue,
create: create
)
}
}

func effect<ViewState, Action, T>(on store: Store<ViewState, Action>,
_ keyPath: KeyPath<ViewState, T>,
on queue: DispatchQueue = .main,
create: @escaping (ViewState) -> Effect) -> some View
where T: Equatable {

effect(store) { anyObject, store in

store.effect(
anyObject,
keyPath,
on: queue,
create: create
)
}
}

func effect<ViewState, Action>(
on store: Store<ViewState, Action>,
_ keyPath: KeyPath<ViewState, Bool>,
on queue: DispatchQueue = .main,
create: @escaping (ViewState) -> Effect) -> some View {

effect(store) { anyObject, store in

store.effect(
anyObject,
keyPath,
on: queue,
create: create
)
}
}
}

@available(iOS 13.0, *)
extension View {
func effect<ViewState, Action>(on store: Store<ViewState, Action>,
withDelay interval: TimeInterval,
removeStateDuplicates: Equating<ViewState>?,
on dispatchQueue: DispatchQueue,
create: @escaping (ViewState) -> Effect) -> some View {

effect(store) { anyObject, store in

store.effect(
anyObject,
withDelay: interval,
removeStateDuplicates: removeStateDuplicates,
on: dispatchQueue,
create: create
)
}
}
}

@available(iOS 13.0, *)
private extension View {
func effect<ViewState, Action>(
_ store: Store<ViewState, Action>,
createEffect: @escaping (AnyObject, Store<ViewState, Action>) -> Void) -> some View {

modifier(
EffectModifier(
store: store,
createEffect: createEffect
)
)
}
}

0 comments on commit 41bfbf8

Please sign in to comment.