Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
48cc892
Add rules file
SwiftEnProfundidad Apr 15, 2025
14d0dcc
Información sensible a proteger
SwiftEnProfundidad Apr 15, 2025
1060d89
✅ Pruebas unitarias completas que validan todos los escenarios del do…
SwiftEnProfundidad Apr 15, 2025
c3000c0
set config project file
SwiftEnProfundidad Apr 15, 2025
701b39d
[Security][BDD] Añade test unitario con spy y refactoriza KeychainSec…
SwiftEnProfundidad Apr 15, 2025
6d6c8bf
Implementación real de KeychainSecureStorage usando Keychain de iOS. …
SwiftEnProfundidad Apr 15, 2025
85b8ffe
Remove duplicate KeychainProtocol declarations. Now only one protocol…
SwiftEnProfundidad Apr 15, 2025
1744823
Refactor KeychainSecureStorage: Null Object fallback, SOLID, TDD. All…
SwiftEnProfundidad Apr 15, 2025
9308be1
[Security][BDD] Complete SecureStorage use case with fallback & alter…
SwiftEnProfundidad Apr 15, 2025
44a2b8b
feat: ✅ Completa caso de uso 'Almacenamiento Seguro' (SecureStorage)
SwiftEnProfundidad Apr 15, 2025
d6a8263
Implement minimal UserRegistrationUseCase logic to pass user registra…
SwiftEnProfundidad Apr 15, 2025
b9ef90a
✅ [Registro de Usuario] Almacenamiento seguro en Keychain implementad…
SwiftEnProfundidad Apr 15, 2025
7dc5471
✅ [User Registration] Secure Keychain storage implemented and verifie…
SwiftEnProfundidad Apr 15, 2025
1cd9841
✅ Update BDD: technical checklist now uses only status emojis for dev…
SwiftEnProfundidad Apr 15, 2025
c883b26
Document HTTPClient integration test pattern, update BDD: all HTTPCli…
SwiftEnProfundidad Apr 16, 2025
74c6078
Refactor Keychain protocol and implementation with clean architecture…
SwiftEnProfundidad Apr 16, 2025
6da4ac8
[Registro de usuario] Subtarea completada: envío de solicitud de regi…
SwiftEnProfundidad Apr 16, 2025
703f168
✅ User registration BDD: tests pass, validation and secure credential…
SwiftEnProfundidad Apr 16, 2025
060a6b2
[BDD][Registration] Sad path for already registered email covered and…
SwiftEnProfundidad Apr 16, 2025
ba03661
Docs: add key documentation section with links to BDD and technical r…
SwiftEnProfundidad Apr 16, 2025
8cfe057
[Registration][BDD] Add sad path test for connectivity error and upda…
SwiftEnProfundidad Apr 16, 2025
73f906a
[Registration][BDD] ✅ Sad path: test y resultado para error de conect…
SwiftEnProfundidad Apr 16, 2025
449ff2b
[Registration][Infra][BDD] ✅ Separación de código producción/tests y …
SwiftEnProfundidad Apr 16, 2025
0a821a0
[Registration][BDD] ✅ Notificar que el correo ya está en uso (UserReg…
SwiftEnProfundidad Apr 16, 2025
602db7b
refactor: simplify external provider authentication flow diagram for …
SwiftEnProfundidad Apr 17, 2025
552e44d
refactor: simplify external provider authentication flow diagram for …
SwiftEnProfundidad Apr 17, 2025
492f811
feat(tests): async robustness for already-registered email notification
SwiftEnProfundidad Apr 19, 2025
8c1670b
[Keychain][BDD] Full coverage: edge case tests with spies and BDD syn…
SwiftEnProfundidad Apr 19, 2025
b35f33c
[Keychain][BDD] Add unicode/large data test, update BDD sync, next: c…
SwiftEnProfundidad Apr 19, 2025
d75032e
docs: update BDD to reflect full Keychain secure storage coverage, in…
SwiftEnProfundidad Apr 19, 2025
c6cd5ee
Keychain: dependency injection, delete integration, spy for tests, an…
SwiftEnProfundidad Apr 19, 2025
37ee7f0
docs(tests): update BDD and coverage-summary with advanced Keychain t…
SwiftEnProfundidad Apr 19, 2025
c83f1b3
Mejora trazabilidad de cobertura: ahora la columna 'Test que lo cubre…
SwiftEnProfundidad Apr 19, 2025
34665d3
✅ Añadidos tests que cubren la implementación real de SystemKeychain …
SwiftEnProfundidad Apr 19, 2025
d77b80b
Unify and professionalize BDD documentation: checklist and traceabili…
SwiftEnProfundidad Apr 20, 2025
9674f80
Unify and professionalize BDD documentation: checklist and traceabili…
SwiftEnProfundidad Apr 20, 2025
df78f6a
[Docs][Keychain] Add professional disclaimer: prefer macOS target for…
SwiftEnProfundidad Apr 20, 2025
d1d3832
Refactor Keychain test helpers: centralize makeKeychainFullSpy, expos…
SwiftEnProfundidad Apr 20, 2025
e0b5b0e
[Keychain][Test] Fix deadlocks in KeychainFullSpy hooks, enforce corr…
SwiftEnProfundidad Apr 20, 2025
c10aefe
Fix Keychain error simulation in spy and test: test_save_handlesSpeci…
SwiftEnProfundidad Apr 21, 2025
62580d0
Fix memory leak in KeychainFullSpy test: willValidateAfterSave now ca…
SwiftEnProfundidad Apr 21, 2025
f1f0697
KeychainSpy: thread-safe, leak-free, robust post-save validation. All…
SwiftEnProfundidad Apr 21, 2025
81258f7
KeychainSpy: thread-safe, leak-free, robust post-save validation. All…
SwiftEnProfundidad Apr 21, 2025
8515b38
chore: snapshot before increasing SystemKeychain coverage (pre-cobert…
SwiftEnProfundidad Apr 21, 2025
d2588d0
Revert status
SwiftEnProfundidad Apr 21, 2025
57c146d
Add coverage test for SystemKeychain.update(data:forKey:) (success/er…
SwiftEnProfundidad Apr 21, 2025
da3147a
Add test for SystemKeychain.delete(forKey:) covering success and erro…
SwiftEnProfundidad Apr 21, 2025
9ce3c7c
Add tests for _save (validation) and NoFallback (always fails), maint…
SwiftEnProfundidad Apr 21, 2025
9116d34
Add test for handleDuplicateItem max attempts (SystemKeychain), clean…
SwiftEnProfundidad Apr 21, 2025
af279c9
Refactor SystemKeychain _update test: only validate empty key/data, r…
SwiftEnProfundidad Apr 21, 2025
2841212
Remove redundant SystemKeychain tests that did not increase code cove…
SwiftEnProfundidad Apr 21, 2025
1333857
Refactor: Unificación profesional de checklist técnico y tabla de tra…
SwiftEnProfundidad Apr 21, 2025
d81cb18
docs: resumen de implementación con columna de estado solo emoji (ali…
SwiftEnProfundidad Apr 21, 2025
cadaff8
docs: actualización masiva de BDD-Security-Features.md con tablas pro…
SwiftEnProfundidad Apr 21, 2025
39c2af1
Increase SystemKeychain.swift coverage: tests now exercise NoFallback…
SwiftEnProfundidad Apr 21, 2025
8ac958c
chore(project): all Xcode targets and signing configuration are corre…
SwiftEnProfundidad Apr 21, 2025
ef42443
Fix code signing: set CODE_SIGN_IDENTITY to empty for EssentialApp an…
SwiftEnProfundidad Apr 21, 2025
b29ef64
Green build: all Keychain and SecureStorage tests passing after envir…
SwiftEnProfundidad Apr 21, 2025
2a0c1bd
[Docs][Coverage] Align test coverage workflow for SystemKeychain: rem…
SwiftEnProfundidad Apr 21, 2025
7734d6a
[Docs][Coverage] Add professional note on Keychain CLI coverage limit…
SwiftEnProfundidad Apr 21, 2025
24b54a6
refactor: tabla HTML de cobertura dinámica, robusta y profesional (só…
SwiftEnProfundidad Apr 22, 2025
f2d651b
refactor: cobertura HTML dinámica y robusta, limpieza y mejoras varias
SwiftEnProfundidad Apr 22, 2025
34c7ed8
Move UserLoginUseCase to Authentication Feature folder
SwiftEnProfundidad Apr 22, 2025
01a97cb
Move UserLoginUseCase to Authentication Feature folder
SwiftEnProfundidad Apr 22, 2025
e816ec6
Fix indentation and formatting in multiple files
SwiftEnProfundidad Apr 22, 2025
b76dfeb
Add edge case tests and improve test coverage for SystemKeychain oper…
SwiftEnProfundidad Apr 22, 2025
5af8dbf
Add SystemKeychain integration tests and improve test coverage for er…
SwiftEnProfundidad Apr 22, 2025
4a9f42c
Refactor SystemKeychain: remove code duplication, pyramid of doom, an…
SwiftEnProfundidad Apr 22, 2025
8a6dce3
Add professional test coverage summary to project and docs: coverage,…
SwiftEnProfundidad Apr 22, 2025
e87b3f6
Remove coverage_html_latest directory
SwiftEnProfundidad Apr 22, 2025
00581f7
Update security features documentation with clearer status legend and…
SwiftEnProfundidad Apr 22, 2025
704bbc6
Update login checklist marking session registration as in progress
SwiftEnProfundidad Apr 22, 2025
295e9d0
Add SessionManaging protocol with register session capability
SwiftEnProfundidad Apr 23, 2025
0a748ac
Add SessionManaging protocol with test spy for registering user sessions
SwiftEnProfundidad Apr 23, 2025
7822cc8
Add SystemSessionManager with store delegation and tests
SwiftEnProfundidad Apr 23, 2025
4da3fa1
Fix code indentation and add BDD checklist for session management
SwiftEnProfundidad Apr 23, 2025
6879b7d
Add login success notification acceptance criteria and test strategy
SwiftEnProfundidad Apr 23, 2025
8db56f6
Add LoginPresenter with success and error clearing view protocols
SwiftEnProfundidad Apr 23, 2025
5bf3569
Add architectural decision notes and translate docs to English
SwiftEnProfundidad Apr 23, 2025
5f53f1b
Add authentication flow with login and composer setup
SwiftEnProfundidad Apr 26, 2025
ba7bf7c
Remove iOS+AI Master Guide documentation file
SwiftEnProfundidad Apr 26, 2025
bf906bc
Reorganize Authentication Feature files and update login UI implement…
SwiftEnProfundidad Apr 26, 2025
6fe3c72
Rename LoginPresentingProtocols to LoginPresentation for better namin…
SwiftEnProfundidad Apr 27, 2025
8191a90
Make LoginPresenter views optional and add test coverage for nil views
SwiftEnProfundidad Apr 27, 2025
a116f30
Improve login presenter test documentation with clearer BDD checklist
SwiftEnProfundidad Apr 27, 2025
ca994fc
Add email format validation to login use case with tests
SwiftEnProfundidad Apr 27, 2025
43cdefa
Add email format validation with error message for invalid login atte…
SwiftEnProfundidad Apr 27, 2025
f44a28b
Add password validation with minimum 8 character requirement
SwiftEnProfundidad Apr 27, 2025
2c8a3a3
Add login error message mapper with user-facing messages for each err…
SwiftEnProfundidad Apr 27, 2025
2fff8fd
Add login integration tests and improve validation with whitespace ha…
SwiftEnProfundidad Apr 27, 2025
d5ef28a
Mark validation tests as completed in BDD security checklist
SwiftEnProfundidad Apr 27, 2025
d51f1b6
Add CI workflow for macOS with unit and integration tests
SwiftEnProfundidad Apr 27, 2025
e532b60
Merge branch 'master' into feature/AuthModule
SwiftEnProfundidad Apr 27, 2025
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
21 changes: 21 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: EssentialFeed CI

on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]

jobs:
build-and-test:
runs-on: macos-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Xcode
uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: latest
- name: Build and test (unit + integración)
run: |
xcodebuild test -project EssentialFeed/EssentialFeed.xcodeproj -scheme CI_macOS -destination 'platform=macOS' | xcpretty
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Ignorar archivos de sistema de macOS
.DS_Store

# Created by https://www.gitignore.io/api/swift,xcode

### Swift ###
Expand Down Expand Up @@ -91,4 +94,7 @@ fastlane/test_output
!*.xcworkspace/contents.xcworkspacedata
/*.gcno

/coverage-reports/*
!/coverage-reports/README.md

# End of https://www.gitignore.io/api/swift,xcode
6 changes: 6 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"sweetpad.build.xcodeWorkspacePath": "EssentialApp/EssentialApp.xcworkspace",
"window.autoDetectColorScheme": true,
"window.confirmSaveUntitledWorkspace": true,
"interactiveWindow.promptToSaveOnClose": true
}
80 changes: 80 additions & 0 deletions .windsurfrules
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<communication>
Speak to me in Spanish for explanation
My name is Carlos
You are a senior developer
You are a senior software engineer
You are a senior software designer
You are a senior software architect
</communication>

<language>
I work with the Spanish for explanation
I work code in English
</language>

<framework>
I work with the SwiftUI
I work with the UIKit
I work with the Combine
I work with the Foundation
I work with the XCTest
I work with the Core Data
</framework>

<tools>
I work with the Xcode
I work with the Git
I work with the GitHub
I work with the CI
I work with the Git Actions
I work with the CD
</tools>

<coding>
Don't use any APIs that require authorization
Don't use any APIs third party
My project's programming language is swift
Use XCTest as my test framework
I work with BDD and TDD
I work with clean architecture
I work with SOLID principles
I work with Single Responsibility Principle
I work with Open/Closed Principle
I work with Liskov Substitution Principle
I work with Interface Segregation Principle
I work with Dependency Inversion Principle
I work with Command Query Separation
I work with loose coupling
I work with high cohesion
I work with design patterns
I work with reactive programming in the UI
I work with the modular architecture
I work with the dependency injection
</coding>

<update>
Update the code
Update the tests
Update the documentation
Update the README
Update the UI
Update the UI tests
Update the UI screenshots
Update the UI screenshots tests
Update xcode project
Update xcode project settings
Update xcode project scheme
Update xcode project configuration
Update xcworkspace
Update xcconfig
Update xcodeproj
</update>

<testing>
I work with the unit testing
I work with the integration testing
I work with the UI testing
I work with the end to end testing
I work with the snapshot testing
I work with the acceptance criteria testing
</testing>
Binary file added EssentialApp/.DS_Store
Binary file not shown.
62 changes: 49 additions & 13 deletions EssentialApp/EssentialApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
0895DAAC234B3F7E0031BB2D /* EssentialFeed.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0895DAA9234B3F7E0031BB2D /* EssentialFeed.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
0895DAAD234B3F7E0031BB2D /* EssentialFeediOS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0895DAAA234B3F7E0031BB2D /* EssentialFeediOS.framework */; };
0895DAAE234B3F7E0031BB2D /* EssentialFeediOS.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0895DAAA234B3F7E0031BB2D /* EssentialFeediOS.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
08CF92F62555A15B006B7E7D /* NullStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08CF92F52555A15B006B7E7D /* NullStore.swift */; };
B449F2082DBD6ABC0078F27B /* LoginComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B449F2032DBD6ABC0078F27B /* LoginComposer.swift */; };
B449F2092DBD6ABC0078F27B /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B449F2052DBD6ABC0078F27B /* LoginView.swift */; };
B449F20A2DBD6ABC0078F27B /* AuthComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B449F2022DBD6ABC0078F27B /* AuthComposer.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -100,6 +104,10 @@
08B5033725346BAC003FF218 /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/LaunchScreen.strings; sourceTree = "<group>"; };
08B5033925346BE1003FF218 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/LaunchScreen.strings"; sourceTree = "<group>"; };
08B5033B25346BFE003FF218 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/LaunchScreen.strings; sourceTree = "<group>"; };
08CF92F52555A15B006B7E7D /* NullStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NullStore.swift; sourceTree = "<group>"; };
B449F2022DBD6ABC0078F27B /* AuthComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthComposer.swift; sourceTree = "<group>"; };
B449F2032DBD6ABC0078F27B /* LoginComposer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginComposer.swift; sourceTree = "<group>"; };
B449F2052DBD6ABC0078F27B /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -164,6 +172,7 @@
0895DA85234B3B950031BB2D /* EssentialApp */ = {
isa = PBXGroup;
children = (
B449F2072DBD6ABC0078F27B /* Authentication Feature */,
0895DA86234B3B950031BB2D /* AppDelegate.swift */,
0895DA88234B3B950031BB2D /* SceneDelegate.swift */,
0835BF6C24850F9800A793D2 /* CombineHelpers.swift */,
Expand Down Expand Up @@ -201,6 +210,32 @@
name = Frameworks;
sourceTree = "<group>";
};
B449F2042DBD6ABC0078F27B /* Composition */ = {
isa = PBXGroup;
children = (
B449F2022DBD6ABC0078F27B /* AuthComposer.swift */,
B449F2032DBD6ABC0078F27B /* LoginComposer.swift */,
);
path = Composition;
sourceTree = "<group>";
};
B449F2062DBD6ABC0078F27B /* UI */ = {
isa = PBXGroup;
children = (
B449F2052DBD6ABC0078F27B /* LoginView.swift */,
);
path = UI;
sourceTree = "<group>";
};
B449F2072DBD6ABC0078F27B /* Authentication Feature */ = {
isa = PBXGroup;
children = (
B449F2042DBD6ABC0078F27B /* Composition */,
B449F2062DBD6ABC0078F27B /* UI */,
);
path = "Authentication Feature";
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down Expand Up @@ -248,7 +283,7 @@
attributes = {
BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 1120;
LastUpgradeCheck = 1620;
LastUpgradeCheck = 1630;
ORGANIZATIONNAME = "Essential Developer";
TargetAttributes = {
0895DA82234B3B950031BB2D = {
Expand Down Expand Up @@ -308,6 +343,9 @@
files = (
08073B44238D2DFA00A75DC6 /* FeedUIComposer.swift in Sources */,
0895DA87234B3B950031BB2D /* AppDelegate.swift in Sources */,
B449F2082DBD6ABC0078F27B /* LoginComposer.swift in Sources */,
B449F2092DBD6ABC0078F27B /* LoginView.swift in Sources */,
B449F20A2DBD6ABC0078F27B /* AuthComposer.swift in Sources */,
08073B45238D2DFA00A75DC6 /* LoadResourcePresentationAdapter.swift in Sources */,
08073B48238D2DFA00A75DC6 /* WeakRefVirtualProxy.swift in Sources */,
0895DA89234B3B950031BB2D /* SceneDelegate.swift in Sources */,
Expand Down Expand Up @@ -403,6 +441,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = Z273P8K3M7;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
Expand Down Expand Up @@ -468,6 +507,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = Z273P8K3M7;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
Expand All @@ -493,9 +533,8 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = VRJ2W4578X;
INFOPLIST_FILE = EssentialApp/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
Expand All @@ -517,21 +556,16 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_IDENTITY = "Apple Distribution";
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = VRJ2W4578X;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic;
INFOPLIST_FILE = EssentialApp/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.essentialdeveloper.EssentialAppCaseStudy;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "Essential App Case Study (Production)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
};
Expand All @@ -542,8 +576,9 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = VRJ2W4578X;
INFOPLIST_FILE = EssentialAppTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
Expand All @@ -568,8 +603,9 @@
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = VRJ2W4578X;
INFOPLIST_FILE = EssentialAppTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1620"
LastUpgradeVersion = "1630"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1620"
LastUpgradeVersion = "1630"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import UIKit
import SwiftUI

public enum AuthComposer {
public static func authViewController(
onAuthenticated: @escaping () -> Void) -> UIViewController {
// For now, show the login flow. Registration can be added easily here.
let loginVC = LoginComposer.loginViewController(onAuthenticated: onAuthenticated)
return loginVC
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import UIKit
import SwiftUI
import Combine
import EssentialFeed

public enum LoginComposer {
private static var cancellables = Set<AnyCancellable>()
public static func loginViewController(
onAuthenticated: @escaping () -> Void) -> UIViewController {
let viewModel = LoginViewModel()
let loginView = LoginView(viewModel: viewModel)
let controller = UIHostingController(rootView: loginView)
viewModel.authenticated
.sink { onAuthenticated() }
.store(in: &cancellables)
return controller
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import SwiftUI
import EssentialFeed

struct BorderedProminentIfAvailable: ViewModifier {
func body(content: Content) -> some View {
if #available(iOS 15.0, *) {
content.buttonStyle(.borderedProminent)
} else {
content.buttonStyle(DefaultButtonStyle())
}
}
}

public struct LoginView: View {
@ObservedObject var viewModel: LoginViewModel

public init(viewModel: LoginViewModel) {
self.viewModel = viewModel
}

public var body: some View {
VStack(spacing: 16) {
TextField("Username", text: $viewModel.username)
.autocapitalization(.none)
.textFieldStyle(RoundedBorderTextFieldStyle())
SecureField("Password", text: $viewModel.password)
.textFieldStyle(RoundedBorderTextFieldStyle())
if let error = viewModel.errorMessage {
Text(error)
.foregroundColor(.red)
}
Button("Login") {
viewModel.login()
}
.modifier(BorderedProminentIfAvailable())
}
.padding()
.alert(isPresented: $viewModel.loginSuccess) {
Alert(
title: Text("Login Successful"),
message: Text("Welcome!"),
dismissButton: .default(Text("OK"), action: {
viewModel.onSuccessAlertDismissed()
})
)
}
}
}

public protocol LoginSuccessView: AnyObject {
func showLoginSuccess()
}

public protocol LoginErrorClearingView: AnyObject {
func clearErrorMessages()
}
Binary file added EssentialFeed/.DS_Store
Binary file not shown.
Loading
Loading