Hacker News new | past | comments | ask | show | jobs | submit login
Using Apple's Combine Framework (heckj.github.io)
97 points by aaronbrethorst on Sept 19, 2019 | hide | past | favorite | 31 comments



I fear that battle is already lost on this one, but I'm disappointed to see FRP making it into the officially supported workflow on mobile.

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.


I must say I welcome it, unreadable tangles are agnostic, and can be written with any kind of pattern or language, and stack traces still work as you expect (although stack traces never been great on iOS).

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.


> 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.

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.


> In terms of practical mobile development, the asynchronicity you're dealing with is mostly network operations which execute exactly once per page.

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.


Both of the cases you mentioned could easily be modeled by simple event emitters. Yeah you could use and RX implementation, but 90% of the library would not be relevant and you'd be adding a lot of complexity without adding any value.


More reactive usually means less global state, which is generally for the better. It also encourages functional purity where possible. Previously (briefly) in iOS/macOS there was an attempt to move to closure-based APIs, which led to unreadable tangles of function invocations with often surprising calling threads. Before that for a long time there was a weak delegate object pattern, probably the least convenient way of interacting with things.


Doesn't an application still have a state, even if you don't store/manage it explicitly?

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.

https://prog21.dadgum.com/55.html


The state is there, of course. The goal here is to minimize the amount of state the code has access to at any particular time.

Functional purity is not the goal, it's just one way to reduce state.


So you still have state, but you can't get at it because it's been moved from the heap to the call stack?

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.


> Well managed accessible state can be greatly simplifying.

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.


I'm talking about the difficultly of reasoning about high level application state when it isn't explicit.

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.


Totally agreed. Being obsessive about (almost) anything in software is a recipe for pain.


I don't know, actually. Should the state other that what the task (or sub-task) can observe matter for it to function? Where would be the boundary, as there are lots of things happening simultaneously in a computer, and the global state is huge.


At least with closure based code, you can set a breakpoint and see the chain of executions which led to your critical portion. With FRP, basic flow of control is surrendered to the scheduler, and your stack trace doesn't tell you anything.

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.


There is no dichotomy here, it's perfectly fine to use imperative code where it fits better.

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.


It also means more memory being used.


Do you mean that it tends to require using immutable data structures?


Yes, and that it gets generated and thrown away in each view update, even if a view diff is taken into account.


I couldn’t agree more. « All reactive GUI» on iOS has been a total disaster in the only place i’ve seen it tried. It’s just the new fashion buzzword of the day, and it will follow the same bell-curve as the rest : people will use it everywhere, then after they realize it sometimes makes simple things harder, will learn to use it only where it really makes sense.


Since when did FRP come to denote any random framework that focused on reactivity and was somewhat functional vs the stuff Elliott and Hudak did in Haskell?


Not long ago I got tired of some web technologies and decided to look into iOS native app building. It took me some time to un-learn many things and have that "aha moment" but when I got it, I'm no longer craving for JS - except for few things that I think are better on the JS world and that is the when UI building is done better with code. In my experience when you have a situation where you want to represent data visually, it's more intuitive to create the UI by code and I find that UIKit is not much fun when dealt with only through code and no WYSIWYG interface building tools(btw WYSIWYG on XCode is not like Dreamviewer, it actually works pretty well and helps you more than limits you).

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


Apple is very good about getting iPhone users upgraded.

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.


For professional projects I have worked on, the rule of thumb we have landed on is usually to support 2 versions older than the current version.

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.


True but the maturity of the framework is also questionable, they changed quite a few things since the introduction. I think we should wait a bit before going all in :)


I've done a little bit of Native iOS, the main thing I found annoying was having to build UI's using a drag/drop editor.

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.


I see. You don't have to use the Interface Builder(the WYSIWYG drag and drop stuff) at all. The empty state of any new project will be initiated with it but you can simply delete it, remove any reference to it from the other files and do code-only UI.

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.


I on the other hand miss Delphi for Web, but thanks to WebAssembly that might be a thing.


Can combine in it's current state completely replace RxSwift in a project? (disregarding compatability).


That's my question as well. Should I learn both technologies for greenfield projects or just use Combine?


Combine is only iOS-13 If you wanna support any OS before that you have to use RxSwift for that


I believe it does replace RxSwift other then some operators that the Combine doesn't have currently can be easily added.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: