Skip to content

Commit

Permalink
Merge branch 'main' into bugFix/toggle-bg-color
Browse files Browse the repository at this point in the history
  • Loading branch information
pelumy committed Sep 11, 2024
2 parents e70977d + 045b186 commit 89215d1
Show file tree
Hide file tree
Showing 19 changed files with 144 additions and 145 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fixed issue where relay metadata is never updated. [#1472](https://github.com/planetary-social/nos/issues/1472)
- Updated the copy on the 3 dots note menu. [#1028](https://github.com/planetary-social/nos/issues/1028)
- Added functionality to share notes link through the 3 dots note menu. [#1272](https://github.com/planetary-social/nos/issues/1272)
- Fixes and improvements related to Core Data usage. [#1443](https://github.com/planetary-social/nos/issues/1443)
- Fixed a bug where toggles in the settings screen were white instead of green when toggled on. [#1251](https://github.com/planetary-social/nos/issues/1251)

### Internal Changes
Expand Down
167 changes: 73 additions & 94 deletions Nos/Controller/PersistenceController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@ import CoreData
import Logger
import Dependencies

class PersistenceController {
final class PersistenceController {

@Dependency(\.currentUser) var currentUser
@Dependency(\.crashReporting) var crashReporting

/// Increment this to delete core data on update
static let version = 3
static let versionKey = "NosPersistenceControllerVersion"
private static let version = 3
private static let versionKey = "NosPersistenceControllerVersion"

static var preview: PersistenceController = {
let controller = PersistenceController(inMemory: true)
let viewContext = controller.container.viewContext
let viewContext = controller.viewContext
return controller
}()

static var empty: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
let viewContext = result.viewContext
return result
}()

Expand All @@ -28,22 +28,18 @@ class PersistenceController {
}

/// A context for parsing Nostr events from relays.
lazy var parseContext = {
newBackgroundContext()
}()
private(set) lazy var parseContext = newBackgroundContext()

/// A context for Views to do expensive queries that we want to keep off the viewContext.
lazy var backgroundViewContext = {
self.newBackgroundContext()
}()
private(set) lazy var backgroundViewContext = newBackgroundContext()

var sqliteURL: URL? {
container.persistentStoreDescriptions.first?.url
}

private(set) var container: NSPersistentContainer
private var model: NSManagedObjectModel
private var inMemory: Bool
private let model: NSManagedObjectModel
private let inMemory: Bool

init(containerName: String = "Nos", inMemory: Bool = false, erase: Bool = false) {
self.inMemory = inMemory
Expand All @@ -53,57 +49,19 @@ class PersistenceController {
setUp(erasingPrevious: erase)
}

func tearDown() throws {
for store in container.persistentStoreCoordinator.persistentStores {
try container.persistentStoreCoordinator.remove(store)
}

try container.persistentStoreDescriptions.forEach { storeDescription in
try container.persistentStoreCoordinator.destroyPersistentStore(
at: storeDescription.url!,
ofType: NSSQLiteStoreType,
options: nil
)
}

viewContext.reset()
backgroundViewContext.reset()
parseContext.reset()
}

func setUp(erasingPrevious: Bool) {
private func setUp(erasingPrevious: Bool) {
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}

loadPersistentStores(from: container, erasingPrevious: erasingPrevious)

container.viewContext.automaticallyMergesChangesFromParent = true
let mergeType = NSMergePolicyType.mergeByPropertyStoreTrumpMergePolicyType
container.viewContext.mergePolicy = NSMergePolicy(merge: mergeType)
}

#if DEBUG
func resetForTesting() {
container = NSPersistentContainer(name: "Nos", managedObjectModel: model)
if !inMemory {
container.loadPersistentStores(completionHandler: { (storeDescription, _) in
guard let storeURL = storeDescription.url else {
Log.error("Could not get store URL")
return
}
Self.clearCoreData(store: storeURL, in: self.container)
})
}
setUp(erasingPrevious: true)
viewContext.reset()
backgroundViewContext.reset()
parseContext.reset()
viewContext.automaticallyMergesChangesFromParent = true
viewContext.mergePolicy = NSMergePolicy.mergeByPropertyStoreTrump
}
#endif

private func loadPersistentStores(from container: NSPersistentContainer, erasingPrevious: Bool) {
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
container.loadPersistentStores { storeDescription, error in

// Drop database if necessary
if Self.loadVersionFromDisk() < Self.version || erasingPrevious {
Expand All @@ -124,7 +82,7 @@ class PersistenceController {
}
fatalError("Could not initialize database \(error), \(error.userInfo)")
}
})
}
}

@MainActor
Expand All @@ -135,7 +93,7 @@ class PersistenceController {
}
}

static func clearCoreData(store storeURL: URL, in container: NSPersistentContainer) {
private static func clearCoreData(store storeURL: URL, in container: NSPersistentContainer) {
Log.info("Dropping Core Data...")
do {
try container.persistentStoreCoordinator.destroyPersistentStore(at: storeURL, type: .sqlite)
Expand All @@ -144,56 +102,22 @@ class PersistenceController {
}
}

func loadSampleData(context: NSManagedObjectContext) async throws {
guard let sampleFile = Bundle.current.url(forResource: "sample_data", withExtension: "json") else {
Log.error("Error: bad sample file location")
return
}

guard let sampleData = try? Data(contentsOf: sampleFile) else {
print("Error: Debug data not found")
return
}

Event.deleteAll(context: context)
context.reset()

guard let events = try? EventProcessor.parse(jsonData: sampleData, from: nil, in: context) else {
print("Error: Could not parse events")
return
}

print("Successfully preloaded \(events.count) events")

let verifiedEvents = Event.all(context: context)
print("Successfully fetched \(verifiedEvents.count) events")

// Force follow sample data users; This will be wiped if you sync with a relay.
let authors = Author.all(context: context)
let follows = try context.fetch(Follow.followsRequest(sources: authors))

if let publicKey = currentUser.publicKeyHex {
let currentAuthor = try Author.findOrCreate(by: publicKey, context: context)
currentAuthor.follows = Set(follows)
}
}

func newBackgroundContext() -> NSManagedObjectContext {
let context = container.newBackgroundContext()
context.automaticallyMergesChangesFromParent = true
context.mergePolicy = NSMergePolicy.mergeByPropertyStoreTrump
return context
}

static func loadVersionFromDisk() -> Int {
private static func loadVersionFromDisk() -> Int {
UserDefaults.standard.integer(forKey: Self.versionKey)
}

static func saveVersionToDisk(_ newVersion: Int) {
private static func saveVersionToDisk(_ newVersion: Int) {
UserDefaults.standard.set(newVersion, forKey: Self.versionKey)
}

/// Cleans up uneeded entities from the database. Our local database is really just a cache, and we need to
/// Cleans up unneeded entities from the database. Our local database is really just a cache, and we need to
/// invalidate old items to keep it from growing indefinitely.
///
/// This should only be called once right at app launch.
Expand Down Expand Up @@ -230,3 +154,58 @@ class PersistenceController {
}
}
}

#if DEBUG
extension PersistenceController {
func loadSampleData(context: NSManagedObjectContext) async throws {
guard let sampleFile = Bundle.current.url(forResource: "sample_data", withExtension: "json") else {
Log.error("Error: bad sample file location")
return
}

guard let sampleData = try? Data(contentsOf: sampleFile) else {
print("Error: Debug data not found")
return
}

Event.deleteAll(context: context)
context.reset()

guard let events = try? EventProcessor.parse(jsonData: sampleData, from: nil, in: context) else {
print("Error: Could not parse events")
return
}

print("Successfully preloaded \(events.count) events")

let verifiedEvents = Event.all(context: context)
print("Successfully fetched \(verifiedEvents.count) events")

// Force follow sample data users; This will be wiped if you sync with a relay.
let authors = Author.all(context: context)
let follows = try context.fetch(Follow.followsRequest(sources: authors))

if let publicKey = currentUser.publicKeyHex {
let currentAuthor = try Author.findOrCreate(by: publicKey, context: context)
currentAuthor.follows = Set(follows)
}
}

func resetForTesting() {
container = NSPersistentContainer(name: "Nos", managedObjectModel: model)
if !inMemory {
container.loadPersistentStores(completionHandler: { (storeDescription, _) in
guard let storeURL = storeDescription.url else {
Log.error("Could not get store URL")
return
}
Self.clearCoreData(store: storeURL, in: self.container)
})
}
setUp(erasingPrevious: true)
viewContext.reset()
backgroundViewContext.reset()
parseContext.reset()
}
}
#endif
6 changes: 2 additions & 4 deletions Nos/Controller/SearchController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@ class SearchController: ObservableObject {
/// The timer for showing the "not finding results" view. Resets any time the query is changed.
private var timer: Timer?

private lazy var context: NSManagedObjectContext = {
persistenceController.viewContext
}()
private lazy var context = persistenceController.viewContext

/// The amount of time, in seconds, to remain in the `.loading` state until switching to `.stillLoading`.
private let stillLoadingTime: TimeInterval = 10
Expand Down Expand Up @@ -141,7 +139,7 @@ class SearchController: ObservableObject {
authorResults = []
}

func note(fromPublicKey publicKeyString: String) -> Event? {
private func note(fromPublicKey publicKeyString: String) -> Event? {
let strippedString = publicKeyString.trimmingCharacters(
in: NSCharacterSet.whitespacesAndNewlines
)
Expand Down
Loading

0 comments on commit 89215d1

Please sign in to comment.