From 58ad4d47fd37a6857eeefd8edf10857cf4eae264 Mon Sep 17 00:00:00 2001 From: Oschly Date: Sun, 5 Oct 2025 23:23:19 +0200 Subject: [PATCH] Refactor project, add loading/editing/saving notes, add client/server basic logic --- Peered/ContentView.swift | 7 ++- .../NoteEditor/NoteEditingSessionServer.swift | 37 +++++++++++++ Peered/NoteEditor/NoteEditorScreen.swift | 26 ++++++++- .../NotesList/NoteEditingSessionClient.swift | 53 +++++++++++++++++++ Peered/NotesList/NotesStorage.swift | 11 +++- 5 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 Peered/NoteEditor/NoteEditingSessionServer.swift create mode 100644 Peered/NotesList/NoteEditingSessionClient.swift diff --git a/Peered/ContentView.swift b/Peered/ContentView.swift index cbd7878..a4c00ec 100644 --- a/Peered/ContentView.swift +++ b/Peered/ContentView.swift @@ -10,12 +10,13 @@ import SwiftUI struct ContentView: View { @AppStorage("peered_username") private var username: String = "" @State private var notes = [Note]() + @State private var notesClient: NoteEditingSessionClient? var body: some View { NavigationView { List(notes) { note in NavigationLink(note.name) { - NoteEditorScreen(note: note) + NoteEditorScreen(note: note, username: username) } } .navigationTitle("Peered") @@ -28,6 +29,10 @@ struct ContentView: View { } .onAppear { notes = NotesStorage().loadNotes() + if notesClient == nil { + notesClient = .init(id: username) + } + notesClient?.startBrowsingForNotes() } .sheet(isPresented: .constant(username.isEmpty)) { SetUserNameBottomSheetView(username: $username) diff --git a/Peered/NoteEditor/NoteEditingSessionServer.swift b/Peered/NoteEditor/NoteEditingSessionServer.swift new file mode 100644 index 0000000..9631462 --- /dev/null +++ b/Peered/NoteEditor/NoteEditingSessionServer.swift @@ -0,0 +1,37 @@ +import MultipeerConnectivity +import Foundation + +@Observable +final class NoteEditingSessionServer: NSObject { + private let session: MCSession + private let browser: MCNearbyServiceBrowser + + init(username: String) { + let peer = MCPeerID(displayName: username) + browser = .init(peer: peer, serviceType: "peered") + session = .init(peer: peer) + super.init() + browser.delegate = self + } + + func startServer() { + browser.startBrowsingForPeers() + } + + func stopServer() { + browser.stopBrowsingForPeers() + } +} + +extension NoteEditingSessionServer: MCNearbyServiceBrowserDelegate { + func browser( + _ browser: MCNearbyServiceBrowser, + foundPeer peerID: MCPeerID, + withDiscoveryInfo info: [String : String]? + ) { + browser.invitePeer(peerID, to: session, withContext: nil, timeout: 30) + print("seeing peer \(peerID.displayName)") + } + + func browser(_ browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID) {} +} diff --git a/Peered/NoteEditor/NoteEditorScreen.swift b/Peered/NoteEditor/NoteEditorScreen.swift index 03bbc38..9d48867 100644 --- a/Peered/NoteEditor/NoteEditorScreen.swift +++ b/Peered/NoteEditor/NoteEditorScreen.swift @@ -9,8 +9,32 @@ import SwiftUI struct NoteEditorScreen: View { let note: Note + @State private var noteAdvertiser: NoteEditingSessionServer + @State private var noteContent: String = "" + @State private var timer = Timer.publish(every: 5, on: .current, in: .common) + .autoconnect() + + init(note: Note, username: String) { + self.note = note + self._noteAdvertiser = .init(wrappedValue: .init(username: username)) + } var body: some View { - TextEditor(text: .constant("eee")) + TextEditor(text: $noteContent) + .onAppear { + noteContent = try! String(contentsOf: note.path, encoding: .utf8) + noteAdvertiser.startServer() + } + .onDisappear { + noteAdvertiser.stopServer() + saveNote() + } + .onReceive(timer) { _ in + saveNote() + } + } + + func saveNote() { + try! noteContent.write(to: note.path, atomically: true, encoding: .utf8) } } diff --git a/Peered/NotesList/NoteEditingSessionClient.swift b/Peered/NotesList/NoteEditingSessionClient.swift new file mode 100644 index 0000000..13b54da --- /dev/null +++ b/Peered/NotesList/NoteEditingSessionClient.swift @@ -0,0 +1,53 @@ +// +// NoteEditingSessionClient.swift +// Peered +// +// Created by Oskar Chybowski on 05/10/2025. +// + + +import MultipeerConnectivity +import Foundation + +@Observable +final class NoteEditingSessionClient: NSObject { + private let session: MCSession + private let advertiser: MCNearbyServiceAdvertiser + + init(id: String) { + let id = MCPeerID(displayName: id) + session = MCSession( + peer: id, + securityIdentity: nil, + encryptionPreference: .required + ) + advertiser = MCNearbyServiceAdvertiser( + peer: id, + discoveryInfo: [:], + serviceType: "peered" + ) + super.init() + advertiser.delegate = self + } + + func startBrowsingForNotes() { + advertiser.startAdvertisingPeer() + } + + func stopBrowsingForNotes() { + advertiser.stopAdvertisingPeer() + } +} + +extension NoteEditingSessionClient: MCNearbyServiceAdvertiserDelegate { + func advertiser( + _ advertiser: MCNearbyServiceAdvertiser, + didReceiveInvitationFromPeer peerID: MCPeerID, + withContext context: Data?, + invitationHandler: @escaping (Bool, MCSession?) -> Void + ) { + DispatchQueue.main.async { + invitationHandler(true, self.session) + } + } +} diff --git a/Peered/NotesList/NotesStorage.swift b/Peered/NotesList/NotesStorage.swift index b71dd99..60a7f29 100644 --- a/Peered/NotesList/NotesStorage.swift +++ b/Peered/NotesList/NotesStorage.swift @@ -11,10 +11,17 @@ struct NotesStorage { let storageProvider: FileManager = FileManager.default func loadNotes() -> [Note] { - let files = try! storageProvider.contentsOfDirectory(atPath: URL.documentsDirectory.path) + let files = try! storageProvider + .contentsOfDirectory(atPath: URL.documentsDirectory.path) var notes = [Note]() - for file in files.compactMap(URL.init) { + for file in files.compactMap({ + URL( + filePath: $0, + directoryHint: .notDirectory, + relativeTo: URL.documentsDirectory + ) + }) { let name = file.lastPathComponent let note = Note( name: name,