basic poc app
This commit is contained in:
1 parent
4a2d58f189
commit
59670cb841
6 files changed
+315
-28
No files matched your search
@@ -6,6 +6,10 @@
|
|||||||
objectVersion = 77;
|
objectVersion = 77;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
6F09B7BB2DEB873F00CDE89C /* ZIPFoundation in Frameworks */ = {isa = PBXBuildFile; productRef = 6F09B7BA2DEB873F00CDE89C /* ZIPFoundation */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
6F09B79D2DEB694B00CDE89C /* Pocket Poster.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Pocket Poster.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
6F09B79D2DEB694B00CDE89C /* Pocket Poster.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Pocket Poster.app"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
@@ -23,6 +27,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
6F09B7BB2DEB873F00CDE89C /* ZIPFoundation in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -65,6 +70,7 @@
|
|||||||
);
|
);
|
||||||
name = "Pocket Poster";
|
name = "Pocket Poster";
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
|
6F09B7BA2DEB873F00CDE89C /* ZIPFoundation */,
|
||||||
);
|
);
|
||||||
productName = "Pocket Poster";
|
productName = "Pocket Poster";
|
||||||
productReference = 6F09B79D2DEB694B00CDE89C /* Pocket Poster.app */;
|
productReference = 6F09B79D2DEB694B00CDE89C /* Pocket Poster.app */;
|
||||||
@@ -94,6 +100,9 @@
|
|||||||
);
|
);
|
||||||
mainGroup = 6F09B7942DEB694B00CDE89C;
|
mainGroup = 6F09B7942DEB694B00CDE89C;
|
||||||
minimizedProjectReferenceProxies = 1;
|
minimizedProjectReferenceProxies = 1;
|
||||||
|
packageReferences = (
|
||||||
|
6F09B7B92DEB873F00CDE89C /* XCRemoteSwiftPackageReference "ZIPFoundation" */,
|
||||||
|
);
|
||||||
preferredProjectObjectVersion = 77;
|
preferredProjectObjectVersion = 77;
|
||||||
productRefGroup = 6F09B79E2DEB694B00CDE89C /* Products */;
|
productRefGroup = 6F09B79E2DEB694B00CDE89C /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
@@ -177,6 +186,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
|
||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
@@ -234,6 +244,7 @@
|
|||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
INFOPLIST_KEY_LSSupportsOpeningDocumentsInPlace = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
|
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
|
||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
@@ -252,13 +263,16 @@
|
|||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"Pocket Poster/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"Pocket Poster/Preview Content\"";
|
||||||
|
DEVELOPMENT_TEAM = 2P4CP47H76;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = "Pocket-Poster-Info.plist";
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait";
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
@@ -280,13 +294,16 @@
|
|||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"Pocket Poster/Preview Content\"";
|
DEVELOPMENT_ASSET_PATHS = "\"Pocket Poster/Preview Content\"";
|
||||||
|
DEVELOPMENT_TEAM = 2P4CP47H76;
|
||||||
ENABLE_PREVIEWS = YES;
|
ENABLE_PREVIEWS = YES;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
|
INFOPLIST_FILE = "Pocket-Poster-Info.plist";
|
||||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait";
|
||||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
@@ -322,6 +339,25 @@
|
|||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
|
6F09B7B92DEB873F00CDE89C /* XCRemoteSwiftPackageReference "ZIPFoundation" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/weichsel/ZIPFoundation";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 0.9.19;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
6F09B7BA2DEB873F00CDE89C /* ZIPFoundation */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = 6F09B7B92DEB873F00CDE89C /* XCRemoteSwiftPackageReference "ZIPFoundation" */;
|
||||||
|
productName = ZIPFoundation;
|
||||||
|
};
|
||||||
|
/* End XCSwiftPackageProductDependency section */
|
||||||
};
|
};
|
||||||
rootObject = 6F09B7952DEB694B00CDE89C /* Project object */;
|
rootObject = 6F09B7952DEB694B00CDE89C /* Project object */;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"originHash" : "d0d4edfdf2bf3cd05b3ba2dec0af1a9c271c93f944cbba8677cc647f74a6b323",
|
||||||
|
"pins" : [
|
||||||
|
{
|
||||||
|
"identity" : "zipfoundation",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/weichsel/ZIPFoundation",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "02b6abe5f6eef7e3cbd5f247c5cc24e246efcfe0",
|
||||||
|
"version" : "0.9.19"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version" : 3
|
||||||
|
}
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
//
|
|
||||||
// ContentView.swift
|
|
||||||
// Pocket Poster
|
|
||||||
//
|
|
||||||
// Created by lemin on 5/31/25.
|
|
||||||
//
|
|
||||||
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct ContentView: View {
|
|
||||||
var body: some View {
|
|
||||||
VStack {
|
|
||||||
Image(systemName: "globe")
|
|
||||||
.imageScale(.large)
|
|
||||||
.foregroundStyle(.tint)
|
|
||||||
Text("Hello, world!")
|
|
||||||
}
|
|
||||||
.padding()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#Preview {
|
|
||||||
ContentView()
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
//
|
||||||
|
// PosterBoardManager.swift
|
||||||
|
// Pocket Poster
|
||||||
|
//
|
||||||
|
// Created by lemin on 5/31/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import ZIPFoundation
|
||||||
|
|
||||||
|
class PosterBoardManager {
|
||||||
|
private static func unzipFile(at url: URL) throws -> URL {
|
||||||
|
let fileName = url.deletingPathExtension().lastPathComponent
|
||||||
|
let fileData = try Data(contentsOf: url)
|
||||||
|
let fileManager = FileManager()
|
||||||
|
|
||||||
|
// Write the file to the Documents Directory
|
||||||
|
let docDir = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
|
||||||
|
let path = docDir[0]
|
||||||
|
let url = path.appending(path: fileName)
|
||||||
|
|
||||||
|
// Remove All files in this directory
|
||||||
|
let existingFiles = try FileManager.default.contentsOfDirectory(at: path, includingPropertiesForKeys: nil, options: .skipsHiddenFiles)
|
||||||
|
for fileUrl in existingFiles
|
||||||
|
{
|
||||||
|
try FileManager.default.removeItem(at: fileUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save our Zip file
|
||||||
|
try fileData.write(to: url, options: [.atomic])
|
||||||
|
|
||||||
|
// Unzip the Zipped Up File
|
||||||
|
var destinationURL = path
|
||||||
|
if FileManager.default.fileExists(atPath: url.path())
|
||||||
|
{
|
||||||
|
destinationURL.append(path: "directory")
|
||||||
|
try fileManager.unzipItem(at: url, to: destinationURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
return destinationURL
|
||||||
|
}
|
||||||
|
|
||||||
|
static func getDescriptorsFromTendie(_ url: URL) throws -> URL? {
|
||||||
|
let unzippedDir = try unzipFile(at: url)
|
||||||
|
|
||||||
|
for dir in try FileManager.default.contentsOfDirectory(at: unzippedDir, includingPropertiesForKeys: nil, options: .skipsHiddenFiles) {
|
||||||
|
let fileName = dir.lastPathComponent
|
||||||
|
if fileName.lowercased() == "descriptor" || fileName.lowercased() == "descriptors" {
|
||||||
|
return dir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
static func randomizeWallpaperId(url: URL) throws {
|
||||||
|
let randomizedID = Int.random(in: 9999...99999)
|
||||||
|
var files = [URL]()
|
||||||
|
if let enumerator = FileManager.default.enumerator(at: url, includingPropertiesForKeys: [.isRegularFileKey], options: [.skipsHiddenFiles, .skipsPackageDescendants]) {
|
||||||
|
for case let fileURL as URL in enumerator
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
let fileAttributes = try fileURL.resourceValues(forKeys:[.isRegularFileKey])
|
||||||
|
if fileAttributes.isRegularFile!
|
||||||
|
{
|
||||||
|
files.append(fileURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
print(error, fileURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setPlistValue(file: String, key: String, value: Any, recursive: Bool = true) {
|
||||||
|
// thanks gpt
|
||||||
|
guard let plistData = FileManager.default.contents(atPath: file),
|
||||||
|
var plist = try? PropertyListSerialization.propertyList(from: plistData, options: [], format: nil) as? [String: Any] else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
plist[key] = value
|
||||||
|
|
||||||
|
guard let updatedData = try? PropertyListSerialization.data(fromPropertyList: plist, format: .xml, options: 0) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
try updatedData.write(to: URL(fileURLWithPath: file))
|
||||||
|
} catch {
|
||||||
|
print("Failed to write updated plist: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for file in files {
|
||||||
|
switch file.lastPathComponent {
|
||||||
|
case "com.apple.posterkit.provider.descriptor.identifier":
|
||||||
|
try String(randomizedID).data(using: .utf8)?.write(to: file)
|
||||||
|
|
||||||
|
case "com.apple.posterkit.provider.contents.userInfo":
|
||||||
|
setPlistValue(file: file.path(), key: "wallpaperRepresentingIdentifier", value: randomizedID)
|
||||||
|
|
||||||
|
case "Wallpaper.plist":
|
||||||
|
setPlistValue(file: file.path(), key: "identifier", value: randomizedID, recursive: false)
|
||||||
|
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func applyTendie(_ url: URL, appHash: String) throws {
|
||||||
|
// scope the resource
|
||||||
|
let accessing = url.startAccessingSecurityScopedResource()
|
||||||
|
defer {
|
||||||
|
if accessing {
|
||||||
|
url.stopAccessingSecurityScopedResource()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let descriptors = try getDescriptorsFromTendie(url) else { return } // TODO: Add error handling
|
||||||
|
// create the folder
|
||||||
|
for descr in try FileManager.default.contentsOfDirectory(at: descriptors, includingPropertiesForKeys: nil, options: .skipsHiddenFiles) {
|
||||||
|
if descr.lastPathComponent != "__MACOSX" {
|
||||||
|
try randomizeWallpaperId(url: descr)
|
||||||
|
let newURL = SymHandler.getDocumentsDirectory().appendingPathComponent(UUID().uuidString, conformingTo: .directory)
|
||||||
|
try FileManager.default.moveItem(at: descr, to: newURL)
|
||||||
|
|
||||||
|
let _ = try SymHandler.createDescriptorsSymlink(appHash: appHash)
|
||||||
|
do {
|
||||||
|
try FileManager.default.trashItem(at: newURL, resultingItemURL: nil)
|
||||||
|
} catch {
|
||||||
|
print(error.localizedDescription)
|
||||||
|
}
|
||||||
|
SymHandler.cleanup()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try? FileManager.default.removeItem(at: descriptors)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
//
|
||||||
|
// SymHandler.swift
|
||||||
|
// Pocket Poster
|
||||||
|
//
|
||||||
|
// Created by lemin on 5/31/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class SymHandler {
|
||||||
|
// MARK: URL Getter Operations
|
||||||
|
static func getDocumentsDirectory() -> URL {
|
||||||
|
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
|
||||||
|
let documentsDirectory = paths[0]
|
||||||
|
return documentsDirectory
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func getSymlinkURL() -> URL {
|
||||||
|
return getDocumentsDirectory().appendingPathComponent(".Trash", conformingTo: .symbolicLink)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Symlink Creation
|
||||||
|
static func createSymlink(to path: String) throws -> URL {
|
||||||
|
// returns the url of the symlink
|
||||||
|
let symURL = getSymlinkURL()
|
||||||
|
cleanup()
|
||||||
|
|
||||||
|
// create the symlink to the hashed app folder
|
||||||
|
try FileManager.default.createSymbolicLink(at: symURL, withDestinationURL: URL(fileURLWithPath: path, isDirectory: true))
|
||||||
|
|
||||||
|
return symURL
|
||||||
|
}
|
||||||
|
|
||||||
|
static func createAppSymlink(for appHash: String) throws -> URL {
|
||||||
|
return try createSymlink(to: "/var/mobile/Containers/Data/Application/\(appHash)")
|
||||||
|
}
|
||||||
|
|
||||||
|
static func createDescriptorsSymlink(appHash: String) throws -> URL {
|
||||||
|
// create a symlink directly to the descriptors
|
||||||
|
return try createAppSymlink(for: "\(appHash)/Library/Application Support/PRBPosterExtensionDataStore/61/Extensions/com.apple.WallpaperKit.CollectionsPoster/descriptors")
|
||||||
|
}
|
||||||
|
|
||||||
|
static func cleanup() {
|
||||||
|
// remove the symlink if it exists
|
||||||
|
let symURL = getSymlinkURL()
|
||||||
|
// remove existing symlink
|
||||||
|
try? FileManager.default.removeItem(at: symURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
//
|
||||||
|
// ContentView.swift
|
||||||
|
// Pocket Poster
|
||||||
|
//
|
||||||
|
// Created by lemin on 5/31/25.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import UniformTypeIdentifiers
|
||||||
|
|
||||||
|
struct ContentView: View {
|
||||||
|
// Prefs
|
||||||
|
@AppStorage("pbHash") var pbHash: String = ""
|
||||||
|
|
||||||
|
@State var showTendiesImporter: Bool = false
|
||||||
|
@State var selectedTendie: URL? = nil
|
||||||
|
|
||||||
|
@State var showErrorAlert = false
|
||||||
|
@State var lastError: String?
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
TextField("App Hash", text: $pbHash)
|
||||||
|
|
||||||
|
Button(action: {
|
||||||
|
showTendiesImporter.toggle()
|
||||||
|
}) {
|
||||||
|
Text("Select Tendie")
|
||||||
|
}
|
||||||
|
|
||||||
|
if let selectedTendie = selectedTendie {
|
||||||
|
Text("Selected Tendie: \(selectedTendie)")
|
||||||
|
if pbHash != "" {
|
||||||
|
Button(action: {
|
||||||
|
do {
|
||||||
|
try PosterBoardManager.applyTendie(selectedTendie, appHash: pbHash)
|
||||||
|
lastError = "Success. Delete in files app."
|
||||||
|
showErrorAlert.toggle()
|
||||||
|
} catch {
|
||||||
|
lastError = error.localizedDescription
|
||||||
|
showErrorAlert.toggle()
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
Text("Apply")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
.fileImporter(isPresented: $showTendiesImporter, allowedContentTypes: [UTType(filenameExtension: "tendies", conformingTo: .data)!], onCompletion: { result in
|
||||||
|
switch result {
|
||||||
|
case .success(let url):
|
||||||
|
selectedTendie = url
|
||||||
|
case .failure(let error):
|
||||||
|
lastError = error.localizedDescription
|
||||||
|
showErrorAlert.toggle()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.alert("Error", isPresented: $showErrorAlert) {
|
||||||
|
Button("OK") {}
|
||||||
|
} message: {
|
||||||
|
Text(lastError ?? "???")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Preview {
|
||||||
|
ContentView()
|
||||||
|
}
|
||||||
Reference in new issue
Block a user