Prep Mac app for release, fix bugs in iOS app (#258)

* Update Sparkle to latest version

* Bump minimum macOS target

For launch, I propose we support the current version of macOS (14.x) and one version earlier (13.x).

* Add WFNavigation wrapper to use NavigationSplitView in macOS

* Replace NavigationView with WFNavigation in ContentView

* Fix deprecation warnings on locale

* Update docs for updating the Mac app

* Fix for being sent back to post list on app reactivate

* Bump build version

* Remove debugging statements

* Bump Sparkle version to address security fix
This commit is contained in:
Angelo Stavrow 2024-07-28 06:02:02 -04:00 committed by GitHub
parent 664eb44882
commit 53ab32b7a3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 165 additions and 90 deletions

View File

@ -94,11 +94,18 @@ extension WriteFreelyModel {
}
if post.language == nil {
if #available(iOS 16, macOS 13, *) {
if let languageCode = Locale.current.language.languageCode?.identifier {
post.language = languageCode
post.rtl = Locale.Language(identifier: languageCode).characterDirection == .rightToLeft
}
} else {
if let languageCode = Locale.current.languageCode {
post.language = languageCode
post.rtl = Locale.characterDirection(forLanguage: languageCode) == .rightToLeft
}
}
}
var wfPost = WFPost(
body: post.body,

View File

@ -5,11 +5,15 @@ struct ContentView: View {
@EnvironmentObject var errorHandling: ErrorHandling
var body: some View {
NavigationView {
#if os(macOS)
WFNavigation(
collectionList: {
CollectionListView()
.withErrorHandling()
.toolbar {
if #available(macOS 13, *) {
EmptyView()
} else {
Button(
action: {
NSApp.keyWindow?.contentViewController?.tryToPerform(
@ -19,6 +23,7 @@ struct ContentView: View {
label: { Image(systemName: "sidebar.left") }
)
.help("Toggle the sidebar's visibility.")
}
Spacer()
Button(action: {
withAnimation {
@ -37,12 +42,8 @@ struct ContentView: View {
.help("Create a new local draft.")
}
.frame(width: 200)
#else
CollectionListView()
.withErrorHandling()
#endif
#if os(macOS)
},
postList: {
ZStack {
PostListView(selectedCollection: model.selectedCollection, showAllPosts: model.showAllPosts)
.withErrorHandling()
@ -54,13 +55,11 @@ struct ContentView: View {
}
}
}
#else
PostListView(selectedCollection: model.selectedCollection, showAllPosts: model.showAllPosts)
.withErrorHandling()
#endif
},
postDetail: {
NoSelectedPostView(isConnected: $model.hasNetworkConnection)
}
)
.environmentObject(model)
.onChange(of: model.hasError) { value in
if value {
@ -72,6 +71,32 @@ struct ContentView: View {
model.hasError = false
}
}
#else
WFNavigation(
collectionList: {
CollectionListView()
.withErrorHandling()
},
postList: {
PostListView(selectedCollection: model.selectedCollection, showAllPosts: model.showAllPosts)
.withErrorHandling()
},
postDetail: {
NoSelectedPostView(isConnected: $model.hasNetworkConnection)
}
)
.environmentObject(model)
.onChange(of: model.hasError) { value in
if value {
if let error = model.currentError {
self.errorHandling.handle(error: error)
} else {
self.errorHandling.handle(error: AppError.genericError())
}
model.hasError = false
}
}
#endif
}
}

View File

@ -0,0 +1,37 @@
import SwiftUI
struct WFNavigation<CollectionList, PostList, PostDetail>: View
where CollectionList: View, PostList: View, PostDetail: View {
private var collectionList: CollectionList
private var postList: PostList
private var postDetail: PostDetail
init(
@ViewBuilder collectionList: () -> CollectionList,
@ViewBuilder postList: () -> PostList,
@ViewBuilder postDetail: () -> PostDetail
) {
self.collectionList = collectionList()
self.postList = postList()
self.postDetail = postDetail()
}
var body: some View {
#if os(macOS)
NavigationSplitView {
collectionList
} content: {
postList
} detail: {
postDetail
}
#else
NavigationView {
collectionList
postList
postDetail
}
#endif
}
}

View File

@ -48,10 +48,17 @@ struct PostEditorModel {
default:
managedPost.appearance = "serif"
}
if #available(iOS 16, macOS 13, *) {
if let languageCode = Locale.current.language.languageCode?.identifier {
managedPost.language = languageCode
managedPost.rtl = Locale.Language(identifier: languageCode).characterDirection == .rightToLeft
}
} else {
if let languageCode = Locale.current.languageCode {
managedPost.language = languageCode
managedPost.rtl = Locale.characterDirection(forLanguage: languageCode) == .rightToLeft
}
}
return managedPost
}

View File

@ -126,19 +126,19 @@ struct PostListView: View {
.frame(height: frameHeight)
.background(Color(UIColor.systemGray5))
.overlay(Divider(), alignment: .top)
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in
}
.ignoresSafeArea(.all, edges: .bottom)
.onAppear {
// Set the selected collection and whether or not we want to show all posts
model.selectedCollection = selectedCollection
model.showAllPosts = showAllPosts
// We use this to invalidate and refresh the view, so that new posts created outside of the app (e.g.,
// in the action extension) show up.
withAnimation {
self.filteredListViewId += 1
}
}
}
.ignoresSafeArea(.all, edges: .bottom)
.onAppear {
model.selectedCollection = selectedCollection
model.showAllPosts = showAllPosts
}
.onChange(of: model.hasError) { value in
if value {
if let error = model.currentError {

View File

@ -2,13 +2,6 @@
To make updating the Mac app easy, we're using the [Sparkle framework][1].
This is added to the project via the Swift Package Manager (SPM), but at the time of writing, tagged versions of Sparkle do not yet support
SPM — the dependency can only be added from a branch or commit. To avoid any surprises arising from updates to the project's `master`
branch, we're using [WriteFreely's fork of Sparkle][2]. Updates to the forked repository from upstream should be considered dangerous and
tested thoroughly before merging into `main`.
WriteFreely for Mac uses the v1.x branch of Sparkle, and is therefore not a sandboxed app.
## Troubleshooting
### If Xcode throws an error when you try to build the project
@ -22,21 +15,22 @@ You should then be able to build and run the Mac target.
### If you can't run `generate_keys` because "Apple cannot check it for malicious software"
There may be a code signing issue with Sparkle. Right-click on `generate_keys` in the Finder and choose Open ([reference][3]).
If you run into a code signing issue with Sparkle, right-click on `generate_keys` in the Finder and choose Open ([reference][2]).
## Deploying Updates
To [publish an update to the app][5], you'll need the **Sparkle-for-Swift-Package-Manager.zip** [archive][4] — specifically, you'll need the
`generate_appcast` tool. Download and de-compress the archive.
To [publish an update to the app][4], you'll need the **Sparkle-for-Swift-Package-Manager.zip** [archive][3] — 
specifically, you'll need the `generate_appcast` tool. Download and de-compress the archive.
You will need some credentials and signing certificates to proceed with this process; speak to the project maintainer if you're responsible for
creating the update, and confirm you have:
You will need some credentials and signing certificates to proceed with this process; speak to the project maintainer if
you're responsible for creating the update, and confirm you have:
- the app's Developer ID Application certificate (check your Mac's system Keychain)
- the Sparkle EdDSA signing key (again, check your Mac's system Keychain)
Sign and notarize the app archive, then click on **Export Notarized App** in Xcode's Organizer window. Open the Terminal and navigate to
where you de-compressed the Sparkle-for-Swift-Package-Manager archive, then create a zip file that preserves symlinks:
Sign and notarize the app archive, then click on **Export Notarized App** in Xcode's Organizer window. Open the Terminal
and navigate to where you de-compressed the Sparkle-for-Swift-Package-Manager archive, then create a zip file that
preserves symlinks:
```bash
% ditto -c -k --sequesterRsrc --keepParent <source_path_to_app> <zip_destination>
@ -60,7 +54,6 @@ and they'll be made available to users.
<!--references-->
[1]: https://sparkle-project.org
[2]: https://github.com/writefreely/Sparkle
[3]: https://github.com/sparkle-project/Sparkle/issues/1701#issuecomment-752249920
[4]: https://github.com/sparkle-project/Sparkle/releases/tag/1.24.0
[5]: https://sparkle-project.org/documentation/publishing/
[2]: https://github.com/sparkle-project/Sparkle/issues/1701#issuecomment-752249920
[3]: https://github.com/sparkle-project/Sparkle/releases/tag/1.24.0
[4]: https://sparkle-project.org/documentation/publishing/

View File

@ -139,6 +139,8 @@
37095AE02AA4A0E700C9C5F8 /* NoSelectedPostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37113EF82A98C10A00B36B98 /* NoSelectedPostView.swift */; };
37113EF92A98C10A00B36B98 /* NoSelectedPostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37113EF82A98C10A00B36B98 /* NoSelectedPostView.swift */; };
375A67E828FC555C007A1AC0 /* MultilineTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375A67E728FC555C007A1AC0 /* MultilineTextView.swift */; };
376A350D2B5D5C8E00255D61 /* WFNavigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376A350C2B5D5C8E00255D61 /* WFNavigation.swift */; };
376A350E2B5D5C8E00255D61 /* WFNavigation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 376A350C2B5D5C8E00255D61 /* WFNavigation.swift */; };
3779389729EC0C880032D6C1 /* HelpCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3779389629EC0C880032D6C1 /* HelpCommands.swift */; };
37F749D129B4D3090087F0BF /* SearchablePostListFilteredView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37F749D029B4D3090087F0BF /* SearchablePostListFilteredView.swift */; };
37F749D229B4D3090087F0BF /* SearchablePostListFilteredView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37F749D029B4D3090087F0BF /* SearchablePostListFilteredView.swift */; };
@ -274,6 +276,7 @@
17E5DF892543610700DCDC9B /* PostTextEditingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostTextEditingView.swift; sourceTree = "<group>"; };
37113EF82A98C10A00B36B98 /* NoSelectedPostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoSelectedPostView.swift; sourceTree = "<group>"; };
375A67E728FC555C007A1AC0 /* MultilineTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultilineTextView.swift; sourceTree = "<group>"; };
376A350C2B5D5C8E00255D61 /* WFNavigation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WFNavigation.swift; sourceTree = "<group>"; };
3779389629EC0C880032D6C1 /* HelpCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelpCommands.swift; sourceTree = "<group>"; };
37F749D029B4D3090087F0BF /* SearchablePostListFilteredView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchablePostListFilteredView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -590,6 +593,7 @@
children = (
17DF328224C87D3300BCE2E3 /* ContentView.swift */,
37113EF82A98C10A00B36B98 /* NoSelectedPostView.swift */,
376A350C2B5D5C8E00255D61 /* WFNavigation.swift */,
);
path = Navigation;
sourceTree = "<group>";
@ -947,6 +951,7 @@
1756AE7724CB2EDD00FD7257 /* PostEditorView.swift in Sources */,
17DF32D524C8CA3400BCE2E3 /* PostStatusBadgeView.swift in Sources */,
37F749D129B4D3090087F0BF /* SearchablePostListFilteredView.swift in Sources */,
376A350D2B5D5C8E00255D61 /* WFNavigation.swift in Sources */,
17D435E824E3128F0036B539 /* PreferencesModel.swift in Sources */,
1756AE7A24CB65DF00FD7257 /* PostListView.swift in Sources */,
17B996D82502D23E0017B536 /* WFAPost+CoreDataClass.swift in Sources */,
@ -997,6 +1002,7 @@
1756AE7B24CB65DF00FD7257 /* PostListView.swift in Sources */,
1753F6AC24E431CC00309365 /* MacPreferencesView.swift in Sources */,
1756DC0424FEE18400207AB8 /* WFACollection+CoreDataProperties.swift in Sources */,
376A350E2B5D5C8E00255D61 /* WFNavigation.swift in Sources */,
17B996DB2502D23E0017B536 /* WFAPost+CoreDataProperties.swift in Sources */,
17BC618A25715318003363CA /* ActivePostToolbarView.swift in Sources */,
171BFDFB24D4AF8300888236 /* CollectionListView.swift in Sources */,
@ -1240,7 +1246,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "WriteFreely-MultiPlatform (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 708;
CURRENT_PROJECT_VERSION = 710;
DEVELOPMENT_TEAM = TPPAB4YBA6;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = iOS/Info.plist;
@ -1249,7 +1255,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.17;
MARKETING_VERSION = 1.0.18;
PRODUCT_BUNDLE_IDENTIFIER = "com.abunchtell.WriteFreely-MultiPlatform";
PRODUCT_NAME = "WriteFreely-MultiPlatform";
SDKROOT = iphoneos;
@ -1266,7 +1272,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "WriteFreely-MultiPlatform (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 708;
CURRENT_PROJECT_VERSION = 710;
DEVELOPMENT_TEAM = TPPAB4YBA6;
ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = iOS/Info.plist;
@ -1275,7 +1281,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0.17;
MARKETING_VERSION = 1.0.18;
PRODUCT_BUNDLE_IDENTIFIER = "com.abunchtell.WriteFreely-MultiPlatform";
PRODUCT_NAME = "WriteFreely-MultiPlatform";
SDKROOT = iphoneos;
@ -1303,7 +1309,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.0;
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.abunchtell.WriteFreely-MultiPlatform";
PRODUCT_NAME = "WriteFreely for Mac";
@ -1330,7 +1336,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.0;
MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.0.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.abunchtell.WriteFreely-MultiPlatform";
PRODUCT_NAME = "WriteFreely for Mac";
@ -1503,7 +1509,7 @@
repositoryURL = "https://github.com/sparkle-project/Sparkle";
requirement = {
kind = upToNextMinorVersion;
minimumVersion = 2.3.0;
minimumVersion = 2.6.2;
};
};
/* End XCRemoteSwiftPackageReference section */