Skip to content

This project is a comprehensive infrastructure study that combines Server-Driven UI (SDUI) principles, one of the most advanced techniques in modern iOS development,

License

Notifications You must be signed in to change notification settings

engingulek/TrendyolGoByUberEatsBase

Repository files navigation

TrendyolGoByUberEatsBase

This project is a comprehensive infrastructure study that combines Server-Driven UI (SDUI) principles, one of the most advanced techniques in modern iOS development, with a highly scalable Modular VIPER architecture.

The primary goal is to dynamically manage the application's home screen structure, component layouts, and navigation flows through backend-provided metadata (JSON), without the need for a new App Store version.

Backend

🏗 Architectural Deep Dive

Protocol-Oriented Modular Structure The application is divided into independent modules that communicate entirely via protocols rather than concrete classes. This structure minimizes coupling while enhancing testability:

  • HomeModule: Homepage logic and the SDUI engine.

  • AllListModule: Dynamic listing screens.

  • CommonKit: Shared UI components and utility tools.

  • ManagerKit: Service and dependency management.

  • ModularProtocols: Interface contracts facilitating inter-module communication.

Generic Collection View Kit

A lightweight and reusable framework for UICollectionView written in Swift.
It provides a generic, type-safe, and highly customizable way to manage collection view data, section headers, delegates, and layouts. Generic Collection View Kit

UI & UX Engineering: Reusable Atomic Components

The visual layer of the project consists of highly customizable and modular UI components. These components encapsulate their own presentation logic in accordance with SOLID principles.

  • CampaingBanner
  • CourierView
  • RatingAndCommentView

Some Define

Modular Protocols

import UIKit

// MARK: - Home Module Protocol
/// Defines the contract for creating the Home module’s view controller.
/// Used to assemble and return the main screen of the application.
public protocol HomeModuleProtocol {
    
    /// Creates and returns the Home module’s main view controller.
    /// - Returns: A configured `UIViewController` representing the Home screen.
    func createHomeModule() -> UIViewController
}

// MARK: - All List Module Protocol
/// Defines the contract for creating the All List module’s view controller.
/// Used to assemble and return the All List screen of the application.
public protocol AllListModuleProtocol{
    
    /// Creates and returns the All List module’s main view controller.
    /// - Returns: A configured `UIViewController` representing the All List screen.
    func createAllListModule(id:Int) -> UIViewController
}

Home Router

import UIKit
import ModularProtocols
import DependencyKit
import CommonKit

/// `HomeRouter` is responsible for navigation in the Home module.
final class HomeRouter: PresenterToRouterHomeProtocol {
    
    // MARK: - Navigation to All List Page
    
    /// Navigates to the All List screen based on the selected section type.
    /// - Parameters:
    ///   - view: The current view conforming to `PresenterToViewHomeProtocol`.
    func toAllList(
        view: (any PresenterToViewHomeProtocol)?,
        id: Int
    ) {
        // Resolve AllList module dependency from DependencyRegister
        let allListModule =
        DependencyRegister.shared.resolve(
            ModularProtocols.AllListModuleProtocol.self
        )
        
        // Create the AllList view controller using module
        let allListVC = allListModule.createAllListModule(id: id)
        view?.pushViewControllerAble(allListVC, animated: true)
    }
}

NetworkRequest

import Foundation
// MARK: - NetworkRequest Protocol
/// A protocol that defines the blueprint for building API requests.
/// Each request specifies its endpoint, HTTP method, headers, and parameters.
public protocol NetworkRequest {
    
    /// The expected response type of the request.
    /// It must conform to both `Decodable` (for JSON parsing)
    associatedtype Response: Decodable
    /// The API endpoint associated with this request, defined using `NetworkPath`.
    var path: NetworkPath { get }
    /// The HTTP method used for the request (e.g., GET, POST, PUT).
    var method: AlamofireMethod { get }
    /// Optional HTTP headers to be included in the request.
    /// Commonly used for authentication tokens or custom headers.
    var headers: [String: String]? { get }
    /// Optional parameters to send with the request.
    /// These can be encoded as query items or JSON depending on the HTTP method.
    var parameters: [String: Any]? { get }
}

Json

[
    {
        "id": 1,
        "title": "Camping",
        "headerImageDTO": null,
        "layoutTemplateDTO": {
            "layoutTemplate": "card_02",
            "scrollDirection": "horizontal",
            "groupOrientation": "horizontal"
        },
        "cellType": "card_image",
        "buttonTypes": [
            "alllist"
        ],
        "data": [
            {
                "id": 1,
                "url": "https://cdn.tgoapps.com/mnresize/640/-/local-commerce-promotion/mars/prod/banner/f078c002-f7b7-4ac8-ac36-8dd9dfe44d5e.jpg"
            }
        ]
    },
    {
        "id": 2,
        "title": "Campaing Restaurant",
        "headerImageDTO": {
            "id": 1,
            "type": "SYSTEM",
            "imageText": "tag.fill",
            "color": "#FFA500"
        },
        "layoutTemplateDTO": {
            "layoutTemplate": "card_03",
            "scrollDirection": "horizontal",
            "groupOrientation": "horizontal"
        },
        "cellType": "card_info",
        "buttonTypes": [
            "alllist"
        ],
        "data": [
            {
                "id": 9,
                "imageUrl": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTIVl8bx-sqjynOaPbHDvVZkhafZ0wtmZui2Q&s",
                "rating": 4.5,
                "reviewCount": 120,
                "name": "Burger House",
                "minimumPrice": 50.0,
                "distanceKm": 1.2,
                "category": "Burger",
                "deliveryTime": 25,
                "deliveryType": "restaurantCourier",
                "campaignList": [
                    "%20 Discount",
                    "Free Drink"
                ]
            }
        ]
    },
    {
        "id": 3,
        "title": "Kitchens",
        "headerImageDTO": null,
        "layoutTemplateDTO": {
            "layoutTemplate": "card_01",
            "scrollDirection": "horizontal",
            "groupOrientation": "horizontal"
        },
        "cellType": "card_image_title",
        "buttonTypes": [
            "alllist"
        ],
        "data": [
            {
                "id": 1,
                "url": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSOX2YyS0W1LnrD5eyd-7Q9kvO1JGvE80AXXA&s",
                "title": "Burger"
            }
        ]
    },
    {
        "id": 5,
        "title": "Go Kampanyası",
        "headerImageDTO": {
            "id": 2,
            "type": "ASSETS",
            "imageText": "go",
            "color": null
        },
        "layoutTemplateDTO": {
            "layoutTemplate": "card_03",
            "scrollDirection": "horizontal",
            "groupOrientation": "horizontal"
        },
        "cellType": "card_info",
        "buttonTypes": [
            "alllist"
        ],
        "data": [
            {
                "id": 13,
                "imageUrl": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTIVl8bx-sqjynOaPbHDvVZkhafZ0wtmZui2Q&s",
                "rating": 4.5,
                "reviewCount": 120,
                "name": "Burger House",
                "minimumPrice": 50.0,
                "distanceKm": 1.2,
                "category": "Burger",
                "deliveryTime": 25,
                "deliveryType": "restaurantCourier",
                "campaignList": [
                    "%20 Discount",
                    "Free Drink"
                ]
            }
        ]
    },
    {
        "id": 4,
        "title": "Restaurants",
        "headerImageDTO": null,
        "layoutTemplateDTO": {
            "layoutTemplate": "featured_01",
            "scrollDirection": "vertical",
            "groupOrientation": "vertical"
        },
        "cellType": "featured_01_info",
        "buttonTypes": [
            "View",
            "Filter"
        ],
        "data": [
            {
                "id": 12,
                "imageUrl": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTIVl8bx-sqjynOaPbHDvVZkhafZ0wtmZui2Q&s",
                "rating": 4.5,
                "reviewCount": 120,
                "name": "Burger House",
                "minimumPrice": 50.0,
                "distanceKm": 1.2,
                "category": "Burger",
                "deliveryTime": 25,
                "deliveryType": "restaurantCourier",
                "campaignList": [
                    "%20 Discount",
                    "Free Drink",
                    "1+1 Pizza"
                ]
            }
        ]
    }
]

Entity

public struct UIModel: Decodable,Sendable {
    public  let id: Int
    public  let title: String
    public  var headerImageDTO : HeaderImage?
    public  var layoutTemplateDTO: LayoutTemplateInfo
    public  var cellType: CellType
    public  let buttonTypes: [TitleForSectionButtonType]
    public  let data: UISectionData
 

    enum CodingKeys: String, CodingKey {
        case id, title, headerImageDTO,layoutTemplateDTO, cellType, buttonTypes, data
    }
    
   public init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)

        self.id = try c.decode(Int.self, forKey: .id)
        self.title = try c.decode(String.self, forKey: .title)
        self.layoutTemplateDTO = try c.decode(LayoutTemplateInfo.self, forKey: .layoutTemplateDTO)
        self.headerImageDTO = try c.decodeIfPresent(HeaderImage.self, forKey: .headerImageDTO)
        self.cellType = try c.decode(CellType.self, forKey: .cellType)

        // buttonTypes → convert
        let rawBtns = try c.decode([String].self, forKey: .buttonTypes)
        self.buttonTypes = rawBtns.map { TitleForSectionButtonType.convert(rawValue: $0) }

        // datas → decode by cellType
        self.data = try UIModel.decodeDatas(container: c, type: self.cellType)
    }
}

Cell Type and Cell Data

// MARK: - Cell Type
// Represents the type of collection view cells
public enum CellType: String, Decodable, Sendable {
    // Card that contains only an image
    case cardImage = "card_image"
    // Card that contains an image and a title
    case cardImageTitle = "card_image_title"
    // Card that displays restaurant information
    case cardInfo = "card_info"
    // Featured restaurant info (version 01)
    case featured01Info = "featured_01_info"
    // Featured restaurant info (version 02)
    case featured02Info = "featured_02_info"
    // Undefined or empty state
    case none
}

// MARK: - Cell Data
// Type-safe representation of data passed to a cell
public enum CellData: Sendable {
    // Image-only model
    case cardImage(OnlyImageModel)
    // Image and title model
    case cardImageTitle(ImageAndTitleModel)
    // Restaurant info model (standard card)
    case cardInfo(RestaurantInfoModel)
    // Featured restaurant info (version 01)
    case featured01Info(RestaurantInfoModel)
    // Featured restaurant info (version 02)
    case featured02Info(RestaurantInfoModel)
    // No data state
    case none
    
    // MARK: - Factory Method
    // Converts RestaurantInfoModel into the correct CellData based on CellType
    public  static func restaurant(_ data: RestaurantInfoModel,type: CellType) -> CellData {
        switch type {
        case .cardInfo:
            return .cardInfo(data)
        case .featured01Info:
            return .featured01Info(data)
        case .featured02Info:
            return .featured02Info(data)
        default:
            // Unsupported or incompatible types
            return .none
        }
    }
}

Videos

Videos

Home.Page.mov
All.Lists.mov
View.Change.mp4
Reflesh.mp4

About

This project is a comprehensive infrastructure study that combines Server-Driven UI (SDUI) principles, one of the most advanced techniques in modern iOS development,

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages