Improving Code for an Experimental Project SwiftUI

Jerry PM
4 min readMar 9, 2023

--

I found the Food app on GitHub and decided to fix it.

I recently worked on an experimental project where I had the opportunity to improve my coding skills. During the process, I made several changes to the code to make it more organized and efficient. Here are some of the changes I made:

Separated Views in ContentView

One of the first things I did was to separate the views that were created in one file, ContentView. By creating separate files for each view, I was able to easily locate and modify them. This helped make the code easier to read and understand.

import SwiftUI

struct ContentView: View {
// MARK: - PROPERTIES
@StateObject var viewModel = FoodViewModel()

// MARK: - BODY
var body: some View {
ZStack {
NavigationView {
TabView {
HomeScreen()
.environmentObject(viewModel)
.tabItem {
Image.homeAsset
Text(SC.tabHome.value)
}
OrderScreen().tabItem {
Image.orderAsset
Text(SC.tabOrder.value)
}
AccountScreen().tabItem {
Image.userAsset
Text(SC.tabAccount.value)
}
}.onAppear(){
UIToolbar.appearance().barTintColor = .lightGray
}

.accentColor(.red.opacity(0.8))
}
.onAppear {
viewModel.getBannerData()
}

}
}
}

Created an API Service

I also created an API service to handle the data requests from the server. By doing so, I was able to keep the data requests separate from the views, which made the code more modular and easier to maintain.

struct APIService {
public static let share = APIService()
private let baseURL = URL(string: SC.baseURLMock.value)
private init() {}

func resultRequest<T: Codable>(endpoint: Endpoint, completion: @escaping (Result<T, Error>) -> Void) {
guard let url = baseURL else {return}
var request = URLRequest(url: url)
request.httpMethod = endpoint.method.rawValue

URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
completion(.failure(error))
return
}

guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else {
let statusCode = (response as? HTTPURLResponse)?.statusCode ?? -1
completion(.failure(APIError.invalidResponse(statusCode: statusCode)))
return
}

guard let data = data else {
completion(.failure(APIError.noData))
return
}

do {
let decodedResponse = try JSONDecoder().decode(T.self, from: data)
completion(.success(decodedResponse))
} catch {
completion(.failure(error))
}
}.resume()
}
}

Created Image and String Extensions

To make the code more concise, I created extensions for images and strings. These extensions allowed me to add functionality to the built-in Swift classes and made the code more reusable.

extension String {

public static var empty: String {
return SC.empty.value
}

public static var dash: String {
return SC.dash.value
}

public static var threeSpace: String {
return SC.space.value
}

//: Error response convert

public func errorWith() -> NSError {
let error = NSError(
domain: .empty,
code: 429,
userInfo: [NSLocalizedDescriptionKey: self])
return error
}

}
import SwiftUI

extension Image {
// MARK: Asset
public static var homeAsset: Image {
return Image(uiImage: UIImage(named: SC.homeAsset.value)!)
}

public static var orderAsset: Image {
return Image(uiImage: UIImage(named: SC.orderAsset.value)!)
}

public static var userAsset: Image {
return Image(uiImage: UIImage(named: SC.userAsset.value)!)
}

public static var drinkAsset: Image {
return Image(uiImage: UIImage(named: SC.drink.value)!)
}


public static var fastfoodAsset: Image {
return Image(uiImage: UIImage(named: SC.fastfood.value)!)
}

public static var healthyAsset: Image {
return Image(uiImage: UIImage(named: SC.healthy.value)!)
}

public static var riceAsset: Image {
return Image(uiImage: UIImage(named: SC.rice.value)!)
}

public static var snacksAsset: Image {
return Image(uiImage: UIImage(named: SC.snacks.value)!)
}


// MARK: System
public static var homeSystem: Image {
return Image(systemName: SC.homeSystem.value)
}

public static var caseBagSystem: Image {
return Image(systemName: SC.caseBagSystem.value)
}

public static var personSystem: Image {
return Image(systemName: SC.personSystem.value)
}

public static var emptyImage: Image {
return Image(systemName: SC.emptyImage.value)
}

public static var searchSystem: Image {
return Image(systemName: SC.searchSystem.value)
}


}

Improved Usage of View Models

I also realized that the usage of the view model in the previous code was incorrect. To fix this, I restructured the code to better use the view model, which made the code more efficient.

import SwiftUI

class FoodViewModel: ObservableObject {
@Published var banner: [BannerModel] = []
@Published var collection : [OfferCollections] = []
@Published var voucher: Int?
@Published var restaurant : [RestaurantCollectionModel] = []
@Published var recommend : [RestaurantsModel] = []
@Published var offer : [OffersModel?] = []

func getBannerData() {
APIService.share.resultRequest(endpoint: FoodEndpoint.getMockData) { (result: Result<FoodDataModel, Error>) in
DispatchQueue.main.async {
self.banner.removeAll()
self.collection.removeAll()

switch result {
case .success(let value):
//banner
value.data?.banners.forEach({ item in
self.banner.append(item)
})

//voucher
self.voucher = value.data?.number_of_active_vouchers

//Collection
value.data?.offer_collections.forEach({ item in
self.collection.append(item)
})

//Restaurants
value.data?.restaurant_collections.forEach({ item in
self.restaurant.append(item)
})

//Recomended
value.data?.restaurant_collections[0].restaurants.forEach({ item in
self.recommend.append(item)
})

//offer
value.data?.restaurant_collections[0].restaurants[0].offers.forEach({ item in
self.offer.append(item)
})

case .failure(let error):
print(error)
}
}

}

}

}

Because there is only one API, In ViewModel I made just one request

My Swift UI Wallpaper app Filter and you can edit your image too from your photo in this template has been sold on the CodeCanyon website. Thank you for reading my article.
https://codecanyon.net/item/wallpaper-wizard/43965325 😄

--

--