diff --git a/README.md b/README.md new file mode 100644 index 0000000..c9fff4b --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ + +

TorqueNet

+ +# Torquenet App +TorqueNet Auctions is a modern mobile platform that allows users to discover, bid on, and manage vehicle auctions in real time. The app provides a seamless auction experience where users can explore featured cars, view detailed vehicle specifications, track live bids, and place competitive bids directly from their mobile devices. + +The platform supports multiple auction states such as upcoming, live, completed, and featured auctions, allowing users to easily browse and filter vehicles based on their auction status. Each vehicle listing includes comprehensive details such as specifications, images, vehicle history, inspection reports, and bid history. + +Users can also save vehicles to their wishlist, track auctions they are interested in, and receive updates on bidding activity. Sellers are able to upload vehicles for auction, manage listings, and monitor bidding progress through a structured auction workflow. + +Built with SwiftUI and Firebase, the application leverages real-time data synchronization to ensure bids and auction updates are reflected instantly across users. + +Key Features +- Browse featured, upcoming, and live car auctions +- View detailed vehicle information and inspection reports +- Real-time bidding system +- Track recent bids and highest bids +- Save vehicles to a wishlist +- Upload vehicles for auction +- Filter and search auctions by status +- Secure user authentication and profile management + +## Structural design pattern +The app follows the Model-View-ViewModel (MVVM) pattern, enhanced with principles from Clean Architecture to ensure better separation of concerns and maintainability. +- Models hold the core data and business logic. These are typically lightweight structs or simple classes. +- Views handle the UI layer and display visual elements using SwiftUI. +- ViewModels serve as the bridge between views and models, transforming raw data into view-ready formats. They're implemented as classes to support state observation and reference passing. + +By combining MVVM with clean architecture layers (such as Use Cases, Repositories, and Services), the codebase stays modular, testable, and easy to scale as the app grows. + +### Auth Screen + + +### Home Screen + + +### Auction Screen + + +### Wishlist Screen + + + +### Settings Screen + + + +## 🛠️ Tech Stack + +- [**Swift**](https://developer.apple.com/swift/) + Swift is a powerful and intuitive programming language developed by Apple for building applications across iOS, macOS, watchOS, and tvOS. It is designed to be fast, safe, and modern, helping developers write reliable and maintainable code. + +- [**SwiftUI**](https://developer.apple.com/xcode/swiftui/) + SwiftUI is Apple’s declarative UI framework used to build user interfaces across all Apple platforms. It enables faster development with live previews, reusable components, and seamless integration with Swift. + +- [**Core Data**](https://developer.apple.com/documentation/coredata) Core Data is Apple’s framework for managing the model layer of applications. It provides powerful tools for object graph management and persistence, allowing developers to store and query data efficiently on-device. + +- [**Firebase Firestore**](https://firebase.google.com/docs/firestore) + Cloud Firestore is a flexible, scalable NoSQL cloud database from Firebase used to store and sync data in real time. In this app, it manages auctions, bids, vehicles, and user-generated data while keeping information synchronized across devices. + +- [**Firebase Authentication**](https://firebase.google.com/docs/auth) + Firebase Authentication provides secure user authentication services including email/password and other authentication methods. It is used in this app to manage user accounts, login, registration, and secure access to auction features. + +- [**Clean Architecture**](https://www.geeksforgeeks.org/system-design/complete-guide-to-clean-architecture/) + Clean Architecture organizes the codebase into independent layers such as presentation, domain, and data. This separation improves scalability, maintainability, and testability by enforcing clear boundaries between business logic and UI components. + +- [**Navigation in SwiftUI**](https://developer.apple.com/documentation/swiftui/navigationstack) + Navigation in SwiftUI is handled declaratively using `NavigationStack`, `NavigationPath`, and `NavigationLink`. These tools simplify screen transitions and allow the app to manage complex navigation flows in a structured and predictable way. + +- [**Core Location**](https://developer.apple.com/documentation/corelocation) + Core Location enables access to device location services. Using `CLLocationManager`, apps can request permission, retrieve location updates, and build features that depend on geographic information. + +- [**Swift Concurrency (Async/Await)**](https://developer.apple.com/documentation/swift/concurrency) + Swift’s modern concurrency model using `async/await` simplifies asynchronous programming. It allows network calls, database operations, and background tasks to run efficiently while keeping the code readable and easier to maintain. + +- [**App Theming in SwiftUI**](https://developer.apple.com/documentation/swiftui/environmentvalues/colorscheme) + App theming in SwiftUI allows dynamic styling based on system settings or user preferences. The app supports light and dark mode using `@Environment(\.colorScheme)` and a shared theme configuration to maintain a consistent UI across screens. + + +## Setup Requirements +- IOS device or Simulator +- XCode Editor + +## Getting Started +In order to get the app running yourself, you need to: + +1. Clone this project +2. Import the project into XCode +3. Get googleService info from firebase setup and add it to the codebase. +4. Connect your IOS device with USB or just start your Simulator +5. After the project has finished setting up it stuffs, click the run button + +## Support +- Found this project useful ❤️? Support by clicking the ⭐️ button on the upper right of this page. ✌️ +- Notice anything else missing? File an issue +- Feel free to contribute in any way to the project from typos in docs to code review are all welcome. diff --git a/TorqueNet.xcodeproj/project.xcworkspace/xcuserdata/brandyodhiambo.xcuserdatad/UserInterfaceState.xcuserstate b/TorqueNet.xcodeproj/project.xcworkspace/xcuserdata/brandyodhiambo.xcuserdatad/UserInterfaceState.xcuserstate index ea7fc35..23f23be 100644 Binary files a/TorqueNet.xcodeproj/project.xcworkspace/xcuserdata/brandyodhiambo.xcuserdatad/UserInterfaceState.xcuserstate and b/TorqueNet.xcodeproj/project.xcworkspace/xcuserdata/brandyodhiambo.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/TorqueNet.xcodeproj/project.xcworkspace/xcuserdata/mac.xcuserdatad/UserInterfaceState.xcuserstate b/TorqueNet.xcodeproj/project.xcworkspace/xcuserdata/mac.xcuserdatad/UserInterfaceState.xcuserstate index f412e9b..2b18e67 100644 Binary files a/TorqueNet.xcodeproj/project.xcworkspace/xcuserdata/mac.xcuserdatad/UserInterfaceState.xcuserstate and b/TorqueNet.xcodeproj/project.xcworkspace/xcuserdata/mac.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/TorqueNet/Assets.xcassets/AppIcon.appiconset/1024 1.png b/TorqueNet/Assets.xcassets/AppIcon.appiconset/1024 1.png new file mode 100644 index 0000000..516d67a Binary files /dev/null and b/TorqueNet/Assets.xcassets/AppIcon.appiconset/1024 1.png differ diff --git a/TorqueNet/Assets.xcassets/AppIcon.appiconset/1024 2.png b/TorqueNet/Assets.xcassets/AppIcon.appiconset/1024 2.png new file mode 100644 index 0000000..516d67a Binary files /dev/null and b/TorqueNet/Assets.xcassets/AppIcon.appiconset/1024 2.png differ diff --git a/TorqueNet/Assets.xcassets/AppIcon.appiconset/1024.png b/TorqueNet/Assets.xcassets/AppIcon.appiconset/1024.png new file mode 100644 index 0000000..516d67a Binary files /dev/null and b/TorqueNet/Assets.xcassets/AppIcon.appiconset/1024.png differ diff --git a/TorqueNet/Assets.xcassets/AppIcon.appiconset/Contents.json b/TorqueNet/Assets.xcassets/AppIcon.appiconset/Contents.json index 2305880..a595294 100644 --- a/TorqueNet/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/TorqueNet/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,6 +1,7 @@ { "images" : [ { + "filename" : "1024.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" @@ -12,6 +13,7 @@ "value" : "dark" } ], + "filename" : "1024 1.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" @@ -23,6 +25,7 @@ "value" : "tinted" } ], + "filename" : "1024 2.png", "idiom" : "universal", "platform" : "ios", "size" : "1024x1024" diff --git a/TorqueNet/Features/Auction/Presentation/UploadAuction/AuctionUploadView.swift b/TorqueNet/Features/Auction/Presentation/UploadAuction/AuctionUploadView.swift index 24400fd..64a2c6a 100644 --- a/TorqueNet/Features/Auction/Presentation/UploadAuction/AuctionUploadView.swift +++ b/TorqueNet/Features/Auction/Presentation/UploadAuction/AuctionUploadView.swift @@ -193,7 +193,7 @@ struct AuctionUploadView: View { placeHolder: "85500", text: $uploadAuctionViewModel.form.startingBid, foregroundColor: .theme.surfaceColor, - backgroundColor: .theme.onSurfaceColor.opacity(0.1), + backgroundColor: .theme.onSurfaceColor, keyboardType: .numberPad, inputFieldStyle: .outlined, onTextChange: {text in diff --git a/TorqueNet/Features/AuctionManagement/Presentation/ManageAuctionUiState.swift b/TorqueNet/Features/AuctionManagement/Presentation/ManageAuctionUiState.swift index a1fc0f5..8c0695f 100644 --- a/TorqueNet/Features/AuctionManagement/Presentation/ManageAuctionUiState.swift +++ b/TorqueNet/Features/AuctionManagement/Presentation/ManageAuctionUiState.swift @@ -9,10 +9,7 @@ struct ManageAuctionUiState { var auctions: [ManageAuctionItem] = [] var filteredAuctions: [ManageAuctionItem] = [] var selectedFilter: AuctionStatus? = nil - - // var isLoading: Bool = false var errorMessage: String = "" - // var hasError: Bool = false var showError: Bool = false var auctionState: FetchState = .good var showEditSheet: Bool = false diff --git a/TorqueNet/Features/Notification/Domain/Model/AuctionNotification.swift b/TorqueNet/Features/Notification/Domain/Model/AuctionNotification.swift new file mode 100644 index 0000000..c518228 --- /dev/null +++ b/TorqueNet/Features/Notification/Domain/Model/AuctionNotification.swift @@ -0,0 +1,27 @@ +// +// AuctionNotification.swift +// TorqueNet +// +// Created by MAC on 11/03/2026. +// + +import Foundation + +struct AuctionNotification: Identifiable { + let id = UUID() + let type: NotificationType + let title: String + let message: String + let timestamp: Date + var isRead: Bool = false + let bidAmount: Double? + let auctionId: String + + var timeAgo: String { + let formatter = RelativeDateTimeFormatter() + formatter.unitsStyle = .short + return formatter.localizedString(for: timestamp, relativeTo: Date()) + } +} + + diff --git a/TorqueNet/Features/Notification/Presentation/NotificationsView.swift b/TorqueNet/Features/Notification/Presentation/NotificationsView.swift index 26ef69b..90a22d2 100644 --- a/TorqueNet/Features/Notification/Presentation/NotificationsView.swift +++ b/TorqueNet/Features/Notification/Presentation/NotificationsView.swift @@ -7,6 +7,8 @@ import SwiftUI +//MARK:TO BE IMPLEMENTED WE NEED APNS + struct NotificationsView: View { @State private var notifications: [AuctionNotification] = sampleNotifications @State private var selectedFilter: NotificationFilter = .all @@ -119,7 +121,6 @@ struct NotificationsView: View { } } -// MARK: - Notification Card struct NotificationCard: View { let notification: AuctionNotification let onTap: () -> Void @@ -205,7 +206,6 @@ struct NotificationCard: View { } } -// MARK: - Filter Tab struct FilterTab: View { let title: String let count: Int @@ -243,7 +243,6 @@ struct FilterTab: View { } } -// MARK: - In-App Notification Banner struct NotificationBanner: View { let notification: AuctionNotification @State private var isVisible = false @@ -252,7 +251,6 @@ struct NotificationBanner: View { var body: some View { HStack(spacing: 12) { - // Icon ZStack { Circle() .fill(notification.type.color) @@ -263,7 +261,6 @@ struct NotificationBanner: View { .foregroundColor(.white) } - // Content VStack(alignment: .leading, spacing: 2) { Text(notification.title) .font(.custom("Exo2-SemiBold", size: 14)) @@ -317,24 +314,6 @@ struct NotificationBanner: View { NotificationsView() } -// MARK: - Data Models -struct AuctionNotification: Identifiable { - let id = UUID() - let type: NotificationType - let title: String - let message: String - let timestamp: Date - var isRead: Bool = false - let bidAmount: Double? - let auctionId: String - - var timeAgo: String { - let formatter = RelativeDateTimeFormatter() - formatter.unitsStyle = .short - return formatter.localizedString(for: timestamp, relativeTo: Date()) - } -} - enum NotificationType: CaseIterable { case ongoing case newAuction @@ -404,6 +383,7 @@ enum NotificationFilter: CaseIterable { } } + // MARK: - Sample Data let sampleNotifications: [AuctionNotification] = [ AuctionNotification( diff --git a/TorqueNet/Features/Schedule/Presentation/ScheduleAuctionViewModel.swift b/TorqueNet/Features/Schedule/Presentation/ScheduleAuctionViewModel.swift index 859055e..039b0e4 100644 --- a/TorqueNet/Features/Schedule/Presentation/ScheduleAuctionViewModel.swift +++ b/TorqueNet/Features/Schedule/Presentation/ScheduleAuctionViewModel.swift @@ -27,7 +27,6 @@ class ScheduledAuctionViewModel: ObservableObject { let message = error.errorDescription?.description ?? "An unexpected error occurred." uiState.auctionState = .error(message) uiState.errorMessage = message - //onFailure(message) } } } diff --git a/project/128.png b/project/128.png new file mode 100644 index 0000000..f1fa710 Binary files /dev/null and b/project/128.png differ diff --git a/project/Auction Details.png b/project/Auction Details.png new file mode 100644 index 0000000..9a20619 Binary files /dev/null and b/project/Auction Details.png differ diff --git a/project/Auctions.png b/project/Auctions.png new file mode 100644 index 0000000..15b6b18 Binary files /dev/null and b/project/Auctions.png differ diff --git a/project/Home.png b/project/Home.png new file mode 100644 index 0000000..fb83d9c Binary files /dev/null and b/project/Home.png differ diff --git a/project/ResetPassword.png b/project/ResetPassword.png new file mode 100644 index 0000000..0b7b83e Binary files /dev/null and b/project/ResetPassword.png differ diff --git a/project/Settings.png b/project/Settings.png new file mode 100644 index 0000000..6c3f272 Binary files /dev/null and b/project/Settings.png differ diff --git a/project/SignIn.png b/project/SignIn.png new file mode 100644 index 0000000..62ccddc Binary files /dev/null and b/project/SignIn.png differ diff --git a/project/SignUp.png b/project/SignUp.png new file mode 100644 index 0000000..32b6a43 Binary files /dev/null and b/project/SignUp.png differ diff --git a/project/Upload Auction.png b/project/Upload Auction.png new file mode 100644 index 0000000..575b35e Binary files /dev/null and b/project/Upload Auction.png differ diff --git a/project/Wishlist.png b/project/Wishlist.png new file mode 100644 index 0000000..b0738ac Binary files /dev/null and b/project/Wishlist.png differ