diff --git a/Package.swift b/Package.swift index 563a8305..9958625c 100644 --- a/Package.swift +++ b/Package.swift @@ -22,57 +22,6 @@ let STATIC_LIBRARY = envBool("STATIC_LIBRARY") let unsafe_flags: [String] = INHIBIT_UPSTREAM_WARNINGS ? ["-w"] : [] let unsafe_flags_cxx: [String] = INHIBIT_UPSTREAM_WARNINGS ? ["-w", "-Wno-module-import-in-extern-c"] : ["-Wno-module-import-in-extern-c"] -// let dependencies_cargo: [Package.Dependency] = { -// USE_CARGO ? [ -// // CargoPlugin -// .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.3"), -// .package(url: "https://github.com/apple/swift-package-manager.git", branch: "release/5.7"), -// .package(url: "https://github.com/apple/swift-tools-support-core.git", branch: "release/5.7"), -// ] : [] -// }() - -// let cargo_targets: [Target] = [ -// .executableTarget( -// name: "Cargo", -// dependencies: [ -// .product(name: "ArgumentParser", package: "swift-argument-parser"), -// .product(name: "SwiftPM-auto", package: "swift-package-manager"), -// .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core") -// ] -// ), -// -// .testTarget( -// name: "CargoTests", -// dependencies: ["Cargo"], -// exclude: [ -// "swiftlint", -// "xcframework" -// ] -// ), -// -// .plugin( -// name: "CargoPlugin", -// capability: .buildTool(), -// dependencies: [ -// "Cargo" -// ] -// ), -// -// .plugin( -// name: "CargoPlugin-Generate", -// capability: .command( -// intent: .custom( -// verb: "generate-code-from-rust", -// description: "Creates .c code from your `rust` code" -// ), -// permissions: [ -// .writeToPackageDirectory(reason: "This command generates source code") -// ] -// ), -// dependencies: ["Cargo"] -// ) -// ] - let dependencies: [Package.Dependency] = [ .package(url: "https://github.com/JoeMatt/Roxas", from: "1.2.2"), .package(url: "https://github.com/johnxnguyen/Down", branch: "master"), @@ -89,9 +38,9 @@ let package = Package( name: "SideStore", defaultLocalization: "en", platforms: [ - .iOS(.v13), - .tvOS(.v13), - .macCatalyst(.v13), + .iOS(.v14), + .tvOS(.v14), + .macCatalyst(.v14), .macOS(.v11), ], @@ -105,6 +54,14 @@ let package = Package( name: "SideWidget", targets: ["SideWidget"] ), + + .library( + name: "SideStoreAppKit", + targets: ["SideStoreAppKit"]), + + .plugin(name: "IntentBuilderPlugin", targets: ["IntentBuilderPlugin"]), + .plugin(name: "LoggerPlugin", targets: ["LoggerPlugin"]) + ], dependencies: dependencies, @@ -124,15 +81,24 @@ let package = Package( "AltSign", "SideKit", .product(name: "Roxas", package: "Roxas"), - .product(name: "RoxasUI", package: "Roxas"), + .product(name: "RoxasUI", package: "Roxas"), .product(name: "AppCenterAnalytics", package: "appcenter-sdk-apple"), .product(name: "AppCenterCrashes", package: "appcenter-sdk-apple"), ], + resources: [ + .process("Resources/XIB"), + .process("Resources/Storyboards"), + .process("Resources/Base.lproj"), + .process("Resources/Assets"), + .process("Resources/Sounds"), + .process("Resources/Settings.bundle"), + .copy("Resources/JSON/apps-alpha.json"), + .copy("Resources/JSON/apps.json") + ], linkerSettings: [ - .linkedFramework("UIKit"), + .linkedFramework("UIKit", .when(platforms: [.iOS, .macCatalyst, .tvOS])), .linkedFramework("Avfoundation"), .linkedFramework("Combine"), - .linkedFramework("AppleArchive"), .linkedFramework("Network"), .linkedFramework("CoreData"), .linkedFramework("UniformTypeIdentifiers"), @@ -148,13 +114,66 @@ let package = Package( .linkedFramework("WidgetKit", .when(platforms: [.iOS, .macCatalyst])), .linkedFramework("UserNotifications", .when(platforms: [.iOS, .macCatalyst])), .linkedFramework("MobileCoreServices", .when(platforms: [.iOS, .macCatalyst])), - ] + .linkedLibrary("AppleArchive") + ], + plugins: [ + "IntentBuilderPlugin", + "LoggerPlugin" + ] ), + .target( + name: "SideStoreAppKit", + dependencies: [ + "SidePatcher", + "EmotionalDamage", + "MiniMuxerSwift", + "SideStoreCore", + "Shared", + "Nuke", + "Down", + "AltSign", + "SideKit", + .product(name: "Roxas", package: "Roxas"), + .product(name: "RoxasUI", package: "Roxas"), + .product(name: "AppCenterAnalytics", package: "appcenter-sdk-apple"), + .product(name: "AppCenterCrashes", package: "appcenter-sdk-apple"), + ], + resources: [ +// .process("Resources/Intents/Intents.intentdefinition"), +// .process("Resources/Intents/ViewApp.intentdefinition"), + ], + linkerSettings: [ + .linkedFramework("UIKit", .when(platforms: [.iOS, .macCatalyst, .tvOS])), + .linkedFramework("Avfoundation"), + .linkedFramework("Combine"), + .linkedFramework("Network"), + .linkedFramework("CoreData"), + .linkedFramework("UniformTypeIdentifiers"), + .linkedFramework("QuickLook", .when(platforms: [.iOS, .macCatalyst])), + .linkedFramework("AuthenticationServices", .when(platforms: [.iOS, .macCatalyst])), + .linkedFramework("SafariServices", .when(platforms: [.iOS, .macCatalyst])), + .linkedFramework("Intents", .when(platforms: [.iOS, .macCatalyst])), + .linkedFramework("IntentsUI", .when(platforms: [.iOS, .macCatalyst])), + .linkedFramework("MessageUI", .when(platforms: [.iOS, .macCatalyst])), + .linkedFramework("ARKit", .when(platforms: [.iOS, .macCatalyst])), + .linkedFramework("CoreHaptics", .when(platforms: [.iOS, .macCatalyst])), + .linkedFramework("AudioToolbox", .when(platforms: [.iOS, .macCatalyst])), + .linkedFramework("WidgetKit", .when(platforms: [.iOS, .macCatalyst])), + .linkedFramework("UserNotifications", .when(platforms: [.iOS, .macCatalyst])), + .linkedFramework("MobileCoreServices", .when(platforms: [.iOS, .macCatalyst])), + .linkedLibrary("AppleArchive") + ] + ), + // MARK: - SideWidget .executableTarget( - name: "SideWidget" + name: "SideWidget", + plugins: [ + "IntentBuilderPlugin", + "LoggerPlugin" + ] ), // MARK: - EmotionalDamage @@ -196,7 +215,10 @@ let package = Package( dependencies: [ "minimuxer", "libimobiledevice", - ] + ], + plugins: [ + "LoggerPlugin" + ] ), .binaryTarget( @@ -246,7 +268,10 @@ let package = Package( "AltSign", "SemanticVersion", .product(name: "Roxas", package: "Roxas"), - ] + ], + plugins: [ + "IntentBuilderPlugin", + ] ), .testTarget( @@ -280,7 +305,7 @@ let package = Package( name: "libimobiledevice", dependencies: [ "libimobiledevice-glue", - "libplist", +// "libplist", "libusbmuxd", "OpenSSL" ], @@ -435,7 +460,7 @@ let package = Package( .target( name: "libusbmuxd", dependencies: [ - "libplist", +// "libplist", "libimobiledevice-glue" ], path: "Sources/libimobiledevice/libusbmuxd/", @@ -476,6 +501,11 @@ let package = Package( .unsafeFlags(unsafe_flags_cxx) ] ), + + // MARK: - Plugins + .plugin(name: "IntentBuilderPlugin", capability: .buildTool()), + .plugin(name: "LoggerPlugin", capability: .buildTool()), + ], swiftLanguageVersions: [.v5], cLanguageStandard: .gnu11, @@ -512,3 +542,55 @@ func envBool(_ key: String) -> Bool { // name: "SideDaemonTests", // dependencies: ["SideDaemon"] // ), + + +// let dependencies_cargo: [Package.Dependency] = { +// USE_CARGO ? [ +// // CargoPlugin +// .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.3"), +// .package(url: "https://github.com/apple/swift-package-manager.git", branch: "release/5.7"), +// .package(url: "https://github.com/apple/swift-tools-support-core.git", branch: "release/5.7"), +// ] : [] +// }() + +// let cargo_targets: [Target] = [ +// .executableTarget( +// name: "Cargo", +// dependencies: [ +// .product(name: "ArgumentParser", package: "swift-argument-parser"), +// .product(name: "SwiftPM-auto", package: "swift-package-manager"), +// .product(name: "SwiftToolsSupport-auto", package: "swift-tools-support-core") +// ] +// ), +// +// .testTarget( +// name: "CargoTests", +// dependencies: ["Cargo"], +// exclude: [ +// "swiftlint", +// "xcframework" +// ] +// ), +// +// .plugin( +// name: "CargoPlugin", +// capability: .buildTool(), +// dependencies: [ +// "Cargo" +// ] +// ), +// +// .plugin( +// name: "CargoPlugin-Generate", +// capability: .command( +// intent: .custom( +// verb: "generate-code-from-rust", +// description: "Creates .c code from your `rust` code" +// ), +// permissions: [ +// .writeToPackageDirectory(reason: "This command generates source code") +// ] +// ), +// dependencies: ["Cargo"] +// ) +// ] diff --git a/Plugins/IntentBuilderPlugin/IntentBuilderPlugin.swift b/Plugins/IntentBuilderPlugin/IntentBuilderPlugin.swift new file mode 100644 index 00000000..f326b41d --- /dev/null +++ b/Plugins/IntentBuilderPlugin/IntentBuilderPlugin.swift @@ -0,0 +1,34 @@ +// Copyright 2022 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import PackagePlugin +import Foundation + +@main +struct IntentBuilderPlugin: BuildToolPlugin { + func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] { + let outputDirectory = context.pluginWorkDirectory.appending("GeneratedSources") + guard let target = target as? SourceModuleTarget else { + Diagnostics.error("Attempted to use `IntentBuilderPlugin` on an unsupported module target") + return [] + } + return target.sourceFiles(withSuffix: "intentdefinition") + .map { file in + .prebuildCommand( + displayName: "Generate intents sources", + executable: Path("/usr/bin/xcrun"), + arguments: [ + "intentbuilderc", "generate", + "-input", file.path.string, + "-output", outputDirectory, + "-language", "Swift", + "-swiftVersion", "5.6", + "-classPrefix", "" + ], + outputFilesDirectory: outputDirectory + ) + } + } +} diff --git a/Plugins/LoggerPlugin/LoggerPlugin.swift b/Plugins/LoggerPlugin/LoggerPlugin.swift new file mode 100644 index 00000000..1963af1b --- /dev/null +++ b/Plugins/LoggerPlugin/LoggerPlugin.swift @@ -0,0 +1,48 @@ +// Copyright 2022 The Brave Authors. All rights reserved. +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import PackagePlugin +import Foundation + +@main +struct LoggerPlugin: BuildToolPlugin { + func createBuildCommands(context: PluginContext, target: Target) async throws -> [Command] { + let outputDirectory = context.pluginWorkDirectory.appending("GeneratedSources") + guard let target = target as? SourceModuleTarget else { + Diagnostics.error("Attempted to use `LoggerPlugin` on an unsupported module target") + return [] + } + + try FileManager.default.createDirectory(atPath: outputDirectory.string, withIntermediateDirectories: true) + let source = """ + import Foundation + import os.log + + extension Logger { + static var module: Logger { + .init(subsystem: "\\(Bundle.main.bundleIdentifier ?? "com.sidestore.ios")", category: "\(target.moduleName)") + } + } + """ + + let filePath = outputDirectory.appending("logger.swift") + if !FileManager.default.fileExists(atPath: filePath.string) { + try source.write( + toFile: filePath.string, + atomically: true, + encoding: .utf8 + ) + } + // TODO: Generate the above file in an `executableTarget` when SPM supports building Mac tools while targetting iOS + return [ + .buildCommand( + displayName: "Generate logger", + executable: Path("/bin/zsh"), + arguments: [], + outputFiles: [outputDirectory.appending("logger.swift")] + ) + ] + } +} diff --git a/Sources/MiniMuxerSwift/MiniMuxer.swift b/Sources/MiniMuxerSwift/MiniMuxer.swift index 47bc2e22..6e0adc46 100644 --- a/Sources/MiniMuxerSwift/MiniMuxer.swift +++ b/Sources/MiniMuxerSwift/MiniMuxer.swift @@ -7,7 +7,7 @@ import Foundation import os.log -import minimuxer +@_exported import minimuxer public enum Uhoh: Error { case Good diff --git a/Sources/Shared/Extensions/NSError+AltStore.swift b/Sources/Shared/Extensions/NSError+AltStore.swift index bb549742..98f54ab0 100644 --- a/Sources/Shared/Extensions/NSError+AltStore.swift +++ b/Sources/Shared/Extensions/NSError+AltStore.swift @@ -8,7 +8,7 @@ import Foundation -extension NSError { +public extension NSError { @objc(alt_localizedFailure) var localizedFailure: String? { let localizedFailure = (userInfo[NSLocalizedFailureErrorKey] as? String) ?? (NSError.userInfoValueProvider(forDomain: domain)?(self, NSLocalizedFailureErrorKey) as? String) @@ -78,7 +78,7 @@ extension NSError { } } -extension Error { +public extension Error { var underlyingError: Error? { let underlyingError = (self as NSError).userInfo[NSUnderlyingErrorKey] as? Error return underlyingError @@ -90,13 +90,13 @@ extension Error { } } -protocol ALTLocalizedError: LocalizedError, CustomNSError { +public protocol ALTLocalizedError: LocalizedError, CustomNSError { var failure: String? { get } var underlyingError: Error? { get } } -extension ALTLocalizedError { +public extension ALTLocalizedError { var errorUserInfo: [String: Any] { let userInfo = ([ NSLocalizedDescriptionKey: errorDescription, diff --git a/Sources/SidePatcher/SidePatcher.h b/Sources/SidePatcher/SidePatcher.h index 5b031381..4223f08e 100644 --- a/Sources/SidePatcher/SidePatcher.h +++ b/Sources/SidePatcher/SidePatcher.h @@ -10,7 +10,7 @@ NS_ASSUME_NONNULL_BEGIN -@interface ALTAppPatcher : NSObject +@interface SideAppPatcher : NSObject - (BOOL)patchAppBinaryAtURL:(NSURL *)appFileURL withBinaryAtURL:(NSURL *)patchFileURL error:(NSError *_Nullable *)error; diff --git a/Sources/SidePatcher/SidePatcher.m b/Sources/SidePatcher/SidePatcher.m index f2d8da63..ee3ac50a 100644 --- a/Sources/SidePatcher/SidePatcher.m +++ b/Sources/SidePatcher/SidePatcher.m @@ -102,7 +102,7 @@ void *injectApp(void *originalApp, size_t originalAppSize, void *appToInject, si return fatBuf; } -@implementation ALTAppPatcher +@implementation SideAppPatcher - (BOOL)patchAppBinaryAtURL:(NSURL *)appFileURL withBinaryAtURL:(NSURL *)patchFileURL error:(NSError *__autoreleasing *)error { diff --git a/Sources/SidePatcher/include/SidePatcher/SidePatcher.h b/Sources/SidePatcher/include/SidePatcher/SidePatcher.h index 8f7ea7c8..41a5b164 100644 --- a/Sources/SidePatcher/include/SidePatcher/SidePatcher.h +++ b/Sources/SidePatcher/include/SidePatcher/SidePatcher.h @@ -8,11 +8,11 @@ #import -//! Project version number for ALTAppPatcher. -FOUNDATION_EXPORT double ALTAppPatcherVersionNumber; +//! Project version number for SideAppPatcher. +FOUNDATION_EXPORT double SideAppPatcherVersionNumber; -//! Project version string for ALTAppPatcher. -FOUNDATION_EXPORT const unsigned char ALTAppPatcherVersionString[]; +//! Project version string for SideAppPatcher. +FOUNDATION_EXPORT const unsigned char SideAppPatcherVersionString[]; # pragma mark - SidePatcher diff --git a/Sources/SideStore/App Detail/AppContentViewController.swift b/Sources/SideStore/App Detail/AppContentViewController.swift index 3819300e..9a583fe8 100644 --- a/Sources/SideStore/App Detail/AppContentViewController.swift +++ b/Sources/SideStore/App Detail/AppContentViewController.swift @@ -9,7 +9,7 @@ import UIKit import SideStoreCore -import RoxasUI +import RoxasUIKit import Nuke diff --git a/Sources/SideStore/App Detail/AppViewController.swift b/Sources/SideStore/App Detail/AppViewController.swift index 9f02dadf..91d14468 100644 --- a/Sources/SideStore/App Detail/AppViewController.swift +++ b/Sources/SideStore/App Detail/AppViewController.swift @@ -9,7 +9,7 @@ import UIKit import SideStoreCore -import RoxasUI +import RoxasUIKit import Nuke diff --git a/Sources/SideStore/App IDs/AppIDsViewController.swift b/Sources/SideStore/App IDs/AppIDsViewController.swift index 20c1c53a..9e68cc45 100644 --- a/Sources/SideStore/App IDs/AppIDsViewController.swift +++ b/Sources/SideStore/App IDs/AppIDsViewController.swift @@ -9,7 +9,8 @@ import UIKit import SideStoreCore -import RoxasUI +import RoxasUIKit +import Down final class AppIDsViewController: UICollectionViewController { private lazy var dataSource = self.makeDataSource() @@ -174,15 +175,31 @@ extension AppIDsViewController: UICollectionViewDelegateFlowLayout { headerView.layoutMargins.right = view.layoutMargins.right if let activeTeam = DatabaseManager.shared.activeTeam(), activeTeam.type == .free { - let text = NSLocalizedString(""" + let text = NSLocalizedString( + """ Each app and app extension installed with SideStore must register an App ID with Apple. Apple limits non-developer Apple IDs to 10 App IDs at a time. **App IDs can't be deleted**, but they do expire after one week. SideStore will automatically renew App IDs for all active apps once they've expired. """, comment: "") - let attributedText = NSAttributedString(markdownRepresentation: text, attributes: [.font: headerView.textLabel.font as Any]) - headerView.textLabel.attributedText = attributedText - } else { + let mdown = Down(markdownString: text) + let labelFont: DownFont = headerView.textLabel.font + let fonts: FontCollection = StaticFontCollection( + heading1: labelFont, + heading2: labelFont, + heading3: labelFont, + heading4: labelFont, + heading5: labelFont, + heading6: labelFont, + body: labelFont, + code: labelFont, + listItemPrefix: labelFont) + let config: DownStylerConfiguration = .init(fonts: fonts) + let styler: Styler = DownStyler.init(configuration: config) + let options: DownOptions = .default + headerView.textLabel.attributedText = try? mdown.toAttributedString(options, + styler: styler) ?? NSAttributedString(string: text) + } else { headerView.textLabel.text = NSLocalizedString(""" Each app and app extension installed with SideStore must register an App ID with Apple. @@ -208,3 +225,194 @@ extension AppIDsViewController: UICollectionViewDelegateFlowLayout { } } } + +fileprivate enum MarkdownStyledBlock: Equatable { + case generic + case headline(Int) + case paragraph + case unorderedListElement + case orderedListElement(Int) + case blockquote + case code(String?) +} + +// MARK: - +@available(iOS 15, *) +extension AttributedString { + + init(styledMarkdown markdownString: String, fontSize: CGFloat = UIFont.preferredFont(forTextStyle: .body).pointSize) throws { + + var s = try AttributedString(markdown: markdownString, options: .init(allowsExtendedAttributes: true, interpretedSyntax: .full, failurePolicy: .returnPartiallyParsedIfPossible, languageCode: "en"), baseURL: nil) + + // Looking at the AttributedString’s raw structure helps with understanding the following code. + print(s) + + // Set base font and paragraph style for the whole string + s.font = .systemFont(ofSize: fontSize) + s.paragraphStyle = defaultParagraphStyle + + // Will respect dark mode automatically + s.foregroundColor = .label + + // MARK: Inline Intents + let inlineIntents: [InlinePresentationIntent] = [.emphasized, .stronglyEmphasized, .code, .strikethrough, .softBreak, .lineBreak, .inlineHTML, .blockHTML] + + for inlineIntent in inlineIntents { + + var sourceAttributeContainer = AttributeContainer() + sourceAttributeContainer.inlinePresentationIntent = inlineIntent + + var targetAttributeContainer = AttributeContainer() + switch inlineIntent { + case .emphasized: + targetAttributeContainer.font = .italicSystemFont(ofSize: fontSize) + case .stronglyEmphasized: + targetAttributeContainer.font = .systemFont(ofSize: fontSize, weight: .bold) + case .code: + targetAttributeContainer.font = .monospacedSystemFont(ofSize: fontSize, weight: .regular) + targetAttributeContainer.backgroundColor = .secondarySystemBackground + case .strikethrough: + targetAttributeContainer.strikethroughStyle = .single + case .softBreak: + break // TODO: Implement + case .lineBreak: + break // TODO: Implement + case .inlineHTML: + break // TODO: Implement + case .blockHTML: + break // TODO: Implement + default: + break + } + + s = s.replacingAttributes(sourceAttributeContainer, with: targetAttributeContainer) + } + + // MARK: Blocks + // Accessing via dynamic lookup key path (\.presentationIntent) triggers a warning on Xcode 13.1, so we use the verbose way: AttributeScopes.FoundationAttributes.PresentationIntentAttribute.self + // We use .reversed() iteration to be able to add characters to the string without breaking ranges. + var previousListID = 0 + + for (intentBlock, intentRange) in s.runs[AttributeScopes.FoundationAttributes.PresentationIntentAttribute.self].reversed() { + guard let intentBlock = intentBlock else { continue } + + var block: MarkdownStyledBlock = .generic + var currentElementOrdinal: Int = 0 + + var currentListID = 0 + + for intent in intentBlock.components { + switch intent.kind { + case .paragraph: + if block == .generic { + block = .paragraph + } + case .header(level: let level): + block = .headline(level) + case .orderedList: + block = .orderedListElement(currentElementOrdinal) + currentListID = intent.identity + case .unorderedList: + block = .unorderedListElement + currentListID = intent.identity + case .listItem(ordinal: let ordinal): + currentElementOrdinal = ordinal + if block != .unorderedListElement { + block = .orderedListElement(ordinal) + } + case .codeBlock(languageHint: let languageHint): + block = .code(languageHint) + case .blockQuote: + block = .blockquote + case .thematicBreak: + break // This is ---- in Markdown. + case .table(columns: _): + break + case .tableHeaderRow: + break + case .tableRow(rowIndex: _): + break + case .tableCell(columnIndex: _): + break + @unknown default: + break + } + } + + switch block { + case .generic: + assertionFailure(intentBlock.debugDescription) + case .headline(let level): + switch level { + case 1: + s[intentRange].font = .systemFont(ofSize: 30, weight: .heavy) + case 2: + s[intentRange].font = .systemFont(ofSize: 20, weight: .heavy) + case 3: + s[intentRange].font = .systemFont(ofSize: 15, weight: .heavy) + default: + // TODO: Handle H4 to H6 + s[intentRange].font = .systemFont(ofSize: 15, weight: .heavy) + } + case .paragraph: + break + case .unorderedListElement: + s.characters.insert(contentsOf: "•\t", at: intentRange.lowerBound) + s[intentRange].paragraphStyle = previousListID == currentListID ? listParagraphStyle : lastElementListParagraphStyle + case .orderedListElement(let ordinal): + s.characters.insert(contentsOf: "\(ordinal).\t", at: intentRange.lowerBound) + s[intentRange].paragraphStyle = previousListID == currentListID ? listParagraphStyle : lastElementListParagraphStyle + case .blockquote: + s[intentRange].paragraphStyle = defaultParagraphStyle + s[intentRange].foregroundColor = .secondaryLabel + case .code: + s[intentRange].font = .monospacedSystemFont(ofSize: 13, weight: .regular) + s[intentRange].paragraphStyle = codeParagraphStyle + } + + // Remember the list ID so we can check if it’s identical in the next block + previousListID = currentListID + + // MARK: Add line breaks to separate blocks + if intentRange.lowerBound != s.startIndex { + s.characters.insert(contentsOf: "\n", at: intentRange.lowerBound) + } + } + + self = s + } +} + +fileprivate let defaultParagraphStyle: NSParagraphStyle = { + var paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.paragraphSpacing = 10.0 + paragraphStyle.minimumLineHeight = 20.0 + return paragraphStyle +}() + + +fileprivate let listParagraphStyle: NSMutableParagraphStyle = { + var paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.tabStops = [NSTextTab(textAlignment: .left, location: 20)] + paragraphStyle.headIndent = 20 + paragraphStyle.minimumLineHeight = 20.0 + return paragraphStyle +}() + +fileprivate let lastElementListParagraphStyle: NSMutableParagraphStyle = { + var paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.tabStops = [NSTextTab(textAlignment: .left, location: 20)] + paragraphStyle.headIndent = 20 + paragraphStyle.minimumLineHeight = 20.0 + paragraphStyle.paragraphSpacing = 20.0 // The last element in a list needs extra paragraph spacing + return paragraphStyle +}() + + +fileprivate let codeParagraphStyle: NSParagraphStyle = { + var paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.minimumLineHeight = 20.0 + paragraphStyle.firstLineHeadIndent = 20 + paragraphStyle.headIndent = 20 + return paragraphStyle +}() diff --git a/Sources/SideStore/AppDelegate.swift b/Sources/SideStore/AppDelegate.swift index e17cbcc4..2fd165a4 100644 --- a/Sources/SideStore/AppDelegate.swift +++ b/Sources/SideStore/AppDelegate.swift @@ -14,7 +14,7 @@ import UserNotifications import AltSign import SideStoreCore import EmotionalDamage -import RoxasUI +import RoxasUIKit extension AppDelegate { static let openPatreonSettingsDeepLinkNotification = Notification.Name(Bundle.Info.appbundleIdentifier + ".OpenPatreonSettingsDeepLinkNotification") diff --git a/Sources/SideStore/Intents/IntentHandler.swift b/Sources/SideStore/Authentication/Intents/IntentHandler.swift similarity index 99% rename from Sources/SideStore/Intents/IntentHandler.swift rename to Sources/SideStore/Authentication/Intents/IntentHandler.swift index 431c84bc..ca86eec1 100644 --- a/Sources/SideStore/Intents/IntentHandler.swift +++ b/Sources/SideStore/Authentication/Intents/IntentHandler.swift @@ -7,8 +7,8 @@ // import Foundation - import SideStoreCore +import Intents @available(iOS 14, *) final class IntentHandler: NSObject, RefreshAllIntentHandling { diff --git a/Sources/SideStore/Resources/Intents/Intents.intentdefinition b/Sources/SideStore/Authentication/Intents/Intents.intentdefinition similarity index 100% rename from Sources/SideStore/Resources/Intents/Intents.intentdefinition rename to Sources/SideStore/Authentication/Intents/Intents.intentdefinition diff --git a/Sources/SideStore/Resources/Intents/ViewApp.intentdefinition b/Sources/SideStore/Authentication/Intents/ViewApp.intentdefinition similarity index 100% rename from Sources/SideStore/Resources/Intents/ViewApp.intentdefinition rename to Sources/SideStore/Authentication/Intents/ViewApp.intentdefinition diff --git a/Sources/SideStore/Intents/ViewAppIntentHandler.swift b/Sources/SideStore/Authentication/Intents/ViewAppIntentHandler.swift similarity index 96% rename from Sources/SideStore/Intents/ViewAppIntentHandler.swift rename to Sources/SideStore/Authentication/Intents/ViewAppIntentHandler.swift index 0afe910b..2ac5afdd 100644 --- a/Sources/SideStore/Intents/ViewAppIntentHandler.swift +++ b/Sources/SideStore/Authentication/Intents/ViewAppIntentHandler.swift @@ -7,6 +7,8 @@ // import Intents +import Shared +import SideStoreCore @available(iOS 14, *) public class ViewAppIntentHandler: NSObject, ViewAppIntentHandling { diff --git a/Sources/SideStore/Authentication/RefreshAltStoreViewController.swift b/Sources/SideStore/Authentication/RefreshAltStoreViewController.swift index c440214e..f6cf419b 100644 --- a/Sources/SideStore/Authentication/RefreshAltStoreViewController.swift +++ b/Sources/SideStore/Authentication/RefreshAltStoreViewController.swift @@ -10,7 +10,7 @@ import UIKit import AltSign import SideStoreCore -import RoxasUI +import RoxasUIKit final class RefreshAltStoreViewController: UIViewController { var context: AuthenticatedOperationContext! diff --git a/Sources/SideStore/Authentication/SelectTeamViewController.swift b/Sources/SideStore/Authentication/SelectTeamViewController.swift index d98fd2dc..dd455284 100644 --- a/Sources/SideStore/Authentication/SelectTeamViewController.swift +++ b/Sources/SideStore/Authentication/SelectTeamViewController.swift @@ -11,6 +11,7 @@ import IntentsUI import MessageUI import SafariServices import UIKit +import os.log import AltSign @@ -33,7 +34,19 @@ final class SelectTeamViewController: UITableViewController { } override func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) { - completionHandler!(.success(teams?[indexPath.row]!)) + precondition(completionHandler != nil) + precondition(teams != nil) + precondition(teams!.count <= indexPath.row) + + guard let completionHandler = completionHandler else { + os_log("completionHandler was nil", type: .error) + return + } + guard let teams = teams, teams.count <= indexPath.row else { + os_log("teams nil or out of bounds", type: .error) + return + } + completionHandler(.success(teams[indexPath.row])) } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { diff --git a/Sources/SideStore/Browse/BrowseCollectionViewCell.swift b/Sources/SideStore/Browse/BrowseCollectionViewCell.swift index a5ec7107..3429cb11 100644 --- a/Sources/SideStore/Browse/BrowseCollectionViewCell.swift +++ b/Sources/SideStore/Browse/BrowseCollectionViewCell.swift @@ -8,7 +8,7 @@ import UIKit -import RoxasUI +import RoxasUIKit import Nuke diff --git a/Sources/SideStore/Browse/BrowseViewController.swift b/Sources/SideStore/Browse/BrowseViewController.swift index ccf94be7..f3aabf83 100644 --- a/Sources/SideStore/Browse/BrowseViewController.swift +++ b/Sources/SideStore/Browse/BrowseViewController.swift @@ -9,7 +9,7 @@ import UIKit import SideStoreCore -import RoxasUI +import RoxasUIKit import Nuke diff --git a/Sources/SideStore/Browse/ScreenshotCollectionViewCell.swift b/Sources/SideStore/Browse/ScreenshotCollectionViewCell.swift index 8b165538..8dba8b6c 100644 --- a/Sources/SideStore/Browse/ScreenshotCollectionViewCell.swift +++ b/Sources/SideStore/Browse/ScreenshotCollectionViewCell.swift @@ -8,7 +8,7 @@ import UIKit -import RoxasUI +import RoxasUIKit @objc(ScreenshotCollectionViewCell) class ScreenshotCollectionViewCell: UICollectionViewCell { diff --git a/Sources/SideStore/Components/AppBannerView.swift b/Sources/SideStore/Components/AppBannerView.swift index 511b563a..2d403869 100644 --- a/Sources/SideStore/Components/AppBannerView.swift +++ b/Sources/SideStore/Components/AppBannerView.swift @@ -9,7 +9,7 @@ import UIKit import SideStoreCore -import RoxasUI +import RoxasUIKit class AppBannerView: RSTNibView { override var accessibilityLabel: String? { diff --git a/Sources/SideStore/Components/NavigationBar.swift b/Sources/SideStore/Components/NavigationBar.swift index b599c3ba..a390655a 100644 --- a/Sources/SideStore/Components/NavigationBar.swift +++ b/Sources/SideStore/Components/NavigationBar.swift @@ -8,7 +8,7 @@ import UIKit -import RoxasUI +import RoxasUIKit final class NavigationBar: UINavigationBar { @IBInspectable var automaticallyAdjustsItemPositions: Bool = true diff --git a/Sources/SideStore/Components/ToastView.swift b/Sources/SideStore/Components/ToastView.swift index dfc9be5d..407d501f 100644 --- a/Sources/SideStore/Components/ToastView.swift +++ b/Sources/SideStore/Components/ToastView.swift @@ -6,9 +6,11 @@ // Copyright © 2019 Riley Testut. All rights reserved. // -import RoxasUI - +import RoxasUIKit +import Shared import SideStoreCore +import SideKit +import AltSign extension TimeInterval { static let shortToastViewDuration = 4.0 @@ -47,7 +49,7 @@ final class ToastView: RSTToastView { if let unwrappedUnderlyingError = underlyingError, - error.domain == AltServerErrorDomain && error.code == ALTServerError.Code.underlyingError.rawValue + error.domain == AltServerErrorDomain && error.code == -1 //ALTServerError.underlyingError().rawValue { // Treat underlyingError as the primary error. diff --git a/Sources/SideStore/Extensions/UIDevice+Vibration.swift b/Sources/SideStore/Extensions/UIDevice+Vibration.swift index 8a3b9690..9b492719 100644 --- a/Sources/SideStore/Extensions/UIDevice+Vibration.swift +++ b/Sources/SideStore/Extensions/UIDevice+Vibration.swift @@ -8,6 +8,7 @@ import AudioToolbox import CoreHaptics +import UIKit private extension SystemSoundID { static let pop = SystemSoundID(1520) diff --git a/Sources/SideStore/LaunchViewController.swift b/Sources/SideStore/LaunchViewController.swift index ec642a3c..918a6a4b 100644 --- a/Sources/SideStore/LaunchViewController.swift +++ b/Sources/SideStore/LaunchViewController.swift @@ -8,7 +8,8 @@ import EmotionalDamage import minimuxer -import RoxasUI +import MiniMuxerSwift +import RoxasUIKit import UIKit import SideStoreCore diff --git a/Sources/SideStore/Managing Apps/AppManager.swift b/Sources/SideStore/Managing Apps/AppManager.swift index e066a147..e822a219 100644 --- a/Sources/SideStore/Managing Apps/AppManager.swift +++ b/Sources/SideStore/Managing Apps/AppManager.swift @@ -13,10 +13,12 @@ import MobileCoreServices import UIKit import UserNotifications import WidgetKit +import Shared import AltSign +import SideKit import SideStoreCore -import RoxasUI +import RoxasUIKit extension AppManager { static let didFetchSourceNotification = Notification.Name("io.altstore.AppManager.didFetchSource") diff --git a/Sources/SideStore/My Apps/MyAppsComponents.swift b/Sources/SideStore/My Apps/MyAppsComponents.swift index 3212ca8a..0fff4f65 100644 --- a/Sources/SideStore/My Apps/MyAppsComponents.swift +++ b/Sources/SideStore/My Apps/MyAppsComponents.swift @@ -6,7 +6,7 @@ // Copyright © 2019 Riley Testut. All rights reserved. // -import RoxasUI +import RoxasUIKit import UIKit final class InstalledAppCollectionViewCell: UICollectionViewCell { diff --git a/Sources/SideStore/My Apps/MyAppsViewController.swift b/Sources/SideStore/My Apps/MyAppsViewController.swift index a8427b7f..406369a9 100644 --- a/Sources/SideStore/My Apps/MyAppsViewController.swift +++ b/Sources/SideStore/My Apps/MyAppsViewController.swift @@ -13,7 +13,7 @@ import UIKit import AltSign import SideStoreCore -import RoxasUI +import RoxasUIKit import Nuke diff --git a/Sources/SideStore/News/NewsViewController.swift b/Sources/SideStore/News/NewsViewController.swift index 74540d22..cf69a359 100644 --- a/Sources/SideStore/News/NewsViewController.swift +++ b/Sources/SideStore/News/NewsViewController.swift @@ -10,7 +10,7 @@ import SafariServices import UIKit import SideStoreCore -import RoxasUI +import RoxasUIKit import Nuke diff --git a/Sources/SideStore/Operations/AuthenticationOperation.swift b/Sources/SideStore/Operations/AuthenticationOperation.swift index 5c1d51b6..54a7fbe0 100644 --- a/Sources/SideStore/Operations/AuthenticationOperation.swift +++ b/Sources/SideStore/Operations/AuthenticationOperation.swift @@ -8,7 +8,7 @@ import Foundation import Network -import RoxasUI +import RoxasUIKit import AltSign import SideStoreCore diff --git a/Sources/SideStore/Operations/DeactivateAppOperation.swift b/Sources/SideStore/Operations/DeactivateAppOperation.swift index 9090eaad..674b5e44 100644 --- a/Sources/SideStore/Operations/DeactivateAppOperation.swift +++ b/Sources/SideStore/Operations/DeactivateAppOperation.swift @@ -11,7 +11,9 @@ import Foundation import AltSign import SideStoreCore import minimuxer -import RoxasUI +import MiniMuxerSwift +import RoxasUIKit +import SideKit @objc(DeactivateAppOperation) final class DeactivateAppOperation: ResultOperation { @@ -47,7 +49,7 @@ final class DeactivateAppOperation: ResultOperation { } catch let Uhoh.Bad(code) { self.finish(.failure(minimuxer_to_operation(code: code))) } catch { - self.finish(.failure(ALTServerError(.unknownResponse))) + self.finish(.failure(ALTServerError.unknownResponse)) } } diff --git a/Sources/SideStore/Operations/DownloadAppOperation.swift b/Sources/SideStore/Operations/DownloadAppOperation.swift index 211fe1d2..64371729 100644 --- a/Sources/SideStore/Operations/DownloadAppOperation.swift +++ b/Sources/SideStore/Operations/DownloadAppOperation.swift @@ -7,10 +7,12 @@ // import Foundation -import RoxasUI +import RoxasUIKit import AltSign +import SideKit import SideStoreCore +import Shared private extension DownloadAppOperation { struct DependencyError: ALTLocalizedError { diff --git a/Sources/SideStore/Operations/EnableJITOperation.swift b/Sources/SideStore/Operations/EnableJITOperation.swift index 1ce99e80..8f6f8331 100644 --- a/Sources/SideStore/Operations/EnableJITOperation.swift +++ b/Sources/SideStore/Operations/EnableJITOperation.swift @@ -8,6 +8,7 @@ import Combine import minimuxer +import MiniMuxerSwift import UIKit import SideStoreCore diff --git a/Sources/SideStore/Operations/FetchAnisetteDataOperation.swift b/Sources/SideStore/Operations/FetchAnisetteDataOperation.swift index fbd98b75..08b7a400 100644 --- a/Sources/SideStore/Operations/FetchAnisetteDataOperation.swift +++ b/Sources/SideStore/Operations/FetchAnisetteDataOperation.swift @@ -10,7 +10,7 @@ import Foundation import AltSign import SideStoreCore -import RoxasUI +import RoxasUIKit @objc(FetchAnisetteDataOperation) final class FetchAnisetteDataOperation: ResultOperation { diff --git a/Sources/SideStore/Operations/FetchAppIDsOperation.swift b/Sources/SideStore/Operations/FetchAppIDsOperation.swift index 7d01092a..72df0032 100644 --- a/Sources/SideStore/Operations/FetchAppIDsOperation.swift +++ b/Sources/SideStore/Operations/FetchAppIDsOperation.swift @@ -10,7 +10,7 @@ import Foundation import AltSign import SideStoreCore -import RoxasUI +import RoxasUIKit @objc(FetchAppIDsOperation) final class FetchAppIDsOperation: ResultOperation<([AppID], NSManagedObjectContext)> { diff --git a/Sources/SideStore/Operations/FetchProvisioningProfilesOperation.swift b/Sources/SideStore/Operations/FetchProvisioningProfilesOperation.swift index 3bf1a07d..06124f6d 100644 --- a/Sources/SideStore/Operations/FetchProvisioningProfilesOperation.swift +++ b/Sources/SideStore/Operations/FetchProvisioningProfilesOperation.swift @@ -10,7 +10,7 @@ import Foundation import AltSign import SideStoreCore -import RoxasUI +import RoxasUIKit @objc(FetchProvisioningProfilesOperation) final class FetchProvisioningProfilesOperation: ResultOperation<[String: ALTProvisioningProfile]> { diff --git a/Sources/SideStore/Operations/FetchSourceOperation.swift b/Sources/SideStore/Operations/FetchSourceOperation.swift index 8381c26f..e150952d 100644 --- a/Sources/SideStore/Operations/FetchSourceOperation.swift +++ b/Sources/SideStore/Operations/FetchSourceOperation.swift @@ -10,7 +10,7 @@ import CoreData import Foundation import SideStoreCore -import RoxasUI +import RoxasUIKit @objc(FetchSourceOperation) final class FetchSourceOperation: ResultOperation { @@ -46,7 +46,7 @@ final class FetchSourceOperation: ResultOperation { do { let (data, _) = try Result((data, response), error).get() - let decoder = AltStoreCore.JSONDecoder() + let decoder = SideStoreCore.JSONDecoder() decoder.dateDecodingStrategy = .custom { decoder -> Date in let container = try decoder.singleValueContainer() let text = try container.decode(String.self) diff --git a/Sources/SideStore/Operations/InstallAppOperation.swift b/Sources/SideStore/Operations/InstallAppOperation.swift index 45dade01..0996f0f3 100644 --- a/Sources/SideStore/Operations/InstallAppOperation.swift +++ b/Sources/SideStore/Operations/InstallAppOperation.swift @@ -10,7 +10,9 @@ import Network import AltSign import SideStoreCore -import RoxasUI +import RoxasUIKit +import MiniMuxerSwift +import minimuxer @objc(InstallAppOperation) final class InstallAppOperation: ResultOperation { diff --git a/Sources/SideStore/Operations/Operation.swift b/Sources/SideStore/Operations/Operation.swift index 94ff5c49..b2719fbd 100644 --- a/Sources/SideStore/Operations/Operation.swift +++ b/Sources/SideStore/Operations/Operation.swift @@ -7,7 +7,7 @@ // import Foundation -import RoxasUI +import RoxasUIKit class ResultOperation: Operation { var resultHandler: ((Result) -> Void)? diff --git a/Sources/SideStore/Operations/Patch App/PatchAppOperation.swift b/Sources/SideStore/Operations/Patch App/PatchAppOperation.swift index 4716f1ad..41d96e54 100644 --- a/Sources/SideStore/Operations/Patch App/PatchAppOperation.swift +++ b/Sources/SideStore/Operations/Patch App/PatchAppOperation.swift @@ -10,10 +10,11 @@ import AppleArchive import Combine import System import UIKit +import SidePatcher import AltSign import SideStoreCore -import RoxasUI +import RoxasUIKit @available(iOS 14, *) protocol PatchAppContext { @@ -52,7 +53,7 @@ final class PatchAppOperation: ResultOperation { var progressHandler: ((Progress, String) -> Void)? - private let appPatcher = ALTAppPatcher() + private let appPatcher = SideAppPatcher() private lazy var patchDirectory: URL = self.context.temporaryDirectory.appendingPathComponent("Patch", isDirectory: true) private var cancellable: AnyCancellable? diff --git a/Sources/SideStore/Operations/Patch App/PatchViewController.swift b/Sources/SideStore/Operations/Patch App/PatchViewController.swift index e8b3a575..9feddfbd 100644 --- a/Sources/SideStore/Operations/Patch App/PatchViewController.swift +++ b/Sources/SideStore/Operations/Patch App/PatchViewController.swift @@ -11,7 +11,7 @@ import UIKit import AltSign import SideStoreCore -import RoxasUI +import RoxasUIKit @available(iOS 14.0, *) extension PatchViewController { diff --git a/Sources/SideStore/Operations/RefreshAppOperation.swift b/Sources/SideStore/Operations/RefreshAppOperation.swift index 11837db2..c1d2b665 100644 --- a/Sources/SideStore/Operations/RefreshAppOperation.swift +++ b/Sources/SideStore/Operations/RefreshAppOperation.swift @@ -11,7 +11,8 @@ import Foundation import AltSign import SideStoreCore import minimuxer -import RoxasUI +import MiniMuxerSwift +import RoxasUIKit @objc(RefreshAppOperation) final class RefreshAppOperation: ResultOperation { diff --git a/Sources/SideStore/Operations/RemoveAppOperation.swift b/Sources/SideStore/Operations/RemoveAppOperation.swift index 0d52fd48..9f669eed 100644 --- a/Sources/SideStore/Operations/RemoveAppOperation.swift +++ b/Sources/SideStore/Operations/RemoveAppOperation.swift @@ -10,6 +10,9 @@ import Foundation import SideStoreCore import minimuxer +import MiniMuxerSwift +import Shared +import SideKit @objc(RemoveAppOperation) final class RemoveAppOperation: ResultOperation { @@ -42,7 +45,7 @@ final class RemoveAppOperation: ResultOperation { } catch let Uhoh.Bad(code) { self.finish(.failure(minimuxer_to_operation(code: code))) } catch { - self.finish(.failure(ALTServerError(.appDeletionFailed))) + self.finish(.failure(ALTServerError.appDeletionFailed)) } DatabaseManager.shared.persistentContainer.performBackgroundTask { context in self.progress.completedUnitCount += 1 diff --git a/Sources/SideStore/Operations/ResignAppOperation.swift b/Sources/SideStore/Operations/ResignAppOperation.swift index 5a3ca7fe..8b46ba7f 100644 --- a/Sources/SideStore/Operations/ResignAppOperation.swift +++ b/Sources/SideStore/Operations/ResignAppOperation.swift @@ -7,7 +7,7 @@ // import Foundation -import RoxasUI +import RoxasUIKit import AltSign import SideStoreCore diff --git a/Sources/SideStore/Operations/SendAppOperation.swift b/Sources/SideStore/Operations/SendAppOperation.swift index 5271c5ae..2efb0ea5 100644 --- a/Sources/SideStore/Operations/SendAppOperation.swift +++ b/Sources/SideStore/Operations/SendAppOperation.swift @@ -9,6 +9,9 @@ import Foundation import Network import SideStoreCore +import Shared +import SideKit +import MiniMuxerSwift @objc(SendAppOperation) final class SendAppOperation: ResultOperation { @@ -58,7 +61,7 @@ final class SendAppOperation: ResultOperation { } } else { - finish(.failure(ALTServerError(.underlyingError))) + finish(.failure(ALTServerError.unknownResponse)) } } } diff --git a/Sources/SideStore/Operations/UpdatePatronsOperation.swift b/Sources/SideStore/Operations/UpdatePatronsOperation.swift index bd1d0f55..938d210b 100644 --- a/Sources/SideStore/Operations/UpdatePatronsOperation.swift +++ b/Sources/SideStore/Operations/UpdatePatronsOperation.swift @@ -48,7 +48,7 @@ final class UpdatePatronsOperation: ResultOperation { guard let data = data else { throw error! } - let response = try AltStoreCore.JSONDecoder().decode(Response.self, from: data) + let response = try SideStoreCore.JSONDecoder().decode(Response.self, from: data) Keychain.shared.patreonCreatorAccessToken = response.accessToken let previousRefreshID = UserDefaults.shared.patronsRefreshID diff --git a/Sources/SideStore/Operations/VerifyAppOperation.swift b/Sources/SideStore/Operations/VerifyAppOperation.swift index 56f70a85..85a4ad9a 100644 --- a/Sources/SideStore/Operations/VerifyAppOperation.swift +++ b/Sources/SideStore/Operations/VerifyAppOperation.swift @@ -9,9 +9,11 @@ import Foundation import AltSign -import RoxasUI +import RoxasUIKit +import SideKit +import Shared -enum VerificationError: ALTLocalizedError { +enum VerificationError: LocalizedError { case privateEntitlements(ALTApplication, entitlements: [String: Any]) case mismatchedBundleIdentifiers(ALTApplication, sourceBundleID: String) case iOSVersionNotSupported(ALTApplication) diff --git a/Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/100.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/100.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/100.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/100.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/1024.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/1024.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/1024.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/1024.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/114.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/114.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/114.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/114.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/120.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/120.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/120.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/120.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/144.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/144.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/144.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/144.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/152.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/152.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/152.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/152.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/167.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/167.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/167.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/167.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/180.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/180.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/180.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/180.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/20.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/20.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/20.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/20.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/29.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/29.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/29.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/29.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/40.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/40.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/40.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/40.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/50.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/50.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/50.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/50.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/57.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/57.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/57.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/57.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/58.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/58.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/58.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/58.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/60.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/60.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/60.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/60.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/72.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/72.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/72.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/72.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/76.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/76.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/76.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/76.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/80.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/80.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/80.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/80.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/87.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/87.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/87.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/87.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json b/Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json rename to Sources/SideStore/Resources/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/Sources/SideStore/Resources/Assets.xcassets/Back.imageset/Back@2x.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/Back.imageset/Back@2x.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Back.imageset/Back@2x.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Back.imageset/Back@2x.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/Back.imageset/Contents.json b/Sources/SideStore/Resources/Assets/Assets.xcassets/Back.imageset/Contents.json similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Back.imageset/Contents.json rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Back.imageset/Contents.json diff --git a/Sources/SideStore/Resources/Assets.xcassets/BetaBadge.imageset/BETA.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/BetaBadge.imageset/BETA.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/BetaBadge.imageset/BETA.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/BetaBadge.imageset/BETA.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/BetaBadge.imageset/BETA@2x.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/BetaBadge.imageset/BETA@2x.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/BetaBadge.imageset/BETA@2x.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/BetaBadge.imageset/BETA@2x.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/BetaBadge.imageset/BETA@3x.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/BetaBadge.imageset/BETA@3x.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/BetaBadge.imageset/BETA@3x.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/BetaBadge.imageset/BETA@3x.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/BetaBadge.imageset/Contents.json b/Sources/SideStore/Resources/Assets/Assets.xcassets/BetaBadge.imageset/Contents.json similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/BetaBadge.imageset/Contents.json rename to Sources/SideStore/Resources/Assets/Assets.xcassets/BetaBadge.imageset/Contents.json diff --git a/Sources/SideStore/Resources/Assets.xcassets/Colors/Background.colorset/Contents.json b/Sources/SideStore/Resources/Assets/Assets.xcassets/Colors/Background.colorset/Contents.json similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Colors/Background.colorset/Contents.json rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Colors/Background.colorset/Contents.json diff --git a/Sources/SideStore/Resources/Assets.xcassets/Colors/BlurTint.colorset/Contents.json b/Sources/SideStore/Resources/Assets/Assets.xcassets/Colors/BlurTint.colorset/Contents.json similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Colors/BlurTint.colorset/Contents.json rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Colors/BlurTint.colorset/Contents.json diff --git a/Sources/SideStore/Resources/Assets.xcassets/Colors/Contents.json b/Sources/SideStore/Resources/Assets/Assets.xcassets/Colors/Contents.json similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Colors/Contents.json rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Colors/Contents.json diff --git a/Sources/SideStore/Resources/Assets.xcassets/Colors/SettingsBackground.colorset/Contents.json b/Sources/SideStore/Resources/Assets/Assets.xcassets/Colors/SettingsBackground.colorset/Contents.json similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Colors/SettingsBackground.colorset/Contents.json rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Colors/SettingsBackground.colorset/Contents.json diff --git a/Sources/SideStore/Resources/Assets.xcassets/Colors/SettingsHighlighted.colorset/Contents.json b/Sources/SideStore/Resources/Assets/Assets.xcassets/Colors/SettingsHighlighted.colorset/Contents.json similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Colors/SettingsHighlighted.colorset/Contents.json rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Colors/SettingsHighlighted.colorset/Contents.json diff --git a/Sources/SideStore/Resources/Assets.xcassets/Contents.json b/Sources/SideStore/Resources/Assets/Assets.xcassets/Contents.json similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Contents.json rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Contents.json diff --git a/Sources/SideStore/Resources/Assets.xcassets/Next.imageset/Back@2x.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/Next.imageset/Back@2x.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Next.imageset/Back@2x.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Next.imageset/Back@2x.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/Next.imageset/Contents.json b/Sources/SideStore/Resources/Assets/Assets.xcassets/Next.imageset/Contents.json similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Next.imageset/Contents.json rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Next.imageset/Contents.json diff --git a/Sources/SideStore/Resources/Assets.xcassets/Riley.imageset/Contents.json b/Sources/SideStore/Resources/Assets/Assets.xcassets/Riley.imageset/Contents.json similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Riley.imageset/Contents.json rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Riley.imageset/Contents.json diff --git a/Sources/SideStore/Resources/Assets.xcassets/Riley.imageset/riley.jpg b/Sources/SideStore/Resources/Assets/Assets.xcassets/Riley.imageset/riley.jpg similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Riley.imageset/riley.jpg rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Riley.imageset/riley.jpg diff --git a/Sources/SideStore/Resources/Assets.xcassets/Shane.imageset/Contents.json b/Sources/SideStore/Resources/Assets/Assets.xcassets/Shane.imageset/Contents.json similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Shane.imageset/Contents.json rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Shane.imageset/Contents.json diff --git a/Sources/SideStore/Resources/Assets.xcassets/Shane.imageset/shane.jpeg b/Sources/SideStore/Resources/Assets/Assets.xcassets/Shane.imageset/shane.jpeg similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Shane.imageset/shane.jpeg rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Shane.imageset/shane.jpeg diff --git a/Sources/SideStore/Resources/Assets.xcassets/Tabs/Browse.imageset/Combined Shape.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/Browse.imageset/Combined Shape.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Tabs/Browse.imageset/Combined Shape.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/Browse.imageset/Combined Shape.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/Tabs/Browse.imageset/Combined Shape@2x.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/Browse.imageset/Combined Shape@2x.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Tabs/Browse.imageset/Combined Shape@2x.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/Browse.imageset/Combined Shape@2x.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/Tabs/Browse.imageset/Combined Shape@3x.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/Browse.imageset/Combined Shape@3x.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Tabs/Browse.imageset/Combined Shape@3x.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/Browse.imageset/Combined Shape@3x.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/Tabs/Browse.imageset/Contents.json b/Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/Browse.imageset/Contents.json similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Tabs/Browse.imageset/Contents.json rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/Browse.imageset/Contents.json diff --git a/Sources/SideStore/Resources/Assets.xcassets/Tabs/Contents.json b/Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/Contents.json similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Tabs/Contents.json rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/Contents.json diff --git a/Sources/SideStore/Resources/Assets.xcassets/Tabs/MyApps.imageset/Contents.json b/Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/MyApps.imageset/Contents.json similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Tabs/MyApps.imageset/Contents.json rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/MyApps.imageset/Contents.json diff --git a/Sources/SideStore/Resources/Assets.xcassets/Tabs/MyApps.imageset/Group 10.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/MyApps.imageset/Group 10.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Tabs/MyApps.imageset/Group 10.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/MyApps.imageset/Group 10.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/Tabs/MyApps.imageset/Group 11.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/MyApps.imageset/Group 11.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Tabs/MyApps.imageset/Group 11.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/MyApps.imageset/Group 11.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/Tabs/MyApps.imageset/Group 12.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/MyApps.imageset/Group 12.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Tabs/MyApps.imageset/Group 12.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/MyApps.imageset/Group 12.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/Tabs/News.imageset/Contents.json b/Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/News.imageset/Contents.json similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Tabs/News.imageset/Contents.json rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/News.imageset/Contents.json diff --git a/Sources/SideStore/Resources/Assets.xcassets/Tabs/News.imageset/Group 6@2x.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/News.imageset/Group 6@2x.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Tabs/News.imageset/Group 6@2x.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/News.imageset/Group 6@2x.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/Tabs/News.imageset/Group 6@3x.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/News.imageset/Group 6@3x.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Tabs/News.imageset/Group 6@3x.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/News.imageset/Group 6@3x.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/Tabs/News.imageset/Group 8.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/News.imageset/Group 8.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Tabs/News.imageset/Group 8.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/News.imageset/Group 8.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/Tabs/Settings.imageset/Contents.json b/Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/Settings.imageset/Contents.json similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Tabs/Settings.imageset/Contents.json rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/Settings.imageset/Contents.json diff --git a/Sources/SideStore/Resources/Assets.xcassets/Tabs/Settings.imageset/noun_Settings_1187813.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/Settings.imageset/noun_Settings_1187813.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Tabs/Settings.imageset/noun_Settings_1187813.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/Settings.imageset/noun_Settings_1187813.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/Tabs/Settings.imageset/noun_Settings_1187813@2x.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/Settings.imageset/noun_Settings_1187813@2x.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Tabs/Settings.imageset/noun_Settings_1187813@2x.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/Settings.imageset/noun_Settings_1187813@2x.png diff --git a/Sources/SideStore/Resources/Assets.xcassets/Tabs/Settings.imageset/noun_Settings_1187813@3x.png b/Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/Settings.imageset/noun_Settings_1187813@3x.png similarity index 100% rename from Sources/SideStore/Resources/Assets.xcassets/Tabs/Settings.imageset/noun_Settings_1187813@3x.png rename to Sources/SideStore/Resources/Assets/Assets.xcassets/Tabs/Settings.imageset/noun_Settings_1187813@3x.png diff --git a/Sources/SideStore/Resources/Base.lproj/Main.storyboard b/Sources/SideStore/Resources/Base.lproj/Main.storyboard index 817c39fe..f7a2dee8 100644 --- a/Sources/SideStore/Resources/Base.lproj/Main.storyboard +++ b/Sources/SideStore/Resources/Base.lproj/Main.storyboard @@ -1,9 +1,9 @@ - + - + @@ -191,7 +191,7 @@ - +