Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 21 additions & 16 deletions AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import Bookmarks

class EDSyncAppDelegate: NSObject, UIApplicationDelegate {

private let appViewModel = AppViewModel.shared

public func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {

UNUserNotificationCenter.current().delegate = self
Expand All @@ -32,8 +34,8 @@ class EDSyncAppDelegate: NSObject, UIApplicationDelegate {
configureEventObservers()
requestNotificationAuthorization()

DataStackProvider.shared.load {
if let container = DataStackProvider.shared.persistentContainer {
appViewModel.dataStackProvider.load { [weak self] in
if let container = self?.appViewModel.dataStackProvider.persistentContainer {
let librariesController = LibraryManager(persistentContainer: container)

librariesController.loadOrUpdateLibraries(in: container.viewContext)
Expand Down Expand Up @@ -79,23 +81,27 @@ extension EDSyncAppDelegate: UNUserNotificationCenterDelegate {

private func refresh(response: UNNotificationResponse) async {
do {
try await AccountUpdater().update()
try await AccountUpdater(dataStackProvider: appViewModel.dataStackProvider).update(in: appViewModel.dataStackProvider.backgroundManagedObjectContext)
} catch {

}
}

private func renew(response: UNNotificationResponse) async {
if let barcode = response.notification.request.content.userInfo["barcode"] as? String {
let context = DataStackProvider.shared.foregroundManagedObjectContext
let context = appViewModel.dataStackProvider.foregroundManagedObjectContext

let backendService = DatabaseConnectionFactory().databaseConnection(for: context, accountService: AccountScraper())
let backendService = DatabaseConnectionFactory().databaseConnection(
for: context,
accountService: AccountScraper(),
dataStackProvider: appViewModel.dataStackProvider
)
let bookmarkService = BookmarkService(managedObjectContext: context)
let loanService = BTLBLoanService(backendService: backendService,
bookmarkService: bookmarkService)

do {
let activeAccounts = try await DataStackProvider.shared.activeAccounts(in: context)
let activeAccounts = try await appViewModel.dataStackProvider.activeAccounts(in: context)

if let loanManagedObjectId = await loan(for: barcode, in: context, accounts: activeAccounts),
let loanManagedObject = context.object(with: loanManagedObjectId) as? Persistence.Loan {
Expand Down Expand Up @@ -191,7 +197,6 @@ extension EDSyncAppDelegate: UNUserNotificationCenterDelegate {
notificationCenter.setNotificationCategories([itemRenewableCategory, itemExpiresTodayCategory])
}


func didRegister(deviceToken: Data) {
Task {
let pushManager = PushNotificationManager()
Expand All @@ -210,13 +215,13 @@ extension EDSyncAppDelegate: UNUserNotificationCenterDelegate {
let currentValue = UserDefaults.suite.backgroundNotificationRefreshCount
UserDefaults.suite.backgroundNotificationRefreshCount = currentValue + 1

AppEventPublisher.shared.addObserver(WidgetSynchronisation.shared)
AppEventPublisher.shared.addObserver(NotificationSynchronisation.shared)
AppEventPublisher.shared.addObserver(appViewModel.widgetSynchronisation)
AppEventPublisher.shared.addObserver(appViewModel.notificationSynchronisation)

let context = DataStackProvider.shared.foregroundManagedObjectContext
let context = appViewModel.dataStackProvider.foregroundManagedObjectContext

return await withCheckedContinuation { continuation in
let operation = RefreshAppContentsOperation(updater: AccountUpdater(),
let operation = RefreshAppContentsOperation(updater: AccountUpdater(dataStackProvider: appViewModel.dataStackProvider),
context: context) { updateResult in
let currentValue = UserDefaults.suite.successfulBackgroundNotificationRefreshCount
UserDefaults.suite.successfulBackgroundNotificationRefreshCount = currentValue + 1
Expand All @@ -231,9 +236,9 @@ extension EDSyncAppDelegate: UNUserNotificationCenterDelegate {
}

func configureEventObservers() {
AppEventPublisher.shared.addObserver(WidgetSynchronisation.shared)
AppEventPublisher.shared.addObserver(NotificationSynchronisation.shared)
AppEventPublisher.shared.addObserver(CoreSpotlightSynchronisation.shared)
AppEventPublisher.shared.addObserver(appViewModel.widgetSynchronisation)
AppEventPublisher.shared.addObserver(appViewModel.notificationSynchronisation)
AppEventPublisher.shared.addObserver(appViewModel.coreSpotlightSynchronisation)
}

// MARK: - Background Tasks
Expand Down Expand Up @@ -277,8 +282,8 @@ extension EDSyncAppDelegate: UNUserNotificationCenterDelegate {

scheduleAppRefresh()

let context = DataStackProvider.shared.backgroundManagedObjectContext
let operation = RefreshAppContentsOperation(updater: AccountUpdater(),
let context = appViewModel.dataStackProvider.backgroundManagedObjectContext
let operation = RefreshAppContentsOperation(updater: AccountUpdater(dataStackProvider: appViewModel.dataStackProvider),
context: context) { updateResult in
switch updateResult {
case .finished:
Expand Down
2 changes: 2 additions & 0 deletions BTLB.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@
3BAF8FEB2B0A6C0900D5AF81 /* BTLBAppViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTLBAppViewModel.swift; sourceTree = "<group>"; };
3BB03179164EA89B004030D5 /* libz.1.2.5.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.1.2.5.dylib; path = usr/lib/libz.1.2.5.dylib; sourceTree = SDKROOT; };
3BB5C86A28E233B400D56B04 /* Localization */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Localization; path = Packages/Localization; sourceTree = "<group>"; };
3BB5F0C02E7DFB4F00214A41 /* ci_post_clone.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; name = ci_post_clone.sh; path = ci_scripts/ci_post_clone.sh; sourceTree = "<group>"; };
3BB68ABA1FFFA3DF00A1F616 /* BTLBTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BTLBTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3BB68ABE1FFFA3DF00A1F616 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
3BBA8CAF23F2E8F000795358 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -452,6 +453,7 @@
29B97314FDCFA39411CA2CEA /* CustomTemplate */ = {
isa = PBXGroup;
children = (
3BB5F0C02E7DFB4F00214A41 /* ci_post_clone.sh */,
3B3ED2592BA6366C00C67380 /* PrivacyInfo.xcprivacy */,
3B4A909C1BF26B8A0097962E /* Changelog.md */,
3BBD79C4294CFE320006A165 /* Release.xcconfig */,
Expand Down
17 changes: 8 additions & 9 deletions BTLBApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,10 @@ struct BTLBApp: App {
})
.onContinueUserActivity(CSSearchableItemActionType, perform: handleSpotlight)
.onOpenURL(perform: handleIncomingURL)
.environment(\.accountActivating, AccountUpdater())
.environment(\.accountUpdating, AccountUpdater())
.environment(\.managedObjectContext, DataStackProvider.shared.foregroundManagedObjectContext)
.environment(\.accountActivating, AccountUpdater(dataStackProvider: viewModel.dataStackProvider))
.environment(\.accountUpdating, AccountUpdater(dataStackProvider: viewModel.dataStackProvider))
.environment(\.managedObjectContext, viewModel.dataStackProvider.foregroundManagedObjectContext)
.environment(\.dataStackProvider, viewModel.dataStackProvider)
.environment(\.loanService, viewModel.loanService)
.environment(\.localAccountService, viewModel.localAccountRepository)
.environment(\.accountCredentialStore, viewModel.accountCredentialStore)
Expand Down Expand Up @@ -145,10 +146,8 @@ struct BTLBApp: App {
appReviewService: BTLBSettings.AppReviewService(userDefaults: .suite),
notificationScheduler: NotificationScheduler(), loan: loan)

LoanDetailView(loanDetailViewModel) {
viewModel.deeplinkLoanContainer = nil
}
.containInNavigation
LoanDetailView(loanDetailViewModel)
.containInNavigation
}

@State private var navigationPath = NavigationPath()
Expand Down Expand Up @@ -250,9 +249,9 @@ struct BTLBApp: App {
func handleSpotlight(_ userActivity: NSUserActivity) {
if let id = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String {
Task { @MainActor in
let context = DataStackProvider.shared.foregroundManagedObjectContext
let context = viewModel.dataStackProvider.foregroundManagedObjectContext

let loanManagedObjectId = try await DataStackProvider.shared.loan(for: id, in: context)
let loanManagedObjectId = try await viewModel.dataStackProvider.loan(for: id, in: context)

guard let loan = context.object(with: loanManagedObjectId) as? Persistence.Loan else { return }

Expand Down
90 changes: 50 additions & 40 deletions BTLBAppViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ enum AppState {
class AppViewModel: ObservableObject {

static var shared: AppViewModel = {
AppViewModel(debugCenter: DebugCenter(), appReviewService: AppReviewService(userDefaults: .suite), accountUpdater: AccountUpdater())
AppViewModel(debugCenter: DebugCenter(), appReviewService: AppReviewService(userDefaults: .suite))
}()

enum Tab {
Expand All @@ -52,17 +52,9 @@ class AppViewModel: ObservableObject {
case settings
}

init(debugCenter: DebugCenter, appReviewService: LibraryCore.AppReviewService, accountUpdater: AccountUpdating) {
init(debugCenter: DebugCenter, appReviewService: LibraryCore.AppReviewService) {
self.debugCenter = debugCenter
self.appReviewService = appReviewService
self.accountUpdater = accountUpdater

backendService = DatabaseConnectionFactory()
.databaseConnection(
for: DataStackProvider.shared.foregroundManagedObjectContext,
accountService: AccountScraper()
)
bookmarkService = BookmarkService(managedObjectContext: DataStackProvider.shared.foregroundManagedObjectContext)
}

func updateLaunchCount() {
Expand All @@ -73,7 +65,7 @@ class AppViewModel: ObservableObject {

private let debugCenter: DebugCenter
private let appReviewService: LibraryCore.AppReviewService
private let accountUpdater: AccountUpdating
private lazy var accountUpdater: AccountUpdating = AccountUpdater(dataStackProvider: dataStackProvider)

@Published var route: Route?
@Published var deeplinkLoanContainer: DeeplinkLoanContainer?
Expand All @@ -96,7 +88,7 @@ class AppViewModel: ObservableObject {
@MainActor func refresh() async throws {
guard lastRefreshIsOlderThan12Hours else { return state = .idle }

guard try await DataStackProvider.shared.activeAccounts(in: DataStackProvider.shared.foregroundManagedObjectContext).count > 0 else { return state = .idle }
guard try await dataStackProvider.activeAccounts(in: dataStackProvider.foregroundManagedObjectContext).count > 0 else { return state = .idle }

state = .refreshing

Expand All @@ -123,48 +115,62 @@ class AppViewModel: ObservableObject {

// MARK: Bookmarks

var bookmarksCoordinator = BookmarkListCoordinator(viewModel: BookmarkListViewModel())
@MainActor var bookmarksCoordinator = BookmarkListCoordinator(viewModel: BookmarkListViewModel())

@MainActor lazy var bookmarkService: BookmarkServicing = BookmarkService(managedObjectContext: dataStackProvider.foregroundManagedObjectContext)

let bookmarkService: BookmarkServicing
@MainActor var bookmarksTitle: String = Localization.Titles.bookmarks

var bookmarksTitle: String = Localization.Titles.bookmarks
lazy var widgetSynchronisation = WidgetSynchronisation(dataStackProvider: dataStackProvider)
@MainActor lazy var notificationSynchronisation = NotificationSynchronisation(scheduler: NotificationScheduler())
lazy var coreSpotlightSynchronisation = CoreSpotlightSynchronisation(dataStackProvider: dataStackProvider)

var loansCoordinator = LoansSectionCoordinator(
lazy var loansCoordinator = LoansSectionCoordinator(
databaseFactory: DatabaseConnectionFactory(),
accountUpdater: AccountUpdater(),
accountUpdater: accountUpdater,
dependencies: LoanSectionDependencies(
accountService: AccountScraper()
)
)
var loansTitle = Localization.Titles.loans

let backendService: LoanBackendServicing
lazy var backendService: LoanBackendServicing = DatabaseConnectionFactory()
.databaseConnection(
for: dataStackProvider.foregroundManagedObjectContext,
accountService: AccountScraper(),
dataStackProvider: dataStackProvider
)

lazy var chargesCoordinator = ChargeListCoordinator(refreshable: RefreshHandler { [weak self] in

guard let self else { return }

var chargesCoordinator = ChargeListCoordinator(refreshable: RefreshHandler {
// TODO: unite the 2 update methods into one
// try await AccountUpdater().update(.manual(.now))
try await AccountUpdater().manualUpdate(at: Date())
try await self.accountUpdater.manualUpdate(in: self.dataStackProvider.backgroundManagedObjectContext, at: Date())
})

var chargesTitle: String = Localization.Titles.charges

func createSearchCoordinator() -> SearchSectionCoordinator {
let container = DataStackProvider.shared.persistentContainer!
let container = dataStackProvider.persistentContainer!
let libraryManagedObjectIdentifier = LibraryManager(persistentContainer: container).legacySearchLibrary
let libraryManagedObject = libraryManagedObjectIdentifier.map { DataStackProvider.shared.foregroundManagedObjectContext.object(with: $0) as? any LibraryCore.Library
let libraryManagedObject = libraryManagedObjectIdentifier.map { dataStackProvider.foregroundManagedObjectContext.object(with: $0) as? any LibraryCore.Library
} ?? nil

let name = libraryManagedObject?.name ?? "unknown"
let subtitle = libraryManagedObject?.subtitle ?? ""
let databaseConnection = DatabaseConnectionFactory().databaseConnection(
for: DataStackProvider.shared.foregroundManagedObjectContext,
accountService: AccountScraper()
for: dataStackProvider.foregroundManagedObjectContext,
accountService: AccountScraper(),
dataStackProvider: dataStackProvider
) as? DatabaseConnection

let searchDependencies = SearchDependencies(
databaseConnection: databaseConnection,
searchProvider: SearchScraper(),
detailsProvider: SearchResultDetailScraper()
detailsProvider: SearchResultDetailScraper(),
dataStackProvider: dataStackProvider
)

return SearchSectionCoordinator(
Expand All @@ -179,23 +185,25 @@ class AppViewModel: ObservableObject {
)
}

var searchCoordinator: SearchSectionCoordinator? = {
guard let container = DataStackProvider.shared.persistentContainer else { return nil }
lazy var searchCoordinator: SearchSectionCoordinator? = {
guard let container = dataStackProvider.persistentContainer else { return nil }
let libraryManagedObjectIdentifier = LibraryManager(persistentContainer: container).legacySearchLibrary
let libraryManagedObject = libraryManagedObjectIdentifier.map { DataStackProvider.shared.foregroundManagedObjectContext.object(with: $0) as? any LibraryCore.Library
let libraryManagedObject = libraryManagedObjectIdentifier.map { dataStackProvider.foregroundManagedObjectContext.object(with: $0) as? any LibraryCore.Library
} ?? nil

let name = libraryManagedObject?.name ?? "unknown"
let subtitle = libraryManagedObject?.subtitle ?? ""
let databaseConnection = DatabaseConnectionFactory().databaseConnection(
for: DataStackProvider.shared.foregroundManagedObjectContext,
accountService: AccountScraper()
for: dataStackProvider.foregroundManagedObjectContext,
accountService: AccountScraper(),
dataStackProvider: dataStackProvider
) as? DatabaseConnection

let searchDependencies = SearchDependencies(
databaseConnection: databaseConnection,
searchProvider: SearchScraper(),
detailsProvider: SearchResultDetailScraper()
detailsProvider: SearchResultDetailScraper(),
dataStackProvider: dataStackProvider
)

return SearchSectionCoordinator(
Expand All @@ -214,32 +222,34 @@ class AppViewModel: ObservableObject {

// MARK: More Section

@MainActor var moreCoordinator = {
@MainActor lazy var moreCoordinator = {
let settingsService = SettingsService(
notificationScheduler: NotificationScheduler(),
accountService: AccountRepository.shared, userDefaults: UserDefaults.suite)
accountService: accountRepository, userDefaults: UserDefaults.suite)
let accountCredentialStore = AccountCredentialStore(keychainProvider: KeychainManager())

return MoreSectionCoordinator(entries: [.accounts, .about, .settings(settingsService), .openSource])
}()

@MainActor var settingsService: LibraryCore.SettingsService = SettingsService(
@MainActor lazy var settingsService: LibraryCore.SettingsService = SettingsService(
notificationScheduler: NotificationScheduler(),
accountService: AccountRepository.shared, userDefaults: UserDefaults.suite)
accountService: accountRepository, userDefaults: UserDefaults.suite)

var moreTitle: String = Localization.Titles.more

var localAccountRepository = {
LocalAccountRepository(context: DataStackProvider.shared.foregroundManagedObjectContext)
}()
lazy var localAccountRepository = LocalAccountRepository(context: dataStackProvider.foregroundManagedObjectContext)

lazy var dataStackProvider: DataStackProviding = DataStackProvider()

@MainActor lazy var accountRepository = AccountRepository(dataStackProvider: dataStackProvider)

lazy var loanService: LoanService = {
@MainActor lazy var loanService: LoanService = {
BTLBLoanService(backendService: backendService, bookmarkService: bookmarkService)
}()

var accountCredentialStore = AccountCredentialStore(keychainProvider: KeychainManager())

// TODO: kill the force! !
var libraryProvider: LibraryProvider = LibraryManager(persistentContainer: DataStackProvider.shared.persistentContainer!) as LibraryProvider
lazy var libraryProvider: LibraryProvider = LibraryManager(persistentContainer: dataStackProvider.persistentContainer!) as LibraryProvider

}
Loading