After many months of development and testing, experimenting with multiple projects, and initial beta testings by some of you, I have just released the Clean Swift templates to work for the latest Xcode 8.3.3 and 9.0. You can use it for Swift 3 and 4.
This update also has the following, long-awaited features:
- Improved routing whether you use segues or not
- Improved data passing using the all new data store protocol
- Separated the routing process into two phases: navigation and data passing, with a clean interface
- Removed the need for configurator in favor of cleaner setup
- Combined input and output protocols to remove duplication
- Renamed protocols with better names
- Swiftier models with nested enums and structs
- Use optionals to prevent crash in the VIP cycle when the scene is no longer in memory
- Works whether you use storyboards to build your UI or not
- View controller class names are now recognized when specifying class names in storyboards
I’ll be writing more about the new improvements in the coming weeks. In the meantime, you can check out the updated CleanStore sample app on GitHub to see how to use these latest templates.
A big thanks to those who tested early and provided me a long list of feedback and suggestions. I’ll read everything and see what I can push to the next update.
You can buy The Clean Swift Handbook with a free one-month access to my Slack team to get started with the latest templates, Xcode, and Swift.
Download the latest Clean Swift templates by subscribing below.
In the Swift 3 version of your templates, there’s some errors when you create unit tests. You forgot to change NSBundle to Bundle and NSRunLoop, RunLoop and NSDate to Date in the ViewControllerTests.
need to be changed to:
Good call, Jeremy. I’ve updated the unit test templates.
Regarding your template update, when I test with Bundle.main, the test crash because of a cast error:
Could not cast value of type ‘TestCleanSwift.ListOrdersViewController’ (0x107b4bf98) to ‘TestCleanSwiftTests.ListOrdersViewController’ (0x114cd25b8)
It’s working fine with Bundle(for: type(of: self))
This is working for me:
func setupListOrdersViewController() {
let bundle = Bundle.main
let storyboard = UIStoryboard(name: “Main”, bundle: bundle)
sut = storyboard.instantiateViewController(withIdentifier: “ListOrdersViewController”) as! ListOrdersViewController
}
func loadView() {
window.addSubview(sut.view)
RunLoop.current.run(until: Date())
}
Hey Raymond,
What if I need to share some functionality amongst several scenes? How would you handle that case? E.g. I have a footer, which displays some value. That footer takes place on 4 screens. I wouldn’t want to copy paste the code to all the scenes where I need that functionality.
Hi Andrey,
What about creating a reusable UIView subclass to contain all your display logic in one place? And then supply it with the view models it needs to display your values? Finally, bubble up any user actions up to the view controllers and interactors to handle the business logic?
This way, different scenes can respond differently to user button taps and whatnot. But your display logic is DRY.
Who will responsible for supplying the view with view models in this case? If I’ll need to create several similar VIP cycles, then DRY principle won’t be met.
Hi Andrey,
Your view controllers will supply the views with the view models they receive from the presenters. The VIP cycle is unique to each scene. Don’t mix them. You can have many use cases passing through the VIP cycle. Don’t over-DRY things. If you have complex business logic in an interactor, you can extract it to a worker which can be shared between scenes.
Still, this doesn’t look optimal. I have to define exact the same models in SceneModels and write exact the same method calls for all the scenes where I want to place the shared view. I’ll have to write exact same unit tests for that functionality for all the scenes. If I want to change the behaviour, I have to edit all models/interactors/presenters/unit tests and view controllers. There must be a better way, right?
The only option coming to my mind is to have embedded controller.
Why does the router inherit from NSObject?
If you remove it, it is also working (doing everything in code, not storyboard)
Hi Marco,
The router inherits from
NSObject
so that the view controller’sprepare(for:sender:)
method can invoke therouteToNextScene(segue:)
methods by Objective-C selectors. This way, if you name your storyboard segue with the next scene’s name, it’s done automatically for you. You don’t have to manually dispatch the routing in the extrapassDataToNextScene()
method. I’ll be writing more about the routing process soon.Could you please send me your source code you pushed in github but developed in XCode8? I don’t have XCode9, thanks a lot
Hi Leo,
The CleanStore sample app is compatible in both Xcode 8 and 9. You may just need to tweak the Swift Language Version build setting.
Hi Raymond,
I’m having trouble figuring out how to handle an alertController with multiple dynamic options.
Currently, I’m creating the alertController in the presenter and passing only the object to the viewController, which is where I call viewModel.alert.addAction().
How to handle the UIAlertAction handlers?? How should I create this dynamic alertActions, and where to handle the logic for it? In the viewController or in the presenter?
Hi Mauricio,
When you have multiple dynamic options for the alert, that means the title, message, and buttons will change depending on different success/failure/error conditions, right?
If that’s the case, creating the
UIAlertController
in the presenter makes the most sense.When the user interacts with the alert, it should be the view controller’s job to handle it and invoke the interactor for any possible use case that should be triggered as a result of the user tapping a button on the alert. From then on, you can go through the VIP cycle again.
Hi Raymond,
How do I download the new version of the templates if I have already subscribed to the blog? Thanks!
Hi Justin,
The link in the original email still works. Login to Gumroad and you should see it.
Hope to see macOS platform support in your Templates. Thanks!
Is there a direct link where I can download the Xcode templates? Every time I need to download it (like using a different computer), if I can’t find the files I previously downloaded, I have trouble finding a link to simply download the files. I’m always taken to that form where I put my name and email to subscribe to your mailing list (which I’ve already done), then I get routed to an update-profile screen and never receive the downloads.
The link says “Download Xcode Templates”, but if I’ve already signed up, I can’t download the Xcode templates.
I was able to find the email with the link, so I did find the templates, but I would be more confident that it’s the most current version of the templates if there was a simple and direct way of downloading the most current version. If I may make a suggestion; the interface you have now is fine for collecting emails for your mailing list, but for those who have already signed up, maybe send a quick email with a (seemingly) fresher download link. Thanks!!
I love your work by the way. Very very helpful stuff!!
Hi Brandon,
Looks like you found it! The download link in the original confirmation email always points to the latest version of the templates.
How to integrate CleanSwift template in my xcode
@Sandip
https://useyourloaf.com/blog/creating-custom-xcode-project-templates/
Hi Raymond,
I just want to ask if we can customize the CleanSwift template? I tried doing it, but when I created a new file it still defaults to the old format.
Can’t download the template.
Hi candyman,
Subscribe using the form above, and the download link will be in the confirmation email.
Hi, Raymond.
I’m new to the architecture and I’m currently using Swift 5.2 with the latest versions of XCode, are these templates compatible?
Thank you very much in advance.