mirror of
https://github.com/writeas/writefreely-swiftui-multiplatform.git
synced 2024-11-14 17:01:01 +00:00
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:
parent
664eb44882
commit
53ab32b7a3
@ -94,9 +94,16 @@ extension WriteFreelyModel {
|
||||
}
|
||||
|
||||
if post.language == nil {
|
||||
if let languageCode = Locale.current.languageCode {
|
||||
post.language = languageCode
|
||||
post.rtl = Locale.characterDirection(forLanguage: languageCode) == .rightToLeft
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,62 +5,61 @@ struct ContentView: View {
|
||||
@EnvironmentObject var errorHandling: ErrorHandling
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
#if os(macOS)
|
||||
CollectionListView()
|
||||
.withErrorHandling()
|
||||
.toolbar {
|
||||
Button(
|
||||
action: {
|
||||
NSApp.keyWindow?.contentViewController?.tryToPerform(
|
||||
#selector(NSSplitViewController.toggleSidebar(_:)), with: nil
|
||||
)
|
||||
},
|
||||
label: { Image(systemName: "sidebar.left") }
|
||||
)
|
||||
.help("Toggle the sidebar's visibility.")
|
||||
Spacer()
|
||||
Button(action: {
|
||||
withAnimation {
|
||||
// Un-set the currently selected post
|
||||
self.model.selectedPost = nil
|
||||
}
|
||||
// Create the new-post managed object
|
||||
let managedPost = model.editor.generateNewLocalPost(withFont: model.preferences.font)
|
||||
withAnimation {
|
||||
DispatchQueue.main.async {
|
||||
// Load the new post in the editor
|
||||
self.model.selectedPost = managedPost
|
||||
}
|
||||
}
|
||||
}, label: { Image(systemName: "square.and.pencil") })
|
||||
.help("Create a new local draft.")
|
||||
}
|
||||
.frame(width: 200)
|
||||
#else
|
||||
CollectionListView()
|
||||
.withErrorHandling()
|
||||
#endif
|
||||
|
||||
#if os(macOS)
|
||||
ZStack {
|
||||
PostListView(selectedCollection: model.selectedCollection, showAllPosts: model.showAllPosts)
|
||||
#if os(macOS)
|
||||
WFNavigation(
|
||||
collectionList: {
|
||||
CollectionListView()
|
||||
.withErrorHandling()
|
||||
.frame(width: 300)
|
||||
if model.isProcessingRequest {
|
||||
ZStack {
|
||||
Color(NSColor.controlBackgroundColor).opacity(0.75)
|
||||
ProgressView()
|
||||
.toolbar {
|
||||
if #available(macOS 13, *) {
|
||||
EmptyView()
|
||||
} else {
|
||||
Button(
|
||||
action: {
|
||||
NSApp.keyWindow?.contentViewController?.tryToPerform(
|
||||
#selector(NSSplitViewController.toggleSidebar(_:)), with: nil
|
||||
)
|
||||
},
|
||||
label: { Image(systemName: "sidebar.left") }
|
||||
)
|
||||
.help("Toggle the sidebar's visibility.")
|
||||
}
|
||||
Spacer()
|
||||
Button(action: {
|
||||
withAnimation {
|
||||
// Un-set the currently selected post
|
||||
self.model.selectedPost = nil
|
||||
}
|
||||
// Create the new-post managed object
|
||||
let managedPost = model.editor.generateNewLocalPost(withFont: model.preferences.font)
|
||||
withAnimation {
|
||||
DispatchQueue.main.async {
|
||||
// Load the new post in the editor
|
||||
self.model.selectedPost = managedPost
|
||||
}
|
||||
}
|
||||
}, label: { Image(systemName: "square.and.pencil") })
|
||||
.help("Create a new local draft.")
|
||||
}
|
||||
.frame(width: 200)
|
||||
},
|
||||
postList: {
|
||||
ZStack {
|
||||
PostListView(selectedCollection: model.selectedCollection, showAllPosts: model.showAllPosts)
|
||||
.withErrorHandling()
|
||||
.frame(width: 300)
|
||||
if model.isProcessingRequest {
|
||||
ZStack {
|
||||
Color(NSColor.controlBackgroundColor).opacity(0.75)
|
||||
ProgressView()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
postDetail: {
|
||||
NoSelectedPostView(isConnected: $model.hasNetworkConnection)
|
||||
}
|
||||
#else
|
||||
PostListView(selectedCollection: model.selectedCollection, showAllPosts: model.showAllPosts)
|
||||
.withErrorHandling()
|
||||
#endif
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
37
Shared/Navigation/WFNavigation.swift
Normal file
37
Shared/Navigation/WFNavigation.swift
Normal 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
|
||||
}
|
||||
}
|
@ -48,9 +48,16 @@ struct PostEditorModel {
|
||||
default:
|
||||
managedPost.appearance = "serif"
|
||||
}
|
||||
if let languageCode = Locale.current.languageCode {
|
||||
managedPost.language = languageCode
|
||||
managedPost.rtl = Locale.characterDirection(forLanguage: languageCode) == .rightToLeft
|
||||
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
|
||||
}
|
||||
|
@ -126,18 +126,18 @@ struct PostListView: View {
|
||||
.frame(height: frameHeight)
|
||||
.background(Color(UIColor.systemGray5))
|
||||
.overlay(Divider(), alignment: .top)
|
||||
.onReceive(NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)) { _ in
|
||||
// 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 {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
.onChange(of: model.hasError) { value in
|
||||
if value {
|
||||
|
@ -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/
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user