Created
January 19, 2024 14:39
-
-
Save saroar/393f71f676919a1564d6b46eabb5b8be to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public struct AttachmentS3Client { | |
public let bucket: String | |
public let bucketWithEndpoint: String | |
public let awsS3: S3 | |
private let awsClient: AWSClient | |
private let logger: Logger | |
public typealias UploadImageToS3Handler = @Sendable ( | |
UIImage, | |
String?, // conversationId | |
String?, // userId | |
String, // bucket | |
S3, // awsS3 | |
String // bucketWithEndpoint | |
) async throws -> String | |
public let uploadImageToS3: UploadImageToS3Handler | |
public init( | |
bucket: String, | |
endpoint: String, | |
awsClient: AWSClient, | |
awsS3: S3, | |
logger: Logger, | |
uploadImageToS3: @escaping UploadImageToS3Handler | |
) { | |
self.bucket = bucket | |
self.bucketWithEndpoint = "https://\(bucket).\(endpoint)" | |
self.awsClient = awsClient | |
self.awsS3 = awsS3 | |
self.logger = logger | |
self.uploadImageToS3 = uploadImageToS3 | |
} | |
} | |
extension AttachmentS3Client { | |
static func buildImageKey(conversationId: String? = nil, userId: String? = nil, imageFormat: String ) -> String { | |
let currentTime = Int64(Date().timeIntervalSince1970 * 1000) | |
var imageKey = String(format: "%ld", currentTime) | |
if let conversationId = conversationId { | |
imageKey = "uploads/images/\(conversationId)/\(imageKey).\(imageFormat)" | |
} else if let userId = userId { | |
imageKey = "uploads/images/\(userId)/\(imageKey).\(imageFormat)" | |
} | |
return imageKey | |
} | |
} | |
extension AttachmentS3Client { | |
// upload image to DigitalOcen Spaces | |
@Sendable | |
public static func uploadImage( | |
image: UIImage, | |
conversationId: String? = nil, | |
userId: String? = nil, | |
bucket: String, | |
awsS3: S3, | |
bucketWithEndpoint: String | |
) async throws -> String { | |
return try await withCheckedThrowingContinuation { continuation in | |
do { | |
let (imageData, imageFormat) = try image.compressImage( | |
compressionQuality: conversationId == nil ? .highest : .medium, | |
imageType: .jpeg, | |
passImagesType: .thumbnail | |
) | |
let imageKey = AttachmentS3Client.buildImageKey(conversationId: conversationId, userId: userId, imageFormat: imageFormat) | |
let body = AWSPayload.data(imageData) | |
// Put an Object | |
let putObjectRequest = S3.PutObjectRequest( | |
acl: .publicRead, | |
body: body, | |
bucket: bucket, | |
contentLength: Int64(imageData.count), | |
key: imageKey | |
) | |
let futureOutput = awsS3.putObject(putObjectRequest) | |
futureOutput.whenSuccess { response in | |
print(#line, self, response, imageKey) | |
let finalURL = bucketWithEndpoint + imageKey | |
continuation.resume(returning: finalURL) | |
} | |
futureOutput.whenFailure { error in | |
continuation.resume(throwing: error) | |
} | |
} catch { | |
continuation.resume(throwing: error) | |
} | |
} | |
} | |
} | |
extension AttachmentS3Client { | |
public static var live: AttachmentS3Client { | |
// Configure these values appropriately | |
let bucket = "your-bucket-name" | |
let endpoint = "your-endpoint" | |
let client = AWSClient( | |
credentialProvider: .static( | |
accessKeyId: EnvironmentKeys.accessKeyId, | |
secretAccessKey: EnvironmentKeys.secretAccessKey | |
), | |
httpClientProvider: .createNew | |
) | |
let s3Client = S3( | |
client: client, | |
region: .useast1, | |
endpoint: "https://\(endpoint)" | |
) | |
let logger = Logger(label: "com.learnplaygrow.attachmentS3Client") | |
return AttachmentS3Client( | |
bucket: bucket, | |
endpoint: endpoint, | |
awsClient: client, | |
awsS3: s3Client, | |
logger: logger, | |
uploadImageToS3: uploadImage | |
) | |
} | |
} | |
public enum AttachmentS3ClientKey: DependencyKey { | |
public static var liveValue: AttachmentS3Client = AttachmentS3Client.live | |
} | |
extension AttachmentS3ClientKey: TestDependencyKey { | |
public static let testValue = AttachmentS3Client.happyPath | |
} | |
extension DependencyValues { | |
public var attachmentS3Client: AttachmentS3Client { | |
get { self[AttachmentS3ClientKey.self] } | |
set { self[AttachmentS3ClientKey.self] = newValue } | |
} | |
} | |
public let logger = Logger(label: "com.learnplaygrow.attachmentS3Client") | |
// Use case | |
case .sendButtonTapped: | |
state.isUploadingImages = true | |
guard let userID = state.user?.id.hexString else { | |
logger.debug("Current User is missing") | |
return .none | |
} | |
let images = state.photosSelectorState.images.map { $0.image } | |
return .run { send in | |
for image in images { | |
let imageURLString = try await attachmentS3Client.uploadImageToS3( | |
image, nil, userID, | |
attachmentS3Client.bucket, | |
attachmentS3Client.awsS3, | |
attachmentS3Client.bucketWithEndpoint | |
) | |
// Handle the imageURLString... | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Mocks