Fail to encode/decode view model array - ios

I'm learning how to use MVVM to make a little project recently, and I've encountered some problems when I want to store the information.
Is it possible to make this Observable class conform to codable? Because when I tried to use userDefault to store a view model array in my home page, the system warned me that the view model does not conform to codable. However, when I go to my view model and mark this one var badHabitInfo: Observable<BadHabitModel> = Observable(nil), the warning disappeared. So I think if I make the Observable class conform to codable, it should be ok to encode/decode my view model array.
Does anyone can help? Thanks in advance!
class Observable<T> {
var value: T? {
didSet{
listener?(value)
}
}
init(_ value: T?) {
self.value = value
}
private var listener: ((T?) -> Void)?
func bind(listener: #escaping (T?) -> Void) {
listener(value)
self.listener = listener
}
}
This is my view model array in the home page.
var badHabits: [AddQuitItemViewModel] = []

Related

Swift closure keeps retaining weakly captured object

I use VIP cycle architecture in my project, where UIViewController has strong reference to Interator, Interactor has strong reference to Presenter and Presenter has weak reference to UIViewController:
UIViewController -> Interactor -> Presenter - -> UIViewController
After UIViewController dismiss all other module components usually deallocate too.
I have an someSharedService that fetches data from server in background queue and send updates in the queue via 'reactive' someSharedProperty.
Interactor is subscribed to the property:
someSharedService.someSharedProperty.subscribeForUpdate { [weak self] _ in
self?.presenter.sendUpdates()
}
And presenter updates view:
func sendUpdates() {
// this string crashes the app
view!.render()
}
I know that crash is happening because view is force unwrapped, but the root of the problem is that after viewController is dismissed interactor is still alive. But it must be deallocated right after the view deallocated. After checking out memory graph I found out that it's a closure (and only it) holding the interactor, despite it captured weakly.
Here's my implementation of the reactive property:
protocol Subscribable {
associatedtype SubscribeType
typealias UpdateResultsHandler = (_ values: SubscribeType) -> Void
var subscribers: [UpdateResultsHandler?] { get }
func subscribeForUpdate(_ updateHandler: #escaping UpdateResultsHandler)
}
class SubscribableProperty<Type>: Subscribable {
typealias SubscribeType = Type
private(set) var subscribers: [UpdateResultsHandler?] = []
var value: Type {
didSet {
subscribers.forEach {
$0?(value)
}
}
}
init(value: Type) {
self.value = value
}
func subscribeForUpdate(_ updateHandler: #escaping UpdateResultsHandler) {
subscribers.append(updateHandler)
}
}
That's how it looks in memory graph after crash:

How to create MVP and MVVM architecture in iOS Xcode project

I have read articles about MVC, MVP and MVVM architecture but I am not cleat about how to create each architecture in my iOS app. Which classes/controller files I need to use to make for each architecture. What is the difference between them if we are using with Storyboard/Xib/Programmatically?
As I am using Xcode default MVC structure for iOS apps but I want to create my new project with MVVM structure but I am not sure how to create that structure.
Any help would be highly appreciated.
Thanks in advance.
This is an oversimplification of the many variants of these design patterns, but this is how I like to think about the differences between the two.
MVC
MVP
MVVM
for more information you can look at here
MVVM architecture in iOS can be easily implemented without using third party dependencies. For data binding, we can use a simple combination of Closure and didSet to avoid third-party dependencies.
public final class Observable<Value> {
private var closure: ((Value) -> ())?
public var value: Value {
didSet { closure?(value) }
}
public init(_ value: Value) {
self.value = value
}
public func observe(_ closure: #escaping (Value) -> Void) {
self.closure = closure
closure(value)
}
}
An example of data binding from ViewController:
final class ExampleViewController: UIViewController {
private func bind(to viewModel: ViewModel) {
viewModel.items.observe(on: self) { [weak self] items in
self?.tableViewController?.items = items
// self?.tableViewController?.items = viewModel.items.value // This would be Momory leak. You can access viewModel only with self?.viewModel
}
// Or in one line:
viewModel.items.observe(on: self) { [weak self] in self?.tableViewController?.items = $0 }
}
override func viewDidLoad() {
super.viewDidLoad()
bind(to: viewModel)
viewModel.viewDidLoad()
}
}
protocol ViewModelInput {
func viewDidLoad()
}
protocol ViewModelOutput {
var items: Observable<[ItemViewModel]> { get }
}
protocol ViewModel: ViewModelInput, ViewModelOutput {}
final class DefaultViewModel: ViewModel {
let items: Observable<[ItemViewModel]> = Observable([])
// Implmentation details...
}
Later it can be replaced with SwiftUI and Combine (when a minimum iOS version in of your app is 13)
In this article, there is a more detailed description of MVVM
https://tech.olx.com/clean-architecture-and-mvvm-on-ios-c9d167d9f5b3
In MVVM, the View (which is really the UIViewController) asks the View Model for information.
textLabel.text = viewModel.textToShow
The view model has a representation of everything needed to construct the UI. The View asks the View Model for those values (like, what string to show).
In MVP, the Presenter tells the View what to do.
view.showText(textToShow)
The view controller implements a protocol that translates this request into view controller specifics:
func showText(_ text: String) {
textLabel.text = text
}
Here's an example of MVP: https://stackoverflow.com/a/54499119/246895.

Extension of a protocol where the associatedType is class?

I tried something like this:
protocol MyModelProtocol {
var name: String { get set }
}
protocol MyProtocol {
associatedtype Model: MyModelProtocol
func changeModel(_ model: Model)
}
extension MyProtocol where Model: AnyObject {
}
The compiler goes happy. However, inside this extension the compiler still isn't sure if Model is class or struct. Example:
extension MyProtocol where Model: AnyObject {
func changeModel(_ model: Model) {
model.name = "changed"
}
}
Thus, I get the error: "Cannot assign to property: 'model' is a 'let' constant"
How can I tell the compiler that in that protocol extension the associated type will always be a class?
Btw, this is just a short example. I know I could use an inout parameter in this case, but it doesn't work for me because I want to change the object inside of an async callback like so:
func changeModel(_ model: inout Model, completion: #escaping () -> Void) {
Api.shared.doRandomAsyncStuff() { (_) in
model.name = "changed"
completion()
}
}
And trying to do this leads me to the error: "Escaping closures can only capture inout parameters explicitly by value".
You could just an add intermediate assignment to a var. For a class/reference type, this would have the same effect as setting a property on the original reference. For a struct type it would make a copy, which wouldn't work, but should be avoided by the constraint on the extension.
func changeModel(_ model: Model, completion: #escaping () -> Void) {
var modelRef = model
Api.shared.doRandomAsyncStuff() { (_) in
modelRef.name = "changed"
completion()
}
}

ios ViewModel with ReactiveCocoa v3 and Swift 1.2

I'm having trouble using ReactiveCocoa in version 3. I want to build some view model for my login view controller. In my view controller I have outlet for password text field:
#IBOutlet weak var passwordTextField: UITextField!
In view model I have property for the text that is the password
public let emailText = MutableProperty<String>("")
and the question is how to bind it together? I'm able to get SignalProducer from text field:
emailTextField.rac_textSignal().toSignalProducer()
but how to bind it to emailText property? I've read in documentation that SignalProducer is not a Signal, but it can create now. There is method start() but it takes Sink as parameter and I'm a bit confused with design at this moment. Shouldn't emailText be a Sink?
Note: this is not properly an answer to your question, but I think it might help you.
If you are just want to bind your view to your view model, I suggest you to read this post which provides a one-class solution to the problem.
From there, you can very simply implement a 2-way binding so your viewmodel get updated every time the view changes and vice-versa. Here is my extension:
class TwoWayDynamic<T> {
typealias Listener = T -> Void
private var viewListener: Listener?
private var controllerListener: Listener?
private(set) var value: T
func setValueFromController(value: T) {
self.value = value
viewListener?(value)
}
func setValueFromView(value: T) {
self.value = value
controllerListener?(value)
}
func setValue(value: T) {
self.value = value
controllerListener?(value)
viewListener?(value)
}
init(_ v: T) {
value = v
}
func bindView(listener: Listener?) {
self.viewListener = listener
}
func bindController(listener: Listener?) {
self.controllerListener = listener
}
func bindViewAndFire(listener: Listener?) {
self.viewListener = listener
listener?(value)
}
func bindControllerAndFire(listener: Listener?) {
self.controllerListener = listener
listener?(value)
}
}
Hope it helps!

Generic delegate in Swift

I'm writing a small class which helps to fetch paged data automatically.
I have following delegate:
protocol DataAPIDelegate: class {
func fetchItemsForPage<T>(api: DataAPI<T>, page: Int, onFinish: ([T] -> ())
}
And DataAPI class, which stores delegate:
class DataAPI<T> {
weak var delegate: DataAPIDelegate? = nil
//...
//some other methods
}
Then, what I want to do is to write a view controller which uses DataAPI and communicates with this object by DataAPIDelegate:
class CarsViewController: DataAPIDelegate {
var dataAPI = DataAPI<Car>
// MARK :- DataAPIDelegate
func fetchItemsForPage<T>(api: DataAPI<T>, page: Int, onFinish: ([T] -> ()) {
let cars: [Car] = //some method for getting cars
onFinish(cars)
}
}
And I get the error: Cannot invoke 'onFinish' with an argument list of type '([Car])' in the line: onFinish(cars).
I think about it for a few hours and I don't have any idea why it doesn't work. Anyone meets that problem?
Generic functions provide single template implementation for multiple types referred by T, while what it seems you are trying to do is to provide an implementation of fetchItemsForPage method specifically for Car type.
The way it would work is:
protocol DataAPIDelegate: class {
func fetchItemsForPage<T>(api: DataAPI<T>, page: Int, onFinish: ([T] -> ()))
}
class DataAPI<T> {
weak var delegate: DataAPIDelegate? = nil
//...
//some other methods
}
struct Car { }
class CarsViewController: DataAPIDelegate {
var dataAPI = DataAPI<Car>()
// MARK :- DataAPIDelegate
func fetchItemsForPage<T>(api: DataAPI<T>, page: Int, onFinish: ([T] -> ())) {
let items: [T] = [T]()
onFinish(items)
}
}
... while what you are trying to do is:
struct Bike { }
class BikesViewController: DataAPIDelegate {
var dataAPI = DataAPI<Bike>()
// MARK :- DataAPIDelegate
func fetchItemsForPage<Bike>(api: DataAPI<Bike>, page: Int, onFinish: ([Bike] -> ())) {
let items: [Bike] = [Bike]()
onFinish(items)
}
}
... but in the latter snippet Bike is not a type but a placeholder, which works just the same as if you would use T.
Generic delegates aren't possible in Swift, if they aren't just functions. Because functions (closures) can be generic. See my gist where I tried to resolve the same problem: https://gist.github.com/artyom-razinov/fe8c2b7611a0ba3bd2a3

Resources