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
}
}
}
}
Post
Replies
Boosts
Views
Activity
Actually, all I have to do is enumerate an array inside the onMove(perform:) guy.
Mistake... I meant to leave a comment.
Oops... I've changed
class CodeReadModel: NSObject, NSCoding {}
to
class CodeReadModel: NSObject, NSSecureCoding {}
The dict guy now returns a value.
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")
}
}
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.
Thank you for your kind reply, Eskimo.
The first topic that you have mentioned is mine. And it deals with a [String : Any] object. This topic doesn't. codeReadModel is not a [String : Any] object. I suppose that the second one deals with an array of objects. I don't know about the third one, though.
Actually, I'm not so sure if I should use CoreBluetooth for my objective.
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.
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.
We don't know what you have done. You should simply contact them and share your side of story with them.
What's 'view' in event.location(in: view)? If you are already in SpriteKit, not a view controller, that's the cause of confusion.
Because you didn't include in-app purchases when you submitted the app itself?
I submitted a software update 90 minutes, and it has already been reviewed and accepted. So your case is probably isolated. Perhaps, you have an entered a version lower than the current one?
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.