Merge pull request #20 from rock-n-code/wrapping-things-up

Fixes: Wrapping things up
This commit is contained in:
Javier Cicchelli 2022-12-20 03:42:53 +01:00 committed by GitHub
commit 61e37a87fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 121 additions and 196 deletions

View File

@ -13,7 +13,6 @@
02AE64F129363DBF005A4AF3 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE64F029363DBF005A4AF3 /* ContentView.swift */; }; 02AE64F129363DBF005A4AF3 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE64F029363DBF005A4AF3 /* ContentView.swift */; };
02AE64F329363DC1005A4AF3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 02AE64F229363DC1005A4AF3 /* Assets.xcassets */; }; 02AE64F329363DC1005A4AF3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 02AE64F229363DC1005A4AF3 /* Assets.xcassets */; };
02AE64F629363DC1005A4AF3 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 02AE64F529363DC1005A4AF3 /* Preview Assets.xcassets */; }; 02AE64F629363DC1005A4AF3 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 02AE64F529363DC1005A4AF3 /* Preview Assets.xcassets */; };
02AE650029363DC1005A4AF3 /* BeRealTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE64FF29363DC1005A4AF3 /* BeRealTests.swift */; };
02AE650A29363DC1005A4AF3 /* BeRealUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE650929363DC1005A4AF3 /* BeRealUITests.swift */; }; 02AE650A29363DC1005A4AF3 /* BeRealUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE650929363DC1005A4AF3 /* BeRealUITests.swift */; };
02AE650C29363DC1005A4AF3 /* BeRealUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE650B29363DC1005A4AF3 /* BeRealUITestsLaunchTests.swift */; }; 02AE650C29363DC1005A4AF3 /* BeRealUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AE650B29363DC1005A4AF3 /* BeRealUITestsLaunchTests.swift */; };
4673A2AC294656A1000FE043 /* Cores in Frameworks */ = {isa = PBXBuildFile; productRef = 4673A2AB294656A1000FE043 /* Cores */; }; 4673A2AC294656A1000FE043 /* Cores in Frameworks */ = {isa = PBXBuildFile; productRef = 4673A2AB294656A1000FE043 /* Cores */; };
@ -21,13 +20,6 @@
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
02AE64FC29363DC1005A4AF3 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 02AE64E329363DBF005A4AF3 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 02AE64EA29363DBF005A4AF3;
remoteInfo = BeReal;
};
02AE650629363DC1005A4AF3 /* PBXContainerItemProxy */ = { 02AE650629363DC1005A4AF3 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy; isa = PBXContainerItemProxy;
containerPortal = 02AE64E329363DBF005A4AF3 /* Project object */; containerPortal = 02AE64E329363DBF005A4AF3 /* Project object */;
@ -46,11 +38,10 @@
02AE64F029363DBF005A4AF3 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; }; 02AE64F029363DBF005A4AF3 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
02AE64F229363DC1005A4AF3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 02AE64F229363DC1005A4AF3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
02AE64F529363DC1005A4AF3 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; }; 02AE64F529363DC1005A4AF3 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
02AE64FB29363DC1005A4AF3 /* BeRealTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BeRealTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
02AE64FF29363DC1005A4AF3 /* BeRealTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeRealTests.swift; sourceTree = "<group>"; };
02AE650529363DC1005A4AF3 /* BeRealUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BeRealUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 02AE650529363DC1005A4AF3 /* BeRealUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BeRealUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
02AE650929363DC1005A4AF3 /* BeRealUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeRealUITests.swift; sourceTree = "<group>"; }; 02AE650929363DC1005A4AF3 /* BeRealUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeRealUITests.swift; sourceTree = "<group>"; };
02AE650B29363DC1005A4AF3 /* BeRealUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeRealUITestsLaunchTests.swift; sourceTree = "<group>"; }; 02AE650B29363DC1005A4AF3 /* BeRealUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeRealUITestsLaunchTests.swift; sourceTree = "<group>"; };
02E4701B29506B5100269158 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
466D180A29465340003828DC /* Cores */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Cores; sourceTree = "<group>"; }; 466D180A29465340003828DC /* Cores */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Cores; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
@ -65,13 +56,6 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
02AE64F829363DC1005A4AF3 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
02AE650229363DC1005A4AF3 /* Frameworks */ = { 02AE650229363DC1005A4AF3 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -82,6 +66,23 @@
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
024E087A29514D35002C4DF9 /* App */ = {
isa = PBXGroup;
children = (
02AE64EE29363DBF005A4AF3 /* BeRealApp.swift */,
);
path = App;
sourceTree = "<group>";
};
024E087B29514D40002C4DF9 /* Assets */ = {
isa = PBXGroup;
children = (
02AE64F229363DC1005A4AF3 /* Assets.xcassets */,
02AE64F529363DC1005A4AF3 /* Preview Assets.xcassets */,
);
path = Assets;
sourceTree = "<group>";
};
02659B152946AA2700C3AD63 /* UI */ = { 02659B152946AA2700C3AD63 /* UI */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -110,11 +111,11 @@
02AE64E229363DBF005A4AF3 = { 02AE64E229363DBF005A4AF3 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
02E4701B29506B5100269158 /* README.md */,
466D180A29465340003828DC /* Cores */, 466D180A29465340003828DC /* Cores */,
026D9823293B6365009FE888 /* Libraries */, 026D9823293B6365009FE888 /* Libraries */,
02784F03293A8331005F839D /* Modules */, 02784F03293A8331005F839D /* Modules */,
02AE64ED29363DBF005A4AF3 /* BeReal */, 02AE64ED29363DBF005A4AF3 /* BeReal */,
02AE64FE29363DC1005A4AF3 /* BeRealTests */,
02AE650829363DC1005A4AF3 /* BeRealUITests */, 02AE650829363DC1005A4AF3 /* BeRealUITests */,
02AE64EC29363DBF005A4AF3 /* Products */, 02AE64EC29363DBF005A4AF3 /* Products */,
4694AA9E293A7C8800D54903 /* Frameworks */, 4694AA9E293A7C8800D54903 /* Frameworks */,
@ -125,7 +126,6 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
02AE64EB29363DBF005A4AF3 /* BeReal.app */, 02AE64EB29363DBF005A4AF3 /* BeReal.app */,
02AE64FB29363DC1005A4AF3 /* BeRealTests.xctest */,
02AE650529363DC1005A4AF3 /* BeRealUITests.xctest */, 02AE650529363DC1005A4AF3 /* BeRealUITests.xctest */,
); );
name = Products; name = Products;
@ -134,30 +134,13 @@
02AE64ED29363DBF005A4AF3 /* BeReal */ = { 02AE64ED29363DBF005A4AF3 /* BeReal */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
02AE64EE29363DBF005A4AF3 /* BeRealApp.swift */, 024E087A29514D35002C4DF9 /* App */,
02AE64F229363DC1005A4AF3 /* Assets.xcassets */,
02659B152946AA2700C3AD63 /* UI */, 02659B152946AA2700C3AD63 /* UI */,
02AE64F429363DC1005A4AF3 /* Preview Content */, 024E087B29514D40002C4DF9 /* Assets */,
); );
path = BeReal; path = BeReal;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
02AE64F429363DC1005A4AF3 /* Preview Content */ = {
isa = PBXGroup;
children = (
02AE64F529363DC1005A4AF3 /* Preview Assets.xcassets */,
);
path = "Preview Content";
sourceTree = "<group>";
};
02AE64FE29363DC1005A4AF3 /* BeRealTests */ = {
isa = PBXGroup;
children = (
02AE64FF29363DC1005A4AF3 /* BeRealTests.swift */,
);
path = BeRealTests;
sourceTree = "<group>";
};
02AE650829363DC1005A4AF3 /* BeRealUITests */ = { 02AE650829363DC1005A4AF3 /* BeRealUITests */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -199,24 +182,6 @@
productReference = 02AE64EB29363DBF005A4AF3 /* BeReal.app */; productReference = 02AE64EB29363DBF005A4AF3 /* BeReal.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
}; };
02AE64FA29363DC1005A4AF3 /* BeRealTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 02AE651229363DC1005A4AF3 /* Build configuration list for PBXNativeTarget "BeRealTests" */;
buildPhases = (
02AE64F729363DC1005A4AF3 /* Sources */,
02AE64F829363DC1005A4AF3 /* Frameworks */,
02AE64F929363DC1005A4AF3 /* Resources */,
);
buildRules = (
);
dependencies = (
02AE64FD29363DC1005A4AF3 /* PBXTargetDependency */,
);
name = BeRealTests;
productName = BeRealTests;
productReference = 02AE64FB29363DC1005A4AF3 /* BeRealTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
02AE650429363DC1005A4AF3 /* BeRealUITests */ = { 02AE650429363DC1005A4AF3 /* BeRealUITests */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 02AE651529363DC1005A4AF3 /* Build configuration list for PBXNativeTarget "BeRealUITests" */; buildConfigurationList = 02AE651529363DC1005A4AF3 /* Build configuration list for PBXNativeTarget "BeRealUITests" */;
@ -249,10 +214,6 @@
02AE64EA29363DBF005A4AF3 = { 02AE64EA29363DBF005A4AF3 = {
CreatedOnToolsVersion = 14.1; CreatedOnToolsVersion = 14.1;
}; };
02AE64FA29363DC1005A4AF3 = {
CreatedOnToolsVersion = 14.1;
TestTargetID = 02AE64EA29363DBF005A4AF3;
};
02AE650429363DC1005A4AF3 = { 02AE650429363DC1005A4AF3 = {
CreatedOnToolsVersion = 14.1; CreatedOnToolsVersion = 14.1;
TestTargetID = 02AE64EA29363DBF005A4AF3; TestTargetID = 02AE64EA29363DBF005A4AF3;
@ -273,7 +234,6 @@
projectRoot = ""; projectRoot = "";
targets = ( targets = (
02AE64EA29363DBF005A4AF3 /* BeReal */, 02AE64EA29363DBF005A4AF3 /* BeReal */,
02AE64FA29363DC1005A4AF3 /* BeRealTests */,
02AE650429363DC1005A4AF3 /* BeRealUITests */, 02AE650429363DC1005A4AF3 /* BeRealUITests */,
); );
}; };
@ -289,13 +249,6 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
02AE64F929363DC1005A4AF3 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
02AE650329363DC1005A4AF3 /* Resources */ = { 02AE650329363DC1005A4AF3 /* Resources */ = {
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -316,14 +269,6 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
02AE64F729363DC1005A4AF3 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
02AE650029363DC1005A4AF3 /* BeRealTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
02AE650129363DC1005A4AF3 /* Sources */ = { 02AE650129363DC1005A4AF3 /* Sources */ = {
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -336,11 +281,6 @@
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */ /* Begin PBXTargetDependency section */
02AE64FD29363DC1005A4AF3 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 02AE64EA29363DBF005A4AF3 /* BeReal */;
targetProxy = 02AE64FC29363DC1005A4AF3 /* PBXContainerItemProxy */;
};
02AE650729363DC1005A4AF3 /* PBXTargetDependency */ = { 02AE650729363DC1005A4AF3 /* PBXTargetDependency */ = {
isa = PBXTargetDependency; isa = PBXTargetDependency;
target = 02AE64EA29363DBF005A4AF3 /* BeReal */; target = 02AE64EA29363DBF005A4AF3 /* BeReal */;
@ -470,7 +410,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"BeReal/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"BeReal/Assets\"";
DEVELOPMENT_TEAM = 7FMNM89WKG; DEVELOPMENT_TEAM = 7FMNM89WKG;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@ -504,7 +444,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_ASSET_PATHS = "\"BeReal/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"BeReal/Assets\"";
DEVELOPMENT_TEAM = 7FMNM89WKG; DEVELOPMENT_TEAM = 7FMNM89WKG;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@ -531,52 +471,6 @@
}; };
name = Release; name = Release;
}; };
02AE651329363DC1005A4AF3 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 7FMNM89WKG;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.rockncode.app.assignment.be-real.tests.unit";
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/BeReal.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/BeReal";
};
name = Debug;
};
02AE651429363DC1005A4AF3 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 7FMNM89WKG;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.rockncode.app.assignment.be-real.tests.unit";
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/BeReal.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/BeReal";
};
name = Release;
};
02AE651629363DC1005A4AF3 /* Debug */ = { 02AE651629363DC1005A4AF3 /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
@ -642,15 +536,6 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
02AE651229363DC1005A4AF3 /* Build configuration list for PBXNativeTarget "BeRealTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
02AE651329363DC1005A4AF3 /* Debug */,
02AE651429363DC1005A4AF3 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
02AE651529363DC1005A4AF3 /* Build configuration list for PBXNativeTarget "BeRealUITests" */ = { 02AE651529363DC1005A4AF3 /* Build configuration list for PBXNativeTarget "BeRealUITests" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (

View File

@ -1,6 +1,7 @@
{ {
"images" : [ "images" : [
{ {
"filename" : "MyFiles.png",
"idiom" : "universal", "idiom" : "universal",
"platform" : "ios", "platform" : "ios",
"size" : "1024x1024" "size" : "1024x1024"

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@ -95,7 +95,7 @@ private extension ContentView {
do { do {
user = try await getUser() user = try await getUser()
} catch { } catch {
// TODO: Handle this error appropriately. showSheet = .login
} }
} }
} }

View File

@ -1,37 +0,0 @@
//
// BeRealTests.swift
// BeRealTests
//
// Created by Javier Cicchelli on 29/11/2022.
// Copyright © 2022 Röck+Cöde. All rights reserved.
//
import XCTest
@testable import BeReal
final class BeRealTests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() throws {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
// Any test you write for XCTest can be annotated as throws and async.
// Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
// Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
}
func testPerformanceExample() throws {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}

View File

@ -14,26 +14,24 @@ import KeychainStorage
public actor GetUserUseCase { public actor GetUserUseCase {
// MARK: Storages
@KeychainStorage(key: .KeychainStorage.account) var account: Account?
// MARK: Properties // MARK: Properties
private let apiService: APIService private let apiService: APIService
private var account: Account?
// MARK: Initialisers // MARK: Initialisers
public init( public init(apiService: APIService) {
apiService: APIService,
account: Account?
) {
self.apiService = apiService self.apiService = apiService
self.account = account
} }
// MARK: Functions // MARK: Functions
public func callAsFunction() async throws -> User { public func callAsFunction() async throws -> User {
guard let account else { throw GetUserError .accountNotFound } guard let account else { throw GetUserError.accountNotFound }
return try await getUser( return try await getUser(
username: account.username, username: account.username,
@ -65,12 +63,8 @@ public actor GetUserUseCase {
public extension GetUserUseCase { public extension GetUserUseCase {
init() { init() {
@Dependency(\.apiService) var apiService @Dependency(\.apiService) var apiService
@KeychainStorage(key: .KeychainStorage.account) var account: Account?
self.init(apiService: apiService)
self.init(
apiService: apiService,
account: account
)
} }
} }

View File

@ -118,10 +118,16 @@ extension InputAlertView {
// MARK: UITextFieldDelegate // MARK: UITextFieldDelegate
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { func textField(
_ textField: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String
) -> Bool {
component.textFieldString = { component.textFieldString = {
if let text = textField.text as NSString? { if let text = textField.text as NSString? {
return text.replacingCharacters(in: range, with: string) return text
.replacingCharacters(in: range, with: string)
.trimmingCharacters(in: .newlines)
} else { } else {
return .empty return .empty
} }

View File

@ -100,6 +100,10 @@ private extension DocumentView {
// MARK: Computed // MARK: Computed
var supportedContentTypes: [String] {
[.ContentType.jpeg, .ContentType.png]
}
var imageFromData: UIImage { var imageFromData: UIImage {
guard guard
let loadedData, let loadedData,
@ -114,7 +118,7 @@ private extension DocumentView {
// MARK: Functions // MARK: Functions
func loadDataIfPossible() async { func loadDataIfPossible() async {
guard document.contentType == .Constants.supportedContentType else { guard supportedContentTypes.contains(document.contentType) else {
status = .notSupported status = .notSupported
return return
} }
@ -147,8 +151,9 @@ private extension DocumentView {
// MARK: - String+Constants // MARK: - String+Constants
private extension String { private extension String {
enum Constants { enum ContentType {
static let supportedContentType = "image/jpeg" static let jpeg = "image/jpeg"
static let png = "image/png"
} }
} }

View File

@ -7,6 +7,7 @@
// //
import DataModels import DataModels
import KeychainStorage
import SwiftUI import SwiftUI
public struct ProfileView: View { public struct ProfileView: View {
@ -15,6 +16,10 @@ public struct ProfileView: View {
@Environment(\.dismiss) private var dismiss @Environment(\.dismiss) private var dismiss
// MARK: Storages
@KeychainStorage(key: .KeychainStorage.account) var account: Account?
// MARK: Properties // MARK: Properties
private let user: User? private let user: User?
@ -84,7 +89,7 @@ public struct ProfileView: View {
Section { Section {
Button { Button {
logout() Task { await logUserOut() }
} label: { } label: {
Text( Text(
"profile.button.log_out.text", "profile.button.log_out.text",
@ -107,6 +112,16 @@ public struct ProfileView: View {
} }
// MARK: - Helpers
private extension ProfileView {
func logUserOut() async {
account = nil
logout()
}
}
// MARK: - Images+Constants // MARK: - Images+Constants
private extension Image { private extension Image {

56
README.md Normal file
View File

@ -0,0 +1,56 @@
# BeReal assignment
The _"My Files"_ application was created as a result of the requirements defined by the given assignment by [BeReal](https://bereal.com/en).
## App
In a nutshell, the purpose of this application is to manage a remote file system accessible throught the Internet via a provided REST API endpoint.
In its current state, the application allows its users to:
- [x] handle GET, POST and DELETE requests to the API endpoint;*
- [x] require the user to login to the app to access the file system;*
- [x] load the items located at the root folder;*
- [x] browse through the folders hierarchy;*
- [x] create new folder in the current folder location;*
- [x] support for pull-to-refresh on folders;*
- [x] open JPG and PNG images in the image viewer;*
- [x] upload documents from the phone or an **iCloud** location to the current folder location;*
- [x] download document from the current folder location to the phone or an **iCloud** location;*
- [x] delete existing folders in the current folder location;*
- [x] delete existing documents in the current folder location;*
- [x] show available user information in the profile;*
- [x] allow the user to logout from the app;*
- [x] show to the user relevant errors that could happen while using the app.*
> It is necessary to clarify that, to use any **iCloud** location, the user is required to have an actual **iCloud** account and to be logged to that **iCloud** account in the phone.
## Implementation
This application was built as a `SwiftUI` application as the kind of app this assignment defined actually fits the use case to use this young framework, even though `UIKit` has been battle-tested for more than a decade now. The declarative nature of the framework, which is based in describing the behavior of the UI interface, and no by implementing how it should work (like with the imperative approach we were used to), allows the developer for simpler, more maintenable codebases. Of course, the bridge support to native UI framework, like `UIKit` or `AppKit`, is also available for those cases when the developer needs to build tailored UI components and/or more fine-grained control over UI controls. Finally, another advantage worth mentioning, but given the scope of this assignment, is not really relevant is the possibility of porting the app to other platforms, like iPad, Mac, Apple TV, Apple Watch or even Carplay easily, as the framework implementes an encapsulation (or better said, an erasure...) through its APIs of several `UIKit`, `AppKit`, and `WatchKit` components that developer usually need to build their apps.
With choosing the `SwiftUI` framework to build this app, it also comes the question of the type of architecture to use with it. Arguably, my preferred answer to this particular question is **"None"**. Given this framework is highly opinionated (much more opinionated than the view controllers from `UIKit` or `Appkit`), and that it is supported by the power and capabilities that the Swift language itself provides, this framework has lots of extra, helpful features that a SwiftUI `View` can use out of the box (like the `@State`, `@Binding`, `@AppStorage` and `@FetchRequest` property wrappers), I would rather prefer to fully understand how SwiftUI works and use some appropriate, useful design patterns instead. Given my experience, it is preferable to just go with the flow of the technology you use, because the less time spent swimming against the current (or fighting against your framework of choice), the more time developers have to actually solve the problems that actually matter to their apps and, of course, their users.
Now that design patterns have been mentioned, in this exercise some well-known patterns are being used in some degree. For example, the *Singleton* pattern is used to initialise the container in the `DependencyInjection` core library. Both public and internal *Interfaces* that either describe an object or how it should behave are used throughout this codebase, as this pattern is essential to plug the libraries into the modules, and the modules into the actual app and, as a consequence, for testability purposes as developer can easily create mocks, stubs and spies out of them. Then the *Adapter* pattern is used to transform some object into another object like, for example, when transforming some data from a business model instance to a model used exclusively by a UI component or view. Last, but definitely not least, the *Use cases* are a pattern from Android that basically execute a function based on some given input, and provides an output after that particular function is finished. This pattern is particularly useful to encapsulate the logic that a view needs to execute in a simple way, without the need to create view model classes that inherit from the `ObservableObject` class.
This application was built with scalability in terms of the codebase in mind, which tries to address how this codebase could grow in a controllable, organised manner. For this very reason, this application uses the Swift Package Manager (or simply `SPM`) to define the `Cores`, `Libraries` and `Modules` packages. Inside each package, there are targets that encapsulate a some certain functionality or feature with its respective assets inside. Of course, some of these targets also have related test targets that contain test cases with some relevant unit tests. The application takes a bottom-up approach, as the App could use `Cores`, `Libraries` and `Modules` targets but the Modules could use only `Cores` and `Libraries` targets; Libraries could use only `Cores` targets and 3rd party dependencies; and Cores could use only 3rd party dependencies if needed. This approach forces the deveeloper to think about actual separation of concerns, as the different features and dependencies should be grouped as independent, reusable building blocks, and to move the code into the SPM packages out of the main app target, reducing compiling time and overal weight of the application. The only drawback I have encountered with this approach is that the Xcode previews for those views that implemented previews don't work in the current Xcode version.
## TODO
Of course, this application is not completely done yet and, as mentioned in the assignment text, some more time is definitely required to actually finish the app and polish the code as intended.
Currently, the work that has been left outside of this assignment
- [ ] Better error and connectivity handling;
- [ ] Move duplicated code from Modules to sensible, common Libraries dependencies;
- [ ] Add support for new APIs introduced on iOS 16 (especially for navigation);
- [ ] Improve communication between the public views on the Modules and the core application;
- [ ] Implement API response caching to optimise the use of the network connection;
- [ ] Implement an offline mode with a synchronisation mechanism to keep the device and remote file systems in sync;
- [ ] Write further inline documentation, especially for the public interfaces;
- [ ] Write further `DocC` documentation and articles for the libraries, modules and app;
- [ ] Write more unit tests with the `XCTest` framework for all the relevant logic code distributed in the Core, Library and Modules dependencies;
- [ ] Write UI tests for happy and error paths with the `XCUITest` framework;
- [ ] Set Xcode plugin that uses the `SwiftFormat` library to lint and format the source code on development delivery pipeline;
- [ ] Set Xcode plugin that uses `DocC` library to generate Xcode and web-ready documentation on release delivery pipeline;
- [ ] Set [Xcode Cloud](https://developer.apple.com/xcode-cloud/) as continuous integration and delivery service to **TestFlight** and to the **App Store**.