14

Recently I’ve been rethinking my android architecture project, trying to adapt it to a more “clean architecture”, specifically, the kind of design suggested by “Uncle Bob”.

Which it involves several layers of abstractions, a nice isolation of responsibilities and a very strong dependency inversion achieved by dependency injection; which, ultimately, leads to a very decoupled-portable system. A perfect candidate to be tested by unit testing and integration testing.

In my android implementation I’ve ended up having three different modules or layers:

-domain: entities, interactors, presenters (pure java module)

-data: (acts as a repository to supply the data to the domain) (android library module)

-presentation: ui related stuff, fragments, activities, views, etc (android application module)

So, I’m trying to figure out what would be the best approach on the iOS ecosystem. I’ve tried creating a project with multiple targets to achieve the same solution:

-domain: command line target (which seems very weird but I think is the most pure swift target available)

-data: cocoa touch framework

-presentation: cocoa touch framework

With this approach I can use these targets in the way I did with android modules. But the first caveat I’ve found it is that I need to add manually every new file to the dependent target.

But my knowledge is very limited in projects with multiple targets. I mean I’ve never created an iOS application with multiple targets. So I don’t know even if the solution would be use a framework (cocoa touch/cocoa) as a target instead of a command line module for the domain layer.

Any thought would be really appreciate.

Thanks!

Jesse
  • 3,243
  • 1
  • 22
  • 29
Víctor Albertos
  • 8,093
  • 5
  • 43
  • 71
  • Have a look at MVVM and VIPER patterns and approaches (google). Your Uncle Bob approach I don't think will work for Obj-c. [MVVM link](https://medium.com/@ramshandilya/lets-discuss-mvvm-for-ios-a7960c2f04c7 "MVVM") [VIPER link](http://www.objc.io/issues/13-architecture/viper/) Good luck – latenitecoder Aug 25 '15 at 13:31
  • Ey man thanks for the info, but you haven't answered my question at all. I've asked about how to isolate pure swift code using certain kind of target. And you could elaborate a little bit why you think it isn't possible implement that kind of architecture, because I'm pretty sure it is, regardless the program language (objective-c, swift...) – Víctor Albertos Aug 25 '15 at 13:46
  • I didn't actually see any questions in there just this - "So, I’m trying to figure out what would be the best approach on the iOS ecosystem" I don't know (or like) swift so I couldn't help you with that. Maybe tag your question swift and you might get some swift developers. Did you even look at Viper, because if you bothered to look, you would see the pattern is closely matched to your Uncle Bob's - This is why I like helping people, they flame you for not answering a question they didn't ask. – latenitecoder Aug 25 '15 at 13:58
  • When I read "Your Uncle Bob approach I don't think will work for Obj-c." I stopped reading. Why did you say so when at the same time you were providing a resource where clearly this approach had been implemented. Anyway, thanks for your help, really. But the guys who implemented the viper example... they didn't care much about creating different targets for every layer of abstraction. – Víctor Albertos Aug 25 '15 at 14:32

2 Answers2

25

Uncle Bob's Clean Architecture absolutely applies to iOS, Swift, and Obj-C. Architecture is language agnostic. Uncle Bob himself codes mostly in Java but in his talks he rarely mentions Java. All his slides do not even show any code. It is an architecture meant to be applied to any project.

Why am I so sure? Because I've studied MVC, MVVM, ReactiveCocoa, and Clean Architecture for 2 years. I like Clean Architecture the best, by far. I tested it by converting 7 Apple sample projects to using the Clean Architecture. I've used this approach exclusively for over a year. It works out better every time.

Some of the benefits are:

  • Find and fix bugs faster and easier.
  • Extract business logic from view controllers into interactors.
  • Extract presentation logic from view controllers into presenters.
  • Change existing behaviors with confidence with fast and maintainable unit tests.
  • Write shorter methods with single responsibility.
  • Decouple class dependencies with clear established boundaries.

We also added a router component so we can use multiple storyboards. No more conflicts.

Writing unit tests is greatly simplified too because I only need to test the methods at the boundaries. I don't need to test private methods. On top of that, I didn't even need any mocking framework because writing your own mocks and stubs becomes trivial.

I've written my experience for my last 2 years studying iOS architecture at Clean Swift I also put together some Xcode templates to generate all the Clean Architecture components to save a ton of time.

UPDATE - To answer @Víctor Albertos's question about dependency injection in the comment below.

This is a really great question and demands a long detailed answer.

Always keep the VIP cycle in mind. In this case, the doSomethingOnLoad() method is not a boundary method. Rather, it is an internal method invoked only within CreateOrderViewController. In unit testing, we test a unit's expected behavior. We give inputs, observe outputs, then compare the outputs with our expectations.

Yes, I could have made doSomethingOnLoad() a private method. But I chose not to. One of the goals of Swift is to make it easy for developers to write code. All the boundary methods are already listed in the input and output protocols. There is really no need to litter the class with extraneous private modifiers.

Now, we do need to test this behavior of "The CreateOrderViewController should do something on load with this request data" somehow, right? How do we test this if we can't invoke doSomethingOnLoad() because it is a private method? You call viewDidLoad(). The viewDidLoad() method is a boundary method. Which boundary? The boundary between the user and view controller! The user did something to the device to make it load another screen. So how do we invoke viewDidLoad() then? You do it like this:

let bundle = NSBundle(forClass: self.dynamicType)
let storyboard = UIStoryboard(name: "Main", bundle: bundle)
let createOrderViewController = storyboard.instantiateViewControllerWithIdentifier("CreateOrderViewController") as! CreateOrderViewController
let view = createOrderViewController.view

Simply calling the createOrderViewController.view property will cause viewDidLoad() to be invoked. I learned this trick a long time ago from someone. But Natasha The Robot also recently mentioned it too.

When we decide what to test, it is very important to only test the boundary methods. If we test every method of a class, the tests become extremely fragile. Every change we make to the code will break many, many tests. A lot of people give up because of this.

Or, think about it this way. When you ask how to mock CreateOrderRequest, first ask if doSomethingOnLoad() is a boundary method that you should write test for. If not, what is? The boundary method is actually viewDidLoad() in this case. The input is "when this view loads." The output is "call this method with this request object."

This is another benefit of using Clean Swift. All your boundary methods are listed at the top of the file under explicitly named protocols CreateOrderViewControllerInput and CreateOrderViewControllerOutput. You don't need to look elsewhere!

Think about what happens if you were to test doSomethingOnLoad(). You mock the request object, then assert that it equals to your expected request object. You are mocking something and comparing it. It's like assert(1, 1) instead of var a=1; assert(a, 1). What's the point? Too many tests. Too fragile.

Now, there is a time when you do mock CreateOrderRequest. After you've verified the correct CreateOrderRequest can be generated by the view controller component. When you test CreateOrderInteractor's doSomething() boundary method, you then mock CreateOrderRequest using interface dependency injection.

In short, unit testing is not about testing every unit of a class. It is about testing the class as a unit.

It is a mindset shift.

Hope that helps!

I have 3 series of draft posts in Wordpress on different topics:

  1. In-depth look at each of the Clean Swift components
  2. How to break up complex business logic into workers and service objects.
  3. Writing tests in Clean Swift iOS architecture

Which one of these do you want to hear more first? Should I bump up the series on testing?

Raymond Law
  • 1,188
  • 1
  • 10
  • 14
  • I’m going to read your post because it seems very interesting. But I don’t know yet what kind of target could be the domain layer. Because on the Android ecosystem I create a pure module java -without any kind dependency to Android, so it could be ported to a desktop application for example, but with Xcode I only can to choose between cocoa (OSX) or cocoa touch(iOS). Is that correct? I can’t create a universal framework which will compile on both OSX and iOS system. – Víctor Albertos Sep 03 '15 at 22:55
  • Is there any reason why you don't use dependency injection? All your dependencies are instantiated instead of passed/injected via constructor/method to the specific class. Doesn't it turn unit testing a little hard? – Víctor Albertos Sep 03 '15 at 23:26
  • You can totally build an universal framework for both iOS and OS X. I did just that for DevBase. I've written a [post](http://rayvinly.com/how-to-build-a-truly-universal-framework-for-ios-and-mac-with-just-a-single-codebase/) to show how to do that if you are interested. But it's for an older version of Xcode. The latest Xcode and Swift already supports framework. – Raymond Law Sep 05 '15 at 01:02
  • Can you tell me which specific class and method you are asking about dependency injection? – Raymond Law Sep 05 '15 at 01:03
  • CreateOrderViewController. Line 40. You are instantiating a CreateOrderRequest. You can’t mokc it. So, I don’t know how do you do for unit testing this class. – Víctor Albertos Sep 05 '15 at 08:34
  • @VíctorAlbertos StackOverflow thinks my answer is too long too fit in the comments, so I updated my original answer to answer your question about dependency injection. – Raymond Law Sep 05 '15 at 13:35
0

In my opinion Clean Architecture is a set of ideas, rules, principles... to make a code better.

[Android Clean Architecture]

With this approach I can use these targets in the way I did with android modules.

You are able create a target[About](application target or framework target...) but it depends on your needs

If you read Architecting Android...Reloaded from Fernando Cejas

you might have seen that I used android modules for representing each layer involved in the architecture.

A recurring question in discussions was: Why? The answer is simple… Wrong technical decision

The idea is that is not necessary to use some build components to implement Clean Architecture

yoAlex5
  • 29,217
  • 8
  • 193
  • 205