Post

Replies

Boosts

Views

Activity

Reply to Showing SwiftUI Below NSStatusItem When Button is Clicked on Over SwiftUI View
I've gotten the following lines of code working. import SwiftUI @main struct MyStatusApp_App: App { #if os(macOS) @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate #endif @StateObject private var statusBarViewModel = StatusBarViewModel() var body: some Scene { WindowGroup { ContentView() .onDisappear { NSApplication.shared.terminate(self) } .environmentObject(statusBarViewModel) } .defaultSize(width: 640, height: 360) } } #if os(macOS) class AppDelegate: NSObject, NSApplicationDelegate { let fileManager = FileManager.default func applicationDidFinishLaunching(_ notification: Notification) { hideTitleBar() NSApp.setActivationPolicy(.accessory) } func applicationWillFinishLaunching(_ notification: Notification) { NSWindow.allowsAutomaticWindowTabbing = false } } #endif // ContentView.swift // import SwiftUI struct ContentView: View { @NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate @EnvironmentObject var statusBarViewModel: StatusBarViewModel var body: some View { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Button("Click me!") { if let statusItem = statusBarViewModel.statusItem { if let button = statusItem.button { if statusBarViewModel.popover == nil { let popover = NSPopover() statusBarViewModel.popover = popover popover.behavior = .transient popover.animates = false popover.contentViewController = NSHostingController(rootView: NotificationView()) } statusBarViewModel.popover?.show(relativeTo: button.bounds, of: button, preferredEdge: .minY) } } } } .frame(width: 200, height: 100) .onAppear { statusBarViewModel.makeStatusMenu() } } } class StatusBarViewModel: ObservableObject { @Published var statusItem: NSStatusItem! @Published var popover: NSPopover? func makeStatusMenu() { statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength) if let button = statusItem.button { if let image = NSImage(named: "statusImage") { button.image = image } } } }
Aug ’24
Reply to Unarchiving an object with custom classes
Thank you for your kind assistance, The Eskimo. Your unarchivedObject(ofClasses:from:) doesn't even include NSDictionary, NSArray, NSString? The unarchivedObject guy is quite confusing. I have minimized my work like the following to see how the guy works. import Cocoa class ViewController: NSViewController { override func viewDidLoad() { super.viewDidLoad() } @IBAction func saveClicked(_ sender: NSButton) { showSavePanel() } @IBAction func openClicked(_ sender: NSButton) { showOpenPanel() } func showSavePanel() { let panel: NSSavePanel = NSSavePanel() ... if (panel.runModal().rawValue == NSApplication.ModalResponse.cancel.rawValue) { return } else { showSavePanelNext(fileURL: panel.url!) } } func showSavePanelNext(fileURL: URL) { let codeModel = CodeModel2(identifier: UUID().uuidString, highlightIndex: 1) NSKeyedArchiver.setClassName("CodeModel2", for: CodeModel2.self) do { let data = try NSKeyedArchiver.archivedData(withRootObject: codeModel, requiringSecureCoding: false) try data.write(to: fileURL, options: .atomicWrite) } catch { print("Error: \(error.localizedDescription)") } } func showOpenPanel() { let panel = NSOpenPanel() ... if (panel.runModal().rawValue == NSApplication.ModalResponse.cancel.rawValue) { return } else { showOpenPanelNext(fileURL: panel.url!) } } func showOpenPanelNext(fileURL: URL) { do { NSKeyedUnarchiver.setClass(CodeModel2.self, forClassName: "CodeModel2") let data = try! Data(contentsOf: fileURL) if let dict = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSDictionary.self, NSString.self, CodeModel2.self], from: data) { if let model = dict as? CodeModel2 { let id = model.identifier print("**** \(id)") } } else { print("What!?") } } } } class CodeModel2: NSObject, NSSecureCoding { class var supportsSecureCoding: Bool { true } let identifier: String let highlightIndex: Int init(identifier: String, highlightIndex: Int) { self.identifier = identifier self.highlightIndex = highlightIndex } required init(coder decoder: NSCoder) { self.identifier = decoder.decodeObject(forKey: "identifier") as! String self.highlightIndex = decoder.decodeInteger(forKey: "highlightIndex") } func encode(with coder: NSCoder) { coder.encode(identifier, forKey: "identifier") coder.encode(highlightIndex, forKey: "highlightIndex") } } It's a simple example, and it works. The following doesn't. dict doesn't return a value. import Cocoa class ViewController: NSViewController { func showSavePanelNext(fileURL: URL) { let codeDataModel = CodeDataModel(lastComponent: "hello.png", data: Data("World!".utf8)) let codeModel = CodeReadModel(identifier: UUID().uuidString, highlightIndex: 2, codeDataModels: [codeDataModel]) NSKeyedArchiver.setClassName("CodeReadModel", for: CodeReadModel.self) NSKeyedArchiver.setClassName("CodeDataModel", for: CodeDataModel.self) do { let data = try NSKeyedArchiver.archivedData(withRootObject: codeModel, requiringSecureCoding: false) try data.write(to: fileURL, options: .atomicWrite) } catch { print("Error: \(error.localizedDescription)") } } func showOpenPanel() { let panel = NSOpenPanel() panel.allowsMultipleSelection = false panel.canChooseDirectories = false panel.canCreateDirectories = false panel.canChooseFiles = true panel.allowedContentTypes = [.text] panel.title = "Open me!" panel.message = "" //panel.directoryURL = NSURL.fileURL(withPath: openPath) if (panel.runModal().rawValue == NSApplication.ModalResponse.cancel.rawValue) { return } else { showOpenPanelNext(fileURL: panel.url!) } } func showOpenPanelNext(fileURL: URL) { do { NSKeyedUnarchiver.setClass(CodeReadModel.self, forClassName: "CodeReadModel") NSKeyedUnarchiver.setClass(CodeDataModel.self, forClassName: "CodeDataModel") let data = try! Data(contentsOf: fileURL) if let dict = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSDictionary.self, NSArray.self, NSString.self, CodeReadModel.self, CodeDataModel.self], from: data) { if let model = dict as? CodeReadModel { let id = model.identifier print("**** \(id)") } } else { print("What!?") } } } } class CodeDataModel: NSObject, NSSecureCoding { class var supportsSecureCoding: Bool { true } let lastComponent: String let data: Data init(lastComponent: String, data: Data) { self.lastComponent = lastComponent self.data = data } required init(coder decoder: NSCoder) { self.lastComponent = decoder.decodeObject(forKey: "lastComponent") as! String self.data = decoder.decodeObject(forKey: "data") as! Data } func encode(with coder: NSCoder) { coder.encode(lastComponent, forKey: "lastComponent") coder.encode(data, forKey: "data") } } class CodeReadModel: NSObject, NSSecureCoding { class var supportsSecureCoding: Bool { true } let identifier: String let highlightIndex: Int let codeDataModels: [CodeDataModel] init(identifier: String, highlightIndex: Int, codeDataModels: [CodeDataModel]) { self.identifier = identifier self.highlightIndex = highlightIndex self.codeDataModels = codeDataModels } required init(coder decoder: NSCoder) { self.identifier = decoder.decodeObject(forKey: "identifier") as! String self.highlightIndex = decoder.decodeInteger(forKey: "highlightIndex") self.codeDataModels = decoder.decodeObject(forKey: "codeDataModels") as! [CodeDataModel] } func encode(with coder: NSCoder) { coder.encode(identifier, forKey: "identifier") coder.encode(highlightIndex, forKey: "highlightIndex") coder.encode(codeDataModels, forKey: "codeDataModels") } }
Jul ’24
Reply to Unarchiving an object with custom classes
Silly me... I would have been more productive if I have taken a closer look at the topic that I have accepted as an answer here. Nonetheless, I still don't get it. I have done many variations like the following. if let dict = try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSDictionary.self, NSArray.self, NSString.self, CodeReadModel.self, CodeDataModel.self, NSString.self], from: data) { print("**** \(dict)") } else { print("What!?") } They all go to the second print.
Jul ’24
Reply to Does it possible to release another online version of exist app
If you publish a software title that shares same features with an existing one, it can be rejected. Some reviewers check and see if you already have one or more similar titles. You can get a flat rejection. Or they may suggest that you provide new features as add-ons to an existing software title. So you may consider offering in-app purchases. Of course, you can play dumb and submit a new title, which carries a risk of being rejected not now but later on.
Feb ’24
Reply to Our app I was rejected, reason: Your app is primarily a book and is therefore not appropriate for the App Store.
If you don't like their rules, you are free to open your own online store and sell your products. Nobody can stop you. And you can be bitter for as many weeks and months as you want, thinking about how much money you have lost except that a stranger doesn't care. I would rather either accept a defeat and move on with my life or adjust the product such that it would be allowed into the store, examining similar products of others.
Feb ’24
Reply to Our app I was rejected, reason: Your app is primarily a book and is therefore not appropriate for the App Store.
If I were you, I would either accept a defeat and move on or download similar "interactive" books that you say are accepted into the App Store and find out how they make it. What I wouldn't do is take the case to the Appeals Board. The Appeals Board often takes the side of the reviewer FYI. If they do, that will be the end of your project.
Feb ’24