Skip to content

Commit

Permalink
feat: dynamic protection, easy cert swap, auto save settings... (#218)
Browse files Browse the repository at this point in the history
* feat: dynamic protection

* chore: separate bundle id and display name for debug config

* chore: localize strings for dynamic protection and fix order

* chore: ignore zsign warnings in xcode (ocd moment)

* chore: this should be info

* chore: easier way to ignore cpp warnings

* feat: resolve #187

* chore: fix description to match behaviour + grammar

* feat: auto save signing settings

* feat: resolve #185

* chore: resolve remaining xcode warnings

* chore: remove remaining unusernotification stuff

* fix: toggle action order

* feat: haptic feedback for cert swipe

* feat: helper function for modalPresentationStyle

* chore: add strings

---------

Co-authored-by: khcrysalis <[email protected]>
  • Loading branch information
castdrian and khcrysalis authored Dec 9, 2024
1 parent 75e0c7e commit e36317c
Show file tree
Hide file tree
Showing 26 changed files with 509 additions and 252 deletions.
2 changes: 1 addition & 1 deletion Shared/Design/FloatingActionButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func addAddButtonToView(title: String? = "+",
addButton.setTitle(title, for: .normal)
addButton.setTitleColor(titleColor, for: .normal)
addButton.titleLabel?.font = font
} else if let image = image {
} else if image != nil {
addButton.setImage( UIImage(systemName: "folder.fill"), for: .normal)
addButton.tintColor = Preferences.appTintColor.uiColor

Expand Down
3 changes: 3 additions & 0 deletions Shared/Localizations/de.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_PROTECTIONS" = "Enable Protection";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_PROTECTIONS_DESCRIPTION" = "Enabling protections will pre-append every bundle identifier with a random string, this is to protect the Apple ID related to your certificate from being flagged by Apple. However, if you don't care about this you can ignore.";

"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_DYNAMIC_PROTECTION" = "Dynamic Protection";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_DYNAMIC_PROTECTION_DESCRIPTION" = "Dynamic protection will only apply PPQ protection if the bundle identifier exists on the App Store. This requires an internet connection during signing.";

"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_IDENTIFIERS" = "Bundle Identifiers";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_IDENTIFIERS_NEW" = "New Identifier";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_IDENTIFIERS_ID" = "Identifier";
Expand Down
8 changes: 7 additions & 1 deletion Shared/Localizations/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,10 @@
// MARK: - SigningsOptionsViewController

"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_PROTECTIONS" = "Enable Protection";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_PROTECTIONS_DESCRIPTION" = "Enabling protections will pre-append every bundle identifier with a random string, this is to protect the Apple ID related to your certificate from being flagged by Apple. However, if you don't care about this you can ignore.";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_PROTECTIONS_DESCRIPTION" = "Enabling protection will append a random string to each bundle identifier, this is to protect the Apple ID related to your certificate from being flagged by Apple. However, if you don't care about this you can ignore it.";

"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_DYNAMIC_PROTECTION" = "Dynamic Protection";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_DYNAMIC_PROTECTION_DESCRIPTION" = "Dynamic protection will only apply PPQ protection if the bundle identifier exists on the App Store. This requires an internet connection during signing.";

"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_IDENTIFIERS" = "Bundle Identifiers";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_IDENTIFIERS_NEW" = "New Identifier";
Expand All @@ -206,6 +209,9 @@
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_DISPLAYNAMES_ID" = "Original Display Name";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_DISPLAYNAMES_ID_REPLACEMENT" = "New Display Name";

"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_IMMEDIATELY_INSTALL_FROM_SOURCE" = "Immediately Install from Source";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_IMMEDIATELY_INSTALL_FROM_SOURCE_DESCRIPTION" = "When enabled, apps downloaded from sources will prompt to sign and install after downloading.";

"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_INSTALLAFTERSIGNED" = "Install after Signing";

"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_TITLE" = "Signing Options";
Expand Down
3 changes: 3 additions & 0 deletions Shared/Localizations/es.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_PROTECTIONS" = "Enable Protection";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_PROTECTIONS_DESCRIPTION" = "Enabling protections will pre-append every bundle identifier with a random string, this is to protect the Apple ID related to your certificate from being flagged by Apple. However, if you don't care about this you can ignore.";

"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_DYNAMIC_PROTECTION" = "Dynamic Protection";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_DYNAMIC_PROTECTION_DESCRIPTION" = "Dynamic protection will only apply PPQ protection if the bundle identifier exists on the App Store. This requires an internet connection during signing.";

"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_IDENTIFIERS" = "Bundle Identifiers";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_IDENTIFIERS_NEW" = "New Identifier";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_IDENTIFIERS_ID" = "Identifier";
Expand Down
3 changes: 3 additions & 0 deletions Shared/Localizations/fr.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_PROTECTIONS" = "Enable Protection";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_PROTECTIONS_DESCRIPTION" = "Enabling protections will pre-append every bundle identifier with a random string, this is to protect the Apple ID related to your certificate from being flagged by Apple. However, if you don't care about this you can ignore.";

"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_DYNAMIC_PROTECTION" = "Dynamic Protection";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_DYNAMIC_PROTECTION_DESCRIPTION" = "Dynamic protection will only apply PPQ protection if the bundle identifier exists on the App Store. This requires an internet connection during signing.";

"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_IDENTIFIERS" = "Bundle Identifiers";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_IDENTIFIERS_NEW" = "New Identifier";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_IDENTIFIERS_ID" = "Identifier";
Expand Down
3 changes: 3 additions & 0 deletions Shared/Localizations/zh.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_PROTECTIONS" = "启用保护";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_PROTECTIONS_DESCRIPTION" = "启用保护后,每个捆绑标识符都会预先添加一个随机字符串,这是为了保护与证书相关的 Apple ID 不被 Apple 标记。不过,如果您不关心这个问题,可以忽略。";

"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_DYNAMIC_PROTECTION" = "Dynamic Protection";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_DYNAMIC_PROTECTION_DESCRIPTION" = "Dynamic protection will only apply PPQ protection if the bundle identifier exists on the App Store. This requires an internet connection during signing.";

"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_IDENTIFIERS" = "捆绑标识符";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_IDENTIFIERS_NEW" = "新标识符";
"APP_SIGNING_VIEW_CONTROLLER_CELL_SIGNING_OPTIONS_IDENTIFIERS_ID" = "标识符";
Expand Down
284 changes: 142 additions & 142 deletions Shared/Logging/Logger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,171 +6,171 @@
// Copyright (c) 2024 Samara M (khcrysalis)
//

import AlertKit
import Foundation
import OSLog
import AlertKit

public enum LogType {
/// Default
case notice
/// Call this function to capture information that may be helpful, but isn’t essential, for troubleshooting.
case info
/// Debug-level messages to use in a development environment while actively debugging.
case debug
/// Equivalent of the debug method.
case trace
/// Warning-level messages for reporting unexpected non-fatal failures.
case warning
/// Error-level messages for reporting critical errors and failures.
case error
/// Fault-level messages for capturing system-level or multi-process errors only.
case fault
/// Functional equivalent of the fault method.
case critical
/// Default
case notice
/// Call this function to capture information that may be helpful, but isn’t essential, for troubleshooting.
case info
/// Debug-level messages to use in a development environment while actively debugging.
case debug
/// Equivalent of the debug method.
case trace
/// Warning-level messages for reporting unexpected non-fatal failures.
case warning
/// Error-level messages for reporting critical errors and failures.
case error
/// Fault-level messages for capturing system-level or multi-process errors only.
case fault
/// Functional equivalent of the fault method.
case critical

case success
case success
}

final class Debug {
static let shared = Debug()
private let subsystem = Bundle.main.bundleIdentifier!
static let shared = Debug()
private let subsystem = Bundle.main.bundleIdentifier!

private var logFilePath: URL {
return getDocumentsDirectory().appendingPathComponent("logs.txt")
}
private var logFilePath: URL {
return getDocumentsDirectory().appendingPathComponent("logs.txt")
}

private func appendLogToFile(_ message: String) {
do {
if FileManager.default.fileExists(atPath: logFilePath.path) {
let fileHandle = try FileHandle(forUpdating: logFilePath)
fileHandle.seekToEndOfFile()
if let data = message.data(using: .utf8) {
fileHandle.write(data)
}
fileHandle.closeFile()
}
} catch {
Debug.shared.log(message: "Error writing to logs.txt: \(error)")
}
}
private func appendLogToFile(_ message: String) {
do {
if FileManager.default.fileExists(atPath: logFilePath.path) {
let fileHandle = try FileHandle(forUpdating: logFilePath)
fileHandle.seekToEndOfFile()
if let data = message.data(using: .utf8) {
fileHandle.write(data)
}
fileHandle.closeFile()
}
} catch {
Debug.shared.log(message: "Error writing to logs.txt: \(error)")
}
}

func log(message: String, type: LogType? = nil, function: String = #function, file: String = #file, line: Int = #line) {
lazy var logger = Logger(subsystem: subsystem, category: file + "->" + function)
func log(message: String, type: LogType? = nil, function: String = #function, file: String = #file, line: Int = #line) {
lazy var logger = Logger(subsystem: subsystem, category: file + "->" + function)

// Prepare the emoji based on the log type
var emoji: String
switch type {
case .success:
emoji = ""
logger.info("\(message)")
showSuccessAlert(with: String.localized("ALERT_SUCCESS"), subtitle: message)
case .info:
emoji = "ℹ️"
logger.info("\(message)")
case .debug:
emoji = "🐛"
logger.debug("\(message)")
case .trace:
emoji = "🔍"
logger.trace("\(message)")
showErrorUIAlert(with: String.localized("ALERT_TRACE"), subtitle: message)
case .warning:
emoji = "⚠️"
logger.warning("\(message)")
showErrorAlert(with: String.localized("ALERT_ERROR"), subtitle: message)
case .error:
emoji = ""
logger.error("\(message)")
showErrorAlert(with: String.localized("ALERT_ERROR"), subtitle: message)
case .critical:
emoji = "🔥"
logger.critical("\(message)")
showErrorUIAlert(with: String.localized("ALERT_CRITICAL"), subtitle: message)
default:
emoji = "📝"
logger.log("\(message)")
}
// Prepare the emoji based on the log type
var emoji: String
switch type {
case .success:
emoji = ""
logger.info("\(message)")
showSuccessAlert(with: String.localized("ALERT_SUCCESS"), subtitle: message)
case .info:
emoji = "ℹ️"
logger.info("\(message)")
case .debug:
emoji = "🐛"
logger.debug("\(message)")
case .trace:
emoji = "🔍"
logger.trace("\(message)")
showErrorUIAlert(with: String.localized("ALERT_TRACE"), subtitle: message)
case .warning:
emoji = "⚠️"
logger.warning("\(message)")
showErrorAlert(with: String.localized("ALERT_ERROR"), subtitle: message)
case .error:
emoji = ""
logger.error("\(message)")
showErrorAlert(with: String.localized("ALERT_ERROR"), subtitle: message)
case .critical:
emoji = "🔥"
logger.critical("\(message)")
showErrorUIAlert(with: String.localized("ALERT_CRITICAL"), subtitle: message)
default:
emoji = "📝"
logger.log("\(message)")
}

let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm:ss"
let timeString = dateFormatter.string(from: Date())
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "HH:mm:ss"
let timeString = dateFormatter.string(from: Date())

let logMessage = "[\(timeString)] \(emoji) \(message)\n"
appendLogToFile(logMessage)
}
let logMessage = "[\(timeString)] \(emoji) \(message)\n"
appendLogToFile(logMessage)
}

func showSuccessAlert(with title: String, subtitle: String) {
DispatchQueue.main.async {
let alertView = AlertAppleMusic17View(title: title, subtitle: subtitle, icon: .done)
let keyWindow = UIApplication.shared.connectedScenes.compactMap { ($0 as? UIWindowScene)?.keyWindow }.last
if let viewController = keyWindow?.rootViewController {
alertView.present(on: viewController.view)
}
#if os(iOS)
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.success)
#endif
}
}

func showSuccessAlert(with title: String, subtitle: String) {
DispatchQueue.main.async {
let alertView = AlertAppleMusic17View(title: title, subtitle: subtitle, icon: .done)
if let viewController = UIApplication.shared.windows.first?.rootViewController {
alertView.present(on: viewController.view)
}
#if os(iOS)
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.success)
#endif
}
}

func showErrorAlert(with title: String, subtitle: String) {
DispatchQueue.main.async {
let alertView = AlertAppleMusic17View(title: title, subtitle: subtitle, icon: .error)
if let viewController = UIApplication.shared.windows.first?.rootViewController {
alertView.present(on: viewController.view)
}
#if os(iOS)
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.error)
#endif
}
}
func showErrorAlert(with title: String, subtitle: String) {
DispatchQueue.main.async {
let alertView = AlertAppleMusic17View(title: title, subtitle: subtitle, icon: .error)
let keyWindow = UIApplication.shared.connectedScenes.compactMap { ($0 as? UIWindowScene)?.keyWindow }.last
if let viewController = keyWindow?.rootViewController {
alertView.present(on: viewController.view)
}
#if os(iOS)
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.error)
#endif
}
}

func showErrorUIAlert(with title: String, subtitle: String) {
DispatchQueue.main.async {
if let rootViewController = UIApplication.shared.keyWindow?.rootViewController {
let alert = UIAlertController.error(title: title, message: subtitle, actions: [])
rootViewController.present(alert, animated: true)
}
func showErrorUIAlert(with title: String, subtitle: String) {
DispatchQueue.main.async {
let keyWindow = UIApplication.shared.connectedScenes.compactMap { ($0 as? UIWindowScene)?.keyWindow }.last
if let rootViewController = keyWindow?.rootViewController {
let alert = UIAlertController.error(title: title, message: subtitle, actions: [])
rootViewController.present(alert, animated: true)
}

#if os(iOS)
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.error)
#endif
}
}

#if os(iOS)
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.error)
#endif
}
}
}

extension UIAlertController {
static func error(title: String, message: String, actions: [UIAlertAction]) -> UIAlertController {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
static func error(title: String, message: String, actions: [UIAlertAction]) -> UIAlertController {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)

alertController.addAction(UIAlertAction(title: String.localized("OK"), style: .cancel) { _ in
alertController.dismiss(animated: true)
})
alertController.addAction(UIAlertAction(title: String.localized("OK"), style: .cancel) { _ in
alertController.dismiss(animated: true)
})

for action in actions {
alertController.addAction(action)
}
#if os(iOS)
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.error)
#endif
return alertController
}
for action in actions {
alertController.addAction(action)
}
#if os(iOS)
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.error)
#endif
return alertController
}

static func coolAlert(title: String, message: String, actions: [UIAlertAction]) -> UIAlertController {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
static func coolAlert(title: String, message: String, actions: [UIAlertAction]) -> UIAlertController {
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)

for action in actions {
alertController.addAction(action)
}
#if os(iOS)
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.error)
#endif
return alertController
}

for action in actions {
alertController.addAction(action)
}
#if os(iOS)
let generator = UINotificationFeedbackGenerator()
generator.notificationOccurred(.error)
#endif
return alertController
}
}
Loading

0 comments on commit e36317c

Please sign in to comment.