Skip to content

Commit

Permalink
Merge pull request #109 from protect-earth/al/aws-s3-backend
Browse files Browse the repository at this point in the history
AWS S3/Lambda backend for photo uploads
  • Loading branch information
philsturgeon authored Oct 1, 2023
2 parents ec83497 + 7100bc9 commit 4fe5c83
Show file tree
Hide file tree
Showing 14 changed files with 191 additions and 209 deletions.
21 changes: 15 additions & 6 deletions .github/workflows/build-release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ jobs:
brew install sunshinejr/formulae/pouch
- name: Generate Secrets.swift
env:
CLOUDINARY_CLOUD_NAME: ${{ secrets.CLOUDINARY_CLOUD_NAME }}
CLOUDINARY_UPLOAD_PRESET_NAME: ${{ secrets.CLOUDINARY_UPLOAD_PRESET_NAME }}
AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }}
AWS_SECRET_KEY: ${{ secrets.AWS_SECRET_KEY }}
AWS_BUCKET_NAME: ${{ vars.AWS_BUCKET_NAME }}
AWS_BUCKET_REGION: ${{ vars.AWS_BUCKET_REGION }}
AWS_BUCKET_PREFIX: ${{ vars.AWS_BUCKET_PREFIX }}
PROTECT_EARTH_API_TOKEN: ${{ secrets.PROTECT_EARTH_API_TOKEN }}
PROTECT_EARTH_API_BASE_URL: ${{ secrets.PROTECT_EARTH_API_BASE_URL }}
PROTECT_EARTH_ENV_NAME: ${{ secrets.PROTECT_EARTH_ENV_NAME }}
Expand Down Expand Up @@ -51,8 +54,11 @@ jobs:
brew install sunshinejr/formulae/pouch
- name: Generate Secrets.swift
env:
CLOUDINARY_CLOUD_NAME: ${{ secrets.CLOUDINARY_CLOUD_NAME }}
CLOUDINARY_UPLOAD_PRESET_NAME: ${{ secrets.CLOUDINARY_UPLOAD_PRESET_NAME }}
AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }}
AWS_SECRET_KEY: ${{ secrets.AWS_SECRET_KEY }}
AWS_BUCKET_NAME: ${{ vars.AWS_BUCKET_NAME }}
AWS_BUCKET_REGION: ${{ vars.AWS_BUCKET_REGION }}
AWS_BUCKET_PREFIX: ${{ vars.AWS_BUCKET_PREFIX }}
PROTECT_EARTH_API_TOKEN: ${{ secrets.PROTECT_EARTH_API_TOKEN }}
PROTECT_EARTH_API_BASE_URL: ${{ secrets.PROTECT_EARTH_API_BASE_URL }}
PROTECT_EARTH_ENV_NAME: ${{ secrets.PROTECT_EARTH_ENV_NAME }}
Expand Down Expand Up @@ -120,8 +126,11 @@ jobs:
brew install sunshinejr/formulae/pouch
- name: Generate Secrets.swift
env:
CLOUDINARY_CLOUD_NAME: ${{ secrets.CLOUDINARY_CLOUD_NAME }}
CLOUDINARY_UPLOAD_PRESET_NAME: ${{ secrets.CLOUDINARY_UPLOAD_PRESET_NAME }}
AWS_ACCESS_KEY: ${{ secrets.AWS_ACCESS_KEY }}
AWS_SECRET_KEY: ${{ secrets.AWS_SECRET_KEY }}
AWS_BUCKET_NAME: ${{ vars.AWS_BUCKET_NAME }}
AWS_BUCKET_REGION: ${{ vars.AWS_BUCKET_REGION }}
AWS_BUCKET_PREFIX: ${{ vars.AWS_BUCKET_PREFIX }}
PROTECT_EARTH_API_TOKEN: ${{ secrets.PROTECT_EARTH_API_TOKEN }}
PROTECT_EARTH_API_BASE_URL: ${{ secrets.PROTECT_EARTH_API_BASE_URL }}
PROTECT_EARTH_ENV_NAME: ${{ secrets.PROTECT_EARTH_ENV_NAME }}
Expand Down
7 changes: 5 additions & 2 deletions .pouch.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
secrets:
- CLOUDINARY_CLOUD_NAME
- CLOUDINARY_UPLOAD_PRESET_NAME
- AWS_BUCKET_NAME
- AWS_BUCKET_REGION
- AWS_BUCKET_PREFIX
- AWS_ACCESS_KEY
- AWS_SECRET_KEY
- PROTECT_EARTH_API_TOKEN
- PROTECT_EARTH_API_BASE_URL
- PROTECT_EARTH_ENV_NAME
Expand Down
41 changes: 35 additions & 6 deletions Tree Tracker.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 52;
objectVersion = 54;
objects = {

/* Begin PBXBuildFile section */
Expand Down Expand Up @@ -100,11 +100,14 @@
9D01D566285CD2E50009F753 /* RollbarSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 9D01D565285CD2E50009F753 /* RollbarSwift */; };
9D2B454F2944F54000B09C84 /* LocationWarningOverlayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D2B454E2944F54000B09C84 /* LocationWarningOverlayView.swift */; };
9D2DB5F629606B220040B1DB /* UploadedTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D2DB5F529606B220040B1DB /* UploadedTree.swift */; };
9D36C7EB2A22950B00E04552 /* AWSS3Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D36C7EA2A22950B00E04552 /* AWSS3Configuration.swift */; };
9D375F912AC8873C009AF2D2 /* AWSCore in Frameworks */ = {isa = PBXBuildFile; productRef = 9D375F902AC8873C009AF2D2 /* AWSCore */; };
9D375F932AC8873C009AF2D2 /* AWSS3 in Frameworks */ = {isa = PBXBuildFile; productRef = 9D375F922AC8873C009AF2D2 /* AWSS3 */; };
9D3C323B29F5BDEA00462558 /* UploadCompletionHolder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D3C323A29F5BDEA00462558 /* UploadCompletionHolder.swift */; };
9D47D97F286F293000F7B92F /* ProtectEarthSupervisor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D47D97E286F293000F7B92F /* ProtectEarthSupervisor.swift */; };
9D47D982286F29E100F7B92F /* ProtectEarthCodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D47D981286F29E100F7B92F /* ProtectEarthCodableTests.swift */; };
9D562D6528B81BDE00B66716 /* ProtectEarthUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D562D6428B81BDE00B66716 /* ProtectEarthUpload.swift */; };
9D562D6728B81D5400B66716 /* ProtectEarthIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D562D6628B81D5400B66716 /* ProtectEarthIdentifier.swift */; };
9D562D7228C4129000B66716 /* CloudinarySessionFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D562D7128C4129000B66716 /* CloudinarySessionFactory.swift */; };
9D5CDBD727BBC080007D4F0A /* ExportOptions.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9D5CDBD627BBC080007D4F0A /* ExportOptions.plist */; };
9D5D5E28284B630D00F3AD3E /* SpeciesService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D5D5E27284B630D00F3AD3E /* SpeciesService.swift */; };
9D5D5E2C284B66BB00F3AD3E /* SupervisorService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D5D5E2B284B66BB00F3AD3E /* SupervisorService.swift */; };
Expand Down Expand Up @@ -240,11 +243,12 @@
85E0E06125B35744009D8FC0 /* UIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = "<group>"; };
9D2B454E2944F54000B09C84 /* LocationWarningOverlayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationWarningOverlayView.swift; sourceTree = "<group>"; };
9D2DB5F529606B220040B1DB /* UploadedTree.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadedTree.swift; sourceTree = "<group>"; };
9D36C7EA2A22950B00E04552 /* AWSS3Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSS3Configuration.swift; sourceTree = "<group>"; };
9D3C323A29F5BDEA00462558 /* UploadCompletionHolder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploadCompletionHolder.swift; sourceTree = "<group>"; };
9D47D97E286F293000F7B92F /* ProtectEarthSupervisor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtectEarthSupervisor.swift; sourceTree = "<group>"; };
9D47D981286F29E100F7B92F /* ProtectEarthCodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtectEarthCodableTests.swift; sourceTree = "<group>"; };
9D562D6428B81BDE00B66716 /* ProtectEarthUpload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtectEarthUpload.swift; sourceTree = "<group>"; };
9D562D6628B81D5400B66716 /* ProtectEarthIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProtectEarthIdentifier.swift; sourceTree = "<group>"; };
9D562D7128C4129000B66716 /* CloudinarySessionFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudinarySessionFactory.swift; sourceTree = "<group>"; };
9D5CDBD627BBC080007D4F0A /* ExportOptions.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = ExportOptions.plist; sourceTree = "<group>"; };
9D5D5E27284B630D00F3AD3E /* SpeciesService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpeciesService.swift; sourceTree = "<group>"; };
9D5D5E2B284B66BB00F3AD3E /* SupervisorService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SupervisorService.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -290,6 +294,8 @@
85A0EF7F25A226D9003CE744 /* GRDB in Frameworks */,
85B83A0D25B87C0D0008E167 /* BSImagePicker in Frameworks */,
9DCC548C28073F0A00CF67AA /* Resolver in Frameworks */,
9D375F932AC8873C009AF2D2 /* AWSS3 in Frameworks */,
9D375F912AC8873C009AF2D2 /* AWSCore in Frameworks */,
9D01D566285CD2E50009F753 /* RollbarSwift in Frameworks */,
9D01D564285CD2E50009F753 /* RollbarNotifier in Frameworks */,
85A0EF8325A2271C003CE744 /* Alamofire in Frameworks */,
Expand Down Expand Up @@ -415,6 +421,8 @@
85B839BE25B492550008E167 /* PHImageLoader.swift */,
851DAC1F262C55FD0087E1D4 /* RecentSpeciesManager.swift */,
85C781A025CC744E0034292D /* ScreenLockManager.swift */,
9D3C323A29F5BDEA00462558 /* UploadCompletionHolder.swift */,
9D36C7EA2A22950B00E04552 /* AWSS3Configuration.swift */,
);
path = Utilities;
sourceTree = "<group>";
Expand Down Expand Up @@ -525,7 +533,6 @@
9D5D5E27284B630D00F3AD3E /* SpeciesService.swift */,
9D5D5E2B284B66BB00F3AD3E /* SupervisorService.swift */,
9DA8470E28844E9000B0BB3E /* TreeService.swift */,
9D562D7128C4129000B66716 /* CloudinarySessionFactory.swift */,
);
path = Services;
sourceTree = "<group>";
Expand Down Expand Up @@ -659,6 +666,8 @@
9D01D561285CD2E50009F753 /* RollbarCommon */,
9D01D563285CD2E50009F753 /* RollbarNotifier */,
9D01D565285CD2E50009F753 /* RollbarSwift */,
9D375F902AC8873C009AF2D2 /* AWSCore */,
9D375F922AC8873C009AF2D2 /* AWSS3 */,
);
productName = "Tree Tracker";
productReference = 853ABD522596144900144B0D /* Tree Tracker.app */;
Expand Down Expand Up @@ -699,6 +708,7 @@
85B83A0B25B87C0D0008E167 /* XCRemoteSwiftPackageReference "BSImagePicker" */,
9DCC548A28073F0A00CF67AA /* XCRemoteSwiftPackageReference "Resolver" */,
9D01D560285CD2E50009F753 /* XCRemoteSwiftPackageReference "rollbar-apple" */,
9D375F8F2AC8873C009AF2D2 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */,
);
productRefGroup = 853ABD532596144900144B0D /* Products */;
projectDirPath = "";
Expand Down Expand Up @@ -793,7 +803,6 @@
859F62E425C22D6C005E61F7 /* SelectionsKeyboardView.swift in Sources */,
85763A9425E29CE300CB4ED3 /* Logger.swift in Sources */,
9D562D6528B81BDE00B66716 /* ProtectEarthUpload.swift in Sources */,
9D562D7228C4129000B66716 /* CloudinarySessionFactory.swift in Sources */,
859F62D625C22140005E61F7 /* Species.swift in Sources */,
85B83A2325B9C1BC0008E167 /* NavigationViewController.swift in Sources */,
9DFF45C428C69AAD00D45C73 /* CloudinaryUploadResponse.swift in Sources */,
Expand Down Expand Up @@ -832,6 +841,7 @@
85DC213C25E0FC8F003F0721 /* ImageCacheInfo.swift in Sources */,
858A0F2725D8156100E12C2B /* TreeDetailsFlowViewController.swift in Sources */,
859F62DC25C2218B005E61F7 /* Supervisor.swift in Sources */,
9D36C7EB2A22950B00E04552 /* AWSS3Configuration.swift in Sources */,
85B839FB25B86F1A0008E167 /* ImageLoader.swift in Sources */,
85B839EF25B866370008E167 /* GridCollectionViewLayout.swift in Sources */,
9DA8470D28844E5100B0BB3E /* ProtectEarthTreeService.swift in Sources */,
Expand All @@ -852,6 +862,7 @@
85CC02E9261B6E820016E618 /* TableListItem.swift in Sources */,
85CC02EC261B6EA90016E618 /* TextTableViewCell.swift in Sources */,
9DA8470F28844E9000B0BB3E /* TreeService.swift in Sources */,
9D3C323B29F5BDEA00462558 /* UploadCompletionHolder.swift in Sources */,
9DFF6277282E63100008AEEF /* SupervisorsController.swift in Sources */,
85763A9D25E2B0AB00CB4ED3 /* RotatingUIImagePickerController.swift in Sources */,
9D5F061B286F348F00C8D4A6 /* ProtectEarthSessionFactory.swift in Sources */,
Expand Down Expand Up @@ -1062,7 +1073,7 @@
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = "";
DEVELOPMENT_TEAM = K5RUKV288P;
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = K5RUKV288P;
INFOPLIST_FILE = "Tree Tracker/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
Expand Down Expand Up @@ -1174,6 +1185,14 @@
minimumVersion = 2.0.0;
};
};
9D375F8F2AC8873C009AF2D2 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/aws-amplify/aws-sdk-ios-spm";
requirement = {
kind = upToNextMinorVersion;
minimumVersion = 2.33.0;
};
};
9DCC548A28073F0A00CF67AA /* XCRemoteSwiftPackageReference "Resolver" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/hmlongco/Resolver.git";
Expand Down Expand Up @@ -1215,6 +1234,16 @@
package = 9D01D560285CD2E50009F753 /* XCRemoteSwiftPackageReference "rollbar-apple" */;
productName = RollbarSwift;
};
9D375F902AC8873C009AF2D2 /* AWSCore */ = {
isa = XCSwiftPackageProductDependency;
package = 9D375F8F2AC8873C009AF2D2 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */;
productName = AWSCore;
};
9D375F922AC8873C009AF2D2 /* AWSS3 */ = {
isa = XCSwiftPackageProductDependency;
package = 9D375F8F2AC8873C009AF2D2 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */;
productName = AWSS3;
};
9D5CDBD427BBB7D2007D4F0A /* Alamofire */ = {
isa = XCSwiftPackageProductDependency;
package = 85A0EF8125A2271C003CE744 /* XCRemoteSwiftPackageReference "Alamofire" */;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@
"version" : "5.5.0"
}
},
{
"identity" : "aws-sdk-ios-spm",
"kind" : "remoteSourceControl",
"location" : "https://github.com/aws-amplify/aws-sdk-ios-spm",
"state" : {
"revision" : "ca31418963a90bac80538e13f6b7af87ea14d279",
"version" : "2.33.4"
}
},
{
"identity" : "bsimagepicker",
"kind" : "remoteSourceControl",
Expand Down
9 changes: 5 additions & 4 deletions Tree Tracker/AppDelegate+Injection.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import Resolver
import Photos
import UIKit
import AWSS3
import AWSCore

extension Resolver: ResolverRegistering {

Expand All @@ -19,6 +21,9 @@ extension Resolver: ResolverRegistering {
register { UIScreenLockManager() }
register { PHCachingImageManager() }
register { RecentSpeciesManager(defaults: resolve(), strategy: .todayUsedSpecies) }
register { AWSS3Configuration(accessKey: Secrets.awsAccessKey,
secretKey: Secrets.awsSecretKey,
region: Secrets.awsBucketRegion.aws_regionTypeValue()) }

// MARK: Services
register { ProtectEarthSessionFactory(baseUrl: Constants.Http.protectEarthApiBaseUrl,
Expand All @@ -32,10 +37,6 @@ extension Resolver: ResolverRegistering {
register { ProtectEarthSiteService() as SiteService }
register { ProtectEarthSpeciesService() as SpeciesService }
register { ProtectEarthTreeService() as TreeService }
register { CloudinarySessionFactory(httpRequestTimeoutSeconds: Constants.Http.requestTimeoutSeconds,
httpWaitsForConnectivity: true,
httpRetryDelaySeconds: Constants.Http.requestRetryDelaySeconds,
httpRetryLimit: Constants.Http.requestRetryLimit) }

// MARK: Controllers
register { SitesController() }
Expand Down
4 changes: 0 additions & 4 deletions Tree Tracker/Constants.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import Foundation

enum Constants {
enum Cloudinary {
static let cloudName = Secrets.cloudinaryCloudName
static let uploadPresetName = Secrets.cloudinaryUploadPresetName
}
enum Http {
static let requestWaitsForConnectivity = true
static let requestTimeoutSeconds: TimeInterval = 30
Expand Down
2 changes: 1 addition & 1 deletion Tree Tracker/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>0.10.12</string>
<string>0.11.0</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>ITSAppUsesNonExemptEncryption</key>
Expand Down
5 changes: 4 additions & 1 deletion Tree Tracker/Screens/Settings/SettingsController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ class SettingsController: UITableViewController {

private var entityTypes = ["Sites", "Supervisors", "Species"]
private var apiProperties = [Constants.Http.protectEarthApiBaseUrl,
Constants.Http.protectEarthEnvironmentName]
Constants.Http.protectEarthEnvironmentName,
Secrets.awsBucketName,
"\(Secrets.awsAccessKey.prefix(4))************\(Secrets.awsAccessKey.suffix(4))",
Secrets.awsBucketRegion]

override func viewDidLoad() {
super.viewDidLoad()
Expand Down
24 changes: 13 additions & 11 deletions Tree Tracker/Screens/Upload/UploadViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,19 @@ final class UploadViewModel: CollectionViewModel {
}

private func presentUploadButton(isUploading: Bool) {
self.actionButton = ButtonModel(
title: .text(isUploading ? "Stop uploading" : "Upload"),
action: { [weak self] in
if isUploading {
self?.cancelUploading()
} else {
self?.upload()
}
},
isEnabled: true
)
DispatchQueue.main.async {
self.actionButton = ButtonModel(
title: .text(isUploading ? "Stop uploading" : "Upload"),
action: { [weak self] in
if isUploading {
self?.cancelUploading()
} else {
self?.upload()
}
},
isEnabled: true
)
}
}

private func presentNavigationButtons(isUploading: Bool) {
Expand Down
43 changes: 0 additions & 43 deletions Tree Tracker/Services/CloudinarySessionFactory.swift

This file was deleted.

Loading

0 comments on commit 4fe5c83

Please sign in to comment.