Skip to content

Commit 53ab32b

Browse files
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
1 parent 664eb44 commit 53ab32b

File tree

7 files changed

+161
-86
lines changed

7 files changed

+161
-86
lines changed

Shared/Extensions/WriteFreelyModel+API.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,16 @@ extension WriteFreelyModel {
9494
}
9595

9696
if post.language == nil {
97-
if let languageCode = Locale.current.languageCode {
98-
post.language = languageCode
99-
post.rtl = Locale.characterDirection(forLanguage: languageCode) == .rightToLeft
97+
if #available(iOS 16, macOS 13, *) {
98+
if let languageCode = Locale.current.language.languageCode?.identifier {
99+
post.language = languageCode
100+
post.rtl = Locale.Language(identifier: languageCode).characterDirection == .rightToLeft
101+
}
102+
} else {
103+
if let languageCode = Locale.current.languageCode {
104+
post.language = languageCode
105+
post.rtl = Locale.characterDirection(forLanguage: languageCode) == .rightToLeft
106+
}
100107
}
101108
}
102109

Shared/Navigation/ContentView.swift

Lines changed: 73 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,62 +5,86 @@ struct ContentView: View {
55
@EnvironmentObject var errorHandling: ErrorHandling
66

77
var body: some View {
8-
NavigationView {
9-
#if os(macOS)
10-
CollectionListView()
11-
.withErrorHandling()
12-
.toolbar {
13-
Button(
14-
action: {
15-
NSApp.keyWindow?.contentViewController?.tryToPerform(
16-
#selector(NSSplitViewController.toggleSidebar(_:)), with: nil
8+
#if os(macOS)
9+
WFNavigation(
10+
collectionList: {
11+
CollectionListView()
12+
.withErrorHandling()
13+
.toolbar {
14+
if #available(macOS 13, *) {
15+
EmptyView()
16+
} else {
17+
Button(
18+
action: {
19+
NSApp.keyWindow?.contentViewController?.tryToPerform(
20+
#selector(NSSplitViewController.toggleSidebar(_:)), with: nil
21+
)
22+
},
23+
label: { Image(systemName: "sidebar.left") }
1724
)
18-
},
19-
label: { Image(systemName: "sidebar.left") }
20-
)
21-
.help("Toggle the sidebar's visibility.")
22-
Spacer()
23-
Button(action: {
24-
withAnimation {
25-
// Un-set the currently selected post
26-
self.model.selectedPost = nil
25+
.help("Toggle the sidebar's visibility.")
2726
}
28-
// Create the new-post managed object
29-
let managedPost = model.editor.generateNewLocalPost(withFont: model.preferences.font)
30-
withAnimation {
31-
DispatchQueue.main.async {
32-
// Load the new post in the editor
33-
self.model.selectedPost = managedPost
27+
Spacer()
28+
Button(action: {
29+
withAnimation {
30+
// Un-set the currently selected post
31+
self.model.selectedPost = nil
32+
}
33+
// Create the new-post managed object
34+
let managedPost = model.editor.generateNewLocalPost(withFont: model.preferences.font)
35+
withAnimation {
36+
DispatchQueue.main.async {
37+
// Load the new post in the editor
38+
self.model.selectedPost = managedPost
39+
}
3440
}
41+
}, label: { Image(systemName: "square.and.pencil") })
42+
.help("Create a new local draft.")
43+
}
44+
.frame(width: 200)
45+
},
46+
postList: {
47+
ZStack {
48+
PostListView(selectedCollection: model.selectedCollection, showAllPosts: model.showAllPosts)
49+
.withErrorHandling()
50+
.frame(width: 300)
51+
if model.isProcessingRequest {
52+
ZStack {
53+
Color(NSColor.controlBackgroundColor).opacity(0.75)
54+
ProgressView()
3555
}
36-
}, label: { Image(systemName: "square.and.pencil") })
37-
.help("Create a new local draft.")
38-
}
39-
.frame(width: 200)
40-
#else
41-
CollectionListView()
42-
.withErrorHandling()
43-
#endif
44-
45-
#if os(macOS)
46-
ZStack {
47-
PostListView(selectedCollection: model.selectedCollection, showAllPosts: model.showAllPosts)
48-
.withErrorHandling()
49-
.frame(width: 300)
50-
if model.isProcessingRequest {
51-
ZStack {
52-
Color(NSColor.controlBackgroundColor).opacity(0.75)
53-
ProgressView()
5456
}
5557
}
58+
},
59+
postDetail: {
60+
NoSelectedPostView(isConnected: $model.hasNetworkConnection)
61+
}
62+
)
63+
.environmentObject(model)
64+
.onChange(of: model.hasError) { value in
65+
if value {
66+
if let error = model.currentError {
67+
self.errorHandling.handle(error: error)
68+
} else {
69+
self.errorHandling.handle(error: AppError.genericError())
70+
}
71+
model.hasError = false
5672
}
57-
#else
58-
PostListView(selectedCollection: model.selectedCollection, showAllPosts: model.showAllPosts)
59-
.withErrorHandling()
60-
#endif
61-
62-
NoSelectedPostView(isConnected: $model.hasNetworkConnection)
6373
}
74+
#else
75+
WFNavigation(
76+
collectionList: {
77+
CollectionListView()
78+
.withErrorHandling()
79+
},
80+
postList: {
81+
PostListView(selectedCollection: model.selectedCollection, showAllPosts: model.showAllPosts)
82+
.withErrorHandling()
83+
},
84+
postDetail: {
85+
NoSelectedPostView(isConnected: $model.hasNetworkConnection)
86+
}
87+
)
6488
.environmentObject(model)
6589
.onChange(of: model.hasError) { value in
6690
if value {
@@ -72,6 +96,7 @@ struct ContentView: View {
7296
model.hasError = false
7397
}
7498
}
99+
#endif
75100
}
76101
}
77102

Shared/Navigation/WFNavigation.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import SwiftUI
2+
3+
struct WFNavigation<CollectionList, PostList, PostDetail>: View
4+
where CollectionList: View, PostList: View, PostDetail: View {
5+
6+
private var collectionList: CollectionList
7+
private var postList: PostList
8+
private var postDetail: PostDetail
9+
10+
init(
11+
@ViewBuilder collectionList: () -> CollectionList,
12+
@ViewBuilder postList: () -> PostList,
13+
@ViewBuilder postDetail: () -> PostDetail
14+
) {
15+
self.collectionList = collectionList()
16+
self.postList = postList()
17+
self.postDetail = postDetail()
18+
}
19+
20+
var body: some View {
21+
#if os(macOS)
22+
NavigationSplitView {
23+
collectionList
24+
} content: {
25+
postList
26+
} detail: {
27+
postDetail
28+
}
29+
#else
30+
NavigationView {
31+
collectionList
32+
postList
33+
postDetail
34+
}
35+
#endif
36+
}
37+
}

Shared/PostEditor/PostEditorModel.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,16 @@ struct PostEditorModel {
4848
default:
4949
managedPost.appearance = "serif"
5050
}
51-
if let languageCode = Locale.current.languageCode {
52-
managedPost.language = languageCode
53-
managedPost.rtl = Locale.characterDirection(forLanguage: languageCode) == .rightToLeft
51+
if #available(iOS 16, macOS 13, *) {
52+
if let languageCode = Locale.current.language.languageCode?.identifier {
53+
managedPost.language = languageCode
54+
managedPost.rtl = Locale.Language(identifier: languageCode).characterDirection == .rightToLeft
55+
}
56+
} else {
57+
if let languageCode = Locale.current.languageCode {
58+
managedPost.language = languageCode
59+
managedPost.rtl = Locale.characterDirection(forLanguage: languageCode) == .rightToLeft
60+
}
5461
}
5562
return managedPost
5663
}

Shared/PostList/PostListView.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -126,18 +126,18 @@ struct PostListView: View {
126126
.frame(height: frameHeight)
127127
.background(Color(UIColor.systemGray5))
128128
.overlay(Divider(), alignment: .top)
129-
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in
130-
// We use this to invalidate and refresh the view, so that new posts created outside of the app (e.g.,
131-
// in the action extension) show up.
132-
withAnimation {
133-
self.filteredListViewId += 1
134-
}
135-
}
136129
}
137130
.ignoresSafeArea(.all, edges: .bottom)
138131
.onAppear {
132+
// Set the selected collection and whether or not we want to show all posts
139133
model.selectedCollection = selectedCollection
140134
model.showAllPosts = showAllPosts
135+
136+
// We use this to invalidate and refresh the view, so that new posts created outside of the app (e.g.,
137+
// in the action extension) show up.
138+
withAnimation {
139+
self.filteredListViewId += 1
140+
}
141141
}
142142
.onChange(of: model.hasError) { value in
143143
if value {

Technotes/MacSoftwareUpdater.md

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,6 @@
22

33
To make updating the Mac app easy, we're using the [Sparkle framework][1].
44

5-
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
6-
SPM — the dependency can only be added from a branch or commit. To avoid any surprises arising from updates to the project's `master`
7-
branch, we're using [WriteFreely's fork of Sparkle][2]. Updates to the forked repository from upstream should be considered dangerous and
8-
tested thoroughly before merging into `main`.
9-
10-
WriteFreely for Mac uses the v1.x branch of Sparkle, and is therefore not a sandboxed app.
11-
125
## Troubleshooting
136

147
### 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.
2215

2316
### If you can't run `generate_keys` because "Apple cannot check it for malicious software"
2417

25-
There may be a code signing issue with Sparkle. Right-click on `generate_keys` in the Finder and choose Open ([reference][3]).
18+
If you run into a code signing issue with Sparkle, right-click on `generate_keys` in the Finder and choose Open ([reference][2]).
2619

2720
## Deploying Updates
2821

29-
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
30-
`generate_appcast` tool. Download and de-compress the archive.
22+
To [publish an update to the app][4], you'll need the **Sparkle-for-Swift-Package-Manager.zip** [archive][3] — 
23+
specifically, you'll need the `generate_appcast` tool. Download and de-compress the archive.
3124

32-
You will need some credentials and signing certificates to proceed with this process; speak to the project maintainer if you're responsible for
33-
creating the update, and confirm you have:
25+
You will need some credentials and signing certificates to proceed with this process; speak to the project maintainer if
26+
you're responsible for creating the update, and confirm you have:
3427

3528
- the app's Developer ID Application certificate (check your Mac's system Keychain)
3629
- the Sparkle EdDSA signing key (again, check your Mac's system Keychain)
3730

38-
Sign and notarize the app archive, then click on **Export Notarized App** in Xcode's Organizer window. Open the Terminal and navigate to
39-
where you de-compressed the Sparkle-for-Swift-Package-Manager archive, then create a zip file that preserves symlinks:
31+
Sign and notarize the app archive, then click on **Export Notarized App** in Xcode's Organizer window. Open the Terminal
32+
and navigate to where you de-compressed the Sparkle-for-Swift-Package-Manager archive, then create a zip file that
33+
preserves symlinks:
4034

4135
```bash
4236
% ditto -c -k --sequesterRsrc --keepParent <source_path_to_app> <zip_destination>
@@ -60,7 +54,6 @@ and they'll be made available to users.
6054

6155
<!--references-->
6256
[1]: https://sparkle-project.org
63-
[2]: https://github.com/writefreely/Sparkle
64-
[3]: https://github.com/sparkle-project/Sparkle/issues/1701#issuecomment-752249920
65-
[4]: https://github.com/sparkle-project/Sparkle/releases/tag/1.24.0
66-
[5]: https://sparkle-project.org/documentation/publishing/
57+
[2]: https://github.com/sparkle-project/Sparkle/issues/1701#issuecomment-752249920
58+
[3]: https://github.com/sparkle-project/Sparkle/releases/tag/1.24.0
59+
[4]: https://sparkle-project.org/documentation/publishing/

0 commit comments

Comments
 (0)