RxSwift and control flow - a bad idea? - swift

I have developed a struct that generates random data for testing purposes in my app but only does so if the Realm database is empty. The service class responsible for CRUD operations returns outcomes / results as RxSwift Observables.
Generating new data is predicated on the contents of the Realm - using guard is the logical choice but requires adding a new method to the service that returns Int rather than an Observable which seems to be an unnecessary duplication of code and somewhat inconsistent.
i.e.
class Service {
let realm = try! Realm()
...
// Existing reactive code
func patients() -> Observable<Result<Patient>> {
let results = realm.objects(Patient.self)
return Observable.collection(from: results)
}
func objectCount() -> Int {
let realm = try! Realm()
return realm.objects(Patient.self).count
}
}
struct DataGenerator() {
...
func createRandomPatients() {
let service = Service()
guard service.objectCount() == 0 else { return }
...
//go on to generate patients
The only way I can figure to replicate this control flow using the reactive service method is to bind the Observable to a Variable
i.e.
... service code as above ...
func createRandomPatients() {
let isEmpty = Variable<Bool>(false)
service.patients().map{ $0 == 0 }.bind(to: isEmpty)
guard isEmpty.value else { return }
// etc
It seems like a bit of a fudge as it perhaps isn't quite as clear, but it avoids adding code to the service class that won't be used in the production app. Is this bad practice or just personal preference? I'm open to alternative solutions if anyone has one...

Faking is definitely something that should be handled with polymorphism.
A good design would be to define a common interface; for example it can be PatientsSource:
protocol PatientsSource {
func patients() -> Observable<Result<[Patient]>>
}
Than you can take advantage of polymorphism by defining:
class PatientsService: PatientsSource... {
//w/e
}
struct FakePatients: PatientsSource {
//your random patients generation
}
If you use a Service style architecture then you should pass the Service and the PatientsSource to the user (like your controller) separately even though they might end up to be the same object.

Related

Avoiding Type Erasure when implementing the Repository Pattern

I'm trying to implement the repository pattern in Swift in a generic way. The problem that I'm currently facing is, that it seems like I have to write type erasure wrappers for all my repositories. Am I missing something here? Is there a better way to do this or to make the compiler happy at this point?
// 1
class Item {}
// 2
protocol Repository {
associatedtype T
}
// 3
protocol AnyItemRepository: Repository where T == Item {}
// 4
class ItemRepository: AnyItemRepository {
static let shared = ItemRepository()
private init() {}
}
// 5
class ViewController {
// 6
var itemRepository: AnyItemRepository? = ItemRepository.shared
}
One of many entities
The base repository interface that can be extended if necessary or implemented directly
A special item repository interface that guarantees additional functionality on top of the base repository
Concrete repository implementation for a specific entity type
Some class that needs to access the data
Dependency to any item repository. The compiler errors on this line: Protocol 'AnyItemRepository' can only be used as a generic constraint because it has Self or associated type requirements
You don't need the AnyItemRepository type. Just write extension methods on Repository like so:
public extension Repository where T == Item {
func doSomethingSpecial(with items: [Item]) {
// blah blah
}
}
In your view controller, you can't use Repository or AnyItemRepository in this way because these are generic type constraints. You must either use a concrete type or generically parameterize ViewController.
class RepositoryViewController<R>: UIViewController where R: Repository, R.T == Item {
var itemRepository: R { get }
}
class ViewController: RepositoryViewController<ItemRepository> {
override var itemRepository: ItemRepository {
return ItemRepository.shared
}
}
(The above is untested pseudocode designed to give you the gist. It has never been run by anyone ever and may not even compile.)
It's not quite clear what you're planning to use this Repository for. Repository is designed for environments that have certain characteristics (such as strong linkages to row-style databases). I'll discuss other patterns that often work better in common iOS apps (and even smaller Mac apps).
I usually strongly dislike type erasure, and it often indicates a design problem. But in this case I think type erasure might be a reasonable answer.
So we'll start with the kinds of items we can store. They probably are going to need to have some kind of identifier, and be hashable for many common backends (but maybe you won't need hashing; if not, take it out).
protocol Identified {
associatedtype ID
var id: ID { get }
}
typealias Storable = Identified & Hashable
And then there are things that can act as storage. There is no such thing as a "RepositoryStorage." This is just saying "if you comply with these requirements, then Repository can use you."
protocol RepositoryStorage {
associatedtype Item: Storable
func get(identifier: Item.ID) -> Item?
func add(item: Item)
func delete(identifier: Item.ID)
func allItems() -> [Item]
}
And then the standard, somewhat tedious, type-eraser pattern (there's another pattern that stdlib uses that even more tedious, but this one is good enough for most cases).
// I'm making it a class because I assume it'll have reference semantics.
final class Respository<Item: Storable>: RepositoryStorage {
init<Storage: RepositoryStorage>(storage: Storage) where Storage.Item == Item {
self._get = storage.get
self._add = storage.add
self._delete = storage.delete
self._allItems = storage.allItems
}
let _get: (Item.ID) -> Item?
func get(identifier: Item.ID) -> Item? { return _get(identifier) }
let _add: (Item) -> Void
func add(item: Item) { _add(item) }
let _delete: (Item.ID) -> Void
func delete(identifier: Item.ID) { _delete(identifier) }
let _allItems: () -> [Item]
func allItems() -> [Item] { return _allItems() }
}
So that's fine, it's a general-purpose Repository. And this makes reasonable sense if you're dealing with a large set of items that are probably going to be stored in a SQLite database. But in my experience it is often both too much and too little. Too much if it's just a few items, and too little if you have a lot of items and so probably have to do a lot more than just CRUD. You probably need Query and Join, and then this isn't enough. (Making something flexible in one direction often cuts you off in other directions. There's no universal "generic.")
So can we make it simpler for the case when it's really just a few items? Here's an approach I use regularly:
class DataStore<Key: Hashable & Codable, Value: Codable> {
let identifier: String
private(set) var storage: DataStorage
var dictionary: [Key: Value] {
didSet {
storage[identifier] = try? PropertyListEncoder().encode(dictionary)
}
}
init(identifier: String, storage: DataStorage = UserDefaults.standard) {
self.identifier = identifier
self.storage = storage
let data = storage[identifier] ?? Data()
self.dictionary = (try? PropertyListDecoder().decode([Key: Value].self,
from: data)) ?? [:]
}
subscript(key: Key) -> Value? {
get { return dictionary[key] }
set { dictionary[key] = newValue }
}
}
A DataStore acts as dictionary that you can store key/value pairs in:
let ds = DataStore<String: Item>(identifier: "Item")
ds["first"] = item
It can store anything Codable. With a little modification, you could switch it from a dictionary-like interface to an array or set-like interface; I just usually want a dictionary.
When it's updated, it encodes the entire data store to its storage as Data:
protocol DataStorage {
subscript(identifier: String) -> Data? { get set }
}
This is very fast and efficient for dozens of items. I might rethink if if there were over a hundred items, and it would be inappropriate for hundreds or more items. But for small sets, this is very, very fast.
A very common DataStorage is UserDefaults:
extension UserDefaults: DataStorage {
subscript(identifier: String) -> Data? {
get { return data(forKey: identifier) }
set { set(newValue, forKey: identifier) }
}
}
The key lesson is that this gets rid of all the type-erasure hoop-jumping by creating a common currency (Data) that lower layers work exclusively with. Any time you can do that, separating the upper-layer generic interface from a lower-layer non-generic interface, you'll save yourself a lot of tedium.
This may or may not work for your situation. It's tailored to key/value stores rather than databases, and it's designed to be read much more often than written. But for that usage, it's much simpler and generally faster than the Repository pattern. That's the kind of trade-offs I mean when I say that it's important to know your use case.

RxSwift state changes trigger "Warning: Recursive call or synchronization error!"

I've inherited some Swift 3 code which uses RxSwift to manage a store. The basic layout of the class is:
class UserActivityStore {
fileprivate lazy var internalDataCache: Variable<Set<NSUserActivity>?> = Variable(nil)
func addAction(_ actionToAdd: NSUserActivity) {
var content = internalDataCache.value ?? Set<NSUserActivity>()
content.insert(actionToAdd)
internalDataCache.value = content
}
func resolveAction(_ action: NSUserActivity) {
var content = internalDataCache.value
_ = content?.remove(action)
internalDataCache.value = content
}
func expectActions(_ filter: #escaping ((NSUserActivity) -> Bool)) -> Observable<NSUserActivity> {
let observable = Observable<NSUserActivity>.create { (observer) -> Disposable in
return self.internalDataCache.asObservable().subscribeNext { (newActions) in
guard let newActions = newActions else { return }
for eachAction in newActions where filter(eachAction) {
observer.onNext(eachAction)
self.resolveAction(eachAction)
}
}
}
return observable
}
}
When an action is added to this, it adds the item to the set correctly. However, the observer (in expectActions) catches that change and resolves it. Since this is all in a single thread, the error "Warning: Recursive call or synchronization error!" is triggered.
I think this is a perfectly legitimate error and that RxSwift is probably correct in its handling. However, I can't help thinking that this is a bad model. The code is essentially handling a queue of NSUserActivity objects itself.
Is this genuinely a modelling error / abuse of RxSwift or is my limited understanding of RxSwift misunderstanding this? As a hacky solution, I've tried replacing the resolveAction function with a single line internalDataCache.value?.remove(action) but that still triggers the observable and hence the bug.
Changing the observable to use a different queue (Serial or Concurrent dispatch) fixes the problem but I'm not convinced its the correct fix.

How to fake Realm Results for tests

I have written a test to validate if a function is called :
func test_getTaskLists_doNotCreateOrUpdateTaskListToStorageWhenSynchedLocally() {
...
let (datasource, restAPI, fakeTaskListStorage) = ...
datasource.getTaskLists() { (taskLists, error) -> Void in
...
XCTAssertEqual(1, fakeTaskListStorage.readAllInvocationCount)
...
}
...
}
The function is mocked to bypass super implementation and the issue is that the function returns a Results which I can't figure out to build/mock in order to return a valid object so the compiler stops complaining...I know I could just call super.readAll() but here I actually want to convert my test data (fakeTaskLists) to a fake Result object so everyone is happy...not sure if thats possible
class FakeTaskListsStorageRealm : TaskListStorageRealm {
var fakeTaskLists:[TaskList]?
override func readAll() -> RealmSwift.Results<TaskList> {
readAllInvocationCount += 1
//Here I want to return fakeTaskLists somehow...
}
}
There is no way to instantiate Results directly. Subclassing Results doesn't allow too. I think the best way is hiding Results by protocol like ResultsWrapper rather than using Results directly.
But an easy workaround is using in-memory Realm when testing.
The FakeTaskListsStorageRealm's readAll() can be written using in-memory Realm as follows:
class FakeTaskListsStorageRealm : TaskListStorageRealm {
var fakeTaskLists:[TaskList]?
override func readAll() -> RealmSwift.Results<TaskList> {
readAllInvocationCount += 1
return try! Realm(configuration: Realm.Configuration(inMemoryIdentifier: "test")).objects(TaskList.self)
}
}

How to reduce mutability with nested objects stored in Realm?

Full code on github
I am trying to rewrite my app to reduce mutability and take advantage of functional programming. I am having trouble figuring out where to start, since it seems like my architecture is to use modification in place almost everywhere. I could use some advice on a simple starting point of how to break this down into smaller pieces where I am maintaining immutability at each modification. Should I change my data storage architecture so that I am only storing/modifying/deleting the leaf objects?
Right now, from the root ViewController, I load my one monster object ExerciseProgram (which contains a RealmList of Exercise objects, which contains a RealmList of Workouts, which contains a RealmList of Sets....)
final class ExerciseProgram: Object {
dynamic var name: String = ""
dynamic var startDate = NSDate()
dynamic var userProfile: User?
var program = List<Exercise>()
var count: Int {
return program.count
}
}
Loaded here one time in MasterTableViewController.swift:
func load() -> ExerciseProgram? {
let realm = try! Realm()
return realm.objects(ExerciseProgram).first
}
and then modify the single ExerciseProgram object in place throughout the app, such as when recording a new workout.
To create a new Workout, I instantiate a new Workout object in RecordWorkoutTableViewController.swift:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if doneButton === sender {
if let date = newDate, weight = newWeight, setOne = newSetOne, setTwo = newSetTwo {
let newSets = List<WorkSet>()
newSets.append(WorkSet(weight: weight, repCount: setOne))
newSets.append(WorkSet(weight: weight, repCount: setTwo))
newWorkout = Workout(date: date, sets: newSets)
}
}
}
Which unwinds to ExerciseDetailTableViewController.swift where the storage occurs into the same monster ExerciseProgram object retrieved at the beginning:
#IBAction func unwindToExerciseDetail(sender: UIStoryboardSegue) {
if let sourceViewController = sender.sourceViewController as? RecordWorkoutTableViewController, newWorkout = sourceViewController.newWorkout {
let realm = try! Realm()
try! realm.write {
exercise.recordWorkout(newWorkout)
}
}
}
This behavior is replicated all over my app. If I want to edit or delete an existing workout, it's exactly the same.
The Exercise class is just this:
final class Exercise: Object {
dynamic var name = ""
dynamic var notes: String?
var workoutDiary = List<Workout>()
dynamic var goal = 0
...
func recordWorkout(newWorkout: Workout) {
workoutDiary.append(newWorkout)
}
func replaceWorkout(originalWorkout: Workout, newWorkout: Workout) {
workoutDiary[workoutDiary.indexOf(originalWorkout)!] = newWorkout
}
}
From what I can tell, looking at that schema, no, you shouldn't change it. If it's representing the types of information and their relations properly and it's already working in your app, then there's no need to change it.
If you feel it is overly complex or confusing, then it may be necessary to go back and look at your data model design itself before actually doing more work on the code itself. Review each relationship and each property in the linked objects, and make sure that it's absolutely critical that the data is saved at that level. In any case, Realm itself is very good at handling relationships between objects, so it's not 'wrong' to have several layers of nested objects.
Either way, Realm itself lends itself pretty well to functional programming since every property is explicitly immutable out of the box. Functional programming doesn't mean everything has to be immutable always though. Inevitably, you'll have to reach a point where you'll need to save changes to Realm; the mindset behind it is that you're not transforming data as you're working on it, and you minimise the number of points that actually do so.

What's the best Swift pattern for returning a pending PromiseKit promise if one is already in running?

I have some expensive promises that get called in different spots. Ideally, I'd like to just chain off an existing in-flight promise (with an optional force), so I find myself doing something like this:
class Expensive {
var fooPromise : Promise<Foo>?
var barPromise : Promise<Bar>?
func doExpensiveFoo(force: bool = false) -> Promise<Foo> {
if let existing = fooPromise where existing.pending || (existing.fufilled && !force) {
// Return the existing promise
return existing
}
// Start a new Foo
return firstly {
// ...
}
}
func doExpensiveBar(force: bool = false) -> Promise<Bar> {
if let existing = barPromise where existing.pending || (existing.fufilled && !force) {
// Return the existing promise
return existing
}
// Start a new Bar
return firstly {
// ...
}
}
}
But that feels like a fair amount of boiler-plate (a local variable for each promise, and the existing chunk at the start of each function), so I'm wondering if anyone has seen a good pattern for abstracting away the variables and wrapper?
To borrow a term from Python, I'm looking for a decorator that would hide all that. Something like:
class Expensive {
private func startFoo() -> Promise<Foo> {
return firstly {
//..
}
}
public doExpensiveFoo = wrapExpensive(startFoo)
}
Any suggestions, or should I look at rolling my own?
I'm no expert, but this pattern worked for me:
private var fooPromise : Promise<Foo>?
func doExpensiveFoo() -> Promise<Foo> {
if let fooPromise = self.fooPromise, fooPromise.isPending {
// return the pending promise
return fooPromise
}
// reassign a newly created promise
fooPromise = firstly {
// do your thing
...
}
return fooPromise!
}
What I like about this pattern is that the method handles pending state internally, and that the promise automatically re-executes if called after it is finished. This allows callers to be ignorant of the internal mechanism or the state of the promise. Obviously, if you need to caller to be part of the decision, then keep the "force" flag approach.
I do not see any common base of Foo and Bar in your example. But even if they would have one Swift still does not support covariance on generic type parameters. At first you would need to create a common protocol for both types. Maybe this helps you to get on track:
Storing generic objects in Swift Array