SwiftUI: An In-depth Examination for Real Projects


As an integral part of the iOS development community, I’ve experienced firsthand the transformative power of new technologies and paradigms. A substantial shift we are currently witnessing is brought by SwiftUI, Apple’s declarative UI framework. While SwiftUI promises a more efficient and integrated development experience, is it mature enough to handle real-world, complex projects? Let’s dive deeper into this discussion.

The Pros of SwiftUI: Going Beyond the Surface

SwiftUI vs. UIKit: Pros and Cons

The choice between SwiftUI and UIKit often comes down to specific project requirements. SwiftUI offers a fresh, unified, and efficient approach to UI development but may not yet be the right fit for all projects, particularly those needing extensive customization or backward compatibility. UIKit, on the other hand, is tried and tested and offers comprehensive features but lacks SwiftUI’s simplicity and modernity.

Declarative Syntax
Unlike imperative UIKit, SwiftUI’s declarative nature allows developers to focus more on “what” rather than “how.” We define the desired state of our UI, and SwiftUI renders the interface, significantly reducing the amount of boilerplate code.

Code Reusability
A key advantage of SwiftUI is its cross-platform compatibility. The same codebase can be used across iOS, macOS, watchOS, and tvOS, ensuring consistency, reducing redundancies, and accelerating the development process. This is not only a time-saver but a strategic enabler for maintaining and scaling projects.


Live Preview and Interactive Development
SwiftUI introduces a real-time preview of the UI, a feature that significantly enhances the development process. This interactive preview capability allows developers to see changes immediately, making it an efficient tool for rapid prototyping and agile development.

State and Data Flow Management
SwiftUI’s built-in state management tools such as `@State`, `@Binding`, `@ObservedObject`, and `@EnvironmentObject` simplify the process of managing data flow. With data and state being pivotal aspects in app development, SwiftUI provides a more streamlined approach, which UIKit was often criticized for lacking.

The Cons of SwiftUI: The Challenges

Backward Compatibility and Adoption
SwiftUI’s Achilles’ heel is its lack of backward compatibility. It supports iOS 13 and later versions, which may not suffice for projects aiming for a broad user base. As a result, UIKit remains the go-to option when compatibility with older iOS versions is a concern.


Framework Maturity and Coverage
As of 2023, SwiftUI is still catching up with UIKit in terms of UI components and APIs coverage. UIKit has been around for more than a decade, making it more mature and comprehensive. Developers working on complex projects may find SwiftUI’s offering limited compared to UIKit.

Learning Curve and Paradigm Shift
SwiftUI requires not just learning new syntax but also a shift in paradigm. Transitioning from an imperative approach to a declarative one can be challenging, especially for developers deeply rooted in UIKit.

Handling Dependencies in SwiftUI

SwiftUI, like any other Swift-based project, typically uses the Swift Package Manager (SPM) to handle dependencies. SPM is seamlessly integrated into Xcode, making it easier to manage third-party libraries or frameworks. This integration allows you to add, update, or remove dependencies directly within Xcode, simplifying the process.

Architectural Considerations

Choice of architecture is crucial in determining the structure and scalability of an application. Let’s evaluate SwiftUI in the context of popular architectural patterns.


Model-View-ViewModel (MVVM)
MVVM fits seamlessly with SwiftUI due to its declarative nature. The separation of business logic from UI increases code readability, testability, and maintainability. Binding and ObservableObject concepts lend themselves well to this architecture.


The Redux architecture shines with SwiftUI’s state-driven design. It centralizes app state, enforcing unidirectional data flow, and makes state changes predictable. However, it could potentially increase the complexity of small to mid-sized apps.

Composable Architecture

Composable Architecture
Composable Architecture, a relatively new kid on the block, provides a robust and scalable solution for building applications with SwiftUI. It emphasizes composition, modularity, and testability, complementing SwiftUI’s philosophy perfectly.


Concluding Thoughts

SwiftUI has irrefutably brought a revolution in iOS app development. But whether it’s ready to take over UIKit for complex, real-world applications depends largely on specific project requirements and the targeted user base.

For teams comfortable with SwiftUI and targeting devices running iOS 13 and later, SwiftUI is an excellent choice. For those needing more comprehensive UI capabilities or compatibility with older devices, UIKit remains a reliable option.

As for architecture, it largely depends on the project’s scale, complexity, and team expertise. While MVVM and Composable Architecture seem to be a natural fit with SwiftUI, Redux may still have their place in more complex applications.

As SwiftUI continues to mature, its adoption will undoubtedly increase, making it a powerful tool for iOS app development. But for now, the choice between SwiftUI and UIKit isn’t an either/or proposition — it’s about choosing the right tool for the job.

Embracing the Future with Swift Concurrency: A Comprehensive Overview

As seasoned iOS developers, we are familiar with the challenges of writing robust and efficient concurrent code. Before Swift 5.5, we leaned on tools like PromisesKit, RxSwift, or Grand Central Dispatch (GCD) to handle asynchronous programming, often leading to “callback hell” or convoluted code architectures. However, with the introduction of Swift concurrency, we’re taking a giant leap towards simpler, safer, and more efficient concurrent code.

Unraveling the Problem: The “Callback Hell”

Consider a simple, but common, scenario in app development: You need to fetch user data from a server, then fetch additional information based on that user data, and finally update the UI.

With PromisesKit or RxSwift, we might handle it like this:

The code does its job, but we are entangling our business logic with asynchronous handling code, making it hard to read, maintain, and debug.

Swift’s Solution: Async/Await

Swift 5.5 introduces async/await, significantly streamlining asynchronous code. Let’s see how we would handle the previous scenario with Swift’s async/await:

With async/await, the asynchronous code now appears almost identical to synchronous code. This straight-line coding style enhances readability, maintainability, and reduces cognitive overload.

Encapsulating State: The Actor Model

Concurrency bugs often arise due to shared mutable state across threads. In the past, we’ve used GCD or locks to control access to shared resources, but this approach can lead to issues like deadlocks and race conditions.

Swift 5.5 introduces actors, a new way to protect shared mutable state. An actor is a type that protects access to its own internal state. Let’s look at an example:

In this case, `value` is protected inside the `Counter` actor. All access to `value` is serialized, preventing race conditions.

Coordinating Tasks: Task Priorities and Cancellation

Imagine running a complex operation, like a large file download, but you want to interrupt it when the user moves away from the relevant screen. Previously, we might have struggled with complex cancellation code.

In Swift 5.5, tasks are first-class citizens. They can be prioritized, and more importantly, cancelled:

In the example, the `async` method returns a `Task.Handle` object, allowing us to cancel the task whenever needed.


With the introduction of Swift concurrency in Swift 5.5, we can bid farewell to callback hells, guard our shared state efficiently with actors, and have better control over tasks. The new concurrency model is a testament to Swift’s evolution towards safer, more efficient, and simpler coding paradigms. As Swift developers, let’s dive in, experiment, and adapt to this exciting new era of concurrent programming.

The Benefits of Hiring Contractors (Outside IR35) iOS Developers in the UK/Europe


In today’s rapidly changing business environment, hiring contractors, especially iOS developers, is becoming an increasingly popular option for companies in the UK and Europe. With the growth of the gig economy and the demand for specialized skills, hiring contractors outside of IR35 can offer numerous benefits over traditional full-time employment. In this article, we’ll look at the main benefits of hiring contractors (outside IR35) for UK/Europe iOS development projects.

1. Access to special skills and experience

One of the most significant benefits of hiring contractors (outside IR35) for iOS development is access to specialized skills and experience. Contractors often have extensive experience in their field and have a proven track record of delivering high quality projects. By leveraging their experience, companies can ensure that their iOS development projects are carried out by professionals who are well versed in the latest technologies, frameworks, and best practices.

Contractors also bring a fresh look to the table as they work with a variety of clients and projects. This familiarity allows them to keep abreast of the latest industry trends and implement innovative solutions in their work. By leveraging this specialized knowledge, companies can improve the quality and effectiveness of their iOS development initiatives.

2. Flexibility in resource allocation

Hiring contractors (outside IR35) gives businesses the flexibility to allocate resources according to project requirements. Since iOS development needs can change over time, hiring contractors allows companies to increase or decrease the number of employees as needed. Whether it’s a short-term project or a specific task requiring specialized expertise, contractors offer a flexible solution without the long-term commitment of in-house employees.

This flexibility also extends to working mechanisms. Contractors may work remotely or on site, depending on the nature of the project and requirements. This allows companies to attract talent from different locations, expanding the pool of potential candidates and providing access to top-notch iOS developers that may not be available locally.

3. Economic efficiency and risk reduction

Hiring contractors (other than IR35) can be a cost effective approach for business in the UK/Europe. Contractors are responsible for their own taxes, insurance and benefits, which relieve companies of these financial obligations. Unlike full-time employees, contractors are not eligible for employment benefits such as paid vacations, pensions, or health insurance. This savings aspect can be especially beneficial for startups, small businesses, or project-based companies.

In addition, hiring contractors (outside IR35) reduces certain risks associated with full-time employment. Contractors are self-employed individuals who work in accordance with contractual agreements, thereby minimizing the legal and financial obligations that may arise from employment-related issues. Companies can avoid potential costs such as redundancy payments, sickness pay or long notice periods, improving their financial stability and risk management.

4. Increased productivity and focus

Contractors (outside IR35) are often highly motivated professionals who are guided by project-based compensation. This intrinsic motivation results in increased productivity and focus as contractors strive to achieve excellent results within the stipulated time frame. Their autonomy and independence allows them to effectively manage their work, eliminating the distractions that may arise in traditional employment settings.

In addition, contractors are interested in building a solid reputation in their field. Satisfied clients and successful projects contribute to their professional portfolio and help secure future contracts. This encourages contractors to go the extra mile to ensure they deliver outstanding iOS development work that meets their clients’ goals and expectations.


Hiring contractors (outside IR35) for UK/Europe iOS development projects offers significant business benefits. Access to specialized skills, flexibility in resource allocation, cost efficiency, risk reduction and increased productivity are the key benefits that contractors bring to the table. By leveraging the expertise of contractors, companies can streamline their iOS development processes, achieve optimal results, and stay ahead of the competition in a rapidly evolving technology landscape.

Why Hiring a Contractor (Freelancer) is Better Than Having a Full-Time Employee in the US



As a senior iOS engineer with over nine years of experience, I’ve had the opportunity to work with both full-time employees and contractors. Based on my experience, I strongly believe that hiring contractors has many advantages over permanent workers. In this blog post, I will discuss the top five benefits of hiring contractors and give you some helpful tips for successfully hiring contractors.

Benefits of hiring contractors

1. Flexibility

One of the most significant benefits of hiring contractors is the flexibility they offer. Contractors can work from anywhere, at any time, and on projects that suit their interests. This level of flexibility is especially useful for businesses that need fast turnaround times or access to a wider pool of talent.

For example, if you need to develop a new website, hiring a web developer contractor who can work remotely from their home office can save you the time and money of finding a full-time web developer willing to work on the site.

2. Cost saving

Another major benefit of hiring contractors is the potential for cost savings. Contractors are generally more cost-effective than full-time employees because they don’t require benefits such as health insurance or paid time off. Plus, they don’t need to be physically present on site, which can help reduce office space and other overhead costs.

For example, if you need to hire a graphic designer for a project, the contractor may charge anywhere from $500 to $1,000 for the work. Conversely, hiring a full-time graphic designer will most likely require an annual salary of at least $50,000.

3. Access to specialized skills

Contractors often have special skills that regular employees may not have. This can be very useful for businesses working on projects that require specialized knowledge. For example, if you want to launch a new marketing campaign, hiring a marketing consultant with experience in developing and running successful campaigns can greatly increase your chances of success.

4. Risk reduction

Hiring contractors can help mitigate business risks. Unlike employees, contractors are not eligible for unemployment benefits or workers’ compensation, which can result in cost savings if the contractor is injured or fired.

In addition, contractors tend to be more motivated compared to full-time employees. Since they are not paid by the hour, they are motivated to complete projects quickly and efficiently. This intrinsic motivation often results in improved business performance.

5. Improve performance

Contractors often demonstrate higher levels of productivity than their full-time counterparts. Driven by the desire to complete projects efficiently, contractors strive to deliver quality deliverables on time. In fact, a Freelancers Alliance study found that freelancers are 35% more productive than full-time employees. In addition, research has shown that freelancers take more breaks throughout the day, which helps them stay focused and generally productive.


In conclusion, based on my extensive experience as a senior iOS engineer, hiring contractors has many advantages over hiring full-time employees. The flexibility, cost savings, access to specialized skills, risk reduction and productivity gains associated with hiring contractors make them a great business choice. By carefully selecting and managing contractors, companies can leverage a wealth of experience and talent to effectively optimize their resources.

The clean architecture

Source: Undraw

The Clean Architecture is the system architecture guideline proposed by Robert C. Martin (Uncle Bob) derived from many architectural guidelines like Hexagonal Architecture, Onion Architecture, etc… over the years.

This is one of the guidelines adhered to by software engineers to build scalable, testable, and maintainable software.

Why do we need to architect?

“The goal of software architecture is to minimize the human resources required to build and maintain the required system.” ― Robert C. Martin, Clean Architecture

Advantages of Proper Architecture

  • Testable
  • Maintainable
  • Changeable
  • Easy to Develop
  • Easy to Deploy
  • Independent

The Clean Architecture

Here’s the clean architecture illustration created by Robert Martin:

Image by Robert C. Martin

We can see there are four layers in the diagram. Blue layer, Green layer, Red layer, and Yellow layer.

Each circle represents different areas of the software. The outermost layer is the lowest level of the software and as we move in deeper, the level will be higher. In general, as we move in deeper, the layer is less prone to change.

The Dependency Rule

The Dependency Rule states that the source code dependencies can only point inwards.

This means nothing in an inner circle can know anything at all about something in an outer circle. i.e. the inner circle shouldn’t depend on anything in the outer circle. The Black arrows represented in the diagram show the dependency rule.

This is the important rule that makes this architecture work. Also, this is hard to understand. So I’m gonna break this rule at first to let you understand what problems it brings and then explain and let’s see how to keep up with this rule. So please bear with me.

First of all, this circular representation might be confusing for many. So let’s try to represent it vertically.

Image by author

The colors represented here are the same as the colors represented in the clean architecture diagram.

Remember, the arrow should be read as “depend on”. i.e. Frameworks and Drivers should depend on Interface Adapters, which depend on Application Business Rules which depend on Enterprise Business Rules.

Nothing in the bottom layer should depend on the top layer.

Frameworks and Drivers

Software areas that reside inside this layer are

  • User Interface
  • Database
  • External Interfaces (eg: Native platform API)
  • Web (eg: Network Request)
  • Devices (eg: Printers and Scanners)

Interface Adapters

This layer holds

  • Presenters (UI Logic, States)
  • Controllers (Interface that holds methods needed by the application which is implemented by Web, Devices or External Interfaces)
  • Gateways (Interface that holds every CRUD operation performed by the application, implemented by DB)

Application Business Rules

Rules which are not Core-business-rules but essential for this particular application come under this. This layer holds Use CasesAs the name suggests, it should provide every use case of the application. i.e. it holds each and every functionality provided by the application.

Also, this is the layer that determines which Controller / Gateway to be called for the particular use case. Sometimes we need controllers from different modules.

This is where different modules are coordinated. For instance, we want to apply a discount for the user who purchased for x amount within a month.

Here we need to get the amount the user has spent on this month from the purchase module and then with the result we need to apply the discount for the user in the checkout moduleHere applyDiscountUseCase calls the purchase module’s controller for the data and then applies the discount in the checkout module.

Enterprise Business Rules

This is the layer that holds core-business rules or domain-specific business rules. Also, this layer is the least prone to change.

Change in any outer layer doesn’t affect this layer. Since Business Rules won’t change often, the change in this layer is very rare. This layer holds Entities.

An entity can either be a core data structure necessary for the business rules or an object with methods that hold business logic in it.

For example: calculating Interest module in the banking application is the core business logic that should be inside this layer.

Let’s look at a simple example to understand this well.

The example demonstrates a simple application that has only one network request.

How can we architect an app that translates the sentence given by the user using a translation API? let’s try to architect.

Image by author

Each layer does a specific thing. Looks good right? Let’s check the dependency flow for this above architecture to know if anything is wrong.

Remember Dependency Rule? “The Dependency Rule states that the source code dependencies can only point inwards”.

Image by author

UI → Presenter (✅ Not Violating)

Presenter → Translate Usecase (✅ Not Violating)

Translate Usecase → Translate Controller (❌ Violating)

Translate Controller → Web (❌ Violating)

But it seems correct, right?

UI requests data from Presenter which requests data from Use Case which should request data from Controller which should request data from Web.

After all, how can we expect the web to throw some data to the Controller without the Controller being dependent on it? Also, how can we expect the Use Case to get the proper data from the Controller without depending on it?

But the Dependency Rule strictly says dependencies can only point inwards. It adds up by saying this is the rule that makes the architecture work.

In order to pass this rule, we need to invert the arrow to the opposite direction. Is that possible? Here comes PolymorphismWhen we include some Polymorphism here, something magic happens.

Simply by having an Interface between these 2 layers, we could invert the dependency. This is known as The Dependency Inversion Principle.

Let’s implement the Dependency Inversion Principle in the cases where the Dependency Rule is violated.

Image by author
Image by author

Thus the flow becomes:

Image by author

Let’s check the dependency flow now to know if anything violates it.

Image by author

Now we can see that no inner layer depends on any outer layer. Rather, the outer layer depends on the inner layer.

So why should the outer layer depend on the inner layer but not the other way around?

Imagine you’re in a hotel. We want the hotel to serve us what we want, but not what they offer right?. The same thing is happening here, we want the DB to give the data the application needs but not the data it has.

Application orders what data it wants and it doesn’t care how DB or API prepares the data. This way, the application doesn’t depend on DB or API. If we need/want to change the DB or API Schema in the future, we can simply change it. As far as it gives what the application asks for, the application doesn’t even know the change in DB or API.

Also, the single-way dependency rule saves the application from the deadlock state. i.e. imagine in a 2 layer architecture, the first layer depends on the second layer, and the second layer depends on the first layer. In such a case, If we need to change anything in the first layer, it breaks the second layer. If we need to change anything in the second layer, it breaks the first layer. This can be rejected by following the deadlock state.

This is the clean architecture described by Uncle Bob.

We are yet to see how to move the data across the boundaries and how to handle errors. We’ll do so in future articles.

Thanks for reading.