diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..243c2a14 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://editorconfig.org +[*.swift] + +indent_style = space +tab_width = 4 +indent_size = 4 + +end_of_line = lf +insert_final_newline = true + +max_line_length = 160 +trim_trailing_whitespace = false \ No newline at end of file diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index 48dea849..3adf497b 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -17,7 +17,7 @@ concurrency: jobs: build: - runs-on: macos-13 + runs-on: macos-latest steps: - name: Checkout diff --git a/.swiftlint.yml b/.swiftlint.yml index 612d7709..8ff29ad8 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -3,15 +3,17 @@ disabled_rules: # rule identifiers turned on by default to exclude from running - trailing_whitespace - empty_parentheses_with_trailing_closure opt_in_rules: # some rules are turned off by default, so you need to opt-in - - empty_count - closure_parameter_position - closing_brace - comma - colon - cyclomatic_complexity - duplicate_imports + - empty_collection_literal + - empty_count - for_where - force_try + - private_over_fileprivate # Alternatively, specify all rules explicitly by uncommenting this option: # only_rules: # delete `disabled_rules` & `opt_in_rules` if using this diff --git a/Basic-Car-Maintenance.xcodeproj/project.pbxproj b/Basic-Car-Maintenance.xcodeproj/project.pbxproj index ef9a88fd..68a2dd8d 100644 --- a/Basic-Car-Maintenance.xcodeproj/project.pbxproj +++ b/Basic-Car-Maintenance.xcodeproj/project.pbxproj @@ -153,6 +153,7 @@ FF755B452A90969D00F49A13 /* Bundle+extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+extension.swift"; sourceTree = ""; }; FF755B482A909A0000F49A13 /* AddMaintenanceView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddMaintenanceView.swift; sourceTree = ""; }; FFA392762C54738E00A0AD6D /* Basic-Car-Maintenance.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = "Basic-Car-Maintenance.xctestplan"; sourceTree = ""; }; + FFA392772C547CA300A0AD6D /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; FFAA56EC2AC8905C000120EE /* Documentation.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = Documentation.docc; sourceTree = ""; }; FFBE79BB2AD0A57D0005524E /* UnitTests.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = UnitTests.xcconfig; sourceTree = ""; }; FFBFE0902A98EFEC000A9BEB /* MaintenanceEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MaintenanceEvent.swift; sourceTree = ""; }; @@ -406,6 +407,7 @@ FF755B422A90915E00F49A13 /* Localizable.xcstrings */, FFC8CDA32AA385E800D129A6 /* GoogleService-Info.plist */, 8AEE81732ACF394E00FC0C2A /* Info.plist */, + FFA392772C547CA300A0AD6D /* PrivacyInfo.xcprivacy */, FF5D13A62A86C2D600BC9BD6 /* BasicCarMaintenanceApp.swift */, E4345E602B4CDA4F0086D04B /* Onboarding */, 8A3D74832AD6D9870000FEEB /* MainView */, @@ -606,7 +608,7 @@ attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1500; - LastUpgradeCheck = 1500; + LastUpgradeCheck = 1600; TargetAttributes = { FF5D13A22A86C2D600BC9BD6 = { CreatedOnToolsVersion = 15.0; @@ -837,6 +839,7 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; @@ -866,6 +869,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; @@ -899,6 +903,7 @@ buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; @@ -928,6 +933,7 @@ CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -950,13 +956,13 @@ FF5D13C92A86C2D800BC9BD6 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = ""; "ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES[sdk=*]" = "AppIcon-car-red AppIcon-car-yellow AppIcon-car-dark AppIcon-car-orange AppIcon-car-black"; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = "Basic-Car-Maintenance/Basic_Car_Maintenance.entitlements"; CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"Basic-Car-Maintenance/Preview Content\""; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; @@ -991,12 +997,12 @@ isa = XCBuildConfiguration; baseConfigurationReference = FF098EFA2AB3424E003EC0FE /* Basic-Car-Maintenance.xcconfig */; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = ""; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = "Basic-Car-Maintenance/Basic_Car_Maintenance.entitlements"; CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"Basic-Car-Maintenance/Preview Content\""; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; @@ -1031,9 +1037,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = FFBE79BB2AD0A57D0005524E /* UnitTests.xcconfig */; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.0; MACOSX_DEPLOYMENT_TARGET = 13.4; @@ -1051,9 +1057,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = FFBE79BB2AD0A57D0005524E /* UnitTests.xcconfig */; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.0; MACOSX_DEPLOYMENT_TARGET = 13.4; @@ -1071,8 +1077,8 @@ isa = XCBuildConfiguration; baseConfigurationReference = FF0813562AD0A83000910EFA /* UITests.xcconfig */; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.0; MACOSX_DEPLOYMENT_TARGET = 13.4; @@ -1090,8 +1096,8 @@ isa = XCBuildConfiguration; baseConfigurationReference = FF0813562AD0A83000910EFA /* UITests.xcconfig */; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; + DEAD_CODE_STRIPPING = YES; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.0; MACOSX_DEPLOYMENT_TARGET = 13.4; diff --git a/Basic-Car-Maintenance.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Basic-Car-Maintenance.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index f74a036e..ef750a7f 100644 --- a/Basic-Car-Maintenance.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Basic-Car-Maintenance.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,12 +1,22 @@ { + "originHash" : "a1569f9895aa2be8e24832f98525d5da4eb90b5d158a82691c15b47eb72a13d7", "pins" : [ { "identity" : "abseil-cpp-binary", "kind" : "remoteSourceControl", "location" : "https://github.com/google/abseil-cpp-binary.git", "state" : { - "revision" : "bfc0b6f81adc06ce5121eb23f628473638d67c5c", - "version" : "1.2022062300.0" + "revision" : "194a6706acbd25e4ef639bcaddea16e8758a3e27", + "version" : "1.2024011602.0" + } + }, + { + "identity" : "app-check", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/app-check.git", + "state" : { + "revision" : "3b62f154d00019ae29a71e9738800bb6f18b236d", + "version" : "10.19.2" } }, { @@ -14,8 +24,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/firebase/firebase-ios-sdk.git", "state" : { - "revision" : "8a8ec57a272e0d31480fb0893dda0cf4f769b57e", - "version" : "10.15.0" + "revision" : "eca84fd638116dd6adb633b5a3f31cc7befcbb7d", + "version" : "10.29.0" } }, { @@ -23,8 +33,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/google/GoogleAppMeasurement.git", "state" : { - "revision" : "03b9beee1a61f62d32c521e172e192a1663a5e8b", - "version" : "10.13.0" + "revision" : "fe727587518729046fc1465625b9afd80b5ab361", + "version" : "10.28.0" } }, { @@ -32,8 +42,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/google/GoogleDataTransport.git", "state" : { - "revision" : "aae45a320fd0d11811820335b1eabc8753902a40", - "version" : "9.2.5" + "revision" : "a637d318ae7ae246b02d7305121275bc75ed5565", + "version" : "9.4.0" } }, { @@ -41,8 +51,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/google/GoogleUtilities.git", "state" : { - "revision" : "c38ce365d77b04a9a300c31061c5227589e5597b", - "version" : "7.11.5" + "revision" : "57a1d307f42df690fdef2637f3e5b776da02aad6", + "version" : "7.13.3" } }, { @@ -50,8 +60,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/google/grpc-binary.git", "state" : { - "revision" : "f1b366129d1125be7db83247e003fc333104b569", - "version" : "1.50.2" + "revision" : "e9fad491d0673bdda7063a0341fb6b47a30c5359", + "version" : "1.62.2" } }, { @@ -59,8 +69,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/google/gtm-session-fetcher.git", "state" : { - "revision" : "d415594121c9e8a4f9d79cecee0965cf35e74dbd", - "version" : "3.1.1" + "revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b", + "version" : "3.5.0" } }, { @@ -77,8 +87,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/firebase/leveldb.git", "state" : { - "revision" : "0706abcc6b0bd9cedfbb015ba840e4a780b5159b", - "version" : "1.22.2" + "revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1", + "version" : "1.22.5" } }, { @@ -86,8 +96,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/firebase/nanopb.git", "state" : { - "revision" : "819d0a2173aff699fb8c364b6fb906f7cdb1a692", - "version" : "2.30909.0" + "revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1", + "version" : "2.30910.0" } }, { @@ -95,8 +105,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/google/promises.git", "state" : { - "revision" : "e70e889c0196c76d22759eb50d6a0270ca9f1d9e", - "version" : "2.3.1" + "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", + "version" : "2.4.0" } }, { @@ -104,10 +114,10 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-protobuf.git", "state" : { - "revision" : "3c54ab05249f59f2c6641dd2920b8358ea9ed127", - "version" : "1.24.0" + "revision" : "edb6ed4919f7756157fe02f2552b7e3850a538e5", + "version" : "1.28.1" } } ], - "version" : 2 + "version" : 3 } diff --git a/Basic-Car-Maintenance.xcodeproj/xcshareddata/IDETemplateMacros.plist b/Basic-Car-Maintenance.xcodeproj/xcshareddata/IDETemplateMacros.plist new file mode 100644 index 00000000..ab8fbe5f --- /dev/null +++ b/Basic-Car-Maintenance.xcodeproj/xcshareddata/IDETemplateMacros.plist @@ -0,0 +1,14 @@ + + + + + FILEHEADER + +// ___FILENAME___ +// Basic-Car-Maintenance +// +// https://github.com/mikaelacaron/Basic-Car-Maintenance +// See LICENSE for license information. +// + + \ No newline at end of file diff --git a/Basic-Car-Maintenance.xcodeproj/xcshareddata/xcschemes/Basic-Car-Maintenance-WidgetExtension.xcscheme b/Basic-Car-Maintenance.xcodeproj/xcshareddata/xcschemes/Basic-Car-Maintenance-WidgetExtension.xcscheme index 0cadeb91..fc37ce0a 100644 --- a/Basic-Car-Maintenance.xcodeproj/xcshareddata/xcschemes/Basic-Car-Maintenance-WidgetExtension.xcscheme +++ b/Basic-Car-Maintenance.xcodeproj/xcshareddata/xcschemes/Basic-Car-Maintenance-WidgetExtension.xcscheme @@ -1,6 +1,6 @@ + + diff --git a/Basic-Car-Maintenance/Shared/BasicCarMaintenanceApp.swift b/Basic-Car-Maintenance/Shared/BasicCarMaintenanceApp.swift index 02b2527e..06915ce0 100644 --- a/Basic-Car-Maintenance/Shared/BasicCarMaintenanceApp.swift +++ b/Basic-Car-Maintenance/Shared/BasicCarMaintenanceApp.swift @@ -5,7 +5,9 @@ // Created by Mikaela Caron on 8/11/23. // +import FirebaseAuth import FirebaseCore +import FirebaseFirestore import SwiftUI import TipKit @@ -40,7 +42,20 @@ class AppDelegate: NSObject, UIApplicationDelegate { _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil ) -> Bool { + FirebaseApp.configure() + + let useEmulator = UserDefaults.standard.bool(forKey: "useEmulator") + if useEmulator { + let settings = Firestore.firestore().settings + settings.host = "localhost:8080" + settings.cacheSettings = MemoryCacheSettings() + settings.isSSLEnabled = false + Firestore.firestore().settings = settings + + Auth.auth().useEmulator(withHost: "127.0.0.1", port: 9099) + } + return true } diff --git a/Basic-Car-Maintenance/Shared/Dashboard/Views/AddMaintenanceView.swift b/Basic-Car-Maintenance/Shared/Dashboard/Views/AddMaintenanceView.swift index d33e39cd..ad56f790 100644 --- a/Basic-Car-Maintenance/Shared/Dashboard/Views/AddMaintenanceView.swift +++ b/Basic-Car-Maintenance/Shared/Dashboard/Views/AddMaintenanceView.swift @@ -64,7 +64,7 @@ struct AddMaintenanceView: View { } } header: { Text("Notes", - comment: "Notes text field header") + comment: "Maintenance event notes text field label") } } .analyticsView("\(Self.self)") diff --git a/Basic-Car-Maintenance/Shared/Dashboard/Views/DashboardView.swift b/Basic-Car-Maintenance/Shared/Dashboard/Views/DashboardView.swift index a04e7986..02b79e92 100644 --- a/Basic-Car-Maintenance/Shared/Dashboard/Views/DashboardView.swift +++ b/Basic-Car-Maintenance/Shared/Dashboard/Views/DashboardView.swift @@ -132,8 +132,6 @@ struct DashboardView: View { } Button { - // TODO: Show Paywall - // Can only add 3 events, adding the 4th triggers the paywall viewModel.isShowingAddMaintenanceEvent = true } label: { Image(systemName: SFSymbol.plus) @@ -158,8 +156,6 @@ struct DashboardView: View { .onChange(of: scenePhase) { _, newScenePhase in guard case .active = newScenePhase else { return } - // TODO: Show Paywall - guard let action = actionService.action, action == .newMaintenance else { diff --git a/Basic-Car-Maintenance/Shared/Info.plist b/Basic-Car-Maintenance/Shared/Info.plist index 84b2357d..9b63a014 100644 --- a/Basic-Car-Maintenance/Shared/Info.plist +++ b/Basic-Car-Maintenance/Shared/Info.plist @@ -4,6 +4,10 @@ FirebaseAutomaticScreenReportingEnabled + FIREBASE_ANALYTICS_COLLECTION_ENABLED + + FirebaseCrashlyticsCollectionEnabled + UIApplicationShortcutItems diff --git a/Basic-Car-Maintenance/Shared/Localizable.xcstrings b/Basic-Car-Maintenance/Shared/Localizable.xcstrings index 8eee8845..071667ed 100644 --- a/Basic-Car-Maintenance/Shared/Localizable.xcstrings +++ b/Basic-Car-Maintenance/Shared/Localizable.xcstrings @@ -5863,4 +5863,4 @@ } }, "version" : "1.0" -} +} \ No newline at end of file diff --git a/Basic-Car-Maintenance/Shared/Odometer/Views/OdometerView.swift b/Basic-Car-Maintenance/Shared/Odometer/Views/OdometerView.swift index 491ca8d2..abdceaef 100644 --- a/Basic-Car-Maintenance/Shared/Odometer/Views/OdometerView.swift +++ b/Basic-Car-Maintenance/Shared/Odometer/Views/OdometerView.swift @@ -67,8 +67,6 @@ struct OdometerView: View { .toolbar { ToolbarItemGroup(placement: .primaryAction) { Button { - // TODO: Show Paywall - // if adding a 4th odometer reading, show paywall viewModel.isShowingAddOdometerReading = true } label: { Image(systemName: SFSymbol.plus) @@ -81,6 +79,7 @@ struct OdometerView: View { } .sheet(isPresented: $viewModel.isShowingEditReadingView) { if let selectedReading = viewModel.selectedReading { + // swiftlint:disable:next line_length EditOdometerReadingView(selectedReading: selectedReading, vehicles: viewModel.vehicles) { updatedReading in viewModel.updateOdometerReading(updatedReading) } diff --git a/Basic-Car-Maintenance/Shared/PrivacyInfo.xcprivacy b/Basic-Car-Maintenance/Shared/PrivacyInfo.xcprivacy new file mode 100644 index 00000000..84c09a5e --- /dev/null +++ b/Basic-Car-Maintenance/Shared/PrivacyInfo.xcprivacy @@ -0,0 +1,12 @@ + + + + + + diff --git a/Basic-Car-Maintenance/Shared/Settings/Views/SettingsView.swift b/Basic-Car-Maintenance/Shared/Settings/Views/SettingsView.swift index c0ef0a21..941b6867 100644 --- a/Basic-Car-Maintenance/Shared/Settings/Views/SettingsView.swift +++ b/Basic-Car-Maintenance/Shared/Settings/Views/SettingsView.swift @@ -146,8 +146,6 @@ struct SettingsView: View { } Button { - // TODO: Show Paywall - // Show paywall if adding more than 1 vehicle, or show the `isShowingAddVehicle` view isShowingAddVehicle = true } label: { Text("Add Vehicle", comment: "Label to add a vehicle.") @@ -250,6 +248,7 @@ struct SettingsView: View { showDeleteVehicleAlert = false } } message: { + // swiftlint:disable:next line_length Text("The last vehicle can't be deleted. Please add a new vehicle before removing this one.", comment: "Alert message preventing users from deleting their last vehicle") } .navigationTitle(Text("Settings", comment: "Label to display settings.")) @@ -260,8 +259,6 @@ struct SettingsView: View { .onChange(of: scenePhase) { _, newScenePhase in guard case .active = newScenePhase else { return } - // TODO: Show Paywall - guard let action = actionService.action, action == .addVehicle else { diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d434b223..1a9b028e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,7 +11,7 @@ This app will be deployed on the Apple App Store, available for iOS 17.0 or late # Getting Started ## Prerequisites -* Download Xcode 15.0 or later +* Download Xcode 15.0 or later (preferred to use Xcode 16 or later) * Install [SwiftLint](https://github.com/realm/SwiftLint) onto your machine via [Homebrew](https://brew.sh/) * This is not a requirement, but is preferred. ```sh @@ -24,6 +24,8 @@ brew install swiftlint * Prefer Indent using Spaces * Tab Width: 4 * Indent Width: 4 + * Xcode 16+ + * Check "Prefer Settings from EditorConfig" ## Start Here * **Fork** this repo @@ -46,8 +48,33 @@ PRODUCT_BUNDLE_IDENTIFIER = com.mycompany.Basic-Car-Maintenance * Build the project ✅ +## Setup the Firebase Emulator +We are going to set up the Firebase emulator to be able to load the data locally and not affect production. Please do not skip this step. +* Install [Homebrew](https://brew.sh/) + * Package manager for macOS, to install more things +* Install Xcode command line tools with `xcode-select --install` +* `brew install nvm` + * Installs node version manager, so you don't need to update the system node version + * Add the executable to the $PATH via .zshrc or .bashrc file as prompted after installation, do **NOT** forget this! (and then restart your Terminal) +* `nvm install stable` +* `nvm use stable` + * to download and use the latest stable version of node +* `brew install openjdk` + * Add the executable to the $PATH via .zshrc or .bashrc file as prompted after installation, do **NOT** forget this! (and then restart your Terminal) +* `npm install -g firebase-tools` + * Installs the Firebase tools for running the emulator + + +## Start Working on an Issue +* Anytime you run the project, first in Terminal `cd` to `backend` in the `Basic-Car-Maintenance` directory + * this is the directory with the `firebase.json` file, you should see that if you type `ls` +* Run `firebase emulators:start --import=./local-data --export-on-exit`, which will start the emulators, and keep your data in `local-data` directory. + * Meaning when you start and stop the emulator your data will persist. +* Run the app + * You should see your anonymous user in Authentication, and once you add new data, see it in Firestore emulator UI at: http://127.0.0.1:4000/firestore + * If you don't see your user, delete the app from the simulator, and in the menu go to Device > Erase All Content and Settings (which resets your simulator), and try to run again * **Checkout** a new branch (from the `dev` branch) to work on an issue -* When your feature/fix is complete open a pull request, PR, from your feature branch to the `dev` branch +* When your feature / fix is complete open a pull request, PR, from your feature branch to the `dev` branch * Use a descriptive PR title and fill out the entire PR template, do not delete any sections. # Branches and PRs diff --git a/Gemfile.lock b/Gemfile.lock index b15c32ab..450871d6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -10,20 +10,20 @@ GEM artifactory (3.0.17) atomos (0.1.3) aws-eventstream (1.3.0) - aws-partitions (1.958.0) - aws-sdk-core (3.201.3) + aws-partitions (1.981.0) + aws-sdk-core (3.209.1) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) - aws-sigv4 (~> 1.8) + aws-sigv4 (~> 1.9) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.88.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-kms (1.94.0) + aws-sdk-core (~> 3, >= 3.207.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.156.0) - aws-sdk-core (~> 3, >= 3.201.0) + aws-sdk-s3 (1.166.0) + aws-sdk-core (~> 3, >= 3.207.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) - aws-sigv4 (1.9.0) + aws-sigv4 (1.10.0) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) base64 (0.2.0) @@ -39,7 +39,7 @@ GEM dotenv (2.8.1) emoji_regex (3.2.3) excon (0.111.0) - faraday (1.10.3) + faraday (1.10.4) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) faraday-excon (~> 1.1) @@ -65,10 +65,10 @@ GEM faraday-patron (1.0.0) faraday-rack (1.0.0) faraday-retry (1.0.3) - faraday_middleware (1.2.0) + faraday_middleware (1.2.1) faraday (~> 1.0) fastimage (2.3.1) - fastlane (2.221.1) + fastlane (2.223.1) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.8, < 3.0.0) artifactory (~> 3.0) @@ -126,7 +126,7 @@ GEM google-apis-core (>= 0.11.0, < 2.a) google-apis-storage_v1 (0.31.0) google-apis-core (>= 0.11.0, < 2.a) - google-cloud-core (1.7.0) + google-cloud-core (1.7.1) google-cloud-env (>= 1.0, < 3.a) google-cloud-errors (~> 1.0) google-cloud-env (1.6.0) @@ -147,12 +147,12 @@ GEM os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) highline (2.0.3) - http-cookie (1.0.6) + http-cookie (1.0.7) domain_name (~> 0.5) httpclient (2.8.3) jmespath (1.6.2) json (2.7.2) - jwt (2.8.2) + jwt (2.9.1) base64 mini_magick (4.13.2) mini_mime (1.1.5) @@ -171,8 +171,7 @@ GEM trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.2.9) - strscan + rexml (3.3.8) rouge (2.0.7) ruby2_keywords (0.0.5) rubyzip (2.3.2) @@ -186,7 +185,6 @@ GEM CFPropertyList naturally slack-notifier (2.4.0) - strscan (3.1.0) terminal-notifier (2.0.0) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) @@ -196,15 +194,15 @@ GEM tty-spinner (0.9.3) tty-cursor (~> 0.7) uber (0.1.0) - unicode-display_width (2.5.0) + unicode-display_width (2.6.0) word_wrap (1.0.0) - xcodeproj (1.24.0) + xcodeproj (1.25.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) nanaimo (~> 0.3.0) - rexml (~> 3.2.4) + rexml (>= 3.3.2, < 4.0) xcov (1.8.1) fastlane (>= 2.141.0, < 3.0.0) multipart-post diff --git a/README.md b/README.md index 6dabafbe..62a25aa3 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,4 @@ # Basic Car Maintenance -Welcome to my open source app! It is ready for contributors for [Hacktoberfest](https://hacktoberfest.com/)! Use this app to gain experience getting started in open source for iOS and macOS development using Swift and SwiftUI. - -# Getting Started - ![Static Badge](https://img.shields.io/badge/status-active-brightgreen) ![GitHub last commit (branch)](https://img.shields.io/github/last-commit/mikaelacaron/Basic-Car-Maintenance/dev?logo=github) ![GitHub contributors](https://img.shields.io/github/contributors/mikaelacaron/Basic-Car-Maintenance) @@ -10,9 +6,12 @@ Welcome to my open source app! It is ready for contributors for [Hacktoberfest]( [![Unit Tests](https://github.com/mikaelacaron/Basic-Car-Maintenance/actions/workflows/unit-tests.yml/badge.svg?event=push)](https://github.com/mikaelacaron/Basic-Car-Maintenance/actions/workflows/unit-tests.yml) [![Deploy DocC Documentation](https://github.com/mikaelacaron/Basic-Car-Maintenance/actions/workflows/docc.yml/badge.svg?branch=dev)](https://github.com/mikaelacaron/Basic-Car-Maintenance/actions/workflows/docc.yml) -* Read the [Code of Conduct](https://github.com/mikaelacaron/Basic-Car-Maintenance/blob/main/CODE_OF_CONDUCT.md) -* Read the [CONTRIBUTING.md](https://github.com/mikaelacaron/Basic-Car-Maintenance/blob/main/CONTRIBUTING.md) guidelines -* Download Xcode 15 or later +Welcome to my open source app! It is ready for contributors for [Hacktoberfest](https://hacktoberfest.com/)! Use this app to gain experience getting started in open source for iOS and macOS development using Swift and SwiftUI. + +# Getting Started +* Read the [Code of Conduct](https://github.com/mikaelacaron/Basic-Car-Maintenance/blob/dev/CODE_OF_CONDUCT.md) +* Read the [CONTRIBUTING.md](https://github.com/mikaelacaron/Basic-Car-Maintenance/blob/dev/CONTRIBUTING.md) guidelines +* Download Xcode 15 or later (preferred to use Xcode 16 or later) * Browse the open [issues](https://github.com/mikaelacaron/Basic-Car-Maintenance/issues) and **comment** which you would like to work on * It is only one person per issue, except where noted. * **Fork** this repo @@ -35,7 +34,15 @@ PRODUCT_BUNDLE_IDENTIFIER = com.mycompany.Basic-Car-Maintenance * Build the project ✅ -* **Checkout** a new branch (from the `dev` branch) to work on an issue +* Setup the Firebase emulator, following the steps in [CONTRIBUTING.md](https://github.com/mikaelacaron/Basic-Car-Maintenance/blob/dev/CONTRIBUTING.md#setup-the-firebase-Emulator) + +> [!WARNING] +> DO **NOT** skip setting up the emulators! or your app won't work + +* Start the emulator with: `firebase emulators:start --import=./local-data --export-on-exit` + * Be sure to be in the `backend` folder that contains the `firebase.json` file. + +* **Checkout** a new branch, from the `dev` branch, to work on an issue # Contributing To start contributing, review [CONTRIBUTING.md](https://github.com/mikaelacaron/Basic-Car-Maintenance/blob/main/CONTRIBUTING.md). New contributors are always welcome to support this project. diff --git a/backend/.firebaserc b/backend/.firebaserc new file mode 100644 index 00000000..c360e913 --- /dev/null +++ b/backend/.firebaserc @@ -0,0 +1,5 @@ +{ + "projects": { + "default": "basic-car-maintenance" + } +} diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 00000000..6b5557eb --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,72 @@ +# Ignore the local data using the emulator +local-data + +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +firebase-debug.log* +firebase-debug.*.log* + +# Firebase cache +.firebase/ + +# Firebase config + +# Uncomment this if you'd like others to create their own Firebase project. +# For a team working on the same Firebase project(s), it is recommended to leave +# it commented so all members can deploy to the same project(s) in .firebaserc. +# .firebaserc + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# dataconnect generated files +.dataconnect diff --git a/backend/firebase.json b/backend/firebase.json new file mode 100644 index 00000000..494cec0a --- /dev/null +++ b/backend/firebase.json @@ -0,0 +1,18 @@ +{ + "firestore": { + "rules": "firestore.rules", + "indexes": "firestore.indexes.json" + }, + "emulators": { + "auth": { + "port": 9099 + }, + "firestore": { + "port": 8080 + }, + "ui": { + "enabled": true + }, + "singleProjectMode": true + } +} diff --git a/backend/firestore.indexes.json b/backend/firestore.indexes.json new file mode 100644 index 00000000..6f59674f --- /dev/null +++ b/backend/firestore.indexes.json @@ -0,0 +1,74 @@ +{ + "indexes": [ + { + "collectionGroup": "alerts", + "queryScope": "COLLECTION", + "fields": [ + { + "fieldPath": "isOn", + "order": "ASCENDING" + }, + { + "fieldPath": "_id", + "order": "ASCENDING" + } + ] + } + ], + "fieldOverrides": [ + { + "collectionGroup": "maintenance_events", + "fieldPath": "userID", + "ttl": false, + "indexes": [ + { + "order": "ASCENDING", + "queryScope": "COLLECTION" + }, + { + "order": "DESCENDING", + "queryScope": "COLLECTION" + }, + { + "arrayConfig": "CONTAINS", + "queryScope": "COLLECTION" + }, + { + "order": "ASCENDING", + "queryScope": "COLLECTION_GROUP" + }, + { + "order": "DESCENDING", + "queryScope": "COLLECTION_GROUP" + } + ] + }, + { + "collectionGroup": "odometer_readings", + "fieldPath": "userID", + "ttl": false, + "indexes": [ + { + "order": "ASCENDING", + "queryScope": "COLLECTION" + }, + { + "order": "DESCENDING", + "queryScope": "COLLECTION" + }, + { + "arrayConfig": "CONTAINS", + "queryScope": "COLLECTION" + }, + { + "order": "ASCENDING", + "queryScope": "COLLECTION_GROUP" + }, + { + "order": "DESCENDING", + "queryScope": "COLLECTION_GROUP" + } + ] + } + ] +} diff --git a/backend/firestore.rules b/backend/firestore.rules new file mode 100644 index 00000000..50713dba --- /dev/null +++ b/backend/firestore.rules @@ -0,0 +1,19 @@ +rules_version = '2'; + +service cloud.firestore { + match /databases/{database}/documents { + + // This rule allows anyone with your Firestore database reference to view, edit, + // and delete all data in your Firestore database. It is useful for getting + // started, but it is configured to expire after 30 days because it + // leaves your app open to attackers. At that time, all client + // requests to your Firestore database will be denied. + // + // Make sure to write security rules for your app before that time, or else + // all client requests to your Firestore database will be denied until you Update + // your rules + match /{document=**} { + allow read, write: if true; + } + } +} \ No newline at end of file