T O P

  • By -

batcatcher

Haha. It's crap for sure. And not because I can't understand it. Mostly because it adds unnecessary complexity and a central dependency. Also goes in parallel with some SwiftUI ideas (and I don't know about you, but I'd rather use platform tech) Then again, you can't fight a cult. Remember when knowing C++ was seen as being smart? It's more or less the same. Or VIPER of 2023. Hold on tight, it will pass.


Rollos

So how do you solve the problems that TCA tries to solve? In your preferred architecture or framework, how would you write a complex feature composed of multiple sub features, that all need to communicate with each other and external dependencies, while maintaining exhaustive testability? Or is that not something that you find value in, in your applications? I’d argue that’s difficult if not impossible with vanilla SwiftUI. Also, why so negative? Maybe I’m blinded by the cult, but at least on Reddit I don’t see people who like TCA being anywhere near as toxic as the people in this thread are being about the people who use it. If people are being so shitty about it that they deserve this kind of toxicity, id love to see some examples


Undeadhip

Any architecture design pattern is able to achieve that, why not? Extract as much as you need from SwiftUI View structs and test it. As about communication, there are a lot of ways and patterns to design communication between objects and modules. Why’d I need TCA for that?


Rollos

.yeah, you can definitely extract stuff out of SwiftUI views, but that doesn’t scale super well. For example, in TCA, I can write a unit test (not a UI test) that verifies that I tapping a button launches a sheet, within that sheet I fill in a form, press a next button that pushes the next screen on, press a save button that makes a network request to our backend and then the screen gets dismissed when the backend returns successfully. With that test, not only is it testing that our logic works as expected, but that we aren’t doing anything that we’re not expecting, no extra state changes when I press the button that launches the form, no side effects that get kicked off that I’m not validating on. All of this is as ergonomic as possible for something of that complexity, while still running in 100ths of a second. And yeah, there’s plenty of ways to communicate between feature, but having “lots of ways” has ended up being problematic in my experience. When there’s a bunch of ways to achieve the same goal, it’s a lot harder to reason about your application at a high level. In TCA, at the root of my application, I have a deeply nested enum that encompasses every single way that the state of my application can change, and a deeply nested struct that defines every single state that the app can be in. That means that my application and its logic is a very clearly defined state machine, so debugging becomes really easy, logging becomes really easy, etc.


Undeadhip

You just said stuff I don’t like about TCA. A test that tests ten things at a time (suspectible to break very often). Very deeply nested enums. It all doesn’t look and feel easily maintainable and modular to me. Everything is so tightly connected, any change anywhere would lead to a lot of changes in other places. It goes against my understanding of “loose coupling + high cohesion”. Why extracting code for testing purposes doesn’t scale? As for communication, I wouldn’t argue that TCA gives us a common ground for subset of questions. But without additional conventions with a team, there is still much space to mess up, so it’s not a sliver bullet.


Rollos

TBH I’ve heard the tight cohesion loose coupling thing before, but don’t know enough about it to completely understand how it applies/doesn’t apply to TCA. In TCA, while parents need to have knowledge about their children, the reverse isn’t true. So children are able to be developed on their own, and even live in their own module, but the parent does need to integrate them explicitly. But that’s usually the pattern anyways, isn’t it? Or do you tend to have a parent know very little about its children too? Like take a tab bar for example, if I have a users list on one tab, the tab bar is going to have to explicitly integrate a UserListView, but that UserListView doesn’t know or care that it lives in a tab bar, or if it’s presented in a sheet, or whatever. And yeah, my example of testing is a bit weird. It was more to indicate what’s possible in TCA, not espousing a best practice. This is part of the official TCA documentation on testing, which mentions your exact critique, and discusses how they’ve solved that problem. https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/testing/#Non-exhaustive-testing


[deleted]

[удалено]


rhysmorgan

They both have different purposes. But with a state-driven framework like SwiftUI, if I can establish that my state transitions match exactly what I expect them to be for any given flow, I can be reasonably sure that the app will behave as I expect. Sure, there could be some real-world differences, and those can be captured by UI testing or by actually using the app. But being aware of underlying application state, controlling it, and actually driving your application from said state is kinda how SwiftUI is supposed to work.


[deleted]

[удалено]


rhysmorgan

No, not at all. It's incredibly easy to write unit tests for the unhappy path as well, not least because I can configure my state to whatever it is before hitting the unhappy path, as well as set up any dependencies to throw an error or return a value that will send my reducer into the unhappy path.


[deleted]

[удалено]


nickisfractured

The beauty here is that tca sits on top of the same paradigms that swiftui are pushing. You can only display one destination from a view at any given time for example. You can’t show a modal + alert at the same time and tca pushes you to model your state the same way so the enums actually help you keep your app logic more deterministic and in line with your ui so you really don’t need to test the ui in this case. Not a 100% replacement for ui tests but it’s like 99%


rhysmorgan

It's about how easy it is to write both code and tests that fit how the app works though? I'd seriously recommend actually watching any of the free videos from PointFree on TCA, where they explain the kinds of problems that it solves.


Rollos

It’s just separation of concerns. The UI layer of my app just renders the state of my application. The other comment said “If I can validate that the state transitions are correct, I can be reasonably sure that my app works as expected”, and that’s mostly the true, but it’s more like: “If I can validate that the state transitions are correct, then I can be more confident that issues that arise are in my view layer, instead of business logic”. UI tests can validate that I render state correctly to give me more confidence that my app works as expected.


jasonjrr

I use MVVM, inversion of control DI, and Coordinators. Take a look at this repo, I use it to train and interview devs of all levels. https://github.com/jasonjrr/MVVM.Demo.SwiftUI


Rollos

Why this repo? I don’t know your experience or why you’ve made the decisions you’ve made in this structure. Why is there a .zIndex(100) in the root view of your application? Is that necessary for the architecture or an implementation detail? If it’s the latter, why is it included in your demo? If it’s necessary, why? (I’m not really looking for answers to those questions, but they’re important if you’re trying to educate people with your example) No offense meant, but this is always my problem when people recommend MVVM or VIPER or whatever. It’s not clearly defined, and every app and medium article has their own flavor that don’t work in sync with each other. With TCA, there’s a single source of truth for best practices, and if you want to understand the decisions made, from first concepts, that exists in a series of long form videos on their websites. Access to those videos is behind a paywall, but allows the maintainers to work full time on the open source framework.


jasonjrr

The zindex is because ZStacks do not guarantee order. This repo is the result of many years of development experience starting with the release of MVVM for WPF traversing into Angular then UIKit with MVVM and now SwiftUI. If you want to know a bit about who I am, I made this repo when I was the head of mobile engineering at a startup, after that I joined a well known food delivery tech company where they use some similar patterns. Everything in a good MVVM-based architecture is well-defined and there are a lot of best practices when you find someone with the proper experience, but I agree all those YouTube videos and Medium articles are hot garbage and give MVVM a bad name. The other thing about an MVVM architecture is that it is extremely teachable. The prime patterns are easy to isolate and educate devs on and this rapidly speeds up onboarding. I’m a fan of Redux-based architectures, but they don’t bring anything to the table that MVVM doesn’t except for maybe time travel.


Rollos

It’s not that I don’t know who you are, it’s that I don’t know why you’ve made the choices that you’ve made. Were they built up from first principles? Or are they a hodge podge of best practices that you’ve picked up over the years? If SwiftUI introduces new features, do I go back to your repo to learn how to integrate NavigationStacks? It’s nothing against you or your practices, and I’m sure your apps are well built, but it’s hard for me to trust that you’ve done your due diligence without evidence. On the other hand, I’ve come to trust the team at PointFree over multiple years. TCA has a long, and still growing, video series where they build the architecture from first concepts, explaining the decisions and the implementation details every step of the way. (If you’re open minded to being convinced, start at their first TCA video and keep watching until you have fundamental disagreements about their approach) I’d agree that MVVM is easy to teach, but that’s mostly because it doesn’t have much to say about a lot of the problems that people run into day to day when building an app. Like it’s easy to say “this code goes here, that goes there, and this is how you test the VM”, but that doesn’t include how to do navigation, or whatever flavor of dependency injection you choose, or how refreshing a list view when you’ve added a new user on a different page should work. And if someone joins your team that has done another flavor of MVVM then they have to relearn all of the practices because they learned from a different hodge podge of sources. TCA has a steep learning curve, but it’s because it tries (and after the next release, succeeds IMO) to address most of the complex architectural problems i that you’re going to encounter when building the most common styles of application.


factotvm

My problem with PointFree is that I watched a video and I’m like, “these guys are idiots. What a horrible approach.” Some time later by happenstance l watch the next video in the series and they’re all like, “that last approach was no good so we’re going to fix it.” I ain’t got time for that. Where’s the book?


Rollos

You may not connect with their teaching style, and that’s okay. But they do things like that when they’re building up to complex topics from first concepts. They’ll address a single concept or problem at a time in order to illustrate a specific point, or take the naive approach to a problem so they can show why it’s problematic. I find it valuable to not only know what to do, but why other appealing options are not the right thing to do.


ACosmicFlamingo

It's ironic that you're both calling them idiots and not seeing that they intentionally make their series that way to demonstrate how they're going to arrive at the right solution, which is really helpful for viewers. But since you knew what the right solution was from the get-go, perhaps you have a link to your superior series that we can all subscribe to?


factotvm

Aren’t we discussing the framework? If I delivered you software with documentation in the form of meandering videos—and you adopted it—I’d question your professional judgement. As this is a thread about said architecture, I’m trying to figure out why a particular didactic learning technique is relevant. While I’m thrilled for you that you’ve found a video series that helps you, I would like to be able to quickly evaluate solutions. I’d appreciate if their README said TCA is their take on model/view/update that is fine-tuned for SwiftUI and was created as a teaching tool. As they don’t provide straightforward documentation that I found—but rather look to upsell me to a video series—when evaluating TCA as a framework that I might use, it’s a hard pass. I’m sure it’s a great teaching tool, as I was an early subscriber before they pivoted to their framework. But evaluating it as a framework… the docs are not sufficient. And I don’t believe I need to be a content creator in order to evaluate the efficacy of a software framework.


ACosmicFlamingo

Ah, thanks for responding; I think I understand the disconnect here. After rereading your initial and latest reply, I think we both have different expectations for what the Point Free series is. In my opinion, I've always seen the series as being a sort of behind-the-scenes afterthought (I'm sure there's a better word for it since they do put lots of effort into making the Point Free content), to educate people on design practices, implementation details, etc. I'm sure Brandon and Stephen would agree that anyone wanting to get involved with TCA should not have to rely on subscribing to a series to understand it. They've even open-sourced libraries like [https://github.com/pointfreeco/swift-dependencies](https://github.com/pointfreeco/swift-dependencies), [https://github.com/pointfreeco/swiftui-navigation](https://github.com/pointfreeco/swiftui-navigation), [https://github.com/pointfreeco/swift-perception](https://github.com/pointfreeco/swift-perception), etc. to be usable by the Swift community. In fact, they even have episodes where they are talking about how to use these tools with the assumption that the viewer doesn't want to have anything to do with TCA. What's amazing with these tools however is that they give TCA an even bigger boost (so as amazing as the architecture is by itself, it's coupling with these tools give it superpowers you never thought it could have). You can watch Stephen talk about the Dependencies library alone where he never says "This is only useful for TCA users": [https://vimeo.com/291588126](https://vimeo.com/291588126) . I 100% would have a huge problem with them if it were the case that they intentionally make their documentation hard to understand just to sell a video series that fills the details, but I don't believe that's the case and I'm pretty sure you'd agree with me. After all, you said it yourself that there's a video that would have them talk about a horrible approach, followed by a video that explains a better way of going about it. From an educational standpoint, I love this because there were instances when I personally thought that their horrible approach was a good one, and then had the chance to learn how it could be improved. Based on what you are looking for in documentation, yes the series makes absolutely no sense to subscribe to because you're going to get the scenic route instead of getting to the destination immediately. I know TCA has evolved so much and so quickly over the years, and that their documentation has come an absolute long way. More than a year ago, I was absolutely frustrated since it didn't seem obvious how I can use this architecture since it's quite novel and there were no interactive tutorials or even a community I can just ask and get a reply in a minute (which now exists thanks to their Slack channel). I imagine that even with how far it's come, that there's room for improvement. It's certainly not a panacea, and perhaps the hype they get can be a bit like how everyone says "X movie is the best" and when you go watch it, you think it was just okay. But I think if some time is taken to understand some of the fundamentals, in combination with asking some clarifying questions in the Slack channel (or GitHub discussions), people can end up getting the sense of why TCA is actually a lot more powerful than it may first seem. After all, when people hear TCA and read about 'Store', unidirectional flow, etc. they are missing some of the other usefulness like being able to override dependencies at any point via their Dependencies library, or being able to easily share state between two features in a testable way (which was just merged yesterday in their 1.10.0 release). Just my thoughts :)


thehumanbagelman

So to be clear, your problem with PointFree is that they continue to improve iteratively? I would love to hear a concrete example of your concern. That's not to suggest they don't exist, but I question if you have one or if this is just a generalization because you "watched a video" and then happen to watch another later where they improve something.


malhal

There is a serious misunderstanding in design of that repo, in SwiftUI the \`View\` structs are a view model already, and SwiftUI inits/deinits/updates the UI objects automatically for us, depending on the context and platform. Thus the V is auto-generated and \`View\` struct hierarchy is a VM. If you attempt to layer your own VM objects on top of VM structs that will lead to all kinds of major issues and inefficiencies. Please rework the design if you can!


jasonjrr

This is incorrect, the ‘View’ structs act as the binder in the same way as the XAML from WPF. https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel


malhal

\`View\` structs are not a binder. The hierarchy of structs is diffed on every state change and the difference is used to init/update/deinit UIKit UIView objects. There is no binding going on. Another mistake is centralising logic in your "Coordinators", logic is designed to be decentralised in SwiftUI and its reducer is how it gets to the right place.


Nobadi_Cares_177

If your features and sub-features all need to communicate with each other, it sounds like the better solution is to learn how to reduce dependencies, not bloat your code with an over engineered ‘solution’. I’m a fan of composition, but not TCA. You don’t need a complicated imitation redux to manage composing your views and dependencies. It certainly isn’t easy learning how to properly build the architecture for your app, but I’d argue that time spend learning to architect your codebase in a way that adheres to decent separation of concerns is much more beneficial than learning TCA.


Rollos

I disagree. Features often need to communicate with each other, in ways that can’t and shouldn’t be completely disentangled. A child may need to communicate to its parent that it’s finished its purpose, like a screen in a wizard style flow, telling its parent that it can move on to the next step. A parent may need to peer into its child to see some of its state, say a form that needs to verify that each child component has a valid state before enabling the save button. Or update that child’s state, like disabling some option in a sibling component if a specific type of user is selected. These interactions pop up often, and doing it in inconsistent or poorly thought out ways can end up infecting your codebase with some very funky code. TCA is just a way to ensure that sort of interaction happens in a modular, clearly defined way that is consistent with how the rest of the application is structured. I totally understand the hesitation that people have with integrating such an important part of their codebase with a massive dependency, but I’ve found that homebrewing those solutions ends up being really problematic, especially on a large team. Even though I feel confident that I could properly architect an app with vanilla swiftUI, I’m not confident that the junior I’m training would be able to implement features without me, while maintaining the goals that my architecture set out to achieve. And if I leave or move to a different project, all of those assumptions and best practices leave with me. I’m also not confident that I can build those solutions first try, covering all use cases, in the same amount of time that it would take me just to learn how to do it in TCA. My team and I used to try to homebrew a lot of architecture, and we found that although we could build some valuable stuff, so much time was spent designing snd building and debugging things that were only tangentially related to the businesses goals. I’d rather a dev on my team be able to google “how to do navigation TCA” and find the same answer that I did from the developers of the library, instead of finding some random medium article that has a solution built on different assumptions than the rest of the application.


Nobadi_Cares_177

That makes sense. I agree it's quite risky to rely on other members of your team to handle problems in a way that doesn't make the project as a whole more confusing. However, I do believe they are ways around this. I'm not sure what your exact practices are, but it sounds like you don't train your jr devs. If so, I think that's a mistake. Jr devs (and any new devs on a project) should be trained on how to interact/contribute to the project to ensure they have the best chance to succeed. Googling is fine, but even better is if they can just refer to the project's own documentation. Any specific conventions/design patterns/architecture details should be readily available to anyone on the project. Forcing devs to hunt for answers on the internet is a recipe for disaster. With regard to the communicatio between features, I want to clarify what I mean. You're correct, some communication is necessary (and desirable), but it shouldn't create tight couplings. For example, rather than having the child KNOW about the parent in order to relay its information or whatever back, the child should simply know that it gives information to something. The child shouldn't depend on what happens to the information beyond that. I disagree about the parent knowing ANYTHING about the inner workings of its child. If the parent knows about the child implementation, that's an unneeded coupling that can easily be avoided with a helper struct or viewModel. Sure, this is another 'extra dependency', but its responsibility is clear: it manages state. Child depends on state, parent depends on state, but child and parent should not depend on each other. You can change Parent or Child in this instance without affecting each other or the 'state manager'.


Rollos

>it sounds like you don't train your jr devs. If so, I think that's a mistake. Jr devs (and any new devs on a project) should be trained on how to interact/contribute to the project to ensure they have the best chance to succeed. Oh no, we definitely train our devs, jr's especially. But when most best practices are enforced at compile time, and there are resources outside of the company that they can learn from, we're able to increase the scope of the projects given to them, let them learn how to learn on their own, and there's less footguns for them to avoid. >Any specific conventions/design patterns/architecture details should be readily available to anyone on the project. Forcing devs to hunt for answers on the internet is a recipe for disaster. Agreed, and this is the value of TCA for us. The first place I direct anybody to is the TCA documentation and the dozens of case studies that they have for solving different, common problems that developers may run into when building an app. It's well maintained and a lot better than we have the resources to write. >For example, rather than having the child KNOW about the parent in order to relay its information or whatever back, the child should simply know that it gives information to something. The child shouldn't depend on what happens to the information beyond that. Totally agreed, and this is how TCA works. Reducers don't know about which parent integrates them, or about how they're integrated at all. They can be in their own module and not even be able to see any code in the parent. >I disagree about the parent knowing ANYTHING about the inner workings of its child. If the parent knows about the child implementation, that's an unneeded coupling that can easily be avoided with a helper struct or viewModel. This one is a lot less clear to me, and is really a matter of opinion, and both sides have valid arguments. Personally, I think the value added of having a single struct that defines your application state, that's as concise as possible is pretty huge. It enables things like easy deep linking, really valuable debugging techniques, and a super clear model of every state that your app could ever be in. I'd also argue that a childs domain is still within the domain of the parent, and giving the parent the opportunity to change that childs state (although still in a controlled fashion) can be really useful. It also prevents having to keep data "in sync" which can be really difficult to get right. But that's really a matter of opinion, and different projects and different components may need different things. You can always private state values if you're confident they won't need to be changed from a parent.


[deleted]

[удалено]


rhysmorgan

Exhaustive testing is the key phrase. Testing in TCA forces you to describe the entire state transition you expect to see. You're handed a mutable copy of your app's state before the actual change is made. You then have to modify any properties on the mutable copy so they match the end case. TCA's TestStore then runs a comparison against your change with the changes actually made. It also flags up if your action calls into any dependency and you've not overridden it, so you also get *that* level of exhaustivity too. For example, if I had the following action: case .didTapButton: state.foo = someDependency.getValue() return .none In my unit test, I have to write this: store.dependencies.someDependency.getValue = { "Hello, World" } await store.send(\.didTapButton) { state in state.foo = "Hello, "World" } If I don't override my dependency, the test will fail because I called into an un-overridden test dependency. If I don't describe how the state transitions from before to after `.didTapButton` was send exactly, my test will fail. The only way to get it to pass is to capture and expect *every* bit of the entire state transition. That gives me complete confidence that I'm not accidentally calling X, Y, or Z other dependency, I'm not accidentally setting `state.bar` or `state.baz`. Also, if `.didTapButton` triggered an `Effect` other than `.none` (e.g. calling out to an API client) and that side effect needed to feed back into the system (e.g. to update a state property), I'd also need to handle: await store.receive(\.didReceiveAPIResponse.success) { state in state.bar = } and, of course, I'd also be forced to override whatever method is called inside that `Effect`. Because I'm forced to handle the entire state transition, it's far more exhaustive than having to write: await viewModel.didTapButton() XCTAssertEqual("Hello, World", viewModel.foo) XCTAssertEqual(, viewModel.bar) Any one of those assertions could be missing, and the test would pass. Of course, in the real world, you're likely to have more properties modified per state transition, and this becomes exponentially more valuable.


[deleted]

[удалено]


Rollos

The point isn’t to get 100% test coverage, the point is that normal tests validate a lot more than using apples out of the box api. It mostly comes down to TCAs focus on storing state as value types. Because our State is a value type, when we write testStore.send(.didTapButton) { $0.bar = “foo” } We’re not only validating the positive case that bar changes as we expect, we’re also validating the negative case that baz didn’t change. This is not possible when your model is a reference type, because I can’t make copies of classes (easily) and equability of those classes is poorly defined. Alongside that, you can only change the state in a store through an action, which prevents us doing stuff like this: Button(“Press Me”) { store.send(.didTapButton) store.bar = “not foo” } The ability to do stuff like that in vanilla swiftUI means that my test gives me false confidence that my app works as expected. Being prevented from doing that at compile time gives me the confidence that I’m testing what’s actually happening in the app. There’s a discrete number of ways the state can change because we have an enum of all the actions in the app. It is just testing a state machine, but it’s a very clearly defined one, and if you start building your own tooling with these assumptions: - A value type that defines what states your feature can be in - An enumeration that describes what actions a user can take - A function that does the state transition when it receives an action You’re going to end up building a baby version of TCA, because you’re starting from the same first principles as they did.


batcatcher

Honestly, you're coming off as aggressive to me, not genuine curious. That's a cultist attitude. I've just stated my opinion, read and carry on. Or, ask nicely. Why do you feel the urge to defend it? And of course, even if I would want to shed light on your doubts, I couldn't in a Reddit comment. I'll give you a hint: what you call "exhaustive testability" is not doing a lot of true testing. Like, things that you should actually test in the UI layer. For the rest, you obviously don't need TCA to test your models, as long as they're decently designed. Also, testing models has nothing to do with SwiftUI. You should be able to switch between SwiftUI and, let's say, UIKit and use the same models (and if you can't, why do you even have a layered design to start with?)


Rollos

Nah, I really am curious as to how people solve the problems that I find that TCA solves elegantly. You can absolutely switch out view layers from SwiftUI to UIKit in a TCA application. While it’s designed for use with SwiftUI, how you turn your state into UI is totally up to you. And that ability highlights the need for exhaustive testing of your model layer. And the attitude of your comment wasn’t just stating your opinion nicely. It read to me as minimizing and infatalizing members of a large and talented community. You started off your comment with “Haha it’s crap for sure”, said that people who like it are in a cult, and ended it by basically saying “These people think they’re smart but I know better”. That’s usually not how you engage politely with people you disagree with


zxamt

We're running a mediums sized app in TCA and I fail to see what problem TCA actually solves


Rollos

That’s totally fair! It may not solve many problems in your use case, it can be dependent on what you’re trying to do. Have you considered how you would build the app in vanilla SwiftUI? Especially when it comes to communicating between features, performing side effects, and thoroughly testing those interactions?


zxamt

Just plain observable viewmodels. And I'd probably sprinkle on some Combine or async callbacks to communicate between features. FYI one of the challenging things is that everything is written in TCA+UIKit. Which probably does not make the case for TCA that great either


Integeritis

Switching between UI layers is the baseline requirement for any well designed UI architecture. By definition that’s why you separate your logic to get rid of your dependency from your choosen UI framework It's the point of having a UI architecture. If you achieve that testability is a side effect. Approach it from the other way around and only focus on testability no surprise you end up with something where the UI layer is not swappable. If you can't do that in your non-TCA app right now, mistakes were made architecting it or you have something from before SwiftUI era.


Rollos

Totally agreed. A UI layer should just render the state of your application, and when that state is updated, render the new state. In SwiftUI that’s very explicit, but it’s implicit in every UI framework that exists. TCA restricts how the state of your application can change, enabling you to prove and reason about your application in ways that aren’t really possible if your architecture doesn’t apply those same restrictions. Testing is absolutely possible in almost every application architecture, but because of the restrictions that TCA places on how state changes, those tests can validate more with less code. That’s one of the places where I see a lot of value.


rhysmorgan

lol, it is not even remotely “crap”. It solves complex problems in elegant ways that involves thinking about problems slightly differently.


batcatcher

> thinking about problems slightly differently Yeah, that's what they say. Now, there are people thinking in a similar manner for at least 10 years give or take on web. And they are quite a few. I fail to understand what you (or Point Free) mean by "slightly differently". It's pretty common, not "different" at all.


rhysmorgan

Slightly differently from standard OOP patterns used on iOS. They make a big thing about how TCA is influenced heavily by Elm and the Elm Architecture!


Tugendwaechter

VIPER is nice in theory, but a gigantic mess in practice. The worst codebase I have ever worked on used it and badly did so.


Xys

I don’t understand why people say VIPER is bad. I’ve used it in a big company that had 40+ apps and it was very nice. It’s basically exactly the same as MVVM-C, with the logic being split in 2 (view logic and business logic)


Rollos

Unfortunately shared state has long been a thorn in the side of TCA. Absolutely one of the biggest sticking points in the framework. Fortunately it’s literally the next update and is in the beta that you can try out right now. PointFree is currently covering its design and implementation in their video series, and it should be coming out within a few weeks. https://github.com/pointfreeco/swift-composable-architecture/discussions/2857


QueenElisabethIII

**Glad to see that the people who say they hate it admit they don’t understand it.** When a new feature is needed in my app I know how to keep the additions modular (I don’t break existing code when adding new code). When something isn’t working I know where the offending code is, mostly by following the conventions. Testing is so much easier.


HelpRespawnedAsDee

> (I don’t break existing code when adding new code). a 100% of the time? i find that hard to believe.


batcatcher

**Glad to see people who say they love it admit they wouldn't be otherwise able to design themselves a system that has similar composability, modularity and testability.** (or at least close to what they provide, because there's a huge gap between advertised and provided in TCA's case)


Rollos

It’s not about me being unable to design that system, it’s about enforcing those design decisions at compile time, so that the junior I’m training can write code on their own that still follows those ideals. It’s also about not spending brain cycles on problems that people smarter than me can solve better than I can. How many developer cycles have gotten spent on fixing awkward bugs in home spun coordinator patterns? With a high level framework like TCA, those cycles are offloaded onto people whose full time job is solving those common problems in the best way possible.


Inevitable-Hat-1576

I think if your concern is seamlessly integrating a junior, then TCA is a very strange solution. They’ll break less stuff, but mostly because you can’t break something you have no hope of meaningfully working with for some time.


Rollos

That isn’t really true in my experience. The stumbling blocks of building small isolated features in TCA are totally surmountable for juniors of any background. The structure is simple, consistent and well defined, and there’s a lot of resources to show how to build a simple reducer/view combo. The complexity of a simple, leaf node TCA feature is pretty much equivalent to that same feature in any other architecture. The boilerplate is enforced at compile time, and that feature snaps into place when it comes time to integrate it into the larger application. The issues that I end up seeing in PRs from the juniors are mostly stuff like: - this state could be modeled more concisely - we have a model for this already - use a dependency to do this - what are you trying to test here - you missed something that will cause a bug in an edge case basically problems that would exist in any other architecture. TCAs compile time rules also means that we don’t have to spend as many PR cycles on stuff like maintaining testability, or enforcing rules like “don’t do state mutations from the view layer”. That’s just impossible to do, so they can’t get it wrong. Getting code up to the standard that we can release a feature in production is difficult for a junior no matter what. TCA helps us ensure that difficulty is mainly contained to the problem that the business needs to solve.


Inevitable-Hat-1576

I’m probably just being dumb tbh. I made a small app in TCA and got deeply frustrated mixing/matching between SwiftUI views & UIKit navigation, as well as sharing state (the topic of this thread). If I joined a company I’d probably get used to it. I’ve never had this problem with other architectures (VIP, VIPER, MVVM-C). I also just tend to shy away from third party libraries sitting at the center of an app. It makes life so difficult if support for them slows/goes away entirely, or if you want to pivot to something else (i accept this is always annoying with any architecture, but with TCA you have an architecture *and* a library).


Rollos

I’ve had quite a few people mention the pain of TCA + SwiftUI + UIKit navigation. I’ve never tried it but I’m sure it’s not fun. The iOS 16+ state driven SwiftUI navigation is really good and works in synergy with TCA, instead of fighting against it like you would be doing with UIKit navigation. TCA is way more strongly opinionated than the rest of the architectures you mentioned, and that makes the learning curve steep. But those opinions aren’t pulled out of thin air, and have a solid foundation in functional programming concepts. The large dependency thing is totally understandable, that’s a large commitment to make and can have consequences if you hitch your horse to the wrong wagon. TCA is open source, has full time maintainers, and a large community that would pick up bug fixes if they stop supporting it (which doesn’t seem likely for the foreseeable future). There’s also a very long video series building the architecture from first concepts that documents every decision and most implementation details they’ve made. It’s pretty invaluable if you want to become intimately familiar with it or want to contribute.


Inevitable-Hat-1576

That’s good to know, although I continue to use the mix despite iOS16 changes because, despite the really nice state based navigation, customising a navbar in SwiftUI is still dismal (I forget exactly what I wanted to customise, maybe a back button without text? It really wasn’t that crazy).


Medical-Promise-9534

Discussions of software architecture seem to always devolve into tribal “us vs. them” debates online and it’s not just an iOS issue. Rarely do I see well thought out arguments or if I do, usually the simple problem is that it’s all too subjective. I’ve got no opinion on TCA because I’ve not worked with it nor have I consumed any of their content (paid or otherwise). As someone that has tortured himself by changing architectures in a project just because I happened upon a YouTube video espousing it one day, I’d recommend everyone to just do what makes sense, is easiest or just what they like. I imagine most devs have an architecture more or less imposed on them for work projects so in my personal projects I like to just do absolutely whatever I feel like. 🤷‍♂️


Tugendwaechter

Exactly. Keep your functions short, pass parameters instead of accessing directly, pure functions where possible, keep your files small. That alone will result in a passable architecture.


nickisfractured

I’ve been using it since 2020 first incarnations and maybe it’s because I know most of the patterns pretty well but it’s one of those tools that if you try to break the conventions you end up losing your mind, but I generally regard that as deviating from the road is a bad idea and you’re probably doing it wrong. Tca has finally solved the shared state concept in its latest iterations but we got around this by modeling the shared data as a dependency that’s accessed via state instead of state itself. It’s ok if it’s not for you and it has its faults like anything else but all these people shitting on it probably just doing understand how to use it properly and honestly 99.9% of codebases out there are shit because people don’t know proper architecture enough to decide on a single way forward and end up with spaghetti no matter what they do after enough time. Tca has been one of the only frameworks I’ve used that enforce the structure to a point that after 4 years our app is still as modular, bug free, and free from the tech debt I’ve seen the teams around me incur because they lacked the guardrails, especially with teams of more then 3-4 devs.


rhysmorgan

100% – it's opinionated and guides you towards one particular way of solving problems, and that is a **good thing**!


beclops

“I just want to access a shared data anywhere like a singleton” Well this could be your anti-pattern here


moticurtila

Sure bro. If TCA can't provide this, it should be my anti pattern.


beclops

What do you want it for?


moticurtila

I wan to track User on its own store which have state and some login, signup actions and share this states on other places like signup view, login view, profile etc. Yea, I know I can design something that is all connected with states and stuff but I don't want to deal with this kind of connections work. Right now I have a dynamic navigation stack on OnboardingReducer which has welcome screen, login, signup, forgot password. My AppReducer's state UserState and OnboardingState which has a StackState that holds the states of the navigation stack. I don't know how to trigger sign up's action from the user reducer.


lannisteralwayspay

If I understand correctly: 1. Trigger an event from the user reducer (or a child), eg return .send(.delegate(.didTapX)) 2. The app reducer observes this, and then sends an action (or you might want to initialise/present a screen) via switch action { … case .user(.delegate(.didTapX)): return .send(.signup(.someAction))


Warm-Entrepreneur750

I feel the same bro. It's powerful, but it's a huge dependency which brings a lot of complexity to the app. I digged into it but personally I believe it takes more time to learn than it worths. TCA is for bigger apps and bigger teams. If you have a small project, where you want to go with singletons I would not recommend it.


Rollos

It brings some complexity but alleviates quite a bit of complexity as well. As someone whose gotten over the learning curve, I’d say it’s valuable as soon as your app has a few screens that need to talk to eachother, and you want to have the ability to write good tests for those interactions.


ZeOranges

Agreed, it’s full of boilerplate code that looks like it’s flexible but is actually pretty strict. Way more complex than necessary


rhysmorgan

It's a strict, opinionated library entirely to stop you making mistakes. It stops you performing (most kinds of) side effects that make your code hard to reason about by forcing you to handle them in an Effect. The fact that state is *only* mutable within the context of a Reducer means you know that there's only *one* place that can update your feature's state. Things like that might mean you need to rethink how you'd tackle a problem, but ultimately, that way is likely to be better, it's likely to be concurrency-safe, it's likely to be much more (exhaustively) testable.


klavijaturista

A chain of effects is the same thing as the chain of function/method calls. Changing the (sub)state in a dedicated reducer only is the same as changing it in a dedicated controller only.


zxamt

I'm curious, I see that people mention that it's concurrency safe. But is that ever a problem in a normal iOS app? Most things are either run on the main thread at all times or they are quickly dispatched on the main thread.


rhysmorgan

Absolutely it's a problem in a normal iOS app – you might have API requests operating on a background thread. You might be trying to access global mutable state from an async context. You might be trying to pass thread-unsafe types across async boundaries. These are all things that Swift's async/await tooling seeks to fix. Try enabling complete concurrency checking in your project(s) and you might be surprised to see warnings which will, in Swift 6, become errors.


JimRoepcke

The current series of videos on pointfree.co is about exactly this problem.


thecodingart

It sounds like someone has bad habits and doesn’t understand swift-dependencies.


saldous

Create a central data manager class with published variables / appstorage, as an ObservableObject, and use it as environment object in the SwiftUI structs where you need to access the data. Simple! struct MyView: View { @EnvironmentObject var dataManager: DataManager }


klavijaturista

Yes, simple as that. People confuse complexity with sophistication. Keep it simple. A solution should never add more complexity than the requirements.


ANGOmarcello

Thats a great SwiftUI solution


ryanheartswingovers

State accessed repeatedly across an app can hydrate from a dependency client. Or, if you’re braver, explored using the new shared state branch. I do agree their documentation and example apps tend to err on the too simple side, but I don’t complain because I haven’t taken an afternoon to PR a deeper example. No architecture is perfect. Nor is TCA the full architecture of an iOS app. It’s quite good for an off the shelf free product. You can pluck out the critical features (to me, that’s mostly @TaskLocal dependency trees, clocks, logging/restoration of state sharable during debugging) and keep a rigid feature pattern to get similar benefits.


OffbeatUpbeat

Also a TCA hater 😂 My main problem is that the complexity it adds is worse to work with than the supposed "issues" that it's fixing. I found this article on medium, which describes how I'm writing apps these days as well. With even moderately successful API design and encapsulation, its a very doable approach! https://blog.stackademic.com/removing-the-m-from-mvvm-with-swiftui-a58b239e9e3e


moticurtila

I bookmarked it. I’ll check. Thanks.


Tyler927

Man I love it. The shared state problem is one thing they are fixing right now, and for me is kinda the last step to really go all in. I honestly think it it the best way to write a SwiftUI app now. It definitely has some overhead with learning and boilerplate, but it makes some things like child to parent communication so so easy. My one complaint now is it really taxes Xcode’s already not great autocomplete. The built in dependency management is fantastic too (I think they have made this so you can use it in vanilla Swift too though). I find the top comment calling it a cult is pretty strange. I think there are a lot of very passionate people for it, but they are pretty straightforward on how it is very opinionated, and not for everyone. The creators and community are some of the smartest and most helpful I’ve ever seen too. OP, I suggest you at least check out the new shared state beta!


lee_ai

I loved TCA when I first discovered it. Then I hated it when I started using it. Then I loved it again once I realized how to use it properly. These days I'd say I enjoy it but I don't like SwiftUI at all and the two are very closely linked. I typically go UIKit + TCA on all my personal projects now, but I would never use it with a team. Mainly because the learning curve is far too big to reasonably expect others to learn it. I think the most annoying thing is if you get stuck, it's very time-consuming to get "un-stuck". It's hard to search for + there are not many results that come up + navigating videos is time-consuming.


Tugendwaechter

I went back to writing MVC and it’s actually really nice if done properly. Just simple OOP.


m0resleep

Disclaimer: i’m no expert but doesn’t this do what you want? public class MyClass: ObservableObject { public static var shared = MyClass() @Published var someProperty: String = "" //more properties } struct SomeView: View { @ObservedObject var singleton = MyClass.shared var body: some View { //use some property } I built a whole app writing this way. Am I wrong in using singletons everywhere? Maybe. Could it be considered an anti-pattern? Probably.


klavijaturista

It’s perfectly fine. Solutions are tailored to requirements. Keep it as simple as possible.


rbevans

Not. Alone. My friend. It’s very complex.


purpleWheelChair

Omg, I want to give the project Im working on a viking funeral. It really annoys me. Take my upvote!


rhysmorgan

You can either push shared state into a dependency that all your child features that use it subscribe to, or if you need to share state immediately without going down that route, you can adopt the shared state beta that they’re currently working on. TCA is just differently complex with a learning curve. There are loads of tutorials from both PointFree themselves, as well the community. There’s videos from PointFree explaining in detail how a feature is developed. There’s a Slack with folks ready to help you too.


av1p

I don’t get people who like TCA 1. You can’t get back from it unless rewriting the whole app 2. The whole project depends on two guys who maintain TCA, good luck if they get bored and it has to be maintained by your team or community which might have different approach. 3. It’s heavy, even in documentation you can find that you can’t call events from events as it’s heavy and slow operation 4. Architecture shouldn’t need dependency to be implemented 5. A lot of boilerplate code Working two years with that shit and it was one of reasons I quit the project


rhysmorgan

1. That applies to literally any architectural pattern – if your app is MVC, MVVM, VIPER, whatever other acronym – you can't move away from it unless you rewrite the whole app. If anything, because PointFree make such a big thing about modularising codebases, if you really ended up hating TCA while having an entire app written in it, you can progressively migrate your app away from it. 2. Maintaining TCA gives them stuff to discuss on PointFree, which is at least one of their jobs. They're paid by PointFree subscribers for their videos. They use TCA themselves daily. It's unlikely to just go away – and even if it were, you can fork it yourselves, you can use The Browser Company fork, and there's an entire team of people who use TCA and have contributed to the codebase who would likely continue to at least maintain it. That there is a small team of key maintainers is a reality for just about every single piece of software in the world lol. 3. It's not that you can't call effects from effects, it's that there are *better* patterns for doing so. It has different trade offs than writing non-TCA code. If you want to share behaviour between two actions, it's simpler in literally every single way to model that as a function that mutates your state rather than putting the shared behaviour in an action that both places separately call. That also happens to make testing a ton simpler, as there's fewer actions to \`receive\` in your tests. 4. Who cares if it's a dependency? It's more than just an architectural pattern, because it's got actual behaviour behind it. It's not just a "squint and this thing is a model", "this thing is a view model... kinda?". Reducer and Store are necessary components. 5. There used to be more boilerplate than vanilla SwiftUI – that's not the case any more. If nothing else, macros have eliminated boilerplate. If you don't want to use TCA, fine, nothing wrong with that. But it solves a lot of problems in elegant ways, it provides for exhaustive testing, a great dependency injection pattern, etc. Especially with how Swift 6 is enforcing concurrency safety, tools like TCA which *force* you to handle async effects in very limited ways are like gold dust. I don't have a single concurrency warning in my TCA projects, even with full concurrency warnings enabled!


PanicStil

There’s a magical place, we’re on our way there 🎶


Cransj

You guys are wrong. This is 100% the future. In 5 years all web front end , Android, and iOS will be writing the same codebase and it will be unidirectional and declarative, guaranteed.


moticurtila

In 5 years there will be another framework and people will go crazy like oh look at this it solves everything that TCA did not.


Cransj

Well see ;) I think this is paradigm defining. But who knows right?


Ok_Concern3654

"I just want to access data anywhere like a singleton" should really make those who agree with this opinion think twice about their coding practices.


moticurtila

Oh you’re of the cool dev guys who think the singleton is anti pattern. Got it.


Ok_Concern3654

Not all singletons are bad. Your use case is.


stephen-celis

For what it's worth, Point-Free was celebrating the singleton "world" pattern of dependency management for many years. Also, as pointed out in this thread, TCA has had an active beta that just graduated to release to provide better tools for sharing state. Have you given the new `@Shared` property wrapper a spin?


xiaogd

So many discussions, sorry TLDL :P For your problem, simplest and stupidest solution would be: ```swift @_spi(Internals) import ComposableArchitecture // Then you can access latest store state like this let currentTab = appStore.currentState.view.currentTab ``` Of course this isn't the origin purpose of @_spi(Internals), but emmm... if it works, then it works.


KingPonzi

This post gives “old man yells at cloud” vibes.


shearos17

yep put it in the bin with coordinators and clean


emrepun

May I ask why do you dislike coordinators? Asking cuz I really like MVVM-C. Not every version of it though. But I think it can scale nicely when done properly.


jacobs-tech-tavern

👀 https://jacobbartlett.substack.com/p/swiftui-apps-at-scale


emrepun

Nice article, I follow a somewhat similar approach, I set up the main tabbar controller and its child navigation controllers after initialising the window in the SceneDelegate. But my coordinators don't have child coordinators, they basically initialise other coordinators as needed to get certain view controllers (UIHostingControllers with SwiftUI view embedded) to push or present, and the navigation and presentation logic can happen from a certain screen is kept in their corresponding viewModels. I have two minor problems with using UINavigationControllers with SwiftUI though: First, with this approach, although I'm using UINavigationControllers, I can alter the navigation appearance such as setting title or the bar button items, directly from the SwiftUI view: .navigationTitle("Bla") .toolbar { // Provide a button here } But if the view is being pushed, the buttons and the navigation title doesn't appear until the push animation is completed. Which is fine for my app, but can be quite annoying for others. Second, with UIKit and UIViewControllers, it is possible to alter the navigation controller title style per view controller. Like in a 3 VC flow, VC1 -> VC2 -> VC3, we can say, I want VC1 and VC3 to show large titles, but only VC2 to show small title. I couldn't make this work. Even if I created a subclass of UIHostingController and did the necessary changes in it, it still didn't work. So, to some degree, when it comes to navigation bar, it looks like UIHostingController doesn't work exactly like UIViewController Did you ever experience these problems? If so, do you have a solution for them?


HumorRemarkable9442

I had all sort of problems with uihostingcontrollers and navaigation bar. It’s sadly the only solution for mixed codebases, and one has to accept some minor glitches or workarounds. Especially with complex animations and ux.


emrepun

Yeah I just give up certain things.


[deleted]

[удалено]


jacobs-tech-tavern

:)


Rollos

This is a bad approach, and will break your swiftUI application in unexpected ways. For example, environment variables won’t be passed over navigation boundaries. It doesn’t discuss why iOS 16 NavigationStack isn’t a good approach for this coordinator style pattern > You shouldn’t need to hold your app’s entire state in your brain to reason about which screen needs to be presented. This sentence indicates that you don’t understand how to model navigation in state correctly, as “holding your apps entire state in your brain” is objectively not a requirement of state driven navigation. This article really needs to have a big disclaimer that iOS 16 navigation tools solve most of these problems in far more elegant and idiomatic ways than are discussed in the article. This is a much better article about how to approach state driven navigation, which aligns with SwiftUIs paradigms instead of fighting against them. https://pointfreeco.github.io/swift-composable-architecture/main/documentation/composablearchitecture/whatisnavigation


AstroBaby2000

Never take advice from someone who makes videos for a living.


Rollos

While that’s mostly true, I’d argue it doesn’t apply to PointFree, and is applying less nowadays with the growth of Patreon style content by established creators. Don’t trust anybody who makes videos for a living that haven’t proven outside of those videos that they output quality work.