Continue adopting MainActor.

This commit is contained in:
Brent Simmons
2023-07-09 11:34:56 -07:00
parent f7afdfc6c4
commit b49731cc34
38 changed files with 204 additions and 170 deletions

View File

@@ -12,7 +12,7 @@ import Articles
import RSCore
@objc(ScriptableAccount)
class ScriptableAccount: NSObject, UniqueIdScriptingObject, ScriptingObjectContainer {
@MainActor class ScriptableAccount: NSObject, UniqueIdScriptingObject, ScriptingObjectContainer {
let account:Account
init (_ account:Account) {

View File

@@ -11,7 +11,7 @@ import Account
import Articles
@objc(ScriptableArticle)
class ScriptableArticle: NSObject, UniqueIdScriptingObject, ScriptingObjectContainer {
@MainActor class ScriptableArticle: NSObject, UniqueIdScriptingObject, ScriptingObjectContainer {
let article:Article
let container:ScriptingObjectContainer
@@ -111,7 +111,9 @@ class ScriptableArticle: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
return article.status.boolStatus(forKey:.read)
}
set {
markArticles([self.article], statusKey: .read, flag: newValue)
Task { @MainActor in
markArticles([self.article], statusKey: .read, flag: newValue)
}
}
}
@@ -121,7 +123,9 @@ class ScriptableArticle: NSObject, UniqueIdScriptingObject, ScriptingObjectConta
return article.status.boolStatus(forKey:.starred)
}
set {
markArticles([self.article], statusKey: .starred, flag: newValue)
Task { @MainActor in
markArticles([self.article], statusKey: .starred, flag: newValue)
}
}
}

View File

@@ -50,7 +50,7 @@ class ScriptableFolder: NSObject, UniqueIdScriptingObject, ScriptingObjectContai
return self.classDescription as! NSScriptClassDescription
}
func deleteElement(_ element:ScriptingObject) {
@MainActor func deleteElement(_ element:ScriptingObject) {
if let scriptableFeed = element as? ScriptableFeed {
BatchUpdate.shared.perform {
folder.account?.removeFeed(scriptableFeed.feed, from: folder) { result in }
@@ -65,38 +65,43 @@ class ScriptableFolder: NSObject, UniqueIdScriptingObject, ScriptingObjectContai
or
tell account X to make new folder at end with properties {name:"new folder name"}
*/
class func handleCreateElement(command:NSCreateCommand) -> Any? {
class func handleCreateElement(command:NSCreateCommand) -> Any? {
guard command.isCreateCommand(forClass:"fold") else { return nil }
let name = command.property(forKey:"name") as? String ?? ""
// some combination of the tell target and the location specifier ("in" or "at")
// identifies where the new folder should be created
let (account, folder) = command.accountAndFolderForNewChild()
guard folder == nil else {
print("support for folders within folders is NYI");
return nil
}
command.suspendExecution()
account.addFolder(name) { result in
switch result {
case .success(let folder):
let scriptableAccount = ScriptableAccount(account)
let scriptableFolder = ScriptableFolder(folder, container:scriptableAccount)
command.resumeExecution(withResult:scriptableFolder.objectSpecifier)
case .failure:
Task { @MainActor in
// some combination of the tell target and the location specifier ("in" or "at")
// identifies where the new folder should be created
let (account, folder) = command.accountAndFolderForNewChild()
guard folder == nil else {
print("NetNewsWire does not support creating folders within folders.");
command.resumeExecution(withResult:nil)
return
}
account.addFolder(name) { result in
switch result {
case .success(let folder):
let scriptableAccount = ScriptableAccount(account)
let scriptableFolder = ScriptableFolder(folder, container:scriptableAccount)
command.resumeExecution(withResult:scriptableFolder.objectSpecifier)
case .failure:
command.resumeExecution(withResult:nil)
}
}
}
return nil
}
// MARK: --- Scriptable elements ---
@objc(feeds)
var feeds:NSArray {
@MainActor var feeds:NSArray {
let feeds = Array(folder.topLevelFeeds)
return feeds.map { ScriptableFeed($0, container:self) } as NSArray
}

View File

@@ -26,7 +26,7 @@ extension NSScriptCommand {
return true
}
func accountAndFolderForNewChild() -> (Account, Folder?) {
@MainActor func accountAndFolderForNewChild() -> (Account, Folder?) {
let appleEvent = self.appleEvent
var account = AccountManager.shared.defaultAccount
var folder:Folder? = nil

View File

@@ -71,7 +71,7 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine
return url
}
class func scriptableFeed(_ feed: Feed, account: Account, folder: Folder?) -> ScriptableFeed {
@MainActor class func scriptableFeed(_ feed: Feed, account: Account, folder: Folder?) -> ScriptableFeed {
let scriptableAccount = ScriptableAccount(account)
if let folder = folder {
let scriptableFolder = ScriptableFolder(folder, container:scriptableAccount)
@@ -85,33 +85,39 @@ class ScriptableFeed: NSObject, UniqueIdScriptingObject, ScriptingObjectContaine
guard command.isCreateCommand(forClass:"Feed") else { return nil }
guard let arguments = command.arguments else {return nil}
let titleFromArgs = command.property(forKey:"name") as? String
let (account, folder) = command.accountAndFolderForNewChild()
guard let url = self.urlForNewFeed(arguments:arguments) else {return nil}
if let existingFeed = account.existingFeed(withURL:url) {
return scriptableFeed(existingFeed, account:account, folder:folder).objectSpecifier
}
let container: Container = folder != nil ? folder! : account
// We need to download the feed and parse it.
// RSParser does the callback for the download on the main thread.
// Because we can't wait here (on the main thread) for the callback, we have to return from this function.
// Generally, returning from an AppleEvent handler function means that handling the Apple event is over,
// but we dont yet have the result of the event yet, so we prevent the Apple event from returning by calling
// suspendExecution(). When we get the callback, we supply the event result and call resumeExecution().
command.suspendExecution()
account.createFeed(url: url, name: titleFromArgs, container: container, validateFeed: true) { result in
switch result {
case .success(let feed):
NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed])
let scriptableFeed = self.scriptableFeed(feed, account:account, folder:folder)
command.resumeExecution(withResult:scriptableFeed.objectSpecifier)
case .failure:
// We need to download the feed and parse it.
// RSParser does the callback for the download on the main thread.
// Because we can't wait here (on the main thread) for the callback, we have to return from this function.
// Generally, returning from an AppleEvent handler function means that handling the Apple event is over,
// but we dont yet have the result of the event yet, so we prevent the Apple event from returning by calling
// suspendExecution(). When we get the callback, we supply the event result and call resumeExecution().
command.suspendExecution()
Task { @MainActor in
let (account, folder) = command.accountAndFolderForNewChild()
guard let url = self.urlForNewFeed(arguments:arguments) else {
command.resumeExecution(withResult:nil)
return
}
if let existingFeed = account.existingFeed(withURL:url) {
let scriptableFeed = scriptableFeed(existingFeed, account:account, folder:folder)
command.resumeExecution(withResult:scriptableFeed.objectSpecifier)
return
}
let container: Container = folder != nil ? folder! : account
account.createFeed(url: url, name: titleFromArgs, container: container, validateFeed: true) { result in
switch result {
case .success(let feed):
NotificationCenter.default.post(name: .UserDidAddFeed, object: self, userInfo: [UserInfoKey.feed: feed])
let scriptableFeed = self.scriptableFeed(feed, account:account, folder:folder)
command.resumeExecution(withResult:scriptableFeed.objectSpecifier)
case .failure:
command.resumeExecution(withResult:nil)
}
}
}
return nil