diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..638e813
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,36 @@
+name: Build
+on: [push, pull_request]
+jobs:
+ build:
+ name: Build
+ runs-on: macos-12
+ steps:
+ - uses: actions/checkout@v3
+ - uses: swift-actions/setup-swift@v1
+ - name: Install Apple Developer Application Certificate
+ env:
+ APPLE_DEVELOPER_CERTIFICATE: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE }}
+ APPLE_DEVELOPER_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_PASSWORD }}
+ APPLE_DEVELOPER_CERTIFICATE_AUTHORITY: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_AUTHORITY }}
+ APPLE_DEVELOPER_KEYCHAIN_PASSWORD: ${{ secrets.APPLE_DEVELOPER_KEYCHAIN_PASSWORD }}
+ run: |
+ CERTIFICATE_PATH="$RUNNER_TEMP/apple-developer-application-certificate.p12"
+ CERTIFICATE_AUTHORITY_PATH="$RUNNER_TEMP/apple-developer-certificate-authority.cer"
+ KEYCHAIN_PATH="$RUNNER_TEMP/apple-developer.keychain-db"
+ echo -n "$APPLE_DEVELOPER_CERTIFICATE" | base64 --decode --output "$CERTIFICATE_PATH"
+ echo -n "$APPLE_DEVELOPER_CERTIFICATE_AUTHORITY" | base64 --decode --output "$CERTIFICATE_AUTHORITY_PATH"
+ security create-keychain -p "$APPLE_DEVELOPER_KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
+ security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
+ security unlock-keychain -p "$APPLE_DEVELOPER_KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
+ security import "$CERTIFICATE_PATH" -P "$APPLE_DEVELOPER_CERTIFICATE_PASSWORD" -A -t cert -f pkcs12 -k "$KEYCHAIN_PATH"
+ security import "$CERTIFICATE_AUTHORITY_PATH" -P "$APPLE_DEVELOPER_CERTIFICATE_PASSWORD" -A -t cert -f pkcs7 -k "$KEYCHAIN_PATH"
+ security list-keychain -d user -s "$KEYCHAIN_PATH"
+ - name: Archive Mist
+ run: xcodebuild -scheme Mist clean archive -configuration release -archivePath Mist -quiet
+ - name: Export Mist
+ run: xcodebuild -exportArchive -archivePath Mist.xcarchive -exportPath Export -exportOptionsPlist ExportOptions.plist
+ - name: Print Mist version
+ run: defaults read "$GITHUB_WORKSPACE/Export/Mist.app/Contents/Info.plist" CFBundleShortVersionString
+ - name: Remove Apple Developer Keychain
+ if: ${{ always() }}
+ run: security delete-keychain $RUNNER_TEMP/apple-developer.keychain-db
diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml
new file mode 100644
index 0000000..1a5301c
--- /dev/null
+++ b/.github/workflows/linting.yml
@@ -0,0 +1,24 @@
+name: Linting
+on: [push, pull_request]
+jobs:
+ linting:
+ name: Linting
+ runs-on: macos-12
+ steps:
+ - uses: actions/checkout@v3
+ - name: Print SwiftLint version
+ run: swiftlint --version
+ - name: Run SwiftLint
+ run: swiftlint --strict
+ - name: Download DrString
+ run: curl --location --remote-name https://github.com/dduan/DrString/releases/latest/download/drstring-x86_64-apple-darwin.tar.gz
+ - name: Extract DrString
+ run: |
+ mkdir drstring-x86_64-apple-darwin
+ tar --extract --file drstring-x86_64-apple-darwin.tar.gz --directory drstring-x86_64-apple-darwin
+ - name: Add DrString to $PATH
+ run: echo "$GITHUB_WORKSPACE/drstring-x86_64-apple-darwin" >> $GITHUB_PATH
+ - name: Print DrString version
+ run: drstring --version
+ - name: Run DrString
+ run: drstring check --config-file .drstring.toml
diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml
new file mode 100644
index 0000000..2f109bf
--- /dev/null
+++ b/.github/workflows/unit_tests.yml
@@ -0,0 +1,10 @@
+name: Unit Tests
+on: [push, pull_request]
+jobs:
+ unit_tests:
+ name: Unit Tests
+ runs-on: macos-12
+ steps:
+ - uses: actions/checkout@v3
+ - name: Run Unit Tests
+ run: xcodebuild -scheme MistTests clean test -quiet || true
diff --git a/.swiftlint.yml b/.swiftlint.yml
index defcca0..36cc38a 100644
--- a/.swiftlint.yml
+++ b/.swiftlint.yml
@@ -1,9 +1,9 @@
excluded:
- .build
-closure_body_length:
- warning: 20
- error: 40
+large_tuple:
+ warning: 3
+ error: 5
line_length:
warning: 200
diff --git a/ExportOptions.plist b/ExportOptions.plist
new file mode 100644
index 0000000..055f67c
--- /dev/null
+++ b/ExportOptions.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ method
+ developer-id
+
+
diff --git a/Mist.xcodeproj/project.pbxproj b/Mist.xcodeproj/project.pbxproj
index 8d01f1a..342ee08 100644
--- a/Mist.xcodeproj/project.pbxproj
+++ b/Mist.xcodeproj/project.pbxproj
@@ -26,7 +26,7 @@
390451DC28573F1000E0B563 /* Dictionary+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 390451DB28573F1000E0B563 /* Dictionary+Extension.swift */; };
390451DF28573FAA00E0B563 /* Yams in Frameworks */ = {isa = PBXBuildFile; productRef = 390451DE28573FAA00E0B563 /* Yams */; };
390451E1285740E800E0B563 /* Sequence+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 390451E0285740E800E0B563 /* Sequence+Extension.swift */; };
- 390451E528574F0000E0B563 /* Catalog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 390451E428574F0000E0B563 /* Catalog.swift */; };
+ 390451E528574F0000E0B563 /* CatalogType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 390451E428574F0000E0B563 /* CatalogType.swift */; };
390451E72857510C00E0B563 /* TextTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 390451E62857510B00E0B563 /* TextTag.swift */; };
39148CFC28DD55B300011FF5 /* PathControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39148CFB28DD55B300011FF5 /* PathControl.swift */; };
39252A77285A849F00956C74 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39252A76285A849F00956C74 /* AppDelegate.swift */; };
@@ -94,7 +94,9 @@
398734D028603D9E00B4C357 /* UInt8+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 398734CF28603D9E00B4C357 /* UInt8+Extension.swift */; };
398734D228603DE700B4C357 /* Array+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 398734D128603DE700B4C357 /* Array+Extension.swift */; };
398734D4286046B000B4C357 /* UInt32+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 398734D3286046B000B4C357 /* UInt32+Extension.swift */; };
- 39CF4E732859C03D009E708C /* CatalogRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CF4E722859C03D009E708C /* CatalogRow.swift */; };
+ 39CB5E3D293F5C2E00CFDBB8 /* Catalog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CB5E3C293F5C2E00CFDBB8 /* Catalog.swift */; };
+ 39CB5E3F2941486D00CFDBB8 /* CatalogSeedType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CB5E3E2941486D00CFDBB8 /* CatalogSeedType.swift */; };
+ 39CB5E5429418A2900CFDBB8 /* MistTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CB5E5329418A2900CFDBB8 /* MistTests.swift */; };
39CF55A028614DD8006FB5D2 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 39CF559F28614DD8006FB5D2 /* main.swift */; };
39CF55AA286154A5006FB5D2 /* Blessed in Frameworks */ = {isa = PBXBuildFile; productRef = 39CF55A9286154A5006FB5D2 /* Blessed */; };
39CF55AB286154D1006FB5D2 /* com.ninxsoft.mist.helper in CopyFiles */ = {isa = PBXBuildFile; fileRef = 39CF559D28614DD8006FB5D2 /* com.ninxsoft.mist.helper */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
@@ -173,7 +175,7 @@
390451D928573ADC00E0B563 /* ExportListType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExportListType.swift; sourceTree = ""; };
390451DB28573F1000E0B563 /* Dictionary+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dictionary+Extension.swift"; sourceTree = ""; };
390451E0285740E800E0B563 /* Sequence+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sequence+Extension.swift"; sourceTree = ""; };
- 390451E428574F0000E0B563 /* Catalog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Catalog.swift; sourceTree = ""; };
+ 390451E428574F0000E0B563 /* CatalogType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CatalogType.swift; sourceTree = ""; };
390451E62857510B00E0B563 /* TextTag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextTag.swift; sourceTree = ""; };
39148CFB28DD55B300011FF5 /* PathControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathControl.swift; sourceTree = ""; };
39252A76285A849F00956C74 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
@@ -237,7 +239,10 @@
398734CF28603D9E00B4C357 /* UInt8+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UInt8+Extension.swift"; sourceTree = ""; };
398734D128603DE700B4C357 /* Array+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Extension.swift"; sourceTree = ""; };
398734D3286046B000B4C357 /* UInt32+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UInt32+Extension.swift"; sourceTree = ""; };
- 39CF4E722859C03D009E708C /* CatalogRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CatalogRow.swift; sourceTree = ""; };
+ 39CB5E3C293F5C2E00CFDBB8 /* Catalog.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Catalog.swift; sourceTree = ""; };
+ 39CB5E3E2941486D00CFDBB8 /* CatalogSeedType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CatalogSeedType.swift; sourceTree = ""; };
+ 39CB5E5129418A2900CFDBB8 /* MistTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MistTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 39CB5E5329418A2900CFDBB8 /* MistTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MistTests.swift; sourceTree = ""; };
39CF559D28614DD8006FB5D2 /* com.ninxsoft.mist.helper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = com.ninxsoft.mist.helper; sourceTree = BUILT_PRODUCTS_DIR; };
39CF559F28614DD8006FB5D2 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; };
39CF55A528614E66006FB5D2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
@@ -281,6 +286,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 39CB5E4E29418A2900CFDBB8 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
39CF559A28614DD8006FB5D2 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -299,6 +311,7 @@
390451A82856E1D900E0B563 /* Mist */,
39CF559E28614DD8006FB5D2 /* MistHelperTool */,
39CF55D4286162DC006FB5D2 /* Shared */,
+ 39CB5E5229418A2900CFDBB8 /* MistTests */,
390451A72856E1D900E0B563 /* Products */,
);
sourceTree = "";
@@ -308,6 +321,7 @@
children = (
390451A62856E1D900E0B563 /* Mist.app */,
39CF559D28614DD8006FB5D2 /* com.ninxsoft.mist.helper */,
+ 39CB5E5129418A2900CFDBB8 /* MistTests.xctest */,
);
name = Products;
sourceTree = "";
@@ -379,8 +393,9 @@
390451C32856E4A500E0B563 /* Model */ = {
isa = PBXGroup;
children = (
- 390451E428574F0000E0B563 /* Catalog.swift */,
- 39CF4E722859C03D009E708C /* CatalogRow.swift */,
+ 39CB5E3C293F5C2E00CFDBB8 /* Catalog.swift */,
+ 390451E428574F0000E0B563 /* CatalogType.swift */,
+ 39CB5E3E2941486D00CFDBB8 /* CatalogSeedType.swift */,
398734CB28603D5F00B4C357 /* Chunklist.swift */,
398734CD28603D7F00B4C357 /* Chunk.swift */,
395DCD15287FE36E00C411CE /* DownloadAlertType.swift */,
@@ -452,6 +467,14 @@
path = Refresh;
sourceTree = "";
};
+ 39CB5E5229418A2900CFDBB8 /* MistTests */ = {
+ isa = PBXGroup;
+ children = (
+ 39CB5E5329418A2900CFDBB8 /* MistTests.swift */,
+ );
+ path = MistTests;
+ sourceTree = "";
+ };
39CF559E28614DD8006FB5D2 /* MistHelperTool */ = {
isa = PBXGroup;
children = (
@@ -545,6 +568,23 @@
productReference = 390451A62856E1D900E0B563 /* Mist.app */;
productType = "com.apple.product-type.application";
};
+ 39CB5E5029418A2900CFDBB8 /* MistTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 39CB5E5529418A2900CFDBB8 /* Build configuration list for PBXNativeTarget "MistTests" */;
+ buildPhases = (
+ 39CB5E4D29418A2900CFDBB8 /* Sources */,
+ 39CB5E4E29418A2900CFDBB8 /* Frameworks */,
+ 39CB5E4F29418A2900CFDBB8 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = MistTests;
+ productName = MistTests;
+ productReference = 39CB5E5129418A2900CFDBB8 /* MistTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
39CF559C28614DD8006FB5D2 /* MistHelperTool */ = {
isa = PBXNativeTarget;
buildConfigurationList = 39CF55A128614DD8006FB5D2 /* Build configuration list for PBXNativeTarget "MistHelperTool" */;
@@ -574,12 +614,15 @@
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
- LastSwiftUpdateCheck = 1400;
+ LastSwiftUpdateCheck = 1410;
LastUpgradeCheck = 1410;
TargetAttributes = {
390451A52856E1D900E0B563 = {
CreatedOnToolsVersion = 14.0;
};
+ 39CB5E5029418A2900CFDBB8 = {
+ CreatedOnToolsVersion = 14.1;
+ };
39CF559C28614DD8006FB5D2 = {
CreatedOnToolsVersion = 14.0;
};
@@ -606,6 +649,7 @@
targets = (
390451A52856E1D900E0B563 /* Mist */,
39CF559C28614DD8006FB5D2 /* MistHelperTool */,
+ 39CB5E5029418A2900CFDBB8 /* MistTests */,
);
};
/* End PBXProject section */
@@ -619,6 +663,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 39CB5E4F29418A2900CFDBB8 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
@@ -740,6 +791,7 @@
398734C428600E6E00B4C357 /* TaskManager.swift in Sources */,
390451D62856F7FE00E0B563 /* UInt64+Extension.swift in Sources */,
3935F47E2864813B00760AB0 /* DownloadManager.swift in Sources */,
+ 39CB5E3D293F5C2E00CFDBB8 /* Catalog.swift in Sources */,
39252AA1285C2A1600956C74 /* PaddedDivider.swift in Sources */,
39CF560C2861AE93006FB5D2 /* HelperToolCommandResponse.swift in Sources */,
39252A99285BFE2C00956C74 /* MistTaskState.swift in Sources */,
@@ -767,14 +819,14 @@
390451C22856E3F500E0B563 /* Hardware.swift in Sources */,
39CF56092861AE7F006FB5D2 /* HelperToolCommandRequest.swift in Sources */,
390451C82856E94900E0B563 /* FirmwareListRow.swift in Sources */,
- 39CF4E732859C03D009E708C /* CatalogRow.swift in Sources */,
- 390451E528574F0000E0B563 /* Catalog.swift in Sources */,
+ 390451E528574F0000E0B563 /* CatalogType.swift in Sources */,
3935F4852866B64900760AB0 /* MistTaskSection.swift in Sources */,
390451AC2856E1D900E0B563 /* ContentView.swift in Sources */,
3935F4A4286AD21000760AB0 /* DownloadProgressView.swift in Sources */,
39252A89285AD0AB00956C74 /* SettingsHeaderView.swift in Sources */,
39252A85285ACDC800956C74 /* ResetToDefaultButton.swift in Sources */,
39CF560F2861B857006FB5D2 /* XPCRoute+Extension.swift in Sources */,
+ 39CB5E3F2941486D00CFDBB8 /* CatalogSeedType.swift in Sources */,
39252A7F285AC6F600956C74 /* SettingsPackagesView.swift in Sources */,
39CF562F2862A797006FB5D2 /* ISOConverter.swift in Sources */,
39CF56392862D75D006FB5D2 /* FileCreator.swift in Sources */,
@@ -795,6 +847,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
+ 39CB5E4D29418A2900CFDBB8 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 39CB5E5429418A2900CFDBB8 /* MistTests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
39CF559928614DD8006FB5D2 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -996,6 +1056,46 @@
};
name = Release;
};
+ 39CB5E5629418A2900CFDBB8 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application";
+ CODE_SIGN_STYLE = Manual;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = "";
+ "DEVELOPMENT_TEAM[sdk=macosx*]" = 7K3HVCLV7Z;
+ GENERATE_INFOPLIST_FILE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 13.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.ninxsoft.mist.tests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ 39CB5E5729418A2900CFDBB8 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
+ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Developer ID Application";
+ CODE_SIGN_STYLE = Manual;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = "";
+ "DEVELOPMENT_TEAM[sdk=macosx*]" = 7K3HVCLV7Z;
+ GENERATE_INFOPLIST_FILE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 13.0;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.ninxsoft.mist.tests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ };
+ name = Release;
+ };
39CF55A228614DD8006FB5D2 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -1085,6 +1185,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
+ 39CB5E5529418A2900CFDBB8 /* Build configuration list for PBXNativeTarget "MistTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 39CB5E5629418A2900CFDBB8 /* Debug */,
+ 39CB5E5729418A2900CFDBB8 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
39CF55A128614DD8006FB5D2 /* Build configuration list for PBXNativeTarget "MistHelperTool" */ = {
isa = XCConfigurationList;
buildConfigurations = (
diff --git a/Mist.xcodeproj/xcshareddata/xcschemes/MistHelperTool.xcscheme b/Mist.xcodeproj/xcshareddata/xcschemes/MistHelperTool.xcscheme
index 89fe2b4..6e14a6f 100644
--- a/Mist.xcodeproj/xcshareddata/xcschemes/MistHelperTool.xcscheme
+++ b/Mist.xcodeproj/xcshareddata/xcschemes/MistHelperTool.xcscheme
@@ -39,7 +39,8 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
- allowLocationSimulation = "YES">
+ allowLocationSimulation = "YES"
+ viewDebuggingEnabled = "No">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Mist/Helpers/DownloadManager.swift b/Mist/Helpers/DownloadManager.swift
index 2623c3e..957b25d 100644
--- a/Mist/Helpers/DownloadManager.swift
+++ b/Mist/Helpers/DownloadManager.swift
@@ -16,6 +16,7 @@ class DownloadManager: NSObject, ObservableObject {
progress.fractionCompleted
}
+ // swiftlint:disable:next cyclomatic_complexity function_body_length
func download(_ url: URL, to destination: URL, retries retriesMaximum: Int, delay retryDelay: Int) async throws {
guard !FileManager.default.fileExists(atPath: destination.path) else {
@@ -27,6 +28,7 @@ class DownloadManager: NSObject, ObservableObject {
var urlError: URLError?
var retries: Int = 0
var completed: Bool = false
+ // swiftlint:disable:next closure_body_length
let completionHandler: (URL?, URLResponse?, Error?) -> Void = { url, _, error in
if let error: URLError = error as? URLError {
diff --git a/Mist/Helpers/TaskManager.swift b/Mist/Helpers/TaskManager.swift
index de27903..21027ae 100644
--- a/Mist/Helpers/TaskManager.swift
+++ b/Mist/Helpers/TaskManager.swift
@@ -87,8 +87,7 @@ class TaskManager: ObservableObject {
]
}
- // swiftlint:disable function_parameter_count
-
+ // swiftlint:disable:next function_parameter_count
private static func firmwareDownloadTasks(
firmware: Firmware,
firmwareURL: URL,
@@ -120,8 +119,6 @@ class TaskManager: ObservableObject {
return tasks
}
- // swiftlint:enable function_parameter_count
-
private static func firmwareCleanupTasks(temporaryDirectory temporaryDirectoryURL: URL) -> [MistTask] {
[
MistTask(type: .remove, description: "temporary directory") {
@@ -130,8 +127,7 @@ class TaskManager: ObservableObject {
]
}
- // swiftlint:disable function_parameter_count
-
+ // swiftlint:disable:next function_body_length function_parameter_count
static func taskGroups(
for installer: Installer,
destination destinationURL: URL?,
@@ -225,8 +221,6 @@ class TaskManager: ObservableObject {
return taskGroups
}
- // swiftlint:enable function_parameter_count
-
private static func downloadTasks(for installer: Installer, cacheDirectory cacheDirectoryURL: URL, retries: Int, delay retryDelay: Int) throws -> [MistTask] {
var tasks: [MistTask] = []
@@ -292,8 +286,7 @@ class TaskManager: ObservableObject {
]
}
- // swiftlint:disable function_parameter_count
-
+ // swiftlint:disable:next function_parameter_count
private static func diskImageTasks(
for installer: Installer,
filename: String,
@@ -338,8 +331,6 @@ class TaskManager: ObservableObject {
return tasks
}
- // swiftlint:enable function_parameter_count
-
private static func isoTasks(for installer: Installer, filename: String, destination destinationURL: URL, temporaryDirectory temporaryDirectoryURL: URL) -> [MistTask] {
let temporaryImageURL: URL = temporaryDirectoryURL.appendingPathComponent("\(installer.id).dmg")
@@ -369,8 +360,7 @@ class TaskManager: ObservableObject {
]
}
- // swiftlint:disable function_parameter_count
-
+ // swiftlint:disable:next function_parameter_count
private static func packageTasks(
for installer: Installer,
filename: String,
@@ -411,8 +401,6 @@ class TaskManager: ObservableObject {
return tasks
}
- // swiftlint:enable function_parameter_count
-
private static func cleanupTasks(mountPoint mountPointURL: URL, temporaryDirectory temporaryDirectoryURL: URL, cacheDownloads: Bool, cacheDirectory cacheDirectoryURL: URL) -> [MistTask] {
var tasks: [MistTask] = [
diff --git a/Mist/Model/Catalog.swift b/Mist/Model/Catalog.swift
index b9f5a3b..9bb50aa 100644
--- a/Mist/Model/Catalog.swift
+++ b/Mist/Model/Catalog.swift
@@ -2,32 +2,61 @@
// Catalog.swift
// Mist
//
-// Created by Nindi Gill on 13/6/2022.
+// Created by Nindi Gill on 6/12/2022.
//
import Foundation
-enum Catalog: String, CaseIterable {
- // swiftlint:disable redundant_string_enum_value
- case standard = "standard"
- case customer = "customer"
- case developer = "developer"
- case `public` = "public"
+struct Catalog: Identifiable, Decodable, Equatable {
- static var urls: [String] {
- self.allCases.map { $0.url }
+ enum CodingKeys: String, CodingKey {
+ // swiftlint:disable:next redundant_string_enum_value
+ case type = "type"
+ // swiftlint:disable:next redundant_string_enum_value
+ case standard = "standard"
+ // swiftlint:disable:next redundant_string_enum_value
+ case customerSeed = "customerSeed"
+ // swiftlint:disable:next redundant_string_enum_value
+ case developerSeed = "developerSeed"
+ // swiftlint:disable:next redundant_string_enum_value
+ case publicSeed = "publicSeed"
}
- var url: String {
- switch self {
- case .standard:
- return "https://swscan.apple.com/content/catalogs/others/index-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz"
- case .customer:
- return "https://swscan.apple.com/content/catalogs/others/index-12customerseed-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz"
- case .developer:
- return "https://swscan.apple.com/content/catalogs/others/index-12seed-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz"
- case .`public`:
- return "https://swscan.apple.com/content/catalogs/others/index-12beta-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz"
- }
+ static var example: Catalog {
+ Catalog(type: .ventura, standard: true, customerSeed: false, developerSeed: false, publicSeed: false)
+ }
+
+ var id: UUID = UUID()
+ var type: CatalogType
+ var standard: Bool
+ var customerSeed: Bool
+ var developerSeed: Bool
+ var publicSeed: Bool
+
+ init(type: CatalogType, standard: Bool, customerSeed: Bool, developerSeed: Bool, publicSeed: Bool) {
+ self.type = type
+ self.standard = standard
+ self.customerSeed = customerSeed
+ self.developerSeed = developerSeed
+ self.publicSeed = publicSeed
+ }
+
+ init(from decoder: Decoder) throws {
+ let container: KeyedDecodingContainer = try decoder.container(keyedBy: CodingKeys.self)
+ type = try container.decode(CatalogType.self, forKey: .type)
+ standard = try container.decode(Bool.self, forKey: .standard)
+ customerSeed = try container.decode(Bool.self, forKey: .customerSeed)
+ developerSeed = try container.decode(Bool.self, forKey: .developerSeed)
+ publicSeed = try container.decode(Bool.self, forKey: .publicSeed)
+ }
+
+ func dictionary() -> [String: Any] {
+ [
+ "type": type.description,
+ "standard": standard,
+ "customerSeed": customerSeed,
+ "developerSeed": developerSeed,
+ "publicSeed": publicSeed
+ ]
}
}
diff --git a/Mist/Model/CatalogRow.swift b/Mist/Model/CatalogRow.swift
deleted file mode 100644
index 9ab13a5..0000000
--- a/Mist/Model/CatalogRow.swift
+++ /dev/null
@@ -1,18 +0,0 @@
-//
-// CatalogRow.swift
-// Mist
-//
-// Created by Nindi Gill on 15/6/2022.
-//
-
-import Foundation
-
-struct CatalogRow: Identifiable, Hashable {
-
- static var example: CatalogRow {
- CatalogRow(url: Catalog.standard.url)
- }
-
- var id: UUID = UUID()
- var url: String
-}
diff --git a/Mist/Model/CatalogSeedType.swift b/Mist/Model/CatalogSeedType.swift
new file mode 100644
index 0000000..c329d86
--- /dev/null
+++ b/Mist/Model/CatalogSeedType.swift
@@ -0,0 +1,19 @@
+//
+// CatalogSeedType.swift
+// Mist
+//
+// Created by Nindi Gill on 8/12/2022.
+//
+
+import Foundation
+
+enum CatalogSeedType: String {
+ case standard = "Standard"
+ case customer = "Customer"
+ case developer = "Developer"
+ case `public` = "Public"
+
+ var description: String {
+ rawValue
+ }
+}
diff --git a/Mist/Model/CatalogType.swift b/Mist/Model/CatalogType.swift
new file mode 100644
index 0000000..f09af2c
--- /dev/null
+++ b/Mist/Model/CatalogType.swift
@@ -0,0 +1,80 @@
+//
+// CatalogType.swift
+// Mist
+//
+// Created by Nindi Gill on 13/6/2022.
+//
+
+import Foundation
+
+enum CatalogType: String, CaseIterable, Comparable, Decodable {
+ case ventura = "macOS Ventura"
+ case monterey = "macOS Monterey"
+ case bigSur = "macOS Big Sur"
+
+ var description: String {
+ rawValue
+ }
+
+ var imageName: String {
+ rawValue
+ }
+
+ private var sortOrder: Int {
+ switch self {
+ case .ventura:
+ return 0
+ case .monterey:
+ return 1
+ case .bigSur:
+ return 2
+ }
+ }
+
+ static func < (lhs: CatalogType, rhs: CatalogType) -> Bool {
+ lhs.sortOrder < rhs.sortOrder
+ }
+
+ // swiftlint:disable:next cyclomatic_complexity
+ func url(for seedType: CatalogSeedType) -> String {
+
+ switch self {
+ case .ventura:
+ switch seedType {
+ case .standard:
+ return "https://swscan.apple.com/content/catalogs/others/index-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz"
+ case .customer:
+ // swiftlint:disable:next line_length
+ return "https://swscan.apple.com/content/catalogs/others/index-13customerseed-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz"
+ case .developer:
+ return "https://swscan.apple.com/content/catalogs/others/index-13seed-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz"
+ case .public:
+ return "https://swscan.apple.com/content/catalogs/others/index-13beta-13-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz"
+ }
+ case .monterey:
+ switch seedType {
+ case .standard:
+ return "https://swscan.apple.com/content/catalogs/others/index-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz"
+ case .customer:
+ // swiftlint:disable:next line_length
+ return "https://swscan.apple.com/content/catalogs/others/index-12customerseed-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz"
+ case .developer:
+ return "https://swscan.apple.com/content/catalogs/others/index-12seed-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz"
+ case .public:
+ return "https://swscan.apple.com/content/catalogs/others/index-12beta-12-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz"
+ }
+ case .bigSur:
+ switch seedType {
+ case .standard:
+ return "https://swscan.apple.com/content/catalogs/others/index-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz"
+ case .customer:
+ // swiftlint:disable:next line_length
+ return "https://swscan.apple.com/content/catalogs/others/index-10.16customerseed-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz"
+ case .developer:
+ return "https://swscan.apple.com/content/catalogs/others/index-10.16seed-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz"
+ case .public:
+ return "https://swscan.apple.com/content/catalogs/others/index-10.16beta-10.16-10.15-10.14-10.13-10.12-10.11-10.10-10.9-mountainlion-lion-snowleopard-leopard.merged-1.sucatalog.gz"
+ }
+ }
+ }
+}
diff --git a/Mist/Model/ExportListType.swift b/Mist/Model/ExportListType.swift
index 05561ce..2ba7ee7 100644
--- a/Mist/Model/ExportListType.swift
+++ b/Mist/Model/ExportListType.swift
@@ -9,10 +9,13 @@ import Foundation
import UniformTypeIdentifiers
enum ExportListType: String, CaseIterable, Identifiable {
- // swiftlint:disable redundant_string_enum_value
+ // swiftlint:disable:next redundant_string_enum_value
case csv = "csv"
+ // swiftlint:disable:next redundant_string_enum_value
case json = "json"
+ // swiftlint:disable:next redundant_string_enum_value
case plist = "plist"
+ // swiftlint:disable:next redundant_string_enum_value
case yaml = "yaml"
var id: String {
diff --git a/Mist/Views/Download/DownloadView.swift b/Mist/Views/Download/DownloadView.swift
index f4f66a5..d0b4bfd 100644
--- a/Mist/Views/Download/DownloadView.swift
+++ b/Mist/Views/Download/DownloadView.swift
@@ -140,7 +140,7 @@ struct DownloadView: View {
private func checkForUserCancellation(_ failure: Error) -> Bool {
- if let _: CancellationError = failure as? CancellationError {
+ if failure as? CancellationError != nil {
return true
}
diff --git a/Mist/Views/List/InstallerListRow.swift b/Mist/Views/List/InstallerListRow.swift
index 4754baa..d256292 100644
--- a/Mist/Views/List/InstallerListRow.swift
+++ b/Mist/Views/List/InstallerListRow.swift
@@ -71,7 +71,6 @@ struct InstallerListRow: View {
openPanel.accessoryView = NSHostingView(rootView: InstallerExportView(exports: $exports))
openPanel.isAccessoryViewDisclosed = true
- // swiftlint:disable:next closure_body_length
Task {
let response: NSApplication.ModalResponse = openPanel.runModal()
diff --git a/Mist/Views/Refresh/RefreshView.swift b/Mist/Views/Refresh/RefreshView.swift
index 9e726fe..c9cef57 100644
--- a/Mist/Views/Refresh/RefreshView.swift
+++ b/Mist/Views/Refresh/RefreshView.swift
@@ -7,6 +7,7 @@
import SwiftUI
+// swiftlint:disable:next type_body_length
struct RefreshView: View {
@Environment(\.presentationMode) var presentationMode: Binding
@Binding var firmwares: [Firmware]
@@ -124,11 +125,11 @@ struct RefreshView: View {
private func retrieveInstallers() throws -> [Installer] {
var installers: [Installer] = []
- let catalogs: [String] = UserDefaults.standard.array(forKey: "catalogURLs") as? [String] ?? Catalog.urls
+ let catalogURLs: [String] = getCatalogURLs()
- for catalog in catalogs {
+ for catalogURL in catalogURLs {
- guard let url: URL = URL(string: catalog) else {
+ guard let url: URL = URL(string: catalogURL) else {
continue
}
@@ -161,6 +162,49 @@ struct RefreshView: View {
return installers
}
+ private func getCatalogURLs() -> [String] {
+
+ var catalogURLs: [String] = []
+ var catalogs: [Catalog] = []
+ let defaultCatalogs: [Catalog] = CatalogType.allCases.map { Catalog(type: $0, standard: true, customerSeed: false, developerSeed: false, publicSeed: false) }
+
+ if let array: [[String: Any]] = UserDefaults.standard.array(forKey: "catalogs") as? [[String: Any]] {
+ do {
+ catalogs = try JSONDecoder().decode([Catalog].self, from: JSONSerialization.data(withJSONObject: array))
+ let catalogTypes: [CatalogType] = catalogs.map { $0.type }
+
+ for catalogType in CatalogType.allCases where !catalogTypes.contains(catalogType) {
+ let catalog: Catalog = Catalog(type: catalogType, standard: true, customerSeed: false, developerSeed: false, publicSeed: false)
+ catalogs.append(catalog)
+ }
+ } catch {
+ catalogs = defaultCatalogs
+ }
+ } else {
+ catalogs = defaultCatalogs
+ }
+
+ for catalog in catalogs {
+ if catalog.standard {
+ catalogURLs.append(catalog.type.url(for: .standard))
+ }
+
+ if catalog.customerSeed {
+ catalogURLs.append(catalog.type.url(for: .customer))
+ }
+
+ if catalog.developerSeed {
+ catalogURLs.append(catalog.type.url(for: .developer))
+ }
+
+ if catalog.publicSeed {
+ catalogURLs.append(catalog.type.url(for: .public))
+ }
+ }
+
+ return catalogURLs
+ }
+
private func getInstallers(from dictionary: [String: Any]) -> [Installer] {
var installers: [Installer] = []
diff --git a/Mist/Views/Settings/SettingsInstallersCatalogsView.swift b/Mist/Views/Settings/SettingsInstallersCatalogsView.swift
index 7dc332e..6aa59bb 100644
--- a/Mist/Views/Settings/SettingsInstallersCatalogsView.swift
+++ b/Mist/Views/Settings/SettingsInstallersCatalogsView.swift
@@ -8,47 +8,81 @@
import SwiftUI
struct SettingsInstallersCatalogsView: View {
- @Binding var catalogRows: [CatalogRow]
- @Binding var selectedCatalogRow: CatalogRow?
+ @Binding var catalogs: [Catalog]
+ // swiftlint:disable:next line_length
+ private let description: String = "Apple Software Update Catalogs are used to determine available macOS Installers.\n\n- **Standard:** The default catalog that ships with macOS\n- **Customer Seed:** The catalog available as part of the [AppleSeed Program](https://appleseed.apple.com/)\n- **Developer Seed:** The catalog available as part of the [Apple Developer Program](https://developer.apple.com/programs/)\n- **Public Seed:** The catalog available as part of the [Apple Beta Software Program](https://beta.apple.com/)\n\n**Note:** Catalogs from the Seed Programs may contain beta / unreleased versions of macOS. Ensure you are a member of these programs before proceeding."
+ private let height: CGFloat = 120
+ private let width: CGFloat = 150
private let length: CGFloat = 16
- private let height: CGFloat = 200
var body: some View {
VStack(alignment: .leading) {
- Text("Catalog URLs:")
- FooterText("Apple Software Update Catalogs are used to determine all available macOS Installers.")
- List(selection: $selectedCatalogRow) {
- ForEach($catalogRows) { catalogRow in
- HStack {
- ScaledSystemImage(systemName: "line.3.horizontal", length: length)
- .foregroundColor(.secondary)
- TextEditor(text: catalogRow.url)
- }
+ Text("Software Update Catalogs:")
+ FooterText(description)
+ Table(catalogs) {
+ TableColumn("") { catalog in
+ ScaledImage(name: catalog.type.imageName, length: length)
}
- .onMove { indexSet, offset in
- catalogRows.move(fromOffsets: indexSet, toOffset: offset)
+ .width(length)
+ TableColumn("Catalog Type") { catalog in
+ Text(catalog.type.description)
}
- .onDelete { indexSet in
- catalogRows.remove(atOffsets: indexSet)
- }
- }
- .frame(minHeight: height)
- HStack {
- Spacer()
- Button("Add") {
- addCatalog()
+ .width(width)
+ TableColumn(CatalogSeedType.standard.description) { catalog in
+ toggle(.standard, using: catalog)
+ }
+ TableColumn(CatalogSeedType.customer.description) { catalog in
+ toggle(.customer, using: catalog)
+ }
+ TableColumn(CatalogSeedType.developer.description) { catalog in
+ toggle(.developer, using: catalog)
+ }
+ TableColumn(CatalogSeedType.public.description) { catalog in
+ toggle(.public, using: catalog)
}
}
+ .tableStyle(.bordered)
+ .frame(minHeight: height, maxHeight: height)
}
}
- private func addCatalog() {
- catalogRows.append(CatalogRow(url: "https://"))
+ private func toggle(_ catalogSeedType: CatalogSeedType, using catalog: Catalog) -> some View {
+ Toggle(catalogSeedType.description, isOn: Binding(
+ get: {
+ switch catalogSeedType {
+ case .standard:
+ return catalog.standard
+ case .customer:
+ return catalog.customerSeed
+ case .developer:
+ return catalog.developerSeed
+ case .public:
+ return catalog.publicSeed
+ }
+ },
+ set: {
+ guard let index: Int = catalogs.firstIndex(where: { $0.id == catalog.id }) else {
+ return
+ }
+
+ switch catalogSeedType {
+ case .standard:
+ catalogs[index].standard = $0
+ case .customer:
+ catalogs[index].customerSeed = $0
+ case .developer:
+ catalogs[index].developerSeed = $0
+ case .public:
+ catalogs[index].publicSeed = $0
+ }
+ }
+ ))
+ .labelsHidden()
}
}
struct SettingsInstallersCatalogsView_Previews: PreviewProvider {
static var previews: some View {
- SettingsInstallersCatalogsView(catalogRows: .constant([.example]), selectedCatalogRow: .constant(.example))
+ SettingsInstallersCatalogsView(catalogs: .constant([.example]))
}
}
diff --git a/Mist/Views/Settings/SettingsInstallersView.swift b/Mist/Views/Settings/SettingsInstallersView.swift
index e9cf193..cbaa917 100644
--- a/Mist/Views/Settings/SettingsInstallersView.swift
+++ b/Mist/Views/Settings/SettingsInstallersView.swift
@@ -10,11 +10,10 @@ import SwiftUI
struct SettingsInstallersView: View {
@AppStorage("cacheDownloads") private var cacheDownloads: Bool = false
@AppStorage("cacheDirectory") private var cacheDirectory: String = .cacheDirectory
- @State private var catalogRows: [CatalogRow] = []
- @State private var selectedCatalogRow: CatalogRow?
+ @State private var catalogs: [Catalog] = []
private let cacheDownloadsDefault: Bool = false
private let cacheDirectoryDefault: String = .cacheDirectory
- private var defaultCatalogRows: [CatalogRow] = Catalog.urls.map { CatalogRow(url: $0) }
+ private let defaultCatalogs: [Catalog] = CatalogType.allCases.map { Catalog(type: $0, standard: true, customerSeed: false, developerSeed: false, publicSeed: false) }
private let imageName: String = "Installer"
private let title: String = "Installers"
private let description: String = "macOS Installers are a collection of files that can be used to build macOS Installer **Applications**, **Disk Images**, **ISOs** and **Packages**."
@@ -25,7 +24,7 @@ struct SettingsInstallersView: View {
PaddedDivider()
SettingsInstallersCacheView(cacheDownloads: $cacheDownloads, cacheDirectory: $cacheDirectory)
PaddedDivider()
- SettingsInstallersCatalogsView(catalogRows: $catalogRows, selectedCatalogRow: $selectedCatalogRow)
+ SettingsInstallersCatalogsView(catalogs: $catalogs)
PaddedDivider()
ResetToDefaultButton {
reset()
@@ -33,27 +32,38 @@ struct SettingsInstallersView: View {
}
.padding()
.onAppear {
- populateCatalogURLs()
+ catalogs = getCatalogs()
}
- .onChange(of: catalogRows) { catalogRows in
- UserDefaults.standard.setValue(catalogRows.map { $0.url }, forKey: "catalogURLs")
+ .onChange(of: catalogs) { catalogs in
+ UserDefaults.standard.setValue(catalogs.map { $0.dictionary() }, forKey: "catalogs")
}
}
- private func populateCatalogURLs() {
+ private func getCatalogs() -> [Catalog] {
- guard let urls: [String] = UserDefaults.standard.array(forKey: "catalogURLs") as? [String] else {
- catalogRows = defaultCatalogRows
- return
+ guard let array: [[String: Any]] = UserDefaults.standard.array(forKey: "catalogs") as? [[String: Any]] else {
+ return defaultCatalogs
}
- catalogRows = urls.map { CatalogRow(url: $0) }
+ do {
+ var catalogs: [Catalog] = try JSONDecoder().decode([Catalog].self, from: JSONSerialization.data(withJSONObject: array))
+ let catalogTypes: [CatalogType] = catalogs.map { $0.type }
+
+ for catalogType in CatalogType.allCases where !catalogTypes.contains(catalogType) {
+ let catalog: Catalog = Catalog(type: catalogType, standard: true, customerSeed: false, developerSeed: false, publicSeed: false)
+ catalogs.append(catalog)
+ }
+
+ return catalogs.sorted { $0.type < $1.type }
+ } catch {
+ return defaultCatalogs
+ }
}
private func reset() {
cacheDownloads = cacheDownloadsDefault
cacheDirectory = cacheDirectoryDefault
- catalogRows = defaultCatalogRows
+ catalogs = defaultCatalogs
}
}
diff --git a/MistTests/MistTests.swift b/MistTests/MistTests.swift
new file mode 100644
index 0000000..bfca38d
--- /dev/null
+++ b/MistTests/MistTests.swift
@@ -0,0 +1,15 @@
+//
+// MistTests.swift
+// MistTests
+//
+// Created by Nindi Gill on 8/12/2022.
+//
+
+import XCTest
+
+final class MistTests: XCTestCase {
+
+ func test() throws {
+ XCTAssertTrue(true)
+ }
+}
diff --git a/README.md b/README.md
index 88c05fc..968232b 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,8 @@
# MIST - macOS Installer Super Tool
+ [](https://github.com/ninxsoft/Mist/actions/workflows/linting.yml) [](https://github.com/ninxsoft/Mist/actions/workflows/unit_tests.yml) [](https://github.com/ninxsoft/Mist/actions/workflows/build.yml)
+
A Mac utility that automatically downloads macOS Firmwares / Installers:

diff --git a/Shared/HelperToolCommandType.swift b/Shared/HelperToolCommandType.swift
index 43f4482..566547c 100644
--- a/Shared/HelperToolCommandType.swift
+++ b/Shared/HelperToolCommandType.swift
@@ -8,9 +8,12 @@
import Foundation
enum HelperToolCommandType: String, Codable {
- // swiftlint:disable redundant_string_enum_value
+ // swiftlint:disable:next redundant_string_enum_value
case remove = "remove"
+ // swiftlint:disable:next redundant_string_enum_value
case installer = "installer"
+ // swiftlint:disable:next redundant_string_enum_value
case createinstallmedia = "createinstallmedia"
+ // swiftlint:disable:next redundant_string_enum_value
case kill = "kill"
}
diff --git a/Shared/ShellExecutor.swift b/Shared/ShellExecutor.swift
index 2028822..3b17bc1 100644
--- a/Shared/ShellExecutor.swift
+++ b/Shared/ShellExecutor.swift
@@ -13,8 +13,6 @@ class ShellExecutor: NSObject {
static var shared: ShellExecutor = ShellExecutor()
private var process: Process = Process()
- // swiftlint:disable large_tuple
-
/// Executes custom shell commands.
///
/// - Parameters: