Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
5364f89
feat: window rootViewController and navigation flow
TecoAdamo Mar 4, 2026
56fb7da
feat: implement FlowController to manage app navigation
TecoAdamo Mar 4, 2026
7ec51e9
feat: implement UI screens and custom components
TecoAdamo Mar 4, 2026
4e60836
feat: implement ViewModels for home and quiz logic
TecoAdamo Mar 4, 2026
713c607
feat: implement APIService for fetching quiz data
TecoAdamo Mar 4, 2026
3326264
feat: add data models for API response mapping
TecoAdamo Mar 4, 2026
60a7ea2
feat: implement MainTabBarController and navigation hierarchy
TecoAdamo Mar 4, 2026
fdae283
feat: setup AppDelegate and app lifecycle entry point
TecoAdamo Mar 4, 2026
bef2b41
chore: add CocoaPods configuration files
TecoAdamo Mar 4, 2026
8d9ce08
chore: add gitignore and final adjustments to SceneDelegate
TecoAdamo Mar 4, 2026
c5a6156
refactor: move APIService to Core/Network
TecoAdamo Mar 6, 2026
14a1641
feat: add loading state to Quiz flow
TecoAdamo Mar 6, 2026
62dd378
refactor: update ProfileViewModel
TecoAdamo Mar 6, 2026
258126d
refactor: cleanup and update remaining files
TecoAdamo Mar 6, 2026
53ac1de
fix: add AccentColor to assets
TecoAdamo Mar 7, 2026
6bf0de1
fix: resolve MainActor-isolated warnings in ProfileView
TecoAdamo Mar 7, 2026
f731a03
fix: add inverse relationship in Core Data entities
TecoAdamo Mar 7, 2026
d0ca180
test: reorganize and update quiz view model tests
TecoAdamo Mar 7, 2026
b557c0c
refactor: update network and repository layers
TecoAdamo Mar 7, 2026
9bf5f3a
refactor: update profile and quiz view models
TecoAdamo Mar 7, 2026
ec0f476
chore: update pods and project configuration
TecoAdamo Mar 7, 2026
1b88342
Create README.md for DynamoxQuiz project
TecoAdamo Mar 8, 2026
fd920ec
Update screenshots in README with new links
TecoAdamo Mar 8, 2026
264275f
Enhance README with architecture information
TecoAdamo Mar 8, 2026
ae7caa5
Update unit test descriptions in README.md
TecoAdamo Mar 8, 2026
32a6a67
Update image widths in README.md
TecoAdamo Mar 8, 2026
e3afdf3
Adjust image widths in README.md
TecoAdamo Mar 8, 2026
a4935a1
Adjust screenshot image widths to 180px
TecoAdamo Mar 8, 2026
30b3566
Update installation instructions in README.md
TecoAdamo Mar 8, 2026
ae6e113
feat: add ToastMessageComponent
TecoAdamo Mar 9, 2026
f320102
feat: update home and quiz view controllers
TecoAdamo Mar 9, 2026
48a6766
feat: update home and quiz viewmodels
TecoAdamo Mar 9, 2026
9f1c277
feat: update api service
TecoAdamo Mar 9, 2026
47aabd9
test: update quiz viewmodel unit tests
TecoAdamo Mar 9, 2026
39c41da
chore: update podfile and project config
TecoAdamo Mar 9, 2026
992b210
docs: update README with missing test description
TecoAdamo Mar 9, 2026
cc84f05
fix: fixing toast message
TecoAdamo Mar 9, 2026
bc4a80d
refactor: coordinator owns window and simplifies navigation flow
TecoAdamo Mar 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# CocoaPods
Pods/

# Xcode pessoal e temporários
.DS_Store
DerivedData/
*.xcuserstate
618 changes: 618 additions & 0 deletions DynamoxQuiz.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SchemeUserState</key>
<dict>
<key>DynamoxQuiz.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>6</integer>
</dict>
</dict>
</dict>
</plist>
10 changes: 10 additions & 0 deletions DynamoxQuiz.xcworkspace/contents.xcworkspacedata

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array/>
</plist>
36 changes: 36 additions & 0 deletions DynamoxQuiz/App/AppDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// AppDelegate.swift
// DynamoxQuiz
//
// Created by Mateus on 02/03/26.
//

import UIKit

@main
class AppDelegate: UIResponder, UIApplicationDelegate {



func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}

// MARK: UISceneSession Lifecycle

func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}

func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}


}

68 changes: 68 additions & 0 deletions DynamoxQuiz/App/SceneDelegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// SceneDelegate.swift
// DynamoxQuiz
//
// Created by Mateus on 02/03/26.
//

import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

var window: UIWindow?

let profileViewModel = ProfileViewModel()
var flowController: AppCoordinator?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }

let window = UIWindow(windowScene: windowScene)
self.window = window

window.rootViewController = SplashViewController()
window.makeKeyAndVisible()

// SceneDelegate
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
self.flowController = AppCoordinator(profileViewModel: self.profileViewModel)

if let navController = self.flowController?.start() {
UIView.transition(with: window, duration: 0.5, options: .transitionCrossDissolve, animations: {
window.rootViewController = navController
})
}
}
}

func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
}

func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}

func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
}

func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}

func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
}


}

27 changes: 27 additions & 0 deletions DynamoxQuiz/Assets.xcassets/AccentColor.colorset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0.659",
"green" : "0.780",
"red" : "0.098"
}
},
"idiom" : "universal"
},
{
"idiom" : "universal",
"locale" : "en"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"localizable" : true
}
}
14 changes: 14 additions & 0 deletions DynamoxQuiz/Assets.xcassets/AppIcon.appiconset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"images" : [
{
"filename" : "Design sem nome-8.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions DynamoxQuiz/Assets.xcassets/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}
12 changes: 12 additions & 0 deletions DynamoxQuiz/Assets.xcassets/LogoSplash.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "Design sem nome-8.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 39 additions & 0 deletions DynamoxQuiz/Components/Button/ButtonViewComponent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// ButtonViewComponent.swift
// DynamoxQuiz
//
// Created by Mateus on 02/03/26.
//

import UIKit

class ButtonViewComponent: UIButton {

private var action: (() -> Void)?

init(title: String){
super.init(frame: .zero)

setTitle(title, for: .normal)
setTitleColor(.white, for: .normal)
titleLabel?.font = .systemFont(ofSize: 16, weight: .bold)
backgroundColor = Colors.primaryGreenBase
layer.cornerRadius = 6
translatesAutoresizingMaskIntoConstraints = false

addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
}

func setAction(_ action: @escaping () -> Void){
self.action = action
}


@objc private func buttonTapped() {
action?()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
35 changes: 35 additions & 0 deletions DynamoxQuiz/Components/Cards/CardViewComponent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// CardViewComponent.swift
// DynamoxQuiz
//
// Created by Mateus on 05/03/26.
//

import SwiftUI

struct CardViewComponent: View {

let title: String
let value: String

var body: some View {
HStack {
VStack {
Text(title)
.font(.headline)
.bold()
Text(value)
.font(.subheadline)
}
.frame(width: 100, height: 80)
.background(Color.white)
.clipShape(RoundedRectangle(cornerRadius: 12))
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(Color(Colors.primaryGreenBase), lineWidth: 1)
)
.shadow(color: Color.black.opacity(0.05), radius: 5, x: 0, y: 2)
}
.padding(.horizontal, 8)
}
}
70 changes: 70 additions & 0 deletions DynamoxQuiz/Components/Cards/CardViewHistoryComponent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//
// CardViewHistoryComponent.swift
// DynamoxQuiz
//
// Created by Mateus on 05/03/26.
//

import SwiftUI

struct CardViewHistoryComponent: View {

let icon: String
let title: String
let date: String
let store: String

var body: some View {

HStack(spacing: 12) {

Image(systemName: icon)
.resizable()
.scaledToFit()
.frame(width: 34, height: 34)
.padding(12)
.foregroundStyle(Color.white)
.background(Color(Colors.primaryGreenBase))
.cornerRadius(.infinity)

VStack(alignment: .leading, spacing: 4) {
Text(title)
.font(.subheadline)
.bold()
Text(date)
.font(.caption)
.foregroundStyle(.gray)
}

Spacer()

VStack(spacing: 2) {

Text(store)
.font(.title3)
.bold()
.foregroundStyle(Color(Colors.primaryGreenBase))

Text("acerto")
.font(.caption2)
.foregroundStyle(.gray)
}

.padding(.horizontal, 10)
.padding(.vertical, 6)
.background(Color(Colors.primaryGreenBase).opacity(0.1))
.clipShape(RoundedRectangle(cornerRadius: 8))

}
.padding()
.background(Color.white)
.clipShape(RoundedRectangle(cornerRadius: 12))
.overlay(
RoundedRectangle(cornerRadius: 12)
.stroke(Color(Colors.primaryGreenBase).opacity(0.5), lineWidth: 1)
)
.shadow(color: Color.black.opacity(0.05), radius: 5, x: 0, y: 2)
.padding(.horizontal)

}
}
33 changes: 33 additions & 0 deletions DynamoxQuiz/Components/Profile/ProfileImageComponent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// ProfileImageComponent.swift
// DynamoxQuiz
//
// Created by Mateus on 07/03/26.
//

import SwiftUI

struct ProfileImageView: View {
let image: UIImage?

var body: some View {
if let image {
Image(uiImage: image)
.resizable()
.scaledToFill()
.frame(width: 120, height: 120)
.clipShape(Circle())
.overlay(
Circle()
.stroke(Color(Colors.primaryGreenBase), lineWidth: 3)
)
} else {
Image(systemName: "person.circle.fill")
.resizable()
.scaledToFill()
.frame(width: 120, height: 120)
.clipShape(Circle())
.foregroundStyle(Color(Colors.primaryGreenBase))
}
}
}
Loading