diff --git a/GradientGames.entitlements b/GradientGames.entitlements index bfa408c..a53058b 100644 --- a/GradientGames.entitlements +++ b/GradientGames.entitlements @@ -4,10 +4,6 @@ aps-environment development - com.apple.developer.associated-domains - - applinks:www.sammcb.com - com.apple.developer.icloud-container-identifiers iCloud.GradientGames diff --git a/GradientGames.xcodeproj/project.pbxproj b/GradientGames.xcodeproj/project.pbxproj index 2b4e430..9dbdf8f 100644 --- a/GradientGames.xcodeproj/project.pbxproj +++ b/GradientGames.xcodeproj/project.pbxproj @@ -30,7 +30,6 @@ 22A4FE932B545D6700882E87 /* Chess.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22A4FE922B545D6700882E87 /* Chess.swift */; }; 22B007E12B6076D500530F93 /* ThemesBackup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22B007E02B6076D500530F93 /* ThemesBackup.swift */; }; 22B007E72B6234FF00530F93 /* ThemeColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22B007E62B6234FF00530F93 /* ThemeColor.swift */; }; - 22BF667E28D3A6C600C13CA3 /* UniversalLinks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22BF667D28D3A6C600C13CA3 /* UniversalLinks.swift */; }; 22C7E53A2E909358004CBC17 /* AppIcon.icon in Resources */ = {isa = PBXBuildFile; fileRef = 22C7E5392E909358004CBC17 /* AppIcon.icon */; }; 22DB9D9227B95101002D8312 /* GradientGamesApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22DB9D9127B95101002D8312 /* GradientGamesApp.swift */; }; 22DB9DA927B9533A002D8312 /* GamesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22DB9DA827B9533A002D8312 /* GamesView.swift */; }; @@ -111,7 +110,6 @@ 22A4FE922B545D6700882E87 /* Chess.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Chess.swift; sourceTree = ""; }; 22B007E02B6076D500530F93 /* ThemesBackup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemesBackup.swift; sourceTree = ""; }; 22B007E62B6234FF00530F93 /* ThemeColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeColor.swift; sourceTree = ""; }; - 22BF667D28D3A6C600C13CA3 /* UniversalLinks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniversalLinks.swift; sourceTree = ""; }; 22C7E5392E909358004CBC17 /* AppIcon.icon */ = {isa = PBXFileReference; lastKnownFileType = folder.iconcomposer.icon; path = AppIcon.icon; sourceTree = ""; }; 22DB9D8E27B95101002D8312 /* Gradient Games.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Gradient Games.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 22DB9D9127B95101002D8312 /* GradientGamesApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradientGamesApp.swift; sourceTree = ""; }; @@ -259,7 +257,6 @@ isa = PBXGroup; children = ( 22DB9D9127B95101002D8312 /* GradientGamesApp.swift */, - 22BF667D28D3A6C600C13CA3 /* UniversalLinks.swift */, 22B007E02B6076D500530F93 /* ThemesBackup.swift */, 22E43CEC2B5E3B4B00CA1F8C /* ColorConverter.swift */, 22DB9DA827B9533A002D8312 /* GamesView.swift */, @@ -485,7 +482,6 @@ files = ( 22454CE32E3D00DA00432208 /* ThemeExportView.swift in Sources */, 22877A0F2B558DA50081DA0B /* ReversiEngine.swift in Sources */, - 22BF667E28D3A6C600C13CA3 /* UniversalLinks.swift in Sources */, 226EE7F427D125FE00C8A473 /* CheckersSquareView.swift in Sources */, 22ED01C92B65F7C800CFE8A5 /* OldThemes.swift in Sources */, 22DB9DBF27B96273002D8312 /* Times.swift in Sources */, @@ -817,7 +813,7 @@ ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; CODE_SIGN_ENTITLEMENTS = GradientGames.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; ENABLE_APP_SANDBOX = YES; ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES; ENABLE_PREVIEWS = YES; @@ -835,7 +831,7 @@ "@executable_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 26.0; - MARKETING_VERSION = 3.0.0; + MARKETING_VERSION = 3.1.0; PRODUCT_BUNDLE_IDENTIFIER = com.sammcb.GradientGames; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"; @@ -858,7 +854,7 @@ ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; CODE_SIGN_ENTITLEMENTS = GradientGames.entitlements; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 2; + CURRENT_PROJECT_VERSION = 3; ENABLE_APP_SANDBOX = YES; ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES; ENABLE_PREVIEWS = YES; @@ -876,7 +872,7 @@ "@executable_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 26.0; - MARKETING_VERSION = 3.0.0; + MARKETING_VERSION = 3.1.0; PRODUCT_BUNDLE_IDENTIFIER = com.sammcb.GradientGames; PRODUCT_NAME = "$(TARGET_NAME)"; SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"; diff --git a/Shared/GamesView.swift b/Shared/GamesView.swift index aa4867d..b30cf98 100644 --- a/Shared/GamesView.swift +++ b/Shared/GamesView.swift @@ -41,7 +41,7 @@ private enum DetailView: String, Identifiable, CaseIterable { } } -struct GamesView: View, UniversalLinkReciever { +struct GamesView: View { @Environment(\.modelContext) private var context @Query(sort: \Theme.index) private var themes: [Theme] @Query private var chessBoards: [ChessBoard] @@ -57,25 +57,6 @@ struct GamesView: View, UniversalLinkReciever { @Query private var oldReversiThemes: [ReversiTheme] @Query private var oldCheckersThemes: [CheckersTheme] - private func parseTheme(_ url: URL) { - guard let theme = try? parseUniversalLink(url) else { - return - } - - let gameThemes = themes.filter({ $0.game == theme.game }) - if let lastThemeIndex = gameThemes.last?.index { - theme.index = lastThemeIndex + 1 - } - - context.insert(theme) - switch theme.game { - case .chess: chessTheme = theme.id.uuidString - case .reversi: reversiTheme = theme.id.uuidString - case .checkers: checkersTheme = theme.id.uuidString - } - selectedView = DetailView(game: theme.game) - } - private func resetGame(for detailView: DetailView) { switch detailView { case .chess: @@ -164,9 +145,6 @@ struct GamesView: View, UniversalLinkReciever { #endif .navigationTitle("Games") .toolbarBackgroundVisibility(.hidden) - .onOpenURL { url in - parseTheme(url) - } // TODO: Delete after most users have updated to 2.0.0 .onChange(of: oldChessThemes.count) { guard !oldChessThemes.isEmpty else { diff --git a/Shared/ThemeView.swift b/Shared/ThemeView.swift index 4b9b49a..d910033 100644 --- a/Shared/ThemeView.swift +++ b/Shared/ThemeView.swift @@ -13,23 +13,8 @@ struct ThemeView: View, ColorConverter { @Environment(\.dismiss) private var dismiss @Bindable var theme: Theme - private var themeURL: URL { - var components = URLComponents() - components.scheme = "https" - components.host = "www.sammcb.com" - components.path = UniversalLink.themePath - var queryItems = [ - URLQueryItem(name: "symbol", value: theme.symbol), - URLQueryItem(name: "game", value: theme.game.rawValue), - ] - for themeColor in theme.colors { - let colorHexString = hexFrom(themeColor.color) - let colorQueryItem = URLQueryItem(name: themeColor.target.rawValue, value: colorHexString) - queryItems.append(colorQueryItem) - } - components.queryItems = queryItems - - return components.url ?? URL(string: "https://www.sammcb.com")! + private var themeData: String { + return ThemesDocument([theme]).toString() } var body: some View { @@ -55,7 +40,7 @@ struct ThemeView: View, ColorConverter { } } ToolbarItem { - ShareLink(item: themeURL) + ShareLink(item: themeData) } } #if os(macOS) diff --git a/Shared/ThemesBackup.swift b/Shared/ThemesBackup.swift index de5352f..4d61493 100644 --- a/Shared/ThemesBackup.swift +++ b/Shared/ThemesBackup.swift @@ -20,7 +20,7 @@ struct ThemesBackup { static var supportedSchemaVersions: [Schema.Version] { [.init(1, 0, 0)] } -// static let currentSchemaVersion = SemanticVersion(1, 0, 0) + static var currentSchemaVersion: Schema.Version { .init(1, 0, 0) } @@ -82,11 +82,24 @@ struct ThemesDocument: FileDocument { } func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper { - let backup = ThemeBackupData(schemaVersion: ThemesBackup.currentSchemaVersion, themes: themes) + let data = try toData(themes) + return FileWrapper(regularFileWithContents: data) + } + + private func toData(_ themeDatas: [ThemeData], outputFormatting: JSONEncoder.OutputFormatting = .prettyPrinted) throws -> Data { + let backup = ThemeBackupData(schemaVersion: ThemesBackup.currentSchemaVersion, themes: themeDatas) let encoder = JSONEncoder() - encoder.outputFormatting = .prettyPrinted + encoder.outputFormatting = outputFormatting let data = try encoder.encode(backup) - return FileWrapper(regularFileWithContents: data) + return data + } + + func toString() -> String { + guard let data = try? toData(themes, outputFormatting: .sortedKeys), let themeDataString = String(data: data, encoding: .utf8) else { + return "Failed to parse theme data." + } + + return themeDataString } } #endif diff --git a/Shared/UniversalLinks.swift b/Shared/UniversalLinks.swift deleted file mode 100644 index 00af28a..0000000 --- a/Shared/UniversalLinks.swift +++ /dev/null @@ -1,77 +0,0 @@ -// -// UniversalLinks.swift -// GradientGames -// -// Created by Sam McBroom on 9/15/22. -// - -import SwiftUI - -enum UniversalLinkParseError: Error { - case pathError, componentsError, gameError, symbolError, countError, colorError -} - -struct UniversalLink { - static let themePath = "/gradientgames/theme" - - private init() {} -} - -protocol UniversalLinkReciever: ColorConverter {} - -extension UniversalLinkReciever { - func parseUniversalLink(_ url: URL) throws -> Theme { - guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { - throw UniversalLinkParseError.componentsError - } - - let themePath = UniversalLink.themePath - guard components.path == themePath else { - throw UniversalLinkParseError.pathError - } - - guard let queryItems = components.queryItems else { - throw UniversalLinkParseError.componentsError - } - - let gameQueryName = "game" - guard let queryGame = queryItems.first(where: { queryItem in queryItem.name == gameQueryName })?.value else { - throw UniversalLinkParseError.gameError - } - - guard let game = Theme.Game(rawValue: queryGame) else { - throw UniversalLinkParseError.gameError - } - - let expectedQueryItemsCount = switch game { - case .chess, .reversi, .checkers: 6 - } - - guard queryItems.count == expectedQueryItemsCount else { - throw UniversalLinkParseError.countError - } - - let symbolQueryName = "symbol" - guard let symbol = queryItems.first(where: { queryItem in queryItem.name == symbolQueryName })?.value else { - throw UniversalLinkParseError.symbolError - } - - let theme = Theme(game: game) - theme.symbol = symbol - - var themeColors: ThemeColors = [] - for themeColor in theme.colors { - guard let colorHex = queryItems.first(where: { queryItem in queryItem.name == themeColor.target.rawValue })?.value else { - throw UniversalLinkParseError.colorError - } - - let color = colorFrom(colorHex) - let parsedThemeColor = ThemeColor(target: themeColor.target, color: color) - themeColors.append(parsedThemeColor) - } - - theme.colors = themeColors - - return theme - } -}