diff --git a/AltStore/App Detail/AppViewController.swift b/AltStore/App Detail/AppViewController.swift
index ffba2ba1..7977ce79 100644
--- a/AltStore/App Detail/AppViewController.swift
+++ b/AltStore/App Detail/AppViewController.swift
@@ -510,7 +510,7 @@ extension AppViewController
catch
{
DispatchQueue.main.async {
- let toastView = ToastView(error: error)
+ let toastView = ToastView(error: error, opensLog: true)
toastView.show(in: self)
}
}
diff --git a/AltStore/Browse/BrowseViewController.swift b/AltStore/Browse/BrowseViewController.swift
index a23fbbe9..21ab4073 100644
--- a/AltStore/Browse/BrowseViewController.swift
+++ b/AltStore/Browse/BrowseViewController.swift
@@ -278,7 +278,7 @@ private extension BrowseViewController
{
case .failure(OperationError.cancelled): break // Ignore
case .failure(let error):
- let toastView = ToastView(error: error)
+ let toastView = ToastView(error: error, opensLog: true)
toastView.show(in: self)
case .success: print("Installed app:", app.bundleIdentifier)
diff --git a/AltStore/Components/ToastView.swift b/AltStore/Components/ToastView.swift
index 5aba48e7..4cbe6db2 100644
--- a/AltStore/Components/ToastView.swift
+++ b/AltStore/Components/ToastView.swift
@@ -18,8 +18,17 @@ extension TimeInterval
final class ToastView: RSTToastView
{
+ static let openErrorLogNotification = Notification.Name("ALTOpenErrorLogNotification")
+
var preferredDuration: TimeInterval
-
+
+ var opensErrorLog: Bool = false
+
+ convenience init(text: String, detailText: String?, opensLog: Bool = false) {
+ self.init(text: text, detailText: detailText)
+ self.opensErrorLog = opensLog
+ }
+
override init(text: String, detailText detailedText: String?)
{
if detailedText == nil
@@ -43,9 +52,16 @@ final class ToastView: RSTToastView
// RSTToastView does not expose stack view containing labels,
// so we access it indirectly as the labels' superview.
stackView.spacing = (detailedText != nil) ? 4.0 : 0.0
+ stackView.alignment = .leading
}
+ self.addTarget(self, action: #selector(ToastView.showErrorLog), for: .touchUpInside)
}
-
+
+ convenience init(error: Error, opensLog: Bool = false) {
+ self.init(error: error)
+ self.opensErrorLog = opensLog
+ }
+
convenience init(error: Error)
{
var error = error as NSError
@@ -95,6 +111,18 @@ final class ToastView: RSTToastView
override func show(in view: UIView, duration: TimeInterval)
{
+ if opensErrorLog, #available(iOS 13.0, *), case let configuration = UIImage.SymbolConfiguration(font: self.textLabel.font),
+ let icon = UIImage(systemName: "chevron.right.circle", withConfiguration: configuration) {
+ let tintedIcon = icon.withTintColor(.white, renderingMode: .alwaysOriginal)
+ let moreIconImageView = UIImageView(image: tintedIcon)
+ moreIconImageView.translatesAutoresizingMaskIntoConstraints = false
+ self.addSubview(moreIconImageView)
+ NSLayoutConstraint.activate([
+ moreIconImageView.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -self.layoutMargins.right),
+ moreIconImageView.centerYAnchor.constraint(equalTo: self.textLabel.centerYAnchor),
+ moreIconImageView.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: self.textLabel.trailingAnchor, multiplier: 1.0)
+ ])
+ }
super.show(in: view, duration: duration)
let announcement = (self.textLabel.text ?? "") + ". " + (self.detailTextLabel.text ?? "")
@@ -110,4 +138,10 @@ final class ToastView: RSTToastView
{
self.show(in: view, duration: self.preferredDuration)
}
+
+ @objc
+ func showErrorLog() {
+ guard self.opensErrorLog else { return }
+ NotificationCenter.default.post(name: ToastView.openErrorLogNotification, object: self)
+ }
}
diff --git a/AltStore/My Apps/MyAppsViewController.swift b/AltStore/My Apps/MyAppsViewController.swift
index 298f502c..3a3cb08c 100644
--- a/AltStore/My Apps/MyAppsViewController.swift
+++ b/AltStore/My Apps/MyAppsViewController.swift
@@ -535,11 +535,9 @@ private extension MyAppsViewController
guard !failures.isEmpty else { return }
- let toastView: ToastView
-
if let failure = failures.first, results.count == 1
{
- toastView = ToastView(error: failure.value)
+ ToastView(error: failure.value).show(in: self)
}
else
{
@@ -557,11 +555,10 @@ private extension MyAppsViewController
let error = failures.first?.value as NSError?
let detailText = error?.localizedFailure ?? error?.localizedFailureReason ?? error?.localizedDescription
- toastView = ToastView(text: localizedText, detailText: detailText)
+ let toastView = ToastView(text: localizedText, detailText: detailText, opensLog: true)
toastView.preferredDuration = 4.0
+ toastView.show(in: self)
}
-
- toastView.show(in: self)
}
self.refreshGroup = nil
@@ -697,8 +694,7 @@ private extension MyAppsViewController
self.collectionView.reloadItems(at: [indexPath])
case .failure(let error):
- let toastView = ToastView(error: error)
- toastView.show(in: self)
+ ToastView(error: error, opensLog: true).show(in: self)
self.collectionView.reloadItems(at: [indexPath])
@@ -899,9 +895,8 @@ private extension MyAppsViewController
completion(.failure((OperationError.cancelled)))
case .failure(let error):
- let toastView = ToastView(error: error)
- toastView.show(in: self)
-
+ ToastView(error: error, opensLog: true).show(in: self)
+
completion(.failure(error))
}
}
@@ -1015,8 +1010,7 @@ private extension MyAppsViewController
UIApplication.shared.open(installedApp.openAppURL) { success in
guard !success else { return }
- let toastView = ToastView(error: OperationError.openAppFailed(name: installedApp.name))
- toastView.show(in: self)
+ ToastView(error: OperationError.openAppFailed(name: installedApp.name), opensLog: true).show(in: self)
}
}
@@ -1067,8 +1061,7 @@ private extension MyAppsViewController
DispatchQueue.main.async {
installedApp.isActive = false
- let toastView = ToastView(error: error)
- toastView.show(in: self)
+ ToastView(error: error, opensLog: true).show(in: self)
}
}
}
@@ -1141,8 +1134,7 @@ private extension MyAppsViewController
DispatchQueue.main.async {
installedApp.isActive = true
- let toastView = ToastView(error: error)
- toastView.show(in: self)
+ ToastView(error: error, opensLog: true).show(in: self)
}
}
@@ -1173,8 +1165,7 @@ private extension MyAppsViewController
case .success: break
case .failure(let error):
DispatchQueue.main.async {
- let toastView = ToastView(error: error)
- toastView.show(in: self)
+ ToastView(error: error, opensLog: true).show(in: self)
}
}
}
@@ -1208,9 +1199,8 @@ private extension MyAppsViewController
print("Failed to back up app:", error)
DispatchQueue.main.async {
- let toastView = ToastView(error: error)
- toastView.show(in: self)
-
+ ToastView(error: error, opensLog: true).show(in: self)
+
self.collectionView.reloadSections([Section.activeApps.rawValue, Section.inactiveApps.rawValue])
}
}
@@ -1245,8 +1235,7 @@ private extension MyAppsViewController
print("Failed to restore app:", error)
DispatchQueue.main.async {
- let toastView = ToastView(error: error)
- toastView.show(in: self)
+ ToastView(error: error, opensLog: true).show(in: self)
}
}
}
@@ -1321,8 +1310,7 @@ private extension MyAppsViewController
print("Failed to change app icon.", error)
DispatchQueue.main.async {
- let toastView = ToastView(error: error)
- toastView.show(in: self)
+ ToastView(error: error, opensLog: true).show(in: self)
}
}
}
@@ -1334,9 +1322,8 @@ private extension MyAppsViewController
guard minimuxerStatus else { return }
if #available(iOS 17, *) {
- let toastView = ToastView(error: (OperationError.tooNewError as NSError).withLocalizedTitle("No iOS 17 On Device JIT!"))
+ ToastView(error: (OperationError.tooNewError as NSError).withLocalizedTitle("No iOS 17 On Device JIT!"), opensLog: true).show(in: self)
AppManager.shared.log(OperationError.tooNewError, operation: .enableJIT, app: installedApp)
- toastView.show(in: self)
return
}
@@ -1346,8 +1333,8 @@ private extension MyAppsViewController
{
case .success: break
case .failure(let error):
- let toastView = ToastView(error: error)
- toastView.show(in: self.navigationController?.view ?? self.view, duration: 5)
+ ToastView(error: error, opensLog: true).show(in: self)
+ AppManager.shared.log(error, operation: .enableJIT, app: installedApp)
}
}
}
diff --git a/AltStore/News/NewsViewController.swift b/AltStore/News/NewsViewController.swift
index 945a0822..f57e9117 100644
--- a/AltStore/News/NewsViewController.swift
+++ b/AltStore/News/NewsViewController.swift
@@ -313,9 +313,8 @@ private extension NewsViewController
{
case .failure(OperationError.cancelled): break // Ignore
case .failure(let error):
- let toastView = ToastView(error: error)
- toastView.show(in: self)
-
+ ToastView(error: error, opensLog: true).show(in: self)
+
case .success: print("Installed app:", storeApp.bundleIdentifier)
}
diff --git a/AltStore/Settings/Settings.storyboard b/AltStore/Settings/Settings.storyboard
index 999bcb52..a0b20cbb 100644
--- a/AltStore/Settings/Settings.storyboard
+++ b/AltStore/Settings/Settings.storyboard
@@ -21,7 +21,7 @@