10 tips to increase your iOS code quality (pt. 2)
Boost your Swift programming expertise and optimize your iOS app code
Quick and straight to the point, here are 10 tips that you can adopt in your iOS projects.
If you want to check part 1 and add another 10 tips to your toolkit, here it is π
1. Composable UI
Break down your views into smaller pieces to make them more readable.
If you want, go one step further and make some reusable views to avoid code duplication.
struct HomeView: View {
var body: some View {
VStack {
headerView
mainView
footerView
}
}
}
// MARK: - UI Components
private extension HomeView {
var headerView: some View {...}
var mainView: some View {...}
var footerView: some View {...}
}
2. Reusable styling with view modifiers
If you have some custom styling that repeats across the app, take advantage of SwiftUI view modifiers to maintain the code in one place.
struct TitleStyle: ViewModifier {
func body(content: Content) -> some View {
content
.font(.system(size: 22, weight: .bold))
.foregroundColor(Color.black)
}
}
extension View {
func titleStyle() -> some View {
modifier(TitleStyle())
}
}
struct HomeView: View {
var body: some View {
Text("Hello!")
.titleStyle()
}
}
3. Include Unit Tests
One of the most undervalued processes that will considerably improve your code quality.
When adding a new piece of logic to your code, think about how would you ensure its correctness. Write a unit test and run your test cases. It will save a ton of time later.
4. Error handling
Don't be afraid of using throws
. It's a great way to propagate errors without much effort.
enum NetworkError: Error {
case fetching(Error)
}
func fetchData(from url: URL) throws -> Data {
do {
let data = try Data(contentsOf: url)
return data
} catch {
throw NetworkError.fetching(error)
}
}
func fetchProducts() throws -> [Product] {
let productsURL = URL(string: "https:\\myapi.com/products")
let data = try fetchData(from: productsURL)
return try JSONDecoder().decode([Product].self, from: data)
}
do {
let products = fetchProducts()
} catch NetworkError.serverError(let message) {
print("Server error: \(message)")
} catch NetworkError.networkError(let error) {
print("Network error: \(error.localizedDescription)")
} catch {
print("An unknown error occurred: \(error)")
}
5. Code documentation
I'm a strong believer that the best documentation is the auto-document code. However, if you're designing a complex API or library, providing custom documentation will help others better understand your code interfaces.
It's super easy to add, just double-click on the function you want and click on Add Documentation
.
/// Create a new request with the information provided
/// ```swift
/// do {
/// let request = try createRequest(from: .signing)
/// } catch {
/// print(error)
/// }
/// ```
/// - Parameter endpoint: Endpoint to perform the request
/// - Returns: A URLRequest instance with the information provided in `endpoint`
func createRequest(from endpoint: ApiEndpoint) throws -> URLRequest {...}
6. Enums
Use enums if you need to model states, options, or configurations. They provide a type-safe and self-documented way of representation.
You can accomplish more than you think.
enum AppTheme {
case light
case dark
case custom(primaryColor: Color, backgroundColor: Color)
}
7. Core Data + SwiftUI
If you're using Core Data in your app, use @FetchRequest
property wrapper to get data directly in a View.
struct HomeView: View {
@FetchRequest(sortDescriptors: [SortDescriptor(\.id, order: .reverse)])
private var users: FetchedResults<User>
var body: some View {
VStack {
List(users) { user in
Text(user.name ?? "Unknown")
}
}
}
}
8. Coding Style Guide
Define a style guide for your team to provide a set of coding conventions and best practices to maintain consistency across your project.
Your style guide could include information like:
- Naming conventions
- Code formatting
- Documentation
- Files and project structure
- Error handling
- Swift language features to use
- Testing best practices
9. Dependency injection
Use it to enhance modularity, maintainability, and testability of your code. DI promotes the Single Responsibility Principle, encouraging components to focus on a single task.
10. Design System
Create a Design System to manage all UI components in a flexible and reusable way. It will help to scale your app and adapt to possible changes in the future.
If you never made a DS, a good starting point is to define a manager for your app's font and labels. You can combine it with custom view modifiers and create a visual representation to serve as a guide.