arkavo-org / arkavo-org/app

Implement ModelActor for thread-safe SwiftData access

Open
#151 0 comments 0 reactions 0 assignees View on GitHub
enhancement
Dominant language
Swift
Stars
0
Forks
0
Avg merge
1h 41m
Merged PRs (30d)
1

Description

### Problem
Current SwiftData access pattern has concurrency safety issues:
- Models marked `@unchecked Sendable` without proper isolation
- Direct `ModelContext` access from multiple actors
- No structured concurrency boundaries
- Risk of data races and crashes

### Proposed Solution: ModelActor Pattern

#### Create ArkavoDataActor
```swift
@ModelActor
actor ArkavoDataActor {
func getOrCreateAccount() throws -> Account {
let descriptor = FetchDescriptor(predicate: #Predicate { $0.id == 0 })
if let account = try modelContext.fetch(descriptor).first {
return account
}
let account = Account()
modelContext.insert(account)
try modelContext.save()
return account
}

func fetchProfile(withPublicID publicID: Data) throws -> Profile? {
let descriptor = FetchDescriptor(
predicate: #Predicate { $0.publicID == publicID }
)
return try modelContext.fetch(descriptor).first
}

func saveThought(_ thought: Thought) throws -> Bool {
let existing = try modelContext.fetch(
FetchDescriptor(predicate: #Predicate { $0.publicID == thought.publicID })
)
guard existing.isEmpty else { return false }

modelContext.insert(thought)
try modelContext.save()
return true
}
}
```

#### Usage Pattern
```swift
class CreatorViewModel: ObservableObject {
@Published var videoThoughts: [Thought] = []
private let dataActor: ArkavoDataActor

init(container: ModelContainer) {
self.dataActor = ArkavoDataActor(modelContainer: container)
}

func loadVideoThoughts() async {
do {
let thoughts = try await dataActor.fetchThoughts(
forStreamPublicID: videoStreamPublicID
)
await MainActor.run {
self.videoThoughts = thoughts
}
} catch {
print("Error: \(error)")
}
}
}
```

### Benefits
- ✅ Structured concurrency with clear actor boundaries
- ✅ No `@unchecked Sendable` needed
- ✅ Compile-time actor isolation checking
- ✅ Prevents data races
- ✅ Centralized data access logic

### Implementation Checklist
- [ ] Create `ArkavoDataActor.swift` with `@ModelActor`
- [ ] Migrate database operations to actor methods
- [ ] Update `PersistenceController` to provide actor instances
- [ ] Update ViewModels to use actor pattern
- [ ] Remove `@unchecked Sendable` annotations
- [ ] Add concurrency tests

### Files to Create
- `ArkavoDataActor.swift`

### Files to Modify
- `PersistenceController.swift`
- `CreatorViewModel.swift`
- `VideoFeedViewModel.swift`
- `ChatViewModel.swift`
- `GroupViewModel.swift`

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by reading PersistenceController.swift and the proposed ArkavoDataActor.swift structure, then review CreatorViewModel.swift, VideoFeedViewModel.swift, ChatViewModel.swift, and GroupViewModel.swift. Trace existing database operations and @unchecked Sendable annotations before planning the migration. Done means actor-based access is adopted, the listed view models are updated, unsafe annotations are removed, and concurrency tests are added.

Written by the indexing model from the issue text.

Assessment

Tech stack
swift
Domain
database, mobile
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.