{"action":"created","issue":{"url":"https://api.github.com/repos/ReSwift/ReSwift/issues/372","repository_url":"https://api.github.com/repos/ReSwift/ReSwift","labels_url":"https://api.github.com/repos/ReSwift/ReSwift/issues/372/labels{/name}","comments_url":"https://api.github.com/repos/ReSwift/ReSwift/issues/372/comments","events_url":"https://api.github.com/repos/ReSwift/ReSwift/issues/372/events","html_url":"https://github.com/ReSwift/ReSwift/pull/372","id":358118901,"node_id":"MDExOlB1bGxSZXF1ZXN0MjEzOTY4NDU4","number":372,"title":"ReSwift thunks","user":{"login":"danielmartinprieto","id":2881251,"node_id":"MDQ6VXNlcjI4ODEyNTE=","avatar_url":"https://avatars2.githubusercontent.com/u/2881251?v=4","gravatar_id":"","url":"https://api.github.com/users/danielmartinprieto","html_url":"https://github.com/danielmartinprieto","followers_url":"https://api.github.com/users/danielmartinprieto/followers","following_url":"https://api.github.com/users/danielmartinprieto/following{/other_user}","gists_url":"https://api.github.com/users/danielmartinprieto/gists{/gist_id}","starred_url":"https://api.github.com/users/danielmartinprieto/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/danielmartinprieto/subscriptions","organizations_url":"https://api.github.com/users/danielmartinprieto/orgs","repos_url":"https://api.github.com/users/danielmartinprieto/repos","events_url":"https://api.github.com/users/danielmartinprieto/events{/privacy}","received_events_url":"https://api.github.com/users/danielmartinprieto/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":5,"created_at":"2018-09-07T15:44:46Z","updated_at":"2018-10-24T12:41:41Z","closed_at":null,"author_association":"NONE","pull_request":{"url":"https://api.github.com/repos/ReSwift/ReSwift/pulls/372","html_url":"https://github.com/ReSwift/ReSwift/pull/372","diff_url":"https://github.com/ReSwift/ReSwift/pull/372.diff","patch_url":"https://github.com/ReSwift/ReSwift/pull/372.patch"},"body":"In order to get rid of the confusion created by the _action creators_ and _async action creators_, discussed several times here, I've created this PR with a proposal:\r\n- I've removed the code related with _action creators_. As some people have mentioned before in the issues of this repo, an action creator in Swift is just the constructor of the type conforming to the `Action` protocol. This is different from the original Redux written in JavaScript; because the actions there are just plain objects, they created these pure functions in order to avoid mistakes assigning the correct `type` key (a string) and making them easier to create. Of course, in Swift we can also have a function that returns an instance or value of a type conforming `Action`, and then dispatch it, but unless the construction of that action is super complex, I don't think it's justified and, at the end, what the store really cares about is just an `Action`, independently of the way it was created.\r\n- I've also removed the code related with _async action creators_, which I think they are even more confusing, in conjunction with the previously mentioned _action creators_, because you can do the same with both of them and it's not clear where's the distinction. Again, the store should only be able to dispatch plain actions, and the only place where side effects can happen should be the middleware chain. This makes reasoning about ReSwift a lot easier, reducing the API surface of the library.\r\n- One way of producing side effects (there are several) in a simple manner and widely used by the original Redux library users is called _thunks_. The way this is implemented in JavaScript is through a little external package called [`redux-thunk`](https://github.com/reduxjs/redux-thunk), which gives you just a way of creating a middleware that will check every action and treat the _special_ ones in a certain way, letting the rest pass through the middleware chain, unaltered. I've implemented the Swift version of this middleware, adapting it the way ReSwift already works. Note that this code could perfectly live in an external package as well, given that it's just an extension and doesn't need access to private parts of the current library, or modify it in any way. This is an example of how it works:\r\n```\r\n// First, you create the middleware, which needs to know the type of your `State`.\r\nlet thunksMiddleware: Middleware<MyState> = createThunksMiddleware()\r\n\r\n// Note that it can perfectly live with other middleware in the chain.\r\nlet store = Store<MyState>(reducer: reducer, state: nil, middleware: [thunksMiddleware])\r\n\r\n// A thunk represents an action that can perform side effects, access the current state of the store, and dispatch new actions, as if it were a ReSwift middleware.\r\nlet thunk = Thunk<MyState> { dispatch, getState in \r\n    if getState!.loading {\r\n        return\r\n    }\r\n    dispatch(RequestStart())\r\n    api.getSomething() { something in\r\n        if something != nil {\r\n            dispatch(RequestSuccess(something))\r\n        } else {\r\n            dispatch(RequestError())\r\n        }\r\n    }\r\n}\r\n\r\n// As the thunk type conforms to the `Action` protocol, you can dispatch it as usual, without having to implement an overload of the `dispatch` function inside the ReSwift library.\r\nstore.dispatch(thunk)\r\n\r\n// Note that this action won't reach the reducers, instead, the thunks middleware will catch it and execute its body, producing the desired side effects.\r\n```\r\n\r\nI hope you like this change, as it simplifies the ReSwift API and its usage, and brings it closer to how the original Redux library works. I don't use thunks myself, neither in JavaScript nor Swift. Instead, I create a middleware for each entity that produces side effects in my program (an API module, a music player, a local database, etc.), and dispatch plain actions with the purpose of reaching one of those middleware, the reducer, or both. This is why I think this thunks middleware and the `Thunk` type could be considered an extension of ReSwift and exist on their own package.\r\n\r\nI've added some tests as well, but I haven't touched the docs, as I wanted to get feedback from the community first. I can later add some comments and docs, and maybe also put examples of how I avoid thunks just using specialised middleware.\r\n\r\nFinally, as a technical note, I've implemented the `Thunk` type as a struct conforming to `Action`. At the beginning, I was trying to accomplish the same declaring a `Thunk` protocol extending `Action`, but it was cumbersome dealing with PATs and found the struct approach simpler. Let me know if you have ideas to improve both the API and its implementation!"},"comment":{"url":"https://api.github.com/repos/ReSwift/ReSwift/issues/comments/432638937","html_url":"https://github.com/ReSwift/ReSwift/pull/372#issuecomment-432638937","issue_url":"https://api.github.com/repos/ReSwift/ReSwift/issues/372","id":432638937,"node_id":"MDEyOklzc3VlQ29tbWVudDQzMjYzODkzNw==","user":{"login":"kedio","id":3375074,"node_id":"MDQ6VXNlcjMzNzUwNzQ=","avatar_url":"https://avatars0.githubusercontent.com/u/3375074?v=4","gravatar_id":"","url":"https://api.github.com/users/kedio","html_url":"https://github.com/kedio","followers_url":"https://api.github.com/users/kedio/followers","following_url":"https://api.github.com/users/kedio/following{/other_user}","gists_url":"https://api.github.com/users/kedio/gists{/gist_id}","starred_url":"https://api.github.com/users/kedio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kedio/subscriptions","organizations_url":"https://api.github.com/users/kedio/orgs","repos_url":"https://api.github.com/users/kedio/repos","events_url":"https://api.github.com/users/kedio/events{/privacy}","received_events_url":"https://api.github.com/users/kedio/received_events","type":"User","site_admin":false},"created_at":"2018-10-24T12:41:41Z","updated_at":"2018-10-24T12:41:41Z","author_association":"NONE","body":"@danielmartinprieto you said in your first post:\r\n>  I can later add some comments and docs, and maybe also put examples of how I avoid thunks just using specialised middleware.\r\n\r\nI would really like to see this example of how to avoid thunks by using specialized middleware. I've been going over the issues and doc on this repository and still can't find a consensus for doing async network calls.\r\n\r\nI feel like the best approach is by using middleware and I like the idea to have specialized middleware by API/service instead of one thunk middleware that can be used for all sorts of side effects. I will probably have a shot at implementing it myself, but I would appreciate seeing how you do it!"}}
{"actor":{"display_login":"kedio"}}