From b36320974db3c2d8fda718331ce80c23a063d054 Mon Sep 17 00:00:00 2001 From: ginsudev Date: Wed, 4 Jan 2023 13:28:42 +1100 Subject: [PATCH 1/6] Generate font map for all font files --- WDBFontOverwrite.xcodeproj/project.pbxproj | 8 +- WDBFontOverwrite/ContentView.ViewModel.swift | 94 +++++++++++--------- WDBFontOverwrite/ContentView.swift | 27 ++++++ 3 files changed, 81 insertions(+), 48 deletions(-) diff --git a/WDBFontOverwrite.xcodeproj/project.pbxproj b/WDBFontOverwrite.xcodeproj/project.pbxproj index 0468fa7..9f399f9 100644 --- a/WDBFontOverwrite.xcodeproj/project.pbxproj +++ b/WDBFontOverwrite.xcodeproj/project.pbxproj @@ -341,7 +341,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"WDBFontOverwrite/Preview Content\""; - DEVELOPMENT_TEAM = WNUZX4NA7T; + DEVELOPMENT_TEAM = Y88XFZJFQK; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = WDBFontOverwrite/Info.plist; @@ -360,7 +360,7 @@ "$(PROJECT_DIR)/WDBFontOverwrite", ); MARKETING_VERSION = 1.9.1; - PRODUCT_BUNDLE_IDENTIFIER = com.ginsudev.WDBFontOverwrite; + PRODUCT_BUNDLE_IDENTIFIER = com.ginsudevc.WDBFontOverwrite; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "WDBFontOverwrite/WDBFontOverwrite-Bridging-Header.h"; @@ -379,7 +379,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"WDBFontOverwrite/Preview Content\""; - DEVELOPMENT_TEAM = WNUZX4NA7T; + DEVELOPMENT_TEAM = Y88XFZJFQK; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = WDBFontOverwrite/Info.plist; @@ -398,7 +398,7 @@ "$(PROJECT_DIR)/WDBFontOverwrite", ); MARKETING_VERSION = 1.9.1; - PRODUCT_BUNDLE_IDENTIFIER = com.ginsudev.WDBFontOverwrite; + PRODUCT_BUNDLE_IDENTIFIER = com.ginsudevc.WDBFontOverwrite; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "WDBFontOverwrite/WDBFontOverwrite-Bridging-Header.h"; diff --git a/WDBFontOverwrite/ContentView.ViewModel.swift b/WDBFontOverwrite/ContentView.ViewModel.swift index f9a80ed..fb9a439 100644 --- a/WDBFontOverwrite/ContentView.ViewModel.swift +++ b/WDBFontOverwrite/ContentView.ViewModel.swift @@ -42,10 +42,6 @@ extension ContentView { @Published var importName: String = "" @Published var importTTCRepackMode: TTCRepackMode = .woff2 - var selectedCustomFont: CustomFont { - return customFonts[customFontPickerSelection] - } - let fonts = [ FontToReplace( name: "DejaVu Sans Condensed", @@ -94,13 +90,7 @@ extension ContentView { ), ] - let customFonts = [ - CustomFont( - name: "SFUI.ttf", - targetPath: .single("/System/Library/Fonts/CoreUI/SFUI.ttf"), - localPath: "CustomSFUI.woff2", - alternativeTTCRepackMode: .ttcpad - ), + let specialCustomFonts = [ CustomFont( name: "Emoji", targetPath: .many([ @@ -108,40 +98,56 @@ extension ContentView { "/System/Library/Fonts/Core/AppleColorEmoji.ttc", ]), localPath: "CustomAppleColorEmoji.woff2" - ), - CustomFont( - name: "SFUISoft.ttc", - targetPath: .single("/System/Library/Fonts/CoreUI/SFUISoft.ttc"), - localPath: "CustomSFUISoft.woff2", - alternativeTTCRepackMode: .ttcpad - ), - CustomFont( - name: "PingFang.ttc", - targetPath: .single("/System/Library/Fonts/LanguageSupport/PingFang.ttc"), - localPath: "CustomPingFang.woff2", - alternativeTTCRepackMode: .ttcpad - ), - CustomFont( - name: "Keycaps.ttc", - targetPath: .single("/System/Library/Fonts/CoreAddition/Keycaps.ttc"), - localPath: "CustomKeycaps.woff2", - alternativeTTCRepackMode: .ttcpad, - notice: .keyboard - ), - CustomFont( - name: "KeycapsPad.ttc", - targetPath: .single("/System/Library/Fonts/CoreAddition/KeycapsPad.ttc"), - localPath: "CustomKeycapsPad.woff2", - alternativeTTCRepackMode: .ttcpad, - notice: .keyboard - ), - CustomFont( - name: "PhoneKeyCaps.ttf", - targetPath: .single("/System/Library/Fonts/CoreAddition/PhoneKeyCaps.ttf"), - localPath: "CustomPhoneKeyCaps.woff2", - alternativeTTCRepackMode: .ttcpad, - notice: .keyboard ) ] + + var customFontMap = [String: CustomFont]() + + func populateFontMap() async { + let fm = FileManager.default + let fontDirPath = "/System/Library/Fonts/" + + do { + let fontSubDirectories = try fm.contentsOfDirectory(atPath: fontDirPath) + for dir in fontSubDirectories { + let fontFiles = try fm.contentsOfDirectory(atPath: "\(fontDirPath)\(dir)") + for font in fontFiles { + guard !font.contains("AppleColorEmoji") else { + continue + } + guard let validatedLocalPath = validateFont(name: font) else { + continue + } + customFontMap[font] = CustomFont( + name: font, + targetPath: .single("\(fontDirPath)\(dir)/\(font)"), + localPath: "Custom\(validatedLocalPath)", + alternativeTTCRepackMode: .ttcpad, + notice: notice(forFont: font) + ) + } + } + } catch { + print(error) + } + + print(customFontMap) + } + + private func validateFont(name: String) -> String? { + var components = name.components(separatedBy: ".") + guard components.last == "ttc" || components.last == "ttf" else { + return nil + } + components[components.count - 1] = "woff2" + return components.joined(separator: ".") + } + + private func notice(forFont font: String) -> Notice? { + if font.lowercased().contains("keycaps") { + return .keyboard + } + return nil + } } } diff --git a/WDBFontOverwrite/ContentView.swift b/WDBFontOverwrite/ContentView.swift index 2cbd1b2..2b3da5d 100644 --- a/WDBFontOverwrite/ContentView.swift +++ b/WDBFontOverwrite/ContentView.swift @@ -34,6 +34,11 @@ struct ContentView: View { viewModel.message = $0 } } + .onAppear { + Task(priority: .background) { + await viewModel.populateFontMap() + } + } } private var segmentControl: some View { @@ -90,6 +95,28 @@ struct ContentView: View { private var customFontsList: some View { Section { NoticeView(notice: .beforeUse) + Picker("Custom fonts", selection: $viewModel.customFontPickerSelection) { + Text("Custom font") + .tag(0) + Text("Custom Emoji") + .tag(1) + } + + switch viewModel.customFontPickerSelection { + case 0: + Button { + <#code#> + } label: { + Text("Import custom fonts") + } + case 1: + Button { + <#code#> + } label: { + Text("Import custom fonts") + } + } + Picker("Custom font", selection: $viewModel.customFontPickerSelection) { ForEach(Array(viewModel.customFonts.enumerated()), id: \.element.name) { index, font in Text(font.name) From 49576170ec2b2dbc3c07d47614b49af25c5533a1 Mon Sep 17 00:00:00 2001 From: ginsudev Date: Wed, 4 Jan 2023 20:49:56 +1100 Subject: [PATCH 2/6] Support batch importing and batch applying fonts. --- WDBFontOverwrite.xcodeproj/project.pbxproj | 26 +++- WDBFontOverwrite/ContentView.ViewModel.swift | 116 ++++++++---------- WDBFontOverwrite/ContentView.swift | 82 +++++-------- .../FileEditor/FileEditorView.ViewModel.swift | 47 +++++++ .../FileEditor/FileEditorView.swift | 65 ++++++++++ WDBFontOverwrite/FontMap.swift | 64 ++++++++++ WDBFontOverwrite/OverwriteFontImpl.swift | 24 ++-- ...stomFontPickerViewControllerDelegate.swift | 72 +++++++---- 8 files changed, 345 insertions(+), 151 deletions(-) create mode 100644 WDBFontOverwrite/FileEditor/FileEditorView.ViewModel.swift create mode 100644 WDBFontOverwrite/FileEditor/FileEditorView.swift create mode 100644 WDBFontOverwrite/FontMap.swift diff --git a/WDBFontOverwrite.xcodeproj/project.pbxproj b/WDBFontOverwrite.xcodeproj/project.pbxproj index 9f399f9..a3b6423 100644 --- a/WDBFontOverwrite.xcodeproj/project.pbxproj +++ b/WDBFontOverwrite.xcodeproj/project.pbxproj @@ -10,6 +10,9 @@ 4F4E64A7295F9AB600D4F04D /* ContentView.ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F4E64A6295F9AB600D4F04D /* ContentView.ViewModel.swift */; }; 4FE5EF312963E460003384EC /* NoticeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE5EF302963E460003384EC /* NoticeView.swift */; }; 4FE5EF3329640075003384EC /* WDBImportCustomFontPickerViewControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE5EF3229640075003384EC /* WDBImportCustomFontPickerViewControllerDelegate.swift */; }; + 4FE5EF3529653188003384EC /* FontMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE5EF3429653188003384EC /* FontMap.swift */; }; + 4FE5EF38296561A5003384EC /* FileEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE5EF37296561A5003384EC /* FileEditorView.swift */; }; + 4FE5EF3A296561B2003384EC /* FileEditorView.ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE5EF39296561B2003384EC /* FileEditorView.ViewModel.swift */; }; C55CF774295BA37D000DE71C /* woff2_wrapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C55CF772295BA37D000DE71C /* woff2_wrapper.cpp */; }; C55CF776295BA9B1000DE71C /* BrotliPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = C55CF775295BA9B1000DE71C /* BrotliPadding.swift */; }; C55CF778295BAF42000DE71C /* libwoff2enc.a in Frameworks */ = {isa = PBXBuildFile; fileRef = C55CF76E295BA346000DE71C /* libwoff2enc.a */; }; @@ -30,6 +33,9 @@ 4F4E64A6295F9AB600D4F04D /* ContentView.ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.ViewModel.swift; sourceTree = ""; }; 4FE5EF302963E460003384EC /* NoticeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeView.swift; sourceTree = ""; }; 4FE5EF3229640075003384EC /* WDBImportCustomFontPickerViewControllerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WDBImportCustomFontPickerViewControllerDelegate.swift; sourceTree = ""; }; + 4FE5EF3429653188003384EC /* FontMap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FontMap.swift; sourceTree = ""; }; + 4FE5EF37296561A5003384EC /* FileEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileEditorView.swift; sourceTree = ""; }; + 4FE5EF39296561B2003384EC /* FileEditorView.ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileEditorView.ViewModel.swift; sourceTree = ""; }; C55CF76E295BA346000DE71C /* libwoff2enc.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libwoff2enc.a; sourceTree = ""; }; C55CF76F295BA346000DE71C /* libwoff2common.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libwoff2common.a; sourceTree = ""; }; C55CF772295BA37D000DE71C /* woff2_wrapper.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = woff2_wrapper.cpp; sourceTree = ""; }; @@ -66,6 +72,15 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 4FE5EF362965617D003384EC /* FileEditor */ = { + isa = PBXGroup; + children = ( + 4FE5EF37296561A5003384EC /* FileEditorView.swift */, + 4FE5EF39296561B2003384EC /* FileEditorView.ViewModel.swift */, + ); + path = FileEditor; + sourceTree = ""; + }; C55CF777295BAF42000DE71C /* Frameworks */ = { isa = PBXGroup; children = ( @@ -96,12 +111,14 @@ C5C9A7A72959351A00466D87 /* Info.plist */, C5A95F45295964AE00C58FDB /* PreviewFonts */, C5C9A7A22959341600466D87 /* RepackedFonts */, - C5C9A7922959261000466D87 /* WDBFontOverwriteApp.swift */, + 4FE5EF362965617D003384EC /* FileEditor */, + C55CF775295BA9B1000DE71C /* BrotliPadding.swift */, C5C9A7942959261000466D87 /* ContentView.swift */, 4F4E64A6295F9AB600D4F04D /* ContentView.ViewModel.swift */, - C5C9A7A02959263A00466D87 /* OverwriteFontImpl.swift */, - C55CF775295BA9B1000DE71C /* BrotliPadding.swift */, + 4FE5EF3429653188003384EC /* FontMap.swift */, 4FE5EF302963E460003384EC /* NoticeView.swift */, + C5C9A7A02959263A00466D87 /* OverwriteFontImpl.swift */, + C5C9A7922959261000466D87 /* WDBFontOverwriteApp.swift */, 4FE5EF3229640075003384EC /* WDBImportCustomFontPickerViewControllerDelegate.swift */, C5C9A7962959261200466D87 /* Assets.xcassets */, C5C9A7A92959417100466D87 /* vm_unaligned_copy_switch_race.c */, @@ -202,10 +219,13 @@ buildActionMask = 2147483647; files = ( C5999BFF295E769700BBBE1F /* stripttc.c in Sources */, + 4FE5EF3A296561B2003384EC /* FileEditorView.ViewModel.swift in Sources */, C5C9A7952959261000466D87 /* ContentView.swift in Sources */, C55CF776295BA9B1000DE71C /* BrotliPadding.swift in Sources */, C5C9A7932959261000466D87 /* WDBFontOverwriteApp.swift in Sources */, + 4FE5EF38296561A5003384EC /* FileEditorView.swift in Sources */, C5C9A7AA2959417100466D87 /* vm_unaligned_copy_switch_race.c in Sources */, + 4FE5EF3529653188003384EC /* FontMap.swift in Sources */, C5999BFC295E69C200BBBE1F /* ttcpad.m in Sources */, 4FE5EF312963E460003384EC /* NoticeView.swift in Sources */, 4FE5EF3329640075003384EC /* WDBImportCustomFontPickerViewControllerDelegate.swift in Sources */, diff --git a/WDBFontOverwrite/ContentView.ViewModel.swift b/WDBFontOverwrite/ContentView.ViewModel.swift index fb9a439..cd11d8b 100644 --- a/WDBFontOverwrite/ContentView.ViewModel.swift +++ b/WDBFontOverwrite/ContentView.ViewModel.swift @@ -18,29 +18,40 @@ enum Notice: String { case keyboard = "Keyboard fonts may not be applied immediately due to iOS caching issues. IF POSSIBLE, remove the folder /var/mobile/Library/Caches/com.apple.keyboards/ if you wish for changes to take effect immediately." } +struct CustomFont { + var name: String + var targetPath: PathType? + var localPath: String + var alternativeTTCRepackMode: TTCRepackMode? + var notice: Notice? +} + +struct FontToReplace { + var name: String + var postScriptName: String + var repackedPath: String +} + +enum CustomFontType: String { + case font = "font" + case emoji = "emoji" +} + extension ContentView { - struct FontToReplace { - var name: String - var postScriptName: String - var repackedPath: String - } - - struct CustomFont { - var name: String - var targetPath: PathType? - var localPath: String - var alternativeTTCRepackMode: TTCRepackMode? - var notice: Notice? - } - final class ViewModel: ObservableObject { @Published var fontListSelection: Int = 0 @Published var customFontPickerSelection: Int = 0 @Published var message = "Choose a font." @Published var progress: Progress! @Published var importPresented: Bool = false - @Published var importName: String = "" + @Published var isPresentedFileEditor: Bool = false @Published var importTTCRepackMode: TTCRepackMode = .woff2 + @Published var allowsMultipleSelection: Bool = true + @Published var importType: CustomFontType = .font + + var selectedCustomFontType: CustomFontType { + return customFontPickerSelection == 0 ? .font : .emoji + } let fonts = [ FontToReplace( @@ -89,65 +100,36 @@ extension ContentView { repackedPath: "Chococooky.woff2" ), ] - - let specialCustomFonts = [ - CustomFont( - name: "Emoji", - targetPath: .many([ - "/System/Library/Fonts/CoreAddition/AppleColorEmoji-160px.ttc", - "/System/Library/Fonts/Core/AppleColorEmoji.ttc", - ]), - localPath: "CustomAppleColorEmoji.woff2" - ) - ] - - var customFontMap = [String: CustomFont]() - func populateFontMap() async { - let fm = FileManager.default - let fontDirPath = "/System/Library/Fonts/" - + func batchOverwriteFonts() async { + let fileManager = FileManager.default + let documentsDirectory = fileManager.urls( + for: .documentDirectory, + in: .userDomainMask + )[0] do { - let fontSubDirectories = try fm.contentsOfDirectory(atPath: fontDirPath) - for dir in fontSubDirectories { - let fontFiles = try fm.contentsOfDirectory(atPath: "\(fontDirPath)\(dir)") - for font in fontFiles { - guard !font.contains("AppleColorEmoji") else { - continue - } - guard let validatedLocalPath = validateFont(name: font) else { - continue - } - customFontMap[font] = CustomFont( - name: font, - targetPath: .single("\(fontDirPath)\(dir)/\(font)"), - localPath: "Custom\(validatedLocalPath)", - alternativeTTCRepackMode: .ttcpad, - notice: notice(forFont: font) + let fonts = try fileManager.contentsOfDirectory(atPath: documentsDirectory.relativePath) + print(fonts) + for font in fonts { + let key = FontMap.key(forFont: font) + if let customFont = FontMap.fontMap[key] { + overwriteWithCustomFont( + name: customFont.localPath, + targetPath: customFont.targetPath, + progress: progress ) } + print("loop done for \(key)") } - } catch { + print("exited loop") + + await MainActor.run { [weak self] in + self?.progress = nil + } + } catch { print(error) + message = "Failed to read imported fonts." } - - print(customFontMap) - } - - private func validateFont(name: String) -> String? { - var components = name.components(separatedBy: ".") - guard components.last == "ttc" || components.last == "ttf" else { - return nil - } - components[components.count - 1] = "woff2" - return components.joined(separator: ".") - } - - private func notice(forFont font: String) -> Notice? { - if font.lowercased().contains("keycaps") { - return .keyboard - } - return nil } } } diff --git a/WDBFontOverwrite/ContentView.swift b/WDBFontOverwrite/ContentView.swift index 2b3da5d..ff7b64d 100644 --- a/WDBFontOverwrite/ContentView.swift +++ b/WDBFontOverwrite/ContentView.swift @@ -16,7 +16,7 @@ struct ContentView: View { NavigationView { Form { progressView - segmentControl + listPickerView if viewModel.fontListSelection == 0 { fontsList } else { @@ -29,19 +29,27 @@ struct ContentView: View { .navigationViewStyle(.stack) .sheet(isPresented: $viewModel.importPresented) { DocumentPicker( - name: viewModel.importName, - ttcRepackMode: viewModel.importTTCRepackMode) { - viewModel.message = $0 - } + importType: viewModel.importType, + ttcRepackMode: viewModel.importTTCRepackMode + ) { + viewModel.message = $0 + } + } + .sheet(isPresented: $viewModel.isPresentedFileEditor) { + FileEditorView() } .onAppear { Task(priority: .background) { - await viewModel.populateFontMap() + do { + try await FontMap.populateFontMap() + } catch { + viewModel.message = "Error: Unable to populate font map." + } } } } - private var segmentControl: some View { + private var listPickerView: some View { Picker("Font choice", selection: $viewModel.fontListSelection) { Text("Preset") .tag(0) @@ -101,75 +109,45 @@ struct ContentView: View { Text("Custom Emoji") .tag(1) } - - switch viewModel.customFontPickerSelection { - case 0: - Button { - <#code#> - } label: { - Text("Import custom fonts") - } - case 1: - Button { - <#code#> - } label: { - Text("Import custom fonts") - } - } - - Picker("Custom font", selection: $viewModel.customFontPickerSelection) { - ForEach(Array(viewModel.customFonts.enumerated()), id: \.element.name) { index, font in - Text(font.name) - .tag(index) - } - } .pickerStyle(.wheel) - } header: { - Text("Custom fonts") - } - - Section { + Button { viewModel.message = "Importing..." - viewModel.importName = viewModel.selectedCustomFont.localPath viewModel.importTTCRepackMode = .woff2 viewModel.importPresented = true } label: { - Text("Import custom \(viewModel.selectedCustomFont.name)") + Text("Import custom \(viewModel.selectedCustomFontType.rawValue)") } - if let alternativeTTCRepackMode = viewModel.selectedCustomFont.alternativeTTCRepackMode { + if viewModel.selectedCustomFontType == .font { Button { viewModel.message = "Importing..." - viewModel.importName = viewModel.selectedCustomFont.localPath - viewModel.importTTCRepackMode = alternativeTTCRepackMode + viewModel.importTTCRepackMode = .ttcpad viewModel.importPresented = true } label: { - Text("Import custom \(viewModel.selectedCustomFont.name) with fix for .ttc") + Text("Import custom \(viewModel.selectedCustomFontType.rawValue) with fix for .ttc") } } Button { viewModel.message = "Running" viewModel.progress = Progress(totalUnitCount: 1) - overwriteWithCustomFont( - name: viewModel.selectedCustomFont.localPath, - targetName: viewModel.selectedCustomFont.targetPath, - progress: viewModel.progress - ) { - viewModel.message = $0 - viewModel.progress = nil + Task { + await viewModel.batchOverwriteFonts() } } label: { - Text("Apply \(viewModel.selectedCustomFont.name)") - } - - if let notice = viewModel.selectedCustomFont.notice { - NoticeView(notice: notice) + Text("Apply \(viewModel.selectedCustomFontType.rawValue)") } + } header: { + Text("Custom fonts") } } private var actionSection: some View { Section { + Button { + viewModel.isPresentedFileEditor = true + } label: { + Text("Manage imported fonts") + } Button { let sharedApplication = UIApplication.shared let windows = sharedApplication.windows diff --git a/WDBFontOverwrite/FileEditor/FileEditorView.ViewModel.swift b/WDBFontOverwrite/FileEditor/FileEditorView.ViewModel.swift new file mode 100644 index 0000000..6a6ef68 --- /dev/null +++ b/WDBFontOverwrite/FileEditor/FileEditorView.ViewModel.swift @@ -0,0 +1,47 @@ +// +// FileEditorView.ViewModel.swift +// WDBFontOverwrite +// +// Created by Noah Little on 4/1/2023. +// + +import Foundation + +extension FileEditorView { + final class ViewModel: ObservableObject { + let fileManager = FileManager.default + @Published var files = [String]() + @Published var isVisibleRemoveAllAlert = false + + func populateFiles() { + do { + let path = documentsDirectory().relativePath + print(path) + files = try fileManager.contentsOfDirectory(atPath: path) + } catch { + print(error) + } + } + + func remove(file: String) { + do { + try fileManager.removeItem(at: documentsDirectory().appendingPathComponent(file)) + } catch { + print(error) + } + } + + func removeAllFiles() { + for file in files { + remove(file: file) + } + } + + private func documentsDirectory() -> URL { + return fileManager.urls( + for: .documentDirectory, + in: .userDomainMask + )[0] + } + } +} diff --git a/WDBFontOverwrite/FileEditor/FileEditorView.swift b/WDBFontOverwrite/FileEditor/FileEditorView.swift new file mode 100644 index 0000000..759c973 --- /dev/null +++ b/WDBFontOverwrite/FileEditor/FileEditorView.swift @@ -0,0 +1,65 @@ +// +// FileEditorView.swift +// WDBFontOverwrite +// +// Created by Noah Little on 4/1/2023. +// + +import SwiftUI + +struct FileEditorView: View { + @StateObject private var viewModel = ViewModel() + + var body: some View { + NavigationView { + List(viewModel.files, id: \.self) { file in + HStack { + Text(file) + Spacer() + Button { + viewModel.remove(file: file) + viewModel.populateFiles() + } label: { + Image(systemName: "trash") + .padding() + .foregroundColor(.white) + .background(Color.red) + .clipShape(Circle()) + } + } + .padding() + } + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button { + viewModel.isVisibleRemoveAllAlert = true + } label: { + Image(systemName: "trash") + .foregroundColor(.red) + } + } + } + .navigationTitle("Imported fonts (\(viewModel.files.count))") + } + .alert(isPresented: $viewModel.isVisibleRemoveAllAlert) { + Alert( + title: Text("Remove all"), + message: Text("Are you sure you want to remove all imported font files?"), + primaryButton: .destructive(Text("Remove all")) { + viewModel.removeAllFiles() + viewModel.populateFiles() + }, + secondaryButton: .cancel() + ) + } + .onAppear { + viewModel.populateFiles() + } + } +} + +struct FileEditorView_Previews: PreviewProvider { + static var previews: some View { + FileEditorView() + } +} diff --git a/WDBFontOverwrite/FontMap.swift b/WDBFontOverwrite/FontMap.swift new file mode 100644 index 0000000..746223d --- /dev/null +++ b/WDBFontOverwrite/FontMap.swift @@ -0,0 +1,64 @@ +// +// FontMap.swift +// WDBFontOverwrite +// +// Created by Noah Little on 4/1/2023. +// + +import Foundation + +struct FontMap { + static var fontMap = [String: CustomFont]() + + static var emojiCustomFont = CustomFont( + name: "Emoji", + targetPath: .many([ + "/System/Library/Fonts/CoreAddition/AppleColorEmoji-160px.ttc", + "/System/Library/Fonts/Core/AppleColorEmoji.ttc", + ]), + localPath: "CustomAppleColorEmoji.woff2" + ) + + static func populateFontMap() async throws { + let fm = FileManager.default + let fontDirPath = "/System/Library/Fonts/" + + let fontSubDirectories = try fm.contentsOfDirectory(atPath: fontDirPath) + for dir in fontSubDirectories { + let fontFiles = try fm.contentsOfDirectory(atPath: "\(fontDirPath)\(dir)") + for font in fontFiles { + guard !font.contains("AppleColorEmoji") else { + continue + } + guard let validatedLocalPath = validateFont(name: font) else { + continue + } + fontMap[key(forFont: font)] = CustomFont( + name: font, + targetPath: .single("\(fontDirPath)\(dir)/\(font)"), + localPath: "Custom\(validatedLocalPath)", + alternativeTTCRepackMode: .ttcpad + ) + } + } + } + + public static func key(forFont font: String) -> String { + var components = font.components(separatedBy: ".") + components.removeLast() + var rejoinedString = components.joined(separator: ".") + if rejoinedString.hasPrefix("Custom") { + rejoinedString = rejoinedString.replacingOccurrences(of: "Custom", with: "") + } + return rejoinedString + } + + private static func validateFont(name: String) -> String? { + var components = name.components(separatedBy: ".") + guard components.last == "ttc" || components.last == "ttf" else { + return nil + } + components[components.count - 1] = "woff2" + return components.joined(separator: ".") + } +} diff --git a/WDBFontOverwrite/OverwriteFontImpl.swift b/WDBFontOverwrite/OverwriteFontImpl.swift index b466777..93c21e1 100644 --- a/WDBFontOverwrite/OverwriteFontImpl.swift +++ b/WDBFontOverwrite/OverwriteFontImpl.swift @@ -26,13 +26,19 @@ func overwriteWithFont( fontURL: URL, pathToTargetFont: String, progress: Progress, - completion: @escaping (String) -> Void + completion: ((String) -> Void)? ) { DispatchQueue.global(qos: .userInteractive).async { let succeeded = overwriteWithFontImpl( - fontURL: fontURL, pathToTargetFont: pathToTargetFont, progress: progress) + fontURL: fontURL, + pathToTargetFont: pathToTargetFont, + progress: progress + ) + + print("done") + DispatchQueue.main.async { - completion(succeeded ? "Success: force close an app to see results" : "Failed") + completion?(succeeded ? "Success: force close an app to see results" : "Failed") } } } @@ -145,9 +151,9 @@ func dumpCurrentFont() { func overwriteWithCustomFont( name: String, - targetName: PathType?, + targetPath: PathType?, progress: Progress, - completion: @escaping (String) -> Void + completion: ((String) -> Void)? = nil ) { let documentDirectory = FileManager.default.urls( for: .documentDirectory, @@ -156,11 +162,13 @@ func overwriteWithCustomFont( let fontURL = documentDirectory.appendingPathComponent(name) guard FileManager.default.fileExists(atPath: fontURL.path) else { - completion("No custom font imported") + completion?("No custom font imported") return } + + print("b4 switch") - switch targetName { + switch targetPath { case .single(let path): overwriteWithFont( fontURL: fontURL, @@ -180,7 +188,7 @@ func overwriteWithCustomFont( } } default: - completion("Either targetName or targetNames must be provided") + completion?("Either targetName or targetNames must be provided") } } diff --git a/WDBFontOverwrite/WDBImportCustomFontPickerViewControllerDelegate.swift b/WDBFontOverwrite/WDBImportCustomFontPickerViewControllerDelegate.swift index 1c62354..259e629 100644 --- a/WDBFontOverwrite/WDBImportCustomFontPickerViewControllerDelegate.swift +++ b/WDBFontOverwrite/WDBImportCustomFontPickerViewControllerDelegate.swift @@ -1,37 +1,57 @@ +// +// ContentView.ViewModel.swift +// WDBFontOverwrite +// +// Created by Noah Little (@ginsudev) on 3/1/2023. +// + import SwiftUI import UniformTypeIdentifiers class WDBImportCustomFontPickerViewControllerDelegate: NSObject, UIDocumentPickerDelegate { - let name: String + let importType: CustomFontType let ttcRepackMode: TTCRepackMode let completion: (String) -> Void - init(name: String, ttcRepackMode: TTCRepackMode, completion: @escaping (String) -> Void) { - self.name = name + init(importType: CustomFontType, ttcRepackMode: TTCRepackMode, completion: @escaping (String) -> Void) { + self.importType = importType self.ttcRepackMode = ttcRepackMode self.completion = completion } func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { - guard urls.count == 1 else { - completion("import one file at a time") - return - } - - Task(priority: .background) { - let fileURL = urls[0] + Task { let documentDirectory = FileManager.default.urls( for: .documentDirectory, in: .userDomainMask )[0] - let targetURL = documentDirectory.appendingPathComponent(self.name) - let success = importCustomFontImpl( - fileURL: fileURL, - targetURL: targetURL, - ttcRepackMode: self.ttcRepackMode - ) + + var successfullyImportedCount = 0 + + // Import selected font files into the documents directory, one by one. + for url in urls { + if importType == .emoji { + let emojiFont = FontMap.emojiCustomFont + let targetURL = documentDirectory.appendingPathComponent(emojiFont.localPath) + successfullyImportedCount += importFont(withFileURL: url, targetURL: targetURL) + } else { + let key = FontMap.key(forFont: url.lastPathComponent) + if let customFont = FontMap.fontMap[key] { + let targetURL = documentDirectory.appendingPathComponent(customFont.localPath) + successfullyImportedCount += importFont(withFileURL: url, targetURL: targetURL) + } + } + } + await MainActor.run { [weak self] in - self?.completion(success ?? "Imported") + self?.completion( + String( + format: "Successfully imported %d/%d files.%@", + successfullyImportedCount, + urls.count, + successfullyImportedCount == urls.count ? "" : " Some files were skipped because your device doesn't have those fonts or because they don't support your iOS/device." + ) + ) } } } @@ -39,25 +59,34 @@ class WDBImportCustomFontPickerViewControllerDelegate: NSObject, UIDocumentPicke func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) { completion("Cancelled") } + + private func importFont(withFileURL fileURL: URL, targetURL: URL) -> Int { + if importCustomFontImpl( + fileURL: fileURL, + targetURL: targetURL, + ttcRepackMode: self.ttcRepackMode + ) == nil { + return 1 + } + return 0 + } } // https://capps.tech/blog/read-files-with-documentpicker-in-swiftui struct DocumentPicker: UIViewControllerRepresentable { - var name: String + var importType: CustomFontType var ttcRepackMode: TTCRepackMode var completion: (String) -> Void func makeCoordinator() -> WDBImportCustomFontPickerViewControllerDelegate { return WDBImportCustomFontPickerViewControllerDelegate( - name: name, + importType: importType, ttcRepackMode: ttcRepackMode, completion: completion ) } func makeUIViewController(context: UIViewControllerRepresentableContext) -> UIDocumentPickerViewController { - print("make ui view controller?") - let pickerViewController = UIDocumentPickerViewController( forOpeningContentTypes: [ UTType.font, @@ -69,6 +98,7 @@ struct DocumentPicker: UIViewControllerRepresentable { asCopy: true ) + pickerViewController.allowsMultipleSelection = importType == .font pickerViewController.delegate = context.coordinator return pickerViewController } From 83dbe5a2b1f105a118d7b4c299b2aeaba5413e0e Mon Sep 17 00:00:00 2001 From: ginsudev Date: Wed, 4 Jan 2023 21:32:03 +1100 Subject: [PATCH 3/6] Do importing asynchronously --- WDBFontOverwrite/ContentView.ViewModel.swift | 11 +-- WDBFontOverwrite/OverwriteFontImpl.swift | 4 +- ...stomFontPickerViewControllerDelegate.swift | 80 ++++++++++--------- 3 files changed, 49 insertions(+), 46 deletions(-) diff --git a/WDBFontOverwrite/ContentView.ViewModel.swift b/WDBFontOverwrite/ContentView.ViewModel.swift index cd11d8b..bb2306f 100644 --- a/WDBFontOverwrite/ContentView.ViewModel.swift +++ b/WDBFontOverwrite/ContentView.ViewModel.swift @@ -117,14 +117,11 @@ extension ContentView { name: customFont.localPath, targetPath: customFont.targetPath, progress: progress - ) + ) { + self.progress = nil + self.message = $0 + } } - print("loop done for \(key)") - } - print("exited loop") - - await MainActor.run { [weak self] in - self?.progress = nil } } catch { print(error) diff --git a/WDBFontOverwrite/OverwriteFontImpl.swift b/WDBFontOverwrite/OverwriteFontImpl.swift index 93c21e1..2b9ca94 100644 --- a/WDBFontOverwrite/OverwriteFontImpl.swift +++ b/WDBFontOverwrite/OverwriteFontImpl.swift @@ -198,9 +198,7 @@ enum TTCRepackMode { case firstFontOnly } -func importCustomFontImpl(fileURL: URL, targetURL: URL, ttcRepackMode: TTCRepackMode = .woff2) --> String? -{ +func importCustomFontImpl(fileURL: URL, targetURL: URL, ttcRepackMode: TTCRepackMode = .woff2) async -> String? { // read first 16k of font let fileHandle = try! FileHandle(forReadingFrom: fileURL) defer { fileHandle.closeFile() } diff --git a/WDBFontOverwrite/WDBImportCustomFontPickerViewControllerDelegate.swift b/WDBFontOverwrite/WDBImportCustomFontPickerViewControllerDelegate.swift index 259e629..82edb97 100644 --- a/WDBFontOverwrite/WDBImportCustomFontPickerViewControllerDelegate.swift +++ b/WDBFontOverwrite/WDBImportCustomFontPickerViewControllerDelegate.swift @@ -21,38 +21,7 @@ class WDBImportCustomFontPickerViewControllerDelegate: NSObject, UIDocumentPicke func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) { Task { - let documentDirectory = FileManager.default.urls( - for: .documentDirectory, - in: .userDomainMask - )[0] - - var successfullyImportedCount = 0 - - // Import selected font files into the documents directory, one by one. - for url in urls { - if importType == .emoji { - let emojiFont = FontMap.emojiCustomFont - let targetURL = documentDirectory.appendingPathComponent(emojiFont.localPath) - successfullyImportedCount += importFont(withFileURL: url, targetURL: targetURL) - } else { - let key = FontMap.key(forFont: url.lastPathComponent) - if let customFont = FontMap.fontMap[key] { - let targetURL = documentDirectory.appendingPathComponent(customFont.localPath) - successfullyImportedCount += importFont(withFileURL: url, targetURL: targetURL) - } - } - } - - await MainActor.run { [weak self] in - self?.completion( - String( - format: "Successfully imported %d/%d files.%@", - successfullyImportedCount, - urls.count, - successfullyImportedCount == urls.count ? "" : " Some files were skipped because your device doesn't have those fonts or because they don't support your iOS/device." - ) - ) - } + await importSelectedFonts(atURLs: urls) } } @@ -60,15 +29,54 @@ class WDBImportCustomFontPickerViewControllerDelegate: NSObject, UIDocumentPicke completion("Cancelled") } - private func importFont(withFileURL fileURL: URL, targetURL: URL) -> Int { - if importCustomFontImpl( + private func importSelectedFonts(atURLs urls: [URL]) async { + let documentDirectory = FileManager.default.urls( + for: .documentDirectory, + in: .userDomainMask + )[0] + + var successfullyImportedCount = 0 + + // Import selected font files into the documents directory, one by one. + for url in urls { + if importType == .emoji { + let emojiFont = FontMap.emojiCustomFont + let targetURL = documentDirectory.appendingPathComponent(emojiFont.localPath) + let success = await importFont(withFileURL: url, targetURL: targetURL) + successfullyImportedCount += success + } else { + let key = FontMap.key(forFont: url.lastPathComponent) + if let customFont = FontMap.fontMap[key] { + let targetURL = documentDirectory.appendingPathComponent(customFont.localPath) + let success = await importFont(withFileURL: url, targetURL: targetURL) + successfullyImportedCount += success + } + } + } + + await MainActor.run { [weak self] in + self?.completion( + String( + format: "Successfully imported %d/%d files.%@", + successfullyImportedCount, + urls.count, + successfullyImportedCount == urls.count ? "" : " Some files were skipped because your device doesn't have those fonts or because they don't support your iOS/device." + ) + ) + } + } + + private func importFont(withFileURL fileURL: URL, targetURL: URL) async -> Int { + let success = await importCustomFontImpl( fileURL: fileURL, targetURL: targetURL, ttcRepackMode: self.ttcRepackMode - ) == nil { + ) + if success == nil { return 1 + } else { + return 0 } - return 0 } } From 172643fe360ec456885aaee8b44efbe31a38dbc0 Mon Sep 17 00:00:00 2001 From: ginsudev Date: Wed, 4 Jan 2023 21:48:57 +1100 Subject: [PATCH 4/6] Button icons --- WDBFontOverwrite/ContentView.swift | 45 ++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/WDBFontOverwrite/ContentView.swift b/WDBFontOverwrite/ContentView.swift index ff7b64d..8a7c871 100644 --- a/WDBFontOverwrite/ContentView.swift +++ b/WDBFontOverwrite/ContentView.swift @@ -116,7 +116,14 @@ struct ContentView: View { viewModel.importTTCRepackMode = .woff2 viewModel.importPresented = true } label: { - Text("Import custom \(viewModel.selectedCustomFontType.rawValue)") + HStack { + Image(systemName: "square.and.arrow.down") + .font(.system(size: 20)) + .frame(width: 32, height: 32) + .alignmentGuide(.leading) { d in d[HorizontalAlignment.center] } + Text("Import custom \(viewModel.selectedCustomFontType.rawValue)") + .alignmentGuide(.leading) { d in d[HorizontalAlignment.leading] } + } } if viewModel.selectedCustomFontType == .font { Button { @@ -124,7 +131,14 @@ struct ContentView: View { viewModel.importTTCRepackMode = .ttcpad viewModel.importPresented = true } label: { - Text("Import custom \(viewModel.selectedCustomFontType.rawValue) with fix for .ttc") + HStack { + Image(systemName: "square.and.arrow.down") + .font(.system(size: 20)) + .frame(width: 32, height: 32) + .alignmentGuide(.leading) { d in d[HorizontalAlignment.center] } + Text("Import custom \(viewModel.selectedCustomFontType.rawValue) with fix for .ttc") + .alignmentGuide(.leading) { d in d[HorizontalAlignment.leading] } + } } } Button { @@ -134,7 +148,14 @@ struct ContentView: View { await viewModel.batchOverwriteFonts() } } label: { - Text("Apply \(viewModel.selectedCustomFontType.rawValue)") + HStack { + Image(systemName: "checkmark.circle") + .font(.system(size: 20)) + .frame(width: 32, height: 32) + .alignmentGuide(.leading) { d in d[HorizontalAlignment.center] } + Text("Apply \(viewModel.selectedCustomFontType.rawValue)") + .alignmentGuide(.leading) { d in d[HorizontalAlignment.leading] } + } } } header: { Text("Custom fonts") @@ -146,7 +167,14 @@ struct ContentView: View { Button { viewModel.isPresentedFileEditor = true } label: { - Text("Manage imported fonts") + HStack { + Image(systemName: "doc.badge.gearshape") + .font(.system(size: 20)) + .frame(width: 32, height: 32) + .alignmentGuide(.leading) { d in d[HorizontalAlignment.center] } + Text("Manage imported fonts") + .alignmentGuide(.leading) { d in d[HorizontalAlignment.leading] } + } } Button { let sharedApplication = UIApplication.shared @@ -157,7 +185,14 @@ struct ContentView: View { } } } label: { - Text("Restart SpringBoard") + HStack { + Image(systemName: "arrow.triangle.2.circlepath") + .font(.system(size: 20)) + .frame(width: 32, height: 32) + .alignmentGuide(.leading) { d in d[HorizontalAlignment.center] } + Text("Restart SpringBoard") + .alignmentGuide(.leading) { d in d[HorizontalAlignment.leading] } + } } } header: { Text("Actions") From 302b032c0a4925e4e466055e7f6d33eb47fdbdd4 Mon Sep 17 00:00:00 2001 From: ginsudev Date: Wed, 4 Jan 2023 22:06:04 +1100 Subject: [PATCH 5/6] Fix importing emojis not working. --- WDBFontOverwrite/ContentView.ViewModel.swift | 17 +++++++++++++++-- WDBFontOverwrite/ContentView.swift | 2 +- WDBFontOverwrite/FontMap.swift | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/WDBFontOverwrite/ContentView.ViewModel.swift b/WDBFontOverwrite/ContentView.ViewModel.swift index bb2306f..81faf0a 100644 --- a/WDBFontOverwrite/ContentView.ViewModel.swift +++ b/WDBFontOverwrite/ContentView.ViewModel.swift @@ -102,14 +102,27 @@ extension ContentView { ] func batchOverwriteFonts() async { + guard selectedCustomFontType == .font else { + // Overwrite emoji + let emojiFont = FontMap.emojiCustomFont + overwriteWithCustomFont( + name: emojiFont.localPath, + targetPath: emojiFont.targetPath, + progress: progress + ) { + self.progress = nil + self.message = $0 + } + return + } + let fileManager = FileManager.default let documentsDirectory = fileManager.urls( for: .documentDirectory, in: .userDomainMask )[0] do { - let fonts = try fileManager.contentsOfDirectory(atPath: documentsDirectory.relativePath) - print(fonts) + let fonts = try fileManager.contentsOfDirectory(atPath: documentsDirectory.relativePath).filter({!$0.contains("AppleColorEmoji")}) for font in fonts { let key = FontMap.key(forFont: font) if let customFont = FontMap.fontMap[key] { diff --git a/WDBFontOverwrite/ContentView.swift b/WDBFontOverwrite/ContentView.swift index 8a7c871..53c0a5e 100644 --- a/WDBFontOverwrite/ContentView.swift +++ b/WDBFontOverwrite/ContentView.swift @@ -29,7 +29,7 @@ struct ContentView: View { .navigationViewStyle(.stack) .sheet(isPresented: $viewModel.importPresented) { DocumentPicker( - importType: viewModel.importType, + importType: viewModel.selectedCustomFontType, ttcRepackMode: viewModel.importTTCRepackMode ) { viewModel.message = $0 diff --git a/WDBFontOverwrite/FontMap.swift b/WDBFontOverwrite/FontMap.swift index 746223d..a88c199 100644 --- a/WDBFontOverwrite/FontMap.swift +++ b/WDBFontOverwrite/FontMap.swift @@ -10,7 +10,7 @@ import Foundation struct FontMap { static var fontMap = [String: CustomFont]() - static var emojiCustomFont = CustomFont( + static let emojiCustomFont = CustomFont( name: "Emoji", targetPath: .many([ "/System/Library/Fonts/CoreAddition/AppleColorEmoji-160px.ttc", From 07006762144a7f9d4a072d313f3a4e05d8ecb884 Mon Sep 17 00:00:00 2001 From: ginsudev Date: Wed, 4 Jan 2023 22:37:56 +1100 Subject: [PATCH 6/6] Version bump --- WDBFontOverwrite.xcodeproj/project.pbxproj | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/WDBFontOverwrite.xcodeproj/project.pbxproj b/WDBFontOverwrite.xcodeproj/project.pbxproj index a3b6423..48c4322 100644 --- a/WDBFontOverwrite.xcodeproj/project.pbxproj +++ b/WDBFontOverwrite.xcodeproj/project.pbxproj @@ -361,7 +361,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"WDBFontOverwrite/Preview Content\""; - DEVELOPMENT_TEAM = Y88XFZJFQK; + DEVELOPMENT_TEAM = WNUZX4NA7T; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = WDBFontOverwrite/Info.plist; @@ -379,8 +379,8 @@ "$(inherited)", "$(PROJECT_DIR)/WDBFontOverwrite", ); - MARKETING_VERSION = 1.9.1; - PRODUCT_BUNDLE_IDENTIFIER = com.ginsudevc.WDBFontOverwrite; + MARKETING_VERSION = 1.10.0; + PRODUCT_BUNDLE_IDENTIFIER = com.ginsudev.WDBFontOverwrite; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "WDBFontOverwrite/WDBFontOverwrite-Bridging-Header.h"; @@ -399,7 +399,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"WDBFontOverwrite/Preview Content\""; - DEVELOPMENT_TEAM = Y88XFZJFQK; + DEVELOPMENT_TEAM = WNUZX4NA7T; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = WDBFontOverwrite/Info.plist; @@ -417,8 +417,8 @@ "$(inherited)", "$(PROJECT_DIR)/WDBFontOverwrite", ); - MARKETING_VERSION = 1.9.1; - PRODUCT_BUNDLE_IDENTIFIER = com.ginsudevc.WDBFontOverwrite; + MARKETING_VERSION = 1.10.0; + PRODUCT_BUNDLE_IDENTIFIER = com.ginsudev.WDBFontOverwrite; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OBJC_BRIDGING_HEADER = "WDBFontOverwrite/WDBFontOverwrite-Bridging-Header.h";