From c06b18792625f772ff06f23fe0c4c6b35bb9db9a Mon Sep 17 00:00:00 2001 From: cdricms <36056008+cdricms@users.noreply.github.com> Date: Tue, 9 Jul 2024 15:00:09 +0200 Subject: [PATCH] Release of version 0.1.3-beta --- .gitignore | 2 +- .swift-format | 69 ++ Brewer.xcodeproj/project.pbxproj | 673 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/swiftpm/Package.resolved | 15 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 58 ++ Brewer/Assets.xcassets/Contents.json | 6 + Brewer/Brewer.entitlements | 5 + Brewer/BrewerApp.swift | 64 ++ Brewer/CaskDetailView.swift | 39 + Brewer/Components/DownloadButton.swift | 53 ++ Brewer/Components/UninstallButton.swift | 45 ++ Brewer/Components/UpdateButton.swift | 61 ++ Brewer/ContentView.swift | 39 + Brewer/InstalledView.swift | 106 +++ Brewer/Model/Homebrew.swift | 248 +++++++ .../Preview Assets.xcassets/Contents.json | 6 + Brewer/SearchView.swift | 66 ++ Brewer/formulae.json | 226 ++++++ BrewerTests/BrewerTests.swift | 37 + BrewerUITests/BrewerUITests.swift | 41 ++ BrewerUITests/BrewerUITestsLaunchTests.swift | 32 + 24 files changed, 1916 insertions(+), 1 deletion(-) create mode 100644 .swift-format create mode 100644 Brewer.xcodeproj/project.pbxproj create mode 100644 Brewer.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Brewer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 Brewer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 Brewer/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Brewer/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Brewer/Assets.xcassets/Contents.json create mode 100644 Brewer/Brewer.entitlements create mode 100644 Brewer/BrewerApp.swift create mode 100644 Brewer/CaskDetailView.swift create mode 100644 Brewer/Components/DownloadButton.swift create mode 100644 Brewer/Components/UninstallButton.swift create mode 100644 Brewer/Components/UpdateButton.swift create mode 100644 Brewer/ContentView.swift create mode 100644 Brewer/InstalledView.swift create mode 100644 Brewer/Model/Homebrew.swift create mode 100644 Brewer/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 Brewer/SearchView.swift create mode 100644 Brewer/formulae.json create mode 100644 BrewerTests/BrewerTests.swift create mode 100644 BrewerUITests/BrewerUITests.swift create mode 100644 BrewerUITests/BrewerUITestsLaunchTests.swift diff --git a/.gitignore b/.gitignore index 2cbab10..bfeaaf8 100644 --- a/.gitignore +++ b/.gitignore @@ -97,4 +97,4 @@ fastlane/test_output # https://github.com/johnno1962/injectionforxcode iOSInjectionProject/ - +.DS_Store diff --git a/.swift-format b/.swift-format new file mode 100644 index 0000000..d226c08 --- /dev/null +++ b/.swift-format @@ -0,0 +1,69 @@ +{ + "fileScopedDeclarationPrivacy" : { + "accessLevel" : "private" + }, + "indentation" : { + "tabs" : 1 + }, + "indentConditionalCompilationBlocks" : true, + "indentSwitchCaseLabels" : false, + "lineBreakAroundMultilineExpressionChainComponents" : false, + "lineBreakBeforeControlFlowKeywords" : false, + "lineBreakBeforeEachArgument" : false, + "lineBreakBeforeEachGenericRequirement" : false, + "lineLength" : 80, + "maximumBlankLines" : 1, + "multiElementCollectionTrailingCommas" : true, + "noAssignmentInExpressions" : { + "allowedFunctions" : [ + "XCTAssertNoThrow" + ] + }, + "prioritizeKeepingFunctionOutputTogether" : false, + "respectsExistingLineBreaks" : true, + "rules" : { + "AllPublicDeclarationsHaveDocumentation" : false, + "AlwaysUseLiteralForEmptyCollectionInit" : false, + "AlwaysUseLowerCamelCase" : true, + "AmbiguousTrailingClosureOverload" : true, + "BeginDocumentationCommentWithOneLineSummary" : false, + "DoNotUseSemicolons" : true, + "DontRepeatTypeInStaticProperties" : true, + "FileScopedDeclarationPrivacy" : true, + "FullyIndirectEnum" : true, + "GroupNumericLiterals" : true, + "IdentifiersMustBeASCII" : true, + "NeverForceUnwrap" : false, + "NeverUseForceTry" : false, + "NeverUseImplicitlyUnwrappedOptionals" : false, + "NoAccessLevelOnExtensionDeclaration" : true, + "NoAssignmentInExpressions" : true, + "NoBlockComments" : true, + "NoCasesWithOnlyFallthrough" : true, + "NoEmptyTrailingClosureParentheses" : true, + "NoLabelsInCasePatterns" : true, + "NoLeadingUnderscores" : false, + "NoParensAroundConditions" : true, + "NoPlaygroundLiterals" : true, + "NoVoidReturnOnFunctionSignature" : true, + "OmitExplicitReturns" : false, + "OneCasePerLine" : true, + "OneVariableDeclarationPerLine" : true, + "OnlyOneTrailingClosureArgument" : true, + "OrderedImports" : true, + "ReplaceForEachWithForLoop" : true, + "ReturnVoidInsteadOfEmptyTuple" : true, + "TypeNamesShouldBeCapitalized" : true, + "UseEarlyExits" : false, + "UseLetInEveryBoundCaseVariable" : true, + "UseShorthandTypeNames" : true, + "UseSingleLinePropertyGetter" : true, + "UseSynthesizedInitializer" : true, + "UseTripleSlashForDocumentationComments" : true, + "UseWhereClausesInForLoops" : false, + "ValidateDocumentationComments" : false + }, + "spacesAroundRangeFormationOperators" : false, + "tabWidth" : 4, + "version" : 1 +} diff --git a/Brewer.xcodeproj/project.pbxproj b/Brewer.xcodeproj/project.pbxproj new file mode 100644 index 0000000..c8c3810 --- /dev/null +++ b/Brewer.xcodeproj/project.pbxproj @@ -0,0 +1,673 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 9E6C73072C3D5E570056ADDC /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6C73062C3D5E570056ADDC /* SearchView.swift */; }; + 9E6C73092C3D5E950056ADDC /* InstalledView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6C73082C3D5E950056ADDC /* InstalledView.swift */; }; + 9E6C730C2C3D796D0056ADDC /* DownloadButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6C730B2C3D796D0056ADDC /* DownloadButton.swift */; }; + 9E6C730E2C3DB16F0056ADDC /* UninstallButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6C730D2C3DB16F0056ADDC /* UninstallButton.swift */; }; + 9E6C73112C3DB5940056ADDC /* CaskDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6C73102C3DB5940056ADDC /* CaskDetailView.swift */; }; + 9E6C73182C3E853E0056ADDC /* UpdateButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6C73172C3E853E0056ADDC /* UpdateButton.swift */; }; + 9E7ADB5A2C3EE41F00F2B8CA /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = 9E7ADB592C3EE41F00F2B8CA /* LaunchAtLogin */; }; + 9E8CE5362C3C545600A39146 /* BrewerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E8CE5352C3C545600A39146 /* BrewerApp.swift */; }; + 9E8CE5382C3C545600A39146 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E8CE5372C3C545600A39146 /* ContentView.swift */; }; + 9E8CE53A2C3C545700A39146 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9E8CE5392C3C545700A39146 /* Assets.xcassets */; }; + 9E8CE53D2C3C545700A39146 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 9E8CE53C2C3C545700A39146 /* Preview Assets.xcassets */; }; + 9E8CE5482C3C545800A39146 /* BrewerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E8CE5472C3C545800A39146 /* BrewerTests.swift */; }; + 9E8CE5522C3C545800A39146 /* BrewerUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E8CE5512C3C545800A39146 /* BrewerUITests.swift */; }; + 9E8CE5542C3C545800A39146 /* BrewerUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E8CE5532C3C545800A39146 /* BrewerUITestsLaunchTests.swift */; }; + 9E8CE5622C3C5A6A00A39146 /* Homebrew.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E8CE5612C3C5A6A00A39146 /* Homebrew.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 9E8CE5442C3C545800A39146 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 9E8CE52A2C3C545600A39146 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 9E8CE5312C3C545600A39146; + remoteInfo = Brewer; + }; + 9E8CE54E2C3C545800A39146 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 9E8CE52A2C3C545600A39146 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 9E8CE5312C3C545600A39146; + remoteInfo = Brewer; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 9E6C73062C3D5E570056ADDC /* SearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchView.swift; sourceTree = ""; }; + 9E6C73082C3D5E950056ADDC /* InstalledView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstalledView.swift; sourceTree = ""; }; + 9E6C730B2C3D796D0056ADDC /* DownloadButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadButton.swift; sourceTree = ""; }; + 9E6C730D2C3DB16F0056ADDC /* UninstallButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UninstallButton.swift; sourceTree = ""; }; + 9E6C73102C3DB5940056ADDC /* CaskDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaskDetailView.swift; sourceTree = ""; }; + 9E6C73172C3E853E0056ADDC /* UpdateButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateButton.swift; sourceTree = ""; }; + 9E8CE5322C3C545600A39146 /* Brewer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Brewer.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 9E8CE5352C3C545600A39146 /* BrewerApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewerApp.swift; sourceTree = ""; }; + 9E8CE5372C3C545600A39146 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 9E8CE5392C3C545700A39146 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 9E8CE53C2C3C545700A39146 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 9E8CE53E2C3C545700A39146 /* Brewer.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Brewer.entitlements; sourceTree = ""; }; + 9E8CE5432C3C545800A39146 /* BrewerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BrewerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 9E8CE5472C3C545800A39146 /* BrewerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewerTests.swift; sourceTree = ""; }; + 9E8CE54D2C3C545800A39146 /* BrewerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BrewerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 9E8CE5512C3C545800A39146 /* BrewerUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewerUITests.swift; sourceTree = ""; }; + 9E8CE5532C3C545800A39146 /* BrewerUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrewerUITestsLaunchTests.swift; sourceTree = ""; }; + 9E8CE5612C3C5A6A00A39146 /* Homebrew.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Homebrew.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 9E8CE52F2C3C545600A39146 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9E7ADB5A2C3EE41F00F2B8CA /* LaunchAtLogin in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9E8CE5402C3C545800A39146 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9E8CE54A2C3C545800A39146 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9E6C730A2C3D795D0056ADDC /* Components */ = { + isa = PBXGroup; + children = ( + 9E6C730B2C3D796D0056ADDC /* DownloadButton.swift */, + 9E6C73172C3E853E0056ADDC /* UpdateButton.swift */, + 9E6C730D2C3DB16F0056ADDC /* UninstallButton.swift */, + ); + path = Components; + sourceTree = ""; + }; + 9E6C730F2C3DB5850056ADDC /* Details */ = { + isa = PBXGroup; + children = ( + 9E6C73102C3DB5940056ADDC /* CaskDetailView.swift */, + ); + name = Details; + sourceTree = ""; + }; + 9E8CE5292C3C545600A39146 = { + isa = PBXGroup; + children = ( + 9E8CE5342C3C545600A39146 /* Brewer */, + 9E8CE5462C3C545800A39146 /* BrewerTests */, + 9E8CE5502C3C545800A39146 /* BrewerUITests */, + 9E8CE5332C3C545600A39146 /* Products */, + ); + sourceTree = ""; + }; + 9E8CE5332C3C545600A39146 /* Products */ = { + isa = PBXGroup; + children = ( + 9E8CE5322C3C545600A39146 /* Brewer.app */, + 9E8CE5432C3C545800A39146 /* BrewerTests.xctest */, + 9E8CE54D2C3C545800A39146 /* BrewerUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 9E8CE5342C3C545600A39146 /* Brewer */ = { + isa = PBXGroup; + children = ( + 9E6C730A2C3D795D0056ADDC /* Components */, + 9E8CE5602C3C5A5000A39146 /* Model */, + 9E8CE5352C3C545600A39146 /* BrewerApp.swift */, + 9E6C73062C3D5E570056ADDC /* SearchView.swift */, + 9E6C730F2C3DB5850056ADDC /* Details */, + 9E6C73082C3D5E950056ADDC /* InstalledView.swift */, + 9E8CE5372C3C545600A39146 /* ContentView.swift */, + 9E8CE5392C3C545700A39146 /* Assets.xcassets */, + 9E8CE53E2C3C545700A39146 /* Brewer.entitlements */, + 9E8CE53B2C3C545700A39146 /* Preview Content */, + ); + path = Brewer; + sourceTree = ""; + }; + 9E8CE53B2C3C545700A39146 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 9E8CE53C2C3C545700A39146 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 9E8CE5462C3C545800A39146 /* BrewerTests */ = { + isa = PBXGroup; + children = ( + 9E8CE5472C3C545800A39146 /* BrewerTests.swift */, + ); + path = BrewerTests; + sourceTree = ""; + }; + 9E8CE5502C3C545800A39146 /* BrewerUITests */ = { + isa = PBXGroup; + children = ( + 9E8CE5512C3C545800A39146 /* BrewerUITests.swift */, + 9E8CE5532C3C545800A39146 /* BrewerUITestsLaunchTests.swift */, + ); + path = BrewerUITests; + sourceTree = ""; + }; + 9E8CE5602C3C5A5000A39146 /* Model */ = { + isa = PBXGroup; + children = ( + 9E8CE5612C3C5A6A00A39146 /* Homebrew.swift */, + ); + path = Model; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 9E8CE5312C3C545600A39146 /* Brewer */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9E8CE5572C3C545800A39146 /* Build configuration list for PBXNativeTarget "Brewer" */; + buildPhases = ( + 9E8CE52E2C3C545600A39146 /* Sources */, + 9E8CE52F2C3C545600A39146 /* Frameworks */, + 9E8CE5302C3C545600A39146 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Brewer; + packageProductDependencies = ( + 9E7ADB592C3EE41F00F2B8CA /* LaunchAtLogin */, + ); + productName = Brewer; + productReference = 9E8CE5322C3C545600A39146 /* Brewer.app */; + productType = "com.apple.product-type.application"; + }; + 9E8CE5422C3C545800A39146 /* BrewerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9E8CE55A2C3C545800A39146 /* Build configuration list for PBXNativeTarget "BrewerTests" */; + buildPhases = ( + 9E8CE53F2C3C545800A39146 /* Sources */, + 9E8CE5402C3C545800A39146 /* Frameworks */, + 9E8CE5412C3C545800A39146 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 9E8CE5452C3C545800A39146 /* PBXTargetDependency */, + ); + name = BrewerTests; + productName = BrewerTests; + productReference = 9E8CE5432C3C545800A39146 /* BrewerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 9E8CE54C2C3C545800A39146 /* BrewerUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9E8CE55D2C3C545800A39146 /* Build configuration list for PBXNativeTarget "BrewerUITests" */; + buildPhases = ( + 9E8CE5492C3C545800A39146 /* Sources */, + 9E8CE54A2C3C545800A39146 /* Frameworks */, + 9E8CE54B2C3C545800A39146 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 9E8CE54F2C3C545800A39146 /* PBXTargetDependency */, + ); + name = BrewerUITests; + productName = BrewerUITests; + productReference = 9E8CE54D2C3C545800A39146 /* BrewerUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 9E8CE52A2C3C545600A39146 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1540; + LastUpgradeCheck = 1540; + TargetAttributes = { + 9E8CE5312C3C545600A39146 = { + CreatedOnToolsVersion = 15.4; + }; + 9E8CE5422C3C545800A39146 = { + CreatedOnToolsVersion = 15.4; + TestTargetID = 9E8CE5312C3C545600A39146; + }; + 9E8CE54C2C3C545800A39146 = { + CreatedOnToolsVersion = 15.4; + TestTargetID = 9E8CE5312C3C545600A39146; + }; + }; + }; + buildConfigurationList = 9E8CE52D2C3C545600A39146 /* Build configuration list for PBXProject "Brewer" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 9E8CE5292C3C545600A39146; + packageReferences = ( + 9E98BE4E2C3EE353006ED274 /* XCRemoteSwiftPackageReference "LaunchAtLogin-Modern" */, + ); + productRefGroup = 9E8CE5332C3C545600A39146 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 9E8CE5312C3C545600A39146 /* Brewer */, + 9E8CE5422C3C545800A39146 /* BrewerTests */, + 9E8CE54C2C3C545800A39146 /* BrewerUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 9E8CE5302C3C545600A39146 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9E8CE53D2C3C545700A39146 /* Preview Assets.xcassets in Resources */, + 9E8CE53A2C3C545700A39146 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9E8CE5412C3C545800A39146 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9E8CE54B2C3C545800A39146 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 9E8CE52E2C3C545600A39146 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9E6C73072C3D5E570056ADDC /* SearchView.swift in Sources */, + 9E6C730C2C3D796D0056ADDC /* DownloadButton.swift in Sources */, + 9E8CE5382C3C545600A39146 /* ContentView.swift in Sources */, + 9E6C73182C3E853E0056ADDC /* UpdateButton.swift in Sources */, + 9E6C730E2C3DB16F0056ADDC /* UninstallButton.swift in Sources */, + 9E8CE5622C3C5A6A00A39146 /* Homebrew.swift in Sources */, + 9E6C73092C3D5E950056ADDC /* InstalledView.swift in Sources */, + 9E8CE5362C3C545600A39146 /* BrewerApp.swift in Sources */, + 9E6C73112C3DB5940056ADDC /* CaskDetailView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9E8CE53F2C3C545800A39146 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9E8CE5482C3C545800A39146 /* BrewerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9E8CE5492C3C545800A39146 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9E8CE5522C3C545800A39146 /* BrewerUITests.swift in Sources */, + 9E8CE5542C3C545800A39146 /* BrewerUITestsLaunchTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 9E8CE5452C3C545800A39146 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 9E8CE5312C3C545600A39146 /* Brewer */; + targetProxy = 9E8CE5442C3C545800A39146 /* PBXContainerItemProxy */; + }; + 9E8CE54F2C3C545800A39146 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 9E8CE5312C3C545600A39146 /* Brewer */; + targetProxy = 9E8CE54E2C3C545800A39146 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 9E8CE5552C3C545800A39146 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 9E8CE5562C3C545800A39146 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MACOSX_DEPLOYMENT_TARGET = 14.5; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + }; + name = Release; + }; + 9E8CE5582C3C545800A39146 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = Brewer/Brewer.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = "0.1.3-beta"; + DEVELOPMENT_ASSET_PATHS = "\"Brewer/Preview Content\""; + DEVELOPMENT_TEAM = 96S93Z7LTG; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = Brewer; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + INFOPLIST_KEY_LSUIElement = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MARKETING_VERSION = "0.1.3-beta"; + PRODUCT_BUNDLE_IDENTIFIER = dev.cems.Brewer; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 9E8CE5592C3C545800A39146 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = Brewer/Brewer.entitlements; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = "0.1.3-beta"; + DEVELOPMENT_ASSET_PATHS = "\"Brewer/Preview Content\""; + DEVELOPMENT_TEAM = 96S93Z7LTG; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_CFBundleDisplayName = Brewer; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; + INFOPLIST_KEY_LSUIElement = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MARKETING_VERSION = "0.1.3-beta"; + PRODUCT_BUNDLE_IDENTIFIER = dev.cems.Brewer; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 9E8CE55B2C3C545800A39146 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 96S93Z7LTG; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 14.5; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = dev.cems.BrewerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Brewer.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Brewer"; + }; + name = Debug; + }; + 9E8CE55C2C3C545800A39146 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 96S93Z7LTG; + GENERATE_INFOPLIST_FILE = YES; + MACOSX_DEPLOYMENT_TARGET = 14.5; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = dev.cems.BrewerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Brewer.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Brewer"; + }; + name = Release; + }; + 9E8CE55E2C3C545800A39146 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 96S93Z7LTG; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = dev.cems.BrewerUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TEST_TARGET_NAME = Brewer; + }; + name = Debug; + }; + 9E8CE55F2C3C545800A39146 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 96S93Z7LTG; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = dev.cems.BrewerUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TEST_TARGET_NAME = Brewer; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 9E8CE52D2C3C545600A39146 /* Build configuration list for PBXProject "Brewer" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9E8CE5552C3C545800A39146 /* Debug */, + 9E8CE5562C3C545800A39146 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9E8CE5572C3C545800A39146 /* Build configuration list for PBXNativeTarget "Brewer" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9E8CE5582C3C545800A39146 /* Debug */, + 9E8CE5592C3C545800A39146 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9E8CE55A2C3C545800A39146 /* Build configuration list for PBXNativeTarget "BrewerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9E8CE55B2C3C545800A39146 /* Debug */, + 9E8CE55C2C3C545800A39146 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9E8CE55D2C3C545800A39146 /* Build configuration list for PBXNativeTarget "BrewerUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9E8CE55E2C3C545800A39146 /* Debug */, + 9E8CE55F2C3C545800A39146 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 9E98BE4E2C3EE353006ED274 /* XCRemoteSwiftPackageReference "LaunchAtLogin-Modern" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/sindresorhus/LaunchAtLogin-Modern"; + requirement = { + branch = main; + kind = branch; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 9E7ADB592C3EE41F00F2B8CA /* LaunchAtLogin */ = { + isa = XCSwiftPackageProductDependency; + package = 9E98BE4E2C3EE353006ED274 /* XCRemoteSwiftPackageReference "LaunchAtLogin-Modern" */; + productName = LaunchAtLogin; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 9E8CE52A2C3C545600A39146 /* Project object */; +} diff --git a/Brewer.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Brewer.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Brewer.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Brewer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Brewer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Brewer.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Brewer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Brewer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..827d44a --- /dev/null +++ b/Brewer.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "aae61040c30e63b9cf0f5455365680cfeabef0aac829c0941d4d6f6180010fc9", + "pins" : [ + { + "identity" : "launchatlogin-modern", + "kind" : "remoteSourceControl", + "location" : "https://github.com/sindresorhus/LaunchAtLogin-Modern", + "state" : { + "branch" : "main", + "revision" : "a04ec1c363be3627734f6dad757d82f5d4fa8fcc" + } + } + ], + "version" : 3 +} diff --git a/Brewer/Assets.xcassets/AccentColor.colorset/Contents.json b/Brewer/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/Brewer/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Brewer/Assets.xcassets/AppIcon.appiconset/Contents.json b/Brewer/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..3f00db4 --- /dev/null +++ b/Brewer/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Brewer/Assets.xcassets/Contents.json b/Brewer/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Brewer/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Brewer/Brewer.entitlements b/Brewer/Brewer.entitlements new file mode 100644 index 0000000..0c67376 --- /dev/null +++ b/Brewer/Brewer.entitlements @@ -0,0 +1,5 @@ + + + + + diff --git a/Brewer/BrewerApp.swift b/Brewer/BrewerApp.swift new file mode 100644 index 0000000..8cb5c25 --- /dev/null +++ b/Brewer/BrewerApp.swift @@ -0,0 +1,64 @@ +// +// BrewerApp.swift +// Brewer +// +// Created by Cédric MAS on 08/07/2024. +// + +import LaunchAtLogin +import SwiftUI + +@main +struct BrewerApp: App { + @AppStorage("isUpToDate") var isUpToDate: Bool = true + init() { + do { + let res = try shell( + "/opt/homebrew/bin/brew outdated --greedy-latest -g | wc -l") + if let number = Int( + res.trimmingCharacters(in: .whitespacesAndNewlines)), number > 0 + { + print(number) + isUpToDate = false + } + } catch { + print(error.localizedDescription) + } + } + + var body: some Scene { + MenuBarExtra { + VStack(spacing: 0) { + ContentView() + Form { + HStack { + LaunchAtLogin.Toggle() + Button { + NSApplication.shared.terminate(self) + } label: { + Label("Quit", systemImage: "x") + .symbolRenderingMode(.palette) + .symbolVariant(.circle) + .symbolVariant(.fill) + .foregroundStyle(.white, .red) + } + .frame(maxWidth: .infinity, alignment: .trailing) + } + } + }.padding() + } label: { + if isUpToDate { + Label("Brewer", systemImage: "sparkle.magnifyingglass") + } else { + Label("Brewer", systemImage: "sparkle.magnifyingglass") + .symbolRenderingMode(.palette) + .foregroundStyle(.orange) + } + } + .menuBarExtraStyle(.window) + WindowGroup { + EmptyView() + } + } + +} diff --git a/Brewer/CaskDetailView.swift b/Brewer/CaskDetailView.swift new file mode 100644 index 0000000..efdbc10 --- /dev/null +++ b/Brewer/CaskDetailView.swift @@ -0,0 +1,39 @@ +// +// CaskDetailView.swift +// Brewer +// +// Created by Cédric MAS on 09/07/2024. +// + +import SwiftUI + +struct CaskDetailView: View { + var cask: Cask + @Bindable var brewListing: Homebrew + var body: some View { + NavigationStack { + VStack { + Text("Identifier: \(cask.fullToken)") + Text("Version: \(cask.version)") + } + .navigationTitle(cask.name.first ?? cask.fullToken) + .toolbar { + ToolbarItem(placement: .primaryAction) { + if cask.installed == nil { + DownloadButton(name: cask.fullToken, isCask: true) + } else if cask.outdated { + Button("Update") { + + } + } + } + if cask.installed != nil { + ToolbarItem(placement: .primaryAction) { + UninstallButton( + name: cask.fullToken, brewListing: brewListing) + } + } + } + } + } +} diff --git a/Brewer/Components/DownloadButton.swift b/Brewer/Components/DownloadButton.swift new file mode 100644 index 0000000..68ae45f --- /dev/null +++ b/Brewer/Components/DownloadButton.swift @@ -0,0 +1,53 @@ +// +// DownloadButton.swift +// Brewer +// +// Created by Cédric MAS on 09/07/2024. +// + +import SwiftUI + +struct DownloadButton: View { + let name: String + var isCask: Bool = false + @State private var brew = Homebrew() + @State private var downloaded = false + + var body: some View { + VStack { + if downloaded { + Image(systemName: "checkmark") + .symbolRenderingMode(.palette) + .symbolVariant(.circle) + .symbolVariant(.fill) + .foregroundStyle(.white, .green) + } else if brew.isLoading { + ProgressView() + .controlSize(.small) + } else if brew.errorMessage != nil { + Image(systemName: "x") + .symbolRenderingMode(.palette) + .symbolVariant(.circle) + .symbolVariant(.fill) + .foregroundStyle(.white, .red) + } else { + Button("Get") { + Task { + downloaded = await brew.install(name, isCask: isCask) + } + } + .buttonBorderShape(.capsule) + .buttonStyle(.borderedProminent) + } + } + .onAppear { + Task { + downloaded = await brew.isDownloaded(name) + } + } + } +} + +#Preview { + DownloadButton(name: "firefox") +} diff --git a/Brewer/Components/UninstallButton.swift b/Brewer/Components/UninstallButton.swift new file mode 100644 index 0000000..4382022 --- /dev/null +++ b/Brewer/Components/UninstallButton.swift @@ -0,0 +1,45 @@ +// +// UninstallButton.swift +// Brewer +// +// Created by Cédric MAS on 09/07/2024. +// + +import SwiftUI + +struct UninstallButton: View { + let name: String + @State private var brew = Homebrew() + @State private var uninstalled = false + @Bindable var brewListing: Homebrew + var body: some View { + VStack { + if uninstalled { + EmptyView() + } else if brew.isLoading { + ProgressView() + .controlSize(.small) + } else if brew.errorMessage != nil { + Image(systemName: "x") + .symbolRenderingMode(.palette) + .symbolVariant(.circle) + .symbolVariant(.fill) + .foregroundStyle(.white, .red) + } else { + Button("Uninstall", role: .destructive) { + Task { + uninstalled = await brew.uninstall(name) + brewListing.getInstalled() + } + } + .buttonBorderShape(.capsule) + .buttonStyle(.borderedProminent) + } + } + .onAppear { + Task { + uninstalled = await brew.isDownloaded(name) + } + } + } +} diff --git a/Brewer/Components/UpdateButton.swift b/Brewer/Components/UpdateButton.swift new file mode 100644 index 0000000..59cf959 --- /dev/null +++ b/Brewer/Components/UpdateButton.swift @@ -0,0 +1,61 @@ +// +// UpdateButton.swift +// Brewer +// +// Created by Cédric MAS on 10/07/2024. +// + +import SwiftUI + +struct UpdateButton: View { + let name: String + @State private var brew = Homebrew() + @State private var newInfo = Homebrew() + + private var updated: String? { + if let cask = newInfo.data?.casks.first(where: { $0.fullToken == name } + ), !cask.outdated { + return cask.version + } + if let formulae = newInfo.data?.formulae.first(where: { + $0.fullName == name + }), !formulae.outdated { + return formulae.versions.stable + } + + return nil + } + + var body: some View { + if let newVersion = updated { + Text("Updated: \(newVersion)") + } else if brew.isLoading { + ProgressView() + .controlSize(.small) + } else if brew.errorMessage != nil { + Image(systemName: "x") + .symbolRenderingMode(.palette) + .symbolVariant(.circle) + .symbolVariant(.fill) + .foregroundStyle(.white, .red) + } else { + Button { + Task { + await brew.update(name) + newInfo.getInfo(on: name) + } + } label: { + Label( + "Update", + systemImage: + "arrow.counterclockwise" + ) + .symbolRenderingMode(.palette) + .symbolVariant(.circle) + .symbolVariant(.fill) + .foregroundStyle( + .white, .orange) + } + } + } +} diff --git a/Brewer/ContentView.swift b/Brewer/ContentView.swift new file mode 100644 index 0000000..2a55b05 --- /dev/null +++ b/Brewer/ContentView.swift @@ -0,0 +1,39 @@ +// +// ContentView.swift +// Brewer +// +// Created by Cédric MAS on 08/07/2024. +// + +import SwiftUI + +enum SegementedSelection: String, CaseIterable { + case search = "Search" + case installed = "Installed" +} + +struct ContentView: View { + @State private var segmentedSelection: SegementedSelection = .search + var body: some View { + NavigationStack { + VStack { + Picker("", selection: $segmentedSelection) { + ForEach(SegementedSelection.allCases, id: \.self) { sel in + Text(sel.rawValue).tag(sel) + } + }.pickerStyle(.segmented) + .padding(.leading, -8) + switch segmentedSelection { + case .search: + SearchView() + case .installed: + InstalledView() + } + } + } + } +} + +#Preview { + ContentView() +} diff --git a/Brewer/InstalledView.swift b/Brewer/InstalledView.swift new file mode 100644 index 0000000..081aa04 --- /dev/null +++ b/Brewer/InstalledView.swift @@ -0,0 +1,106 @@ +// +// InstalledView.swift +// Brewer +// +// Created by Cédric MAS on 09/07/2024. +// + +import SwiftUI + +struct InstalledView: View { + @State private var brew = Homebrew() + var body: some View { + VStack { + if brew.isLoading { + ProgressView("Loading...") + } else if let data = brew.data { + List { + if !data.casks.isEmpty { + Section { + ForEach(data.casks, id: \.fullToken) { cask in + NavigationLink { + CaskDetailView( + cask: cask, brewListing: brew) + } label: { + HStack { + Text(cask.name.first ?? cask.fullToken) + Spacer() + if cask.outdated { + UpdateButton(name: cask.fullToken) + } else { + Text(cask.version) + } + } + .contextMenu { + UninstallButton( + name: cask.fullToken, + brewListing: brew) + } + } + } + } header: { + HStack { + Text("Casks") + Spacer() + Text("\(data.casks.count) installed") + } + } + } + + if !data.formulae.isEmpty { + Section { + ForEach(data.formulae, id: \.fullName) { formulae in + HStack { + Text(formulae.fullName) + Spacer() + if formulae.outdated { + UpdateButton(name: formulae.fullName) + } else { + Text(formulae.versions.stable) + } + } + .contextMenu { + UninstallButton( + name: formulae.fullName, + brewListing: brew) + } + } + } header: { + HStack { + Text("Formulae") + Spacer() + Text("\(data.formulae.count) installed") + } + } + } + } + .listStyle(.inset(alternatesRowBackgrounds: true)) + let areOutdated = + data.casks.filter(\.outdated).count + + data.formulae.filter(\.outdated).count + let _ = UserDefaults.standard.set( + !(areOutdated > 0), forKey: "isUpToDate") + if areOutdated > 0 { + Button { + + } label: { + Label( + "Update all", + systemImage: "arrow.counterclockwise.circle.fill") + } + .buttonStyle(.borderedProminent) + .tint(.orange) + } + } else if let error = brew.errorMessage { + Text(error) + .foregroundStyle(.red) + } + }.task { + brew.getInstalled() + } + } +} + +#Preview { + InstalledView() +} diff --git a/Brewer/Model/Homebrew.swift b/Brewer/Model/Homebrew.swift new file mode 100644 index 0000000..0da1b38 --- /dev/null +++ b/Brewer/Model/Homebrew.swift @@ -0,0 +1,248 @@ +// +// Cask.swift +// Brewer +// +// Created by Cédric MAS on 08/07/2024. +// + +import Foundation + +enum ShellError: Error { + case emptyOutput +} + +@discardableResult +func shell(_ command: String) throws -> String { + let task = Process() + task.executableURL = URL(filePath: "/bin/bash") + task.arguments = ["-c", command] + + let pipe = Pipe() + task.standardError = pipe + task.standardOutput = pipe + + try task.run() + if let data = try pipe.fileHandleForReading.readToEnd(), + let output = String(data: data, encoding: .utf8) + { + return output + } + + throw ShellError.emptyOutput +} + +struct Cask: Codable { + let token: String + let fullToken: String + let tap: String + let name: [String] + let desc: String + let homepage: String + let url: String + let version: String + let installed: String? + var outdated: Bool + + enum CodingKeys: String, CodingKey { + case token, tap, name, desc, homepage, url, version, installed, outdated + case fullToken = "full_token" + } +} + +struct Formulae: Codable { + + struct Version: Codable { + let stable: String + let head: String? + let bottle: Bool + + enum CodingKeys: String, CodingKey { + case stable + case head, bottle + } + } + + let name: String + let fullName: String + let tap: String + let oldNames: [String] + let aliases: [String] + let versionedFormulae: [String] + let desc: String + let license: String? + let homepage: String + let versions: Version + let outdated: Bool + + enum CodingKeys: String, CodingKey { + case name + case tap + case aliases + case desc + case license + case homepage + case versions + case outdated + case fullName = "full_name" + case oldNames = "oldnames" + case versionedFormulae = "versioned_formulae" + } +} + +struct InfoResponse: Codable { + let formulae: [Formulae] + let casks: [Cask] + + enum CodingKeys: String, CodingKey { + case casks + case formulae + } +} + +@Observable +class Homebrew { + var data: InfoResponse? + var isLoading = false + var errorMessage: String? + + func getInfo(on query: String) { + self.isLoading = true + self.errorMessage = nil + self.data = nil + Task { [weak self] in + do { + let res = try shell( + "/opt/homebrew/bin/brew info --json=v2 \(query)") + if let data = res.data(using: .utf8) { + let output = try JSONDecoder().decode( + InfoResponse.self, from: data) + self?.data = output + } + } catch { + self?.errorMessage = error.localizedDescription + } + self?.isLoading = false + } + } + + func getInstalled() { + self.isLoading = true + self.data = nil + self.errorMessage = nil + Task { [weak self] in + do { + let res = try shell( + "/opt/homebrew/bin/brew info --json=v2 --installed") + if let data = res.data(using: .utf8) { + let output = try JSONDecoder().decode( + InfoResponse.self, from: data) + self?.data = output + } + } catch { + self?.errorMessage = error.localizedDescription + } + self?.isLoading = false + } + + } + + func isDownloaded(_ name: String) async -> Bool { + self.isLoading = true + self.errorMessage = nil + self.data = nil + let task = Task { [weak self] in + do { + let res = try shell( + "/opt/homebrew/bin/brew list -1 \(name) >/dev/null 2>&1; echo $?" + ) + .trimmingCharacters(in: .whitespacesAndNewlines) + self?.isLoading = false + return res == "0" + } catch { + self?.errorMessage = error.localizedDescription + } + + return false + } + + switch await task.result { + case .success(let success): + return success + case .failure(let fail): + return false + } + } + + func install(_ fullToken: String, isCask: Bool = false) async -> Bool { + self.isLoading = true + self.data = nil + let task = Task { [weak self] in + do { + try shell( + "/opt/homebrew/bin/brew install \(isCask ? "--cask" : "") \(fullToken)" + ) + } catch { + self?.errorMessage = error.localizedDescription + } + self?.isLoading = false + return await self?.isDownloaded(fullToken) ?? false + } + + return switch await task.result { + case .success(let success): + success + case .failure: + false + } + } + + func uninstall(_ fullToken: String) async -> Bool { + self.isLoading = true + self.data = nil + let task = Task { [weak self] in + do { + try shell( + "/opt/homebrew/bin/brew uninstall \(fullToken)" + ) + } catch { + self?.errorMessage = error.localizedDescription + } + self?.isLoading = false + return await !(self?.isDownloaded(fullToken) ?? true) + } + + return switch await task.result { + case .success(let success): + success + case .failure: + false + } + } + + func update(_ name: String) async -> Bool { + self.isLoading = true + self.errorMessage = nil + self.data = nil + let task = Task { [weak self] in + do { + let res = try shell( + "/opt/homebrew/bin/brew upgrade \(name); echo $?" + ) + .trimmingCharacters(in: .whitespacesAndNewlines) + self?.isLoading = false + return res == "0" + } catch { + self?.errorMessage = error.localizedDescription + } + self?.isLoading = false + return false + } + + return switch await task.result { + case .success(let success): + success + case .failure: + false + } + } +} diff --git a/Brewer/Preview Content/Preview Assets.xcassets/Contents.json b/Brewer/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Brewer/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Brewer/SearchView.swift b/Brewer/SearchView.swift new file mode 100644 index 0000000..9a673a0 --- /dev/null +++ b/Brewer/SearchView.swift @@ -0,0 +1,66 @@ +// +// SearchView.swift +// Brewer +// +// Created by Cédric MAS on 09/07/2024. +// + +import SwiftUI + +struct SearchView: View { + @State private var query = "" + @State private var brew = Homebrew() + var body: some View { + VStack { + TextField("Search", text: $query) + .onSubmit { + brew.getInfo(on: query) + } + Spacer() + if brew.isLoading { + ProgressView("Loading...") + } else if let data = brew.data { + List { + if !data.casks.isEmpty { + Section("Casks") { + ForEach(data.casks, id: \.fullToken) { cask in + HStack { + Text(cask.fullToken) + Spacer() + if cask.installed != nil { + Image( + systemName: "checkmark.circle.fill" + ) + .symbolRenderingMode(.palette) + .foregroundStyle(.white, .green) + } else { + DownloadButton( + name: cask.fullToken, isCask: true) + } + } + } + } + } + if !data.formulae.isEmpty { + + Section("Formulaes") { + ForEach(data.formulae, id: \.fullName) { formulae in + HStack { + Text(formulae.fullName) + } + } + } + } + } + } else if let errorMessage = brew.errorMessage { + Text(errorMessage) + .foregroundStyle(.red) + } + } + } + +} + +#Preview { + SearchView() +} diff --git a/Brewer/formulae.json b/Brewer/formulae.json new file mode 100644 index 0000000..21a8211 --- /dev/null +++ b/Brewer/formulae.json @@ -0,0 +1,226 @@ +{ + "name": "aom", + "full_name": "aom", + "tap": "homebrew/core", + "oldnames": [], + "aliases": [], + "versioned_formulae": [], + "desc": "Codec library for encoding and decoding AV1 video streams", + "license": "BSD-2-Clause", + "homepage": "https://aomedia.googlesource.com/aom", + "versions": { + "stable": "3.9.1", + "head": null, + "bottle": true + }, + "urls": { + "stable": { + "url": "https://aomedia.googlesource.com/aom.git", + "tag": "v3.9.1", + "revision": "8ad484f8a18ed1853c094e7d3a4e023b2a92df28", + "using": null, + "checksum": null + } + }, + "revision": 0, + "version_scheme": 0, + "bottle": { + "stable": { + "rebuild": 0, + "root_url": "https://ghcr.io/v2/homebrew/core", + "files": { + "arm64_sonoma": { + "cellar": ":any", + "url": "https://ghcr.io/v2/homebrew/core/aom/blobs/sha256:8cb9a41dca274a535e5cf7ea728a694f3e9621d97ea48fa8fd270cd5d99f0347", + "sha256": "8cb9a41dca274a535e5cf7ea728a694f3e9621d97ea48fa8fd270cd5d99f0347" + }, + "arm64_ventura": { + "cellar": ":any", + "url": "https://ghcr.io/v2/homebrew/core/aom/blobs/sha256:7b35304138ab104ff4c05cc78d16fdd6f3a4a19393d50cab274356f0dfc72479", + "sha256": "7b35304138ab104ff4c05cc78d16fdd6f3a4a19393d50cab274356f0dfc72479" + }, + "arm64_monterey": { + "cellar": ":any", + "url": "https://ghcr.io/v2/homebrew/core/aom/blobs/sha256:1274fcede1882cd3cd3f176e2a44f461e3dba57f2fa77151722774c77753455b", + "sha256": "1274fcede1882cd3cd3f176e2a44f461e3dba57f2fa77151722774c77753455b" + }, + "sonoma": { + "cellar": ":any", + "url": "https://ghcr.io/v2/homebrew/core/aom/blobs/sha256:cb0a6ac49c83d5377c1fe7fdc4df43e403af231c2d6db3398b01cd70b31f1ab7", + "sha256": "cb0a6ac49c83d5377c1fe7fdc4df43e403af231c2d6db3398b01cd70b31f1ab7" + }, + "ventura": { + "cellar": ":any", + "url": "https://ghcr.io/v2/homebrew/core/aom/blobs/sha256:7410d19b27f952bc12722457ea58b904a6de0416f3d7ada42cfad2b1cd67e795", + "sha256": "7410d19b27f952bc12722457ea58b904a6de0416f3d7ada42cfad2b1cd67e795" + }, + "monterey": { + "cellar": ":any", + "url": "https://ghcr.io/v2/homebrew/core/aom/blobs/sha256:9abf106327c58ffaae970744caad8b9bdace79220ba3ecf4e47fe4eb500f6fb0", + "sha256": "9abf106327c58ffaae970744caad8b9bdace79220ba3ecf4e47fe4eb500f6fb0" + }, + "x86_64_linux": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/aom/blobs/sha256:d018fbe0e71201de17504c3a5ed73f3499dfd36b1b07a09173e826418abd00b8", + "sha256": "d018fbe0e71201de17504c3a5ed73f3499dfd36b1b07a09173e826418abd00b8" + } + } + } + }, + "pour_bottle_only_if": null, + "keg_only": false, + "keg_only_reason": null, + "options": [], + "build_dependencies": [ + "cmake", + "pkg-config" + ], + "dependencies": [ + "jpeg-xl", + "libvmaf" + ], + "test_dependencies": [], + "recommended_dependencies": [], + "optional_dependencies": [], + "uses_from_macos": [], + "uses_from_macos_bounds": [], + "requirements": [], + "conflicts_with": [], + "conflicts_with_reasons": [], + "link_overwrite": [], + "caveats": null, + "installed": [ + { + "version": "3.9.1", + "used_options": [], + "built_as_bottle": true, + "poured_from_bottle": true, + "time": 1718172114, + "runtime_dependencies": [ + { + "full_name": "brotli", + "version": "1.1.0", + "revision": 0, + "pkg_version": "1.1.0", + "declared_directly": false + }, + { + "full_name": "giflib", + "version": "5.2.2", + "revision": 0, + "pkg_version": "5.2.2", + "declared_directly": false + }, + { + "full_name": "highway", + "version": "1.2.0", + "revision": 0, + "pkg_version": "1.2.0", + "declared_directly": false + }, + { + "full_name": "imath", + "version": "3.1.11", + "revision": 0, + "pkg_version": "3.1.11", + "declared_directly": false + }, + { + "full_name": "jpeg-turbo", + "version": "3.0.3", + "revision": 0, + "pkg_version": "3.0.3", + "declared_directly": false + }, + { + "full_name": "libpng", + "version": "1.6.43", + "revision": 0, + "pkg_version": "1.6.43", + "declared_directly": false + }, + { + "full_name": "xz", + "version": "5.4.6", + "revision": 0, + "pkg_version": "5.4.6", + "declared_directly": false + }, + { + "full_name": "lz4", + "version": "1.9.4", + "revision": 0, + "pkg_version": "1.9.4", + "declared_directly": false + }, + { + "full_name": "zstd", + "version": "1.5.6", + "revision": 0, + "pkg_version": "1.5.6", + "declared_directly": false + }, + { + "full_name": "libtiff", + "version": "4.6.0", + "revision": 0, + "pkg_version": "4.6.0", + "declared_directly": false + }, + { + "full_name": "little-cms2", + "version": "2.16", + "revision": 0, + "pkg_version": "2.16", + "declared_directly": false + }, + { + "full_name": "openexr", + "version": "3.2.4", + "revision": 0, + "pkg_version": "3.2.4", + "declared_directly": false + }, + { + "full_name": "webp", + "version": "1.4.0", + "revision": 0, + "pkg_version": "1.4.0", + "declared_directly": false + }, + { + "full_name": "jpeg-xl", + "version": "0.10.2", + "revision": 0, + "pkg_version": "0.10.2", + "declared_directly": true + }, + { + "full_name": "libvmaf", + "version": "3.0.0", + "revision": 0, + "pkg_version": "3.0.0", + "declared_directly": true + } + ], + "installed_as_dependency": true, + "installed_on_request": false + } + ], + "linked_keg": "3.9.1", + "pinned": false, + "outdated": false, + "deprecated": false, + "deprecation_date": null, + "deprecation_reason": null, + "disabled": false, + "disable_date": null, + "disable_reason": null, + "post_install_defined": false, + "service": null, + "tap_git_head": "e63757f09f6098330ee6073b756d52e9e4957b4b", + "ruby_source_path": "Formula/a/aom.rb", + "ruby_source_checksum": { + "sha256": "3de11c9a16f43364bf721cb6253993143ca9e5c9baeef1e6cb6170ecb176524f" + } +} diff --git a/BrewerTests/BrewerTests.swift b/BrewerTests/BrewerTests.swift new file mode 100644 index 0000000..b788195 --- /dev/null +++ b/BrewerTests/BrewerTests.swift @@ -0,0 +1,37 @@ +// +// BrewerTests.swift +// BrewerTests +// +// Created by Cédric MAS on 08/07/2024. +// + +import XCTest + +@testable import Brewer + +final class BrewerTests: 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. + } + } + +} diff --git a/BrewerUITests/BrewerUITests.swift b/BrewerUITests/BrewerUITests.swift new file mode 100644 index 0000000..c7cf997 --- /dev/null +++ b/BrewerUITests/BrewerUITests.swift @@ -0,0 +1,41 @@ +// +// BrewerUITests.swift +// BrewerUITests +// +// Created by Cédric MAS on 08/07/2024. +// + +import XCTest + +final class BrewerUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + 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 { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } + } +} diff --git a/BrewerUITests/BrewerUITestsLaunchTests.swift b/BrewerUITests/BrewerUITestsLaunchTests.swift new file mode 100644 index 0000000..145a7cf --- /dev/null +++ b/BrewerUITests/BrewerUITestsLaunchTests.swift @@ -0,0 +1,32 @@ +// +// BrewerUITestsLaunchTests.swift +// BrewerUITests +// +// Created by Cédric MAS on 08/07/2024. +// + +import XCTest + +final class BrewerUITestsLaunchTests: XCTestCase { + + override class var runsForEachTargetApplicationUIConfiguration: Bool { + true + } + + override func setUpWithError() throws { + continueAfterFailure = false + } + + func testLaunch() throws { + let app = XCUIApplication() + app.launch() + + // Insert steps here to perform after app launch but before taking a screenshot, + // such as logging into a test account or navigating somewhere in the app + + let attachment = XCTAttachment(screenshot: app.screenshot()) + attachment.name = "Launch Screen" + attachment.lifetime = .keepAlways + add(attachment) + } +}