Using Apple's Combine Framework
heckj.github.io
heckj.github.io
I think FRP has a place, and React has been a transformative technology for web, but in that case you generally don't even have to think about the rx primitives at all if you don't want to.
As far as RX as it's penetrated mobile development in the past few years, the result I have seen is that many projects have become unreadable tangles of reactive chains, where the stack trace is no longer useful, because of the unfounded assumption that "more reactive" means better code.
The iOS APIs are certainly showing their age, and there's a lot of warts related to their Objective-C pedigree, but I think they have always had a strength in terms of being fairly agnostic about how they are integrated into your program's structure. I really fear that sense will be lost if third party libraries adopt combine as the dominant interface paradigm.
The other thing I'm not a fan of with FRP is that it makes the assumption that it's somehow good to deal with everything through one big unified abstraction layer. Rather than just dealing with your concrete business logic, you also have to set-shift to think about the flow of your program in terms of abstract chains of observables.
This level of abstraction makes the interfaces in your code less clear as well. For instance, with the delegate pattern, you can take a look at the protocol of the object you're consuming and get a clear idea about what the intent of that protocol is. In an FRP world, your interface point might be an Int-valued observable, which could be just about anything. To see how it's going to behave, you have to go see how that particular observable is defined in the calling context, which might end up being some chain of map-zip-filter-debounce operators which further obfuscate the intent. More abstraction rarely makes a program more readable or more performant, and FRP is all about abstraction.
Also I think you're poking at a false dichotomy with reference to things like global state, and functional purity. It's very possible to write simple, imperative code which makes use of pure functional programming, and immutable state. FRP puts you in kind of a "jail" which encourages you to produce code with those properties, but it comes with a lot of unnecessary costs as well.
I would not agree that using any particular technology makes code less clear. Also, I'm not sure how protocol interfaces, callback-based interfaces and reactive event sources differ in readability as they all basically provide a way to call your code at (without referring to documentation) random times.
Isn't a program more understandable if that state is sometimes made explicit rather than implicit?
Also if your goal is 'functional purity' then you might want to read this article from ten years ago.
Functional purity is not the goal, it's just one way to reduce state.
I'm not sure that makes programs:
1. Easily evolve-able.
2. Easy to understand.
Well managed accessible state can be greatly simplifying.
In my view the goal should be simple understandable code, not a meta-goal like removing all accessible state.
Absolutely--and functional programming is one way to achieve that. From the perspective a given function, all "state" is captured in its inputs and outputs. You know exactly what existing state can be used by the function (inputs) and exactly what state can be generated by the function (outputs) simply from looking at the signature.
To take a low level example - sorting a list efficiently is easy with imperative programming and mutable state ( Note you can still encapsulate that state within a object - it doesn't need to be global ).
On the other hand took about 20 years before somebody worked how to do it reasonably with a functional approach.
Being obsessive about a pure functional approach and/or having a holy war against heap managed state is a mistake in my view.
Functional approach is a tool in the toolbox, not a golden hammer.
In every project you always end up trying to solve the same problem of how to best propagate events and errors, often in an asynchronous fashion. So you end up with delegates, notifications, callbacks - but after a while you realize that all of that can be neatly distilled down to a single way of doing things. Then you got all these powerful build-in operations, which come in handy sometimes, and it's also much better at making sure you're actually handling all the possible cases.
I have seen this argument in favor of FRP a lot of places, but in my experience the problem is just vastly overstated. In terms of practical mobile development, the asynchronicity you're dealing with is mostly network operations which execute exactly once per page. The abstractions RxSwift, for example, give you for that are complete overkill for something which can be handled perfectly well by throwing a few lines of imperative code onto a background thread.
Besides that, you might have a few global events to handle like login and logout, which can be handled perfectly well by any number of event/emitter APIs or even a hand-rolled implementation of a few dozen lines. I don't understand why I need to import thousands of lines of code into my project just to "unify" these concerns.
Absolutely not true, I have been dealing with a lot of "simple" apps and always once you start introducing stuff like Push Notifications that trigger actions, websockets that send and receive messages and other "unexpected" messages to handle the traditional MVC methods just fall apart.
Having said that you can do pretty similar stuff without introducing RxSwift into your application.
With or without it, getting these entangled multi-source event handling mechanisms to work well takes a really disciplined approach.
This SwiftUI + Combine thing seems to fix that pretty well. It's unfortunate that it's iOS13+ only. One thing that I kept craving when I switched to native app development was ReduxJS like state management and visual UI for data management so I build one for myself[0]. Right now there are few friction points of keeping the UI in sync with the state but I think there's now a real opportunity with Combine to have nice state management like with ReduxJS in iOS.
[0] SwiftyState is an open source state management library inspired by ReduxJS and built with Swift 5: https://github.com/mrtksn/SwiftyState
https://gs.statcounter.com/ios-version-market-share/mobile-t...
If you’re new to iOS or don’t support a large audience, within 6 months most people will be using iOS 13.
Yes it's usually a small minority of users who haven't upgraded, but that tends to be people with older phones who can't update to the latest version. It makes my life harder, but it's better than punishing users who can't afford to upgrade their phone often.
On web you have HTML, on Android you can ignore the UI builder and write XML, on WPF/Xamarin Forms you have XAML, but on iOS you have to either use the drag/drop editor or build your UI in Swift/Objective C. Both feel messy.
You can edit the XML generated manually of course, but it's clearly not supported, and isn't as intuitive as other UI frameworks (as far as I can tell).
Maybe I just need to spend more time with iOS, I only spent a couple of months working on that app, and didn't do a lot of UI work but that was my initial takeaway.
That said, after some un-learning, I found that this Interface Builder is actually very powerful in taking care of the mundane stuff and so you can concentrate on the more interesting things. This is because you can interact with the drag-and-drop elements as if they were written in code, this thing doesn't generate some weird markup that you have to understand, instead, you interact with it through API in your code(essentially, linking the visually created element to a variable in your code. Once linked, you can work with that visual element as if it was created through code).
The positioning is done through constraints, you can have conflicting constraints with different priority and you can change these priorities in your code to have nice animations. You can add constraints, remove constraints, turn on/off constraints from your code, you can do everything nicely mixing the code with the Interface Builder. It's really amazing once you get over your previous ways of doing things and embrace the Apple way.