I guess I'm just not sure what advantage I'd get by taking a (frankly) mind-bending approach to building an iOS app instead of following normal iOS conventions. And just to be clear I've done React development on the web, and I've thoroughly enjoyed it. Modern web development is mind-bending enough that React (and other approaches) felt like they brought some sanity to the table, but this doesn't feel like that. It feels overly complicated for iOS development, which feels like a largely solve problem.
What am I missing? I'm really not trying to be critical, I'm really trying to understand why you'd recommend I use this approach over standard iOS patterns. In the 8 years I've been developing for iOS I've never felt like I needed anything I've seen in here (or in any of the getting started tutorials I read through), but I'd like to try to see what I'm missing here.
It's a similar problem that AsyncDisplayKit was trying to solve, who's ideas later moved into the react world in general.
Another general design problem in iOS land is the MVC (massive view controller). View controllers tend to get huge as time goes on and become large 5000 line god objects that do everything. A general structure that encourages you to separate things out helps in avoiding this.
There is also a general trend of dependency management being singleton sharedInstance objects everywhere, which can make unit testing a pain. Especially in swift land today with it's lack of dyanmic mocking with OCMock.
Also I notice a general lack of unit / integration testing in mobile apps.
Another one is autolayout being a layout system that crashes your app. It doesn't let you recover in a more elegant way.
---
Now it's totally possible to avoid these caveats with skill, but the architecture of things in iOS land tends to land you into these problems naturally as your team and code size grows bigger. I don't know if react's architecture solves some of this, but those are general problems with standard iOS conventions I've noticed over 7 years with iOS dev.
I've seen nothing in React's (or Katana's) architecture that avoids doing things on the main thread when you shouldn't. I'd also argue that GCD offers a nice, elegant approach to not doing things on the main thread when you shouldn't.
But I'd say that MVC-discipline and thread-discipline are baseline skills that I'd expect from competent iOS devs.
RE: dependency management/unit testing, that's an area I agree needs improvement in general for iOS (though I do not see anything in Katana that significantly helps with that). Swinject (as one example) is alright. I'm not hugely impressed.
At the end of the day it was just a bad replacement for Core Data / Realm / whatever.
I wish I could tell you more but like yourself I really had absolutely no idea what the point was or what it was supposed to make easier.
In my experience, state management hasn't really been a bottleneck in iOS development, at least not to the point where it could account for half the effort of a project (or even 25%), and I've worked on some monster iOS apps.
Forgive me for being skeptical here, but I think this is one of those extraordinary claims requires extraordinary evidence situations. "It works great" sounds great and all, but I'd have a hard time convincing a dev team to adopt an approach because someone says it works great :)
The benefits I've seen so far:
- Swift's type safety means you can validate at the type level that your app state is correct. As in, you could have an enum with separate cases for your authenticated and unauthenticated states, instead of optional values to indicate what state you're in. This makes your state stronger, in that the types hold only valid states rather than some possible states being invalid (e.g. unauthenticated with non-nil user info).
- State transitions -- reducer methods, (state,action) -> state -- are purely functional which makes (IMHO) feature work straightforward, testing trivial, and debugging fairly easy.
- Your view controllers become super dumb. All they have to do is render the data and relay input events. This is a nice clean separation of concerns.
- If your navigation route is managed in state, you get deep linking "for free". This is especially helpful in one of our apps for handling global errors that could occur on any network request (update state, route to error).
- Every bit of code has an easily identifiable home.
- Communicating between view controllers is simple. Rather than "passing the baton" as state is accumulated (say a multistep form), state accumulates naturally in the store.
I'm working on combining ReSwift with app coordinators here: https://github.com/willowtreeapps/cordux
I've written about it a little bit here: http://willowtreeapps.com/blog/app-coordinators-and-redux-on...
All that said, I still consider this an experiment. We've written about 40-50k lines of Swift across two apps with 8 developers so far, and opinions are mixed between considering it excellent and considering it burdensome.
MVC pattern in iOS usually creates Massive View Controllers that are really hard to maintain and test.
I'll turn the question around - don't you see the potential for the same kinds of poor development practices that lead to "massive view controllers" to be just as prevalent with an approach like Katana? So far, I see nothing in Katana that will prevent poor developer discipline.
So - (and I say this mostly in jest) - if you have poorly disciplined developers, now, with Katana, you have two problems: 1) the same kinds of code quality issue that you'd have without Katana, with 2) the added downside of no one understanding yet another overly complex application framework.
The web complicates MVC but in standard desktop/mobile apps MVC is pretty straight forward.
Also, if I understand correctly, when you open an iOS app, it should always come back to where you left it, regardless of whether it was killed in the meantime (possibly by the OS).
I'm guessing these things are easier to deal with correctly if you use something like React+Redux or Katana.
That said, for people used to the React patterns, this could be a somewhat easy way to make the transition over to iOS programming! So that's cool :D
EDIT: https://gist.github.com/lucaquerella/74d8bbb5855f26249a27b4a...
I'm curious how frameworks like this handle dealing with large amounts of data? Let's say my data source has 25,000 rows that I need to search and filter through at any given time.
Using standard iOS paradigms I might store the data in a SQLite database using Core Data, use an NSFetchedResultsController to search the data, and display it using a UITableView. Any changes to the underlying data (insertions/deletions/updates) are automatically tracked and dealt with so the UITableView is kept up to date as the data changes. Behind the scenes there are lots of optimizations occurring in Core Data and the NSFetchedResultsController so only the data for visible rows are loaded into memory at any given time.
How would I achieve this behavior using a framework like this? How big/complex can this "single serializable data structure" get?
1) Deterministic View Renders 2) Deterministic State Reproduction
I think the following article gives a good overview of why this is a good idea:
https://medium.com/javascript-scene/10-tips-for-better-redux...
Quoting from the article:
"When your view render is isolated from network I/O and state updates, you can achieve a deterministic view render, meaning: given the same state, the view will always render the same output. It eliminates the possibility of problems such as race conditions from asynchronous stuff randomly wiping out bits of your view, or mutilating bits of your state as your view is in the process of rendering."
ComponentKit is another example of a native library inspired by react (also developed by FB):
The general things Cocoa could improve from JS:
* State Management * View lifecycle management * JSON -> UI (this became a lot worse with Swift) * Developer Happiness ( e.g. compare https://github.com/orta/vscode-jest to TDD in Xcode ) * Open toolchains (Swift is improving this)
The JS ecosystem focused on building apps that talk to APIs and act as simpler clients, the Cocoa one is about building apps that don't.
The last big project I worked on in Swift used an MVVM-style design. But I encapsulated the application state in a Swift enum.
The enum adhered to a protocol `AppState` which defined a method to turn an `AppState` into a view controller, as well as a static function to transition a view controller to a new `AppState`. Swift's strong type system ensured that it was very easy to guarantee that every view controller / view model had access to well-defined data types and that the flow of information only happened in one direction.
This meant that the entire transition logic for the application sat in one relatively small file that handled how each state could be displayed (and whether special cases were needed if coming from a particular state).
So for example, to present an alert in the application a view controller would ask its view model for the next AppState, and then ask to transition to that state.
E.g., in the view controller:
let nextState = viewModel.nextStateForSomeButtonPressed()
AppState.transitionViewToState(self, nextState)
And the view model's next state logic might be: return MyAppState.BasicAlert("Title", "Message")
The enum .BasicAlert has all the logic needed to transform into a view controller (in this case a `UIAlertController`), and the transitionViewToState has all the logic needed to present that controller.I have since found that this is one of the most pleasurable projects to maintain and update.
Edit:
This also allowed some nice functions to be written, like:
func initialApplicationState() -> [AppState]
Which the AppDelegate uses on startup to display the correct array of view controllers (in this case, the app displays a tutorial sequence on first-run, but that logic is completely decoupled from the AppDelegate).this is definitely something interesting. We started looking into that at the beginning of the project but later we dropped it since we haven't found a use case when that was really needed, the performance are quite satisfying at the moment! Katana already has a diffing algorithm in place and some other optimizations.
If you have a project that could benefit from it, please open an issue so we can take a look at it!
var payload: ()
What does that mean? What type has payload? I throwed it inside Xcode, it does compile but i still don't understand it.