ReSwiftRecorder Add Action with property - swift

Recently I have used ReSwift API, And I want to add ReSwiftRecorder too!
The sample of ReSwiftRecorder in Github is very simple app
I need to to something more complicated. I have an object which get data from server and I need to It reloads its data when app is not connected to net. Here is my code:
AppState:
struct AppState: StateType {
var menus: Result<[Menu]>?
}
MenuReducer:
func menusReducer(state: Result<[Menu]>?, action: Action) -> Result<[Menu]>? {
switch action {
case let action as SetMenusAction:
return action.menus
default:
return state
}
}
AppReducer:
struct AppReducer: Reducer {
func handleAction(action: Action, state: AppState?) -> AppState {
return AppState(
menus: menusReducer(state: state?.menus, action: action),
)
}
}
MenuActions:
struct SetMenus: Action {
let menus: Result<[Menu]>
}
I know I need to change MenuAction to Something like this:
let SetMenusActionTypeMap: TypeMap = [SetMenusAction.type: SetMenusAction.self]
struct SetMenusAction: StandardActionConvertible {
static let type = "SET_MENU_ACTION"
let menus: Result<[Menu]>
init() {}
init(_ standardAction: StandardAction) {}
func toStandardAction() -> StandardAction {
return StandardAction(type: SetMenusAction.type, payload: [:], isTypedAction: true)
}
}
but I got error on init functions
Return from initializer without initializing all stored properties
when I set a initializer code the error disappear but app does not restore saved data! How can I fix it?

You will want to add serialization/deserialization code. The menus property needs to be set. Also, you will want to serialize that property as payload:
let SetMenusActionTypeMap: TypeMap = [SetMenusAction.type: SetMenusAction.self]
struct SetMenusAction: StandardActionConvertible {
static let type = "SET_MENU_ACTION"
let menus: Result<[Menu]>
init() {
self.menus = // however you initialize that
}
init(_ standardAction: StandardAction) {
let maybeMenus = standardAction.payload["menus"] as? [Menu]?
self.menus = // create Result from Optional<[Menu]>
}
func toStandardAction() -> StandardAction {
let maybeMenus = self.menus.asOptional // Cannot serialize Result itself
return StandardAction(type: SetMenusAction.type, payload: ["menus" : maybeMenus], isTypedAction: true)
}
}
So problems I see here: JSON serialization depends on Dictionary representation of your payload data, i.e. the properties of your object. Can Result be serialized directly? I guess not, so you need to convert it, probably easiest to nil.
All in all, the payload is the key you missed and now you have to figure out how to use it with the data you have at hand. Also, it makes me a bit suspicious that the Result type itself is part of the AppState. I expected it to be reduced away or handled before dispatching an action, like SettingMenusFailedAction instead of ChangeMenusAction(result:) or similar. Just as a sidenote: actions should be more than typed property setters.

Related

How to return value from a closure in a Struct in Swift?

I'm retrieving data from a website.
Networking works well. Data is parsed correctly from JSON.
A couple of references - In this struct:
Replies is the datamodel for the JSON
PrepareQuestions is a func which performs the parsing (I have it in an extension of the same Struct)
I'd like to have an object within this struct (downloadedData - 'Replies' is the struct with the datamodel) containing all the information downloaded, but I incur into an error due to "self being an immutable capture". Any suggestions? Thank you!
struct QuestionsManager {
var downloadedData:Replies?
func useData() {
manageQuestions(url: K.urlForRetreival, numberOfQuestions: K.numberOfSquares) { [self] (replies, error) in
if let replies = replies {
DispatchQueue.main.async {
downloadedData = replies // Here I got the error
}
}
}
}
func manageQuestions(url: String, numberOfQuestions: String, myCompletion: #escaping (Replies?, Error?)->()) {
let generatedUrl = URL(string: url + numberOfQuestions)!
let urlSession = URLSession(configuration: .default)
let task = urlSession.dataTask(with: generatedUrl) { (data, response, error) in
if error == nil {
if let fetchedData = data {
let fetchedProcessedData = prepareQuestions(data: fetchedData)
myCompletion(fetchedProcessedData, nil)
return
}
} else {
myCompletion(nil, error)
return
}
}
task.resume()
}
}
You're seeing this error because the closure captures an immutable self.
Just like primitive types (e.g. Int), structs are value-types, and Swift is built with the notion of immutability of value-types.
In other words, if you had let questionManager = QuestionManager(), you'd expect questionManager not to change. Even if it was a var, it can only mutate via direct action by the caller, e.g. questionManager.doMutatingFunc().
But, if a closure was allowed to capture self, it could modify itself at some later point. This is not allowed.
This simplest (only?) way to fix this is to turn QuestionManager into a class:
class QuestionManager {
// ...
}
struct is a value type. For value types, only methods explicitly
marked as mutating can modify the properties of self, so this is not
possible within a computed property.
If you change struct to be a class then your code compiles without
problems.
Structs are value types which means they are copied when they are
passed around.So if you change a copy you are changing only that copy,
not the original and not any other copies which might be around.If
your struct is immutable then all automatic copies resulting from
being passed by value will be the same.If you want to change it you
have to consciously do it by creating a new instance of the struct
with the modified data.
From https://stackoverflow.com/a/49253452/11734662

How to use an array from another class Swift

having a bit of a dilemma here. My method GetData() stores an array in jsondata1. How would I use that array in another class? I already set it as a subclass of Data. Thanks!
class Data {
let parameters = ["test": ViewController().myinfo]
func GetData() {
AF.request("my url", parameters: parameters).responseDecodable(of: Array<JsonData>.self ) { (response) in
let jsondata1 = response.value
// debugPrint(response)
print(jsondata1)
}
}
}
class Testing : Data {
func AnnualTesting() {
//How would I reference jsondata1 here?
debugPrint(jsondata1)
}
}
A few things...see comments.
class Data {
// This kind of thing is the source of more bugs than I can count.
// Are you *sure* you want to create a brand new ViewController that's connected to nothing?
let parameters = ["test": ViewController().myinfo]
// Declare an instance variable so it's visible to the subclass
// (Not sure what data type you want to use here. I'm calling it a String for convenience.)
var jsondata1: String?
func GetData() {
AF.request("my url", parameters: parameters).responseDecodable(of: Array<JsonData>.self ) { (response) in
// Note that this is filled in asynchronously,
// so isn't available as soon as the function is called.
// Currently you have no way of knowing in other code when the result *is* available.
self.jsondata1 = response.value
// debugPrint(response)
print(jsondata1)
}
}
}
class Testing : Data {
func AnnualTesting() {
//How would I reference jsondata1 here?
debugPrint(jsondata1 ?? "Nothing to see here")
}
}
Your declaration of jsondata1 is in scope of some block.
It is not visible from outside.
You have to declare it in interface of class,
just like you did with variable "parameters".
In general you can access variable from another class if it's declared in interface. It can be declared as instance variable or static (class) variable. And you should have instance of class to access instance var and should not have instance to access class / static variable.
Or as you did, via inheritance.

Referring to properties of containing class when using internal structs in swift

I'm refactoring a project to use MVVM and using protocols to ensure that my view models have a consistent structure. This works fine for defining public properties relating to input and output (which are based on internal structs) but defining actions in the same way is proving problemmatic as, currently, they are defined as closures which have to refer to view model properties. If I use the same approach as I have to input and output properties, I don't think I can access properties of the containing instance.
Example:
protocol ViewModelType {
associatedtype Input
associatedtype Output
associatedtype Action
}
final class MyViewModel: ViewModelType {
struct Input { var test: String }
struct Output { var result: String }
struct Action {
lazy var createMyAction: Action<String, Void> = { ... closure to generate Action which uses a MyViewModel property }
}
var input: Input
var output: Output
var action: Action
}
It's not a deal breaker if I can't do it, but I was curious as I can't see any way of getting access to the parent's properties.
Answer to your question
Let's begin with a note that createMyAction: Action<String, Void> refers to the type (struct) named Action as if it was a generic, but you have not declared it as such and will thus not work.
And to answer your question of the nested struct Action can refer its outer class MyViewModel - yes you can refer static properties, like this:
struct Foo {
struct Bar {
let biz = Foo.buz
}
static let buz = "buz"
}
let foobar = Foo.Bar()
print(foobar.biz)
But you should probably avoid such circular references. And I will omit any ugly hack that might be able to achive such a circular reference on non static properties (would probably involve mutable optional types). It is a code smell.
Suggestion for MVVM
Sounds like you would like to declare Action as a function? I'm using this protocol myself:
protocol ViewModelType {
associatedtype Input
associatedtype Output
func transform(input: Input) -> Output
}
Originally inspired by SergDort's CleanArchitecture.
You can prepare an instance of input (containing Observables) from the UIViewController and call the transform function and then map the Output of transform (being Observabless) to update the GUI.
So this code assumes you have basic Reactive knowledge. As for Observables you can chose between RxSwift or ReactiveSwift - yes their names are similar.
If you are comfortable with Rx, it is an excellent way of achieving a nice MVVM architecture with simple async updates of the GUI. In the example below, you will find the type Driver which is documented here, but the short explanation is that is what you want to use for input from views and input to views, since it updates the views on the GUI thread and it is guaranteed to not error out.
CleanArchitecture contains e.g. PostsViewModel :
final class PostsViewModel: ViewModelType {
struct Input {
let trigger: Driver<Void>
let createPostTrigger: Driver<Void>
let selection: Driver<IndexPath>
}
struct Output {
let fetching: Driver<Bool>
let posts: Driver<[PostItemViewModel]>
let createPost: Driver<Void>
let selectedPost: Driver<Post>
let error: Driver<Error>
}
private let useCase: PostsUseCase
private let navigator: PostsNavigator
init(useCase: PostsUseCase, navigator: PostsNavigator) {
self.useCase = useCase
self.navigator = navigator
}
func transform(input: Input) -> Output {
let activityIndicator = ActivityIndicator()
let errorTracker = ErrorTracker()
let posts = input.trigger.flatMapLatest {
return self.useCase.posts()
.trackActivity(activityIndicator)
.trackError(errorTracker)
.asDriverOnErrorJustComplete()
.map { $0.map { PostItemViewModel(with: $0) } }
}
let fetching = activityIndicator.asDriver()
let errors = errorTracker.asDriver()
let selectedPost = input.selection
.withLatestFrom(posts) { (indexPath, posts) -> Post in
return posts[indexPath.row].post
}
.do(onNext: navigator.toPost)
let createPost = input.createPostTrigger
.do(onNext: navigator.toCreatePost)
return Output(fetching: fetching,
posts: posts,
createPost: createPost,
selectedPost: selectedPost,
error: errors)
}
}

In Swift, how can you call a function immediately after object creation

I have some objects, which are structs, that I initialize from JSON dictionaries ([String : Any]) via an init function provided from an extension on the Decodable protocol (see Init an object conforming to Codable with a dictionary/array).
So basically, I have objects that look like this:
struct ObjectModelA: Codable {
var stringVal: String
var intVal: Int
var boolVal: Bool
// Coding keys omitted for brevity
}
struct ObjectModelB: Codable {
var doubleVal: Double
var arrayOfObjectModelAVal: [ObjectModelA]
// Coding keys omitted for brevity
var complicatedComputedVar: String {
// expensive operations using the values in arrayOfObjectModelAVal
}
}
ObjectModelB contains an array of ObjectModelA, and it also has a property which I only really want to set if the arrayOfObjectModelAVal changes.
I can use a didSet on arrayOfObjectModelAVal, but that only catches future changes to the arrayOfObjectModelAVal property. The problem is that I'm using a webservice to retrieve JSON data to create an array of ObjectModelB ([[String : Any]]), and I build it like this:
guard let objectDictArray = responseObject as? [[String : Any]] else { return }
let objects = objectDictArray.compactMap({ try? ObjectModelB(any: $0) })
My objects get created inside the compactMap closure, and init doesn't trigger the didSet.
I also can't "override" the init provided by the init from the Decodable protocol (the one in the closure: try? ObjectModelB(any: $0)) because my object is a struct and this isn't inheritance, it's just an initializer provided by a protocol. Otherwise, I'd "override" the init in my object and then just do super.init followed by some sort of mutating function that updates my complicated property and I'd make my complicated property private(set).
The only other option I can think of is creating that mutating function I just mentioned, and calling it in both the didSet when arrayOfObjectModelAVal changes, and then update my object initialization call with something like this:
guard let objectDictArray = responseObject as? [[String : Any]] else { return }
let objects = objectDictArray
.compactMap({ try? ObjectModelB(any: $0) })
.forEach({ $0.updateProperties() })
But now updateProperties could be called at any time by anyone (which is bad because it's really taxing), and there's no guarantee that it even gets called when creating the array of objects because the dev could forget to do the forEach part. Hence why I want a way to automatically call the updateProperties function right after object initialization.
I figured out a way to accomplish this using a factory method. Like I said in the original question, the initializer I want to use is being provided by a protocol extension on Decodable (see Init an object conforming to Codable with a dictionary/array). I went ahead and added a createFrom static func inside of the Decodable extension like this:
extension Decodable {
init(any: Any) throws {
// https://stackoverflow.com/questions/46327302
}
static func createFrom(any: Any) throws -> Self {
return try Self.init(any: any)
}
}
Now if I define an init on ObjectModelB with the same function signature as the init provided in the Decodable extension, like so:
struct ObjectModelB: Codable {
var doubleVal: Double {
didSet {
computeComplicatedVar()
}
}
var arrayOfObjectModelAVal: [ObjectModelA] {
didSet {
computeComplicatedVar()
}
}
// Coding keys omitted for brevity
private(set) var complicatedVar: String = ""
mutating private func computeComplicatedVar() {
// complicated stuff here
}
init() {
doubleVal = 0.0
arrayOfObjectModelAVal = []
}
init(any: Any) throws {
self.init()
self = try ObjectModelB.createFrom(any: any)
computeComplicatedVar()
}
}
This seems to work. I like it because if I don't add the init that exactly matches the one provided in the Decodable extension, then my object can still use the one provided in the Decodable extension. But if I do provide my own, I just use the createFrom factory method to create an instance of my type using the init from Decodable, and then do whatever else I want after that. This way, I control which objects need special init treatment and which ones don't, but at the point of creating the object, nothing changes. You still use the same init(any:) function.

Is it possible to store pattern of enum with associated value in array?

Let's say we have simple enum with message types:
enum MessageType {
case audio
case photo
case text
}
There is Handler class which handles messages with specific types only:
class Handler {
let allowed: [MessageType]
init(_ allowed: [MessageType]) { self.allowed = allowed }
func canHandle(_ messageType: MessageType) -> Bool {
return allowed.contains(messageType)
}
}
Basic usage example:
let handler = Handler([.audio, .photo])
print(handler.canHandle(.text)) // Prints false
I want to upgrade my MessageType and add associated value for some of message types.
class Audio {}
enum MessageType {
case audio(Audio)
case photo
case text
}
Problem is that I can't store enum's pattern in allowed array for the future check in canHandle:
// error: '_' can only appear in a pattern or on the left side of an assignment
let handler = Handler([.audio(_), .photo])
Is it possible to resolve such case in a "clean" way?
It's not possible to modify MessageType because it's in third-party library (e.g. making arguments optional and passing nil)
It's not possible to init MessageType.audio(Audio) with fake Audio because it could have private initializer
I hope to avoid switch, case let or other hard-coded checks in canHandle
Any suggestions? Thanks
If instantiating the enum with a default (or garbage) value isn't a big deal
enum MessageType {
case audio(String)
case photo
case text
}
protocol SneakyEquatableMessage {
func equals(message: MessageType) -> Bool
}
extension MessageType: SneakyEquatableMessage {
func equals(message: MessageType) -> Bool {
switch (self, message) {
case (.audio(_), .audio(_)),
(.photo, .photo),
(.text, .text):
return true
default:
return false
}
}
}
class Handler {
let allowed: [MessageType]
init(_ allowed: [MessageType]) { self.allowed = allowed }
func canHandle(_ messageType: MessageType) -> Bool {
return allowed.contains { $0.equals(message: messageType) }
}
}
Basic Usage
let handler = Handler([.audio(""), .photo])
print(handler.canHandle(.text)) // Prints false
print(handler.canHandle(.audio("abc")) //Prints true
Default (or garbage) values are unrealistic
This particular section is more specific in this context, but ultimately you're going to have breakdown your enum somehow as of Swift 4. So this is my suggestion: Dependency Injection of the Factory Pattern inside Handler. This solves all of your problems pretty cleanly without having to touch switch within Handler or optionals.
enum DisassembledMessage {
case audio
case photo
case text
}
protocol MessageTypeFactory {
func disassemble(message: MessageType) -> DisassembledMessage
func disassemble(messages: [MessageType]) -> [DisassembledMessage]
}
class Handler {
let allowed: [MessageType]
let factory: MessageTypeFactory
init(allowed: [MessageType], with factory: MessageTypeFactory) {
self.allowed = allowed
self.factory = factory
}
func canHandle(_ messageType: DisassembledMessage) -> Bool {
return factory
.disassemble(messages: allowed)
.contains { $0 == messageType }
}
}
Basic Usage
let audioValue: Audio = //...
let audioMessage = MessageType.audio(audioValue)
let factory: MessageTypeFactory = //...
let handler = Handler(allowed: [audioMessage, .photo], with: factory)
print(handler.canHandle(.text)) // Prints false
print(handler.canHandle(factory.disassemble(message: audioMessage))) //Prints true
You may be asking: wait... you just created another enum (also this is just an example, you could convert it to whatever you want in that protocol). Well I say: the enum you're using is from a library... see my notes section. Also, you can now use that factory anywhere you need to break down the library type to something, including inside Handler. You could easily expand the protocol MessageTypeFactory to convert your enum to other types (hopefully behaviors) you've created, and basically just distance yourself from the library type when you need to. I hope this helps clarify what I was getting at! I don't even think you should store MessageType in your class. You should store your own type which is some kind of mapped version of MessageType, like DisassembledType.
I hope this helps!
Notes
A few things:
I'm sorry your soul is owned by a library, really.
Use the Adapter Pattern. Clean C++ is one of many places you
can learn about it. Don't pollute your entire code base with one of
their types! That's just a tip.
I know you said you didn't want to use a switch... but you're working with enums... At least it's within the enum!
Use your own types! (Did I say that?)
Use the Adapter Pattern! (Stop it.)