Skip to content

Commit 72418d6

Browse files
authored
Resolver based services abstraction for data access (#61)
* 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
1 parent e81f910 commit 72418d6

32 files changed

+765
-281
lines changed

.github/workflows/build-release.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ jobs:
2727
AIRTABLE_SITES_TABLE_NAME: ${{ secrets.AIRTABLE_SITES_TABLE_NAME }}
2828
CLOUDINARY_CLOUD_NAME: ${{ secrets.CLOUDINARY_CLOUD_NAME }}
2929
CLOUDINARY_UPLOAD_PRESET_NAME: ${{ secrets.CLOUDINARY_UPLOAD_PRESET_NAME }}
30+
TEST_AIRTABLE_API_KEY: ${{ secrets.TEST_AIRTABLE_API_KEY }}
31+
TEST_AIRTABLE_BASE_ID: ${{ secrets.TEST_AIRTABLE_BASE_ID }}
32+
TEST_AIRTABLE_TABLE_NAME_PREFIX: ${{ secrets.TEST_AIRTABLE_TABLE_NAME_PREFIX }}
3033
run: pouch
3134
- name: Set build number
3235
run: agvtool new-version $GITHUB_RUN_NUMBER

.pouch.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,8 @@ secrets:
77
- AIRTABLE_SITES_TABLE_NAME
88
- CLOUDINARY_CLOUD_NAME
99
- CLOUDINARY_UPLOAD_PRESET_NAME
10+
- TEST_AIRTABLE_API_KEY
11+
- TEST_AIRTABLE_BASE_ID
12+
- TEST_AIRTABLE_TABLE_NAME_PREFIX
1013
outputs:
1114
- ./Tree Tracker/Secrets.swift

Tree Tracker.xcodeproj/project.pbxproj

Lines changed: 60 additions & 12 deletions
Large diffs are not rendered by default.

Tree Tracker.xcodeproj/xcshareddata/xcschemes/Unit Tests.xcscheme

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@
1010
buildConfiguration = "Debug"
1111
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
1212
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
13-
shouldUseLaunchSchemeArgsEnv = "YES">
13+
shouldUseLaunchSchemeArgsEnv = "NO">
14+
<CommandLineArguments>
15+
<CommandLineArgument
16+
argument = "--integration-test"
17+
isEnabled = "YES">
18+
</CommandLineArgument>
19+
</CommandLineArguments>
1420
<Testables>
1521
<TestableReference
1622
skipped = "NO">
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import Resolver
2+
import Photos
3+
import UIKit
4+
5+
extension Resolver: ResolverRegistering {
6+
7+
static let mock = Resolver(child: main)
8+
static let integrationTest = Resolver(child: main)
9+
10+
public static func registerAllServices() {
11+
// register all components as singletons for lifetime of application
12+
// defaultScope = .application
13+
14+
// MARK: Base services
15+
register { Logger(output: .print) }.implements(Logging.self)
16+
register { Database(logger: resolve()) }
17+
register { AlamofireApi(logger: resolve()) }.implements(Api.self)
18+
register { Defaults() }
19+
register { GRDBImageCache(logger: resolve()) }
20+
register { UIScreenLockManager() }
21+
register { PHCachingImageManager() }
22+
register { RecentSpeciesManager(defaults: resolve(), strategy: .todayUsedSpecies) }
23+
24+
// MARK: Services
25+
register { AirtableSessionFactory(airtableBaseId: Constants.Airtable.baseId,
26+
airtableApiKey: Constants.Airtable.apiKey,
27+
httpRequestTimeoutSeconds: Constants.Http.requestTimeoutSeconds,
28+
httpWaitsForConnectivity: true,
29+
httpRetryDelaySeconds: Constants.Http.requestRetryDelaySeconds,
30+
httpRetryLimit: Constants.Http.requestRetryLimit) }
31+
register { AirtableSiteService() as SiteService }
32+
register { AirtableSpeciesService() as SpeciesService }
33+
register { AirtableSupervisorService() as SupervisorService }
34+
35+
// MARK: Controllers
36+
register { SitesController() }
37+
register { SpeciesController() }
38+
register { SupervisorsController() }
39+
register { SettingsController(style: UITableView.Style.grouped) }
40+
41+
// MARK: test component registrations
42+
mock.register { MockApi() as Api }
43+
44+
integrationTest.register { AirtableSessionFactory(airtableBaseId: Secrets.testAirtableBaseId,
45+
airtableApiKey: Secrets.testAirtableApiKey,
46+
airtableTablePrefix: Secrets.testAirtableTableNamePrefix,
47+
httpRequestTimeoutSeconds: Constants.Http.requestTimeoutSeconds,
48+
httpWaitsForConnectivity: true,
49+
httpRetryDelaySeconds: Constants.Http.requestRetryDelaySeconds,
50+
httpRetryLimit: Constants.Http.requestRetryLimit) }
51+
52+
if CommandLine.arguments.contains("--mock-server") {
53+
Resolver.root = Resolver.mock
54+
}
55+
56+
if CommandLine.arguments.contains("--integration-test") {
57+
Resolver.root = Resolver.integrationTest
58+
}
59+
}
60+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import Foundation
2+
3+
enum DataAccessError: Error {
4+
case remoteError(errorCode: Int, errorMessage: String)
5+
}

Tree Tracker/Info.plist

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
33
<plist version="1.0">
44
<dict>
5-
<key>ITSAppUsesNonExemptEncryption</key>
6-
<false/>
75
<key>CFBundleDevelopmentRegion</key>
86
<string>$(DEVELOPMENT_LANGUAGE)</string>
97
<key>CFBundleDisplayName</key>
@@ -19,9 +17,11 @@
1917
<key>CFBundlePackageType</key>
2018
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
2119
<key>CFBundleShortVersionString</key>
22-
<string>0.8.0</string>
20+
<string>0.8.1</string>
2321
<key>CFBundleVersion</key>
2422
<string>$(CURRENT_PROJECT_VERSION)</string>
23+
<key>ITSAppUsesNonExemptEncryption</key>
24+
<false/>
2525
<key>LSRequiresIPhoneOS</key>
2626
<true/>
2727
<key>NSCameraUsageDescription</key>

Tree Tracker/Navigation/SettingsNavigationController.swift

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
import Foundation
22
import UIKit
3+
import Resolver
34

45
/*
56
Navigation controller for Settings - acts as a container for child view controllers
67
*/
78
class SettingsNavigationController: UINavigationController {
89

10+
@Injected var settingsContoller: SettingsController
11+
912
init() {
1013
super.init(nibName: nil, bundle: nil)
11-
12-
let top = SettingsController(style: UITableView.Style.grouped)
13-
14-
self.viewControllers = [top]
15-
14+
self.viewControllers = [settingsContoller]
1615
self.title = "Settings"
1716
}
1817

Tree Tracker/Screens/Details/AddLocalTreeViewModel.swift

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Combine
22
import Photos
3+
import Resolver
34

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

36-
private let api: Api
37-
private let database: Database
38-
private let defaults: Defaults
37+
@Injected private var database: Database
38+
@Injected private var defaults: Defaults
39+
3940
private let recentSpeciesManager: RecentSpeciesManaging
4041
private let initialAssetCount: Int
4142
private var currentAsset: Int
@@ -47,10 +48,7 @@ final class AddLocalTreeViewModel: TreeDetailsViewModel {
4748
private var supervisors: [Supervisor] = []
4849
private weak var navigation: TreeDetailsNavigating?
4950

50-
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) {
51-
self.api = api
52-
self.database = database
53-
self.defaults = defaults
51+
init(recentSpeciesManager: RecentSpeciesManaging = CurrentEnvironment.recentSpeciesManager, assets: [PHAsset], staticSupervisor: Supervisor?, staticSite: Site?, navigation: TreeDetailsNavigating) {
5452
self.recentSpeciesManager = recentSpeciesManager
5553
self.navigation = navigation
5654
self.assets = assets

Tree Tracker/Screens/Details/EditLocalTreeViewModel.swift

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Combine
22
import Photos
3+
import Resolver
34

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

21-
private let api: Api
22-
private let database: Database
23-
private let defaults: Defaults
22+
@Injected private var database: Database
23+
@Injected private var defaults: Defaults
24+
2425
private var tree: LocalTree
2526
private var sites: [Site] = []
2627
private var species: [Species] = []
2728
private var supervisors: [Supervisor] = []
2829
private weak var navigation: TreeDetailsNavigating?
2930

30-
init(api: Api = CurrentEnvironment.api, database: Database = CurrentEnvironment.database, defaults: Defaults = CurrentEnvironment.defaults, tree: LocalTree, navigation: TreeDetailsNavigating) {
31-
self.api = api
32-
self.database = database
33-
self.defaults = defaults
31+
init(tree: LocalTree, navigation: TreeDetailsNavigating) {
3432
self.navigation = navigation
3533
self.tree = tree
3634
self.fields = []

0 commit comments

Comments
 (0)