Should I put my UserDefaults saving process into ViewModel? (good architecture) - swift

I'm creating a simple NewsApp. I want to create the best app architecture I can made. So my question is that if I want save really simple data like username and maybe 5-6 tags as strings, should I put userDefaults logic into my viewModel or should I create a layer between ViewModel and UserDefaultsAPI which will take care about saving data?
I mean I will create StoreData protocol which UserDefaultsAPI will implement. And if I should do it how I can achieve that? I am using RxSwift and I don't now how to subscribe changing data in UserDefaults by UserDefaultsAPI.

You should create a layer between, but given an Rx/functional architecture, it shouldn't be something heavy weight like a protocol.
Learn How to Control the World and do something like this instead:
struct Storage {
static var shared = Storage()
var saveProperty: (Property) -> Void = { property in
UserDefaults.standard.set(try? JSONEncoder().encode(property), forKey: "property")
}
var deleteProperty: () -> Void = {
UserDefaults.standard.removeObject(forKey: "property")
}
var currentProperty: () -> Observable<Property?> = {
UserDefaults.standard.rx.observe(Data.self, "property")
.map { $0.flatMap { try? JSONDecoder().decode(Property.self, from: $0) } }
.distinctUntilChanged()
}
}
struct Property: Codable, Equatable { }

It depends what your doing creating a seperate layer gives you, the opportunity to have different sources for data that implement the same protocol could be useful, and your data may be complex types than need to be encoded and decoded, so it makes sense to encapsulate that out, you may also want to provide some limit range to some of your values, but UserDefaults is just a service like NotificationCenter, you are not going to automatically wrap NotificationCenter in ever class, just do what is simplest, but doesn't run the risk of painting yourself in a corner later on. You are not going to get every issue decided at the get go right, the skill is to make sure you can quickly change if the need arises, and knowing about what possibility's you will need to take advantage of in the future and how can you avoid needing to make complex changes in the moment that don't add squat. There are lots of things you need to do, and being able to prioritise them is an important part of what you do, don't try make cleaver designs, be cleaver in making designs that can be easy for other to understand and modify.

Related

How to store value, received in delegate class, inside external one?

There is a BLEManager class, that is responsible for scanning, connecting, and receiving data from Bluetooth Low Energy (BLE) devices. Looks like that:
class BLEManager: ObservableObject, OtherProtocols {
private var myCentral: CBCentralManager!
#Published var data = 0.0
override init() {
super.init()
myCentral = CBCentralManager(delegate: self, queue: nil)
myCentral.delegate = self
}
// ...some functions that scan, establish connection, etc.
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
// here I receive raw value, handle it and get a data to work with
data = 10.0 // imagine it's the data received from BLE device
}
}
Right now the "data to work with" is stored inside this class. I'd like to move this data in such a way, so the current class (BLEManager) is responsible for the BLE logic only, and data is stored together with other user data.
Is it possible in Swift?
P.s. I'm pretty new to Swift. Have experience in JS.
EDITED
In the current case, BLEmanager receives data from one specific peripheral. The data represents a human weight, to be clear. Other than that, there is a struct with human biometric data (age, height, gender). At the end of the day, biometric data + data from the device (weight) are closely related and are used in the same calculations.
RESULT
I was able to implement the Cristik’s approach. The only difference that in my case the subsription happens in a View’s .onAppear() modifier and not on class init, as he described. Had troubles with passing a publisher to the class.
I'd like to move this data in such a way, so the current class (BLEManager) is responsible for the BLE logic only, and data is stored together with other user data
This is a good mindset, as currently your BLEManager breaks the Single Responsibility Principle, i.e. has multiple responsibilities. The ObservedObject part is a SwiftUI specific thing, so it makes sense to be extracted out of that class.
Now, implementation-wise, one first step that you could make, is to transform the data property to a publisher. This will allow clients to connect to the data stream, and allows you to circulate the publisher instead of the BLEManager class, in the rest of your app.
import Combine
class BLEManager: OtherProtocols {
// you can use `Error` instead of `Never` if you also want to
// report errors which make the stream come to an end
var dataPublisher: AnyPublisher<Int, Never> { _dataPublisher.eraseToAnyPublisher() }
private var myCentral: CBCentralManager!
// hiding/encapsulating the true nature of the publisher
private var _dataPublisher = PassthroughSubject<Int, Never>()
// ...
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
_dataPublisher.send(10.0) // imagine it's the data received from BLE device
}
This way anyone who's interested in receiving BLE data simply subscribes to that publisher.
Now, on the receiving side, assuming that you also need an ObservableObject for your SwiftUI view, you can write something along of the following:
class ViewModel: ObservableObject {
#Published var data: Int = 0
init(dataPublisher: AnyPublisher<Int, Never>) {
// automatically updates `data` when new values arrive
dataPublisher.assign(to: &$data)
}
}
If you don't use SwiftUI (I assumed you do, due to the ObservableObject conformance), then you can sink to the same publisher in order to receive the data.
Either SwiftUI, or UIKit, once you have a BLEManager instantiated somewhere, you can hide it from the rest of the app, and still provide the means to subscribe to the BLE data, by circulating the publisher. This also helps with the separation of concerns in the rest of the app.
If you plan to connect to a single device, I would keep this very simple, and let BLEManager pass this data to a delegate as it comes in. The delegate would then be responsible for deciding what to do with the data. Swift by Sundell has a nice introduction to delegates if you're not familiar with the pattern.
You'd make a separate object, a "DataManager" or whatever you'd like to call it, and it would be the delegate to the BLEManager (and would own the BLEManager). Whenever new data comes in, call methods like bleManager(_:didReceiveWeight:) or bleManager(_:didReceiveBiometricData:).
This will split up the process of getting data from the device from managing that data and performing computations, which I think is a worthy goal.
If this is a "real" project, and you're just starting, I highly recommend this pattern. It's well understood, there are dozens of blog posts about it going back many years. It's easy to understand and implement. The only pattern that's even easier to implement is to post Notifications, from BLEManager.
On the other hand, if this is more of an exploration, and you want to jump into the deep-end and the future of Swift (and limit yourself to iOS 15), you could also look at AsyncStream to emit new values as they occur into an AsyncSequence. That eventually will likely be the "Swifty" way to do this. But it's a much steeper learning curve, and the tools are not all fully developed yet, and no one really knows how to use it yet (not even Apple; it's just too new). If getting in on the ground floor of technology excites you, it's an area that everyone is exploring right now.
But if you just want this thing to work, or you want to focus on learning Swift right now rather than learning "the future of Swift," I'd use a delegate.

Proper way of using Singletons (class with structs inside) in Swift

I would like to understand which is the proper way of structuring my code. I have created a Singleton class for video processing (detecting silences) and storing its processing output (the silence timestamps and other info) as a Struct. As this should be a single reference in the whole program, I am using a Singleton pattern, as I will run some multithreading tasks after this one and want to have a single source of truth for that class.
class SilenceDetector {
static let shared = SilenceDetector() // Singleton-pattern
// <-- should I init it?
func detectSilence(videoURL: URL) -> SilencesInfo { ... }
private struct Silence {...}
struct SilencesInfo { // <-- Should I use a Singleton-class?
// here I run different functions with Silence struct
...
I then use SilenceDetector.shared.detectSilence() but to my surprise I cannot access SilenceDetector.shared.SilenceInfo... but SilenceDetector.SilenceInfo.
Whats the proper way of doing it?
There is no proper way of using the singleton pattern. It was always a hack, but has been made completely obsolete by the SwiftUI environment.
Your SilenceDetector should be an EnvironmentObject, whether or not you're using SwiftUI Views.

For testing a Swift application that pulls from Firestore, how do I mock QuerySnapshot?

I have a swift function with the signature func getItem(querySnapshot: QuerySnapshot) and I don't know how to mock querySnapshot.
QuerySnapshot is a .h file which I'm not too familiar with. From reading, it seems like it's just a public-facing interface which is used instead of the actual implementation (please correct if wrong). I tried subclassing from this and using that but I'm not even sure where to add test data to it.
func getItem(querySnapshot: QuerySnapshot) {
for document in querySnapshot.documents {
let cost = document.get("cost");
}
}
I'm in the process of refactoring to where I am less reliant on the QuerySnapshot and convert to an Item prior to the call to this method; which would make my life a lot easier.
When dealing with testing and Firebase, there doesn't seem to be a lot of great testing infrastructure, especially for unit tests. The best thing to do is to create data objects that abstract away Firestore, use those objects throughout your code (instead of relying on Firestore), and test with that.
One thing that's been useful is CodableFirebase, which will let you decode and encode your snapshot into a Codable object. For example, if you create the simple data object
class MyDocument: Codable {
var name: String
var age: Int
}
You can then decode/encode your data using FirestoreEncoder().encode or FirestoreDecoder.decode()

Mocking a static class method in a swift unit test in a swifty way?

I'm a seasoned Objective-c programmer but I can't say the same for Swift, I'm having a hard time unit testing a class in swift without using frameworks like OCMock.
The Problem: I'm integrating Firebase into a mixed Objective-C/Swift project, and I need to configure it based on the build configuration of the app.
I've written a Swift class for that (that will be used by the obj-c app delegate), however since the firebase framework is configured trough a static class method, precisely FIRApp.configure(with: FIROptions), I need to mock this method somehow in order to unit test it.
My code, without any handle for Dependency Injection, looks like that:
#objc class FirebaseConfigurator: NSObject{
func configureFirebase(){
let config = configManager.buildConfiguration
var optionsPlistBaseName = getPlistName()
let optionsFile = Bundle.main.path(forResource: optionsPlistBaseName, ofType: "plist")
guard let opts = FIROptions(contentsOfFile: optionsFile) else{
assert(false, "fatal: unable to load \(optionsFile)")
return
}
FIRApp.configure(with: opts)
}
func getPlistName() -> String{
// retrieves correct plist name and returns it
}
}
I've done some research but so far I didn't find nothing that fits my solution, however I was thinking of one of the following:
I could pass a function that defaults to FIRApp.configure(with:) however I should do this from objective-c and the function also accepts a parameter, I was struggling with the syntax
I could use a wrapper around FIRApp, but I wanted to avoid it unless the only viable clean solution.
I could keep on playing with protocols and do dependency inversion, however being the method static I was struggling with the syntax again, I can't find an easy way to do DI with a mock class with a static method.
As a reference (both personal and for who might need it) these are some of the resources I found useful and upon which I will keep on digging:
Dealing with static cling in Swift
This Question
This article about generic unit testing
In the meanwhile, every help would be really appreciated.
As a sidenote, there are many ways I can solve this problem without struggling with mocking a static class method, but my aim here is to find out a way of mocking it in order to have a better understanding of the best practices when testing more complex situations.
You can indeed do any of those.
Closure Argument
You can have your configureFirebase function take an "applier" closure that defaults to what you originally used:
func configureFirebase(
using apply: (_ options: FIROptions) -> Void
= { opts in FIRApp.configure(opts) }
) {
// building |opts| as before
// Now replace this: FIRApp.configure(with: opts)
apply(opts)
}
Protocols
You need a Configurable protocol, and then to conform FIRApp to it for the default case:
protocol Configurable {
static func configure(with options: FIROptions)
}
extension FIRApp: Configurable {}
class FirebaseConfigurator {
var configurable: Configurable
init(configurable: Configurable = FIRApp) {
self.configurable = configurable
}
func configureFirebase() {
//load |opts|…
configurable.configure(with: opts)
}
}
If you're just going to use this in one method, though, it's merely transient state, and it should probably be a function argument rather than stored property.
(If it's unclear whether it's persistent or transient state because the whole point of the class is to call a single function, perhaps you don't even need a class, just a function.)

Does a read only BehaviorSubject interface exist in RX and if not, is it a bad idea to make one?

Implementations of rx provide BehaviorSubject<T> and Variable<T> as mechanisms for modeling properties that change over time (a useful replacement for C# INotifyPropertyChanged).
Generally these are exposed as Observable<T> but it would be more useful to expose properties as something like:
class ObservableValue<T> : Observable<T>{
var currentValue:T { get }
}
This can be created along these lines in swift:
class ObservableValue<Element> : ObservableType {
typealias E = Element
private let subject:BehaviorSubject<E>
var currentValue:E {
get {
return try! subject.value()
}
}
init(subject:BehaviorSubject<E>) {
self.subject = subject
}
func subscribe<O: ObserverType where O.E == E>(observer: O) -> Disposable {
return self.subject.subscribe(observer)
}
}
Does this already exist? and if not is it because it's against the aims of Rx?
The only way around it is to expose a separate currentValue or write consumers that assume the concrete implementation behind the exposed Observable is a BehaviourSubject or somewhere in the chain a replay() has occured e.g. the following snippet doesn't make it explicit that as soon as I subscribe I will get a value:
class MyViewModel {
// 'I will notify you of changes perhaps including my current value'
myProperty:Observable<String>
}
so code has to be written as if its 'asynchronous' with an underlying assumption it will act in an almost synchronous manner rather than:
class MyViewModel {
// 'I have a current value and will notify you of changes going forward'
myProperty:ObservableValue<String>
}
Having thought it over and discussed it a bit more presumably the reason it doesn't (and perhaps shouldn't exist) is that it's an introduction of imperatively accessed state.
Other mechanisms of maintaining state (such as scan) do so within the confines of chained observables rather than as 'dead-end' direct calls such as 'give me the value right now'.
Perhaps it would have it's place in a hybrid reactive/imperative approach but it may just hinder full embracement of the reactive style.
It's analogous to using promises or tasks in half of the code then reverting to synchronous blocking code in other parts.
In most cases what people do is create a standard view model that exposes properties via INotifyPropertyChanged. This allows UI elements to bind to them and receive property change events and keep the UI in sync.
Then if you want an IObservable for said property you take advantage of standard Rx operators that turn events into IObservable. You can google this to find lots of different implementations. You would generally create and consume these observables from something that is observing the view model rather than expose them on the view model directly.