Clean up project warnings (#199)

* Set "recommended project settings" for Mac app

* Add default fallback for font typingAttribute

* Clean up linter errors

* Update extension iconset

* Fix spm errors and upgrade sparkle (#203)

* Switch WriteFreely package import from ssh to https

* Sign macOS project target for Development

This fixes a crash-on-launch bug when running Product > Run from Xcode (Product > Archive should work correctly). See https://github.com/sparkle-project/Sparkle/issues/2056 for more details.

Bug introduced by commit:
671b0540ac

* Update Sparkle to v2.0 from official repo

Previously, we forked the repo to the WriteFreely org because Sparkle didn't support tag-based SPM updates. We therefore had to pull from the `master` branch, so forking insulated us from unexpected changes.

As of 2.x Sparkle now allows for tag-based updating via SPM; we'll allow patch updates but stop short of 2.1 until we can vet changes.

* Add MacUpdatesViewModel ObservableObject

* Use MacUpdatesViewModel to handle Sparkle update logic
This commit is contained in:
Angelo Stavrow 2022-04-02 08:04:50 -04:00 committed by GitHub
parent 77822cbc63
commit 549ad3039f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 168 additions and 132 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -1,93 +1,109 @@
{
"images" : [
{
"filename" : "notification-icon@2x.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"filename" : "notification-icon@3x.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"filename" : "icon-small@2x.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"filename" : "icon-small@3x.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"filename" : "icon-40@2x.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"filename" : "icon-40@3x.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"filename" : "AppIconExtension@2x.png",
"filename" : "icon-60@2x.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"filename" : "AppIconExtension@3x.png",
"filename" : "icon-60@3x.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"filename" : "notification-icon~ipad.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"filename" : "notification-icon~ipad@2x.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"filename" : "icon-small.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"filename" : "icon-small@2x-1.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"filename" : "icon-40.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"filename" : "icon-40@2x-1.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"filename" : "icon-76.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"filename" : "icon-76@2x.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"filename" : "icon-83.5@2x.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"filename" : "ios-marketing.png",
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 763 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -147,17 +147,8 @@ extension WriteFreelyModel {
} else {
DispatchQueue.main.async {
let managedPost = WFAPost(context: LocalStorageManager.standard.container.viewContext)
managedPost.postId = fetchedPost.postId
managedPost.slug = fetchedPost.slug
managedPost.appearance = fetchedPost.appearance
managedPost.language = fetchedPost.language
managedPost.rtl = fetchedPost.rtl ?? false
managedPost.createdDate = fetchedPost.createdDate
managedPost.updatedDate = fetchedPost.updatedDate
managedPost.title = fetchedPost.title ?? ""
managedPost.body = fetchedPost.body
self.importData(from: fetchedPost, into: managedPost)
managedPost.collectionAlias = fetchedPost.collectionAlias
managedPost.status = PostStatus.published.rawValue
managedPost.wasDeletedFromServer = false
}
}
@ -193,16 +184,7 @@ extension WriteFreelyModel {
let fetchedPost = try result.get()
// If this is an updated post, check it against postToUpdate.
if let updatingPost = self.postToUpdate {
updatingPost.appearance = fetchedPost.appearance
updatingPost.body = fetchedPost.body
updatingPost.createdDate = fetchedPost.createdDate
updatingPost.language = fetchedPost.language
updatingPost.postId = fetchedPost.postId
updatingPost.rtl = fetchedPost.rtl ?? false
updatingPost.slug = fetchedPost.slug
updatingPost.status = PostStatus.published.rawValue
updatingPost.title = fetchedPost.title ?? ""
updatingPost.updatedDate = fetchedPost.updatedDate
importData(from: fetchedPost, into: updatingPost)
DispatchQueue.main.async {
LocalStorageManager.standard.saveContext()
}
@ -224,16 +206,7 @@ extension WriteFreelyModel {
do {
let cachedPostsResults = try LocalStorageManager.standard.container.viewContext.fetch(request)
guard let cachedPost = cachedPostsResults.first else { return }
cachedPost.appearance = fetchedPost.appearance
cachedPost.body = fetchedPost.body
cachedPost.createdDate = fetchedPost.createdDate
cachedPost.language = fetchedPost.language
cachedPost.postId = fetchedPost.postId
cachedPost.rtl = fetchedPost.rtl ?? false
cachedPost.slug = fetchedPost.slug
cachedPost.status = PostStatus.published.rawValue
cachedPost.title = fetchedPost.title ?? ""
cachedPost.updatedDate = fetchedPost.updatedDate
importData(from: fetchedPost, into: cachedPost)
DispatchQueue.main.async {
LocalStorageManager.standard.saveContext()
}
@ -258,16 +231,7 @@ extension WriteFreelyModel {
do {
let fetchedPost = try result.get()
guard let cachedPost = self.selectedPost else { return }
cachedPost.appearance = fetchedPost.appearance
cachedPost.body = fetchedPost.body
cachedPost.createdDate = fetchedPost.createdDate
cachedPost.language = fetchedPost.language
cachedPost.postId = fetchedPost.postId
cachedPost.rtl = fetchedPost.rtl ?? false
cachedPost.slug = fetchedPost.slug
cachedPost.status = PostStatus.published.rawValue
cachedPost.title = fetchedPost.title ?? ""
cachedPost.updatedDate = fetchedPost.updatedDate
importData(from: fetchedPost, into: cachedPost)
cachedPost.hasNewerRemoteCopy = false
DispatchQueue.main.async {
LocalStorageManager.standard.saveContext()
@ -298,4 +262,17 @@ extension WriteFreelyModel {
print(error)
}
}
private func importData(from fetchedPost: WFPost, into cachedPost: WFAPost) {
cachedPost.appearance = fetchedPost.appearance
cachedPost.body = fetchedPost.body
cachedPost.createdDate = fetchedPost.createdDate
cachedPost.language = fetchedPost.language
cachedPost.postId = fetchedPost.postId
cachedPost.rtl = fetchedPost.rtl ?? false
cachedPost.slug = fetchedPost.slug
cachedPost.status = PostStatus.published.rawValue
cachedPost.title = fetchedPost.title ?? ""
cachedPost.updatedDate = fetchedPost.updatedDate
}
}

View File

@ -59,7 +59,7 @@ private extension LocalStorageManager {
container.persistentStoreDescriptions.first!.url = sharedStoreURL
}
container.loadPersistentStores { description, error in
container.loadPersistentStores { _, error in
if let error = error {
fatalError("Core Data store failed to load with error: \(error)")
}

View File

@ -25,8 +25,8 @@ struct WriteFreely_MultiPlatformApp: App {
@StateObject private var model = WriteFreelyModel.shared
#if os(macOS)
// swiftlint:disable:next weak_delegate
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
@StateObject var updaterViewModel = MacUpdatesViewModel()
@State private var selectedTab = 0
#endif
@ -47,13 +47,6 @@ struct WriteFreely_MultiPlatformApp: App {
showLastDraftOrCreateNewLocalPost()
}
}
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
// if model.editor.lastDraftURL != nil {
// self.model.selectedPost = model.editor.fetchLastDraftFromAppStorage()
// } else {
// createNewLocalPost()
// }
// }
})
.environmentObject(model)
.environment(\.managedObjectContext, LocalStorageManager.standard.container.viewContext)
@ -61,11 +54,9 @@ struct WriteFreely_MultiPlatformApp: App {
}
.commands {
#if os(macOS)
CommandGroup(after: .appInfo, addition: {
Button("Check For Updates") {
SUUpdater.shared()?.checkForUpdates(self)
CommandGroup(after: .appInfo) {
CheckForUpdatesView(updaterViewModel: updaterViewModel)
}
})
#endif
CommandGroup(replacing: .newItem, addition: {
Button("New Post") {
@ -116,7 +107,7 @@ struct WriteFreely_MultiPlatformApp: App {
Text("Preferences")
}
.tag(1)
MacUpdatesView()
MacUpdatesView(updaterViewModel: updaterViewModel)
.tabItem {
Image(systemName: "arrow.down.circle")
Text("Updates")

View File

@ -20,8 +20,6 @@
17120DAC24E1B99F002B9F6C /* AccountLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17120DAB24E1B99F002B9F6C /* AccountLoginView.swift */; };
17120DAD24E1B99F002B9F6C /* AccountLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17120DAB24E1B99F002B9F6C /* AccountLoginView.swift */; };
17120DB224E1E19C002B9F6C /* SettingsHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17120DB124E1E19C002B9F6C /* SettingsHeaderView.swift */; };
1714DD65260BAC14000C0DFF /* WriteFreely in Frameworks */ = {isa = PBXBuildFile; productRef = 1714DD64260BAC14000C0DFF /* WriteFreely */; };
1714DD6B260BAC2C000C0DFF /* WriteFreely in Frameworks */ = {isa = PBXBuildFile; productRef = 1714DD6A260BAC2C000C0DFF /* WriteFreely */; };
171BFDFA24D4AF8300888236 /* CollectionListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171BFDF924D4AF8300888236 /* CollectionListView.swift */; };
171BFDFB24D4AF8300888236 /* CollectionListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171BFDF924D4AF8300888236 /* CollectionListView.swift */; };
171DC677272C7D0B002B9B8A /* UserDefaults+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 171DC676272C7D0B002B9B8A /* UserDefaults+Extensions.swift */; };
@ -35,7 +33,6 @@
172E10152735C2BD00061372 /* UIHostingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 172E10142735C2BD00061372 /* UIHostingView.swift */; };
172E10172735C2DF00061372 /* EnvironmentValues+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 172E10162735C2DF00061372 /* EnvironmentValues+Extensions.swift */; };
172E10192735C3DB00061372 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 172E10182735C3DB00061372 /* ContentView.swift */; };
172E101B2735C54400061372 /* WriteFreely in Frameworks */ = {isa = PBXBuildFile; productRef = 172E101A2735C54400061372 /* WriteFreely */; };
172E101C2735C57400061372 /* LocalStorageManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1756DBB924FED45500207AB8 /* LocalStorageManager.swift */; };
172E101D2735C5AB00061372 /* LocalStorageModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 1756DBB524FED3A400207AB8 /* LocalStorageModel.xcdatamodeld */; };
172E101E2735C62F00061372 /* PostStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17C42E612507D8E600072984 /* PostStatus.swift */; };
@ -78,7 +75,9 @@
17836C15273F0FBB0047AF61 /* Hack-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 17D4F3A42514F1E900517CE6 /* Hack-Regular.ttf */; };
17836C16273F0FBB0047AF61 /* LoraGX.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 17D4F36B2514EE2F00517CE6 /* LoraGX.ttf */; };
17836C17273F0FBB0047AF61 /* OpenSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 17D4F39D2514F0E500517CE6 /* OpenSans-Regular.ttf */; };
17A4FEDA25924AF70037E96B /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 17A4FED925924AF70037E96B /* Sparkle */; };
1784D2ED27946D880033E72E /* WriteFreely in Frameworks */ = {isa = PBXBuildFile; productRef = 1784D2EC27946D880033E72E /* WriteFreely */; };
1784D2EF27946D9A0033E72E /* WriteFreely in Frameworks */ = {isa = PBXBuildFile; productRef = 1784D2EE27946D9A0033E72E /* WriteFreely */; };
1784D2F127946DA10033E72E /* WriteFreely in Frameworks */ = {isa = PBXBuildFile; productRef = 1784D2F027946DA10033E72E /* WriteFreely */; };
17A4FEED25927E730037E96B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A4FEEC25927E730037E96B /* AppDelegate.swift */; };
17A5388824DDA31F00DEFF9A /* MacAccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A5388724DDA31F00DEFF9A /* MacAccountView.swift */; };
17A5388C24DDC83F00DEFF9A /* AccountModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A5388B24DDC83F00DEFF9A /* AccountModel.swift */; };
@ -108,6 +107,8 @@
17C42E71250AAFD500072984 /* NSManagedObjectContext+ExecuteAndMergeChanges.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17C42E6F250AA12200072984 /* NSManagedObjectContext+ExecuteAndMergeChanges.swift */; };
17D435E824E3128F0036B539 /* PreferencesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D435E724E3128F0036B539 /* PreferencesModel.swift */; };
17D435E924E3128F0036B539 /* PreferencesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D435E724E3128F0036B539 /* PreferencesModel.swift */; };
17D4926527947B4D0035BD7E /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = 17D4926427947B4D0035BD7E /* Sparkle */; };
17D4926727947D780035BD7E /* MacUpdatesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D4926627947D780035BD7E /* MacUpdatesViewModel.swift */; };
17D4F36C2514EE2F00517CE6 /* LoraGX.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 17D4F36B2514EE2F00517CE6 /* LoraGX.ttf */; };
17D4F36D2514EE2F00517CE6 /* LoraGX.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 17D4F36B2514EE2F00517CE6 /* LoraGX.ttf */; };
17D4F39E2514F0E500517CE6 /* OpenSans-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 17D4F39D2514F0E500517CE6 /* OpenSans-Regular.ttf */; };
@ -234,6 +235,7 @@
17C42E642509237800072984 /* PostListFilteredView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostListFilteredView.swift; sourceTree = "<group>"; };
17C42E6F250AA12200072984 /* NSManagedObjectContext+ExecuteAndMergeChanges.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+ExecuteAndMergeChanges.swift"; sourceTree = "<group>"; };
17D435E724E3128F0036B539 /* PreferencesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesModel.swift; sourceTree = "<group>"; };
17D4926627947D780035BD7E /* MacUpdatesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacUpdatesViewModel.swift; sourceTree = "<group>"; };
17D4F36B2514EE2F00517CE6 /* LoraGX.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = LoraGX.ttf; sourceTree = "<group>"; };
17D4F39D2514F0E500517CE6 /* OpenSans-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "OpenSans-Regular.ttf"; sourceTree = "<group>"; };
17D4F3A42514F1E900517CE6 /* Hack-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Hack-Regular.ttf"; sourceTree = "<group>"; };
@ -269,7 +271,7 @@
buildActionMask = 2147483647;
files = (
172E10012735B83E00061372 /* UniformTypeIdentifiers.framework in Frameworks */,
172E101B2735C54400061372 /* WriteFreely in Frameworks */,
1784D2F127946DA10033E72E /* WriteFreely in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -277,7 +279,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
1714DD65260BAC14000C0DFF /* WriteFreely in Frameworks */,
1784D2ED27946D880033E72E /* WriteFreely in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -285,8 +287,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
17A4FEDA25924AF70037E96B /* Sparkle in Frameworks */,
1714DD6B260BAC2C000C0DFF /* WriteFreely in Frameworks */,
1784D2EF27946D9A0033E72E /* WriteFreely in Frameworks */,
17D4926527947B4D0035BD7E /* Sparkle in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -400,6 +402,7 @@
17A5388724DDA31F00DEFF9A /* MacAccountView.swift */,
1753F6AB24E431CC00309365 /* MacPreferencesView.swift */,
172C492D2593981900E20ADF /* MacUpdatesView.swift */,
17D4926627947D780035BD7E /* MacUpdatesViewModel.swift */,
);
path = Settings;
sourceTree = "<group>";
@ -619,7 +622,7 @@
);
name = "ActionExtension-iOS";
packageProductDependencies = (
172E101A2735C54400061372 /* WriteFreely */,
1784D2F027946DA10033E72E /* WriteFreely */,
);
productName = "ActionExtension-iOS";
productReference = 172E0FFF2735B83E00061372 /* ActionExtension-iOS.appex */;
@ -642,7 +645,7 @@
);
name = "WriteFreely-MultiPlatform (iOS)";
packageProductDependencies = (
1714DD64260BAC14000C0DFF /* WriteFreely */,
1784D2EC27946D880033E72E /* WriteFreely */,
);
productName = "WriteFreely-MultiPlatform (iOS)";
productReference = 17DF328824C87D3500BCE2E3 /* WriteFreely-MultiPlatform.app */;
@ -663,8 +666,8 @@
);
name = "WriteFreely-MultiPlatform (macOS)";
packageProductDependencies = (
17A4FED925924AF70037E96B /* Sparkle */,
1714DD6A260BAC2C000C0DFF /* WriteFreely */,
1784D2EE27946D9A0033E72E /* WriteFreely */,
17D4926427947B4D0035BD7E /* Sparkle */,
);
productName = "WriteFreely-MultiPlatform (macOS)";
productReference = 17DF329024C87D3500BCE2E3 /* WriteFreely for Mac.app */;
@ -713,7 +716,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1310;
LastUpgradeCheck = 1200;
LastUpgradeCheck = 1320;
TargetAttributes = {
172E0FFE2735B83E00061372 = {
CreatedOnToolsVersion = 13.1;
@ -744,8 +747,8 @@
);
mainGroup = 17DF327B24C87D3300BCE2E3;
packageReferences = (
17A4FED825924AF70037E96B /* XCRemoteSwiftPackageReference "Sparkle" */,
1714DD63260BAC14000C0DFF /* XCRemoteSwiftPackageReference "writefreely-swift" */,
1784D2EB27946D880033E72E /* XCRemoteSwiftPackageReference "writefreely-swift" */,
17D4926327947B4D0035BD7E /* XCRemoteSwiftPackageReference "Sparkle" */,
);
productRefGroup = 17DF328924C87D3500BCE2E3 /* Products */;
projectDirPath = "";
@ -942,6 +945,7 @@
17480CA6251272EE00EB7765 /* Bundle+AppVersion.swift in Sources */,
17C42E662509237800072984 /* PostListFilteredView.swift in Sources */,
17120DAD24E1B99F002B9F6C /* AccountLoginView.swift in Sources */,
17D4926727947D780035BD7E /* MacUpdatesViewModel.swift in Sources */,
17466626256C0D0600629997 /* MacEditorTextView.swift in Sources */,
170A7EC226F5186A00F1CBD4 /* CollectionListModel.swift in Sources */,
17E5DF8A2543610700DCDC9B /* PostTextEditingView.swift in Sources */,
@ -1243,6 +1247,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 620;
@ -1269,6 +1274,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 620;
@ -1438,43 +1444,43 @@
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
1714DD63260BAC14000C0DFF /* XCRemoteSwiftPackageReference "writefreely-swift" */ = {
1784D2EB27946D880033E72E /* XCRemoteSwiftPackageReference "writefreely-swift" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "git@github.com:writefreely/writefreely-swift.git";
repositoryURL = "https://github.com/writefreely/writefreely-swift";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 0.3.2;
minimumVersion = 0.3.4;
};
};
17A4FED825924AF70037E96B /* XCRemoteSwiftPackageReference "Sparkle" */ = {
17D4926327947B4D0035BD7E /* XCRemoteSwiftPackageReference "Sparkle" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/writefreely/Sparkle";
repositoryURL = "https://github.com/sparkle-project/Sparkle";
requirement = {
branch = master;
kind = branch;
kind = upToNextMinorVersion;
minimumVersion = 2.0.0;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
1714DD64260BAC14000C0DFF /* WriteFreely */ = {
1784D2EC27946D880033E72E /* WriteFreely */ = {
isa = XCSwiftPackageProductDependency;
package = 1714DD63260BAC14000C0DFF /* XCRemoteSwiftPackageReference "writefreely-swift" */;
package = 1784D2EB27946D880033E72E /* XCRemoteSwiftPackageReference "writefreely-swift" */;
productName = WriteFreely;
};
1714DD6A260BAC2C000C0DFF /* WriteFreely */ = {
1784D2EE27946D9A0033E72E /* WriteFreely */ = {
isa = XCSwiftPackageProductDependency;
package = 1714DD63260BAC14000C0DFF /* XCRemoteSwiftPackageReference "writefreely-swift" */;
package = 1784D2EB27946D880033E72E /* XCRemoteSwiftPackageReference "writefreely-swift" */;
productName = WriteFreely;
};
172E101A2735C54400061372 /* WriteFreely */ = {
1784D2F027946DA10033E72E /* WriteFreely */ = {
isa = XCSwiftPackageProductDependency;
package = 1714DD63260BAC14000C0DFF /* XCRemoteSwiftPackageReference "writefreely-swift" */;
package = 1784D2EB27946D880033E72E /* XCRemoteSwiftPackageReference "writefreely-swift" */;
productName = WriteFreely;
};
17A4FED925924AF70037E96B /* Sparkle */ = {
17D4926427947B4D0035BD7E /* Sparkle */ = {
isa = XCSwiftPackageProductDependency;
package = 17A4FED825924AF70037E96B /* XCRemoteSwiftPackageReference "Sparkle" */;
package = 17D4926327947B4D0035BD7E /* XCRemoteSwiftPackageReference "Sparkle" */;
productName = Sparkle;
};
/* End XCSwiftPackageProductDependency section */

View File

@ -7,17 +7,17 @@
<key>ActionExtension-iOS.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
<integer>1</integer>
</dict>
<key>WriteFreely-MultiPlatform (iOS).xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
<integer>2</integer>
</dict>
<key>WriteFreely-MultiPlatform (macOS).xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>2</integer>
<integer>0</integer>
</dict>
</dict>
</dict>

View File

@ -3,26 +3,6 @@ import Sparkle
class AppDelegate: NSObject, NSApplicationDelegate {
func applicationWillFinishLaunching(_ notification: Notification) {
// Check UserDefaults for values; if the key doesn't exist (e.g., if MacUpdatesView hasn't ever been shown),
// bool(forKey:) returns false, so set SUUpdater.shared() appropriately.
let automaticallyChecksForUpdates = UserDefaults.shared.bool(forKey: WFDefaults.automaticallyChecksForUpdates)
let subscribeToBetaUpdates = UserDefaults.shared.bool(forKey: WFDefaults.subscribeToBetaUpdates)
// Set Sparkle properties.
SUUpdater.shared()?.automaticallyChecksForUpdates = automaticallyChecksForUpdates
if subscribeToBetaUpdates {
SUUpdater.shared()?.feedURL = URL(string: AppcastFeedUrl.beta.rawValue)
} else {
SUUpdater.shared()?.feedURL = URL(string: AppcastFeedUrl.release.rawValue)
}
// If enabled, check for updates.
if automaticallyChecksForUpdates {
SUUpdater.shared()?.checkForUpdatesInBackground()
}
}
// MARK: - Window handling when miniaturized into app icon on the Dock
// Credit to Henry Cooper (pillboxer) on GitHub:
// https://github.com/tact/beta-bugs/issues/31#issuecomment-855914705
@ -30,7 +10,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
// If the window is currently minimized into the Dock, de-miniaturize it (note that if it's minimized
// and the user uses OPT+TAB to switch to it, it will be de-miniaturized and brought to the foreground).
func applicationDidBecomeActive(_ notification: Notification) {
print("💬 Fired:", #function)
if let window = NSApp.windows.first {
window.deminiaturize(nil)
}
@ -39,7 +18,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
// If we're miniaturizing the window, deactivate it as well by activating Finder.app (note that
// this will bring any Finder windows that are behind other apps to the foreground).
func applicationDidChangeOcclusionState(_ notification: Notification) {
print("💬 Fired:", #function)
if let window = NSApp.windows.first, window.isMiniaturized {
NSWorkspace.shared.runningApplications.first(where: {
$0.activationPolicy == .regular
@ -49,7 +27,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
lazy var windows = NSWindow()
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
print("💬 Fired:", #function)
if !flag {
for window in sender.windows {
window.makeKeyAndOrderFront(self)

View File

@ -164,7 +164,7 @@ final class CustomTextView: NSView {
textView.isRichText = false
textView.typingAttributes = [
.paragraphStyle: paragraphStyle, // H/T Daniel Jalkut
.font: font,
.font: font ?? NSFont.systemFont(ofSize: 17), // Fall back to system font if we can't unwrap font argument
.foregroundColor: NSColor.labelColor
]

View File

@ -1,16 +1,14 @@
import SwiftUI
import Sparkle
enum AppcastFeedUrl: String {
case release = "https://writefreely-files.s3.amazonaws.com/apps/mac/appcast.xml"
case beta = "https://writefreely-files.s3.amazonaws.com/apps/mac/appcast-beta.xml"
}
struct MacUpdatesView: View {
@ObservedObject var updaterViewModel: MacUpdatesViewModel
@AppStorage(WFDefaults.automaticallyChecksForUpdates, store: UserDefaults.shared)
var automaticallyChecksForUpdates: Bool = false
@AppStorage(WFDefaults.subscribeToBetaUpdates, store: UserDefaults.shared)
var subscribeToBetaUpdates: Bool = false
@State private var lastUpdateCheck: Date?
private let betaWarningString = """
@ -34,9 +32,11 @@ that can cause crashes and data loss.
VStack {
Button(action: {
SUUpdater.shared()?.checkForUpdates(self)
DispatchQueue.main.async {
lastUpdateCheck = SUUpdater.shared()?.lastUpdateCheckDate
updaterViewModel.checkForUpdates()
// There's a delay between requesting an update, and the timestamp for that update request being
// written to user defaults; we therefore delay updating the "Last checked" UI for one second.
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
lastUpdateCheck = updaterViewModel.getLastUpdateCheckDate()
}
}, label: {
Text("Check For Updates")
@ -73,23 +73,19 @@ that can cause crashes and data loss.
}
.padding()
.onAppear {
lastUpdateCheck = SUUpdater.shared()?.lastUpdateCheckDate
lastUpdateCheck = updaterViewModel.getLastUpdateCheckDate()
}
.onChange(of: automaticallyChecksForUpdates) { value in
SUUpdater.shared()?.automaticallyChecksForUpdates = value
}
.onChange(of: subscribeToBetaUpdates) { value in
if value {
SUUpdater.shared()?.feedURL = URL(string: AppcastFeedUrl.beta.rawValue)
} else {
SUUpdater.shared()?.feedURL = URL(string: AppcastFeedUrl.release.rawValue)
updaterViewModel.automaticallyCheckForUpdates = value
}
.onChange(of: subscribeToBetaUpdates) { _ in
updaterViewModel.toggleAllowedChannels()
}
}
}
struct MacUpdatesView_Previews: PreviewProvider {
static var previews: some View {
MacUpdatesView()
MacUpdatesView(updaterViewModel: MacUpdatesViewModel())
}
}

View File

@ -0,0 +1,73 @@
/// See https://sparkle-project.org/documentation/programmatic-setup#create-an-updater-in-swiftui
import SwiftUI
import Sparkle
/// This view model class manages Sparkle's updater and publishes when new updates are allowed to be checked.
final class MacUpdatesViewModel: ObservableObject {
@Published var canCheckForUpdates = false
private let updaterController: SPUStandardUpdaterController
private let updaterDelegate = MacUpdatesViewModelDelegate()
var automaticallyCheckForUpdates: Bool {
get {
return updaterController.updater.automaticallyChecksForUpdates
}
set(newValue) {
updaterController.updater.automaticallyChecksForUpdates = newValue
}
}
init() {
updaterController = SPUStandardUpdaterController(startingUpdater: true,
updaterDelegate: updaterDelegate,
userDriverDelegate: nil)
updaterController.updater.publisher(for: \.canCheckForUpdates)
.assign(to: &$canCheckForUpdates)
if automaticallyCheckForUpdates {
updaterController.updater.checkForUpdatesInBackground()
}
}
func checkForUpdates() {
updaterController.checkForUpdates(nil)
}
func getLastUpdateCheckDate() -> Date? {
return updaterController.updater.lastUpdateCheckDate
}
@discardableResult
func toggleAllowedChannels() -> Set<String> {
return updaterDelegate.allowedChannels(for: updaterController.updater)
}
}
final class MacUpdatesViewModelDelegate: NSObject, SPUUpdaterDelegate {
@AppStorage(WFDefaults.subscribeToBetaUpdates, store: UserDefaults.shared)
var subscribeToBetaUpdates: Bool = false
func allowedChannels(for updater: SPUUpdater) -> Set<String> {
let allowedChannels = Set(subscribeToBetaUpdates ? ["beta"] : [])
return allowedChannels
}
}
// This additional view is needed for the disabled state on the menu item to work properly before Monterey.
// See https://stackoverflow.com/questions/68553092/menu-not-updating-swiftui-bug for more information
struct CheckForUpdatesView: View {
@ObservedObject var updaterViewModel: MacUpdatesViewModel
var body: some View {
Button("Check for Updates…", action: updaterViewModel.checkForUpdates)
.disabled(!updaterViewModel.canCheckForUpdates)
}
}