Skip to content

Lainaaa/NordVPN-Test-Assignment

Repository files navigation

NordVPN Test Assignment

A two-screen iOS application built with SwiftUI that demonstrates authentication and server list functionality according to the provided design specifications.

Features

  • User authentication with credential storage in Keychain
  • Server list display with sorting capabilities
  • Persistent data storage with cache management
  • Automatic token validation and error handling
  • Modern SwiftUI architecture with MVVM pattern
  • Protocol-based router for navigation

Architecture

The application follows a clean MVVM (Model-View-ViewModel) architecture with clear separation of concerns and protocol-oriented design for better testability and maintainability.

Core Components

1. Models

  • AuthModels.swift: Contains data structures for authentication and server data
    • LoginRequest: Request payload for authentication
    • AuthResponse: Authentication response containing token
    • Server: Server data model with name and distance properties (all with Sendable conformance)
  • NetworkModels.swift: Contains networking layer abstractions
    • Request: Protocol defining network request structure with associated response types
    • LoginNetworkRequest: Concrete implementation for authentication requests
    • ServersNetworkRequest: Concrete implementation for server list requests

2. Views

  • ContentView.swift: Root view with protocol-based router for navigation
  • AuthView.swift: Login screen implementation
  • ServerListView.swift: Server list display with clean switch-based UI state management

3. ViewModels

  • AuthViewModel.swift: Manages authentication state and user credentials
    • Handles login/logout operations
    • Manages authentication state transitions
    • Integrates with AuthService for authentication operations
  • ServerListViewModel.swift: Manages server data and sorting
    • Handles server data fetching and caching
    • Implements sorting by distance and alphabetically
    • Manages loading states and error handling
    • Integrates with ServerService for server operations

4. Services

NetworkService (Generic Network Layer)
  • Protocol: NetworkServiceProtocol
  • Implementation: NetworkService
  • Responsibilities:
    • Generic execute<R: Request>(_ request: R) async throws -> R.ResponseType method
    • Eliminates code duplication through protocol-based requests
    • HTTP error handling with custom NetworkError enum
    • Automatic error mapping from Alamofire errors
    • Works with any request type conforming to Request protocol
AuthService
  • Protocol: AuthServiceProtocol
  • Implementation: AuthService
  • Responsibilities:
    • Authentication-specific business logic
    • Uses NetworkService internally for API calls
    • Single responsibility: only authentication operations
ServerService
  • Protocol: ServerServiceProtocol
  • Implementation: ServerService
  • Responsibilities:
    • Server-related business logic
    • Uses NetworkService internally for API calls
    • Single responsibility: only server operations
KeychainService
  • Protocol: KeychainServiceProtocol
  • Implementation: KeychainService
  • Responsibilities:
    • Secure token storage in iOS Keychain
    • Token retrieval and deletion
    • Keychain error handling
PersistentService
  • Protocol: PersistentServiceProtocol
  • Implementation: UserDefaultsPersistentService
  • Responsibilities:
    • Server data caching with configurable keys
    • Cache expiration management (5-minute TTL)
    • Data persistence using UserDefaults
    • Configurable for testing with different storage keys
AppRouter
  • Protocol: AppRouter
  • Implementation: Mini router with enum-based navigation
  • Responsibilities:
    • Centralized navigation logic
    • Route management through AppRoute enum
    • Clean separation of navigation concerns

Architecture Benefits

  1. Protocol-Oriented Design: All services use protocols, enabling easy mocking for testing and future implementation changes
  2. Single Responsibility Principle: Each service has one clear responsibility
  3. Generic Network Layer: One universal execute method handles all request types
  4. Separation of Concerns: Clear boundaries between networking, persistence, security, and UI logic
  5. Reactive UI: SwiftUI with ObservableObject ensures automatic UI updates
  6. Enhanced Error Handling: Custom error types with automatic mapping

Network Layer Architecture

The networking layer follows a generic, protocol-based approach:

protocol Request: Sendable {
    associatedtype ResponseType: Codable & Sendable
    var path: String { get }
    var method: HTTPMethod { get }
    var parameters: [String: Any]? { get }
    var headers: HTTPHeaders? { get }
}

// Usage
let loginRequest = LoginNetworkRequest(username: "user", password: "pass")
let response: AuthResponse = try await networkService.execute(loginRequest)

Benefits:

  • Type Safety: Response type is determined by request type
  • Code Reuse: One method handles all network requests
  • Easy Extension: Add new requests by conforming to Request protocol
  • No Code Duplication: Eliminates repetitive networking code

Data Flow

  1. App Launch: ContentView with AppRouter checks for stored credentials via AuthViewModel
  2. Authentication: AuthView collects credentials and triggers login through AuthService
  3. Token Storage: Successful authentication stores token in Keychain
  4. Navigation: AppRouter manages transition to server list
  5. Server Loading: ServerListView loads servers via ServerService
  6. Caching: Servers are cached locally with timestamp for offline access
  7. UI State Management: Clean switch-based UI states (loading, loaded, error, empty)

Testing

The project includes unit tests for critical components with improved isolation:

Test Coverage

PersistentServiceTests: Tests for data persistence functionality

  • Server saving and loading
  • Cache expiration logic
  • Data clearing operations
  • Isolated Testing: Uses configurable keys to avoid conflicts with production data

Test Architecture

  • Tests use XCTest framework
  • Configurable services eliminate need for separate test implementations
  • Mock services can be easily created using protocols
  • Each service component is independently testable

Possible Improvements

Technical Improvements

  1. Routing Architecture: Introduce a dedicated NavigationRouter layer to separate navigation logic from views.
  2. Dependency Injection: Adopt a DI container (e.g., Resolver or Swift Concurrency–based injection) to enable easy service substitution and unit-testing.
  3. Reusable Design System: Extract typography, color tokens, and shared UI components into a standalone design-system module for consistent styling.
  4. Centralized Constants: Move hard-coded numbers, strings, and keys into structured enums such as UIConstants, LocalizationKeys, and APIEndpoints.
  5. Core Data Migration: Replace UserDefaults with Core Data for higher-performance persistence and scalable querying.
  6. Modularization: Split into separate Swift Packages
  7. CI/CD: Automated testing and deployment pipeline
  8. Code Coverage: Increase test coverage to 90%+
  9. Static Analysis: Integrate SwiftLint and other code quality tools

Performance Optimizations

  1. Pagination: Add server list pagination for large datasets
  2. Background Refresh: Implement background app refresh for server data
  3. Memory Management: Optimize memory usage for large server lists
  4. Request Deduplication: Prevent duplicate simultaneous requests

User Experience Enhancements

  1. Pull-to-Refresh: Add pull-to-refresh gesture for server list
  2. Search Functionality: Implement server search and filtering
  3. Favorites: Allow users to mark favorite servers
  4. Connection Status: Show server connection status and ping times
  5. Biometric Authentication: Add Touch ID/Face ID support
  6. Dark Mode: Enhanced dark mode support with custom themes

Known Limitations

Current Implementation Constraints

  1. Cache Storage: Uses UserDefaults which has size limitations and security concerns
  2. Error Recovery: Limited retry mechanisms for network failures

Requirements

  • iOS 18.0+
  • Xcode 16.3+
  • Swift 6+

External Dependencies

  • Alamofire: provides sugar syntax for network requests which simplifies API calls specially on the small and medium size projects.

Result

You can find the screen recording here

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages