This commit tries to replace all usage of `String` to represent relay URLs and use `RelayURL` which automatically converts strings to a canonical relay URL format that is more reliable and avoids issues related to trailing slashes. Test 1: Main issue fix ----------------------- PASS Device: iPhone 15 Simulator iOS: 17.4 Damus: This commit Steps: 1. Delete all connected relays 2. Add `wss://relay.damus.io/` (with the trailing slash) to the relay list 3. Try to post. Post should succeed. PASS 4. Try removing this newly added relay. Relay should be removed successfully. PASS Test 2: Persistent relay list after upgrade -------------------------------------------- PASS Device: iPhone 15 Simulator iOS: 17.4 Damus: 1.8 (1) `247f313b` + This commit Steps: 1. Downgrade to old version 2. Add some relays to the list, some without a trailing slash, some with 3. Upgrade to this commit 4. All relays added in step 2 should still be there, and ones with a trailing slash should have been corrected to remove the trailing slash Test 3: Miscellaneous regression tests -------------------------------------- Device: iPhone 15 Simulator iOS: 17.4 Damus: This commit Coverage: 1. Posting works 2. Search works 3. Relay connection status works 4. Adding relays work 5. Removing relays work 6. Adding relay with trailing slashes works (it fixes itself to remove the trailing slash) 7. Adding relays with different paths works (e.g. wss://yabu.me/v1 and wss://yabu.me/v2) 8. Adding duplicate relay (but with trailing slash) gets rejected as expected 9. Relay details page works. All items on that view loads correctly 10. Relay logs work 11. Getting follower counts and seeing follow lists on profiles still work 12. Relay list changes persist after app restart 13. Notifications view still work 14. Copying the user's pubkey and profile link works 15. Share note + copy link button still works 16. Connecting NWC wallet works 17. One-tap zaps work 18. Onboarding works 19. Unit tests all passing Closes: https://github.com/damus-io/damus/issues/2072 Changelog-Fixed: Fix bug that would cause connection issues with relays defined with a trailing slash URL, and an inability to delete them. Signed-off-by: Daniel D’Aquino <daniel@daquino.me> Signed-off-by: William Casarin <jb55@jb55.com>
147 lines
4.7 KiB
Swift
147 lines
4.7 KiB
Swift
//
|
|
// RelayLog.swift
|
|
// damus
|
|
//
|
|
// Created by Bryan Montz on 6/1/23.
|
|
//
|
|
|
|
import Combine
|
|
import Foundation
|
|
import UIKit
|
|
|
|
/// Stores a running list of events and state changes related to a relay, so that users
|
|
/// will have information to help developers debug issues.
|
|
final class RelayLog: ObservableObject {
|
|
private static let line_limit = 250
|
|
private let relay_url: RelayURL?
|
|
private lazy var formatter: DateFormatter = {
|
|
let formatter = DateFormatter()
|
|
formatter.dateStyle = .short
|
|
formatter.timeStyle = .medium
|
|
return formatter
|
|
}()
|
|
|
|
private(set) var lines = [String]()
|
|
|
|
private var notification_token: AnyCancellable?
|
|
|
|
/// Creates a RelayLog
|
|
/// - Parameter relay_url: the relay url the log represents. Pass nil for the url to create
|
|
/// a RelayLog that does nothing. This is required to allow RelayLog to be used as a StateObject,
|
|
/// because they cannot be Optional.
|
|
init(_ relay_url: RelayURL? = nil) {
|
|
self.relay_url = relay_url
|
|
|
|
setUp()
|
|
}
|
|
|
|
private var log_files_directory: URL {
|
|
FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!.appendingPathComponent("RelayLogs", isDirectory: true)
|
|
}
|
|
|
|
private var log_file_url: URL? {
|
|
guard let file_name = relay_url?.absoluteString.data(using: .utf8) else {
|
|
return nil
|
|
}
|
|
return log_files_directory.appendingPathComponent(file_name.base64EncodedString())
|
|
}
|
|
|
|
/// Sets up the log file and prepares to listen to app state changes
|
|
private func setUp() {
|
|
guard let log_file_url else {
|
|
return
|
|
}
|
|
|
|
try? FileManager.default.createDirectory(at: log_files_directory, withIntermediateDirectories: false)
|
|
|
|
if !FileManager.default.fileExists(atPath: log_file_url.path) {
|
|
// create the log file if it doesn't exist yet
|
|
FileManager.default.createFile(atPath: log_file_url.path, contents: nil)
|
|
} else {
|
|
// otherwise load it into memory
|
|
readFromDisk()
|
|
}
|
|
|
|
let willResignPublisher = NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)
|
|
let willTerminatePublisher = NotificationCenter.default.publisher(for: UIApplication.willTerminateNotification)
|
|
notification_token = Publishers.Merge(willResignPublisher, willTerminatePublisher)
|
|
.sink { [weak self] _ in
|
|
self?.writeToDisk()
|
|
}
|
|
}
|
|
|
|
/// The current contents of the log
|
|
var contents: String? {
|
|
guard !lines.isEmpty else {
|
|
return nil
|
|
}
|
|
return lines.joined(separator: "\n")
|
|
}
|
|
|
|
/// Adds content to the log
|
|
/// - Parameter content: what to add to the log. The date and time are prepended to the content.
|
|
func add(_ content: String) {
|
|
Task {
|
|
await addLine(content)
|
|
await publishChanges()
|
|
}
|
|
}
|
|
|
|
@MainActor private func addLine(_ line: String) {
|
|
let line = "\(formatter.string(from: .now)) - \(line)"
|
|
lines.insert(line, at: 0)
|
|
truncateLines()
|
|
}
|
|
|
|
/// Tells views that our log has been updated
|
|
@MainActor private func publishChanges() {
|
|
objectWillChange.send()
|
|
}
|
|
|
|
private func truncateLines() {
|
|
lines = Array(lines.prefix(RelayLog.line_limit))
|
|
}
|
|
|
|
/// Reads the contents of the log file from disk into memory
|
|
private func readFromDisk() {
|
|
guard let log_file_url else {
|
|
return
|
|
}
|
|
|
|
do {
|
|
let handle = try FileHandle(forReadingFrom: log_file_url)
|
|
let data = try handle.readToEnd()
|
|
try handle.close()
|
|
|
|
guard let data, let content = String(data: data, encoding: .utf8) else {
|
|
return
|
|
}
|
|
|
|
lines = content.components(separatedBy: "\n")
|
|
|
|
truncateLines()
|
|
} catch {
|
|
print("⚠️ Warning: RelayLog failed to read from \(log_file_url)")
|
|
}
|
|
}
|
|
|
|
/// Writes the contents of the lines in memory to disk
|
|
private func writeToDisk() {
|
|
guard let log_file_url, let relay_url,
|
|
!lines.isEmpty,
|
|
let content = lines.joined(separator: "\n").data(using: .utf8) else {
|
|
return
|
|
}
|
|
|
|
do {
|
|
let handle = try FileHandle(forWritingTo: log_file_url)
|
|
|
|
try handle.truncate(atOffset: 0)
|
|
try handle.write(contentsOf: content)
|
|
try handle.close()
|
|
} catch {
|
|
print("⚠️ Warning: RelayLog(\(relay_url)) failed to write to file: \(error)")
|
|
}
|
|
}
|
|
}
|