Skip to content

Commit

Permalink
Improve null safety
Browse files Browse the repository at this point in the history
Previously if a cask did not have a home page, Applite would crash on load. This commit fixes the issue.
  • Loading branch information
milanvarady committed May 16, 2024
1 parent ab944b8 commit f9d0d7f
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 35 deletions.
4 changes: 2 additions & 2 deletions Applite/Extensions/BundleExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ extension Bundle {
}

var version: String {
return infoDictionary?["CFBundleShortVersionString"] as! String
return infoDictionary?["CFBundleShortVersionString"] as? String ?? "N/A"
}

var buildNumber: String {
return infoDictionary?["CFBundleVersion"] as! String
return infoDictionary?["CFBundleVersion"] as? String ?? "N/A"
}
}
18 changes: 11 additions & 7 deletions Applite/Model/Cask Data/Cask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ final class Cask: Identifiable, Decodable, Hashable, ObservableObject {
let name: String
/// Short description
let description: String
let homepageURL: URL
let homepageURL: URL?
@Published var isInstalled: Bool = false
@Published var isOutdated: Bool = false
/// Number of downloads in the last 365 days
Expand Down Expand Up @@ -51,7 +51,7 @@ final class Cask: Identifiable, Decodable, Hashable, ObservableObject {
self.id = rawData.token
self.name = rawData.nameArray[0]
self.description = rawData.desc ?? "N/A"
self.homepageURL = URL(string: rawData.homepage)!
self.homepageURL = URL(string: rawData.homepage)
self.caveats = rawData.caveats
self.pkgInstaller = rawData.url.hasSuffix("pkg")
}
Expand All @@ -60,7 +60,7 @@ final class Cask: Identifiable, Decodable, Hashable, ObservableObject {
self.id = "test"
self.name = "Test app"
self.description = "An application to test this application"
self.homepageURL = URL(string: "https://aerolite.dev/")!
self.homepageURL = URL(string: "https://aerolite.dev/")
self.caveats = nil
self.pkgInstaller = false
}
Expand All @@ -79,8 +79,10 @@ final class Cask: Identifiable, Decodable, Hashable, ObservableObject {
Self.logger.info("Cask \"\(self.id)\" installation started")

// Check if pinentry is installed
guard ((try? await checkPinentry()) != nil) else { return ShellResult(output: "Pinentry check error", didFail: true) }

guard ((try? await checkPinentry()) != nil) else {
return ShellResult(output: "Pinentry check error", didFail: true)
}

var cancellables = Set<AnyCancellable>()
let shellOutputStream = ShellOutputStream()
let appdirOn = UserDefaults.standard.bool(forKey: Preferences.appdirOn.rawValue)
Expand Down Expand Up @@ -242,8 +244,10 @@ final class Cask: Identifiable, Decodable, Hashable, ObservableObject {
notificationSuccess: String, notificationFailure: String, onSuccess: (() -> Void)? = nil) async -> Bool {

// Check if pinentry is installed
guard ((try? await checkPinentry()) != nil) else { return true }

guard ((try? await checkPinentry()) != nil) else {
return true
}

await MainActor.run {
let localizedTaskDescription = String.LocalizationValue(stringLiteral: taskDescription)
self.progressState = .busy(withTask: String(localized: localizedTaskDescription))
Expand Down
6 changes: 3 additions & 3 deletions Applite/Model/Categories/Category.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import Foundation

/// Holds the app categories
let categories: [Category] = loadLocalJson(fileName: "categories")!
let categories: [Category] = loadLocalJson(fileName: "categories")

/// App category object
struct Category: Decodable, Identifiable {
Expand All @@ -21,14 +21,14 @@ struct Category: Decodable, Identifiable {
}

/// Loads a json from resources
fileprivate func loadLocalJson(fileName: String) -> [Category]? {
fileprivate func loadLocalJson(fileName: String) -> [Category] {
let decoder = JSONDecoder()
guard
let url = Bundle.main.url(forResource: fileName, withExtension: "json"),
let data = try? Data(contentsOf: url),
let categories = try? decoder.decode([Category].self, from: data)
else {
return nil
return []
}

return categories
Expand Down
5 changes: 1 addition & 4 deletions Applite/Utilities/Brew Installation/DependencyManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@ public struct DependencyManager {
subsystem: Bundle.main.bundleIdentifier!,
category: String(describing: DependencyManager.self)
)

/// Extracts percentage from shell output when installing brew
static let percentageRegex = try! NSRegularExpression(pattern: #"(Receiving|Resolving).+:\s+(\d{1,3})%"#)


/// Message shown when brew path is broken
static public var brokenPathOrIstallMessage = "Error. Broken brew path, or damaged installation. Check brew path in settings, or try reinstalling Homebrew (Manage Homebrew->Reinstall)"

Expand Down
32 changes: 20 additions & 12 deletions Applite/Views/App Views/AppView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,16 @@ struct AppView: View {

private var iconAndDescriptionView: some View {
return HStack {
AppIconView(
iconURL: URL(string: "https://github.com/App-Fair/appcasks/releases/download/cask-\(cask.id)/AppIcon.png")!,
faviconURL: URL(string: "https://icon.horse/icon/\(cask.homepageURL.host ?? "")")!,
cacheKey: cask.id
)
.padding(.leading, 5)

if let iconURL = URL(string: "https://github.com/App-Fair/appcasks/releases/download/cask-\(cask.id)/AppIcon.png"),
let faviconURL = URL(string: "https://icon.horse/icon/\(cask.homepageURL?.host ?? "")") {
AppIconView(
iconURL: iconURL,
faviconURL: faviconURL,
cacheKey: cask.id
)
.padding(.leading, 5)
}

// Name and description
VStack(alignment: .leading) {
Text(cask.name)
Expand Down Expand Up @@ -260,11 +263,16 @@ struct AppView: View {
.popover(isPresented: $showingPopover) {
VStack(alignment: .leading, spacing: 6) {
// Open homepage
Link(destination: cask.homepageURL, label: {
Label("Homepage", systemImage: "house")
})
.foregroundColor(.primary)

if let homepageLink = cask.homepageURL {
Link(destination: homepageLink, label: {
Label("Homepage", systemImage: "house")
})
.foregroundColor(.primary)
} else {
Text("No homepage found")
.fontWeight(.thin)
}

// Force install button
Button {
showingForceInstallConfirmation = true
Expand Down
8 changes: 3 additions & 5 deletions Applite/Views/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,10 @@ struct ContentView: View {
BrewManagementView(modifyingBrew: $modifyingBrew)

default:
let category = categories.first(where: { $0.id == selection })

if category == nil {
Text("No Selection")
if let category = categories.first(where: { $0.id == selection }) {
CategoryView(category: category)
} else {
CategoryView(category: category!)
Text("No Selection")
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions Applite/Views/SetupView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ struct SetupView: View {
HStack {
Spacer()

if pageBefore != nil {
if let pageBeforeUnwrapped = pageBefore {
Button("Back") {
page = pageBefore!
page = pageBeforeUnwrapped
}
.bigButton(backgroundColor: Color(red: 0.7, green: 0.7, blue: 0.7))
}
Expand Down
28 changes: 28 additions & 0 deletions Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -3114,6 +3114,34 @@
}
}
},
"No homepage found" : {
"localizations" : {
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Aucune page d'accueil trouvée"
}
},
"hu" : {
"stringUnit" : {
"state" : "translated",
"value" : "Nem található honlap"
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "未找到主页"
}
},
"zh-HK" : {
"stringUnit" : {
"state" : "translated",
"value" : "沒有找到首頁"
}
}
}
},
"No Selection" : {
"localizations" : {
"fr" : {
Expand Down

0 comments on commit f9d0d7f

Please sign in to comment.