Skip to content

Commit

Permalink
Add initial extension functionality (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
lfroms authored Nov 22, 2024
1 parent 728d287 commit 78bc9f3
Show file tree
Hide file tree
Showing 115 changed files with 3,004 additions and 1,461 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/appcast.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
pull-requests: write
steps:
- name: Select Xcode Version
run: sudo xcode-select -switch /Applications/Xcode_15.4.app
run: sudo xcode-select -switch /Applications/Xcode_16.app

- name: Checkout
uses: actions/checkout@v4
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
lint:
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/xcode.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
build:
Expand All @@ -20,7 +19,7 @@ jobs:
run: brew install xcbeautify

- name: Select Xcode Version
run: sudo xcode-select -switch /Applications/Xcode_15.4.app
run: sudo xcode-select -switch /Applications/Xcode_16.app

- name: Run Tests
run: set -o pipefail && xcodebuild test -project Tophat.xcodeproj -scheme TophatTests -destination 'platform=macOS,arch=arm64' CODE_SIGNING_ALLOWED=NO | xcbeautify
357 changes: 288 additions & 69 deletions Tophat.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

13 changes: 12 additions & 1 deletion Tophat.xcodeproj/xcshareddata/xcschemes/TophatTests.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,18 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES">
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "7F35024E24A5060500EE76EA"
BuildableName = "Tophat.app"
BlueprintName = "Tophat"
ReferencedContainer = "container:Tophat.xcodeproj">
</BuildableReference>
</CodeCoverageTargets>
<Testables>
<TestableReference
skipped = "NO">
Expand Down
12 changes: 0 additions & 12 deletions Tophat/Assets.xcassets/GoogleCloudSDK.imageset/Contents.json

This file was deleted.

Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
// ArtifactDownloaderError+LocalizedError.swift
// Tophat
//
// Created by Lukas Romsicki on 2023-04-14.
// Copyright © 2023 Shopify. All rights reserved.
// Created by Lukas Romsicki on 2024-11-21.
// Copyright © 2024 Shopify. All rights reserved.
//

import Foundation
Expand All @@ -12,27 +12,21 @@ extension ArtifactDownloaderError: LocalizedError {
var errorDescription: String? {
switch self {
case .failedToDownloadArtifact:
return "The artifact could not be downloaded"
default:
return nil
return "The build could not be downloaded"
}
}

var failureReason: String? {
switch self {
case .failedToDownloadArtifact:
return "An unexpected error occurred while downloading the artifact."
default:
return nil
return "An unexpected error occurred while downloading the build."
}
}

var recoverySuggestion: String? {
switch self {
case .failedToDownloadArtifact:
return "Check your network connection and try again."
default:
return nil
}
}
}
6 changes: 3 additions & 3 deletions Tophat/Extensions/DeviceError+LocalizedError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ extension DeviceError: LocalizedError {
return "The device could not be started due to an unexpected error."
case .deviceNotAvailable:
return "The device is not available."
case .failedToInstallApp(_, deviceType: .physical):
case .failedToInstallApp(_, deviceType: .device):
return "The application could not be installed."
case .failedToInstallApp:
return "The application could not be installed due to an unexpected error."
Expand All @@ -52,11 +52,11 @@ extension DeviceError: LocalizedError {
switch self {
case .deviceNotAvailable:
return "Ensure that it is connected and try again."
case .failedToInstallApp(_, deviceType: .physical), .deviceUnlockTimedOut:
case .failedToInstallApp(_, deviceType: .device), .deviceUnlockTimedOut:
return "Make sure that the device is connected and unlocked and try again."
case .failedToLaunchApp(_, .requiresManualProfileTrust, _):
return "Go to Settings → General → VPN & Device Management to trust the developer."
case .failedToLaunchApp(_, _, deviceType: .physical):
case .failedToLaunchApp(_, _, deviceType: .device):
return "Make sure that the device is connected and unlocked and try again."
default:
return nil
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//
// InstallationTicketMachineError+LocalizedError.swift
// Tophat
//
// Created by Lukas Romsicki on 2024-10-04.
// Copyright © 2024 Shopify. All rights reserved.
//

import Foundation
import TophatFoundation

extension InstallationTicketMachineError: LocalizedError {
var errorDescription: String? {
switch self {
case .noCompatibleDevices(let providedBuildTypes):
"No \(description(for: providedBuildTypes)) Selected"
case .noSelectedDevices:
"No Device Selected"
}
}

var failureReason: String? {
switch self {
case .noCompatibleDevices:
"None of the specified builds are compatible with the selected devices."
default:
nil
}
}

var recoverySuggestion: String? {
switch self {
case .noCompatibleDevices(let providedBuildTypes):
let text = description(for: providedBuildTypes)
return "Select \(text.startsWithVowel ? "an" : "a") \(text) using the Tophat menu and try again."
case .noSelectedDevices:
return "Select a device from the Tophat menu and try again."
}
}

private func description(for providedBuildTypes: [Platform: Set<DeviceType>]) -> String {
providedBuildTypes.map { "\($0.key) \(description(for: $0.value))" }.formatted(.list(type: .or))
}

private func description(for platforms: Set<Platform>) -> String {
platforms.map { String(describing: $0) }.formatted(.list(type: .or))
}

private func description(for targets: Set<DeviceType>) -> String {
targets.map { String(describing: $0) }.formatted(.list(type: .or))
}

}

extension Character {
var isVowel: Bool { "aeiou".contains { String($0).compare(String(self).folding(options: .diacriticInsensitive, locale: nil), options: .caseInsensitive) == .orderedSame } }
}

extension StringProtocol {
var startsWithVowel: Bool { first?.isVowel == true }
}
45 changes: 0 additions & 45 deletions Tophat/Extensions/LaunchRequestBuilderError+LocalizedError.swift

This file was deleted.

8 changes: 6 additions & 2 deletions Tophat/Models/AndroidApplication.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ struct AndroidApplication: Application {

var icon: URL? {
guard let path = try? ApkAnalyzer.getIconPath(apkUrl: url),
let archive = Archive(url: url, accessMode: .read),
let archive = try? Archive(url: url, accessMode: .read, pathEncoding: nil),
let entry = archive[path]
else {
return nil
Expand All @@ -34,7 +34,7 @@ struct AndroidApplication: Application {

var targets: Set<DeviceType> {
// Android applications run anywhere.
[.virtual, .physical]
[.simulator, .device]
}

var platform: Platform {
Expand All @@ -52,6 +52,10 @@ struct AndroidApplication: Application {
}

func validateEligibility(for device: Device) throws {
guard platform == device.runtime.platform else {
throw ApplicationError.incompatibleDeviceType
}

// Android applications can be installed on any Android device.
}
}
Expand Down
12 changes: 8 additions & 4 deletions Tophat/Models/AppleApplication.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ struct AppleApplication: Application {
platformNames.forEach { platformName in
switch platformName {
case "iPhoneOS":
set.insert(.physical)
set.insert(.device)
case "iPhoneSimulator":
set.insert(.virtual)
set.insert(.simulator)
default:
break
}
Expand All @@ -83,12 +83,16 @@ struct AppleApplication: Application {
}

func validateEligibility(for device: Device) throws {
guard platform == device.runtime.platform else {
throw ApplicationError.incompatibleDeviceType
}

if !targets.contains(device.type) {
throw ApplicationError.incompatibleDeviceType
}

if device.type == .virtual {
// Remaining checks are not needed for virtual devices.
if device.type == .simulator {
// Remaining checks are not needed for simulator devices.
return
}

Expand Down
4 changes: 1 addition & 3 deletions Tophat/Models/LaunchContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,9 @@
struct LaunchContext {
let appName: String?
let pinnedApplicationId: PinnedApplication.ID?
let arguments: [String]?

init(appName: String? = nil, pinnedApplicationId: PinnedApplication.ID? = nil, arguments: [String]? = nil) {
init(appName: String? = nil, pinnedApplicationId: PinnedApplication.ID? = nil) {
self.appName = appName
self.pinnedApplicationId = pinnedApplicationId
self.arguments = arguments
}
}
14 changes: 0 additions & 14 deletions Tophat/Models/LaunchRequest.swift

This file was deleted.

14 changes: 8 additions & 6 deletions Tophat/Models/PinnedApplication.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ import TophatFoundation
struct PinnedApplication: Identifiable, Codable {
let id: String
let name: String
let platform: Platform
let artifacts: [Artifact]
let recipes: [InstallRecipe]
var icon: ApplicationIcon? = nil

/// Creates a new pinned application.
Expand All @@ -25,12 +24,15 @@ struct PinnedApplication: Identifiable, Codable {
/// - id: The identifier of the pinned application, if used for updating purposes.
/// - name: The name of the pinned application.
/// - platform: The platform of the pinned application.
/// - artifacts: The set of artifacts at which this pinned application can be found.
init(id: String? = nil, name: String, platform: Platform, artifacts: [Artifact] = []) {
/// - recipes: The set of recipes at which this pinned application can be found.
init(id: String? = nil, name: String, recipes: [InstallRecipe] = []) {
self.id = id ?? UUID().uuidString
self.name = name
self.platform = platform
self.artifacts = artifacts
self.recipes = recipes
}

var platform: Platform {
recipes.first?.platformHint ?? .unknown
}
}

Expand Down
Loading

0 comments on commit 78bc9f3

Please sign in to comment.