Skip to content

Repository files navigation

EulerityDynamicForm

A server-driven dynamic form engine built with SwiftUI using MVVM + lightweight Clean Architecture.

The application dynamically renders form UI from local JSON configuration and supports validation, reusable components, theming, rich text links, and scalable form state management while keeping the architecture practical and maintainable.


Features

  • Dynamic form rendering from local JSON
  • MVVM + lightweight Clean Architecture
  • DTO → Domain mapping
  • Dynamic field validation
  • Reusable form components
  • Theme support using EnvironmentValues
  • Rich text terms & conditions component using AttributedString
  • Multi-select, dropdown, checkbox, text, email and number fields
  • URL handling with in-app Safari support
  • Unit testing for decoding, validation and ViewModel logic

Overall Approach & Architecture

My overall approach was to keep UI, business logic, and data handling separated into clear layers for better maintainability, readability, and testing.

I used:

  • SwiftUI
  • MVVM
  • Lightweight Clean Architecture

I intentionally avoided overengineering the project with unnecessary layers, networking abstractions, or heavy dependency injection frameworks since the task uses local JSON.

The architecture flow is:

Local JSON
   ↓
DTO Decoding
   ↓
DTO → Domain Mapping
   ↓
Use Case
   ↓
ViewModel
   ↓
SwiftUI Rendering

Project Structure

EulerityDynamicForm
│
├── App
│
├── Core
│   ├── Extensions
│   └── Utilities
│
├── Data
│   ├── DTOs
│   ├── Mappers
│   └── Sources
│
├── Domain
│   ├── Entities
│   ├── Validation
│   └── UseCases
│
├── Presentation
│   ├── Components
│   ├── ViewModels
│   ├── Views
│   └── Theme
│
└── Resources

Key Technical Decisions

1. DTO → Domain Mapping

I separated DTOs from Domain models instead of directly using decoded JSON models throughout the app.

This allowed:

  • safer type conversion
  • cleaner business models
  • fallback handling
  • isolation from backend/schema changes

Example:

JSON String Type → Domain Enum Type

2. Form State Management

I used:

[String: FormValue]

instead of:

[String: String]

because the form contains multiple field types such as:

  • text
  • checkbox
  • dropdown
  • multi-select

This made state management more scalable and type-safe.


3. Dynamic Theme Injection

The form theme is injected using EnvironmentValues so deeply nested components can access styling configuration without prop drilling.


Product / Edge Case Decisions

1. Unknown Field Types

If an unknown field type comes from JSON, the app does not crash.

Instead:

  • the field is skipped safely
  • no broken UI is rendered

I chose this because dynamic/server-driven UI should degrade gracefully instead of crashing on unsupported schema values.


2. Validation Timing

Validation is not aggressively triggered while the user types the first character.

Instead:

  • validation happens on submit
  • errors are cleared as user updates values

This creates a cleaner and less frustrating UX.


3. Max Length Handling

For text fields with max length:

  • I used String.prefix
  • typing is prevented beyond allowed length

I also added a safety check:

maxLength > 0

before applying the restriction.

This prevents accidental blocking when max length is missing or invalid.


Rich Text Terms & Conditions

I implemented a reusable rich text component using AttributedString.

The component accepts:

message: String
metadata: [String: String]

Where:

  • key = clickable text
  • value = URL

I used custom URL handling to support opening links either:

  • inside an in-app Safari view
  • or externally if needed

Validation

Validation is handled separately from UI rendering through a dedicated validator layer.

Supported validation includes:

  • required text fields
  • email validation
  • checkbox validation
  • multi-select validation
  • numeric input filtering

Testing

The project includes unit tests for:

  • JSON loading & decoding
  • DTO → Domain mapping
  • Validation logic
  • ViewModel state updates
  • Form submission logic

I focused testing on business logic and edge cases instead of snapshot/UI-heavy testing.


What I Would Improve With More Time

If I had more time, I would improve:

  • accessibility support
  • better keyboard navigation handling
  • stronger form submission abstraction
  • field-level async validation support
  • improved animation polish
  • snapshot/UI testing coverage
  • dynamic section/group support
  • more advanced schema-driven validation rules

Challenges & How I Solved Them

1. Testing Local JSON Loading

One issue I encountered was loading local JSON correctly during unit testing.

Bundle.main does not work reliably inside test targets because tests run in a separate bundle context.

I solved this by:

  • injecting Bundle into the data source
  • using Bundle(for:) inside tests
  • adding JSON resources to the test target membership

This made decoding logic properly testable.


2. Form State for Multiple Field Types

Initially, I considered using:

[String: String]

for form state.

However, this quickly became limiting because the form included:

  • checkboxes
  • dropdowns
  • multi-select fields

I redesigned the state using:

[String: FormValue]

with associated enum values, which made the solution much more scalable and type-safe.


Tech Stack

  • SwiftUI
  • MVVM
  • Clean Architecture
  • XCTest / Swift Testing
  • AttributedString
  • EnvironmentValues
  • SFSafariViewController

About

A server-driven dynamic form engine built with SwiftUI using MVVM + lightweight Clean Architecture. Features dynamic field rendering from local JSON, DTO → Domain mapping, validation handling, reusable form components, theme support, rich text links, and scalable form state management without overengineering.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages