Skip to content

Commit 9f57866

Browse files
committed
Harden crash-prone code paths
1 parent 0a53394 commit 9f57866

23 files changed

Lines changed: 356 additions & 146 deletions

Planet/API/PlanetAPIController.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -475,12 +475,15 @@ class PlanetAPIController: NSObject, ObservableObject {
475475
}
476476
return ""
477477
}()
478-
let planetTemplateName: String = {
478+
let planetTemplateName: String = try {
479479
let template: String = p.template ?? ""
480480
if TemplateStore.shared.hasTemplate(named: template) {
481481
return template
482482
}
483-
return TemplateStore.shared.templates.first!.name
483+
guard let defaultTemplate = TemplateStore.shared.templates.first else {
484+
throw Abort(.internalServerError, reason: "No templates are available.")
485+
}
486+
return defaultTemplate.name
484487
}()
485488
let planet = try await MyPlanetModel.create(
486489
name: planetName,

Planet/Downloads/PlanetDownloadsWebView.swift

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -71,40 +71,29 @@ class PlanetDownloadsWebView: WKWebView {
7171
return true
7272
}
7373
}
74-
return DownloadsScriptMessageHandler.instance.href == nil && DownloadsScriptMessageHandler.instance.src == nil
74+
return !DownloadsScriptMessageHandler.instance.hasSelectedURL
7575
}
7676

7777
@objc private func openLinkAction(_ sender: NSMenuItem) {
78-
guard let urlString = DownloadsScriptMessageHandler.instance.href else { return }
79-
if urlString.hasPrefix("file:///") {
80-
let targetURL = URL(fileURLWithPath: urlString)
81-
ArticleWebViewModel.shared.processInternalFileLink(targetURL)
78+
guard let url = DownloadsScriptMessageHandler.instance.hrefURL else { return }
79+
if url.isFileURL {
80+
ArticleWebViewModel.shared.processInternalFileLink(url)
8281
}
83-
if let url = URL(string: urlString) {
84-
ArticleWebViewModel.shared.processPossibleInternalLink(url)
85-
if !ArticleWebViewModel.shared.checkInternalLink(url) {
86-
NSWorkspace.shared.open(url)
87-
}
82+
ArticleWebViewModel.shared.processPossibleInternalLink(url)
83+
if !ArticleWebViewModel.shared.checkInternalLink(url) {
84+
NSWorkspace.shared.open(url)
8885
}
8986
}
9087

9188
@objc private func downloadFileAction(_ sender: NSMenuItem) {
92-
if let _ = DownloadsScriptMessageHandler.instance.href, let srcString = DownloadsScriptMessageHandler.instance.src {
93-
self.load(URLRequest(url: URL(string: srcString)!))
94-
} else if let urlString = DownloadsScriptMessageHandler.instance.href {
95-
self.load(URLRequest(url: URL(string: urlString)!))
96-
} else if let srcString = DownloadsScriptMessageHandler.instance.src {
97-
self.load(URLRequest(url: URL(string: srcString)!))
89+
if let url = DownloadsScriptMessageHandler.instance.selectedSourceURL {
90+
self.load(URLRequest(url: url))
9891
}
9992
}
10093

10194
@objc private func openImageAction(_ sender: NSMenuItem) {
102-
if let _ = DownloadsScriptMessageHandler.instance.href, let srcString = DownloadsScriptMessageHandler.instance.src {
103-
NSWorkspace.shared.open(URL(string: srcString)!)
104-
} else if let urlString = DownloadsScriptMessageHandler.instance.href {
105-
NSWorkspace.shared.open(URL(string: urlString)!)
106-
} else if let srcString = DownloadsScriptMessageHandler.instance.src {
107-
NSWorkspace.shared.open(URL(string: srcString)!)
95+
if let url = DownloadsScriptMessageHandler.instance.selectedSourceURL {
96+
NSWorkspace.shared.open(url)
10897
}
10998
}
11099

Planet/Entities/FollowingPlanetModel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class FollowingPlanetModel: Equatable, Hashable, Identifiable, ObservableObject,
6060

6161
static func followingPlanetsPath() -> URL {
6262
let url = URLUtils.repoPath().appendingPathComponent("Following", isDirectory: true)
63-
try! FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
63+
try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
6464
return url
6565
}
6666
var basePath: URL {

Planet/Entities/MyPlanetModel.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ class MyPlanetModel: Equatable, Hashable, Identifiable, ObservableObject, Codabl
148148

149149
static func myPlanetsPath() -> URL {
150150
let url = URLUtils.repoPath().appendingPathComponent("My", isDirectory: true)
151-
try! FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
151+
try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
152152
return url
153153
}
154154
static func isReservedTag(_ tag: String) -> Bool {
@@ -271,7 +271,7 @@ class MyPlanetModel: Equatable, Hashable, Identifiable, ObservableObject, Codabl
271271

272272
static func publicPlanetsPath() -> URL {
273273
let url = URLUtils.repoPath().appendingPathComponent("Public", isDirectory: true)
274-
try! FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
274+
try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
275275
return url
276276
}
277277
var publicBasePath: URL {

Planet/Entities/PlanetStore.swift

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,10 @@ final class MyJSONDirectoryMonitor {
346346
do {
347347
try load()
348348
} catch {
349-
fatalError("Error when accessing planet repo: \(error)")
349+
logger.error("Error when accessing planet repo: \(error.localizedDescription, privacy: .public)")
350+
alertTitle = L10n("Failed to Load Planet Library")
351+
alertMessage = error.localizedDescription
352+
isShowingAlert = true
350353
}
351354
rebuildSearchSnapshots()
352355

@@ -1044,7 +1047,6 @@ final class MyJSONDirectoryMonitor {
10441047
return a.id != article.id
10451048
})
10461049
let articleIDString: String = article.id.uuidString
1047-
let fromPlanetIDString: String = fromPlanet.id.uuidString
10481050
let toPlanetIDString: String = toPlanet.id.uuidString
10491051
let fromArticlePath = article.path
10501052
let targetArticlePath = fromArticlePath.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent().appendingPathComponent(toPlanetIDString).appendingPathComponent("Articles").appendingPathComponent("\(articleIDString).json")
@@ -1081,11 +1083,20 @@ final class MyJSONDirectoryMonitor {
10811083
movedArticle.planet = toPlanet
10821084
movedArticle.draft = nil
10831085

1084-
movedArticle.path = URL(string: article.path.absoluteString.replacingOccurrences(of: fromPlanetIDString, with: toPlanetIDString))!
1085-
movedArticle.publicBasePath = URL(string: article.publicBasePath.absoluteString.replacingOccurrences(of: fromPlanetIDString, with: toPlanetIDString))!
1086-
movedArticle.publicIndexPath = URL(string: article.publicIndexPath.absoluteString.replacingOccurrences(of: fromPlanetIDString, with: toPlanetIDString))!
1087-
movedArticle.publicInfoPath = URL(string: article.publicInfoPath.absoluteString.replacingOccurrences(of: fromPlanetIDString, with: toPlanetIDString))!
1088-
movedArticle.publicNFTMetadataPath = URL(string: article.publicNFTMetadataPath.absoluteString.replacingOccurrences(of: fromPlanetIDString, with: toPlanetIDString))!
1086+
movedArticle.path = targetArticlePath
1087+
movedArticle.publicBasePath = targetArticlePublicPath
1088+
movedArticle.publicIndexPath = targetArticlePublicPath.appendingPathComponent(
1089+
"index.html",
1090+
isDirectory: false
1091+
)
1092+
movedArticle.publicInfoPath = targetArticlePublicPath.appendingPathComponent(
1093+
"article.json",
1094+
isDirectory: false
1095+
)
1096+
movedArticle.publicNFTMetadataPath = targetArticlePublicPath.appendingPathComponent(
1097+
"nft.json",
1098+
isDirectory: false
1099+
)
10891100

10901101
toPlanet.articles.append(movedArticle)
10911102
toPlanet.articles = toPlanet.articles.sorted(by: { $0.created > $1.created })

Planet/Helper/DotBitKit.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class DotBitKit: NSObject {
4141
} catch {
4242
return nil
4343
}
44-
if !(response as! HTTPURLResponse).ok {
44+
guard let httpResponse = response as? HTTPURLResponse, httpResponse.ok else {
4545
return nil
4646
}
4747
if let json = try? JSON(data: data) {

Planet/Helper/URLUtils.swift

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,27 @@ import Foundation
1010
import ImageIO
1111

1212
struct URLUtils {
13-
static let applicationSupportPath = try! FileManager.default.url(
14-
for: .applicationSupportDirectory,
15-
in: .userDomainMask,
16-
appropriateFor: nil,
17-
create: true
18-
)
13+
private static func userDirectory(_ directory: FileManager.SearchPathDirectory) -> URL {
14+
do {
15+
return try FileManager.default.url(
16+
for: directory,
17+
in: .userDomainMask,
18+
appropriateFor: nil,
19+
create: true
20+
)
21+
}
22+
catch {
23+
debugPrint("Failed to resolve user directory \(directory): \(error)")
24+
return FileManager.default.urls(for: directory, in: .userDomainMask).first
25+
?? FileManager.default.temporaryDirectory
26+
}
27+
}
1928

20-
static let documentsPath = try! FileManager.default.url(
21-
for: .documentDirectory,
22-
in: .userDomainMask,
23-
appropriateFor: nil,
24-
create: true
25-
)
29+
static let applicationSupportPath = userDirectory(.applicationSupportDirectory)
2630

27-
static let cachesPath = try! FileManager.default.url(
28-
for: .cachesDirectory,
29-
in: .userDomainMask,
30-
appropriateFor: nil,
31-
create: true
32-
)
31+
static let documentsPath = userDirectory(.documentDirectory)
32+
33+
static let cachesPath = userDirectory(.cachesDirectory)
3334

3435
static let legacyPlanetsPath = applicationSupportPath.appendingPathComponent(
3536
"planets",
@@ -98,13 +99,13 @@ struct URLUtils {
9899

99100
static let defaultRepoPath: URL = {
100101
let url = Self.documentsPath.appendingPathComponent("Planet", isDirectory: true)
101-
try! FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
102+
try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
102103
return url
103104
}()
104105

105106
static let temporaryPath: URL = {
106107
let url = Self.cachesPath.appendingPathComponent("tmp", isDirectory: true)
107-
try! FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
108+
try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
108109
return url
109110
}()
110111
}

Planet/Helper/ViewUtils.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -282,14 +282,18 @@ struct ViewUtils {
282282

283283
static func getPresetGradient(from walletAddress: String) -> Gradient {
284284
let characters: [UInt8] = Array(walletAddress.utf8)
285-
let lastCharUInt8 = characters.last!
285+
guard let lastCharUInt8 = characters.last else {
286+
return presetGradients[0]
287+
}
286288
let index = Int(lastCharUInt8) % presetGradients.count
287289
return presetGradients[index]
288290
}
289291

290292
static func getEmoji(from walletAddress: String) -> String {
291293
let characters: [UInt8] = Array(walletAddress.utf8)
292-
let lastCharUInt8 = characters.last!
294+
guard let lastCharUInt8 = characters.last else {
295+
return emojiList[0]
296+
}
293297
let index = Int(lastCharUInt8) % emojiList.count
294298
return emojiList[index]
295299
}

Planet/Helper/WKScriptHelper.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,31 @@ class DownloadsScriptMessageHandler: NSObject, WKScriptMessageHandler {
2323
public private(set) var href: String?
2424
public private(set) var src: String?
2525

26+
var hrefURL: URL? {
27+
Self.url(from: href)
28+
}
29+
30+
var srcURL: URL? {
31+
Self.url(from: src)
32+
}
33+
34+
var selectedURL: URL? {
35+
hrefURL ?? srcURL
36+
}
37+
38+
var selectedSourceURL: URL? {
39+
srcURL ?? hrefURL
40+
}
41+
42+
var hasSelectedURL: Bool {
43+
selectedURL != nil
44+
}
45+
46+
private static func url(from string: String?) -> URL? {
47+
guard let string, !string.isEmpty else { return nil }
48+
return URL(string: string)
49+
}
50+
2651
static private var contextMenuScript = """
2752
window.oncontextmenu = (event) => {
2853
var target = event.target

Planet/IPFS/IPFSCommand.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ struct IPFSCommand {
2121
static let IPFSRepositoryPath: URL = {
2222
// ~/Library/Containers/xyz.planetable.Planet/Data/Library/Application\ Support/ipfs/
2323
let url = URLUtils.applicationSupportPath.appendingPathComponent("ipfs", isDirectory: true)
24-
try! FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
24+
try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
2525
return url
2626
}()
2727

0 commit comments

Comments
 (0)