Showing preview only (552K chars total). Download the full file or copy to clipboard to get everything.
Repository: muukii/Verge
Branch: main
Commit: 5c8bcd829d57
Files: 199
Total size: 498.0 KB
Directory structure:
gitextract_zegsmnq1/
├── .github/
│ ├── FUNDING.yml
│ └── workflows/
│ └── CommitChecks.yml
├── .gitignore
├── .spi.yml
├── Development/
│ ├── .gitignore
│ ├── Development/
│ │ ├── Resources/
│ │ │ ├── Assets.xcassets/
│ │ │ │ ├── AccentColor.colorset/
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── AppIcon.appiconset/
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ │ └── Preview Content/
│ │ │ └── Preview Assets.xcassets/
│ │ │ └── Contents.json
│ │ ├── Sources/
│ │ │ ├── BookBinding.swift
│ │ │ ├── BookLongList.swift
│ │ │ ├── BookReadingPropertyWrapper.swift
│ │ │ ├── BookStoreReader.swift
│ │ │ ├── ContentView.swift
│ │ │ └── DevelopmentApp.swift
│ │ ├── Tests/
│ │ │ └── DevelopmentTests.swift
│ │ └── UITests/
│ │ ├── ReadingTests.swift
│ │ └── StoreReaderTests.swift
│ ├── DevelopmentUITests/
│ │ ├── DevelopmentUITests.swift
│ │ └── DevelopmentUITestsLaunchTests.swift
│ ├── Project.swift
│ ├── Tuist/
│ │ ├── Package.resolved
│ │ └── Package.swift
│ ├── Tuist.swift
│ └── mise.toml
├── LICENSE
├── Package.resolved
├── Package.swift
├── README.md
├── Sources/
│ ├── Verge/
│ │ ├── Documentation.docc/
│ │ │ ├── Activity.md
│ │ │ ├── Changes.md
│ │ │ ├── ComputedProperty.md
│ │ │ ├── Derived.md
│ │ │ ├── Dispatcher.md
│ │ │ ├── Essentials/
│ │ │ │ └── Motivation.md
│ │ │ ├── Guides/
│ │ │ │ ├── Advanced Usage.md
│ │ │ │ ├── Basic Usage.md
│ │ │ │ └── Migration Guide v9.md
│ │ │ ├── Mutation.md
│ │ │ ├── Resources/
│ │ │ │ └── Tiny.md
│ │ │ ├── State.md
│ │ │ ├── Verge.Store.md
│ │ │ └── Verge.md
│ │ ├── Library/
│ │ │ ├── BackgroundDeallocationQueue.swift
│ │ │ ├── CachedMap.swift
│ │ │ ├── EventEmitter.swift
│ │ │ ├── InoutRef.swift
│ │ │ ├── Log.swift
│ │ │ ├── Signpost.swift
│ │ │ ├── StoreActivitySubscription.swift
│ │ │ ├── StoreStateSubscription.swift
│ │ │ ├── StoreSubscriptionBase.swift
│ │ │ ├── VergeAnyCancellable.swift
│ │ │ ├── VergeConcurrency+SynchronizationTracker.swift
│ │ │ ├── VergeConcurrency.swift
│ │ │ └── _BackingStorage+.swift
│ │ ├── Logging/
│ │ │ ├── ActivityTrace.swift
│ │ │ ├── DefaultStoreLogger.swift
│ │ │ ├── MutationTrace.swift
│ │ │ ├── RuntimeError.swift
│ │ │ ├── RuntimeSanitizer.swift
│ │ │ └── StoreLogger.swift
│ │ ├── Sendable.swift
│ │ ├── Store/
│ │ │ ├── AnyTargetQueue.swift
│ │ │ ├── Changes.swift
│ │ │ ├── DetachedDispatcher.swift
│ │ │ ├── KeyObject.swift
│ │ │ ├── NonAtomicCounter.swift
│ │ │ ├── Pipeline.swift
│ │ │ ├── Scan.swift
│ │ │ ├── StateType.swift
│ │ │ ├── Store+Combine.swift
│ │ │ ├── Store+RunLoop.swift
│ │ │ ├── Store.swift
│ │ │ ├── StoreDriverType+Accumulator.swift
│ │ │ ├── StoreDriverType.swift
│ │ │ ├── StoreMiddleware.swift
│ │ │ ├── StoreOperation.swift
│ │ │ ├── StoreType+Assignee.swift
│ │ │ ├── StoreType+BindingDerived.swift
│ │ │ ├── StoreType+Derived.swift
│ │ │ ├── StoreWrapperType.swift
│ │ │ ├── Transaction.swift
│ │ │ └── UIStateStore.swift
│ │ ├── SwiftUI/
│ │ │ ├── .swift
│ │ │ ├── OnReceive.swift
│ │ │ ├── Reading.swift
│ │ │ ├── StoreObject.swift
│ │ │ └── StoreReader.swift
│ │ ├── Utility/
│ │ │ ├── Edge.swift
│ │ │ ├── ReferenceEdge.swift
│ │ │ └── ThunkToMainActor.swift
│ │ ├── Verge.swift
│ │ └── macros.swift
│ ├── VergeClassic/
│ │ ├── Emitter.swift
│ │ ├── Extensions.swift
│ │ ├── Info.plist
│ │ ├── Storage+Rx.swift
│ │ ├── Storage.swift
│ │ ├── Verge+Extension.swift
│ │ ├── Verge.h
│ │ └── VergeClassic.swift
│ ├── VergeMacros/
│ │ └── Source.swift
│ ├── VergeMacrosPlugin/
│ │ ├── KeyPathMap.swift
│ │ ├── MacroError.swift
│ │ └── Plugin.swift
│ ├── VergeNormalizationDerived/
│ │ ├── DerivedMaking+.swift
│ │ ├── DerivedResult.swift
│ │ ├── EntityType+Typealias.swift
│ │ ├── EntityWrapper.swift
│ │ ├── NonNullEntityWrapper.swift
│ │ ├── Pipelines.swift
│ │ ├── StoreType+.swift
│ │ └── VergeNormalizationDerived.swift
│ ├── VergeRx/
│ │ ├── Extensions.swift
│ │ └── Store+Rx.swift
│ └── VergeTiny/
│ └── Source.swift
├── StoreReaderDemo/
│ └── StoreReaderDemo/
│ ├── Assets.xcassets/
│ │ ├── AccentColor.colorset/
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── ContentView.swift
│ ├── Preview Content/
│ │ └── Preview Assets.xcassets/
│ │ └── Contents.json
│ └── StoreReaderDemoApp.swift
├── TaskManagerPlayground/
│ └── TaskManagerPlayground/
│ ├── Assets.xcassets/
│ │ ├── AccentColor.colorset/
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── Book.swift
│ ├── ContentView.swift
│ ├── Info.plist
│ ├── Preview Content/
│ │ └── Preview Assets.xcassets/
│ │ └── Contents.json
│ └── TaskManagerPlaygroundApp.swift
├── Tests/
│ ├── All.xctestplan
│ ├── DemoState.swift
│ ├── VergeMacrosTests/
│ │ └── KeyPathMapTests.swift
│ ├── VergeNormalizationDerivedTests/
│ │ ├── CombiningTests.swift
│ │ ├── DemoState.swift
│ │ └── VergeNormalizationDerivedTests.swift
│ ├── VergeRxTests/
│ │ ├── ChangedOperatorTests.swift
│ │ ├── DemoState.swift
│ │ ├── ReproduceDeadlockTests.swift
│ │ ├── SubjectCompletionTests.swift
│ │ └── VergeRxTests.swift
│ ├── VergeTests/
│ │ ├── AccumulationTests.swift
│ │ ├── ActivityTests.swift
│ │ ├── BindingDerivedTests.swift
│ │ ├── CachedMapTests.swift
│ │ ├── ChangesTests.swift
│ │ ├── ComparerTests.swift
│ │ ├── ConcurrencyTests.swift
│ │ ├── CopyPerformance.swift
│ │ ├── CounterTests.swift
│ │ ├── DemoState.swift
│ │ ├── DerivedTests.swift
│ │ ├── EdgeTests.swift
│ │ ├── EventEmitterTests.swift
│ │ ├── FilterTests.swift
│ │ ├── IsolatedContextTests.swift
│ │ ├── OldComparer.swift
│ │ ├── PerformanceTests.swift
│ │ ├── PipelineTests.swift
│ │ ├── ReferenceEdgeTests.swift
│ │ ├── Retain/
│ │ │ ├── PublisherCompletionTests.swift
│ │ │ └── StoreSinkSusbscriptionTests.swift
│ │ ├── RunLoopTests.swift
│ │ ├── Sample.swift
│ │ ├── StateTypeTests.swift
│ │ ├── StoreAndDerivedTests.swift
│ │ ├── StoreInitTests.swift
│ │ ├── StoreMiddlewareTests.swift
│ │ ├── StoreSinkTests.swift
│ │ ├── StoreTaskTests.swift
│ │ ├── SynchronizeDisplayValueTests.swift
│ │ ├── SyntaxTests.swift
│ │ ├── TransactionTests.swift
│ │ ├── Usage.swift
│ │ └── VergeStoreTests.swift
│ └── VergeTinyTests/
│ └── VergeTinyTests.swift
├── Verge.playground/
│ ├── Pages/
│ │ ├── Memo.xcplaygroundpage/
│ │ │ └── Contents.swift
│ │ ├── PlainStorePattern1.xcplaygroundpage/
│ │ │ └── Contents.swift
│ │ ├── PlainStorePattern2.xcplaygroundpage/
│ │ │ └── Contents.swift
│ │ ├── PlainStorePattern3.xcplaygroundpage/
│ │ │ └── Contents.swift
│ │ ├── PlainStorePattern4.xcplaygroundpage/
│ │ │ └── Contents.swift
│ │ ├── PlainStorePattern5.xcplaygroundpage/
│ │ │ └── Contents.swift
│ │ ├── VergeStorePartern2.xcplaygroundpage/
│ │ │ └── Contents.swift
│ │ └── VergeStorePattern.xcplaygroundpage/
│ │ └── Contents.swift
│ ├── Sources/
│ │ └── Wire.swift
│ └── contents.xcplayground
├── mise.toml
└── playgrounds/
├── .gitignore
└── PlaySwiftUI/
├── PlaySwiftUI/
│ ├── Assets.xcassets/
│ │ ├── AccentColor.colorset/
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── ContentView.swift
│ ├── PlaySwiftUIApp.swift
│ ├── Preview Content/
│ │ └── Preview Assets.xcassets/
│ │ └── Contents.json
│ └── Simple.swift
└── PlaySwiftUI.xcodeproj/
├── project.pbxproj
└── project.xcworkspace/
├── contents.xcworkspacedata
└── xcshareddata/
└── swiftpm/
└── Package.resolved
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
github: [muukii]
patreon: muukii
ko_fi: muukii
================================================
FILE: .github/workflows/CommitChecks.yml
================================================
name: CommitChecks
on:
push:
branches:
- "**"
jobs:
test:
runs-on: macos-15
steps:
- uses: maxim-lobanov/setup-xcode@v1.1
with:
xcode-version: "26"
- uses: actions/checkout@v2
- name: Run Test
run: set -o pipefail && xcodebuild -scheme Verge-Package test -destination 'platform=iOS Simulator,name=iPhone 17 Pro,OS=26.0' -skipMacroValidation -skipPackagePluginValidation | xcbeautify
ui-test:
runs-on: macos-15
defaults:
run:
working-directory: ./Development
steps:
- uses: maxim-lobanov/setup-xcode@v1.1
with:
xcode-version: "26"
- uses: actions/checkout@v2
- uses: jdx/mise-action@v2
with:
install: true
cache: true
experimental: true
- run: tuist install
- run: tuist generate --no-open
- run: tuist test
================================================
FILE: .gitignore
================================================
# Created by https://www.gitignore.io/api/swift
### Swift ###
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
/worktree
## Build generated
build/
DerivedData/
.swiftpm
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/
## Other
*.moved-aside
*.xccheckout
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM
## Playgrounds
timeline.xctimeline
playground.xcworkspace
# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
.build/
# CocoaPods - Refactored to standalone file
Pods
# Carthage - Refactored to standalone file
Carthage
# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
.DS_Store
# Tuist
Derived
*.xcodeproj
Workspace.xcworkspace/
settings.local.json
# End of https://www.gitignore.io/api/swift
================================================
FILE: .spi.yml
================================================
version: 1
builder:
configs:
- documentation_targets: [Verge, VergeNormalizationDerived]
================================================
FILE: Development/.gitignore
================================================
### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
### Xcode ###
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
## User settings
xcuserdata/
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
*.xcscmblueprint
*.xccheckout
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
build/
DerivedData/
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
### Xcode Patch ###
*.xcodeproj/*
!*.xcodeproj/project.pbxproj
!*.xcodeproj/xcshareddata/
!*.xcworkspace/contents.xcworkspacedata
/*.gcno
### Projects ###
*.xcodeproj
*.xcworkspace
### Tuist derived files ###
graph.dot
Derived/
### Tuist managed dependencies ###
Tuist/.build
================================================
FILE: Development/Development/Resources/Assets.xcassets/AccentColor.colorset/Contents.json
================================================
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
================================================
FILE: Development/Development/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
================================================
FILE: Development/Development/Resources/Assets.xcassets/Contents.json
================================================
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
================================================
FILE: Development/Development/Resources/Preview Content/Preview Assets.xcassets/Contents.json
================================================
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
================================================
FILE: Development/Development/Sources/BookBinding.swift
================================================
import SwiftUI
import Verge
struct BookBindingUsingReading: View {
@ReadingObject<Store<BookBindingState, Never>> var state: BookBindingState
init() {
self._state = .init({
.init(initialState: .init())
})
}
var body: some View {
Counter(value: $state.value)
}
struct Counter: View {
@Binding var value: Int
var body: some View {
VStack {
Text("\(value)")
Button {
value += 1
} label: {
Text("Increment")
}
}
}
}
}
struct BookBindingUsingStoreReader: View {
let store: Store<BookBindingState, Never> = .init(initialState: .init())
init() {
}
var body: some View {
StoreReader(store) { $state in
Counter(value: $state.value)
}
}
struct Counter: View {
@Binding var value: Int
var body: some View {
VStack {
Text("\(value)")
Button {
value += 1
} label: {
Text("Increment")
}
}
}
}
}
@Tracking
struct BookBindingState {
var value: Int = 0
}
#Preview("Binding Reading") {
BookBindingUsingReading()
}
#Preview("Binding StoreReader") {
BookBindingUsingStoreReader()
}
================================================
FILE: Development/Development/Sources/BookLongList.swift
================================================
//
// BookLongList.swift
// Development
//
// Created by Muukii on 2025/03/26.
//
import SwiftUI
import Verge
@Tracking
struct BookState {
var items: [BookItem] = []
init(items: [BookItem]) {
self.items = items
}
}
struct BookItem: Identifiable {
var id: some Hashable {
cellStore
}
let cellStore: Store<BookCellState, Never>
}
@Tracking
struct BookCellState {
let id: Int
var title: String
var isSelected: Bool = false
}
struct BookCellContent: View {
let store: Store<BookCellState, Never>
var body: some View {
StoreReader(store) { $state in
RoundedRectangle(cornerRadius: 8)
.fill(state.isSelected ? Color.red : Color.blue.opacity(0.2))
.aspectRatio(1, contentMode: .fit)
.overlay(
Text("\(state.id + 1)")
.font(.system(size: 16))
)
}
}
}
struct BookCell: View {
let store: Store<BookCellState, Never>
var body: some View {
Button {
store.commit { state in
state.isSelected.toggle()
}
} label: {
BookCellContent(store: store)
}
}
}
struct BookLongList: View {
private let columns = [
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible()),
GridItem(.flexible()),
]
@ReadingObject<Store<BookState, Never>>({
.init(initialState: .init(items: (0..<500).map { index in
BookItem(
cellStore: Store<BookCellState, Never>(
initialState: BookCellState(
id: index,
title: UUID().uuidString
)
)
)
}))
}) var state: BookState
init() {
}
var body: some View {
ScrollView {
LazyVGrid(columns: columns, spacing: 16) {
ForEach(state.items) { item in
BookCell(store: item.cellStore)
}
}
.padding()
}
}
}
#Preview {
BookLongList()
}
================================================
FILE: Development/Development/Sources/BookReadingPropertyWrapper.swift
================================================
import Verge
import SwiftUI
struct BookReading: View {
var body: some View {
ReadingSolution()
}
}
@Tracking
struct MyState {
var value: Int = 0
var a: Int = 0
var b: Int = 0
var c: Int = 0
}
extension Store where State == MyState {
func backgroundUpdate() {
Task {
await self.backgroundCommit {
$0.value += 1
$0.a += 1
$0.b += 1
$0.c += 1
}
}
}
}
enum ItemKind: Identifiable {
case first
case second
case third
var id: String {
switch self {
case .first: return "1"
case .second: return "2"
case .third: return "3"
}
}
}
struct ItemDetail<Item: Identifiable, Content: View>: View {
let items: [Item]
let content: (Item) -> Content
@State var selectedItem: Item?
private let name: String
init(
name: String,
items: [Item],
@ViewBuilder content: @escaping (Item) -> Content
) {
self.name = name
self.items = items
self.content = content
}
var body: some View {
VStack {
ForEach(items) { item in
Button("\(name).\(item.id)") {
selectedItem = item
}
}
if let item = selectedItem {
content(item)
.padding()
.background(Color.orange)
}
}
}
}
private struct ReadingSolution: View {
@State var id: Int = 0
@State var outerValue: Int = 0
init() {
print("init")
}
var body: some View {
VStack {
Button("New") {
id += 1
}
Button("Up Outer") {
outerValue += 1
}
Solution(outerValue: outerValue)
.id(id)
.padding()
.background(Color.green)
}
}
struct Solution: View {
let items: [ItemKind] = [
.first,
.second,
.third,
]
@ReadingObject<Store<MyState, Never>> var state: MyState
private let outerValue: Int
init(outerValue: Int) {
self._state = .init(
label: "A", {
Store<_, Never>.init(initialState: MyState())
}
)
self.outerValue = outerValue
}
var body: some View {
HStack {
VStack {
Text("Using Store holding")
Button("async up") {
$state.driver.backgroundUpdate()
}
Button("A Up") {
$state.driver.commit {
$0.value += 1
}
}
Text("A Value: \(state.value)")
Text("Outer: \(outerValue)")
ItemDetail(name: "A", items: items) { item in
switch item {
case .first:
Button.init("A.1.a: \(state.a)") {
$state.driver.commit {
$0.a += 1
}
}
case .second:
Button.init("A.1.b: \(state.b)") {
$state.driver.commit {
$0.b += 1
}
}
case .third:
Button.init("A.1.c: \(state.c)") {
$state.driver.commit {
$0.c += 1
}
}
}
}
.padding()
.background(Color.yellow)
}
Passed(store: $state.driver)
}
}
}
struct Passed: View {
let items: [ItemKind] = [
.first,
.second,
.third,
]
@Reading<Store<MyState, Never>> var state: MyState
init(
store: Store<MyState, Never>
) {
self._state = .init(label: "B", store)
}
var body: some View {
VStack {
Text("Using Store passed")
Button("B Up") {
$state.driver.commit {
$0.value += 1
}
}
Text("B Value: \(state.value)")
ItemDetail(name: "B",items: items) { item in
switch item {
case .first:
Button.init("B.1.a: \(state.a)") {
$state.driver.commit {
$0.a += 1
}
}
case .second:
Button.init("B.1.b: \(state.b)") {
$state.driver.commit {
$0.b += 1
}
}
case .third:
Button.init("B.1.c: \(state.c)") {
$state.driver.commit {
$0.c += 1
}
}
}
}
.padding()
.background(Color.yellow)
}
}
}
}
struct PassedContainer: View {
private let store: Store<MyState, Never> = .init(initialState: MyState())
@State var count: Int = 0
var body: some View {
VStack {
Button("Outer Up \(count)") {
count += 1
}
ReadingSolution.Passed(store: store)
}
}
}
#Preview("Reading solution") {
ReadingSolution()
}
#Preview("Passed solution") {
PassedContainer()
}
================================================
FILE: Development/Development/Sources/BookStoreReader.swift
================================================
import SwiftUI
import Verge
struct StoreReaderSolution: View {
@State var id: Int = 0
@State var outerValue: Int = 0
init() {
print("init")
}
var body: some View {
VStack {
Button("New") {
id += 1
}
Button("Up Outer") {
outerValue += 1
}
Solution(outerValue: outerValue)
.id(id)
.padding()
.background(Color.green)
}
}
struct Solution: View {
let items: [ItemKind] = [
.first,
.second,
.third,
]
@StoreObject var store = Store<MyState, Never>(initialState: .init())
private let outerValue: Int
init(outerValue: Int) {
self.outerValue = outerValue
}
var body: some View {
HStack {
VStack {
Text("Using Store holding")
StoreReader(store) { $state in
VStack {
Button("A Up") {
store.commit {
$0.value += 1
}
}
Text("A Value: \(state.value)")
Text("Outer: \(outerValue)")
ItemDetail(name: "A", items: items) { item in
switch item {
case .first:
Button("A.1.a: \(state.a)") {
store.commit {
$0.a += 1
}
}
case .second:
Button("A.1.b: \(state.b)") {
store.commit {
$0.b += 1
}
}
case .third:
Button("A.1.c: \(state.c)") {
store.commit {
$0.c += 1
}
}
}
}
.padding()
.background(Color.yellow)
}
}
}
Passed(store: store)
}
}
}
struct Passed: View {
let items: [ItemKind] = [
.first,
.second,
.third,
]
private let store: Store<MyState, Never>
init(store: Store<MyState, Never>) {
self.store = store
}
var body: some View {
VStack {
Text("Using Store passed")
StoreReader(store) { $state in
VStack {
Button("B Up") {
store.commit {
$0.value += 1
}
}
Text("B Value: \(state.value)")
ItemDetail(name: "B", items: items) { item in
switch item {
case .first:
Button("B.1.a: \(state.a)") {
store.commit {
$0.a += 1
}
}
case .second:
Button("B.1.b: \(state.b)") {
store.commit {
$0.b += 1
}
}
case .third:
Button("B.1.c: \(state.c)") {
store.commit {
$0.c += 1
}
}
}
}
.padding()
.background(Color.yellow)
}
}
}
}
}
}
#Preview("Reading solution") {
StoreReaderSolution()
}
================================================
FILE: Development/Development/Sources/ContentView.swift
================================================
import SwiftUI
public struct ContentView: View {
public init() {}
public var body: some View {
NavigationStack {
List {
NavigationLink {
BookReading()
} label: {
Text("@Reading")
}
NavigationLink {
PassedContainer()
} label: {
Text("@Reading - passed")
}
NavigationLink {
StoreReaderSolution()
} label: {
Text("StoreReader")
}
NavigationLink {
BookLongList()
} label: {
Text("Long List")
}
NavigationLink {
BookBindingUsingReading()
} label: {
Text("Binding @Reading")
}
NavigationLink {
BookBindingUsingStoreReader()
} label: {
Text("Binding StoreReader")
}
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
================================================
FILE: Development/Development/Sources/DevelopmentApp.swift
================================================
import SwiftUI
@main
struct DevelopmentApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
================================================
FILE: Development/Development/Tests/DevelopmentTests.swift
================================================
import Foundation
import XCTest
final class DevelopmentTests: XCTestCase {
func test_twoPlusTwo_isFour() {
XCTAssertEqual(2+2, 4)
}
}
================================================
FILE: Development/Development/UITests/ReadingTests.swift
================================================
import XCTest
final class ReadingTests: XCTestCase {
var app: XCUIApplication!
override func setUpWithError() throws {
continueAfterFailure = false
app = XCUIApplication()
app.launch()
}
override func tearDownWithError() throws {
app = nil
}
func testA_up() {
let app = XCUIApplication()
app.collectionViews.buttons[
"@Reading"
]
.tap()
app.buttons["A Up"].tap()
XCTAssertTrue(app.staticTexts["A Value: 1"].exists)
XCTAssertTrue(app.staticTexts["B Value: 1"].exists)
}
func test_sync() {
let app = XCUIApplication()
app.collectionViews.buttons[
"@Reading"
]
.tap()
app.buttons["A.1"].tap()
app.buttons["A.1.a: 0"].tap()
XCTAssertTrue(app.buttons["A.1.a: 1"].exists)
app.buttons["B.1"].tap()
XCTAssertTrue(app.buttons["B.1.a: 1"].exists)
app.buttons["B.1.a: 1"].tap()
XCTAssertTrue(app.buttons["A.1.a: 2"].exists)
XCTAssertTrue(app.buttons["B.1.a: 2"].exists)
}
func test_binding_reading() {
let app = XCUIApplication()
app.collectionViews/*@START_MENU_TOKEN@*/.buttons["Binding @Reading"]/*[[".cells.buttons[\"Binding @Reading\"]",".buttons[\"Binding @Reading\"]"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/.tap()
app.buttons["Increment"].tap()
XCTAssertTrue(app.staticTexts["1"].exists)
}
func test_binding_storeReader() {
let app = XCUIApplication()
app.collectionViews.buttons["Binding StoreReader"].tap()
app.buttons["Increment"].tap()
XCTAssertTrue(app.staticTexts["1"].exists)
}
}
================================================
FILE: Development/Development/UITests/StoreReaderTests.swift
================================================
import XCTest
final class StoreReaderTests: XCTestCase {
var app: XCUIApplication!
override func setUpWithError() throws {
continueAfterFailure = false
app = XCUIApplication()
app.launch()
}
override func tearDownWithError() throws {
app = nil
}
func testA_up() {
let app = XCUIApplication()
app.collectionViews.buttons[
"StoreReader"
]
.tap()
app.buttons["A Up"].tap()
XCTAssertTrue(app.staticTexts["A Value: 1"].exists)
XCTAssertTrue(app.staticTexts["B Value: 1"].exists)
}
func test_sync() {
let app = XCUIApplication()
app.collectionViews.buttons[
"StoreReader"
]
.tap()
app.buttons["A.1"].tap()
app.buttons["A.1.a: 0"].tap()
XCTAssertTrue(app.buttons["A.1.a: 1"].exists)
app.buttons["B.1"].tap()
XCTAssertTrue(app.buttons["B.1.a: 1"].exists)
app.buttons["B.1.a: 1"].tap()
XCTAssertTrue(app.buttons["A.1.a: 2"].exists)
XCTAssertTrue(app.buttons["B.1.a: 2"].exists)
}
}
================================================
FILE: Development/DevelopmentUITests/DevelopmentUITests.swift
================================================
//
// DevelopmentUITests.swift
// DevelopmentUITests
//
// Created by Muukii on 2025/03/22.
//
import XCTest
final class DevelopmentUITests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure = false
// In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
@MainActor
func testExample() throws {
// UI tests must launch the application that they test.
let app = XCUIApplication()
app.launch()
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
@MainActor
func testLaunchPerformance() throws {
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
// This measures how long it takes to launch your application.
measure(metrics: [XCTApplicationLaunchMetric()]) {
XCUIApplication().launch()
}
}
}
}
================================================
FILE: Development/DevelopmentUITests/DevelopmentUITestsLaunchTests.swift
================================================
//
// DevelopmentUITestsLaunchTests.swift
// DevelopmentUITests
//
// Created by Muukii on 2025/03/22.
//
import XCTest
final class DevelopmentUITestsLaunchTests: XCTestCase {
override class var runsForEachTargetApplicationUIConfiguration: Bool {
true
}
override func setUpWithError() throws {
continueAfterFailure = false
}
@MainActor
func testLaunch() throws {
let app = XCUIApplication()
app.launch()
// Insert steps here to perform after app launch but before taking a screenshot,
// such as logging into a test account or navigating somewhere in the app
let attachment = XCTAttachment(screenshot: app.screenshot())
attachment.name = "Launch Screen"
attachment.lifetime = .keepAlways
add(attachment)
}
}
================================================
FILE: Development/Project.swift
================================================
import ProjectDescription
let project = Project(
name: "Development",
targets: [
.target(
name: "Development",
destinations: .iOS,
product: .app,
bundleId: "io.tuist.Development",
infoPlist: .extendingDefault(
with: [
"UILaunchScreen": [
"UIColorName": "",
"UIImageName": "",
]
]
),
sources: ["Development/Sources/**"],
resources: ["Development/Resources/**"],
dependencies: [
.external(name: "Verge")
]
),
.target(
name: "DevelopmentUITests",
destinations: .iOS,
product: .uiTests,
bundleId: "io.tuist.DevelopmentUITests",
infoPlist: .default,
sources: ["Development/UITests/**"],
resources: [],
dependencies: [.target(name: "Development")]
),
.target(
name: "DevelopmentTests",
destinations: .iOS,
product: .unitTests,
bundleId: "io.tuist.DevelopmentTests",
infoPlist: .default,
sources: ["Development/Tests/**"],
resources: [],
dependencies: [.target(name: "Development")]
),
]
)
================================================
FILE: Development/Tuist/Package.resolved
================================================
{
"originHash" : "fb710b9bf77d01a2d48fc3e3b210bfb0e949769f26d1ca1c351ac86995dca6fb",
"pins" : [
{
"identity" : "normalization",
"kind" : "remoteSourceControl",
"location" : "https://github.com/VergeGroup/Normalization",
"state" : {
"revision" : "6e7cb1ddeda4d0f1d2fbf8ca6d25ecd8ed6ba917",
"version" : "1.1.0"
}
},
{
"identity" : "rxswift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ReactiveX/RxSwift.git",
"state" : {
"revision" : "5dd1907d64f0d36f158f61a466bab75067224893",
"version" : "6.9.0"
}
},
{
"identity" : "swift-atomics",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-atomics.git",
"state" : {
"revision" : "cd142fd2f64be2100422d658e7411e39489da985",
"version" : "1.2.0"
}
},
{
"identity" : "swift-collections",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections",
"state" : {
"revision" : "671108c96644956dddcd89dd59c203dcdb36cec7",
"version" : "1.1.4"
}
},
{
"identity" : "swift-concurrency-task-manager",
"kind" : "remoteSourceControl",
"location" : "https://github.com/VergeGroup/swift-concurrency-task-manager",
"state" : {
"revision" : "340cf14e0282977deeeb436605d1810ce4f4fbc9",
"version" : "1.4.0"
}
},
{
"identity" : "swift-macro-state-struct",
"kind" : "remoteSourceControl",
"location" : "https://github.com/VergeGroup/swift-macro-state-struct",
"state" : {
"revision" : "b6ade33024ae04699fa6a4885be70c0299eb3cec",
"version" : "2.0.0"
}
},
{
"identity" : "swift-syntax",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-syntax.git",
"state" : {
"revision" : "0687f71944021d616d34d922343dcef086855920",
"version" : "600.0.1"
}
},
{
"identity" : "typedcomparator",
"kind" : "remoteSourceControl",
"location" : "https://github.com/VergeGroup/TypedComparator",
"state" : {
"revision" : "337ce0e573e7637ddd29392cb88c3b852f042c8e",
"version" : "1.0.0"
}
},
{
"identity" : "typedidentifier",
"kind" : "remoteSourceControl",
"location" : "https://github.com/VergeGroup/TypedIdentifier",
"state" : {
"revision" : "284340409ba47858a1b3c353dcef9c21303953db",
"version" : "2.0.3"
}
}
],
"version" : 3
}
================================================
FILE: Development/Tuist/Package.swift
================================================
// swift-tools-version: 6.0
import PackageDescription
#if TUIST
import struct ProjectDescription.PackageSettings
let packageSettings = PackageSettings(
// Customize the product types for specific package product
// Default is .staticFramework
// productTypes: ["Alamofire": .framework,]
productTypes: [:]
)
#endif
let package = Package(
name: "Development",
dependencies: [
// Add your own dependencies here:
// .package(url: "https://github.com/Alamofire/Alamofire", from: "5.0.0"),
// You can read more about dependencies here: https://docs.tuist.io/documentation/tuist/dependencies
.package(path: "../../")
]
)
================================================
FILE: Development/Tuist.swift
================================================
import ProjectDescription
let tuist = Tuist(project: .tuist())
================================================
FILE: Development/mise.toml
================================================
[tools]
tuist = "4.44.3"
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2017 muukii
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Package.resolved
================================================
{
"originHash" : "e0239f7d7b3b817d553cd88ff38b3bf2eaf6a4d2aa61a19fb57aed0c303e01eb",
"pins" : [
{
"identity" : "normalization",
"kind" : "remoteSourceControl",
"location" : "https://github.com/VergeGroup/Normalization",
"state" : {
"revision" : "0d6adaadd3dcf2aa4c65db8edcf1ea94c73d4a10",
"version" : "2.0.0"
}
},
{
"identity" : "rxswift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ReactiveX/RxSwift.git",
"state" : {
"revision" : "5949cbd3d4f3f97968bb40b6cd232f8315c6341c",
"version" : "6.7.0"
}
},
{
"identity" : "swift-atomics",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-atomics.git",
"state" : {
"revision" : "cd142fd2f64be2100422d658e7411e39489da985",
"version" : "1.2.0"
}
},
{
"identity" : "swift-collections",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-collections",
"state" : {
"revision" : "94cf62b3ba8d4bed62680a282d4c25f9c63c2efb",
"version" : "1.1.0"
}
},
{
"identity" : "swift-concurrency-task-manager",
"kind" : "remoteSourceControl",
"location" : "https://github.com/VergeGroup/swift-concurrency-task-manager",
"state" : {
"revision" : "87605b623fa1a02c657534251ae238a56bc0d15c",
"version" : "3.0.0"
}
},
{
"identity" : "swift-custom-dump",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-custom-dump",
"state" : {
"revision" : "82645ec760917961cfa08c9c0c7104a57a0fa4b1",
"version" : "1.3.3"
}
},
{
"identity" : "swift-macro-state-struct",
"kind" : "remoteSourceControl",
"location" : "https://github.com/VergeGroup/swift-macro-state-struct",
"state" : {
"revision" : "aabd53cd8be48f71bb33e5fe66e38accc0438660",
"version" : "3.0.0"
}
},
{
"identity" : "swift-macro-testing",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-macro-testing.git",
"state" : {
"revision" : "9ab11325daa51c7c5c10fcf16c92bac906717c7e",
"version" : "0.6.4"
}
},
{
"identity" : "swift-snapshot-testing",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-snapshot-testing",
"state" : {
"revision" : "a8b7c5e0ed33d8ab8887d1654d9b59f2cbad529b",
"version" : "1.18.7"
}
},
{
"identity" : "swift-syntax",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftlang/swift-syntax.git",
"state" : {
"revision" : "4799286537280063c85a32f09884cfbca301b1a1",
"version" : "602.0.0"
}
},
{
"identity" : "swift-typed-identifier",
"kind" : "remoteSourceControl",
"location" : "https://github.com/VergeGroup/swift-typed-identifier",
"state" : {
"revision" : "ea7afa2ce943c6bf3ef87d385c8a6e9f67fea4f3",
"version" : "2.0.4"
}
},
{
"identity" : "typedcomparator",
"kind" : "remoteSourceControl",
"location" : "https://github.com/VergeGroup/TypedComparator",
"state" : {
"revision" : "337ce0e573e7637ddd29392cb88c3b852f042c8e",
"version" : "1.0.0"
}
},
{
"identity" : "viewinspector",
"kind" : "remoteSourceControl",
"location" : "https://github.com/nalexn/ViewInspector.git",
"state" : {
"revision" : "5acfa0a3c095ac9ad050abe51c60d1831e8321da",
"version" : "0.10.0"
}
},
{
"identity" : "xctest-dynamic-overlay",
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/xctest-dynamic-overlay",
"state" : {
"revision" : "b2ed9eabefe56202ee4939dd9fc46b6241c88317",
"version" : "1.6.1"
}
}
],
"version" : 3
}
================================================
FILE: Package.swift
================================================
// swift-tools-version: 6.0
import CompilerPluginSupport
import PackageDescription
let package = Package(
name: "Verge",
platforms: [
.macOS(.v13),
.iOS(.v16),
.tvOS(.v13),
.watchOS(.v6),
],
products: [
.library(name: "Verge", targets: ["Verge"]),
.library(name: "VergeTiny", targets: ["VergeTiny"]),
.library(name: "VergeNormalizationDerived", targets: ["VergeNormalizationDerived"]),
.library(name: "VergeRx", targets: ["VergeRx"]),
.library(name: "VergeClassic", targets: ["VergeClassic"]),
.library(name: "VergeMacros", targets: ["VergeMacros"]),
],
dependencies: [
.package(url: "https://github.com/ReactiveX/RxSwift.git", from: "6.0.0"),
.package(url: "https://github.com/apple/swift-atomics.git", from: "1.0.2"),
.package(url: "https://github.com/apple/swift-collections", from: "1.1.0"),
.package(url: "https://github.com/VergeGroup/swift-concurrency-task-manager", from: "3.0.0"),
.package(url: "https://github.com/VergeGroup/TypedComparator", from: "1.0.0"),
.package(url: "https://github.com/VergeGroup/Normalization", from: "2.0.0"),
.package(url: "https://github.com/VergeGroup/swift-macro-state-struct", from: "3.0.0"),
/// for testing
.package(url: "https://github.com/nalexn/ViewInspector.git", from: "0.10.0"),
.package(url: "https://github.com/swiftlang/swift-syntax.git", "600.0.0"..<"603.0.0"),
.package(url: "https://github.com/pointfreeco/swift-macro-testing.git", from: "0.2.1")
],
targets: [
// compiler plugin
.macro(
name: "VergeMacrosPlugin",
dependencies: [
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
]
),
// macro exports
.target(name: "VergeMacros", dependencies: ["VergeMacrosPlugin"]),
.target(name: "VergeTiny", dependencies: []),
.target(
name: "Verge",
dependencies: [
"VergeMacros",
.product(name: "StateStruct", package: "swift-macro-state-struct"),
.product(name: "TypedComparator", package: "TypedComparator"),
.product(name: "Atomics", package: "swift-atomics"),
.product(name: "DequeModule", package: "swift-collections"),
.product(name: "ConcurrencyTaskManager", package: "swift-concurrency-task-manager"),
]
),
.target(
name: "VergeClassic",
dependencies: [
"VergeRx"
]
),
.target(
name: "VergeNormalizationDerived",
dependencies: [
"Verge",
.product(name: "Normalization", package: "Normalization"),
.product(name: "HashTreeCollections", package: "swift-collections"),
]
),
.target(
name: "VergeRx",
dependencies: [
"Verge",
.product(name: "RxSwift", package: "RxSwift"),
.product(name: "RxCocoa", package: "RxSwift"),
]
),
.testTarget(
name: "VergeNormalizationDerivedTests",
dependencies: ["VergeNormalizationDerived"]
),
.testTarget(
name: "VergeRxTests",
dependencies: ["VergeRx", "VergeClassic"]
),
.testTarget(
name: "VergeTests",
dependencies: ["Verge", "ViewInspector"],
swiftSettings: [
.enableExperimentalFeature("StrictConcurrency")
]
),
.testTarget(
name: "VergeTinyTests",
dependencies: ["VergeTiny"]
),
.testTarget(
name: "VergeMacrosTests",
dependencies: [
"VergeMacrosPlugin",
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
.product(name: "MacroTesting", package: "swift-macro-testing"),
]),
],
swiftLanguageModes: [.v6]
)
================================================
FILE: README.md
================================================
> [!WARNING]
> Verge is stabilizing and transitioning to [swift-state-graph](https://github.com/VergeGroup/swift-state-graph) as its next-generation foundation.
> Please check this out
> https://medium.com/p/8c7423692992
<p align="center">
<a href="https://vergegroup.org">
<img width="128" alt="VergeIcon" src="https://user-images.githubusercontent.com/1888355/85241314-5ac19c80-b476-11ea-9be6-e04ed3bc6994.png">
</a>
</p>
<h1 align="center">Verge.swift</h1>
<p align="center">
<b>📍An effective state management architecture for iOS - UIKit, SwiftUI📍</b><br/>
<sub>_ An easier way to get unidirectional data flow _</sub><br/>
<sub>_ Supports concurrent processing _</sub><br/>
</p>
[My development note](https://www.notion.so/muukii/Verge-93813aec77d44bef802a2d92f565c7bb?pvs=4)
## Using StoreReader or @Reading in SwiftUI
In SwiftUI, there are two ways to observe a Store: using the `StoreReader` view or the `@Reading` property wrapper. Both efficiently track state changes and only re-render the view when tracked values change.
First, define your state with `@Tracking` macro:
```swift
@Tracking
struct State {
var count: Int = 0
@Tracking
struct NestedState {
var isActive: Bool = false
var message: String = "Hello, Verge!"
}
var nestedState: NestedState = NestedState()
}
```
### StoreReader Example
```swift
struct MyView: View {
let store = Store<State, Never>(initialState: .init())
var body: some View {
StoreReader(store) { $state in
VStack {
Text("Count: \(state.count)")
Button("Increment") {
store.commit {
$0.count += 1
}
}
Text("Is Active: \(state.nestedState.isActive)")
Text("Message: \(state.nestedState.message)")
Button("Toggle Active") {
store.commit {
$0.nestedState.isActive.toggle()
}
}
}
}
}
}
```
### @Reading Example
```swift
struct MyView: View {
@Reading<Store<State, Never>> var state: State
init() {
self._state = .init(
label: "MyView",
{ Store<State, Never>(initialState: .init()) }
)
}
var body: some View {
VStack {
Text("Count: \(state.count)")
Button("Increment") {
$state.driver.commit {
$0.count += 1
}
}
Text("Is Active: \(state.nestedState.isActive)")
Text("Message: \(state.nestedState.message)")
Button("Toggle Active") {
$state.driver.commit {
$0.nestedState.isActive.toggle()
}
}
}
}
}
```
---
- Dependencies
- [TypedIdentifier](https://github.com/VergeGroup/TypedIdentifier)
- [TypedComparator](https://github.com/VergeGroup/TypedComparator)
- [Normalization](https://github.com/VergeGroup/Normalization)
- Docs
- [Verge](https://swiftpackageindex.com/VergeGroup/swift-Verge/main/documentation/verge)
- [VergeNormalizationDerived](https://swiftpackageindex.com/VergeGroup/swift-Verge/main/documentation/vergenormalizationderived)
## Support this projects
<a href="https://www.buymeacoffee.com/muukii">
<img width="160" alt="yellow-button" src="https://user-images.githubusercontent.com/1888355/146226808-eb2e9ee0-c6bd-44a2-a330-3bbc8a6244cf.png">
</a>
# Verge: A High-Performance, Scalable State Management Library for SwiftUI and UIKit
Verge is a high-performance, scalable state management library for Swift, designed with real-world use cases in mind. It offers a lightweight and easy-to-use approach to managing your application state without the need for complex actions and reducers. This guide will walk you through the basics of using Verge in your Swift projects.
## Key Concepts and Motivations
Verge was designed with the following concepts in mind:
- Inspired by the Flux library, but with a focus on providing a store-pattern as the core concept.
- The store-pattern is a primitive concept found in Flux and Redux, focusing on sharing state between components using a single source of truth.
- Verge does not dictate how to manage actions to modify the state. Instead, it provides a simple `commit` function that accepts a closure describing how to change the state.
- Users can build additional layers on top of Verge, such as implementing enum-based actions for more structured state management.
- Verge supports multi-threading, ensuring fast, safe, and efficient operation.
- Compatible with both UIKit and SwiftUI.
- Includes APIs for handling real-world application development use cases, such as managing asynchronous operations.
- Addresses the complexity of updating state in large and complex applications.
- Provides an ORM for efficient management of a large number of entities.
- Designed for use in business-focused applications.
## Getting Started
To use Verge, follow these steps:
1. Define a state struct with `@Tracking` macro
2. Instantiate a `Store` with your initial state
3. Update the state using the `commit` method on the store instance
4. Subscribe to state updates using the `sinkState` method
## Defining Your State
Create a state struct that represents the state of your application. Use the `@Tracking` macro to make your state trackable by Verge. This allows Verge to detect changes in your state and trigger updates as necessary.
```swift
@Tracking
struct MyState {
var count: Int = 0
}
```
## Instantiating a Store
Create a `Store` instance with the initial state of your application. The `Store` class takes two type parameters:
- The first type parameter represents the state of your application.
- The second type parameter represents any activity you want to use with your store. If you don't need any activity, use `Never`.
```swift
let store = Store<_, Never>(initialState: MyState())
```
## Updating the State
To update your application state, use the `commit` method on your `Store` instance. The `commit` method takes a closure with a single parameter, which is a mutable reference to your state. Inside the closure, modify the state as needed.
```swift
store.commit {
$0.count += 1
}
```
## Subscribing to State Updates
To receive updates when the state changes, use the `sinkState` method on your `Store` instance. This method takes a closure that receives the updated state as its parameter. The closure will be called whenever the state changes.
```swift
store.sinkState { state in
// Receives updates of the state
}
.storeWhileSourceActive()
```
The `storeWhileSourceActive()` call at the end is a method provided by Verge to automatically manage the lifetime of the subscription. It retains the subscription as long as the source (in this case, the `store` instance) is alive.
## Using Activity of Store for Event-Driven Programming
In certain scenarios, event-driven programming is essential for creating responsive and efficient applications. The Verge library's Activity of Store feature is designed to cater to this need, allowing developers to handle events seamlessly within their projects.
The Activity of Store comes into play when your application requires event-driven programming. It enables you to manage events and associated logic independently from the main store management, promoting a clean and organized code structure. This separation of concerns simplifies the overall development process and makes it easier to maintain and extend your application over time.
By leveraging the Activity of Store functionality, you can efficiently handle events within your application while keeping the store management intact. This ensures that your application remains performant and scalable, enabling you to build robust and reliable Swift applications using the Verge library.
Here's an example of using Activity of Store:
```swift
let store: Store<MyState, MyActivity>
store.send(MyActivity.somethingHappened)
```
```swift
store.sinkActivity { (activity: MyActivity) in
// handle activities.
}
.storeWhileSourceActive()
```
## Using Verge with SwiftUI
To use Verge in SwiftUI, you can utilize the `StoreReader` to subscribe to state updates within your SwiftUI views. Here's an example of how to do this:
```swift
import SwiftUI
import Verge
struct ContentView: View {
@StoreObject private var viewModel = CounterViewModel()
var body: some View {
VStack {
StoreReader(viewModel) { state in
Text("Count: \(state.count)")
.font(.largeTitle)
}
Button(action: {
viewModel.increment()
}) {
Text("Increment")
}
}
}
}
final class CounterViewModel: StoreComponentType {
@Tracking
struct State {
var count: Int = 0
}
let store: Store<State, Never> = .init(initialState: .init())
func increment() {
commit {
$0.count += 1
}
}
}
```
In this example, `StoreReader` is used to read the state from the `MyViewModel` store. This allows you to access and display the state within your SwiftUI view. Additionally, you can perform actions by calling methods on the store directly, as demonstrated with the button in the example.
This new section will help users understand how to use Verge with SwiftUI, allowing them to manage state effectively within their SwiftUI views. Let me know if you have any further suggestions or changes!
**StoreObject** property wrapper:
SwiftUI provides the `@StateObject` property wrapper to create and manage a persistent instance of a given object that adheres to the ObservableObject protocol. However, StateObject will cause the view to be refreshed whenever the ObservableObject is updated.
In Verge, we introduce the StoreObject property wrapper, which instantiates a Store object for the duration of the view's lifecycle but does not cause the view to refresh when the Store updates.
This is beneficial when you want to manage the Store in a more granular way, without causing the entire view to refresh when the Store changes. Instead, Store updates can be handled through the StoreReader.
## Using Verge with UIKit
Here's a simple usage example of Verge with a UIViewController:
```swift
class MyViewController: UIViewController {
@Tracking
private struct State {
var count: Int = 0
}
private let store: Store<State, Never> = .init(initialState: .init())
private let label: UILabel = .init()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
// Subscribe to the store's state updates
store.sinkState { [weak self] state in
guard let self = self else { return }
// Check if the value has been updated using ifChanged
state.ifChanged(\.count) { count in
self.label.text = "Count: \(count)"
}
}
.storeWhileSourceActive()
}
private func setupUI() {
// Omitted for brevity
}
private func incrementCount() {
store.commit {
$0.count += 1
}
}
}
```
## Efficient State Updates in UIKit using `sinkState`, `Changed<State>`, and `ifChanged`
In UIKit, which is event-driven, it's crucial to update components efficiently by only updating them as needed. The Verge library provides a way to achieve this using the `sinkState` method, the `Changed<State>` type, and the `ifChanged` method.
When you use the `sinkState` method, the closure you provide receives the latest state wrapped in a `Changed<State>` type. This wrapper also includes the previous state, allowing you to determine which properties have been updated using the `ifChanged` method.
Here's an example of using `sinkState` and `ifChanged` in UIKit to efficiently update components:
```swift
store.sinkState {
$0.ifChanged(\.myProperty) { newValue in
// Update the component only when myProperty has changed
}
}
```
In this example, the component is updated only when `myProperty` has changed, ensuring efficient updates in the UIKit-based application.
Compared to UIKit, SwiftUI works with a declarative view structure, which means that there is less need to check for state changes to update the view. However, when working with UIKit, using `sinkState`, `Changed<State>`, and `ifChanged` helps maintain a performant and responsive application.
## Using TaskManager for Asynchronous Operations
Verge's Store includes a TaskManager that allows you to dispatch and manage asynchronous operations. This feature simplifies handling async tasks while keeping them associated with your Store.
### Basic usage
To use TaskManager, simply call the `task` method on your Store instance, and provide a closure that contains the asynchronous operation:
```swift
store.task {
await runMyOperation()
}
```
### Task management with keys and modes
TaskManager also enables you to manage tasks based on keys and modes. You can assign a unique key to each task and specify a mode for its execution. This allows you to control the execution behavior of tasks based on their keys.
For example, you can use the `.dropCurrent` mode to drop any currently running tasks with the same key and run the new task immediately:
```swift
store.task(key: .init("MyOperation"), mode: .dropCurrent) {
//
}
```
This functionality provides you with fine-grained control over how tasks are executed, ensuring that your application remains responsive and efficient, even when handling multiple asynchronous operations.
## Advanced Usage: Managing Multiple Stores for Complex Applications
In theory, managing your entire application state in a single store is ideal. However, in large and complex applications, the computational complexity can become significant, leading to performance issues and slow application responsiveness. In such cases, it's recommended to separate your state into multiple stores and integrate them as needed.
By dividing your state into multiple stores, you can reduce the complexity and overhead associated with state updates. Each store can manage a specific part of your application state, ensuring that updates are performed efficiently and quickly. This approach also promotes better organization and separation of concerns in your code, making it easier to maintain and extend your application over time.
To use multiple stores, create separate Store instances for different parts of your application state, and then connect them as needed. This may involve passing store instances to child components or sharing stores between sibling components. By structuring your application this way, you can ensure that each part of your application state is managed efficiently and effectively.
### Copying State Between Stores
To copy state between stores, you can use the `sinkState` method along with the `ifChanged` function to only trigger updates when the state has changed. Here's an example:
```swift
store.sinkState {
$0.ifChanged(\.myState) { value in
otherStore.commit {
$0.myState = value
}
}
}
```
In this example, when the state of `myState` changes in `store`, the new value is committed to `otherStore`. This approach allows you to synchronize state between multiple stores efficiently.
## Using Derived for Efficient Computed Properties
Verge's `Derived` feature allows you to create computed properties based on your store's state and efficiently subscribe to updates. This feature can help you optimize your application by reducing unnecessary computations and updates. Derived is inspired by the [reselect](https://github.com/reduxjs/reselect) library and provides similar functionality.
### Creating a Derived Property
To create a derived property, you'll use the `store.derived` method. This method takes a `Pipeline` object that describes how the derived data is generated:
```swift
let derived: Derived<Int> = store.derived(.select(\\.count))
```
You can use `select` or `map` to generate derived data. `select` is used to take a value directly from the state, while `map` can be used to generate new values based on the state, similar to a map function:
```swift
let derived: Derived<Int> = store.derived(.map { $0.count * 2 })
```
The `Pipeline` checks if the derived data has been updated from the previous value. If it hasn't changed, `Derived` won't publish any changes.
### Chaining Derived Instances
You can create another Derived instance from an existing Derived instance, effectively chaining them together:
```swift
let anotherDerived: Derived<String> = derived.derived(.map { $0.description })
```
### Subscribing to Derived Property Updates
To subscribe to updates of a derived property, you can use the `sinkState` method, just like with a store:
```swift
derived.sinkState { value in
// Handle updates of the derived property
}
.storeWhileSourceActive()
```
By using `Derived` for computed properties and subscribing to updates, you can ensure that your application remains efficient and performant, avoiding unnecessary computations and state updates.
# Normalization
VergeGroup and Verge package provide a library for normalization techniques to handle entities in an efficient way.
[Normalization](https://github.com/VergeGroup/Normalization) is a library that makes tables in a struct and manages value-type entities in copy-efficient tables.
It can be used in the same way as handling value types. Which means we can use it with Verge bringing them into store-pattern.
`VergeNormalizationDerived` target provides functionalities for subscribing entity updates when it's using with Verge.
# Installation
## SwiftPM
Verge supports SwiftPM.
## Thanks
- [Redux](https://redux.js.org/)
- [Vuex](https://vuex.vuejs.org/)
- [ReSwift](https://github.com/ReSwift/ReSwift)
- [swift-composable-architecture](https://github.com/pointfreeco/swift-composable-architecture)
## Author
[🇯🇵 Muukii (Hiroshi Kimura)](https://github.com/muukii)
## License
Verge is released under the MIT license.
================================================
FILE: Sources/Verge/Documentation.docc/Activity.md
================================================
# Activity
## What Activity brings to us
Activity enables Event-driven partially.
Verge supports to send any events that won’t be stored persistently. Even if an application runs with State-Driven, it might have some issues that not easy to something with State-Driven.
For example, something that would happen with the timer’s trigger. It’s probably not easy to expressing that as a state.
In this case, Activity helps that can do easily.
This means Verge can use Event-Driven from Data-Driven partially.
We think it’s not so special concept. SwiftUI supports these use cases as well that using Combine’s Publisher.
```swift
func onReceive<P>(_ publisher: P, perform action: @escaping (P.Output) -> Void) -> some View where P : Publisher, P.Failure == Never
```
[Apple’s SwiftUI Ref](https://developer.apple.com/documentation/swiftui/view/3365935-onreceive)
## Add Activity to the Store
In sample code following this:
```swift
final class MyStore: StoreComponentType {
struct State {
...
}
}
```
To enable using Activity, we add new decralation just like this:
```swift
final class MyStore: StoreComponentType {
struct State {
...
}
/// 👇
enum Activity {
case didSendMessage
}
}
```
## Send an Activity
And finally, that Store now can emit an activity that we created.
```swift
extension MyStore {
func sendMessage() {
send(.didSendMessage)
}
}
```
---
## Subscribe the Activity
**Normal**
```swift
store.sinkActivity { activity in
...
}
.store(in: &subscriptions)
```
**Using Combine**
```swift
store
.activityPublisher
.sink { event in
// do something
}
.store(in: &subscriptions)
```
================================================
FILE: Sources/Verge/Documentation.docc/Changes.md
================================================
# ``Verge/Changes``
`Changes<State>` wraps primitive value inside and previous one.
This data structure enables to get differences between now one and previous one.
It helps us to prevent duplicated operation with the same value from the state.

## How we update UI with the state uniquely in UIKit
In subscribing the state and binding UI, it’s most important to reduce the meaningless time to update UI.
What things are the meaningless? that is the update UI operations which contains no updates.
Basically, we can prevent this with like followings:
```swift
func updateUI(newState: State) {
if self.label.text != newState.name {
self.label.text = newState.name
}
}
```
Although, this approach make the code a little bit complicated by increasing the code that updates UI.
Especially, same words come up a lot.
## Changes simplify the code
Actually, state property of Store returns `Changes<State>` implicitly.
From the power of `dynamicMemberLookup`, we can use the property of the State with we don’t know that is actually Changes object.
How to know there is differences is using `ifChanged`.
```swift
let store: MyStore
let state: Changes<MyState> = store.state
state.ifChanged(\.name) { name in
// called when `name` changed only
}
```
## Patterns of `ifChanged`
`ifChanged` has a bunch of overloads.
Let’s take a look of patterns.
```swift
state.ifChanged(\.aaa, \.bbb) { aaa, bbb in
// Executes every two properties change.
}
```
```swift
state.ifChanged({ "Mr." + $0.name }) { composedName in
}
```
```swift
state.ifChanged({ "Mr." + $0.name }, { $0 == $1 }) { aaa, bbb in
}
```
```swift
state.ifChanged({ ($0.aaa, $0.bbb, "Mr" + $0.name)}, ==) { composedName in
// Returning tuple that contains composed value enables to do anything with the value you want.
// It releases us from complicated streaming mixing.
// Current Swift version does not support tuple conforming with Equatable,
// You need passing `==` function in the second argument.
}
```
## Subscribing the state
```swift
class ViewController: UIViewController {
var subscriptions = Set<UntilDeinitCancellable>()
let store: MyStore
override func viewDidLoad() {
super.viewDidLoad()
store.sinkChanges { [weak self] (changes) in
// it will be called on the thread which committed
self?.update(changes: changes)
}
.store(in: &subscriptions)
}
private func update(changes: Changes<MyState> {
changes.ifChanged(\.name) { name in
// called only name changed
}
...
}
}
```
## Make Changes object the first-time value
If you have a `Changes` from anywhere, it might have previous value,
Using `ifChanged` might return false because compared with the previous one.
You can create the Changed object that always returns true from `ifChanged` with followings:
```swift
let changes: Changes<State>
let firstTimeChanges: Changes<State> = changes.droppedPrevious()
```# <#Title#>
================================================
FILE: Sources/Verge/Documentation.docc/ComputedProperty.md
================================================
# Computed Property
## Overview
A declaration to add a computed-property into the state. It helps to add a property that does not need to be stored-property. It’s like Swift’s computed property like following:
```swift
struct State {
var items: [Item] = [] {
var itemsCount: Int {
items.count
}
}
```
However, this Swift’s computed-property will compute the value every state changed. It might become a serious issue on performance.
Compared with Swift’s computed property and this, this does not compute the value every state changes, It does compute depend on specified rules. That rules mainly come from the concept of Memoization.
Example code:
```swift
struct State: ExtendedStateType {
var name: String = ...
var items: [Int] = []
struct Extended: ExtendedType {
static let instance = Extended()
let filteredArray = Field.Computed<[Int]> {
$0.items.filter { $0 > 300 }
}
.dropsInput {
$0.noChanges(\.items)
}
}
}
```
```swift
let store: MyStore<State, Never> = ...
let state = store.state
let result: [Int] = state.computed.filteredArray
```
## Instructions
### Computed Property on State
States may have a property that actually does not need to be stored property. In that case, we can use computed property.
Although, we should take care of the cost of the computing to return value in that. What is that case? Followings explains that.
<aside>
💡 Computed concept is inspired from Vuex Getters. [https://vuex.vuejs.org/guide/getters.html](https://vuex.vuejs.org/guide/getters.html)
</aside>
For example, there is itemsCount.
```swift
struct State {
var items: [Item] = []
var itemsCount: Int = 0
}
```
In order to become itemsCount dynamic value, it needs to be updated with updating items like this.
```swift
struct State {
var items: [Item] = [] {
didSet {
itemsCount = items.count
}
}
var itemsCount: Int = 0
}
```
We got it, but we don’t think it’s pretty simple. Actually we can do this like this.
```swift
struct State {
var items: [Item] = [] {
var itemsCount: Int {
items.count
}
}
```
With this, it did get to be more simple.
```swift
struct State {
var items: [Item] = []
var processedItems: [ProcessedItem] {
items.map { $0.doSomeExpensiveProcessing() }
}
}
```
As an example, Item can be processed with the something operation that takes expensive cost. We can replace this example with filter function.
This code looks is very simple and it has got data from source of truth. Every time we can get correct data. However we can look this takes a lot of the computing resources. In this case, it would be better to use didSet and update data.
```swift
struct State {
var items: [Item] = [] {
didSet {
processedItems = items.map { $0.doSomeExpensiveProcessing() }
}
}
var processedItems: [ProcessedItem] = []
}
```
However, as we said, this approach is not simple. And this can not handle easily a case that combining from multiple stored property. Next introduces one of the solutions.
## Extended Computed Properties
VergeStore has a way of providing computed property with caching to reduce taking computing resource.
Keywords are:
- `ExtendedStateType`
- `ExtendedType`
- `Field.Computed<T>`
Above State code can be improved like following.
```swift
struct State: ExtendedStateType {
var name: String = ...
var items: [Int] = []
struct Extended: ExtendedType {
static let instance = Extended()
let filteredArray = Field.Computed<[Int]> {
$0.items.filter { $0 > 300 }
}
.dropsInput {
$0.noChanges(\.items)
}
}
}
```
To access that computed property, we can do the followings:
```swift
let store: MyStore<State, Never> = ...
let state = store.state
let result: [Int] = state.computed.filteredArray
```
`store.computed.filteredArray` will be updated only when items updated. Since the results are stored as a cache, we can take value without computing.
Followings are the steps describes when it computes while paying the cost.
```swift
let store: MyStore<State, Never> = ...
// It computes
store.state.computed.filteredArray
// no computes because results cached with first-time access
store.state.computed.filteredArray
// State will change but no affects items
store.commit {
$0.name = "Muukii"
}
// no computes because results cached with first-time access
store.state.computed.filteredArray
// State will change with it affects items
store.commit {
$0.items.append(...)
}
// It computes new value
store.state.computed.filteredArray
```# <#Title#>
================================================
FILE: Sources/Verge/Documentation.docc/Derived.md
================================================
# ``Verge/Derived``
Derived’s functions are:
- Computes the derived data from the state tree
- Emit the updated data with updating Store
- Supports subscribe the data
- Supports Memoization
- Compatible with SwiftUI’s observableObject and `StateReader`
## Overview
### Create a Derived object from the Store
**Select a tree from the state**
```swift
let derived: Derived<Int> = store.derived(.map(\.count))
```
```swift
// we can write also this.
// However, we recommend do above way as possible
// because it enables cache.
let derived: Derived<Int> = store.derived(.map { $0.count })
```
**Technically, above method callings are produced from below declaration.**
```swift
extension StoreType {
public func derived<NewState>(
_ memoizeMap: MemoizeMap<Changes<State>, NewState>,
dropsOutput: ((Changes<NewState>) -> Bool)? = nil,
queue: TargetQueue? = nil
) -> Derived<NewState>
}
```
`MemoizeMap` manages to transform value from the state and keep performance that way of drops transform operations if the input value no changes.
**Compute a value from the state**
Derived can create any type of value what we need. MemoizeMap
```swift
let derived = store.derived(
.map(derive: { ($0.name, $0.age) },
dropsDerived: ==
) { args in
let (name, age) = args
...
return ...
})
```
<aside>
💡 This method is quite optimized the performance If you create a Derived object that computes a new shape value that using multiple values from the state.
Because Derived object uses the specified derived value to create a new shape value, It can detect no need to compute that value if the input derived value not changed.
</aside>
**Most manually way of creating a Derived object**
We can create fully tuned up Derived object with using custom initialized `MemoizedMap`. Most of the cases, we don’t need to do this. Because several overloaded methods enable optimizations automatically that depending on doing things. Verge shows current optimization status from the Complexity column of Xcode documentation.

## Take a value
Derived is an object (reference type). It provides a latest value from a store. This supports getting the value ad-hoc or subscribing the value updating.
Derived allows us to take the latest value at the time.
```
let value: Changes<Int> = derived.value
```
## Subscribe the latest value Derived provides
Derived allows us to subscribe to the updated value.
```swift
let cancellable = derived.sinkValue { (changes: Changes<Int>) in
}
```
Please, carefully handle a cancellable object.
A concealable object that returns that subscribe method is similar to AnyCancellable of Combine.framework.
We need to retain that until we don’t need to get the update event.
## Supports other Reactive Frameworks
We might need to use some Reactive framework to integrate other sequence. Derived allows us to make to a sequence from itself. Currently, it supports Combine.framework and RxSwift.framework.
### + Combine
```swift
derived
.valuePublisher()
.sink { (changes: Changes<Int>) in
}
```
### + RxSwift
💡You need to install VergeRx module to use this.
```swift
derived.rx
.changesObservable()
.subscribe(onNext: { (changes: Changes<Int>) in
})
```
## Memoization to keep good performance
Mostly Derived is used for projecting the specified shape from the source object. And some cases may contain an expensive operation. In that case, we can consider to tune Memoization up. We can see the detail of Memoization from below link.
[Wiki - Memoization](https://en.wikipedia.org/wiki/Memoization)
## Skips the map operation if the source state has no changes
In create Derived method, we can get the detail that how we suppress the no need updating and updated event.
```swift
extension StoreType {
public func derived<NewState>(
_ memoizeMap: MemoizeMap<Changes<State>, NewState>,
dropsOutput: @escaping (Changes<NewState>) -> Bool = { _ in false }
) -> Derived<NewState>
}
```
================================================
FILE: Sources/Verge/Documentation.docc/Dispatcher.md
================================================
# ``Verge/DispatcherType``
Dispatcher allows us to update the state of the Store from away the store and to manage dependencies to create Mutation.
- Dispatcher is
- it is an object.
- it does not have its own state.
- it can run commit with specified store’s state.
- it can have a temporary dependency to commit the mutation.
- it can focus on specified tree of the state.
Here is an example store, in this section we create a dispatcher to commit the mutation into this Store:
```swift
struct State: StateType {
var count: Int = 0
}
enum Activity {
case happen
}
final class MyStore: Store<State, Activity> {
...
}
```
MyStore has a typealias to define a dispatcher:
```swift
MyStore.Dispatcher
```
<aside>
💡 Actual type of `MyStore.Dispatcher` is `DispatcherBase<State, Never>` It is a typealias to write shortly.
</aside>
## Define a dispatcher
Let’s take a look how we create a dispatcher with using the typealias:
```swift
final class MyDispatcher: MyStore.Dispatcher {
}
```
Now we can create an instance of `MyDispatcher`:
```swift
let store = MyStore()
let dispatcher = MyDispatcher(targetStore: store)
```
## Add an action to the dispatcher
Next, we add an action that commits a mutation:
```swift
final class MyDispatcher: MyStore.Dispatcher {
func doSomething() {
commit {
$0.count = 100
}
}
}
```
```swift
let store: MyStore
let dispatcher: MyDispatcher
dispatcher.doSomething()
store.state.count == 100 // true
```
## Add a dependency to dispatch an action
In the case of large applications, we need to handle many dependencies to run the application.
For example, if we use multiple HTTP clients.
```swift
final class MyDispatcher: MyStore.Dispatcher {
let apiClient: APIClient
init(apiClient: APIClient, targetStore: Store<RootState>) {
self.apiClient = apiClient
super.init(targetStore: targetStore)
}
// an example of fetching data and commit
func fetchData() {
apiClient.fetchData { [weak self] result in
switch result {
case .success(let data):
let items = data.encode(...)
self?.commit {
$0.fetchedItems = items
}
case .failure(let error):
// handles error
}
}
}
}
```
To use this:
```swift
let store = MyStore()
let apiClient = APIClient()
let dispatcher = MyDispatcher(apiClient: apiClient, target: store)
dispatcher.fetchData()
```
Now we can handle multiple kinds of dependencies each it fits itself life-time.
For example, if you have a restriction that some dependencies can be created only the user’s logging-in, you can create a dispatcher what is for.
Next section explains the detail.
## Create multiple Dispatcher

Creating a dispatcher does not have the restriction of the number of instances or types.
This means that it allows us to define a dispatcher and instantiate an instance of the dispatcher to fill the use-case.
For example, In case the timing of getting dependencies that to be needed by run Action or Mutation is different, it’s not easy to have them in the one dispatcher with type-safety. they must be optional types.
Using creating multiple dispatchers techniques solves this case by defines the dispatcher each the timing of getting dependencies.
```swift
class LoggedInDispatcher: MyStore.Dispatcher {
let apiClientNeedsAuthToken: APIClient = ...
...
}
class LoggedOutDispatcher: MyStore.Dispatcher {
let apiClientWithoutAuthToken: APIClient = ...
...
}
```
```swift
let store = MyStore()
let loggedInDispatcher = LoggedInDispatcher(...)
let loggedOutDispatcher = LoggedOutDispatcher(...)
```
The application will create `LoggedInDispatcher` when the user is logged-in and deinitialize `LoggedOutDispatcher`.
## Create scoped dispatcher
As another feature what Dispatcher provides, It supports to commit specified scope of the state which helps to mutate with focus on a part of the large state tree.
Here is a sample state that assuming a large app.
- AppState (MyStore)
- db: Database
- loggedIn: LoggedInState
- myInfo: MyInfoState
- loggedOut: LoggedOutState
We have `database`, `logged-in` and `logged-out state`. `database` means normalized state shape to manage many entities.
In previous section, that explained we can multiple dispatchers each the user’s state.
However, it needs to the full path to mutate where we need to mutate.
```swift
class LoggedInDispatcher: MyStore.Dispatcher {
func performA() {
commit {
$0.loggedIn.xxx
}
}
func performB() {
commit {
$0.loggedIn.xxx
}
}
func performC() {
commit {
$0.loggedIn.xxx
}
}
}
```
LoggedInDispatcher will often dispatch some action for logged-in-state.
But it calls everytime `$0.loggedIn`, it seems a little bit verbosity.
That will be solved by `ScopedDispatcher`. It will move on target tree of the state when it dispatch the action.
The following code shows how we could create a `ScopedDispatcher`:
```swift
final class LoggedInService: MyStore.ScopedDispatcher<LoggedInState> {
init(store: Store) {
super.init(targetStore: store, scope: \.loggedIn)
}
func someOperation() {
commit { (state: LoggedInState) in
...
}
}
}
```
In LoggedInService, commit mutates `LoggedInState` directly. Like this, we can create a dispatcher each use-cases.
### Detaching to other tree
Just in case, `ScopedDispatcher` supports also mutating on other state tree.
**Moving on more deeper**
```swift
final class LoggedInService: MyStore.ScopedDispatcher<LoggedInState> {
func detachingOperation() {
let myInfo = detached(by: \.myInfo)
myInfo.commit { (state: MyInfo) in
}
}
}
```
**Detaches from root**
```swift
final class LoggedInService: MyStore.ScopedDispatcher<LoggedInState> {
func detachingOperation() {
let db = detached(from: \.db)
db.commit { (state: Database) in
}
}
}
```
================================================
FILE: Sources/Verge/Documentation.docc/Essentials/Motivation.md
================================================
# Motivation
## Verge focuses use-cases in the real-world
Recently, we could say the unidirectional data flow is a popular architecture such as flux.
## Does flux architecture have a good performance?
It depends. The performance will be the worst depends on how it is used.
However, most of the cases, we don't know the app we're creating how it will grow and scales.While the application is scaling up, the performance might decrease by getting complexity.To keep performance, we need to tune it up with several approaches.Considering the performance takes time from the beginning.it will make us be annoying to use flux architecture.
## Verge is designed for use from small and supports to scale.
Setting Verge up quickly, and tune-up when we need it.
Verge automatically tune-up and shows us what makes performance badly while development from Xcode's documentation.
For example, Verge provides these stuff to tune performance up.
- Derived (Similar to [facebookexperimental/Recoil](https://github.com/facebookexperimental/Recoil)'s Selector)
- ORM
## Supports volatile events - Activity
We use an event as `Activity` that won't be stored in the state.This concept would help us to describe something that is not easy to describe as a state in the client application.
================================================
FILE: Sources/Verge/Documentation.docc/Guides/Advanced Usage.md
================================================
# Advanced Usage
## To keep performance and scalability
## Adding a cachable computed property in a State
We can add a computed property in a state to get a derived value with stored property,
and that computed property works fine as well other stored property.
```swift
struct MyState {
var items: [Item] = [] {
var itemsCount: Int {
items.count
}
}
```
However, this patterns might cause an expensive cost of operation depends on how they computes.
To solve it, Verge arrows us to define the computed property with another approach.
```swift
struct MyState: ExtendedStateType {
var name: String = ...
var items: [Int] = []
struct Extended: ExtendedType {
let filteredArray = Field.Computed<[Int]> {
$0.items.filter { $0 > 300 }
}
.ifChanged(selector: \.largeArray)
}
}
```
```swift
let store: MyStore
store.changes.computed.filteredArray
```
This defined computed array calculates only if changed specified value.
That condition to re-calculate is defined with `.ifChanged` method in the example code.
And finally, it caches the result by first-time access and it returns cached value until if the source value changed.
## Making a slice of the state (Selector)
We can create a slice object that derives a data from the state.
```swift
let derived: Derived<Int> = store.derived(.map(\.count))
// take a value
derived.value
// subscribe a value changes
derived.sinkChanges { (changes: Changes<Int>) in
}
```
## Creating a Dispatcher
Store arrows us to define an action in itself, that might cause gain complexity in supporting a large application.
To solve this, Verge offers us to create an object that dispatches an action to the store.
We can separate the code of actions to keep maintainability.
that also help us to manage a different type of dependencies.
For example, the case of those dependencies different between logged-in and logged-out.
```swift
class MyDispatcher: MyStore.Dispatcher {
func moreOperation() {
commit {
...
}
}
}
```
```swift
let store: MyStore
let dispatcher = MyDispatcher(target: store)
dispatcher.moreOperation()
```
================================================
FILE: Sources/Verge/Documentation.docc/Guides/Basic Usage.md
================================================
# Basic Usage
This section shows you how we start to use Verge.
It’s very basic usage. You need to read Advanced Usage section if you’re considering to use in production.
To understand smoothly about Verge, we need to figure the following domains out.
## Domains
### Store
- A storage object to manage a state and emit activities by the action.
- Store can dispatch actions to itself.
### State
- A type of state-tree that describes the data our feature needs.
### Activity (Optional)
- A type that describes an activity that happens during performs the action.
- This instance won’t be stored in anywhere. It would help us to perform something by event-driven.
- Consider to use this depends on that if can be represented as a state.
- For example, to present alert or notifcitaions by the action.
### Action
- Action runs any operations (sync / async) and commits any mutations to the state of the store.
- Action is described by Swift’s method in Store or Dispatcher.
### Dispatcher (Optional)
- A type to dispatch an action to specific store.
- For a large application, to separate the logics each domain.
## Setup a Store
### Define a state
```swift
struct MyState {
var count = 0
}
```
### Define an activity
```swift
enum MyActivity {
case countWasIncremented
}
```
`Activity` is not required type.
If you don’t need to use `Activity`, you can set`Never` in Store’s type parameter.
### Define a store
```swift
class MyStore: Store<MyState, MyActivity> {
init(dependency: Dependency) {
super.init(initialState: .init(), logger: nil)
}
}
```
In example, it created a subclass of `Store`. Of course, we can also create an instance from `Store` without subclassing.
But we can put some dependencies (e.g. API client) with creating a sub-class of `Store`.
<aside>
💡 When don’t use Activity
`class MyStore: Store<MyState, Never>`
</aside>
## Add an action
Next, add an action to mutate the state. Essentially, Verge uses Swift’s method to describe an action against enum or struct based action descriptor other Flux library has. This approach has advantages that adding an action faster and call it naturally and dispatches with a faster way by Swift’s native method dispatching system.
- **Better Performance**
- Swift can perform this action with Swift’s method dispatching instead switch-case computing.
- **Returns anything we need**
- the action can return anything from that action (e.g. state or result)
- If that action dispatch async operation, it can return `Future` object. (such as Vuex action)
As a future direction, Verge might get a dispatching action system with describing with enum or struct based to run action.
However, the current approach would be the base system for it.
We’re currently researching that needs.
```swift
class MyStore: Store<MyState, MyActivity> {
func incrementCount() {
commit {
$0.count += 1
}
}
```
Yes, this point is most different with Redux. we could say it close to Vuex. Store knows what the application’s needs.
To mutate the state, we use `commit` method.
the argument inside commit’s closure is `inout State`, you can modify it anything but you can’t put the asynchronous operations.
If you need to do this, call `commit` from the asynchronous operation. like this:
```swift
func incrementCount() {
DispatchQueue.main.async {
commit {
$0.count += 1
}
}
}
```
<aside>
💡 You can define actions aware from the Store Using `Dispatcher`, you can manage the set of actions aware from the store.
</aside>
### Run the action
For example, call that action.
```swift
let store = MyStore(...)
store.incrementCount()
```
## Send an activity from the action
```swift
func incrementCount() {
...
send(.countWasIncremented)
}
```
## Binding the state with UI
### Use the store in SwiftUI
To bind the state with `View`, it uses `StateReader`.
Since `Store` is also compatible with `ObservableObject`, we can declare `@ObservedObject` or `@EnviromentObject`.
`StateReader` provides several options to reduce no changes updates.
Please check it out from Xcode.
```swift
struct MyView: View {
let store: MyStore
var body: some View {
StateReader(store).content { state in
Text(state.name)
}
.onReceive(session.store.activityPublisher) { (activity) in
...
}
}
}
```
### Use the store in UIKit
In UIKit, UIKit doesn’t work with differentiating.
To keep better performance, we need to set a value if it’s changed.
Verge publishes an object that contains previous state and latest state, `Changes` object would be so helpful to check if a value changed.
```swift
class ViewController: UIViewController {
let store: MyStore
var cancellable: VergeAnyCancellable?
init(store: MyStore) {
...
self.cancellable = store.sinkState { [weak self] state in
self?.update(state: state)
}
}
private func update(state: Changes<MyStore.State>) {
state.ifChanged(\.name) { (name) in
nameLabel.text = name
}
state.ifChanged(\.age) { (age) in
ageLabel.text = age.description
}
}
}
```
================================================
FILE: Sources/Verge/Documentation.docc/Guides/Migration Guide v9.md
================================================
# Changes in v9
## Store requires Equatable State
From v8 complex implementations, v9 becomes it requires Equatable to State associated with Store, Changes, Derived and what else related.
Then Verge v9 now dropped lots of implementations and overloads covering cases if the state don’t have Equatable.
## Store can have multiple databases
Now, Store has `databases` accessor that allows us to read database.
`databases` is `DatabaseDynamicMembers` which provides property following to State shape.
This looks up member only type of `DatabaseType`
```swift
@dynamicMemberLookup
public struct DatabaseDynamicMembers<Store: StoreType> {
unowned let store: Store
init(store: Store) {
self.store = store
}
public subscript<Database: DatabaseType>(dynamicMember keyPath: KeyPath<Store.State, Database>) -> DatabaseContext<Store, Database> {
.init(keyPath: keyPath, store: store)
}
}
```
## Use new syntax for creating Field.Computed
As you know, Changes supports memoized-computed-property.
That can be done writing `Field.Computed`
Now its writing syntax will change.
```swift
let filteredArray = Field.Computed(
.map(
using: { $0.largeArray },
transform: { $0.filter { $0 > 300 } }
)
)
```
`using` specifies the dependencies which used from `transform` function.
`transform` function will create a new value from given dependencies provided from `using` function.
## Detail changes
- Dropped complex implementations related to performance tunings
- Stopped using cache to return Derived internally.
- Deleted `batchCommit`
From v8 complex implementations, v9 becomes it requires Equatable to State associated with Store, Changes, Derived and what else related.
Then Verge v9 now dropped lots of implementations and overloads covering cases if the state don’t have Equatable.
================================================
FILE: Sources/Verge/Documentation.docc/Mutation.md
================================================
# Mutation
## What Mutation is
The only way to actually change state in a Store is by committing a mutation. Define a function that returns Mutation object. That expresses that function is Mutation
<aside>
⚠️ Mutation does NOT allow to run asynchronous operation.
</aside>
Mutation does **NOT** allow to run asynchronous operation.
### Define mutations in the Store
```swift
struct MyState {
var todos: [TODO] = []
}
class MyStore: Store<MyState, Never> {
func addNewTodo(title: String) {
commit { (state: inout InoutRef<MyState>) in
state.todos.append(Todo(title: title, hasCompleted: false))
}
}
}
```
<aside>
💡 If the commit has no modifications, Store skips the mutation.
</aside>
### Run Mutation
```swift
let store = MyStore()
store.addNewTodo(title: "Create SwiftUI App")
print(store.state.todos)
// store.state.todos => [Todo(title: "Create SwiftUI App", hasCompleted: false)]
```
## Batches multiple commtis
Committing multiple mutations in a short time might decrease performance.
Because the subscribers around the store derive a state many times.
Like this,
```swift
class MyStore: Store<MyState, Never> {
func myMutation() {
if ... {
commit {
...
}
// emits updated event
}
if ... {
commit {
...
}
// emits updated event
}
if ... {
commit {
...
}
// emits updated event
}
}
}
```
To keep better performance, we need to keep using fewer commits in a short time.
We have 2 ways.
### Using `commit`
``DispatcherType/commit(_:_:_:_:mutation:)`` provides ``InoutRef``, that can detect how the wrapped state will change.
If there is no change, `commit` does nothing and no emitting the events from the Store.
However, you should attention `commit` is atomically operation which means, the Store getting lock while committing.
```swift
func myMutation() {
commit { (state: inout InoutRef<State>) in
if ... {
state.aaa = ...
}
if ... {
state.bbb = ...
}
if ... {
state.ccc = ...
}
}
}
```
================================================
FILE: Sources/Verge/Documentation.docc/Resources/Tiny.md
================================================
# Yet another super tiny store pattern with Verge/Tiny
In fact, `store-pattern` doesn't need something library to run.
The actually necessary thing is **the changing detection in UIKit.**
Without the changing detection, the code is here.
There is no dependencies.
```swift
class MyView: UIView {
private struct State {
var count: Int = 0
}
private var state: State {
didSet {
update(with: state)
}
}
private func update(with state: State) {
...
}
}
```
Next, we focus on `update(with:)` method.
Try to simulate updating the label's value.
```swift
private func update(with state: State) {
myLabel.text = "\(state.count)"
}
```
As you can see, you will think you want to prevent updating the value until the value changed.
## Use Verge.Tiny module to prevent the duplicated updating.
With installing `Verge/Tiny` module, we can write up like followings.
```swift
private func update(with state: State) {
associatedProperties.doIfChanged(state.count) { count in
myLabel.text = "\(count)"
}
}
```
`associatedProperties` is a storage of the values that associated with its owner object(NSObject).
`doIfChanged` gets the location of the code that would be a unique key by composition in the storage.
With this functions, we can get a filter anywhere in the object.
However, this function might affect code readabilities in Swift.
Please carefully using this.
We recommend you gather those operations into one place.
================================================
FILE: Sources/Verge/Documentation.docc/State.md
================================================
# Thinking in single state tree (Not enforced)
VergeStore uses a **single state-tree. (Recommended)** That means an object contains all of the application’s state. With this, we can get to achieve **“single source of truth”**
That state is managed by ``Store``. It process updating the state and notify updated events to the subscribers.
> Tip: Store DOES support multiple state-tree as well. Depending on the case, we can create another Store instance.
## Add a computed property
```
struct State: Equatable {
var count: Int = 0
var countText: String {
return count.description
}
}
```
Extending properties that computes a value from stored property.
Although in some of cases, the cost of computing might be higher which depends on how it create the value from stored properties.
There is ``ExtendedStateType``.
This provies us to get more stuff that **increases performance** and productivity.
## Attention to Normalization
**If you put the data that has relation-ship or complicated structure into state tree, it would be needed normalization to keep performance. Please check VergeORM module**
[About more Normalization and why we need to do this](https://redux.js.org/recipes/structuring-reducers/normalizing-state-shape/)
================================================
FILE: Sources/Verge/Documentation.docc/Verge.Store.md
================================================
# ``Verge/Store``
- Store:
- Should be a reference type object,
- To share the state they manage to multiple subscribers.
- Receives **Mutation** to update the state with thread-safety
- Compatible with SwiftUI’s observableObject and we can use `StateReader` to read the state partially.
## Ways to creating a store
Verge provides 2 ways to create a store.
1. Declare a class that conforms with `StoreComponentType` - It’s a protocol that indicates the class wraps a store inside and behaves like a store.
2. Subclassing from `Store` - a most basic way, but we need to define State and Activity outside
Now, we recommend using No.1 in order to manage the source code with better portability.
## Declare a class that conforms with `StoreComponentType`
```swift
final class MyStore: StoreComponentType {
struct State: StateType {
var count: Int = 0
}
/// This means wrapping store inside. (Probably it should be renamed as like `innerStore` or `wrappedStore`)
/// `DefaultStore` is a typealias that declared by `StoreComponentType`.
/// You can use any class that inherited from `Store` for your use-cases.
let store: DefaultStore
init() {
self.store = .init(initialState: .init())
}
}
```
### Add a Mutation
```swift
extension MyStore {
func increment() {
commit {
$0.count += 0
}
}
}
```
### Commit the mutation
```swift
let store = MyStore()
store.increment()
```
## Subclassing from `Store`
```swift
struct State: StateType {
var count: Int = 0
}
enum Activity {
case happen
}
final class MyStore: Store<State, Never> {
init() {
super.init(
initialState: .init(),
logger: DefaultStoreLogger.shared
)
}
}
```
### Add a Mutation
```swift
extension MyStore {
func increment() {
commit {
$0.count += 0
}
}
}
```
### Commit the mutation
```swift
let store = MyStore()
store.increment()
```
================================================
FILE: Sources/Verge/Documentation.docc/Verge.md
================================================
# ``Verge``
## 🛹 Small start unidirectional data flow
Verge is designed for use from small and supports to scale. Setting Verge up quickly, and tune-up when we need it.
## 🏎 Focus on performance
Does flux have a good performance?The performance will be the worst depends on how it is used.Verge automatically tune-up and shows us how we could gain a performant.
## ⛱ Available in **UIKit** and **SwiftUI**
Verge supports both of UI framework. Especially, it highly supports to update partially UI on UIKit.
## Verge supports small start and scaling up
Verge is a state management library for iOS (UIKit / SwiftUI).Mostly it's based on Flux architecture.Flux architecture is so beautiful and simplified thinking.Although, we need to do several tuning to bring it into a real product.In fact, Flux needs to consider about computing resources.
Verge contains several ideas to do this from Web technologies such as Redux, Vuex, and Recoil.They have very useful techniques to be successful in real-world productions based on the core-concept of Flux.
Verge can be setting it up quickly, and tune performance up when we need it.Verge automatically tune-up as possible and shows us what makes performance badly while development from Xcode's documentation.
## At a glance
A way to create a ViewModel
```swift
final class MyViewModel: StoreComponentType {
struct State: Equatable {
var name: String = ""
var count: Int = 0
}
let store: DefaultStore = .init(initialState: .init())
func myAction() {
commit {
$0.name = "Hello, Verge"
}
}
func increment() {
commit {
$0.count += 1
}
}
}
```
A way to create a customized store
```swift
struct MyState: Equatable {
var name: String = ""
var count: Int = 0
}
final class MyStore: Store<MyState, Never> {
func myAction() {
commit {
$0.name = "Hello, Verge"
}
}
func increment() {
commit {
$0.count += 1
}
}
}
```
### SwiftUI
```swift
struct MyView: View {
let store: MyViewModel
var body: some View {
StateReader(store) { state in
Text(state.name)
Button(action: {
self.store.myAction()
}) {
Text("Action")
}
}
}
}
```
### UIKit
```swift
final class MyViewController: UIViewController {
let viewModel: MyViewModel
...
var cancellable: VergeAnyCancellable?
init(viewModel: MyViewModel) {
self.viewModel = viewModel
self.cancellable = viewModel.sinkState { [weak self] state in
self?.update(state: state)
}
}
private func update(state: Changes<MyStore.State>) {
state.ifChanged(\.name) { (name) in
nameLabel.text = name
}
state.ifChanged(\.count) { (age) in
countLabel.text = age.description
}
...
}
}
```
## Prepare moving to SwiftUI from now with Verge
SwiftUI's concept is similar to the concept of React, Vue, and Elm.Therefore, the concept of state management will become to be similar as well.
That is Redux or Vuex and more.
Now, almost of iOS Applications are developed on top of UIKit.And We can't say SwiftUI is ready for top production.However, it would change.
It's better to use the state management that fits SwiftUI from now. It's not only for that, current UIKit based applications can get more productivity as well.
## Questions?
We accept your questions about usage of Verge and something else in GitHub Issues.
日本語での質問も大丈夫です
## Topics
### Guides
- <doc:Motivation>
- <doc:Basic-Usage>
- <doc:Advanced-Usage>
### Extras
- <doc:Tiny>
### Migrations
- <doc:Migration-Guide-v9>
### Essentials
- ``Verge/Store``
- ``Verge/Changes``
- ``Verge/Derived``
- ``Verge/DispatcherType``
================================================
FILE: Sources/Verge/Library/BackgroundDeallocationQueue.swift
================================================
//
// Copyright (c) 2020 Hiroshi Kimura(Muukii) <muukii.app@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
import DequeModule
actor BackgroundDeallocationQueue {
private var buffer: Deque<Unmanaged<AnyObject>> = .init()
func releaseObjectInBackground(object: AnyObject) {
let innerCurrentRef = Unmanaged.passRetained(object)
let isFirstEntry = buffer.isEmpty
buffer.append(innerCurrentRef)
if isFirstEntry {
Task {
// accumulate objects to dealloc for batching
try? await Task.sleep(nanoseconds: 1_000_000)
await self.drain()
}
}
}
func drain() async {
guard buffer.isEmpty == false else {
return
}
while let pointer = buffer.popFirst() {
pointer.release()
await Task.yield()
}
await drain()
}
}
================================================
FILE: Sources/Verge/Library/CachedMap.swift
================================================
//
// Copyright (c) 2020 muukii
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
@available(*, deprecated, renamed: "InstancePool")
public typealias CachedMapStorage<Source, Target> = InstancePool<Source, Target>
/// A storage object that retains projected instance from source by identified key.
public final class InstancePool<Source, Target>: @unchecked Sendable {
struct Artifact {
let source: Source
var value: Target
}
private let generateKey: @Sendable (Source) -> AnyHashable
private let updateCondition: @Sendable (Source, Source) -> Bool
private var innerStorage: [AnyHashable: Artifact] = [:]
private let outerLock = NSLock()
/// Creates an instance
///
/// - Parameters:
/// - keySelector: A closure that gives key to identify the mapped instance.
/// - shouldUpdate: A closure that indicates whether cached one replace with new mapped instance.
public init<Key: Hashable>(
keySelector: @escaping @Sendable (Source) -> Key,
shouldUpdate: @escaping @Sendable (_ cached: Source, _ new: Source) -> Bool = { _, _ in false }
) {
self.updateCondition = shouldUpdate
self.generateKey = {
AnyHashable(keySelector($0))
}
}
public func purgeCache() {
outerLock.lock()
defer {
outerLock.unlock()
}
innerStorage = [:]
}
public func map<C: Collection>(
from collection: C,
sweepsUnused: Bool,
makeNew: (C.Element) throws -> Target,
update: (C.Element, inout Target) -> Void
) rethrows -> [Target] where C.Element == Source {
outerLock.lock()
defer {
outerLock.unlock()
}
let keys = collection.map(generateKey)
let result = try zip(collection, keys).map { (element, key) -> Target in
if let cached = innerStorage[key], !updateCondition(cached.source, element) {
update(element, &innerStorage[key]!.value)
return innerStorage[key]!.value
}
let newObject = try makeNew(element)
innerStorage[key] = Artifact(source: element, value: newObject)
return newObject
}
if sweepsUnused {
let unusedKeys = Set(innerStorage.keys).subtracting(keys)
for key in unusedKeys {
innerStorage.removeValue(forKey: key)
}
}
return result
}
public func compactMap<C: Collection>(
from collection: C,
sweepsUnused: Bool,
makeNew: (C.Element) throws -> Target?,
update: (C.Element, inout Target) -> Void
)
rethrows -> [Target] where C.Element == Source
{
outerLock.lock()
defer {
outerLock.unlock()
}
let keys = collection.map(generateKey)
let result = try zip(collection, keys).compactMap { (element, key) -> Target? in
if let cached = innerStorage[key], !updateCondition(cached.source, element) {
update(element, &innerStorage[key]!.value)
return innerStorage[key]!.value
}
guard let newObject = try makeNew(element) else { return nil }
innerStorage[key] = Artifact(source: element, value: newObject)
return newObject
}
if sweepsUnused {
let unusedKeys = Set(innerStorage.keys).subtracting(keys)
for key in unusedKeys {
innerStorage.removeValue(forKey: key)
}
}
return result
}
}
extension Collection {
/**
Returns an array containing the results of mapping the given closure over the sequence’s elements.
Especially, it uses a cached instance to return.
You can set your expectation on how it caches from creating `CachedMapStorage`.
It helps to create a store object or view model from immutable data.
- Author: Verge
*/
public func cachedMap<U>(
using pool: InstancePool<Self.Element, U>,
sweepsUnused: Bool = false,
makeNew: (Self.Element) throws -> U,
update: (Self.Element, inout U) -> Void = { _, _ in }
) rethrows -> [U] {
return try pool.map(from: self, sweepsUnused: sweepsUnused, makeNew: makeNew, update: update)
}
/**
Returns an array containing the results of mapping the given closure over the sequence’s elements.
Especially, it uses a cached instance to return.
You can set your expectation on how it caches from creating `CachedMapStorage`.
It helps to create a store object or view model from immutable data.
- Author: Verge
*/
public func cachedCompactMap<U>(
using pool: InstancePool<Self.Element, U>,
sweepsUnused: Bool = false,
makeNew: (Self.Element) throws -> U?,
update: (Self.Element, inout U) -> Void = { _, _ in }
) rethrows -> [U] {
return try pool.compactMap(from: self, sweepsUnused: sweepsUnused, makeNew: makeNew, update: update)
}
}
================================================
FILE: Sources/Verge/Library/EventEmitter.swift
================================================
//
// Copyright (c) 2019 muukii
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Atomics
import Combine
import DequeModule
import Foundation
import os
public final class EventEmitterCancellable: Hashable, Cancellable, @unchecked Sendable {
public static func == (lhs: EventEmitterCancellable, rhs: EventEmitterCancellable) -> Bool {
lhs === rhs
}
private struct State {
weak var owner: EventEmitterType?
var wasCancelled: Bool = false
}
private let state: VergeConcurrency.ManagedCriticalState<State>
fileprivate init(owner: EventEmitterType) {
self.state = VergeConcurrency.ManagedCriticalState(State(owner: owner))
}
public func hash(into hasher: inout Hasher) {
ObjectIdentifier(self).hash(into: &hasher)
}
public func cancel() {
let continues: EventEmitterType? = state.withCriticalRegion {
guard $0.wasCancelled == false else {
return nil
}
$0.wasCancelled = true
return $0.owner
}
continues?.removeEventHandler(self)
}
}
protocol EventEmitterType: AnyObject, Sendable {
func removeEventHandler(_ token: EventEmitterCancellable)
}
public protocol EventEmitterEventType {
func onComsume()
}
/// Instead of Combine
open class EventEmitter<Event: EventEmitterEventType>: EventEmitterType, @unchecked Sendable {
public var publisher: Publisher {
return .init(eventEmitter: self)
}
private var subscribers:
VergeConcurrency.UnfairLockAtomic<[EventEmitterCancellable: (Event) -> Void]> = .init([:])
private let queue: VergeConcurrency.UnfairLockAtomic<Deque<Event>> = .init(.init())
private let flag = ManagedAtomic<Bool>.init(false)
private var deinitHandlers: VergeConcurrency.UnfairLockAtomic<[() -> Void]> = .init([])
public init() {
}
deinit {
deinitHandlers.value.forEach {
$0()
}
}
@_spi(EventEmitter)
public func accept(_ event: consuming Event) {
/**
https://github.com/VergeGroup/Verge/pull/220
https://github.com/VergeGroup/Verge/issues/221
https://github.com/VergeGroup/Verge/pull/222
*/
// delivers a given event for subscribers at this point.
let capturedSubscribers = subscribers.value
queue.modify {
$0.append(event)
}
if flag.compareExchange(expected: false, desired: true, ordering: .sequentiallyConsistent)
.exchanged
{
while let event: Event = queue.modify({
if $0.isEmpty == false {
return $0.removeFirst()
} else {
return nil
}
}) {
// Emits
receiveEvent(event)
for subscriber in capturedSubscribers {
vergeSignpostEvent("EventEmitter.emitForSubscriber")
subscriber.1(event)
}
event.onComsume()
}
/**
might contain a bug in here?
a conjunction of enqueue and dequeue
*/
_ = flag.compareExchange(expected: true, desired: false, ordering: .sequentiallyConsistent)
} else {
// enqueue only
}
}
open func receiveEvent(_ event: consuming Event) {
}
@_spi(EventEmitter)
@discardableResult
public func addEventHandler(_ eventReceiver: @escaping (Event) -> Void) -> EventEmitterCancellable
{
let token = EventEmitterCancellable(owner: self)
subscribers.modify {
$0[token] = eventReceiver
}
return token
}
func removeEventHandler(_ token: EventEmitterCancellable) {
var itemToRemove: ((Event) -> Void)? = nil
subscribers.modify {
itemToRemove = $0[token]
$0.removeValue(forKey: token)
}
// To avoid triggering deinit inside removing operation
// At this point, deallocation will happen, then ``EventEmitterCancellable` runs operations.
// subscribers is using unfair-lock means it's not recursive lock.
// if removes the item then deallocated inside locking, onDeinit handler runs then entering this method recursively potentially by some others.
// then unfair-lock raises runtime error.
withExtendedLifetime(itemToRemove, {})
}
public func onDeinit(_ onDeinit: @escaping () -> Void) {
deinitHandlers.modify {
$0.append(onDeinit)
}
}
}
extension EventEmitter {
@available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *)
public struct Publisher: Combine.Publisher {
public typealias Output = Event
public typealias Failure = Never
private weak var eventEmitter: EventEmitter<Event>?
public init(eventEmitter: EventEmitter<Event>) {
self.eventEmitter = eventEmitter
}
public func receive<S>(subscriber: S)
where S: Subscriber, Failure == S.Failure, Output == S.Input {
let subscription = Subscription<S>(subscriber: subscriber, eventEmitter: eventEmitter)
subscriber.receive(subscription: subscription)
}
}
@available(iOS 13, macOS 10.15, tvOS 13, watchOS 6, *)
public struct Subscription<S: Subscriber>: Combine.Subscription where S.Input == Event {
public let combineIdentifier: CombineIdentifier = .init()
private let subscriber: S
private let eventEmitterSubscription: EventEmitterCancellable?
private weak var eventEmitter: EventEmitter<Event>?
init(subscriber: S, eventEmitter: EventEmitter<Event>?) {
self.subscriber = subscriber
self.eventEmitter = eventEmitter
self.eventEmitterSubscription = eventEmitter?
.addEventHandler { (event) in
_ = subscriber.receive(event)
}
}
public func request(_ demand: Subscribers.Demand) {
// TODO: implement
}
public func cancel() {
guard let eventEmitterSubscription else { return }
eventEmitter?.removeEventHandler(eventEmitterSubscription)
}
}
}
================================================
FILE: Sources/Verge/Library/InoutRef.swift
================================================
//
// Copyright (c) 2020 muukii
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
import StateStruct
public enum Modification {
case graph(PropertyNode)
case indeterminate
}
public struct InoutRef<Wrapped>: ~Copyable {
// MARK: - Properties
nonisolated(unsafe) private let pointer: UnsafeMutablePointer<Wrapped>
/// A wrapped value
/// You may use this property to call the mutating method which `Wrapped` has.
public var wrapped: Wrapped {
_read {
yield pointer.pointee
}
_modify {
yield &pointer.pointee
}
}
private(set) var modification: Modification?
public var hasModified: Bool {
guard let modification else {
return false
}
switch modification {
case .graph(let graph):
return !graph.isEmpty
case .indeterminate:
return true
}
}
// MARK: - Initializers
/**
Creates an instance
You should take care of using the pointer of value.
Using always `withUnsafeMutablePointer` to pass it, otherwise Swift might crash with Memory error.
*/
init(_ pointer: UnsafeMutablePointer<Wrapped>) {
self.pointer = pointer
}
deinit {
(pointer.pointee as? TrackingObject)?.endTracking()
}
// MARK: - Functions
public mutating func modify<T>(_ perform: (inout Wrapped) throws -> T) rethrows -> T {
if pointer.pointee is TrackingObject {
let identifier = Token()
(pointer.pointee as! TrackingObject)._tracking_context.identifier = identifier
let result = try perform(&pointer.pointee)
let resultIdentifier = (pointer.pointee as! TrackingObject)._tracking_context.identifier
(pointer.pointee as! TrackingObject)._tracking_context.identifier = nil
guard Optional(identifier) == resultIdentifier else {
// replacing instance itself happened.
modification = .indeterminate
return result
}
if var modifyingResult = (pointer.pointee as! TrackingObject).trackingResult {
modifyingResult.graph.shakeAsWrite()
let graph = modifyingResult.graph
self.modification = .graph(graph)
//#if DEBUG
// Log.writeGraph.debug("Modified: \(graph.prettyPrint())")
//#endif
} else {
modification = .indeterminate
}
return result
} else {
let r = try perform(&pointer.pointee)
modification = .indeterminate
return r
}
}
}
private final class Token: Hashable {
static func == (lhs: Token, rhs: Token) -> Bool {
lhs === rhs
}
func hash(into hasher: inout Hasher) {
ObjectIdentifier(self).hash(into: &hasher)
}
}
/// Do not retain on anywhere.
@dynamicMemberLookup
public final class ReadRef<Wrapped> {
private let pointer: UnsafePointer<Wrapped>
/// A wrapped value
/// You may use this property to call the mutating method which `Wrapped` has.
public var wrapped: Wrapped {
_read {
yield pointer.pointee
}
}
// MARK: - Initializers
/**
Creates an instance
You should take care of using the pointer of value.
Using always `withUnsafeMutablePointer` to pass it, otherwise Swift might crash with Memory error.
*/
public init(_ pointer: UnsafePointer<Wrapped>) {
self.pointer = pointer
}
// MARK: - Functions
public subscript<T>(dynamicMember keyPath: KeyPath<Wrapped, T>) -> T {
_read {
yield pointer.pointee[keyPath: keyPath]
}
}
public subscript<T>(dynamicMember keyPath: KeyPath<Wrapped, T?>) -> T? {
_read {
yield pointer.pointee[keyPath: keyPath]
}
}
public subscript<T>(keyPath keyPath: KeyPath<Wrapped, T>) -> T {
_read {
yield pointer.pointee[keyPath: keyPath]
}
}
public subscript<T>(keyPath keyPath: KeyPath<Wrapped, T?>) -> T? {
_read {
yield pointer.pointee[keyPath: keyPath]
}
}
func map<U, Result>(keyPath: KeyPath<Wrapped, U>, perform: (inout ReadRef<U>) throws -> Result)
rethrows -> Result
{
try withUnsafePointer(to: pointer.pointee[keyPath: keyPath]) { (pointer) in
var ref = ReadRef<U>.init(pointer)
return try perform(&ref)
}
}
func map<U, Result>(keyPath: KeyPath<Wrapped, U?>, perform: (inout ReadRef<U>?) throws -> Result)
rethrows -> Result
{
guard pointer.pointee[keyPath: keyPath] != nil else {
var _nil: ReadRef<U>! = .none
return try perform(&_nil)
}
return try withUnsafePointer(to: pointer.pointee[keyPath: keyPath]!) { (pointer) in
var ref: ReadRef<U>! = ReadRef<U>.init(pointer)
return try perform(&ref)
}
}
}
================================================
FILE: Sources/Verge/Library/Log.swift
================================================
//
// Log.swift
// VergeCore
//
// Created by muukii on 2020/02/24.
// Copyright © 2020 muukii. All rights reserved.
//
import Foundation
import os.log
enum Log {
static let store = Logger(OSLog.makeOSLogInDebug { OSLog.init(subsystem: "Verge", category: "store") })
static let storeCommit = Logger(OSLog.makeOSLogInDebug { OSLog.init(subsystem: "Verge", category: "store.commit") })
static let storeReader = Logger(OSLog.makeOSLogInDebug { OSLog.init(subsystem: "Verge", category: "storeReader") })
static let reading = Logger(OSLog.makeOSLogInDebug { OSLog.init(subsystem: "Verge", category: "reading") })
static let writeGraph = Logger(OSLog.makeOSLogInDebug { OSLog.init(subsystem: "Verge", category: "writeGraph") })
}
extension OSLog {
@inline(__always)
fileprivate static func makeOSLogInDebug(isEnabled: Bool = true, _ factory: () -> OSLog) -> OSLog {
#if DEBUG
return factory()
#else
return .disabled
#endif
}
}
================================================
FILE: Sources/Verge/Library/Signpost.swift
================================================
import os
import Foundation
nonisolated(unsafe)
public var _verge_signpost_enabled = ProcessInfo.processInfo.environment["VERGE_SIGNPOST_ENABLED"] != nil
@usableFromInline
enum SignpostConstants {
@usableFromInline
static let performanceLog = { () -> OSLog in
if #available(iOSApplicationExtension 13.0, *) {
return OSLog(subsystem: "lib.verge", category: "performance")
} else {
return OSLog(subsystem: "lib.verge", category: "performance")
}
}()
@usableFromInline
static let pointOfInterestLog = OSLog(subsystem: "lib.verge", category: .pointsOfInterest)
}
@inlinable
public func vergeSignpostEvent(_ event: StaticString) {
#if DEBUG
if _verge_signpost_enabled {
let id = OSSignpostID(log: SignpostConstants.pointOfInterestLog)
os_signpost(.event, log: SignpostConstants.pointOfInterestLog, name: event, signpostID: id)
}
#endif
}
@inlinable
public func vergeSignpostEvent(_ event: StaticString, label: @autoclosure () -> String) {
#if DEBUG
if _verge_signpost_enabled {
let id = OSSignpostID(log: SignpostConstants.pointOfInterestLog)
os_signpost(.event, log: SignpostConstants.pointOfInterestLog, name: event, signpostID: id, "%@", label())
}
#endif
}
public struct VergeSignpostTransaction {
#if DEBUG
@usableFromInline
let _end: () -> Void
public let rawID: os_signpost_id_t
#endif
public init(_ name: StaticString) {
#if DEBUG
if _verge_signpost_enabled {
let id = OSSignpostID(log: SignpostConstants.performanceLog)
self.rawID = id.rawValue
os_signpost(.begin, log: SignpostConstants.performanceLog, name: name, signpostID: id)
_end = {
os_signpost(.end, log: SignpostConstants.performanceLog, name: name, signpostID: id)
}
} else {
rawID = 0
_end = {}
}
#else
#endif
}
public init(_ name: StaticString, label: @autoclosure () -> String) {
#if DEBUG
if _verge_signpost_enabled {
let id = OSSignpostID(log: SignpostConstants.performanceLog)
self.rawID = id.rawValue
let _label = label()
os_signpost(.begin, log: SignpostConstants.performanceLog, name: name, signpostID: id, "Begin: %@", _label)
_end = {
os_signpost(.end, log: SignpostConstants.performanceLog, name: name, signpostID: id, "End: %@", _label)
}
} else {
rawID = 0
_end = {}
}
#else
#endif
}
public func event(name: StaticString, label: @autoclosure () -> String) {
#if DEBUG
if _verge_signpost_enabled {
let id = OSSignpostID(rawID)
os_signpost(.event, log: SignpostConstants.pointOfInterestLog, name: name, signpostID: id, "%@", label())
}
#endif
}
@inlinable
@inline(__always)
public func event(name: StaticString) {
#if DEBUG
if _verge_signpost_enabled {
let id = OSSignpostID(rawID)
os_signpost(.event, log: SignpostConstants.pointOfInterestLog, name: name, signpostID: id)
}
#endif
}
@inlinable
@inline(__always)
public func end() {
#if DEBUG
_end()
#endif
}
}
================================================
FILE: Sources/Verge/Library/StoreActivitySubscription.swift
================================================
import Combine
import Atomics
/**
A subscription that is compatible with Combine’s Cancellable.
You can manage asynchronous tasks either call the ``cancel()`` to halt the subscription, or allow it to terminate upon instance deallocation, and by implementing the ``storeWhileSourceActive()`` technique, the subscription’s active status is maintained until the source store is released.
*/
public final class StoreActivitySubscription: StoreSubscriptionBase, @unchecked Sendable {
}
================================================
FILE: Sources/Verge/Library/StoreStateSubscription.swift
================================================
import Combine
import Atomics
/**
A subscription that is compatible with Combine's Cancellable.
You can manage asynchronous tasks either call the ``cancel()`` to halt the subscription, or allow it to terminate upon instance deallocation, and by implementing the ``storeWhileSourceActive()`` technique, the subscription's active status is maintained until the source store is released.
*/
public final class StoreStateSubscription: StoreSubscriptionBase, @unchecked Sendable {
}
================================================
FILE: Sources/Verge/Library/StoreSubscriptionBase.swift
================================================
public class StoreSubscriptionBase: Hashable, Cancellable {
public static func == (lhs: StoreSubscriptionBase, rhs: StoreSubscriptionBase) -> Bool {
lhs === rhs
}
private struct State {
var wasCancelled: Bool = false
weak var storeCancellable: VergeAnyCancellable?
var associatedStore: (any StoreType)?
}
private let state: VergeConcurrency.ManagedCriticalState<State>
public func hash(into hasher: inout Hasher) {
ObjectIdentifier(self).hash(into: &hasher)
}
private let source: EventEmitterCancellable
init(
_ eventEmitterCancellable: EventEmitterCancellable,
storeCancellable: VergeAnyCancellable
) {
self.source = eventEmitterCancellable
self.state = .init(State(storeCancellable: storeCancellable))
}
public func cancel() {
let continues = state.withCriticalRegion { state -> Bool in
guard state.wasCancelled == false else {
return false
}
state.wasCancelled = true
// if it's associated as storeWhileSourceActive.
state.storeCancellable?.dissociate(self)
state.associatedStore = nil
return true
}
guard continues else {
return
}
source.cancel()
}
/**
Make this subscription alive while the source is active.
the source means a root data store which is Store.
In case of Derived, the source will be Derived's upstream.
If the upstream invalidated, this subscription will stop.
*/
@discardableResult
public func storeWhileSourceActive() -> Self {
state.withCriticalRegion { state in
assert(state.wasCancelled == false)
assert(state.storeCancellable != nil)
state.storeCancellable?.associate(self)
}
return self
}
func associate(store: any StoreType) -> Self {
state.withCriticalRegion { state in
assert(state.wasCancelled == false)
assert(state.storeCancellable != nil)
state.associatedStore = store
}
return self
}
/**
Converts to Combine.AnyCancellable to make it auto cancellable.
*/
public func asAny() -> AnyCancellable {
return .init { [self] in
self.cancel()
}
}
deinit {
cancel()
}
}
================================================
FILE: Sources/Verge/Library/VergeAnyCancellable.swift
================================================
import Combine
import Atomics
/// A typealias to `Set<AnyCancellable>`.
public typealias VergeAnyCancellables = Set<AnyCancellable>
/// A type-erasing cancellable object that executes a provided closure when canceled.
/// An AnyCancellable instance automatically calls cancel() when deinitialized.
/// To cancel depending owner, can be written following
///
/// ```
/// class ViewController {
///
/// var subscriptions = VergeAnyCancellables()
///
/// func something() {
///
/// let derived = store.derived(...)
///
/// derived
/// .subscribeStateChanges { ... }
/// .store(in: &subscriptions)
/// }
///
/// }
/// ```
public final class VergeAnyCancellable: Hashable, Cancellable, Sendable {
private struct State {
var wasCancelled: Bool = false
var actions: ContiguousArray<() -> Void>? = .init()
var retainObjects: [AnyObject] = []
}
private let state: VergeConcurrency.ManagedCriticalState<State> = .init(State())
public static func == (lhs: VergeAnyCancellable, rhs: VergeAnyCancellable) -> Bool {
lhs === rhs
}
public func hash(into hasher: inout Hasher) {
ObjectIdentifier(self).hash(into: &hasher)
}
public init() {
}
public convenience init(onDeinit: @escaping () -> Void) {
self.init()
state.withCriticalRegion { $0.actions = [onDeinit] }
}
public convenience init<C>(_ cancellable: C) where C : Cancellable {
self.init {
cancellable.cancel()
}
}
@discardableResult
public func associate(_ object: AnyObject) -> VergeAnyCancellable {
state.withCriticalRegion { state in
assert(!state.wasCancelled)
state.retainObjects.append(object)
}
return self
}
public func dissociate(_ object: AnyObject) {
let targets = state.withCriticalRegion { state -> [AnyObject] in
var targets: [AnyObject] = .init()
targets.reserveCapacity(state.retainObjects.count)
state.retainObjects.removeAll {
let remove = $0 === object
if remove {
targets.append($0)
}
return remove
}
return targets
}
guard targets.isEmpty == false else {
return
}
withExtendedLifetime(targets, {})
}
public func insert(_ cancellable: Cancellable) {
state.withCriticalRegion { state in
assert(!state.wasCancelled)
state.actions?.append {
cancellable.cancel()
}
}
}
public func insert(onDeinit: @escaping () -> Void) {
state.withCriticalRegion { state in
assert(!state.wasCancelled)
state.actions?.append(onDeinit)
}
}
deinit {
cancel()
}
public func cancel() {
let result = state.withCriticalRegion { state -> (
retainObjects: [AnyObject],
actions: ContiguousArray<() -> Void>?
)? in
guard !state.wasCancelled else {
return nil
}
state.wasCancelled = true
let retainObjects = state.retainObjects
state.retainObjects.removeAll()
let actions = state.actions
state.actions = nil
return (retainObjects, actions)
}
guard let result else {
return
}
withExtendedLifetime(result.retainObjects, {})
result.actions?.forEach {
$0()
}
}
}
================================================
FILE: Sources/Verge/Library/VergeConcurrency+SynchronizationTracker.swift
================================================
//
// Copyright (c) 2020 muukii
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
extension VergeConcurrency {
///
///
/// Modified based on RxSwift's original implementations.
public final class SynchronizationTracker: @unchecked Sendable {
public enum Warning: Hashable {
case reentrancyAnomaly
case synchronizationAnomaly
}
private let _lock = VergeConcurrency.RecursiveLock()
private var _threads = [UnsafeMutableRawPointer: Int]()
private let _isEnabled: Bool
public init(debugOnly: Bool = false) {
if debugOnly {
#if DEBUG
self._isEnabled = true
#else
self._isEnabled = false
#endif
} else {
self._isEnabled = true
}
}
/**
Marks as entering a synchronized operation.
*/
@discardableResult
public func register(
_ file: StaticString = #file,
_ function: StaticString = #function,
_ line: UInt = #line,
printsConsole: Bool = false
) -> Set<Warning> {
guard _isEnabled else { return .init() }
self._lock.lock(); defer { self._lock.unlock() }
var flags = Set<Warning>()
let pointer = Unmanaged.passUnretained(Thread.current).toOpaque()
let count = (self._threads[pointer] ?? 0) + 1
if count > 1 {
flags.insert(.reentrancyAnomaly)
}
self._threads[pointer] = count
if self._threads.count > 1 {
flags.insert(.synchronizationAnomaly)
}
if printsConsole, flags.isEmpty == false {
print("⚠️[SynchronizationTracker] Found issues \(flags) in \(file):\(function):\(line)")
}
return flags
}
/**
Marks as exited a synchronized operation.
*/
public func unregister() {
guard _isEnabled else { return }
self._lock.lock(); defer { self._lock.unlock() }
let pointer = Unmanaged.passUnretained(Thread.current).toOpaque()
self._threads[pointer] = (self._threads[pointer] ?? 1) - 1
if self._threads[pointer] == 0 {
self._threads[pointer] = nil
}
}
}
}
================================================
FILE: Sources/Verge/Library/VergeConcurrency.swift
================================================
//
// Copyright (c) 2020 muukii
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
public enum VergeConcurrency {
public final class RecursiveLock: NSRecursiveLock, @unchecked Sendable {
}
public struct UnfairLock: ~Copyable, @unchecked Sendable {
private let _lock: os_unfair_lock_t
public init() {
_lock = .allocate(capacity: 1)
_lock.initialize(to: os_unfair_lock())
}
public func lock() {
os_unfair_lock_lock(_lock)
}
public func unlock() {
os_unfair_lock_unlock(_lock)
}
public func `try`() -> Bool {
return os_unfair_lock_trylock(_lock)
}
deinit {
_lock.deinitialize(count: 1)
_lock.deallocate()
}
}
/// An atomic variable.
public final class RecursiveLockAtomic<Value>: @unchecked Sendable {
public var unsafelyWrappedValue: Value {
_read { yield _value }
}
private let lock: RecursiveLock
private var _value: Value
/// Atomically get or set the value of the variable.
public var value: Value {
get {
return withValue { $0 }
}
set(newValue) {
swap(newValue)
}
}
/// Initialize the variable with the given initial value.
///
/// - parameters:
/// - value: Initial value for `self`.
public init(_ value: Value) {
_value = value
lock = .init()
}
/// Atomically modifies the variable.
///
/// - parameters:
/// - action: A closure that takes the current value.
///
/// - returns: The result of the action.
@discardableResult
public func modify<Result>(_ action: (inout Value) throws -> Result) rethrows -> Result {
lock.lock()
defer { lock.unlock() }
return try action(&_value)
}
/// Atomically perform an arbitrary action using the current value of the
/// variable.
///
/// - parameters:
/// - action: A closure that takes the current value.
///
/// - returns: The result of the action.
@discardableResult
public func withValue<Result>(_ action: (Value) throws -> Result) rethrows -> Result {
lock.lock()
defer { lock.unlock() }
return try action(_value)
}
/// Atomically replace the contents of the variable.
///
/// - parameters:
/// - newValue: A new value for the variable.
///
/// - returns: The old value.
@discardableResult
public func swap(_ newValue: Value) -> Value {
return modify { (value: inout Value) in
let oldValue = value
value = newValue
return oldValue
}
}
}
/// An atomic variable.
@propertyWrapper
public final class UnfairLockAtomic<Value>: @unchecked Sendable {
public var unsafelyWrappedValue: Value {
_read { yield _value }
}
private let lock: UnfairLock
private var _value: Value
/// Atomically get or set the value of the variable.
public var value: Value {
get {
return withValue { $0 }
}
set(newValue) {
swap(newValue)
}
}
public var wrappedValue: Value {
get {
return withValue { $0 }
}
set(newValue) {
swap(newValue)
}
}
/// Initialize the variable with the given initial value.
///
/// - parameters:
/// - value: Initial value for `self`.
public init(_ wrappedValue: Value) {
_value = wrappedValue
lock = .init()
}
public init(wrappedValue: Value) {
_value = wrappedValue
lock = .init()
}
public var projectedValue: UnfairLockAtomic<Value> { self }
/// Atomically modifies the variable.
///
/// - parameters:
/// - action: A closure that takes the current value.
///
/// - returns: The result of the action.
@discardableResult
public func modify<Result>(_ action: (inout Value) throws -> Result) rethrows -> Result {
lock.lock()
defer { lock.unlock() }
return try action(&_value)
}
/// Atomically perform an arbitrary action using the current value of the
/// variable.
///
/// - parameters:
/// - action: A closure that takes the current value.
///
/// - returns: The result of the action.
@discardableResult
public func withValue<Result>(_ action: (Value) throws -> Result) rethrows -> Result {
lock.lock()
defer { lock.unlock() }
return try action(_value)
}
/// Atomically replace the contents of the variable.
///
/// - parameters:
/// - newValue: A new value for the variable.
///
/// - returns: The old value.
@discardableResult
public func swap(_ newValue: Value) -> Value {
return modify { (value: inout Value) in
let oldValue = value
value = newValue
return oldValue
}
}
}
/// A container that initializes value when it needs.
///
/// Supports multi-threading.
@propertyWrapper
public final class AtomicLazy<T>: @unchecked Sendable {
private enum State {
case initialized(T)
case notInitialized
}
public typealias Initializer = () -> T
private var _onInitialized: (T) -> Void = { _ in }
private let lock: UnfairLock = .init()
public var wrappedValue: T {
lock.lock()
defer {
lock.unlock()
}
return unsafeValue
}
public var projectedValue: AtomicLazy<T> {
self
}
@discardableResult
public func modify<Result>(_ action: (inout T) throws -> Result) rethrows -> Result {
lock.lock()
defer { lock.unlock() }
var new = unsafeValue
let result = try action(&new)
self._synchronized_state = .initialized(new)
return consume result
}
private var _synchronized_state: State = .notInitialized
private var unsafeValue: T {
get {
switch _synchronized_state {
case .notInitialized:
let value = initializer()
_onInitialized(value)
self._synchronized_state = .initialized(value)
self.initializer = nil
return value
case .initialized(let value):
return value
}
}
}
private var initializer: Initializer!
public init(_ initializer: @escaping Initializer) {
self.initializer = initializer
}
public init(wrappedValue initializer: @autoclosure @escaping Initializer) {
self.initializer = initializer
}
/// Set closure on value initialized.
/// the closure would be called on thread which value initialized.
@discardableResult
public func onInitialized(_ perform: @escaping (T) -> Void) -> Self {
_onInitialized = perform
return self
}
}
// From: https://github.com/apple/swift-async-algorithms/blob/4c3ea81f81f0a25d0470188459c6d4bf20cf2f97/Sources/AsyncAlgorithms/Locking.swift#L131
struct ManagedCriticalState<State>: @unchecked Sendable {
private final class LockedBuffer: ManagedBuffer<State, Lock.Primitive> {
deinit {
withUnsafeMutablePointerToElements { Lock.deinitialize($0) }
}
}
private let buffer: ManagedBuffer<State, Lock.Primitive>
init(_ initial: State) {
buffer = LockedBuffer.create(minimumCapacity: 1) { buffer in
buffer.withUnsafeMutablePointerToElements { Lock.initialize($0) }
return initial
}
}
func withCriticalRegion<R>(_ critical: (inout State) throws -> R) rethrows -> R {
try buffer.withUnsafeMutablePointers { header, lock in
Lock.lock(lock)
defer { Lock.unlock(lock) }
return try critical(&header.pointee)
}
}
}
internal struct Lock {
#if canImport(Darwin)
typealias Primitive = os_unfair_lock
#elseif canImport(Glibc) || canImport(Musl) || canImport(Bionic)
typealias Primitive = pthread_mutex_t
#elseif canImport(WinSDK)
typealias Primitive = SRWLOCK
#else
#error("Unsupported platform")
#endif
typealias PlatformLock = UnsafeMutablePointer<Primitive>
let platformLock: PlatformLock
private init(_ platformLock: PlatformLock) {
self.platformLock = platformLock
}
fileprivate static func initialize(_ platformLock: PlatformLock) {
#if canImport(Darwin)
platformLock.initialize(to: os_unfair_lock())
#elseif canImport(Glibc) || canImport(Musl) || canImport(Bionic)
let result = pthread_mutex_init(platformLock, nil)
precondition(result == 0, "pthread_mutex_init failed")
#elseif canImport(WinSDK)
InitializeSRWLock(platformLock)
#else
#error("Unsupported platform")
#endif
}
fileprivate static func deinitialize(_ platformLock: PlatformLock) {
#if canImport(Glibc) || canImport(Musl) || canImport(Bionic)
let result = pthread_mutex_destroy(platformLock)
precondition(result == 0, "pthread_mutex_destroy failed")
#endif
platformLock.deinitialize(count: 1)
}
fileprivate static func lock(_ platformLock: PlatformLock) {
#if canImport(Darwin)
os_unfair_lock_lock(platformLock)
#elseif canImport(Glibc) || canImport(Musl) || canImport(Bionic)
pthread_mutex_lock(platformLock)
#elseif canImport(WinSDK)
AcquireSRWLockExclusive(platformLock)
#else
#error("Unsupported platform")
#endif
}
fileprivate static func unlock(_ platformLock: PlatformLock) {
#if canImport(Darwin)
os_unfair_lock_unlock(platformLock)
#elseif canImport(Glibc) || canImport(Musl) || canImport(Bionic)
let result = pthread_mutex_unlock(platformLock)
precondition(result == 0, "pthread_mutex_unlock failed")
#elseif canImport(WinSDK)
ReleaseSRWLockExclusive(platformLock)
#else
#error("Unsupported platform")
#endif
}
static func allocate() -> Lock {
let platformLock = PlatformLock.allocate(capacity: 1)
initialize(platformLock)
return Lock(platformLock)
}
func deinitialize() {
Lock.deinitialize(platformLock)
platformLock.deallocate()
}
func lock() {
Lock.lock(platformLock)
}
func unlock() {
Lock.unlock(platformLock)
}
/// Acquire the lock for the duration of the given block.
///
/// This convenience method should be preferred to `lock` and `unlock` in
/// most situations, as it ensures that the lock will be released regardless
/// of how `body` exits.
///
/// - Parameter body: The block to execute while holding the lock.
/// - Returns: The value returned by the block.
func withLock<T>(_ body: () throws -> T) rethrows -> T {
self.lock()
defer {
self.unlock()
}
return try body()
}
// specialise Void return (for performance)
func withLockVoid(_ body: () throws -> Void) rethrows -> Void {
try self.withLock(body)
}
}
}
@inline(__always)
func withUncheckedSendable<T>(_ body: () throws -> T) rethrows -> T {
try body()
}
================================================
FILE: Sources/Verge/Library/_BackingStorage+.swift
================================================
extension _BackingStorage {
func map<U>(_ transform: (borrowing Value) throws -> U) rethrows -> _BackingStorage<U> {
return .init(
try transform(value)
)
}
}
================================================
FILE: Sources/Verge/Logging/ActivityTrace.swift
================================================
//
// Copyright (c) 2020 muukii
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
/// A trace that indicates the activity where comes from.
public struct ActivityTrace: Encodable, Sendable {
public let createdAt: Date = .init()
public let name: String
public let file: String
public let function: String
public let line: UInt
}
================================================
FILE: Sources/Verge/Logging/DefaultStoreLogger.swift
================================================
//
// Copyright (c) 2019 muukii
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
import os
public struct CommitLog: Encodable, Sendable {
public let type: String = "commit"
public let tookMilliseconds: Double
public let traces: [MutationTrace]
public let store: String
public init(storeName: String, traces: [MutationTrace], time: CFTimeInterval) {
self.store = storeName
self.tookMilliseconds = time * 1000
self.traces = traces
}
}
public struct ActivityLog: Encodable, Sendable {
public let type: String = "activity"
public let trace: ActivityTrace
public let store: String
public init(storeName: String, trace: ActivityTrace) {
self.store = storeName
self.trace = trace
}
}
public struct DidCreateDispatcherLog: Encodable, Sendable {
public let type: String = "did_create_dispatcher"
public let store: String
public let dispatcher: String
public init(storeName: String, dispatcherName: String) {
self.store = storeName
self.dispatcher = dispatcherName
}
}
public struct DidDestroyDispatcherLog: Encodable, Sendable {
public let type: String = "did_destroy_dispatcher"
public let store: String
public let dispatcher: String
public init(storeName: String, dispatcherName: String) {
self.store = storeName
self.dispatcher = dispatcherName
}
}
/// An object default implementation of VergeStoreLogger.
/// It uses `os_log` to print inside.
/// There are OSLog object each type of action.
/// You can turn off logging each OSLog object.
public struct DefaultStoreLogger: StoreLogger {
public static var `default`: Self {
.init()
}
public let commitLog = OSLog(subsystem: "VergeStore", category: "Commit")
public let activityLog = OSLog(subsystem: "VergeStore", category: "Activity")
public let dispatcherCreationLog = OSLog(subsystem: "VergeStore", category: "Dispatcher_Creation")
public let dispatcherDestructionLog = OSLog(subsystem: "VergeStore", category: "Dispatcher_Destruction")
public init() {
}
private static let encoder: JSONEncoder = {
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
if #available(iOS 11.0, macOS 10.13, tvOS 11, watchOS 4, *) {
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
} else {
encoder.outputFormatting = [.prettyPrinted]
}
return encoder
}()
public func didCommit(log: CommitLog, sender: AnyObject) {
Task { [commitLog]in
let string = String(data: try! DefaultStoreLogger.encoder.encode(log), encoding: .utf8)!
os_log("%@", log: commitLog, type: .default, string)
}
}
public func didSendActivity(log: ActivityLog, sender: AnyObject) {
Task { [activityLog] in
let string = String(data: try! DefaultStoreLogger.encoder.encode(log), encoding: .utf8)!
os_log("%@", log: activityLog, type: .default, string)
}
}
public func didCreateDispatcher(log: DidCreateDispatcherLog, sender: AnyObject) {
Task { [dispatcherCreationLog] in
let string = String(data: try! DefaultStoreLogger.encoder.encode(log), encoding: .utf8)!
os_log("%@", log: dispatcherCreationLog, type: .default, string)
}
}
public func didDestroyDispatcher(log: DidDestroyDispatcherLog, sender: AnyObject) {
Task { [dispatcherDestructionLog] in
let string = String(data: try! DefaultStoreLogger.encoder.encode(log), encoding: .utf8)!
os_log("%@", log: dispatcherDestructionLog, type: .default, string)
}
}
}
================================================
FILE: Sources/Verge/Logging/MutationTrace.swift
================================================
//
// Copyright (c) 2020 muukii
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
public protocol HasTraces {
var traces: [MutationTrace] { get }
}
/// A trace that indicates the mutation where comes from.
public struct MutationTrace: Encodable, Equatable, Sendable {
public static func == (lhs: MutationTrace, rhs: MutationTrace) -> Bool {
lhs.createdAt == rhs.createdAt &&
lhs.name == rhs.name &&
lhs.file.description == rhs.file.description &&
lhs.function.description == rhs.function.description &&
lhs.line == rhs.line
}
public let createdAt: Date = .init()
public let name: String
public let file: StaticString
public let function: StaticString
public let line: UInt
public init(
name: String = "",
file: StaticString = #file,
function: StaticString = #function,
line: UInt = #line
) {
self.name = name
self.file = file
self.function = function
self.line = line
}
}
extension StaticString: @retroactive Encodable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(description)
}
}
================================================
FILE: Sources/Verge/Logging/RuntimeError.swift
================================================
//
// Copyright (c) 2020 Hiroshi Kimura(muukii)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public enum RuntimeError: Swift.Error {
case recoveredStateFromReceivingOlderVersion(latestState: AnyChangesType, receivedState: AnyChangesType)
case recursiveleyCommit(storeName: String, traces: [MutationTrace])
}
================================================
FILE: Sources/Verge/Logging/RuntimeSanitizer.swift
================================================
//
// Copyright (c) 2020 Hiroshi Kimura(muukii)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public struct RuntimeSanitizer: Sendable {
nonisolated(unsafe)
public static var global = RuntimeSanitizer()
public var isSanitizerStateReceivingByCorrectOrder: Bool = false
public var isRecursivelyCommitDetectionEnabled: Bool = false
public var onDidFindRuntimeError: @Sendable (RuntimeError) -> Void = { _ in }
public init() {
}
}
================================================
FILE: Sources/Verge/Logging/StoreLogger.swift
================================================
//
// Copyright (c) 2020 Hiroshi Kimura(muukii)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
/// A protocol to register logger and get the event VergeStore emits.
public protocol StoreLogger {
func didCommit(log: CommitLog, sender: AnyObject)
func didSendActivity(log: ActivityLog, sender: AnyObject)
func didCreateDispatcher(log: DidCreateDispatcherLog, sender: AnyObject)
func didDestroyDispatcher(log: DidDestroyDispatcherLog, sender: AnyObject)
}
================================================
FILE: Sources/Verge/Sendable.swift
================================================
final class UnsafeSendableClass<T>: @unchecked Sendable {
var value: T
init(_ value: T) {
self.value = value
}
}
struct UnsafeSendableWeak<T: AnyObject>: @unchecked Sendable {
weak var value: T?
init(_ value: T) {
self.value = value
}
}
struct UnsafeSendableStruct<T: ~Copyable>: ~Copyable, @unchecked Sendable {
var value: T
init(_ value: consuming T) {
self.value = value
}
consuming func send() -> sending T {
return value
}
consuming func with<Return: ~Copyable>(_ mutation: (inout sending T) throws -> sending Return) rethrows -> sending Return {
try mutation(&value)
}
}
func withUnsafeSending<T: ~Copyable>(_ value: consuming T) -> sending T {
UnsafeSendableStruct(value).send()
}
================================================
FILE: Sources/Verge/Store/AnyTargetQueue.swift
================================================
//
// Copyright (c) 2020 Hiroshi Kimura(Muukii) <muukii.app@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
import Atomics
public protocol TargetQueueType {
func execute(_ workItem: sending @escaping @Sendable () -> Void)
}
/// Describes queue to dispatch event
/// Currently light-weight impl
/// A reason why class is to take an object identifier.
public final class AnyTargetQueue: TargetQueueType {
private let _execute: (@escaping @Sendable () -> Void) -> Void
fileprivate init(
_execute: @escaping (@escaping @Sendable () -> Void) -> Void
) {
self._execute = _execute
}
public func execute(_ workItem: @escaping @Sendable () -> Void) {
_execute(workItem)
}
}
public protocol MainActorTargetQueueType {
func execute(_ workItem: @escaping @MainActor () -> Void)
}
extension MainActorTargetQueueType where Self == ImmediateMainActorTargetQueue {
public static func mainIsolated() -> Self {
.init()
}
public static var main: Self {
.shared
}
}
extension MainActorTargetQueueType where Self == HoppingMainActorTargetQueue {
/// It dispatches to main-queue asynchronously always.
public static var asyncMain: Self {
return .init()
}
}
/// always dispatches to main-queue asynchronously
public struct HoppingMainActorTargetQueue: MainActorTargetQueueType {
init() {
}
public func execute(_ workItem: @escaping @MainActor () -> Void) {
DispatchQueue.main.async {
workItem()
}
}
}
/// It dispatches to main-queue as possible as synchronously. Otherwise, it dispatches asynchronously.
public struct ImmediateMainActorTargetQueue: Sendable, MainActorTargetQueueType {
public static let shared = Self()
private let numberEnqueued = ManagedAtomic<UInt64>.init(0)
init() {
}
public func execute(_ workItem: @escaping @MainActor () -> Void) {
let previousNumberEnqueued = numberEnqueued.loadThenWrappingIncrement(ordering: .sequentiallyConsistent)
if Thread.isMainThread && previousNumberEnqueued == 0 {
MainActor.assumeIsolated {
workItem()
}
numberEnqueued.wrappingDecrement(ordering: .sequentiallyConsistent)
} else {
DispatchQueue.main.async {
workItem()
self.numberEnqueued.wrappingDecrement(ordering: .sequentiallyConsistent)
}
}
}
}
private enum StaticMember {
static let serialBackgroundDispatchQueue: DispatchQueue = .init(
label: "org.verge.background",
qos: .default,
attributes: [],
autoreleaseFrequency: .workItem,
target: nil
)
}
extension TargetQueueType where Self == Queues.Passthrough {
/// Returns a instance that never dispatches.
/// The Sink use this targetQueue performs in the queue which the upstream commit dispatched.
public static var passthrough: Queues.Passthrough {
.init()
}
}
extension TargetQueueType where Self == Queues.AsyncBackground {
/// It dispatches to the serial background queue asynchronously.
public static var asyncSerialBackground: Self {
self.init()
}
}
extension TargetQueueType where Self == AnyTargetQueue {
/// Use specified queue, always dispatches
public static func specific(_ targetQueue: DispatchQueue) -> AnyTargetQueue {
return .init { workItem in
targetQueue.async(execute: workItem)
}
}
/// Enqueue first item on current-thread(synchronously).
/// From then, using specified queue.
public static func startsFromCurrentThread(andUse queue: some TargetQueueType) -> AnyTargetQueue {
let numberEnqueued = ManagedAtomic<Bool>.init(true)
let execute = queue.execute
return .init { workItem in
let isFirst = numberEnqueued.loadThenLogicalAnd(with: false, ordering: .relaxed)
if isFirst {
workItem()
} else {
execute(workItem)
}
}
}
/// Enqueue first item on current-thread(synchronously).
/// From then, using specified queue.
public static func startsFromCurrentThread(andUse queue: some MainActorTargetQueueType) -> AnyTargetQueue {
return startsFromCurrentThread(andUse: Queues.MainActor(queue))
}
}
extension HoppingMainActorTargetQueue {
/// It dispatches to main-queue asynchronously always.
public static var asyncMain: HoppingMainActorTargetQueue {
return .init()
}
/// It dispatches to main-queue as possible as synchronously. Otherwise, it dispatches asynchronously from other background-thread.
public static var main: ImmediateMainActorTargetQueue {
return .shared
}
/// It dispatches to main-queue as possible as synchronously. Otherwise, it dispatches asynchronously from other background-thread.
/// This create isolated queue against using `.main`.
public static func mainIsolated() -> ImmediateMainActorTargetQueue {
return .init()
}
}
public enum Queues {
struct MainActor<Underlying: MainActorTargetQueueType>: TargetQueueType {
let underlying: Underlying
init(_ underlying: Underlying) {
self.underlying = underlying
}
public func execute(_ workItem: sending @escaping @Sendable () -> Void) {
underlying.execute(workItem)
}
}
public struct Passthrough: TargetQueueType {
public func execute(_ workItem: sending @escaping @Sendable () -> Void) {
workItem()
}
}
public struct AsyncBackground: TargetQueueType {
private let executor: BackgroundActor = .init()
public func execute(_ workItem: sending @escaping @Sendable () -> Void) {
Task { [executor, workItem] in
await executor.perform(workItem)
}
}
private actor BackgroundActor: Actor {
init() {
}
func perform<R>(_ operation: sending @Sendable () throws -> R) rethrows -> R {
try operation()
}
}
}
}
================================================
FILE: Sources/Verge/Store/Changes.swift
================================================
//
// Copyright (c) 2020 Hiroshi Kimura(Muukii) <muukii.app@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
@_spi(Internal) import TypedComparator
import StateStruct
private let _shared_changesDeallocationQueue = BackgroundDeallocationQueue()
public protocol AnyChangesType: AnyObject, Sendable {
var traces: [MutationTrace] { get }
var version: UInt64 { get }
}
public protocol ChangesType<Value>: AnyChangesType {
associatedtype Value
var previousPrimitive: Value? { get }
var primitive: Value { get }
var previous: Self? { get }
var modification: Modification? { get }
func asChanges() -> Changes<Value>
}
/// An immutable data object to achieve followings:
/// - To know a property has been modified. (It contains 2 instances (old, new))
/// - To avoid copying cost with wrapping reference type - So, you can embed this object on the other state.
///
/// ```swift
/// struct MyState: Equatable {
/// var name: String
/// var age: String
/// var height: String
/// }
/// ```
///
/// ```swift
/// let changes: Changes<MyState>
/// ```
///
/// It can be accessed with properties of MyState by dynamicMemberLookup
/// ```swift
/// changes.name
/// ```
///
/// It would be helpful to update UI partially
/// ```swift
/// func updateUI(changes: Changes<MyState>) {
///
/// changes.ifChanged(\.name) { name in
/// // update UI
/// }
///
/// changes.ifChanged(\.age) { age in
/// // update UI
/// }
///
/// changes.ifChanged(\.height) { height in
/// // update UI
/// }
/// }
/// ```
///
/// - Attention: Equalities calculates with pointer-personality basically, if the Value type compatibles `Equatable`, it does using also Value's equalities.
/// This means Changes will return equals if different pointer but the value is the same.
@dynamicMemberLookup
public final class Changes<Value>: @unchecked Sendable, ChangesType, Equatable, HasTraces {
public typealias ChangesKeyPath<T> = KeyPath<Value, T>
public static func == (lhs: Changes<Value>, rhs: Changes<Value>) -> Bool {
lhs === rhs
}
// MARK: - Stored Properties
public let previous: Changes<Value>?
private let innerBox: InnerBox
public private(set) var version: UInt64
// MARK: - Computed Properties
@available(*, deprecated, renamed: "previousPrimitive")
public var old: Value? { previousPrimitive }
@available(*, deprecated, renamed: "primitive")
public var current: Value { primitive }
/// Returns a previous value as primitive
/// - Important: a returns value won't change against pointer-personality
public var previousPrimitive: Value? { _read { yield previous?.primitive } }
/// Returns a value as primitive
/// - Important: a returns value won't change against pointer-personality
public var primitive: Value { _read { yield innerBox.value } }
public var primitiveBox: InnerBox { innerBox }
/// Returns a value as primitive
/// - Important: a returns value won't change against pointer-personality
public var root: Value { _read { yield innerBox.value } }
public let traces: [MutationTrace]
public let modification: Modification?
public let _transaction: Transaction
// MARK: - Initializers
public convenience init(
old: consuming Value?,
new: consuming Value
) {
self.init(
previous: old.map { .init(old: nil, new: $0) },
innerBox: .init(consume new),
version: 0,
traces: [],
modification: nil,
transaction: .init()
)
}
private init(
previous: Changes<Value>?,
innerBox: InnerBox,
version: UInt64,
traces: [MutationTrace],
modification: consuming Modification?,
transaction: Transaction
) {
self.previous = previous
self.innerBox = innerBox
self.version = version
self.traces = traces
self.modification = modification
self._transaction = transaction
vergeSignpostEvent("Changes.init", label: "\(type(of: self))")
}
deinit {
vergeSignpostEvent("Changes.deinit", label: "\(type(of: self))")
let unsafeBox = UnsafeSendableStruct(innerBox)
Task {
await _shared_changesDeallocationQueue.releaseObjectInBackground(object: unsafeBox.value)
}
}
@inline(__always)
private func cloneWithDropsPrevious() -> Changes<Value> {
return .init(
previous: nil,
innerBox: innerBox,
version: version,
traces: traces,
modification: nil,
transaction: _transaction
)
}
func replacePrevious(_ previous: Changes<Value>) -> Changes<Value> {
return .init(
previous: previous,
innerBox: innerBox,
version: version,
traces: traces,
modification: nil,
transaction: _transaction
)
}
@inlinable
public func asChanges() -> Changes<Value> {
self
}
/// Returns a Changes object that dropped previous value.
/// It returns always true in `ifChanged`
public func droppedPrevious() -> Changes<Value> {
cloneWithDropsPrevious()
}
@inlinable
public subscript<T>(dynamicMember keyPath: KeyPath<Value, T>) -> T {
_read {
yield primitive[keyPath: keyPath]
}
}
/// Returns a new instance that projects value by transform closure.
///
/// - Warning: modification would be dropped.
public func map<U>(_ transform: (borrowing Value) throws -> U) rethrows -> Changes<U> {
let signpost = VergeSignpostTransaction("Changes.map")
defer {
signpost.end()
}
return Changes<U>(
previous: try previous.map { try $0.map(transform) },
innerBox: try innerBox.map(transform),
version: version,
traces: traces,
modification: modification,
transaction: _transaction
)
}
/**
Returns an ``Changes`` containing the value of mapping the given key path over the root value if value present.
*/
public func mapIfPresent<U>(_ keyPath: KeyPath<Value, U?>) -> Changes<U>? {
guard self[dynamicMember: keyPath] != nil else {
return nil
}
return Changes<U>(
previous: previous.flatMap { $0.mapIfPresent(keyPath) },
innerBox: innerBox.map { $0[keyPath: keyPath]! },
version: version,
traces: traces,
modification: nil,
transaction: _transaction
)
}
/**
Returns an ``Changes`` containing the value of mapping the given key path over the root value if value present.
*/
@available(*, deprecated, renamed: "mapIfPresent")
public func _beta_map<U>(_ keyPath: KeyPath<Value, U?>) -> Changes<U>? {
mapIfPresent(keyPath)
}
public func makeNextChanges(
with nextNewValue: Value,
modification: Modification?,
transaction: Transaction
) -> Changes<Value> {
let previous = cloneWithDropsPrevious()
let nextVersion = previous.version &+ 1
return Changes<Value>.init(
previous: previous,
innerBox: .init(nextNewValue),
version: nextVersion,
traces: traces,
modification: modification,
transaction: transaction
)
}
@discardableResult
public func _read<Return>(perform: (borrowing Value) -> Return) -> Return {
perform(innerBox.value)
}
}
// MARK: - Primitive methods
extension Changes {
/// Takes a composed value if it's changed from old value.
@inline(__always)
public func takeIfChanged<Composed>(
_ compose: (Value) throws -> Composed,
_ comparer: some TypedComparator<Composed>
) rethrows -> Composed? {
try _takeIfChanged(compose, comparer)
}
@inline(__always)
fileprivate func _takeIfChanged<each Element>(
_ compose: (Value) throws -> (repeat each Element),
_ comparer: consuming some TypedComparator<(repeat each Element)>
) rethrows -> (repeat each Element)? {
let current = self.primitive
guard let previousValue = previous else {
return try compose(consume current)
}
let old = previousValue.primitive
let composedFromCurrent = try compose(consume current)
guard !comparer(try compose(consume old), composedFromCurrent) else {
return nil
}
return composedFromCurrent
}
@inline(__always)
fileprivate func _takeIfChanged_packed<each Element: Equatable>(
_ compose: (Value) throws -> (repeat each Element)
) rethrows -> (repeat each Element)? {
let current = self.primitive
guard let previousValue = previous else {
return try compose(consume current)
}
let old = previousValue.primitive
let composedFromCurrent = try compose(consume current)
let composedFromOld = try compose(old)
guard !areEqual(
(repeat each composedFromOld),
(repeat each composedFromCurrent)
) else {
return nil
}
return composedFromCurrent
}
@inline(__always)
fileprivate func _takeIfChanged_packed_nonEquatable<each Element>(
_ compose: (Value) throws -> (repeat each Element),
comparator: some TypedComparator<(repeat each Element)>
) rethrows -> (repeat each Element)? {
let current = self.primitive
guard let previousValue = previous else {
return try compose(consume current)
}
let old = previousValue.primitive
let composedFromCurrent = try compose(consume current)
let composedFromOld = try compose(old)
let isEqual = comparator(composedFromOld, composedFromCurrent)
guard isEqual == false else {
return nil
}
return composedFromCurrent
}
/// Performs a closure if the selected value changed from the previous one.
///
/// - Parameters:
/// - compose: A closure that projects a composed value from self. that executes with both of value(new and old).
/// - comparer: A Comparer that checks if the composed values are different between current and old.
/// - perform: A closure that executes any operation if composed value changed.
///
/// - Returns: An instance that returned from the perform closure if performed.
@inline(__always)
public func ifChanged<Composed, Result>(
_ compose: (Value) -> Composed,
_ comparer: some TypedComparator<Composed>,
_ perform: (Composed) throws -> Result
) rethrows -> Result? {
guard let result = takeIfChanged(compose, comparer) else {
return nil
}
return try perform(result)
}
}
// MARK: - Convenience methods
extension Changes {
/// Takes a composed value if it's changed from old value.
@inline(__always)
public func takeIfChanged<Composed: Equatable>(
_ compose: (Value) throws -> Composed
) rethrows -> Composed? {
try takeIfChanged(compose, .equality())
}
public func ifChanged<Composed: Equatable>(
_ compose: (Value) -> Composed
) -> IfChangedBox<Value, Composed> {
guard let result = takeIfChanged(compose) else {
return .init()
}
return .init(value: consume result, source: innerBox)
}
/**
Packed
```
state.ifChanged({ $0.name }).do { value in
...
}
```
*/
public borrowing func ifChanged<each Element: Equatable>(
_ compose: (borrowing Value) -> (repeat each Element)
) -> IfChangedBox<Value, (repeat each Element)> {
guard let result = _takeIfChanged_packed(compose) else {
return .init()
}
return .init(value: (repeat each result), source: innerBox)
}
public borrowing func ifChanged<each Element>(
_ compose: (borrowing Value) -> (repeat each Element),
comparator: some TypedComparator<(repeat each Element)>
) -> IfChangedBox<Value, (repeat each Element)> {
guard let result = _takeIfChanged_packed_nonEquatable(compose, comparator: comparator) else {
return .init()
}
return .init(value: (repeat each result), source: innerBox)
}
/**
singular variant
*/
public func ifChanged<T: Equatable>(
_ keyPath: KeyPath<Value, T>
) -> IfChangedBox<Value, T> {
ifChanged({ $0[keyPath: keyPath] })
}
/**
multiple variant
*/
public func ifChanged<each T: Equatable>(
_ keyPaths: repeat KeyPath<Value, each T>
) -> IfChangedBox<Value, (repeat each T)> {
ifChanged({ (repeat $0[keyPath: each keyPaths]) })
}
}
// MARK: - Has changes
extension Changes {
/// Returns boolean that indicates value specified by keyPath contains changes with compared old and new.
@inline(__always)
public func hasChanges<T: Equatable>(_ keyPath: ChangesKeyPath<T>) -> Bool {
hasChanges(keyPath, EqualityComparator())
}
/// Returns boolean that indicates value specified by keyPath contains changes with compared old and new.
@inline(__always)
public func hasChanges<T>(
_ keyPath: ChangesKeyPath<T>,
_ comparer: some TypedComparator<T>
) -> Bool {
hasChanges({ $0[keyPath: keyPath] }, comparer)
}
@inline(__always)
public func hasChanges<Composed: Equatable>(
_ compose: (Value) -> Composed
) -> Bool {
hasChanges(compose, EqualityComparator())
}
@inline(__always)
public func hasChanges<Composed>(
_ compose: (Value) -> Composed,
_ comparer: some TypedComparator<Composed>
) -> Bool {
takeIfChanged(compose, comparer) != nil
}
}
// MARK: - NoChanges
extension Changes {
/// Returns boolean that indicates value specified by keyPath contains **NO** changes with compared old and new.
@inline(__always)
public func noChanges<T: Equatable>(_ keyPath: ChangesKeyPath<T>) -> Bool {
!hasChanges(keyPath, EqualityComparator())
}
/// Returns boolean that indicates value specified by keyPath contains **NO** changes with compared old and new.
@inline(__always)
public func noChanges<T>(_ keyPath: ChangesKeyPath<T>, _ comparer: some TypedComparator<T>) -> Bool {
!hasChanges(keyPath, comparer)
}
}
extension Changes: CustomReflectable {
public var customMirror: Mirror {
Mirror(
self,
children: [
"version": version,
"previous": previous as Any,
"primitive": primitive,
"transaction": _transaction,
"traces": traces,
"modification": modification as Any,
],
displayStyle: .struct,
ancestorRepresentation: .generated
)
}
}
// MARK: - Nested Types
extension Changes {
public typealias InnerBox = _BackingStorage<Value>
}
extension Changes where Value: Equatable {
public var hasChanges: Bool {
previousPrimitive != primitive
}
public func ifChanged() -> IfChangedBox<Value, Value> {
ifChanged({ $0 })
}
}
public struct IfChangedBox<Source, T>: ~Copyable {
public let value: T?
@usableFromInline
let source: Changes<Source>.InnerBox?
init(value: consuming T, source: Changes<Source>.InnerBox) {
self.value = consume value
self.source = source
}
init() {
self.value = nil
self.source = nil
}
@discardableResult
@inlinable
public consuming func `do`<Return>(_ perform: (consuming T) throws -> Return) rethrows -> Return? {
if let value {
return try perform(consume value)
}
return nil
}
@discardableResult
@inlinable
public consuming func doWithSource<Return>(_ perform: (consuming Changes<Source>.InnerBox) throws -> Return) rethrows -> Return? {
if let source {
return try perform(source)
}
return nil
}
}
================================================
FILE: Sources/Verge/Store/DetachedDispatcher.swift
================================================
//
// Copyright (c) 2019 muukii
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
public final class DetachedDispatcher<State, Activity: Sendable, Scope>: StoreDriverType
{
public let store: Store<State, Activity>
public let scope: WritableKeyPath<State, Scope> & Sendable
init(
store: Store<State, Activity>,
scope: WritableKeyPath<State, Scope> & Sendable
) {
self.store = store
self.scope = scope
}
}
================================================
FILE: Sources/Verge/Store/KeyObject.swift
================================================
import Foundation
@_spi(Internal)
public final class KeyObject<Content: Hashable>: NSObject, NSCopying {
public func copy(with zone: NSZone? = nil) -> Any {
return KeyObject(content: content)
}
public let content: Content
public init(content: consuming Content) {
self.content = content
}
public override var hash: Int {
content.hashValue
}
public override func isEqual(_ object: Any?) -> Bool {
guard let other = object as? KeyObject<Content> else {
return false
}
guard content == other.content else { return false }
return true
}
}
================================================
FILE: Sources/Verge/Store/NonAtomicCounter.swift
================================================
//
// Copyright (c) 2020 Hiroshi Kimura(Muukii) <muukii.app@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
/// A container that manages raw value to describe mark as updated.
public struct NonAtomicCounter: Hashable, Sendable {
private(set) public var value: UInt64 = 0
public init() {}
public mutating func increment() {
value &+= 1
}
}
================================================
FILE: Sources/Verge/Store/Pipeline.swift
================================================
//
// Copyright (c) 2020 Hiroshi Kimura(Muukii) <muukii.app@gmail.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
import TypedComparator
public enum ContinuousResult<Output> {
case new(Output)
case noUpdates
}
extension ContinuousResult: Equatable where Output: Equatable {
}
/// A filter object that yields the output produced from the input.
public protocol PipelineType<Input, Output>: Sendable {
associatedtype Input
associatedtype Output
associatedtype Storage = Void
func makeStorage() -> Storage
/// Yields the output from the input.
func yield(_ input: Input, storage: inout Storage) -> Output
/// Yields the output from the input if it's needed
func yieldContinuously(_ input: Input, storage: inout Storage) -> ContinuousResult<Output>
}
extension PipelineType where Storage == Void {
public func makeStorage() {
()
}
}
/// It produces outputs from inputs with their own conditions.
///
/// Against just using map closure, it can drop output if there are no changes.
/// This will be helpful in performance. Therefore most type parameters require Equatable.
public enum Pipelines {
/// KeyPath based pipeline, light weight operation just take value from source.
public struct ChangesSelectPassthroughPipeline<Source, Output>: PipelineType {
public typealias Storage = Void
public typealias Input = Changes<Source>
public let selector: @Sendable (borrowing Input.Value) -> Output
public init(
selector: @escaping @Sendable (borrowing Input.Value) -> Output
) {
self.selector = selector
}
public func yieldContinuously(_ input: Input, storage: inout Storage) -> ContinuousResult<Output> {
let target = input._read(perform: selector)
return .new(consume target)
}
public func yield(_ input: Input, storage: inout Storage) -> Output {
input._read(perform: selector)
}
}
/// KeyPath based pipeline, light weight operation just take value from source.
public struct ChangesSelectPipeline<Source, Output: Equatable>: PipelineType {
public typealias Input = Changes<Source>
public let selector: @Sendable (borrowing Input.Value) -> Output
public let additionalDropCondition: (@Sendable (Input) -> Bool)?
public init(
selector: @escaping @Sendable (borrowing Input.Value) -> Output,
additionalDropCondition: (@Sendable (Input) -> Bool)?
) {
self.selector = selector
self.additionalDropCondition = additionalDropCondition
}
public func yieldContinuously(_ input: Input, storage: inout Storage) -> ContinuousResult<Output> {
guard let previous = input.previous else {
return .new(input._read(perform: selector))
}
let target = input._read(perform: selector)
guard
previous._read(perform: selector) == target
else {
guard let additionalDropCondition = additionalDropCondition, additionalDropCondition(input)
else {
return .new(consume target)
}
return .noUpdates
}
return .noUpdates
}
public func yield(_ input: Input, storage: inout Storage) -> Output {
input._read(perform: selector)
}
public func drop(while predicate: @escaping @Sendable (Input) -> Bool) -> Self {
return .init(
selector: selector,
additionalDropCondition: additionalDropCondition.map { currentCondition in
{ input in
currentCondition(input) || predicate(input)
}
} ?? predicate
)
}
}
/// Closure based pipeline,
public struct ChangesMapPipeline<Source, Intermediate, Output: Equatable>: PipelineType {
public typealias Storage = Void
public typealias Input = Changes<Source>
// MARK: - Properties
public let intermediate: @Sendable (Input.Value) -> PipelineIntermediate<Intermediate>
public let transform: @Sendable (Intermediate) -> Output
public let additionalDropCondition: (@Sendable (Input) -> Bool)?
public init(
@PipelineIntermediateBuilder intermediate: @escaping @Sendable (
Input.Value
) -> PipelineIntermediate<Intermediate>,
transform: @escaping @Sendable (Intermediate) -> Output,
additionalDropCondition: (@Sendable (Input) -> Bool)?
) {
self.intermediate = intermediate
self.transform = transform
self.additionalDropCondition = additionalDropCondition
}
// MARK: - Functions
public func yieldContinuously(_ input: Input, storage: inout Storage) -> ContinuousResult<Output> {
guard let previous = input.previous else {
return .new(yield(input, storage: &storage))
}
let previousIntermediate = intermediate(previous.primitive)
let newIntermediate = intermediate(input.primitive)
guard previousIntermediate == newIntermediate else {
let previousMapped = transform(previousIntermediate.value)
let newMapped = transform(newIntermediate.value)
guard previousMapped == newMapped else {
guard let additionalDropCondition = additionalDropCondition,
additionalDropCondition(input)
else {
return .new(newMapped)
}
return .noUpdates
}
return .noUpdates
}
return .noUpdates
}
public func yield(_ input: Input, storage: inout Storage) -> Output {
transform(intermediate(input.primitive).value)
}
public func drop(while predicate: @escaping @Sendable (Input) -> Bool) -> Self {
return .init(
intermediate: intermediate,
transform: transform,
additionalDropCondition: additionalDropCondition.map { currentCondition in
{ input in
currentCondition(input) || predicate(input)
}
} ?? predicate
)
}
}
public struct UniqueFilterEquatable<Map: MapFunction>: PipelineType where Map.Output: Equatable {
public typealias Input = Map.Input
public typealias Output = Map.Output
private let map: Map
public init(map: Map) {
self.map = map
}
public func makeStorage() -> VergeConcurrency.UnfairLockAtomic<Output?> {
.init(nil)
}
public func yield(_ input: Input, storage: inout Storage) -> Output {
let result = map.perform(input)
storage.swap(result)
return result
}
public func yieldContinuously(_ input: Input, storage: inout Storage) -> ContinuousResult<Output> {
// not to check if input has changed because storing the input may cause performance issue by copying.
let result = map.perform(input)
return storage.modify { value in
if value != result {
value = result
return .new(result)
} else {
return .noUpdates
}
}
}
}
public struct UniqueFilter<Map: MapFunction, OutputComparator: TypedComparator>: PipelineType
where OutputComparator.Input == Map.Output? {
public typealias Input = Map.Input
public typealias Output = Map.Output
private let map: Map
private let outputComparator: OutputComparator
public init(map: Map, outputComparator: OutputComparator) {
self.map = map
self.outputComparator = outputComparator
}
public func makeStorage() -> VergeConcurrency.UnfairLockAtomic<Output?> {
.init(nil)
}
public func yield(_ input: Input, storage: inout Storage) -> Output {
let result = map.perform(input)
storage.swap(result)
return result
}
public func yieldContinuously(_ input: Input, storage: inout Storage) -> ContinuousResult<Output> {
// not to check if input has changed because storing the input may cause performance issue by copying.
let result = map.perform(input)
return storage.modify { value in
if !outputComparator(value, result) {
value = result
return .new(result)
} else {
return .noUpdates
}
}
}
}
}
public protocol MapFunction: Sendable {
associatedtype Input
associatedtype Output
func perform(_ input: Input) -> Output
}
public struct AnyMapFunction<Input, Output>: MapFunction {
private let _perform: @Sendable (Inp
gitextract_zegsmnq1/
├── .github/
│ ├── FUNDING.yml
│ └── workflows/
│ └── CommitChecks.yml
├── .gitignore
├── .spi.yml
├── Development/
│ ├── .gitignore
│ ├── Development/
│ │ ├── Resources/
│ │ │ ├── Assets.xcassets/
│ │ │ │ ├── AccentColor.colorset/
│ │ │ │ │ └── Contents.json
│ │ │ │ ├── AppIcon.appiconset/
│ │ │ │ │ └── Contents.json
│ │ │ │ └── Contents.json
│ │ │ └── Preview Content/
│ │ │ └── Preview Assets.xcassets/
│ │ │ └── Contents.json
│ │ ├── Sources/
│ │ │ ├── BookBinding.swift
│ │ │ ├── BookLongList.swift
│ │ │ ├── BookReadingPropertyWrapper.swift
│ │ │ ├── BookStoreReader.swift
│ │ │ ├── ContentView.swift
│ │ │ └── DevelopmentApp.swift
│ │ ├── Tests/
│ │ │ └── DevelopmentTests.swift
│ │ └── UITests/
│ │ ├── ReadingTests.swift
│ │ └── StoreReaderTests.swift
│ ├── DevelopmentUITests/
│ │ ├── DevelopmentUITests.swift
│ │ └── DevelopmentUITestsLaunchTests.swift
│ ├── Project.swift
│ ├── Tuist/
│ │ ├── Package.resolved
│ │ └── Package.swift
│ ├── Tuist.swift
│ └── mise.toml
├── LICENSE
├── Package.resolved
├── Package.swift
├── README.md
├── Sources/
│ ├── Verge/
│ │ ├── Documentation.docc/
│ │ │ ├── Activity.md
│ │ │ ├── Changes.md
│ │ │ ├── ComputedProperty.md
│ │ │ ├── Derived.md
│ │ │ ├── Dispatcher.md
│ │ │ ├── Essentials/
│ │ │ │ └── Motivation.md
│ │ │ ├── Guides/
│ │ │ │ ├── Advanced Usage.md
│ │ │ │ ├── Basic Usage.md
│ │ │ │ └── Migration Guide v9.md
│ │ │ ├── Mutation.md
│ │ │ ├── Resources/
│ │ │ │ └── Tiny.md
│ │ │ ├── State.md
│ │ │ ├── Verge.Store.md
│ │ │ └── Verge.md
│ │ ├── Library/
│ │ │ ├── BackgroundDeallocationQueue.swift
│ │ │ ├── CachedMap.swift
│ │ │ ├── EventEmitter.swift
│ │ │ ├── InoutRef.swift
│ │ │ ├── Log.swift
│ │ │ ├── Signpost.swift
│ │ │ ├── StoreActivitySubscription.swift
│ │ │ ├── StoreStateSubscription.swift
│ │ │ ├── StoreSubscriptionBase.swift
│ │ │ ├── VergeAnyCancellable.swift
│ │ │ ├── VergeConcurrency+SynchronizationTracker.swift
│ │ │ ├── VergeConcurrency.swift
│ │ │ └── _BackingStorage+.swift
│ │ ├── Logging/
│ │ │ ├── ActivityTrace.swift
│ │ │ ├── DefaultStoreLogger.swift
│ │ │ ├── MutationTrace.swift
│ │ │ ├── RuntimeError.swift
│ │ │ ├── RuntimeSanitizer.swift
│ │ │ └── StoreLogger.swift
│ │ ├── Sendable.swift
│ │ ├── Store/
│ │ │ ├── AnyTargetQueue.swift
│ │ │ ├── Changes.swift
│ │ │ ├── DetachedDispatcher.swift
│ │ │ ├── KeyObject.swift
│ │ │ ├── NonAtomicCounter.swift
│ │ │ ├── Pipeline.swift
│ │ │ ├── Scan.swift
│ │ │ ├── StateType.swift
│ │ │ ├── Store+Combine.swift
│ │ │ ├── Store+RunLoop.swift
│ │ │ ├── Store.swift
│ │ │ ├── StoreDriverType+Accumulator.swift
│ │ │ ├── StoreDriverType.swift
│ │ │ ├── StoreMiddleware.swift
│ │ │ ├── StoreOperation.swift
│ │ │ ├── StoreType+Assignee.swift
│ │ │ ├── StoreType+BindingDerived.swift
│ │ │ ├── StoreType+Derived.swift
│ │ │ ├── StoreWrapperType.swift
│ │ │ ├── Transaction.swift
│ │ │ └── UIStateStore.swift
│ │ ├── SwiftUI/
│ │ │ ├── .swift
│ │ │ ├── OnReceive.swift
│ │ │ ├── Reading.swift
│ │ │ ├── StoreObject.swift
│ │ │ └── StoreReader.swift
│ │ ├── Utility/
│ │ │ ├── Edge.swift
│ │ │ ├── ReferenceEdge.swift
│ │ │ └── ThunkToMainActor.swift
│ │ ├── Verge.swift
│ │ └── macros.swift
│ ├── VergeClassic/
│ │ ├── Emitter.swift
│ │ ├── Extensions.swift
│ │ ├── Info.plist
│ │ ├── Storage+Rx.swift
│ │ ├── Storage.swift
│ │ ├── Verge+Extension.swift
│ │ ├── Verge.h
│ │ └── VergeClassic.swift
│ ├── VergeMacros/
│ │ └── Source.swift
│ ├── VergeMacrosPlugin/
│ │ ├── KeyPathMap.swift
│ │ ├── MacroError.swift
│ │ └── Plugin.swift
│ ├── VergeNormalizationDerived/
│ │ ├── DerivedMaking+.swift
│ │ ├── DerivedResult.swift
│ │ ├── EntityType+Typealias.swift
│ │ ├── EntityWrapper.swift
│ │ ├── NonNullEntityWrapper.swift
│ │ ├── Pipelines.swift
│ │ ├── StoreType+.swift
│ │ └── VergeNormalizationDerived.swift
│ ├── VergeRx/
│ │ ├── Extensions.swift
│ │ └── Store+Rx.swift
│ └── VergeTiny/
│ └── Source.swift
├── StoreReaderDemo/
│ └── StoreReaderDemo/
│ ├── Assets.xcassets/
│ │ ├── AccentColor.colorset/
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── ContentView.swift
│ ├── Preview Content/
│ │ └── Preview Assets.xcassets/
│ │ └── Contents.json
│ └── StoreReaderDemoApp.swift
├── TaskManagerPlayground/
│ └── TaskManagerPlayground/
│ ├── Assets.xcassets/
│ │ ├── AccentColor.colorset/
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── Book.swift
│ ├── ContentView.swift
│ ├── Info.plist
│ ├── Preview Content/
│ │ └── Preview Assets.xcassets/
│ │ └── Contents.json
│ └── TaskManagerPlaygroundApp.swift
├── Tests/
│ ├── All.xctestplan
│ ├── DemoState.swift
│ ├── VergeMacrosTests/
│ │ └── KeyPathMapTests.swift
│ ├── VergeNormalizationDerivedTests/
│ │ ├── CombiningTests.swift
│ │ ├── DemoState.swift
│ │ └── VergeNormalizationDerivedTests.swift
│ ├── VergeRxTests/
│ │ ├── ChangedOperatorTests.swift
│ │ ├── DemoState.swift
│ │ ├── ReproduceDeadlockTests.swift
│ │ ├── SubjectCompletionTests.swift
│ │ └── VergeRxTests.swift
│ ├── VergeTests/
│ │ ├── AccumulationTests.swift
│ │ ├── ActivityTests.swift
│ │ ├── BindingDerivedTests.swift
│ │ ├── CachedMapTests.swift
│ │ ├── ChangesTests.swift
│ │ ├── ComparerTests.swift
│ │ ├── ConcurrencyTests.swift
│ │ ├── CopyPerformance.swift
│ │ ├── CounterTests.swift
│ │ ├── DemoState.swift
│ │ ├── DerivedTests.swift
│ │ ├── EdgeTests.swift
│ │ ├── EventEmitterTests.swift
│ │ ├── FilterTests.swift
│ │ ├── IsolatedContextTests.swift
│ │ ├── OldComparer.swift
│ │ ├── PerformanceTests.swift
│ │ ├── PipelineTests.swift
│ │ ├── ReferenceEdgeTests.swift
│ │ ├── Retain/
│ │ │ ├── PublisherCompletionTests.swift
│ │ │ └── StoreSinkSusbscriptionTests.swift
│ │ ├── RunLoopTests.swift
│ │ ├── Sample.swift
│ │ ├── StateTypeTests.swift
│ │ ├── StoreAndDerivedTests.swift
│ │ ├── StoreInitTests.swift
│ │ ├── StoreMiddlewareTests.swift
│ │ ├── StoreSinkTests.swift
│ │ ├── StoreTaskTests.swift
│ │ ├── SynchronizeDisplayValueTests.swift
│ │ ├── SyntaxTests.swift
│ │ ├── TransactionTests.swift
│ │ ├── Usage.swift
│ │ └── VergeStoreTests.swift
│ └── VergeTinyTests/
│ └── VergeTinyTests.swift
├── Verge.playground/
│ ├── Pages/
│ │ ├── Memo.xcplaygroundpage/
│ │ │ └── Contents.swift
│ │ ├── PlainStorePattern1.xcplaygroundpage/
│ │ │ └── Contents.swift
│ │ ├── PlainStorePattern2.xcplaygroundpage/
│ │ │ └── Contents.swift
│ │ ├── PlainStorePattern3.xcplaygroundpage/
│ │ │ └── Contents.swift
│ │ ├── PlainStorePattern4.xcplaygroundpage/
│ │ │ └── Contents.swift
│ │ ├── PlainStorePattern5.xcplaygroundpage/
│ │ │ └── Contents.swift
│ │ ├── VergeStorePartern2.xcplaygroundpage/
│ │ │ └── Contents.swift
│ │ └── VergeStorePattern.xcplaygroundpage/
│ │ └── Contents.swift
│ ├── Sources/
│ │ └── Wire.swift
│ └── contents.xcplayground
├── mise.toml
└── playgrounds/
├── .gitignore
└── PlaySwiftUI/
├── PlaySwiftUI/
│ ├── Assets.xcassets/
│ │ ├── AccentColor.colorset/
│ │ │ └── Contents.json
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── ContentView.swift
│ ├── PlaySwiftUIApp.swift
│ ├── Preview Content/
│ │ └── Preview Assets.xcassets/
│ │ └── Contents.json
│ └── Simple.swift
└── PlaySwiftUI.xcodeproj/
├── project.pbxproj
└── project.xcworkspace/
├── contents.xcworkspacedata
└── xcshareddata/
└── swiftpm/
└── Package.resolved
Condensed preview — 199 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (549K chars).
[
{
"path": ".github/FUNDING.yml",
"chars": 47,
"preview": "github: [muukii]\npatreon: muukii\nko_fi: muukii\n"
},
{
"path": ".github/workflows/CommitChecks.yml",
"chars": 898,
"preview": "name: CommitChecks\n\non:\n push:\n branches:\n - \"**\"\n\njobs:\n test:\n runs-on: macos-15\n\n steps:\n - uses"
},
{
"path": ".gitignore",
"chars": 1344,
"preview": "\n# Created by https://www.gitignore.io/api/swift\n\n### Swift ###\n# Xcode\n#\n# gitignore contributors: remember to update G"
},
{
"path": ".spi.yml",
"chars": 95,
"preview": "version: 1\nbuilder:\n configs:\n - documentation_targets: [Verge, VergeNormalizationDerived]\n"
},
{
"path": "Development/.gitignore",
"chars": 1206,
"preview": "### macOS ###\n# General\n.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must end with two\nIcon\n\n# Thumbnails\n._*\n\n# Files tha"
},
{
"path": "Development/Development/Resources/Assets.xcassets/AccentColor.colorset/Contents.json",
"chars": 123,
"preview": "{\n \"colors\" : [\n {\n \"idiom\" : \"universal\"\n }\n ],\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }"
},
{
"path": "Development/Development/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json",
"chars": 1591,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"iphone\",\n \"scale\" : \"2x\",\n \"size\" : \"20x20\"\n },\n {\n \"idiom\""
},
{
"path": "Development/Development/Resources/Assets.xcassets/Contents.json",
"chars": 63,
"preview": "{\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }\n}\n"
},
{
"path": "Development/Development/Resources/Preview Content/Preview Assets.xcassets/Contents.json",
"chars": 63,
"preview": "{\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }\n}\n"
},
{
"path": "Development/Development/Sources/BookBinding.swift",
"chars": 1248,
"preview": "\nimport SwiftUI\nimport Verge\n\nstruct BookBindingUsingReading: View {\n \n @ReadingObject<Store<BookBindingState, Never>>"
},
{
"path": "Development/Development/Sources/BookLongList.swift",
"chars": 1885,
"preview": "//\n// BookLongList.swift\n// Development\n//\n// Created by Muukii on 2025/03/26.\n//\n\nimport SwiftUI\nimport Verge\n\n@Trac"
},
{
"path": "Development/Development/Sources/BookReadingPropertyWrapper.swift",
"chars": 5032,
"preview": "import Verge\nimport SwiftUI\n\nstruct BookReading: View {\n \n var body: some View {\n ReadingSolution()\n }\n \n}\n\n@Trac"
},
{
"path": "Development/Development/Sources/BookStoreReader.swift",
"chars": 3262,
"preview": "import SwiftUI\nimport Verge\n\nstruct StoreReaderSolution: View {\n @State var id: Int = 0\n @State var outerValue: Int = "
},
{
"path": "Development/Development/Sources/ContentView.swift",
"chars": 1006,
"preview": "import SwiftUI\n\npublic struct ContentView: View {\n public init() {}\n\n public var body: some View {\n NavigationStack"
},
{
"path": "Development/Development/Sources/DevelopmentApp.swift",
"chars": 144,
"preview": "import SwiftUI\n\n@main\nstruct DevelopmentApp: App {\n var body: some Scene {\n WindowGroup {\n ContentV"
},
{
"path": "Development/Development/Tests/DevelopmentTests.swift",
"chars": 150,
"preview": "import Foundation\nimport XCTest\n\nfinal class DevelopmentTests: XCTestCase {\n func test_twoPlusTwo_isFour() {\n "
},
{
"path": "Development/Development/UITests/ReadingTests.swift",
"chars": 1613,
"preview": "import XCTest\n\nfinal class ReadingTests: XCTestCase {\n\n var app: XCUIApplication!\n\n override func setUpWithError() thr"
},
{
"path": "Development/Development/UITests/StoreReaderTests.swift",
"chars": 1080,
"preview": "import XCTest\n\nfinal class StoreReaderTests: XCTestCase {\n \n var app: XCUIApplication!\n \n override func setUpWithErr"
},
{
"path": "Development/DevelopmentUITests/DevelopmentUITests.swift",
"chars": 1411,
"preview": "//\n// DevelopmentUITests.swift\n// DevelopmentUITests\n//\n// Created by Muukii on 2025/03/22.\n//\n\nimport XCTest\n\nfinal "
},
{
"path": "Development/DevelopmentUITests/DevelopmentUITestsLaunchTests.swift",
"chars": 826,
"preview": "//\n// DevelopmentUITestsLaunchTests.swift\n// DevelopmentUITests\n//\n// Created by Muukii on 2025/03/22.\n//\n\nimport XCT"
},
{
"path": "Development/Project.swift",
"chars": 1138,
"preview": "import ProjectDescription\n\nlet project = Project(\n name: \"Development\",\n targets: [\n .target(\n name: \"Developm"
},
{
"path": "Development/Tuist/Package.resolved",
"chars": 2626,
"preview": "{\n \"originHash\" : \"fb710b9bf77d01a2d48fc3e3b210bfb0e949769f26d1ca1c351ac86995dca6fb\",\n \"pins\" : [\n {\n \"identit"
},
{
"path": "Development/Tuist/Package.swift",
"chars": 663,
"preview": "// swift-tools-version: 6.0\nimport PackageDescription\n\n#if TUIST\n import struct ProjectDescription.PackageSettings\n\n l"
},
{
"path": "Development/Tuist.swift",
"chars": 63,
"preview": "import ProjectDescription\n\nlet tuist = Tuist(project: .tuist())"
},
{
"path": "Development/mise.toml",
"chars": 25,
"preview": "[tools]\ntuist = \"4.44.3\"\n"
},
{
"path": "LICENSE",
"chars": 1063,
"preview": "MIT License\n\nCopyright (c) 2017 muukii\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof "
},
{
"path": "Package.resolved",
"chars": 4070,
"preview": "{\n \"originHash\" : \"e0239f7d7b3b817d553cd88ff38b3bf2eaf6a4d2aa61a19fb57aed0c303e01eb\",\n \"pins\" : [\n {\n \"identit"
},
{
"path": "Package.swift",
"chars": 3713,
"preview": "// swift-tools-version: 6.0\nimport CompilerPluginSupport\nimport PackageDescription\n\nlet package = Package(\n name: \"Verg"
},
{
"path": "README.md",
"chars": 17756,
"preview": "> [!WARNING]\n> Verge is stabilizing and transitioning to [swift-state-graph](https://github.com/VergeGroup/swift-state-g"
},
{
"path": "Sources/Verge/Documentation.docc/Activity.md",
"chars": 1662,
"preview": "# Activity\n\n## What Activity brings to us\n\nActivity enables Event-driven partially.\n\nVerge supports to send any events t"
},
{
"path": "Sources/Verge/Documentation.docc/Changes.md",
"chars": 2979,
"preview": "# ``Verge/Changes``\n\n`Changes<State>` wraps primitive value inside and previous one.\nThis data structure enables to get "
},
{
"path": "Sources/Verge/Documentation.docc/ComputedProperty.md",
"chars": 4578,
"preview": "# Computed Property\n\n## Overview\n\nA declaration to add a computed-property into the state. It helps to add a property th"
},
{
"path": "Sources/Verge/Documentation.docc/Derived.md",
"chars": 4181,
"preview": "# ``Verge/Derived``\n\nDerived’s functions are:\n\n- Computes the derived data from the state tree\n- Emit the updated data "
},
{
"path": "Sources/Verge/Documentation.docc/Dispatcher.md",
"chars": 6133,
"preview": "# ``Verge/DispatcherType``\n\nDispatcher allows us to update the state of the Store from away the store and to manage depe"
},
{
"path": "Sources/Verge/Documentation.docc/Essentials/Motivation.md",
"chars": 1283,
"preview": "# Motivation\n\n## Verge focuses use-cases in the real-world\n\nRecently, we could say the unidirectional data flow is a pop"
},
{
"path": "Sources/Verge/Documentation.docc/Guides/Advanced Usage.md",
"chars": 2129,
"preview": "# Advanced Usage\n\n## To keep performance and scalability\n\n## Adding a cachable computed property in a State\n\nWe can add "
},
{
"path": "Sources/Verge/Documentation.docc/Guides/Basic Usage.md",
"chars": 5114,
"preview": "# Basic Usage\n\nThis section shows you how we start to use Verge.\nIt’s very basic usage. You need to read Advanced Usage "
},
{
"path": "Sources/Verge/Documentation.docc/Guides/Migration Guide v9.md",
"chars": 1826,
"preview": "# Changes in v9\n\n## Store requires Equatable State\n\nFrom v8 complex implementations, v9 becomes it requires Equatable to"
},
{
"path": "Sources/Verge/Documentation.docc/Mutation.md",
"chars": 2098,
"preview": "# Mutation\n\n## What Mutation is\n\nThe only way to actually change state in a Store is by committing a mutation. Define a"
},
{
"path": "Sources/Verge/Documentation.docc/Resources/Tiny.md",
"chars": 1474,
"preview": "# Yet another super tiny store pattern with Verge/Tiny\n\nIn fact, `store-pattern` doesn't need something library to run.\n"
},
{
"path": "Sources/Verge/Documentation.docc/State.md",
"chars": 1253,
"preview": "# Thinking in single state tree (Not enforced)\n\nVergeStore uses a **single state-tree. (Recommended)** That means an obj"
},
{
"path": "Sources/Verge/Documentation.docc/Verge.Store.md",
"chars": 1922,
"preview": "# ``Verge/Store``\n\n- Store:\n - Should be a reference type object,\n - To share the state they manage to multiple su"
},
{
"path": "Sources/Verge/Documentation.docc/Verge.md",
"chars": 3694,
"preview": "# ``Verge``\n\n\n## 🛹 Small start unidirectional data flow\n\nVerge is designed for use from small and supports to scale. Set"
},
{
"path": "Sources/Verge/Library/BackgroundDeallocationQueue.swift",
"chars": 1869,
"preview": "//\n// Copyright (c) 2020 Hiroshi Kimura(Muukii) <muukii.app@gmail.com>\n//\n// Permission is hereby granted, free of charg"
},
{
"path": "Sources/Verge/Library/CachedMap.swift",
"chars": 5691,
"preview": "//\n// Copyright (c) 2020 muukii\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of"
},
{
"path": "Sources/Verge/Library/EventEmitter.swift",
"chars": 6741,
"preview": "//\n// Copyright (c) 2019 muukii\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of"
},
{
"path": "Sources/Verge/Library/InoutRef.swift",
"chars": 5745,
"preview": "//\n// Copyright (c) 2020 muukii\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of"
},
{
"path": "Sources/Verge/Library/Log.swift",
"chars": 979,
"preview": "//\n// Log.swift\n// VergeCore\n//\n// Created by muukii on 2020/02/24.\n// Copyright © 2020 muukii. All rights reserved."
},
{
"path": "Sources/Verge/Library/Signpost.swift",
"chars": 3088,
"preview": "import os\nimport Foundation\n\nnonisolated(unsafe)\npublic var _verge_signpost_enabled = ProcessInfo.processInfo.environmen"
},
{
"path": "Sources/Verge/Library/StoreActivitySubscription.swift",
"chars": 488,
"preview": "import Combine\nimport Atomics\n\n/**\n A subscription that is compatible with Combine’s Cancellable.\n You can manage asynch"
},
{
"path": "Sources/Verge/Library/StoreStateSubscription.swift",
"chars": 485,
"preview": "import Combine\nimport Atomics\n\n/**\n A subscription that is compatible with Combine's Cancellable.\n You can manage asynch"
},
{
"path": "Sources/Verge/Library/StoreSubscriptionBase.swift",
"chars": 2220,
"preview": "\npublic class StoreSubscriptionBase: Hashable, Cancellable {\n\n public static func == (lhs: StoreSubscriptionBase, rhs: "
},
{
"path": "Sources/Verge/Library/VergeAnyCancellable.swift",
"chars": 3270,
"preview": "import Combine\nimport Atomics\n\n/// A typealias to `Set<AnyCancellable>`.\npublic typealias VergeAnyCancellables = Set<Any"
},
{
"path": "Sources/Verge/Library/VergeConcurrency+SynchronizationTracker.swift",
"chars": 3242,
"preview": "//\n// Copyright (c) 2020 muukii\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of"
},
{
"path": "Sources/Verge/Library/VergeConcurrency.swift",
"chars": 12074,
"preview": "//\n// Copyright (c) 2020 muukii\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of"
},
{
"path": "Sources/Verge/Library/_BackingStorage+.swift",
"chars": 184,
"preview": "\nextension _BackingStorage {\n \n func map<U>(_ transform: (borrowing Value) throws -> U) rethrows -> _BackingStorage<U>"
},
{
"path": "Sources/Verge/Logging/ActivityTrace.swift",
"chars": 1389,
"preview": "//\n// Copyright (c) 2020 muukii\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of"
},
{
"path": "Sources/Verge/Logging/DefaultStoreLogger.swift",
"chars": 4582,
"preview": "//\n// Copyright (c) 2019 muukii\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of"
},
{
"path": "Sources/Verge/Logging/MutationTrace.swift",
"chars": 2203,
"preview": "//\n// Copyright (c) 2020 muukii\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of"
},
{
"path": "Sources/Verge/Logging/RuntimeError.swift",
"chars": 1346,
"preview": "//\n// Copyright (c) 2020 Hiroshi Kimura(muukii)\n//\n// Permission is hereby granted, free of charge, to any person obtain"
},
{
"path": "Sources/Verge/Logging/RuntimeSanitizer.swift",
"chars": 1489,
"preview": "//\n// Copyright (c) 2020 Hiroshi Kimura(muukii)\n//\n// Permission is hereby granted, free of charge, to any person obtain"
},
{
"path": "Sources/Verge/Logging/StoreLogger.swift",
"chars": 1517,
"preview": "//\n// Copyright (c) 2020 Hiroshi Kimura(muukii)\n//\n// Permission is hereby granted, free of charge, to any person obtain"
},
{
"path": "Sources/Verge/Sendable.swift",
"chars": 759,
"preview": "\nfinal class UnsafeSendableClass<T>: @unchecked Sendable {\n var value: T\n \n init(_ value: T) {\n self.value = value"
},
{
"path": "Sources/Verge/Store/AnyTargetQueue.swift",
"chars": 6865,
"preview": "//\n// Copyright (c) 2020 Hiroshi Kimura(Muukii) <muukii.app@gmail.com>\n//\n// Permission is hereby granted, free of charg"
},
{
"path": "Sources/Verge/Store/Changes.swift",
"chars": 16145,
"preview": "//\n// Copyright (c) 2020 Hiroshi Kimura(Muukii) <muukii.app@gmail.com>\n//\n// Permission is hereby granted, free of charg"
},
{
"path": "Sources/Verge/Store/DetachedDispatcher.swift",
"chars": 1459,
"preview": "//\n// Copyright (c) 2019 muukii\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of"
},
{
"path": "Sources/Verge/Store/KeyObject.swift",
"chars": 598,
"preview": "\nimport Foundation\n\n@_spi(Internal)\npublic final class KeyObject<Content: Hashable>: NSObject, NSCopying {\n\n public fun"
},
{
"path": "Sources/Verge/Store/NonAtomicCounter.swift",
"chars": 1421,
"preview": "//\n// Copyright (c) 2020 Hiroshi Kimura(Muukii) <muukii.app@gmail.com>\n//\n// Permission is hereby granted, free of charg"
},
{
"path": "Sources/Verge/Store/Pipeline.swift",
"chars": 16959,
"preview": "//\n// Copyright (c) 2020 Hiroshi Kimura(Muukii) <muukii.app@gmail.com>\n//\n// Permission is hereby granted, free of charg"
},
{
"path": "Sources/Verge/Store/Scan.swift",
"chars": 1984,
"preview": "//\n// Copyright (c) 2020 Hiroshi Kimura(Muukii) <muukii.app@gmail.com>\n//\n// Permission is hereby granted, free of charg"
},
{
"path": "Sources/Verge/Store/StateType.swift",
"chars": 1627,
"preview": "//\n// Copyright (c) 2019 muukii\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of"
},
{
"path": "Sources/Verge/Store/Store+Combine.swift",
"chars": 2900,
"preview": "//\n// Copyright (c) 2019 muukii\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of"
},
{
"path": "Sources/Verge/Store/Store+RunLoop.swift",
"chars": 2853,
"preview": "import Foundation\n\nextension Store {\n\n /// Push an event to run event loop.\n public func updateMainLoop() {\n RunLoo"
},
{
"path": "Sources/Verge/Store/Store.swift",
"chars": 23723,
"preview": "//\n// Copyright (c) 2019 muukii\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of"
},
{
"path": "Sources/Verge/Store/StoreDriverType+Accumulator.swift",
"chars": 12846,
"preview": "import StateStruct\n\nextension StoreDriverType where TargetStore.State : TrackingObject {\n \n /**\n Subscribes states a"
},
{
"path": "Sources/Verge/Store/StoreDriverType.swift",
"chars": 18995,
"preview": "//\n// Copyright (c) 2019 muukii\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of"
},
{
"path": "Sources/Verge/Store/StoreMiddleware.swift",
"chars": 2320,
"preview": "//\n// Copyright (c) 2019 muukii\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of"
},
{
"path": "Sources/Verge/Store/StoreOperation.swift",
"chars": 451,
"preview": "import class Foundation.NSRecursiveLock\n\npublic enum StoreOperation: Sendable {\n case nonAtomic\n case atomic(NSRecursi"
},
{
"path": "Sources/Verge/Store/StoreType+Assignee.swift",
"chars": 3648,
"preview": "//\n// Copyright (c) 2020 Hiroshi Kimura(Muukii) <muukii.app@gmail.com>\n//\n// Permission is hereby granted, free of charg"
},
{
"path": "Sources/Verge/Store/StoreType+BindingDerived.swift",
"chars": 4792,
"preview": "//\n// Copyright (c) 2020 Hiroshi Kimura(Muukii) <muukii.app@gmail.com>\n//\n// Permission is hereby granted, free of charg"
},
{
"path": "Sources/Verge/Store/StoreType+Derived.swift",
"chars": 4308,
"preview": "//\n// Copyright (c) 2020 Hiroshi Kimura(Muukii) <muukii.app@gmail.com>\n//\n// Permission is hereby granted, free of charg"
},
{
"path": "Sources/Verge/Store/StoreWrapperType.swift",
"chars": 1426,
"preview": "//\n// Copyright (c) 2020 Hiroshi Kimura(Muukii) <muukii.app@gmail.com>\n//\n// Permission is hereby granted, free of charg"
},
{
"path": "Sources/Verge/Store/Transaction.swift",
"chars": 2666,
"preview": "//\n// Copyright (c) 2021 muukii\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of"
},
{
"path": "Sources/Verge/Store/UIStateStore.swift",
"chars": 2482,
"preview": "//\n// Copyright (c) 2021 muukii\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of"
},
{
"path": "Sources/Verge/SwiftUI/.swift",
"chars": 76,
"preview": "//\n// Untitled.swift\n// Verge\n//\n// Created by Muukii on 2025/03/19.\n//\n\n"
},
{
"path": "Sources/Verge/SwiftUI/OnReceive.swift",
"chars": 2358,
"preview": "import SwiftUI\n\nextension View {\n\n /// Adds an action to perform when the specified `DispatcherType` publishes a state "
},
{
"path": "Sources/Verge/SwiftUI/Reading.swift",
"chars": 9224,
"preview": "import StateStruct\nimport SwiftUI\n\npublic protocol ReadingType: DynamicProperty {\n \n associatedtype Driver: StoreDrive"
},
{
"path": "Sources/Verge/SwiftUI/StoreObject.swift",
"chars": 2778,
"preview": "import SwiftUI\n\n/**\n A property wrapper that instantiates a `Store` for the view lifecycle.\n\n This property wrapper is d"
},
{
"path": "Sources/Verge/SwiftUI/StoreReader.swift",
"chars": 1776,
"preview": "import Combine\nimport Foundation\nimport StateStruct\nimport SwiftUI\n\n/**\n A view that reads the state from Store and disp"
},
{
"path": "Sources/Verge/Utility/Edge.swift",
"chars": 9651,
"preview": "//\n// Copyright (c) 2020 Hiroshi Kimura(Muukii) <muukii.app@gmail.com>\n//\n// Permission is hereby granted, free of charg"
},
{
"path": "Sources/Verge/Utility/ReferenceEdge.swift",
"chars": 3391,
"preview": "//\n// Copyright (c) 2020 Hiroshi Kimura(Muukii) <muukii.app@gmail.com>\n//\n// Permission is hereby granted, free of charg"
},
{
"path": "Sources/Verge/Utility/ThunkToMainActor.swift",
"chars": 318,
"preview": "import Foundation\n\n@preconcurrency\n@MainActor\n@inline(__always)\nfunc thunkToMainActor(_ run: @MainActor () throws -> Voi"
},
{
"path": "Sources/Verge/Verge.swift",
"chars": 163,
"preview": "\n@_exported import ConcurrencyTaskManager\n@_exported import Combine\n@_exported import TypedComparator\n@_exported import "
},
{
"path": "Sources/Verge/macros.swift",
"chars": 1,
"preview": "\n"
},
{
"path": "Sources/VergeClassic/Emitter.swift",
"chars": 1117,
"preview": "//\n// Activity.swift\n// Verge\n//\n// Created by muukii on 2019/07/16.\n// Copyright © 2019 muukii. All rights reserved"
},
{
"path": "Sources/VergeClassic/Extensions.swift",
"chars": 1910,
"preview": "import RxSwift\nimport Foundation\nimport Verge\n\nnonisolated(unsafe) fileprivate var storage_subject: Void?\n\nextension Rea"
},
{
"path": "Sources/VergeClassic/Info.plist",
"chars": 776,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Sources/VergeClassic/Storage+Rx.swift",
"chars": 2159,
"preview": "\nimport Foundation\n\nimport RxSwift\nimport RxCocoa\n\n#if !COCOAPODS\nimport Verge\nimport VergeRx\n#endif\n\nextension Storage:"
},
{
"path": "Sources/VergeClassic/Storage.swift",
"chars": 6957,
"preview": "//\n// Copyright (c) 2019 muukii\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of"
},
{
"path": "Sources/VergeClassic/Verge+Extension.swift",
"chars": 1650,
"preview": "\nimport Foundation\n\nimport RxCocoa\n\nextension VergeType {\n \n public func commitBinder<S>(\n name: String = \"\",\n d"
},
{
"path": "Sources/VergeClassic/Verge.h",
"chars": 379,
"preview": "\n#import <Foundation/Foundation.h>\n\n//! Project version number for CycleViewModel.\nFOUNDATION_EXPORT double VergeVersion"
},
{
"path": "Sources/VergeClassic/VergeClassic.swift",
"chars": 7894,
"preview": "\nimport Foundation\nimport ObjectiveC\n\n#if !COCOAPODS\nimport Verge\n#endif\n\n@_exported import RxSwift\n@_exported import Rx"
},
{
"path": "Sources/VergeMacros/Source.swift",
"chars": 203,
"preview": "\n@freestanding(expression)\npublic macro keyPathMap<U, each T>(_ keyPaths: repeat KeyPath<U, each T>) -> (borrowing U) ->"
},
{
"path": "Sources/VergeMacrosPlugin/KeyPathMap.swift",
"chars": 1045,
"preview": "import SwiftSyntax\nimport SwiftSyntaxBuilder\nimport SwiftSyntaxMacros\n\npublic struct KeyPathMap: Macro {\n\n\n}\n\nextension "
},
{
"path": "Sources/VergeMacrosPlugin/MacroError.swift",
"chars": 348,
"preview": "\nimport SwiftDiagnostics\n\npublic struct MacroError: Error, DiagnosticMessage {\n \n public var message: String\n \n publ"
},
{
"path": "Sources/VergeMacrosPlugin/Plugin.swift",
"chars": 203,
"preview": "import SwiftCompilerPlugin\nimport SwiftSyntax\nimport SwiftSyntaxBuilder\nimport SwiftSyntaxMacros\n\n@main\nstruct Plugin: C"
},
{
"path": "Sources/VergeNormalizationDerived/DerivedMaking+.swift",
"chars": 10171,
"preview": "import Foundation\nimport Normalization\n@_spi(Internal) import Verge\nimport Verge\n\nextension StoreDriverType {\n\n filepri"
},
{
"path": "Sources/VergeNormalizationDerived/DerivedResult.swift",
"chars": 987,
"preview": "import Verge\n\n/// A result instance that contains created Derived object\n/// While creating non-null derived from entity"
},
{
"path": "Sources/VergeNormalizationDerived/EntityType+Typealias.swift",
"chars": 424,
"preview": "extension EntityType {\n\n public typealias Derived = Verge.Derived<EntityWrapper<Self>>\n public typealias NonNullDerive"
},
{
"path": "Sources/VergeNormalizationDerived/EntityWrapper.swift",
"chars": 614,
"preview": "import Normalization\nimport StateStruct\n\npublic protocol EntityWrapperType: TrackingObject {\n associatedtype Entity: En"
},
{
"path": "Sources/VergeNormalizationDerived/NonNullEntityWrapper.swift",
"chars": 1135,
"preview": "import Normalization\nimport StateStruct\n\npublic protocol NonNullEntityWrapperType: TrackingObject {\n associatedtype Ent"
},
{
"path": "Sources/VergeNormalizationDerived/Pipelines.swift",
"chars": 5107,
"preview": "import Normalization\nimport Verge\n\nstruct SingleEntityPipeline<\n _StorageSelector: StorageSelector,\n _TableSelector: T"
},
{
"path": "Sources/VergeNormalizationDerived/StoreType+.swift",
"chars": 243,
"preview": "import Verge\nimport Normalization\n\nextension StoreType {\n \n public func normalizedStorage<Selector: StorageSelector>(_"
},
{
"path": "Sources/VergeNormalizationDerived/VergeNormalizationDerived.swift",
"chars": 56,
"preview": "@_exported import Verge\n@_exported import Normalization\n"
},
{
"path": "Sources/VergeRx/Extensions.swift",
"chars": 2110,
"preview": "//\n// Copyright (c) 2020 Hiroshi Kimura(Muukii) <muukii.app@gmail.com>\n//\n// Permission is hereby granted, free of charg"
},
{
"path": "Sources/VergeRx/Store+Rx.swift",
"chars": 2614,
"preview": "\nimport Foundation\n\n#if !COCOAPODS\n@_spi(Package) import Verge\n@_spi(EventEmitter) import Verge\n#endif\n\nimport RxSwift\ni"
},
{
"path": "Sources/VergeTiny/Source.swift",
"chars": 5420,
"preview": "//\n// Copyright (c) 2021 Hiroshi Kimura(Muukii) <muukii.app@gmail.com>\n//\n// Permission is hereby granted, free of charg"
},
{
"path": "StoreReaderDemo/StoreReaderDemo/Assets.xcassets/AccentColor.colorset/Contents.json",
"chars": 123,
"preview": "{\n \"colors\" : [\n {\n \"idiom\" : \"universal\"\n }\n ],\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }"
},
{
"path": "StoreReaderDemo/StoreReaderDemo/Assets.xcassets/AppIcon.appiconset/Contents.json",
"chars": 177,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"platform\" : \"ios\",\n \"size\" : \"1024x1024\"\n }\n ],\n \"i"
},
{
"path": "StoreReaderDemo/StoreReaderDemo/Assets.xcassets/Contents.json",
"chars": 63,
"preview": "{\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }\n}\n"
},
{
"path": "StoreReaderDemo/StoreReaderDemo/ContentView.swift",
"chars": 1611,
"preview": "//\n// ContentView.swift\n// StoreReaderDemo\n//\n// Created by Muukii on 2023/05/11.\n//\n\nimport SwiftUI\nimport SwiftUIHo"
},
{
"path": "StoreReaderDemo/StoreReaderDemo/Preview Content/Preview Assets.xcassets/Contents.json",
"chars": 63,
"preview": "{\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }\n}\n"
},
{
"path": "StoreReaderDemo/StoreReaderDemo/StoreReaderDemoApp.swift",
"chars": 244,
"preview": "//\n// StoreReaderDemoApp.swift\n// StoreReaderDemo\n//\n// Created by Muukii on 2023/05/11.\n//\n\nimport SwiftUI\n\n@main\nst"
},
{
"path": "TaskManagerPlayground/TaskManagerPlayground/Assets.xcassets/AccentColor.colorset/Contents.json",
"chars": 123,
"preview": "{\n \"colors\" : [\n {\n \"idiom\" : \"universal\"\n }\n ],\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }"
},
{
"path": "TaskManagerPlayground/TaskManagerPlayground/Assets.xcassets/AppIcon.appiconset/Contents.json",
"chars": 177,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"platform\" : \"ios\",\n \"size\" : \"1024x1024\"\n }\n ],\n \"i"
},
{
"path": "TaskManagerPlayground/TaskManagerPlayground/Assets.xcassets/Contents.json",
"chars": 63,
"preview": "{\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }\n}\n"
},
{
"path": "TaskManagerPlayground/TaskManagerPlayground/Book.swift",
"chars": 2294,
"preview": "import SwiftUI\nimport Verge\n\nstruct BookVergeTaskManager: View, PreviewProvider {\n var body: some View {\n Content()\n"
},
{
"path": "TaskManagerPlayground/TaskManagerPlayground/ContentView.swift",
"chars": 302,
"preview": "//\n// ContentView.swift\n// TaskManagerPlayground\n//\n// Created by Muukii on 2023/04/07.\n//\n\nimport SwiftUI\n\nstruct Co"
},
{
"path": "TaskManagerPlayground/TaskManagerPlayground/Info.plist",
"chars": 181,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "TaskManagerPlayground/TaskManagerPlayground/Preview Content/Preview Assets.xcassets/Contents.json",
"chars": 63,
"preview": "{\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }\n}\n"
},
{
"path": "TaskManagerPlayground/TaskManagerPlayground/TaskManagerPlaygroundApp.swift",
"chars": 262,
"preview": "//\n// TaskManagerPlaygroundApp.swift\n// TaskManagerPlayground\n//\n// Created by Muukii on 2023/04/07.\n//\n\nimport Swift"
},
{
"path": "Tests/All.xctestplan",
"chars": 1791,
"preview": "{\n \"configurations\" : [\n {\n \"id\" : \"CED3C5CF-7541-4D76-8BD7-C398F4B5517C\",\n \"name\" : \"Basic\",\n \"optio"
},
{
"path": "Tests/DemoState.swift",
"chars": 1001,
"preview": "//\n// DemoState.swift\n// VergeStoreTests\n//\n// Created by muukii on 2020/04/21.\n// Copyright © 2020 muukii. All righ"
},
{
"path": "Tests/VergeMacrosTests/KeyPathMapTests.swift",
"chars": 872,
"preview": "import SwiftSyntaxMacros\nimport SwiftSyntaxMacrosTestSupport\nimport XCTest\n\n#if canImport(VergeMacrosPlugin)\nimport Verg"
},
{
"path": "Tests/VergeNormalizationDerivedTests/CombiningTests.swift",
"chars": 1191,
"preview": "import XCTest\nimport VergeNormalizationDerived\n\nfinal class CombiningTests: XCTestCase {\n\n func test_combine() {\n\n l"
},
{
"path": "Tests/VergeNormalizationDerivedTests/DemoState.swift",
"chars": 1117,
"preview": "import VergeNormalizationDerived\n\n@Tracking\nstruct DemoState {\n\n var count: Int = 0\n\n var db: Database = .init()\n\n st"
},
{
"path": "Tests/VergeNormalizationDerivedTests/VergeNormalizationDerivedTests.swift",
"chars": 1213,
"preview": "import VergeNormalizationDerived\nimport XCTest\nimport os.lock\n\nextension Store where State == DemoState {\n\n var databas"
},
{
"path": "Tests/VergeRxTests/ChangedOperatorTests.swift",
"chars": 838,
"preview": "//\n// ChangedOperatorTests.swift\n// VergeRxTests\n//\n// Created by muukii on 2020/05/01.\n// Copyright © 2020 muukii. "
},
{
"path": "Tests/VergeRxTests/DemoState.swift",
"chars": 988,
"preview": "//\n// DemoState.swift\n// VergeStoreTests\n//\n// Created by muukii on 2020/04/21.\n// Copyright © 2020 muukii. All righ"
},
{
"path": "Tests/VergeRxTests/ReproduceDeadlockTests.swift",
"chars": 923,
"preview": "//\n// ReproduceDeadlockTests.swift\n// VergeStoreTests\n//\n// Created by muukii on 2020/04/20.\n// Copyright © 2020 muu"
},
{
"path": "Tests/VergeRxTests/SubjectCompletionTests.swift",
"chars": 949,
"preview": "//\n// SubjectCompletionTests.swift\n// VergeRxTests\n//\n// Created by Muukii on 2021/04/06.\n// Copyright © 2021 muukii"
},
{
"path": "Tests/VergeRxTests/VergeRxTests.swift",
"chars": 701,
"preview": "//\n// VergeRxTests.swift\n// VergeRxTests\n//\n// Created by muukii on 2020/01/09.\n// Copyright © 2020 muukii. All righ"
},
{
"path": "Tests/VergeTests/AccumulationTests.swift",
"chars": 3968,
"preview": "import Verge\nimport XCTest\n\nfinal class AccumulationTests: XCTestCase {\n \n @MainActor\n func test_tracking() {\n "
},
{
"path": "Tests/VergeTests/ActivityTests.swift",
"chars": 1107,
"preview": "//\n// ActivityTests.swift\n// VergeStoreTests\n//\n// Created by muukii on 2019/12/24.\n// Copyright © 2019 muukii. All "
},
{
"path": "Tests/VergeTests/BindingDerivedTests.swift",
"chars": 1714,
"preview": "import XCTest\nimport Verge\n\nfinal class BindingDerivedTests: XCTestCase {\n\n func testBinding() {\n\n let source = Demo"
},
{
"path": "Tests/VergeTests/CachedMapTests.swift",
"chars": 965,
"preview": "//\n// CachedMapTests.swift\n// VergeStoreTests\n//\n// Created by muukii on 2020/07/25.\n// Copyright © 2020 muukii. All"
},
{
"path": "Tests/VergeTests/ChangesTests.swift",
"chars": 1482,
"preview": "import XCTest\nimport Verge\nimport VergeMacros\n\nfinal class ChangesTests: XCTestCase {\n\n func test_performance_keypath()"
},
{
"path": "Tests/VergeTests/ComparerTests.swift",
"chars": 683,
"preview": "//\n// ComparerTests.swift\n// VergeTests\n//\n// Created by Muukii on 2022/05/11.\n// Copyright © 2022 muukii. All right"
},
{
"path": "Tests/VergeTests/ConcurrencyTests.swift",
"chars": 4122,
"preview": "//\n// ConcurrencyTests.swift\n// VergeTests\n//\n// Created by Muukii on 2021/03/16.\n// Copyright © 2021 muukii. All ri"
},
{
"path": "Tests/VergeTests/CopyPerformance.swift",
"chars": 28214,
"preview": "//\n// CopyPerformance.swift\n// VergeCore\n//\n// Created by muukii on 2020/01/08.\n// Copyright © 2020 muukii. All righ"
},
{
"path": "Tests/VergeTests/CounterTests.swift",
"chars": 968,
"preview": "//\n// DateTests.swift\n// VergeCore\n//\n// Created by muukii on 2020/01/13.\n// Copyright © 2020 muukii. All rights res"
},
{
"path": "Tests/VergeTests/DemoState.swift",
"chars": 1187,
"preview": "\n\n//\n// DemoState.swift\n// VergeStoreTests\n//\n// Created by muukii on 2020/04/21.\n// Copyright © 2020 muukii. All ri"
},
{
"path": "Tests/VergeTests/DerivedTests.swift",
"chars": 6019,
"preview": "//\n// StoreSliceTests.swift\n// VergeStore\n//\n// Created by muukii on 2020/04/21.\n// Copyright © 2020 muukii. All rig"
},
{
"path": "Tests/VergeTests/EdgeTests.swift",
"chars": 1288,
"preview": "//\n// EdgeTests.swift\n// VergeTests\n//\n// Created by Muukii on 2020/12/14.\n// Copyright © 2020 muukii. All rights re"
},
{
"path": "Tests/VergeTests/EventEmitterTests.swift",
"chars": 3424,
"preview": "//\n// EventEmitterTests.swift\n// VergeCoreTests\n//\n// Created by muukii on 2019/12/21.\n// Copyright © 2019 muukii. A"
},
{
"path": "Tests/VergeTests/FilterTests.swift",
"chars": 1974,
"preview": "//\n// FilterTests.swift\n// VergeCoreTests\n//\n// Created by muukii on 2020/01/14.\n// Copyright © 2020 muukii. All rig"
},
{
"path": "Tests/VergeTests/IsolatedContextTests.swift",
"chars": 1215,
"preview": "\nimport XCTest\nimport os.lock\n\n@MainActor\nfileprivate func isInMain() {}\n\nfinal class IsolatedContextTests: XCTestCase {"
},
{
"path": "Tests/VergeTests/OldComparer.swift",
"chars": 2184,
"preview": "/// A component that compares an input value.\n/// It can be combined with other comparers.\npublic struct Comparer<Input>"
},
{
"path": "Tests/VergeTests/PerformanceTests.swift",
"chars": 571,
"preview": "\nimport Foundation\n\nimport XCTest\nimport Verge\n\n/**\n Store's state contains a huge dictionary.\n This test-case tests to "
},
{
"path": "Tests/VergeTests/PipelineTests.swift",
"chars": 3018,
"preview": "import XCTest\nimport Verge\n\nfinal class PipelineTests: XCTestCase {\n \n func test_MapPipeline() {\n \n let mapC"
},
{
"path": "Tests/VergeTests/ReferenceEdgeTests.swift",
"chars": 1082,
"preview": "\nimport XCTest\nimport Verge\n\nfinal class FragmentTests: XCTestCase {\n\n struct State {\n var name: String = \"\"\n @Re"
},
{
"path": "Tests/VergeTests/Retain/PublisherCompletionTests.swift",
"chars": 3923,
"preview": "\nimport Foundation\nimport Combine\nimport Verge\nimport XCTest\n\nfinal class SubjectCompletionTests: XCTestCase {\n\n func t"
},
{
"path": "Tests/VergeTests/Retain/StoreSinkSusbscriptionTests.swift",
"chars": 5766,
"preview": "import Verge\nimport XCTest\nimport Atomics\n\nfinal class StoreSinkSubscriptionTests: XCTestCase {\n\n func test_Store_sinkS"
},
{
"path": "Tests/VergeTests/RunLoopTests.swift",
"chars": 270,
"preview": "import XCTest\n@testable import Verge\n\nfinal class RunLoopTests: XCTestCase {\n\n func test_performance_adding() {\n\n me"
},
{
"path": "Tests/VergeTests/Sample.swift",
"chars": 1105,
"preview": "//\n// Sample.swift\n// VergeStoreTests\n//\n// Created by muukii on 2020/04/18.\n// Copyright © 2020 muukii. All rights "
},
{
"path": "Tests/VergeTests/StateTypeTests.swift",
"chars": 867,
"preview": "import Foundation\nimport Verge\nimport XCTest\n\nfinal class StateTypeTests: XCTestCase {\n \n func test_init() {\n \n "
},
{
"path": "Tests/VergeTests/StoreAndDerivedTests.swift",
"chars": 1658,
"preview": "import Verge\nimport XCTest\n\nfinal class StoreAndDerivedTests: XCTestCase {\n\n @MainActor\n func test() async {\n\n let "
},
{
"path": "Tests/VergeTests/StoreInitTests.swift",
"chars": 743,
"preview": "\nimport XCTest\nimport Verge\n\nfinal class StoreInitTests: XCTestCase {\n \n class RefState {\n \n }\n /*\n class RefView"
},
{
"path": "Tests/VergeTests/StoreMiddlewareTests.swift",
"chars": 1007,
"preview": "import Testing\n\n@Suite(\"StoreMiddlewareTests\")\nstruct StoreMiddlewareTests {\n\n @Test(\"Commit Hook\")\n func testCommitHo"
},
{
"path": "Tests/VergeTests/StoreSinkTests.swift",
"chars": 474,
"preview": "import Verge\nimport XCTest\n\n@MainActor\nfileprivate func UI() {\n\n}\n\nfinal class StoreSinkTests: XCTestCase {\n\n func test"
},
{
"path": "Tests/VergeTests/StoreTaskTests.swift",
"chars": 1134,
"preview": "import Verge\nimport XCTest\nimport Atomics\n\nfinal class StoreTaskTests: XCTestCase {\n\n func test() {\n \n let atomic"
},
{
"path": "Tests/VergeTests/SynchronizeDisplayValueTests.swift",
"chars": 203,
"preview": "//\n// SynchronizeDisplayValueTests.swift\n// VergeStoreTests\n//\n// Created by muukii on 2019/11/09.\n// Copyright © 20"
},
{
"path": "Tests/VergeTests/SyntaxTests.swift",
"chars": 643,
"preview": "//\n// SyntaxTests.swift\n// VergeStoreTests\n//\n// Created by muukii on 2020/05/24.\n// Copyright © 2020 muukii. All ri"
},
{
"path": "Tests/VergeTests/TransactionTests.swift",
"chars": 443,
"preview": "import Verge\nimport XCTest\n\nfinal class TransactionTests: XCTestCase {\n\n func testTransaction() async {\n\n struct MyK"
},
{
"path": "Tests/VergeTests/Usage.swift",
"chars": 1367,
"preview": "\nimport Verge\n\nstruct RootState: Equatable {\n\n struct Nested: Equatable {}\n\n var nested: Nested = .init()\n\n}\n\nfinal cl"
},
{
"path": "Tests/VergeTests/VergeStoreTests.swift",
"chars": 10382,
"preview": "//\n// VergeStoreTests.swift\n// VergeStoreTests\n//\n// Created by muukii on 2019/11/04.\n// Copyright © 2019 muukii. Al"
},
{
"path": "Tests/VergeTinyTests/VergeTinyTests.swift",
"chars": 3228,
"preview": "//\n// Copyright (c) 2021 Hiroshi Kimura(Muukii) <muukii.app@gmail.com>\n//\n// Permission is hereby granted, free of charg"
},
{
"path": "Verge.playground/Pages/Memo.xcplaygroundpage/Contents.swift",
"chars": 95,
"preview": "\nenum Action {\n case increment\n case decrement\n}\n\nclass Store<State> {\n\n var state: State\n}\n"
},
{
"path": "Verge.playground/Pages/PlainStorePattern1.xcplaygroundpage/Contents.swift",
"chars": 233,
"preview": "import UIKit\n\nclass ViewA: DemoUIView {\n \n // MARK: Components\n \n let nameLabel = UILabel()\n \n // MARK: State\n \n "
},
{
"path": "Verge.playground/Pages/PlainStorePattern2.xcplaygroundpage/Contents.swift",
"chars": 358,
"preview": "//: [Previous](@previous)\n\nimport UIKit\n\nclass ViewA: DemoUIView {\n \n // MARK: Components\n \n let nameLabel = UILabel"
},
{
"path": "Verge.playground/Pages/PlainStorePattern3.xcplaygroundpage/Contents.swift",
"chars": 400,
"preview": "//: [Previous](@previous)\nimport UIKit\n\nclass ViewA: DemoUIView {\n \n // MARK: Components\n \n let nameLabel = UILabel("
},
{
"path": "Verge.playground/Pages/PlainStorePattern4.xcplaygroundpage/Contents.swift",
"chars": 1518,
"preview": "//: [Previous](@previous)\n\nimport Foundation\n\nimport UIKit\n\nstruct State {\n var name: String = \"\"\n var age: Int = 0\n}\n"
},
{
"path": "Verge.playground/Pages/PlainStorePattern5.xcplaygroundpage/Contents.swift",
"chars": 1742,
"preview": "//: [Previous](@previous)\n\nimport Foundation\nimport UIKit\n\nstruct State {\n var name: String = \"\"\n var age: Int = 0\n}\n\n"
},
{
"path": "Verge.playground/Pages/VergeStorePartern2.xcplaygroundpage/Contents.swift",
"chars": 991,
"preview": "//: [Previous](@previous)\nimport UIKit\n\nimport Verge\n\nstruct State {\n var name: String = \"\"\n var age: Int = 0\n}\n\nclass"
},
{
"path": "Verge.playground/Pages/VergeStorePattern.xcplaygroundpage/Contents.swift",
"chars": 994,
"preview": "//: [Previous](@previous)\n\nimport Foundation\n\nimport UIKit\nimport Verge\n\nstruct State {\n var name: String = \"\"\n var ag"
},
{
"path": "Verge.playground/Sources/Wire.swift",
"chars": 236,
"preview": "import UIKit\n\nopen class DemoUIView: UIView {\n \n public init() {\n super.init(frame: .zero)\n }\n \n @available(*, u"
},
{
"path": "Verge.playground/contents.xcplayground",
"chars": 483,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<playground version='6.0' target-platform='ios' display-mode='ra"
},
{
"path": "mise.toml",
"chars": 25,
"preview": "[tools]\ntuist = \"4.44.3\"\n"
},
{
"path": "playgrounds/.gitignore",
"chars": 12,
"preview": "!*.xcodeproj"
},
{
"path": "playgrounds/PlaySwiftUI/PlaySwiftUI/Assets.xcassets/AccentColor.colorset/Contents.json",
"chars": 123,
"preview": "{\n \"colors\" : [\n {\n \"idiom\" : \"universal\"\n }\n ],\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }"
},
{
"path": "playgrounds/PlaySwiftUI/PlaySwiftUI/Assets.xcassets/AppIcon.appiconset/Contents.json",
"chars": 607,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"platform\" : \"ios\",\n \"size\" : \"1024x1024\"\n },\n {\n "
},
{
"path": "playgrounds/PlaySwiftUI/PlaySwiftUI/Assets.xcassets/Contents.json",
"chars": 63,
"preview": "{\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }\n}\n"
},
{
"path": "playgrounds/PlaySwiftUI/PlaySwiftUI/ContentView.swift",
"chars": 1329,
"preview": "//\n// ContentView.swift\n// PlaySwiftUI\n//\n// Created by Muukii on 2025/02/13.\n//\n\nimport SwiftUI\nimport Verge\n\n@Track"
},
{
"path": "playgrounds/PlaySwiftUI/PlaySwiftUI/PlaySwiftUIApp.swift",
"chars": 214,
"preview": "//\n// PlaySwiftUIApp.swift\n// PlaySwiftUI\n//\n// Created by Muukii on 2025/02/13.\n//\n\nimport SwiftUI\n\n@main\nstruct Pla"
},
{
"path": "playgrounds/PlaySwiftUI/PlaySwiftUI/Preview Content/Preview Assets.xcassets/Contents.json",
"chars": 63,
"preview": "{\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }\n}\n"
},
{
"path": "playgrounds/PlaySwiftUI/PlaySwiftUI/Simple.swift",
"chars": 862,
"preview": "import SwiftUI\nimport Verge\n\nprivate struct _Book: View {\n \n @Tracking\n struct State {\n var count: Int = 0\n \n "
},
{
"path": "playgrounds/PlaySwiftUI/PlaySwiftUI.xcodeproj/project.pbxproj",
"chars": 13477,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 77;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "playgrounds/PlaySwiftUI/PlaySwiftUI.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"chars": 135,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"self:\">\n </FileRef"
},
{
"path": "playgrounds/PlaySwiftUI/PlaySwiftUI.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved",
"chars": 2624,
"preview": "{\n \"originHash\" : \"91b2bd4d42ac7a19338699d5fcae987277fc6461718b80e90459a6235bafabb2\",\n \"pins\" : [\n {\n \"identit"
}
]
About this extraction
This page contains the full source code of the muukii/Verge GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 199 files (498.0 KB), approximately 135.8k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.