Skip to content

Commit

Permalink
Resolver based services abstraction for data access (#61)
Browse files Browse the repository at this point in the history
* Site settings moved to Site Service with injection
* Add and adopt services for other entities
* Buffer for Sites/Species/Supervisors; use services for upload session
* Resolver for all db / api refs; retire EntitiesViewModel
* Version bump
* Integration tests and necessary config wrap for CI
* Move other entity services to use Session Factory
* Fix flaky test
  • Loading branch information
allanlang authored Jun 17, 2022
1 parent e81f910 commit 72418d6
Show file tree
Hide file tree
Showing 32 changed files with 765 additions and 281 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/build-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ jobs:
AIRTABLE_SITES_TABLE_NAME: ${{ secrets.AIRTABLE_SITES_TABLE_NAME }}
CLOUDINARY_CLOUD_NAME: ${{ secrets.CLOUDINARY_CLOUD_NAME }}
CLOUDINARY_UPLOAD_PRESET_NAME: ${{ secrets.CLOUDINARY_UPLOAD_PRESET_NAME }}
TEST_AIRTABLE_API_KEY: ${{ secrets.TEST_AIRTABLE_API_KEY }}
TEST_AIRTABLE_BASE_ID: ${{ secrets.TEST_AIRTABLE_BASE_ID }}
TEST_AIRTABLE_TABLE_NAME_PREFIX: ${{ secrets.TEST_AIRTABLE_TABLE_NAME_PREFIX }}
run: pouch
- name: Set build number
run: agvtool new-version $GITHUB_RUN_NUMBER
Expand Down
3 changes: 3 additions & 0 deletions .pouch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@ secrets:
- AIRTABLE_SITES_TABLE_NAME
- CLOUDINARY_CLOUD_NAME
- CLOUDINARY_UPLOAD_PRESET_NAME
- TEST_AIRTABLE_API_KEY
- TEST_AIRTABLE_BASE_ID
- TEST_AIRTABLE_TABLE_NAME_PREFIX
outputs:
- ./Tree Tracker/Secrets.swift
72 changes: 60 additions & 12 deletions Tree Tracker.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
shouldUseLaunchSchemeArgsEnv = "NO">
<CommandLineArguments>
<CommandLineArgument
argument = "--integration-test"
isEnabled = "YES">
</CommandLineArgument>
</CommandLineArguments>
<Testables>
<TestableReference
skipped = "NO">
Expand Down
60 changes: 60 additions & 0 deletions Tree Tracker/AppDelegate+Injection.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import Resolver
import Photos
import UIKit

extension Resolver: ResolverRegistering {

static let mock = Resolver(child: main)
static let integrationTest = Resolver(child: main)

public static func registerAllServices() {
// register all components as singletons for lifetime of application
// defaultScope = .application

// MARK: Base services
register { Logger(output: .print) }.implements(Logging.self)
register { Database(logger: resolve()) }
register { AlamofireApi(logger: resolve()) }.implements(Api.self)
register { Defaults() }
register { GRDBImageCache(logger: resolve()) }
register { UIScreenLockManager() }
register { PHCachingImageManager() }
register { RecentSpeciesManager(defaults: resolve(), strategy: .todayUsedSpecies) }

// MARK: Services
register { AirtableSessionFactory(airtableBaseId: Constants.Airtable.baseId,
airtableApiKey: Constants.Airtable.apiKey,
httpRequestTimeoutSeconds: Constants.Http.requestTimeoutSeconds,
httpWaitsForConnectivity: true,
httpRetryDelaySeconds: Constants.Http.requestRetryDelaySeconds,
httpRetryLimit: Constants.Http.requestRetryLimit) }
register { AirtableSiteService() as SiteService }
register { AirtableSpeciesService() as SpeciesService }
register { AirtableSupervisorService() as SupervisorService }

// MARK: Controllers
register { SitesController() }
register { SpeciesController() }
register { SupervisorsController() }
register { SettingsController(style: UITableView.Style.grouped) }

// MARK: test component registrations
mock.register { MockApi() as Api }

integrationTest.register { AirtableSessionFactory(airtableBaseId: Secrets.testAirtableBaseId,
airtableApiKey: Secrets.testAirtableApiKey,
airtableTablePrefix: Secrets.testAirtableTableNamePrefix,
httpRequestTimeoutSeconds: Constants.Http.requestTimeoutSeconds,
httpWaitsForConnectivity: true,
httpRetryDelaySeconds: Constants.Http.requestRetryDelaySeconds,
httpRetryLimit: Constants.Http.requestRetryLimit) }

if CommandLine.arguments.contains("--mock-server") {
Resolver.root = Resolver.mock
}

if CommandLine.arguments.contains("--integration-test") {
Resolver.root = Resolver.integrationTest
}
}
}
5 changes: 5 additions & 0 deletions Tree Tracker/Errors/DataAccessError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Foundation

enum DataAccessError: Error {
case remoteError(errorCode: Int, errorMessage: String)
}
6 changes: 3 additions & 3 deletions Tree Tracker/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
Expand All @@ -19,9 +17,11 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>0.8.0</string>
<string>0.8.1</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSCameraUsageDescription</key>
Expand Down
9 changes: 4 additions & 5 deletions Tree Tracker/Navigation/SettingsNavigationController.swift
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import Foundation
import UIKit
import Resolver

/*
Navigation controller for Settings - acts as a container for child view controllers
*/
class SettingsNavigationController: UINavigationController {

@Injected var settingsContoller: SettingsController

init() {
super.init(nibName: nil, bundle: nil)

let top = SettingsController(style: UITableView.Style.grouped)

self.viewControllers = [top]

self.viewControllers = [settingsContoller]
self.title = "Settings"
}

Expand Down
12 changes: 5 additions & 7 deletions Tree Tracker/Screens/Details/AddLocalTreeViewModel.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Combine
import Photos
import Resolver

protocol TreeDetailsNavigating: AnyObject {
func detailsFilledSuccessfully()
Expand Down Expand Up @@ -33,9 +34,9 @@ final class AddLocalTreeViewModel: TreeDetailsViewModel {
var saveButtonPublisher: Published<ButtonModel?>.Publisher { $saveButton }
var topRightNavigationButtonPublisher: Published<NavigationBarButtonModel?>.Publisher { $topRightNavigationButton }

private let api: Api
private let database: Database
private let defaults: Defaults
@Injected private var database: Database
@Injected private var defaults: Defaults

private let recentSpeciesManager: RecentSpeciesManaging
private let initialAssetCount: Int
private var currentAsset: Int
Expand All @@ -47,10 +48,7 @@ final class AddLocalTreeViewModel: TreeDetailsViewModel {
private var supervisors: [Supervisor] = []
private weak var navigation: TreeDetailsNavigating?

init(api: Api = CurrentEnvironment.api, database: Database = CurrentEnvironment.database, defaults: Defaults = CurrentEnvironment.defaults, recentSpeciesManager: RecentSpeciesManaging = CurrentEnvironment.recentSpeciesManager, assets: [PHAsset], staticSupervisor: Supervisor?, staticSite: Site?, navigation: TreeDetailsNavigating) {
self.api = api
self.database = database
self.defaults = defaults
init(recentSpeciesManager: RecentSpeciesManaging = CurrentEnvironment.recentSpeciesManager, assets: [PHAsset], staticSupervisor: Supervisor?, staticSite: Site?, navigation: TreeDetailsNavigating) {
self.recentSpeciesManager = recentSpeciesManager
self.navigation = navigation
self.assets = assets
Expand Down
12 changes: 5 additions & 7 deletions Tree Tracker/Screens/Details/EditLocalTreeViewModel.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Combine
import Photos
import Resolver

final class EditLocalTreeViewModel: TreeDetailsViewModel {
@DelayedPublished var alert: AlertModel
Expand All @@ -18,19 +19,16 @@ final class EditLocalTreeViewModel: TreeDetailsViewModel {
var saveButtonPublisher: Published<ButtonModel?>.Publisher { $saveButton }
var topRightNavigationButtonPublisher: Published<NavigationBarButtonModel?>.Publisher { $topRightNavigationButton }

private let api: Api
private let database: Database
private let defaults: Defaults
@Injected private var database: Database
@Injected private var defaults: Defaults

private var tree: LocalTree
private var sites: [Site] = []
private var species: [Species] = []
private var supervisors: [Supervisor] = []
private weak var navigation: TreeDetailsNavigating?

init(api: Api = CurrentEnvironment.api, database: Database = CurrentEnvironment.database, defaults: Defaults = CurrentEnvironment.defaults, tree: LocalTree, navigation: TreeDetailsNavigating) {
self.api = api
self.database = database
self.defaults = defaults
init(tree: LocalTree, navigation: TreeDetailsNavigating) {
self.navigation = navigation
self.tree = tree
self.fields = []
Expand Down
158 changes: 0 additions & 158 deletions Tree Tracker/Screens/Entities/EntitiesViewModel.swift

This file was deleted.

Loading

0 comments on commit 72418d6

Please sign in to comment.