From 6aa723b15a450b7f5288871e50ccf0abd69300e2 Mon Sep 17 00:00:00 2001 From: SEUNG E <105618997+SeungEEE@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:23:45 +0900 Subject: [PATCH 01/20] =?UTF-8?q?[#86]=20SelectViewController=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit image 올리기 수정 필요 --- .../xcshareddata/swiftpm/Package.resolved | 17 +- .../Meme-generate/SelectViewController.swift | 159 +++++++++++------- 2 files changed, 111 insertions(+), 65 deletions(-) diff --git a/iOS/Memetory/Memetory.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/iOS/Memetory/Memetory.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 39d86765..c97e2a7e 100644 --- a/iOS/Memetory/Memetory.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/iOS/Memetory/Memetory.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -18,6 +18,15 @@ "version" : "1.7.5" } }, + { + "identity" : "aws-sdk-ios-spm", + "kind" : "remoteSourceControl", + "location" : "https://github.com/aws-amplify/aws-sdk-ios-spm", + "state" : { + "revision" : "f7e79d076556210b1aec4289cc0028d7ac785959", + "version" : "2.36.3" + } + }, { "identity" : "googlesignin-ios", "kind" : "remoteSourceControl", @@ -50,8 +59,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/kakao/kakao-ios-sdk", "state" : { - "revision" : "ab226a7a3625d64e73a52aa3800595dab810156b", - "version" : "2.22.1" + "revision" : "9f9b830dfd7ee66607c6fddcfeb1a6bc07e48714", + "version" : "2.22.2" } }, { @@ -59,8 +68,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/SnapKit/SnapKit.git", "state" : { - "revision" : "e74fe2a978d1216c3602b129447c7301573cc2d8", - "version" : "5.7.0" + "revision" : "2842e6e84e82eb9a8dac0100ca90d9444b0307f4", + "version" : "5.7.1" } } ], diff --git a/iOS/Memetory/Memetory/View/Meme-generate/SelectViewController.swift b/iOS/Memetory/Memetory/View/Meme-generate/SelectViewController.swift index 55d00273..6c8577ee 100644 --- a/iOS/Memetory/Memetory/View/Meme-generate/SelectViewController.swift +++ b/iOS/Memetory/Memetory/View/Meme-generate/SelectViewController.swift @@ -7,9 +7,13 @@ import UIKit import SnapKit +import AWSS3 +import AWSCore class SelectViewController: UIViewController { - + + let S3BucketName = "memetory" + let faceImageView: UIImageView = { let imageView = UIImageView() imageView.contentMode = .scaleAspectFill @@ -63,24 +67,29 @@ class SelectViewController: UIViewController { tv.autocapitalizationType = .none tv.layer.cornerRadius = 12 tv.font = UIFont(name: "Pretendard-Bold", size: 16) + tv.textColor = .black + tv.backgroundColor = .white + tv.layer.borderColor = UIColor.black.cgColor + tv.layer.borderWidth = 1.0 + return tv }() -// let lineTextField: UITextField = { -// let tf = UITextField() -// tf.borderStyle = .roundedRect -// tf.font = UIFont(name: "Pretendard-Bold", size: 16) -// tf.placeholder = "대사를 입력해주세요!" -// return tf -// }() + // let lineTextField: UITextField = { + // let tf = UITextField() + // tf.borderStyle = .roundedRect + // tf.font = UIFont(name: "Pretendard-Bold", size: 16) + // tf.placeholder = "대사를 입력해주세요!" + // return tf + // }() -// let characterCountLabel: UILabel = { -// let label = UILabel() -// label.text = "0/50" -// label.font = UIFont(name: "Pretendard-Bold", size: 12) -// label.textAlignment = .right -// return label -// }() + // let characterCountLabel: UILabel = { + // let label = UILabel() + // label.text = "0/50" + // label.font = UIFont(name: "Pretendard-Bold", size: 12) + // label.textAlignment = .right + // return label + // }() let voiceSelectButton: UIButton = { let button = UIButton(type: .custom) @@ -117,18 +126,20 @@ class SelectViewController: UIViewController { button.addTarget(self, action: #selector(selectButtonTapped), for: .touchUpInside) return button }() - + override func viewDidLoad() { super.viewDidLoad() + view.backgroundColor = .white -// lineTextField.delegate = self - lineTextView.delegate = self - tabBarController?.tabBar.isHidden = true + // lineTextField.delegate = self title = "선택하기" setViews() setConstraints() + + lineTextView.delegate = self + tabBarController?.tabBar.isHidden = true } func setViews() { @@ -137,8 +148,6 @@ class SelectViewController: UIViewController { view.addSubview(cancelImageButton) view.addSubview(adviceLabel) view.addSubview(lineTextView) -// view.addSubview(lineTextField) -// view.addSubview(characterCountLabel) view.addSubview(voiceSelectButton) view.addSubview(checkButton) } @@ -173,19 +182,6 @@ class SelectViewController: UIViewController { make.height.equalTo(100) } -// lineTextField.snp.makeConstraints { make in -// make.top.equalTo(faceImageView.snp.bottom).offset(50) -// make.centerX.equalToSuperview() -// make.leading.equalToSuperview().offset(30) -// make.trailing.equalToSuperview().offset(-30) -// make.height.equalTo(45) -// } - -// characterCountLabel.snp.makeConstraints { make in -// make.top.equalTo(lineTextView.snp.bottom).offset(5) -// make.right.equalTo(lineTextView) -// } - voiceSelectButton.snp.makeConstraints { make in make.top.equalTo(lineTextView.snp.bottom).offset(30) make.leading.equalToSuperview().offset(30) @@ -194,7 +190,7 @@ class SelectViewController: UIViewController { } checkButton.snp.makeConstraints { make in -// make.top.equalTo(faceImageView.snp.bottom).offset(80) + // make.top.equalTo(faceImageView.snp.bottom).offset(80) make.leading.equalToSuperview().offset(30) make.trailing.equalToSuperview().offset(-30) make.top.equalTo(voiceSelectButton.snp.bottom).offset(30) @@ -208,8 +204,48 @@ class SelectViewController: UIViewController { } @objc func selectButtonTapped() { - let temSelectVC = TemSelectViewController() - navigationController?.pushViewController(temSelectVC, animated: true) + // 이미지가 있는지 확인 +// guard let selectedImage = faceImageView.image else { +// // 이미지가 없는 경우 실패 클로저 호출 +// S3Manager.uploadImageToS3(nil, isSuccess: { _ in }, isFailed: { +// self.showAlert(message: "이미지를 선택해주세요.") +// }) +// return +// } + guard let selectedImage = faceImageView.image else { + showAlert(message: "이미지를 선택해주세요.") + return + } + + // 이미지 업로드 +// uploadImageToS3(selectedImage, isSuccess: { result in +// // 업로드 성공 시 추가 작업 수행 +// print("Image uploaded successfully: \(result)") +// }, isFailed: { +// // 업로드 실패 시 알림 표시 +// self.showAlert(message: "이미지 업로드에 실패했습니다.") +// }) +// let temSelectVC = TemSelectViewController() +// navigationController?.pushViewController(temSelectVC, animated: true) + +// S3Manager.shared.uploadImage(image: selectedImage) { [weak self] fileName in +// if let fileName = fileName { +// print("Image uploaded successfully: \(fileName)") +// } else { +// self?.showAlert(message: "이미지 업로드 실패") +// } +// } + + let storageVC = StorageViewController() + navigationController?.pushViewController(storageVC, animated: true) + + } + + // 이미지가 없을 때 알림을 표시하는 함수 + func showAlert(message: String) { + let alert = UIAlertController(title: "알림", message: message, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "확인", style: .default, handler: nil)) + present(alert, animated: true, completion: nil) } } @@ -249,28 +285,29 @@ extension SelectViewController: UITextViewDelegate { self.view.endEditing(true) } -// func textView(_ textView: UITextView, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { -// let newText = (textView.text as NSString?)?.replacingCharacters(in: range, with: string) ?? "" -// let characterCount = newText.count -// -// if characterCount <= 50 { -// characterCountLabel.text = "\(characterCount)/50" -// return true -// } else { -// return false -// } -// } + // func textView(_ textView: UITextView, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + // let newText = (textView.text as NSString?)?.replacingCharacters(in: range, with: string) ?? "" + // let characterCount = newText.count + // + // if characterCount <= 50 { + // characterCountLabel.text = "\(characterCount)/50" + // return true + // } else { + // return false + // } + // } func textViewDidBeginEditing(_ textView: UITextView) { // 텍스트 필드가 편집을 시작할 때 호출되는 메서드 - textView.layer.cornerRadius = 8.0 // 둥근 테두리 반지름 설정 - textView.layer.borderWidth = 1.0 // 테두리 두께 설정 -// textView.text = nil -// textField.layer.borderColor = WithYouAsset.mainColorDark.color.cgColor +// textView.layer.cornerRadius = 8.0 // 둥근 테두리 반지름 설정 +// textView.layer.borderWidth = 1.0 // 테두리 두께 설정 +// textView.layer.borderColor = UIColor.black.cgColor + // textView.text = nil + // textField.layer.borderColor = WithYouAsset.mainColorDark.color.cgColor } func textViewDidEndEditing(_ textView: UITextView) { -// textField.layer.borderColor = WithYouAsset.subColor.color.cgColor + // textField.layer.borderColor = WithYouAsset.subColor.color.cgColor if (lineTextView.text == "") { lineTextView.text = "대사를 입력해주세요!" lineTextView.textColor = .gray @@ -285,17 +322,17 @@ extension SelectViewController: UITextViewDelegate { } //extension SelectViewController : UITextFieldDelegate { - // MARK: - UITextFieldDelegate - - //화면 터치시 키보드 내림 +// MARK: - UITextFieldDelegate + +//화면 터치시 키보드 내림 // override func touchesBegan(_ touches: Set, with event: UIEvent?) { // self.view.endEditing(true) // } - + // func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { // let newText = (textField.text as NSString?)?.replacingCharacters(in: range, with: string) ?? "" // let characterCount = newText.count -// +// // if characterCount <= 50 { // characterCountLabel.text = "\(characterCount)/50" // return true @@ -303,18 +340,18 @@ extension SelectViewController: UITextViewDelegate { // return false // } // } - + // func textFieldDidBeginEditing(_ textField: UITextField) { // // 텍스트 필드가 편집을 시작할 때 호출되는 메서드 // textField.layer.cornerRadius = 8.0 // 둥근 테두리 반지름 설정 // textField.layer.borderWidth = 1.0 // 테두리 두께 설정 //// textField.layer.borderColor = WithYouAsset.mainColorDark.color.cgColor // } -// +// // func textFieldDidEndEditing(_ textField: UITextField) { //// textField.layer.borderColor = WithYouAsset.subColor.color.cgColor // } -// +// // func textFieldShouldReturn(_ textField: UITextField) -> Bool { // // Process of closing the Keyboard when the line feed button is pressed. // textField.resignFirstResponder() From ed973c3da3618abb382310a54fd1e233c816abdf Mon Sep 17 00:00:00 2001 From: SEUNG E <105618997+SeungEEE@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:24:57 +0900 Subject: [PATCH 02/20] =?UTF-8?q?[#86]=20TemSelectViewController=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/Memetory/Memetory/AWS/Environment.swift | 37 ++++++++ iOS/Memetory/Memetory/AWS/S3Manager.swift | 88 +++++++++++++++++++ .../Scene/TemSelectViewController.swift | 4 +- 3 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 iOS/Memetory/Memetory/AWS/Environment.swift create mode 100644 iOS/Memetory/Memetory/AWS/S3Manager.swift diff --git a/iOS/Memetory/Memetory/AWS/Environment.swift b/iOS/Memetory/Memetory/AWS/Environment.swift new file mode 100644 index 00000000..6e574c2b --- /dev/null +++ b/iOS/Memetory/Memetory/AWS/Environment.swift @@ -0,0 +1,37 @@ +// +// Environment.swift +// Memetory +// +// Created by 이승진 on 2024/06/17. +// + +import Foundation + +struct Environment { + static func load() { + guard let envPath = Bundle.main.path(forResource: ".env", ofType: nil) else { + print(".env file not found") + return + } + + // 경로 출력 + print("Env file path: \(envPath)") + + do { + let envContents = try String(contentsOfFile: envPath) + print("Env file contents:\n\(envContents)") + let envLines = envContents.split(separator: "\n") + + for line in envLines { + let keyValue = line.split(separator: "=") + if keyValue.count == 2 { + let key = String(keyValue[0]).trimmingCharacters(in: .whitespaces) + let value = String(keyValue[1]).trimmingCharacters(in: .whitespaces) + setenv(key, value, 1) + } + } + } catch { + print("Failed to read .env file: \(error)") + } + } +} diff --git a/iOS/Memetory/Memetory/AWS/S3Manager.swift b/iOS/Memetory/Memetory/AWS/S3Manager.swift new file mode 100644 index 00000000..4da0ad45 --- /dev/null +++ b/iOS/Memetory/Memetory/AWS/S3Manager.swift @@ -0,0 +1,88 @@ +// +// S3Manager.swift +// Memetory +// +// Created by 이승진 on 2024/06/17. +// + +import UIKit +import AWSS3 + +class S3Manager { + static let shared = S3Manager() + + let S3BucketName = "memetory" + + private init() { + + } + + func uploadImage(image: UIImage, completion: @escaping (String?) -> Void) { + guard let imageData = image.jpegData(compressionQuality: 0.9) else { + print("imgae X") + completion(nil) + return + } + let transferUtility = AWSS3TransferUtility.default() + + let expression = AWSS3TransferUtilityUploadExpression() + expression.setValue("AES256", forRequestHeader: "x-amz-server-side-encryption") + expression.progressBlock = { (task, progress) in + DispatchQueue.main.async { + print("[ Upload progress ]: \(progress.fractionCompleted)") + } + } + + let currentDate = Date() + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyyMMddhhmmssSSS" + let fileName = dateFormatter.string(from: currentDate) + ".jpg" + + transferUtility.uploadData( + imageData, + bucket: S3BucketName, + key: fileName, + contentType: "image/jpg", + expression: expression + ) { (task, error) in + if let error = error { + print("Error uploading image: \(error.localizedDescription)") + completion(nil) + } else { + print("Image uploaded successfully!") + completion(fileName) + } + } + } + + + func uploadFile(data: Data, bucketName: String, key: String, completion: @escaping (Bool, Error?) -> Void) { + let expression = AWSS3TransferUtilityUploadExpression() + let transferUtility = AWSS3TransferUtility.default() + + transferUtility.uploadData(data, bucket: bucketName, key: key, contentType: "text/plain", expression: expression) { task, error in + if let error = error { + print("Upload failed with error: \(error)") + completion(false, error) + } else { + print("Upload successful") + completion(true, nil) + } + } + } + + func downloadFile(bucketName: String, key: String, completion: @escaping (Data?, Error?) -> Void) { + let expression = AWSS3TransferUtilityDownloadExpression() + let transferUtility = AWSS3TransferUtility.default() + + transferUtility.downloadData(fromBucket: bucketName, key: key, expression: expression) { task, url, data, error in + if let error = error { + print("Download failed with error: \(error)") + completion(nil, error) + } else { + print("Download successful") + completion(data, nil) + } + } + } +} diff --git a/iOS/Memetory/Memetory/View/Meme-generate/Scene/TemSelectViewController.swift b/iOS/Memetory/Memetory/View/Meme-generate/Scene/TemSelectViewController.swift index e2c8dbd2..c2dd16c9 100644 --- a/iOS/Memetory/Memetory/View/Meme-generate/Scene/TemSelectViewController.swift +++ b/iOS/Memetory/Memetory/View/Meme-generate/Scene/TemSelectViewController.swift @@ -80,8 +80,8 @@ class TemSelectViewController: UIViewController { } @objc func selectButtonTapped() { - let selectVC = SelectViewController() - navigationController?.pushViewController(selectVC, animated: true) + let storageVC = StorageViewController() + navigationController?.pushViewController(storageVC, animated: true) } func setViews() { From d71df343fc86112544e90ab140f4e31355942642 Mon Sep 17 00:00:00 2001 From: SEUNG E <105618997+SeungEEE@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:25:15 +0900 Subject: [PATCH 03/20] =?UTF-8?q?[#86]=20GenerateViewController=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/Meme-generate/Template/GenerateViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iOS/Memetory/Memetory/View/Meme-generate/Template/GenerateViewController.swift b/iOS/Memetory/Memetory/View/Meme-generate/Template/GenerateViewController.swift index 7b155b51..7685a2b0 100644 --- a/iOS/Memetory/Memetory/View/Meme-generate/Template/GenerateViewController.swift +++ b/iOS/Memetory/Memetory/View/Meme-generate/Template/GenerateViewController.swift @@ -81,7 +81,7 @@ extension GenerateViewController: UICollectionViewDataSource, UICollectionViewDe } func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - let selectVC = SelectViewController() + let selectVC = TemSelectViewController() // settingVC.delegate = self // let array = memberListManager.getMemberList() From defc653f03aaf53558114cfd6d2fea4829405a22 Mon Sep 17 00:00:00 2001 From: SEUNG E <105618997+SeungEEE@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:25:27 +0900 Subject: [PATCH 04/20] =?UTF-8?q?[#86]=20LoadingView=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Meme-generate/Voice/LoadingView.swift | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 iOS/Memetory/Memetory/View/Meme-generate/Voice/LoadingView.swift diff --git a/iOS/Memetory/Memetory/View/Meme-generate/Voice/LoadingView.swift b/iOS/Memetory/Memetory/View/Meme-generate/Voice/LoadingView.swift new file mode 100644 index 00000000..f9f53a25 --- /dev/null +++ b/iOS/Memetory/Memetory/View/Meme-generate/Voice/LoadingView.swift @@ -0,0 +1,59 @@ +// +// LoadingView.swift +// Memetory +// +// Created by 이승진 on 2024/05/16. +// + +import UIKit +import SnapKit + +final class LoadingView: UIView { + + private let backgroundView: UIView = { + let view = UIView() + view.backgroundColor = .white + return view + }() + + private let activityIndicatorView: UIActivityIndicatorView = { + let view = UIActivityIndicatorView(style: .large) + return view + + }() + + var isLoading = false { + didSet { + self.isHidden = !self.isLoading + self.isLoading ? self.activityIndicatorView.startAnimating() : self.activityIndicatorView.stopAnimating() + } + } + + override init(frame: CGRect) { + super.init(frame: frame) + + self.addSubview(self.backgroundView) + self.addSubview(self.activityIndicatorView) + + setConstriants() + } + + func setConstriants() { + backgroundView.snp.makeConstraints { make in + make.leading.equalToSuperview() + make.trailing.equalToSuperview() + make.top.equalToSuperview() + make.bottom.equalToSuperview() + } + + activityIndicatorView.snp.makeConstraints { make in + make.centerX.equalTo(self.snp.centerX) + make.centerY.equalTo(self.snp.centerY) + } + } + required init?(coder: NSCoder) { + fatalError() + } +} + + From 267bbeb899963ef9c5ae553fa0826ef57f72ebee Mon Sep 17 00:00:00 2001 From: SEUNG E <105618997+SeungEEE@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:25:57 +0900 Subject: [PATCH 05/20] =?UTF-8?q?[#86]=20Voice=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/Meme-generate/Models/Vocie/Voice.swift | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 iOS/Memetory/Memetory/View/Meme-generate/Models/Vocie/Voice.swift diff --git a/iOS/Memetory/Memetory/View/Meme-generate/Models/Vocie/Voice.swift b/iOS/Memetory/Memetory/View/Meme-generate/Models/Vocie/Voice.swift new file mode 100644 index 00000000..1185efaa --- /dev/null +++ b/iOS/Memetory/Memetory/View/Meme-generate/Models/Vocie/Voice.swift @@ -0,0 +1,17 @@ +// +// Voice.swift +// Memetory +// +// Created by 이승진 on 2024/05/14. +// + +import Foundation + +struct Voice : Codable { + var s3Key: String + var name: String + var description: String + + static let voiceDemo = ["Martin Li", "Max Mustermann - Ernst", "Thomas", "Grant - Calm Narration", "Devi - Clear Hindi pronunciation"] +} + From ea8bf01b54950544a60240a49e2e24c2c8abbfb8 Mon Sep 17 00:00:00 2001 From: SEUNG E <105618997+SeungEEE@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:26:11 +0900 Subject: [PATCH 06/20] =?UTF-8?q?[#86]=20LoadingViewController=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Voice/LoadingViewController.swift | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 iOS/Memetory/Memetory/View/Meme-generate/Voice/LoadingViewController.swift diff --git a/iOS/Memetory/Memetory/View/Meme-generate/Voice/LoadingViewController.swift b/iOS/Memetory/Memetory/View/Meme-generate/Voice/LoadingViewController.swift new file mode 100644 index 00000000..33df5ca6 --- /dev/null +++ b/iOS/Memetory/Memetory/View/Meme-generate/Voice/LoadingViewController.swift @@ -0,0 +1,144 @@ +// +// LoadingViewController.swift +// Memetory +// +// Created by 이승진 on 2024/05/16. +// + +import UIKit +import SnapKit + +final class LoadingViewController: UIViewController { + + private let loadingView: LoadingView = { + let view = LoadingView() + view.translatesAutoresizingMaskIntoConstraints = false + return view + }() + + private let loadingImage: UIImageView = { + let imageView = UIImageView() + imageView.image = UIImage(named: "LoadingImage") + imageView.contentMode = .scaleAspectFill + imageView.layer.cornerRadius = 10 + imageView.layer.masksToBounds = true + return imageView + }() + + private let completeButton: UIButton = { + let button = UIButton(type: .custom) + button.setTitle("완료", for: .normal) + button.setTitleColor(.white, for: .normal) + button.titleLabel?.font = UIFont(name: "Pretendard-Bold", size: 18) + button.layer.cornerRadius = 10 + button.layer.masksToBounds = true + button.layer.borderWidth = 1 + button.layer.borderColor = UIColor.black.cgColor + button.layer.backgroundColor = UIColor.black.cgColor + button.addTarget(self, action: #selector(completeButtonTapped), for: .touchUpInside) + return button + }() +// +// private let tableView: UITableView = { +// let view = UITableView() +// view.allowsSelection = false +// view.backgroundColor = .clear +// view.separatorStyle = .none +// view.bounces = true +// view.showsVerticalScrollIndicator = true +// view.contentInset = .zero +// view.register(UITableViewCell.self, forCellReuseIdentifier: "cell") +// view.translatesAutoresizingMaskIntoConstraints = false +// return view +// }() + + var items = (1...20).map(String.init) + + override func viewDidLoad() { + super.viewDidLoad() + + self.view.backgroundColor = .white + +// self.view.addSubview(self.tableView) + self.view.addSubview(self.loadingImage) + self.view.addSubview(self.loadingView) + self.view.addSubview(self.completeButton) + +// NSLayoutConstraint.activate([ +// self.tableView.leftAnchor.constraint(equalTo: self.view.leftAnchor), +// self.tableView.rightAnchor.constraint(equalTo: self.view.rightAnchor), +// self.tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor), +// self.tableView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor), +// ]) + + + +// NSLayoutConstraint.activate([ +// self.loadingView.leftAnchor.constraint(equalTo: self.tableView.leftAnchor), +// self.loadingView.rightAnchor.constraint(equalTo: self.tableView.rightAnchor), +// self.loadingView.bottomAnchor.constraint(equalTo: self.tableView.bottomAnchor), +// self.loadingView.topAnchor.constraint(equalTo: self.tableView.topAnchor), +// ]) +// +// self.tableView.dataSource = self +// self.tableView.delegate = self + + self.loadingView.isLoading = true + self.getSomeData { [weak self] in + self?.loadingView.isLoading = false + } + + setConstraints() + } + + private func setConstraints() { + + loadingImage.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.centerY.equalToSuperview() + make.width.height.equalTo(300) + } + + loadingView.snp.makeConstraints { make in + make.leading.trailing.top.bottom.equalToSuperview() + } + + completeButton.snp.makeConstraints { make in + make.top.equalTo(loadingImage.snp.bottom).offset(30) + make.leading.equalToSuperview().offset(30) + make.trailing.equalToSuperview().offset(-30) + } + } + + + + private func getSomeData(completion: @escaping () -> ()) { + DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) { + completion() + } + } + + @objc func completeButtonTapped() { + let templetSetVC = TemSelectViewController() + navigationController?.pushViewController(templetSetVC, animated: true) + } +} + +//extension LoadingViewController: UITableViewDataSource { +// func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { +// self.items.count +// } +// func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { +// let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) +// cell.textLabel?.text = self.items[indexPath.row] +//// cell.backgroundColor = .gray +// return cell +// } +//} +// +//extension LoadingViewController: UITableViewDelegate { +// func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { +// 120 +// } +//} + From 537466df802f42469589f5cb046dd929dea74215 Mon Sep 17 00:00:00 2001 From: SEUNG E <105618997+SeungEEE@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:27:11 +0900 Subject: [PATCH 07/20] =?UTF-8?q?[#86]=20VoiceSelectViewController=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Voice/VoiceSelectViewController.swift | 85 +++++++++++++++++-- 1 file changed, 78 insertions(+), 7 deletions(-) diff --git a/iOS/Memetory/Memetory/View/Meme-generate/Voice/VoiceSelectViewController.swift b/iOS/Memetory/Memetory/View/Meme-generate/Voice/VoiceSelectViewController.swift index 9be98191..94869918 100644 --- a/iOS/Memetory/Memetory/View/Meme-generate/Voice/VoiceSelectViewController.swift +++ b/iOS/Memetory/Memetory/View/Meme-generate/Voice/VoiceSelectViewController.swift @@ -7,10 +7,32 @@ import UIKit import SnapKit +import AWSS3 +import AVFAudio class VoiceSelectViewController: UIViewController { - private let tableView = UITableView() + let S3BucketName = "memetory" + let voiceUploadManager = VoiceManager() + private let voiceDemo = Voice.voiceDemo + + +// private let tableView = UITableView() + lazy var tableView = UITableView() + + let uploadButton: UIButton = { + let button = UIButton(type: .custom) + button.setTitle("나의 업로드", for: .normal) + button.setTitleColor(.white, for: .normal) + button.titleLabel?.font = UIFont(name: "Pretendard-Bold", size: 18) + button.layer.cornerRadius = 10 + button.layer.masksToBounds = true + button.layer.borderWidth = 1 + button.layer.borderColor = UIColor.black.cgColor + button.layer.backgroundColor = UIColor.black.cgColor + button.addTarget(self, action: #selector(uploadButtonTapped), for: .touchUpInside) + return button + }() let checkButton: UIButton = { let button = UIButton(type: .custom) @@ -36,13 +58,12 @@ class VoiceSelectViewController: UIViewController { return button }() - override func viewDidLoad() { super.viewDidLoad() - + title = "목소리 선택하기" view.backgroundColor = .white - + setupTableView() setViews() setConstraints() @@ -50,6 +71,7 @@ class VoiceSelectViewController: UIViewController { func setViews() { view.addSubview(tableView) + view.addSubview(uploadButton) view.addSubview(checkButton) } @@ -62,6 +84,13 @@ class VoiceSelectViewController: UIViewController { } + uploadButton.snp.makeConstraints { make in + make.centerX.equalToSuperview() + make.width.equalTo(320) + make.height.equalTo(45) + make.bottom.equalTo(checkButton.snp.top).offset(-30) + } + checkButton.snp.makeConstraints { make in make.centerX.equalToSuperview() make.width.equalTo(320) @@ -73,25 +102,67 @@ class VoiceSelectViewController: UIViewController { func setupTableView() { tableView.dataSource = self tableView.delegate = self - tableView.rowHeight = 60 - tableView.register(VoiceTableViewCell.self, forCellReuseIdentifier: "VoiceSelectCell") } + + @objc func uploadButtonTapped() { + let audioPickerController = UIImagePickerController() + audioPickerController.delegate = self + audioPickerController.sourceType = .savedPhotosAlbum + audioPickerController.mediaTypes = ["public.audio"] + present(audioPickerController, animated: true, completion: nil) + } + @objc func checkButtonTapped() { + let loadingVC = LoadingViewController() + navigationController?.pushViewController(loadingVC, animated: true) +// let SelectVC = SelectViewController() +// navigationController?.pushViewController(SelectVC, animated: true) } } extension VoiceSelectViewController: UITableViewDelegate, UITableViewDataSource { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 5 + return voiceDemo.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "VoiceSelectCell", for: indexPath) as! VoiceTableViewCell cell.selectionStyle = .none + cell.nickNameLabel.text = voiceDemo[indexPath.item] return cell } } + +extension VoiceSelectViewController: UINavigationControllerDelegate, UIImagePickerControllerDelegate { + + func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { + picker.dismiss(animated: true, completion: nil) + + guard let audioURL = info[UIImagePickerController.InfoKey.mediaURL] as? URL else { + print("Error: No audio selected") + return + } + + uploadVoice(audioURL) + } + + func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { + picker.dismiss(animated: true, completion: nil) + } + + func uploadVoice(_ audioURL: URL) { + voiceUploadManager.uploadVoiceToS3(audioURL) { (voiceURLString) in + if let voiceURLString = voiceURLString { + print("Uploaded voice URL: \(voiceURLString)") + // 업로드 성공 시 여기에서 추가 작업을 수행할 수 있습니다. + } else { + print("Failed to upload voice.") + // 업로드 실패 시 여기에서 처리할 수 있는 로직을 추가하세요. + } + } + } +} From fd63ca165ffc39d8887b533c74a000dd2e34be4a Mon Sep 17 00:00:00 2001 From: SEUNG E <105618997+SeungEEE@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:27:30 +0900 Subject: [PATCH 08/20] =?UTF-8?q?[#86]=20VoiceTableViewCell=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Voice/VoiceTableViewCell.swift | 61 ++++++++++++++++--- 1 file changed, 51 insertions(+), 10 deletions(-) diff --git a/iOS/Memetory/Memetory/View/Meme-generate/Voice/VoiceTableViewCell.swift b/iOS/Memetory/Memetory/View/Meme-generate/Voice/VoiceTableViewCell.swift index a79ec274..68074a32 100644 --- a/iOS/Memetory/Memetory/View/Meme-generate/Voice/VoiceTableViewCell.swift +++ b/iOS/Memetory/Memetory/View/Meme-generate/Voice/VoiceTableViewCell.swift @@ -11,12 +11,20 @@ import SnapKit class VoiceTableViewCell: UITableViewCell { static let cellId = "VoiceTableViewCell" + var isCheck: Bool = false { + didSet { + let imageName = isCheck ? "checkmark.square" : "checkmark.square.fill" + checkButton.setImage(UIImage(systemName: imageName), for: .normal) + } + } + let playButton: UIButton = { let button = UIButton(type: .custom) // button.layer.cornerRadius = 15 // button.layer.masksToBounds = true // button.layer.borderWidth = 1 button.setImage(UIImage(systemName: "play.circle"), for: .normal) + button.tintColor = .black return button }() @@ -24,30 +32,58 @@ class VoiceTableViewCell: UITableViewCell { let label = UILabel() label.text = "기본 목소리" label.font = UIFont.systemFont(ofSize: 18) + label.textColor = .black label.textAlignment = .center return label }() +// lazy var checkButton: UIButton = { +// let button = UIButton() +// button.layer.cornerRadius = 10 +// button.layer.masksToBounds = true +// button.layer.borderWidth = 1 +// button.layer.borderColor = #colorLiteral(red: 0.6666666865, green: 0.6666666865, blue: 0.6666666865, alpha: 1) +// button.backgroundColor = .white +//// button.isEnabled = true +// button.isUserInteractionEnabled = false +// button.setImage(UIImage(named: "Icon-Check-off"), for: .normal) +//// button.setImage(UIImage(named: "Icon-Check-on"), for: .selected) +// return button +// }() + let checkButton: UIButton = { - let button = UIButton(type: .custom) - button.layer.cornerRadius = 15 - button.layer.masksToBounds = true - button.layer.borderWidth = 1 - button.layer.borderColor = #colorLiteral(red: 0.6666666865, green: 0.6666666865, blue: 0.6666666865, alpha: 1) - button.isEnabled = true - button.setImage(UIImage(named: "Icon-Check-off"), for: .normal) - button.setImage(UIImage(named: "Icon-Check-on"), for: .selected) + let button = UIButton() +// button.layer.cornerRadius = 10 +// button.layer.masksToBounds = true +// button.layer.borderWidth = 1 +// button.layer.borderColor = UIColor.lightGray.cgColor +// button.setImage(UIImage(named: "Icon-Check-off"), for: .normal) +// button.setImage(UIImage(named: "Icon-Check-On"), for: .selected) + + button.setImage(UIImage(systemName: "checkmark.square"), for: .normal) + button.tintColor = .black + button.isUserInteractionEnabled = false + + return button }() + override func setSelected(_ selected: Bool, animated: Bool) { + isCheck.toggle() + } override init(style: UITableViewCell.CellStyle, reuseIdentifier reuseIndetifier: String?) { super.init(style: .default, reuseIdentifier: reuseIndetifier) - + self.backgroundColor = .white self.addSubview(playButton) self.addSubview(nickNameLabel) self.addSubview(checkButton) setConstraints() + + // playButton 또는 nickNameLabel을 탭하여도 셀이 선택되도록 탭 제스처를 추가합니다. + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(cellTapped)) + playButton.addGestureRecognizer(tapGesture) + nickNameLabel.addGestureRecognizer(tapGesture) } required init?(coder: NSCoder) { @@ -69,7 +105,12 @@ class VoiceTableViewCell: UITableViewCell { checkButton.snp.makeConstraints { make in make.centerY.equalTo(self.snp.centerY) make.trailing.equalTo(self.snp.trailing ).offset(-15) - make.width.height.equalTo(30) + make.width.height.equalTo(20) } } + // 셀이 탭되었을 때 isSelected 속성을 토글합니다. + @objc func cellTapped() { + isSelected.toggle() + } + } From 1e8314660c5d5077ae44b33b2d9c43ba04c51f56 Mon Sep 17 00:00:00 2001 From: SEUNG E <105618997+SeungEEE@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:27:42 +0900 Subject: [PATCH 09/20] =?UTF-8?q?[#86]=20Scene=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/Meme-generate/Models/Scene.swift | 20 ------------------- 1 file changed, 20 deletions(-) delete mode 100644 iOS/Memetory/Memetory/View/Meme-generate/Models/Scene.swift diff --git a/iOS/Memetory/Memetory/View/Meme-generate/Models/Scene.swift b/iOS/Memetory/Memetory/View/Meme-generate/Models/Scene.swift deleted file mode 100644 index 207dcf48..00000000 --- a/iOS/Memetory/Memetory/View/Meme-generate/Models/Scene.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// Scene.swift -// Memetory -// -// Created by 이승진 on 2024/03/05. -// - -import UIKit - -struct Scene { - static let sceneImage = ["demo1", "demo2"] - static let templateTitle = ["해리포터", "제8일의밤", "범죄도시4", "악마와의 토크쇼", " 스마일", "인시디어스"] - - -// lazy var sceneImage: UIImage? = { -// return UIImage(named: "demo\(num).jpeg") -// }() -// -// var num: Int -} From 034fd8e20d40fdaedca4d3f33dd27e374156a88c Mon Sep 17 00:00:00 2001 From: SEUNG E <105618997+SeungEEE@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:27:52 +0900 Subject: [PATCH 10/20] =?UTF-8?q?[#86]=20Scene=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Meme-generate/Models/Scene/Scene.swift | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 iOS/Memetory/Memetory/View/Meme-generate/Models/Scene/Scene.swift diff --git a/iOS/Memetory/Memetory/View/Meme-generate/Models/Scene/Scene.swift b/iOS/Memetory/Memetory/View/Meme-generate/Models/Scene/Scene.swift new file mode 100644 index 00000000..0bbd2eaf --- /dev/null +++ b/iOS/Memetory/Memetory/View/Meme-generate/Models/Scene/Scene.swift @@ -0,0 +1,27 @@ +// +// Scene.swift +// Memetory +// +// Created by 이승진 on 2024/03/05. +// + +import UIKit + +struct Scene : Codable { + var sourceImage: String + var targetImage: String + var text: String + var voiceId: String + + static let sceneImage = ["demo1", "demo2"] + static let templateTitle = ["해리포터", "파묘", "범죄도시4", "악마와의 토크쇼", "혹성탈출", "쿵푸팬더4"] + + static let videoTitle = ["해리포터 패러디", "파묘 패러디", "범죄도시4 패러디", "악마와의 토크쇼 패러디", "혹성탈출 패러디", "쿵푸팬더4 패러디"] + + static let timeLabel = ["0:27","0:10", "0:20", "0:35", "0:12", "0:23" ] +// lazy var sceneImage: UIImage? = { +// return UIImage(named: "demo\(num).jpeg") +// }() +// +// var num: Int +} From f049e4045bcd5cb82a6ad8979f2f0e1083ca4851 Mon Sep 17 00:00:00 2001 From: SEUNG E <105618997+SeungEEE@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:35:09 +0900 Subject: [PATCH 11/20] =?UTF-8?q?[#86]=20Member=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Meme-generate/Models/PackingItem/Member.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 iOS/Memetory/Memetory/View/Meme-generate/Models/PackingItem/Member.swift diff --git a/iOS/Memetory/Memetory/View/Meme-generate/Models/PackingItem/Member.swift b/iOS/Memetory/Memetory/View/Meme-generate/Models/PackingItem/Member.swift new file mode 100644 index 00000000..8f1f8459 --- /dev/null +++ b/iOS/Memetory/Memetory/View/Meme-generate/Models/PackingItem/Member.swift @@ -0,0 +1,13 @@ +// +// Member.swift +// Memetory +// +// Created by 이승진 on 2024/05/15. +// + +import Foundation + +struct Member : Codable { + var name : String + var imageUrl : String +} From 5171e0f127a6b4bbe60766dc71a7299311ab2486 Mon Sep 17 00:00:00 2001 From: SEUNG E <105618997+SeungEEE@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:36:50 +0900 Subject: [PATCH 12/20] =?UTF-8?q?[#86]=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../LoadingImage.imageset/Contents.json | 21 ++++++++++++++++++ .../LoadingImage.imageset/LoadingImage.png | Bin 0 -> 45128 bytes .../radio-button-line.imageset/Contents.json | 21 ++++++++++++++++++ .../radio-button-line.png | Bin 0 -> 705 bytes 4 files changed, 42 insertions(+) create mode 100644 iOS/Memetory/Memetory/Resources/Assets.xcassets/LoadingImage.imageset/Contents.json create mode 100644 iOS/Memetory/Memetory/Resources/Assets.xcassets/LoadingImage.imageset/LoadingImage.png create mode 100644 iOS/Memetory/Memetory/Resources/Assets.xcassets/radio-button-line.imageset/Contents.json create mode 100644 iOS/Memetory/Memetory/Resources/Assets.xcassets/radio-button-line.imageset/radio-button-line.png diff --git a/iOS/Memetory/Memetory/Resources/Assets.xcassets/LoadingImage.imageset/Contents.json b/iOS/Memetory/Memetory/Resources/Assets.xcassets/LoadingImage.imageset/Contents.json new file mode 100644 index 00000000..11b69e7c --- /dev/null +++ b/iOS/Memetory/Memetory/Resources/Assets.xcassets/LoadingImage.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "LoadingImage.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOS/Memetory/Memetory/Resources/Assets.xcassets/LoadingImage.imageset/LoadingImage.png b/iOS/Memetory/Memetory/Resources/Assets.xcassets/LoadingImage.imageset/LoadingImage.png new file mode 100644 index 0000000000000000000000000000000000000000..56f3417432cf7ff597e4e4dab1474a1c0a9426f0 GIT binary patch literal 45128 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

6eckbFLpCN6tghb?tf|wI9R&nguv6RclnkAZ& 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�J9g}3x$PzI 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 M07*qoM6N<$f@}JrQUCw| literal 0 HcmV?d00001 diff --git a/iOS/Memetory/Memetory/Resources/Assets.xcassets/radio-button-line.imageset/Contents.json b/iOS/Memetory/Memetory/Resources/Assets.xcassets/radio-button-line.imageset/Contents.json new file mode 100644 index 00000000..2b3d3cfa --- /dev/null +++ b/iOS/Memetory/Memetory/Resources/Assets.xcassets/radio-button-line.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "radio-button-line.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iOS/Memetory/Memetory/Resources/Assets.xcassets/radio-button-line.imageset/radio-button-line.png b/iOS/Memetory/Memetory/Resources/Assets.xcassets/radio-button-line.imageset/radio-button-line.png new file mode 100644 index 0000000000000000000000000000000000000000..36b251a615eefd52433989377d371864a3578b77 GIT binary patch literal 705 zcmV;y0zUnTP)xK~#7F)m6Q6 z(?Ag36_{8~W{~BiwT~_x<_Yi{BqdbD9bLi`#5_UZ2{2DU@&wpjN|kh+=ooNNm`NND zv6fv)Cn>fi8M=Hknmh09-|c>T2DtCQvO`BHXZ9No0O>gpbXlYlBaaZY1$cHf(Zl}= z&}gZBBKjb4pd8vld|MRAS|zmI_6a-x6gfv6aUO3d#3$1WALY9Z@fjiZ#kt%nt|f0q$nL zwXJ$~tMfU{l=qvQ3@qOyN>0dOW*82(Hz z$0E3$2IsYg)8p;#e1_lUx5(2bTR?l!%8bYdC7=NmfB*+|zFj83v*e(c`OUOdw9dgZ zA_RH`6+ohtJru%eB7BQ)`s(Cj_{hdyEVIcX?V=*=wYMjBcjw=f%FCOoGk3$O#IY2585FwFZk2f z7k+YQRHg1}J*(xm(`V z4;>Lfm==*i`emjT>HT!_?P#q4Q4o2X;-)DmN5K8*Ur+Susza Date: Tue, 18 Jun 2024 15:38:54 +0900 Subject: [PATCH 13/20] =?UTF-8?q?[#86]=20NickNameSetViewController=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Login/NickNameSetViewController.swift | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/iOS/Memetory/Memetory/View/Login/NickNameSetViewController.swift b/iOS/Memetory/Memetory/View/Login/NickNameSetViewController.swift index d7bef67b..b4d25a8b 100644 --- a/iOS/Memetory/Memetory/View/Login/NickNameSetViewController.swift +++ b/iOS/Memetory/Memetory/View/Login/NickNameSetViewController.swift @@ -34,6 +34,7 @@ class NickNameSetViewController: UIViewController { let tf = UITextField() tf.textAlignment = .left tf.borderStyle = .none + tf.textColor = .black return tf }() @@ -138,3 +139,44 @@ class NickNameSetViewController: UIViewController { } } + +extension NickNameSetViewController: UITextViewDelegate { + //화면 터치시 키보드 내림 + override func touchesBegan(_ touches: Set, with event: UIEvent?) { + self.view.endEditing(true) + } + +// func textView(_ textView: UITextView, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { +// let newText = (textView.text as NSString?)?.replacingCharacters(in: range, with: string) ?? "" +// let characterCount = newText.count +// +// if characterCount <= 50 { +// characterCountLabel.text = "\(characterCount)/50" +// return true +// } else { +// return false +// } +// } + + func textViewDidBeginEditing(_ textView: UITextView) { + // 텍스트 필드가 편집을 시작할 때 호출되는 메서드 + textView.layer.cornerRadius = 8.0 // 둥근 테두리 반지름 설정 + textView.layer.borderWidth = 1.0 // 테두리 두께 설정 +// textView.text = nil +// textField.layer.borderColor = WithYouAsset.mainColorDark.color.cgColor + } + +// func textViewDidEndEditing(_ textView: UITextView) { +//// textField.layer.borderColor = WithYouAsset.subColor.color.cgColor +// if (lineTextView.text == "") { +// lineTextView.text = "대사를 입력해주세요!" +// lineTextView.textColor = .gray +// } +// } + + func textViewShouldReturn(_ textView: UITextView) -> Bool { + // Process of closing the Keyboard when the line feed button is pressed. + textView.resignFirstResponder() + return true + } +} From dbbd801490078f06a32c278f5c0c615ec056357a Mon Sep 17 00:00:00 2001 From: SEUNG E <105618997+SeungEEE@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:39:14 +0900 Subject: [PATCH 14/20] =?UTF-8?q?[#86]=20StorageCollectionViewCell=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/StorageCollectionViewCell.swift | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 iOS/Memetory/Memetory/View/StorageCollectionViewCell.swift diff --git a/iOS/Memetory/Memetory/View/StorageCollectionViewCell.swift b/iOS/Memetory/Memetory/View/StorageCollectionViewCell.swift new file mode 100644 index 00000000..474092d9 --- /dev/null +++ b/iOS/Memetory/Memetory/View/StorageCollectionViewCell.swift @@ -0,0 +1,73 @@ +// +// StorageCollectionViewCell.swift +// Memetory +// +// Created by 이승진 on 2024/05/16. +// + +import UIKit +import SnapKit + +class StorageCollectionViewCell: UICollectionViewCell { + static let cellId = "StorageCollectionViewCell" + + let titleLabel = { + let label = UILabel() + label.textColor = .white + label.font = UIFont(name: "Pretendard-Bold", size: 15) + return label + }() + + let playButton: UIButton = { + let button = UIButton(type: .custom) + button.setImage(UIImage(systemName: "play.circle"), for: .normal) + button.tintColor = .white + return button + }() + + let timeLabel: UILabel = { + let label = UILabel() + label.textColor = .white + label.font = UIFont(name: "Pretendard-Bold", size: 12) + return label + }() + + + override init(frame : CGRect){ + super.init(frame: frame) + + self.backgroundColor = .black + setUp() + setConstraints() + } + + private func setUp(){ + self.clipsToBounds = true +// self.addSubview(mainImageView) + self.addSubview(titleLabel) + self.addSubview(playButton) + self.addSubview(timeLabel) + } + + func setConstraints() { + titleLabel.snp.makeConstraints { make in + make.top.equalToSuperview().offset(10) + make.centerX.equalTo(self.snp.centerX) + } + + playButton.snp.makeConstraints { make in + make.centerX.equalTo(self.snp.centerX) + make.centerY.equalTo(self.snp.centerY) + make.width.height.equalTo(200) + } + + timeLabel.snp.makeConstraints { make in + make.trailing.equalToSuperview().offset(-15) + make.bottom.equalToSuperview().offset(-15) + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} From 24c134c1f4beebff0642cff432f7c6aee2e0452e Mon Sep 17 00:00:00 2001 From: SEUNG E <105618997+SeungEEE@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:39:31 +0900 Subject: [PATCH 15/20] =?UTF-8?q?[#86]=20StorageViewController=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Memetory/View/StorageViewController.swift | 62 ++++++++++++++++--- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/iOS/Memetory/Memetory/View/StorageViewController.swift b/iOS/Memetory/Memetory/View/StorageViewController.swift index 1a105473..a9718f5f 100644 --- a/iOS/Memetory/Memetory/View/StorageViewController.swift +++ b/iOS/Memetory/Memetory/View/StorageViewController.swift @@ -10,6 +10,9 @@ import SnapKit class StorageViewController: UIViewController { + private let videoTitle = Scene.videoTitle + private let timeLabel = Scene.timeLabel + let titleLabel: UILabel = { let label = UILabel() label.text = "보관함" @@ -18,17 +21,37 @@ class StorageViewController: UIViewController { label.font = UIFont(name: "Pretendard-Bold", size: 25) return label }() + + lazy var gridView = { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .vertical + layout.itemSize = CGSize(width: UIScreen.main.bounds.width / 2 - 20, height: 225) + layout.minimumInteritemSpacing = 10 + + let grid = UICollectionView(frame: .zero, collectionViewLayout: layout) + grid.register(StorageCollectionViewCell.self, forCellWithReuseIdentifier: StorageCollectionViewCell.cellId) + grid.showsVerticalScrollIndicator = false + return grid + }() override func viewDidLoad() { super.viewDidLoad() - + + navigationItem.setHidesBackButton(true, animated: false) view.backgroundColor = .white + setupColletionView() setViews() setConstrainsts() } + func setupColletionView() { + gridView.delegate = self + gridView.dataSource = self + } + private func setViews() { view.addSubview(titleLabel) + view.addSubview(gridView) } private func setConstrainsts() { @@ -36,16 +59,37 @@ class StorageViewController: UIViewController { make.top.equalToSuperview().offset(80) make.leading.equalToSuperview().offset(15) } + + gridView.snp.makeConstraints{ + $0.leading.equalToSuperview().offset(15) + $0.trailing.equalToSuperview().offset(-15) + $0.top.equalTo(titleLabel.snp.bottom).offset(30) + $0.bottom.equalTo(view.safeAreaLayoutGuide) + } } +} - /* - // MARK: - Navigation - // In a storyboard-based application, you will often want to do a little preparation before navigation - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - // Get the new view controller using segue.destination. - // Pass the selected object to the new view controller. +extension StorageViewController: UICollectionViewDataSource, UICollectionViewDelegate { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + // self.items.count + return videoTitle.count + } + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: StorageCollectionViewCell.cellId, for: indexPath) as! StorageCollectionViewCell + // cell.prepare(color: self.items[indexPath.item]) + cell.titleLabel.text = videoTitle[indexPath.item] + cell.timeLabel.text = timeLabel[indexPath.item] + return cell + + } + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let selectVC = TemSelectViewController() + // settingVC.delegate = self + + // let array = memberListManager.getMemberList() + // detailVC.member = array[indexPath.row] + navigationController?.pushViewController(selectVC, animated: true) } - */ - } + From f8c6d742b93090f5bfc23958712fc50fc3efaad7 Mon Sep 17 00:00:00 2001 From: SEUNG E <105618997+SeungEEE@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:39:50 +0900 Subject: [PATCH 16/20] =?UTF-8?q?[#86]=20HomeViewController=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/Memetory/Memetory/View/HomeViewController.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/iOS/Memetory/Memetory/View/HomeViewController.swift b/iOS/Memetory/Memetory/View/HomeViewController.swift index 372321f4..6613ff56 100644 --- a/iOS/Memetory/Memetory/View/HomeViewController.swift +++ b/iOS/Memetory/Memetory/View/HomeViewController.swift @@ -18,6 +18,19 @@ class HomeViewController: UIViewController { label.font = UIFont(name: "Pretendard-Bold", size: 25) return label }() + + let monthLikeView: UIView = { + let view = UIView() + view.backgroundColor = .clear + return view + }() + + let playButton: UIButton = { + let button = UIButton(type: .custom) + button.setImage(UIImage(systemName: "play.circle"), for: .normal) + button.tintColor = .white + return button + }() override func viewDidLoad() { super.viewDidLoad() From 05ceeeef4037b6cbee59d27d5f7a4debb07acdf5 Mon Sep 17 00:00:00 2001 From: SEUNG E <105618997+SeungEEE@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:40:31 +0900 Subject: [PATCH 17/20] =?UTF-8?q?[#86]=20S3Configuration=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Memetory/AWS/S3Configuration.swift | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 iOS/Memetory/Memetory/AWS/S3Configuration.swift diff --git a/iOS/Memetory/Memetory/AWS/S3Configuration.swift b/iOS/Memetory/Memetory/AWS/S3Configuration.swift new file mode 100644 index 00000000..333ff619 --- /dev/null +++ b/iOS/Memetory/Memetory/AWS/S3Configuration.swift @@ -0,0 +1,18 @@ +// +// S3Configuration.swift +// Memetory +// +// Created by 이승진 on 2024/05/12. +// + +import Foundation + +enum S3Configuration : String +{ +// case IDENTITY_POOL_ID = "Your Identity Pool Id" + case BUCKET_NAME = "memetory" +// case CALLBACK_KEY = "Personality Message" + case CONTENT_TYPE_IMAGE = "image/jpg" + case CONTENT_TYPE_VIDEO = "video/mp4" + case CONTENT_TYPE_TXT = "text/plain" +} From 2ed995e8f94d34da16a7b484b02d29fb5314c9cf Mon Sep 17 00:00:00 2001 From: SEUNG E <105618997+SeungEEE@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:40:49 +0900 Subject: [PATCH 18/20] =?UTF-8?q?[#86]=20AWS=20=EC=9C=84=ED=95=9C=20AppDel?= =?UTF-8?q?egate=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- iOS/Memetory/Memetory/AppDelegate.swift | 46 +++++++++++++++++++++---- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/iOS/Memetory/Memetory/AppDelegate.swift b/iOS/Memetory/Memetory/AppDelegate.swift index 1c8aca02..4d360a3e 100644 --- a/iOS/Memetory/Memetory/AppDelegate.swift +++ b/iOS/Memetory/Memetory/AppDelegate.swift @@ -6,13 +6,15 @@ // import UIKit +import AWSS3 import KakaoSDKCommon +import AWSCore @main class AppDelegate: UIResponder, UIApplicationDelegate { - + var window: UIWindow? - + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. sleep(3) // 런치스크린 3초 유지 @@ -20,23 +22,53 @@ class AppDelegate: UIResponder, UIApplicationDelegate { let navigationBarAppearance = UINavigationBar.appearance() navigationBarAppearance.tintColor = .black +// return true + + // AWS S3 +// let credentialProvider = AWSCognitoCredentialsProvider(regionType: .APNortheast2, identityPoolId: S3Configuration.IDENTITY_POOL_ID.rawValue) +// guard let configuration = AWSServiceConfiguration(region: .APNortheast2, credentialsProvider: credentialProvider) else { +// print("[ FAILED TO CONNECT AWS S3 ]") +// return false +// } +// AWSS3TransferUtility.register(with: configuration, forKey: S3Configuration.CALLBACK_KEY.rawValue) +// print("[ SUCCESS TO CONNECT AWS S3 ]") +// return true + + print("Start loading environment") + // 환경 변수 로드 + Environment.load() + + // 환경 변수 자격 증명 읽기 + guard let accessKey = ProcessInfo.processInfo.environment["AWS_ACCESS_KEY"], + let secretKey = ProcessInfo.processInfo.environment["AWS_SECRET_KEY"] else { + print("Missing AWS") + return false + } + print("엑세스키: \(accessKey)") + print("시크릿키: \(secretKey)") + + let credentialsProvider = AWSStaticCredentialsProvider(accessKey: accessKey, secretKey: secretKey) + let configuration = AWSServiceConfiguration(region: .APNortheast2, credentialsProvider: credentialsProvider) + AWSServiceManager.default().defaultServiceConfiguration = configuration + + print("[ SUCCESS TO CONNECT AWS S3 ]") return true } - + // MARK: UISceneSession Lifecycle - + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { // Called when a new scene session is being created. // Use this method to select a configuration to create the new scene with. return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } - + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { // Called when the user discards a scene session. // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. // Use this method to release any resources that were specific to the discarded scenes, as they will not return. } - - + + } From 1b35770488de89acdab2f7effb61bdac38ca9307 Mon Sep 17 00:00:00 2001 From: SEUNG E <105618997+SeungEEE@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:40:57 +0900 Subject: [PATCH 19/20] Update project.pbxproj --- .../Memetory.xcodeproj/project.pbxproj | 469 +++++++++++++++++- 1 file changed, 468 insertions(+), 1 deletion(-) diff --git a/iOS/Memetory/Memetory.xcodeproj/project.pbxproj b/iOS/Memetory/Memetory.xcodeproj/project.pbxproj index b8ffa708..b310f134 100644 --- a/iOS/Memetory/Memetory.xcodeproj/project.pbxproj +++ b/iOS/Memetory/Memetory.xcodeproj/project.pbxproj @@ -10,6 +10,8 @@ 5C25F6712B74A4280038F9AD /* OnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C25F6702B74A4270038F9AD /* OnboardingViewController.swift */; }; 5C25F6732B74A6530038F9AD /* OnboardingCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C25F6722B74A6530038F9AD /* OnboardingCollectionViewCell.swift */; }; 5C25F6752B74AA6B0038F9AD /* OnboardingDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C25F6742B74AA6B0038F9AD /* OnboardingDataModel.swift */; }; + 5C2B97462C20200A000EE2A1 /* Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2B97452C20200A000EE2A1 /* Environment.swift */; }; + 5C2B97482C2022CA000EE2A1 /* S3Manager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2B97472C2022CA000EE2A1 /* S3Manager.swift */; }; 5C382FD22B95B093003DD4C6 /* TemplateCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C382FD12B95B093003DD4C6 /* TemplateCollectionViewCell.swift */; }; 5C382FD42B95E49D003DD4C6 /* CheckViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C382FD32B95E49D003DD4C6 /* CheckViewController.swift */; }; 5C382FD62B95E4BC003DD4C6 /* VoiceSelectViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C382FD52B95E4BC003DD4C6 /* VoiceSelectViewController.swift */; }; @@ -59,16 +61,72 @@ 5CAE246D2BC804DA00322AC6 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CAE246C2BC804DA00322AC6 /* Constants.swift */; }; 5CAE24702BC91C0400322AC6 /* GoogleSignIn in Frameworks */ = {isa = PBXBuildFile; productRef = 5CAE246F2BC91C0400322AC6 /* GoogleSignIn */; }; 5CAE24722BC91C0400322AC6 /* GoogleSignInSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 5CAE24712BC91C0400322AC6 /* GoogleSignInSwift */; }; + 5CC28F882BF4A60F00089EC7 /* Member.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC28F872BF4A60F00089EC7 /* Member.swift */; }; + 5CC28F8A2BF4BFE200089EC7 /* VoiceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC28F892BF4BFE200089EC7 /* VoiceManager.swift */; }; + 5CC28F8C2BF5AC8900089EC7 /* StorageCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC28F8B2BF5AC8900089EC7 /* StorageCollectionViewCell.swift */; }; + 5CC28F8E2BF5BEDF00089EC7 /* LoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC28F8D2BF5BEDF00089EC7 /* LoadingView.swift */; }; + 5CC28F902BF5C02F00089EC7 /* LoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC28F8F2BF5C02F00089EC7 /* LoadingViewController.swift */; }; 5CC4BADD2B68E3DD00A2D259 /* NickNameSetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC4BADC2B68E3DD00A2D259 /* NickNameSetViewController.swift */; }; 5CC889BF2BCD0EDB0074FFA2 /* DataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC889BE2BCD0EDB0074FFA2 /* DataManager.swift */; }; 5CC889C12BCD0F060074FFA2 /* SecureDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC889C02BCD0F060074FFA2 /* SecureDataManager.swift */; }; 5CE3A12E2BEFD453001BF120 /* MainTemplateCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CE3A12D2BEFD453001BF120 /* MainTemplateCollectionViewCell.swift */; }; + 5CE3A1312BF0A804001BF120 /* AWSAPIGateway in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1302BF0A804001BF120 /* AWSAPIGateway */; }; + 5CE3A1332BF0A804001BF120 /* AWSAppleSignIn in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1322BF0A804001BF120 /* AWSAppleSignIn */; }; + 5CE3A1352BF0A804001BF120 /* AWSAuthCore in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1342BF0A804001BF120 /* AWSAuthCore */; }; + 5CE3A1372BF0A804001BF120 /* AWSAuthUI in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1362BF0A804001BF120 /* AWSAuthUI */; }; + 5CE3A1392BF0A804001BF120 /* AWSAutoScaling in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1382BF0A804001BF120 /* AWSAutoScaling */; }; + 5CE3A13B2BF0A804001BF120 /* AWSChimeSDKIdentity in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A13A2BF0A804001BF120 /* AWSChimeSDKIdentity */; }; + 5CE3A13D2BF0A804001BF120 /* AWSChimeSDKMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A13C2BF0A804001BF120 /* AWSChimeSDKMessaging */; }; + 5CE3A13F2BF0A804001BF120 /* AWSCloudWatch in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A13E2BF0A804001BF120 /* AWSCloudWatch */; }; + 5CE3A1412BF0A804001BF120 /* AWSCognitoAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1402BF0A804001BF120 /* AWSCognitoAuth */; }; + 5CE3A1432BF0A804001BF120 /* AWSCognitoIdentityProvider in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1422BF0A804001BF120 /* AWSCognitoIdentityProvider */; }; + 5CE3A1452BF0A804001BF120 /* AWSCognitoIdentityProviderASF in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1442BF0A804001BF120 /* AWSCognitoIdentityProviderASF */; }; + 5CE3A1472BF0A804001BF120 /* AWSComprehend in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1462BF0A804001BF120 /* AWSComprehend */; }; + 5CE3A1492BF0A804001BF120 /* AWSConnect in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1482BF0A804001BF120 /* AWSConnect */; }; + 5CE3A14B2BF0A804001BF120 /* AWSConnectParticipant in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A14A2BF0A804001BF120 /* AWSConnectParticipant */; }; + 5CE3A14D2BF0A804001BF120 /* AWSCore in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A14C2BF0A804001BF120 /* AWSCore */; }; + 5CE3A14F2BF0A804001BF120 /* AWSDynamoDB in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A14E2BF0A804001BF120 /* AWSDynamoDB */; }; + 5CE3A1512BF0A804001BF120 /* AWSEC2 in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1502BF0A804001BF120 /* AWSEC2 */; }; + 5CE3A1532BF0A804001BF120 /* AWSElasticLoadBalancing in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1522BF0A804001BF120 /* AWSElasticLoadBalancing */; }; + 5CE3A1552BF0A804001BF120 /* AWSFacebookSignIn in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1542BF0A804001BF120 /* AWSFacebookSignIn */; }; + 5CE3A1572BF0A804001BF120 /* AWSGoogleSignIn in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1562BF0A804001BF120 /* AWSGoogleSignIn */; }; + 5CE3A1592BF0A804001BF120 /* AWSIoT in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1582BF0A804001BF120 /* AWSIoT */; }; + 5CE3A15B2BF0A804001BF120 /* AWSKMS in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A15A2BF0A804001BF120 /* AWSKMS */; }; + 5CE3A15D2BF0A804001BF120 /* AWSKinesis in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A15C2BF0A804001BF120 /* AWSKinesis */; }; + 5CE3A15F2BF0A804001BF120 /* AWSKinesisVideo in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A15E2BF0A804001BF120 /* AWSKinesisVideo */; }; + 5CE3A1612BF0A804001BF120 /* AWSKinesisVideoArchivedMedia in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1602BF0A804001BF120 /* AWSKinesisVideoArchivedMedia */; }; + 5CE3A1632BF0A804001BF120 /* AWSKinesisVideoSignaling in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1622BF0A804001BF120 /* AWSKinesisVideoSignaling */; }; + 5CE3A1652BF0A804001BF120 /* AWSKinesisVideoWebRTCStorage in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1642BF0A804001BF120 /* AWSKinesisVideoWebRTCStorage */; }; + 5CE3A1672BF0A804001BF120 /* AWSLambda in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1662BF0A804001BF120 /* AWSLambda */; }; + 5CE3A1692BF0A804001BF120 /* AWSLex in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1682BF0A804001BF120 /* AWSLex */; }; + 5CE3A16B2BF0A804001BF120 /* AWSLocationXCF in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A16A2BF0A804001BF120 /* AWSLocationXCF */; }; + 5CE3A16D2BF0A804001BF120 /* AWSLogs in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A16C2BF0A804001BF120 /* AWSLogs */; }; + 5CE3A16F2BF0A804001BF120 /* AWSMachineLearning in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A16E2BF0A804001BF120 /* AWSMachineLearning */; }; + 5CE3A1712BF0A804001BF120 /* AWSMobileClientXCF in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1702BF0A804001BF120 /* AWSMobileClientXCF */; }; + 5CE3A1732BF0A804001BF120 /* AWSPinpoint in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1722BF0A804001BF120 /* AWSPinpoint */; }; + 5CE3A1752BF0A804001BF120 /* AWSPolly in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1742BF0A804001BF120 /* AWSPolly */; }; + 5CE3A1772BF0A804001BF120 /* AWSRekognition in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1762BF0A804001BF120 /* AWSRekognition */; }; + 5CE3A1792BF0A804001BF120 /* AWSS3 in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1782BF0A804001BF120 /* AWSS3 */; }; + 5CE3A17B2BF0A804001BF120 /* AWSSES in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A17A2BF0A804001BF120 /* AWSSES */; }; + 5CE3A17D2BF0A804001BF120 /* AWSSNS in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A17C2BF0A804001BF120 /* AWSSNS */; }; + 5CE3A17F2BF0A804001BF120 /* AWSSQS in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A17E2BF0A804001BF120 /* AWSSQS */; }; + 5CE3A1812BF0A804001BF120 /* AWSSageMakerRuntime in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1802BF0A804001BF120 /* AWSSageMakerRuntime */; }; + 5CE3A1832BF0A804001BF120 /* AWSSimpleDB in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1822BF0A804001BF120 /* AWSSimpleDB */; }; + 5CE3A1852BF0A804001BF120 /* AWSTextract in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1842BF0A804001BF120 /* AWSTextract */; }; + 5CE3A1872BF0A804001BF120 /* AWSTranscribe in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1862BF0A804001BF120 /* AWSTranscribe */; }; + 5CE3A1892BF0A804001BF120 /* AWSTranscribeStreaming in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A1882BF0A804001BF120 /* AWSTranscribeStreaming */; }; + 5CE3A18B2BF0A804001BF120 /* AWSTranslate in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A18A2BF0A804001BF120 /* AWSTranslate */; }; + 5CE3A18D2BF0A804001BF120 /* AWSUserPoolsSignIn in Frameworks */ = {isa = PBXBuildFile; productRef = 5CE3A18C2BF0A804001BF120 /* AWSUserPoolsSignIn */; }; + 5CE3A1902BF0C6CA001BF120 /* S3Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CE3A18F2BF0C6CA001BF120 /* S3Configuration.swift */; }; + 5CF617842BF328FD00CCB297 /* Voice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF617832BF328FD00CCB297 /* Voice.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ 5C25F6702B74A4270038F9AD /* OnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewController.swift; sourceTree = ""; }; 5C25F6722B74A6530038F9AD /* OnboardingCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingCollectionViewCell.swift; sourceTree = ""; }; 5C25F6742B74AA6B0038F9AD /* OnboardingDataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingDataModel.swift; sourceTree = ""; }; + 5C2B97452C20200A000EE2A1 /* Environment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Environment.swift; sourceTree = ""; }; + 5C2B97472C2022CA000EE2A1 /* S3Manager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = S3Manager.swift; sourceTree = ""; }; 5C382FD12B95B093003DD4C6 /* TemplateCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateCollectionViewCell.swift; sourceTree = ""; }; 5C382FD32B95E49D003DD4C6 /* CheckViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckViewController.swift; sourceTree = ""; }; 5C382FD52B95E4BC003DD4C6 /* VoiceSelectViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceSelectViewController.swift; sourceTree = ""; }; @@ -104,10 +162,17 @@ 5CAE24682BC8040900322AC6 /* RequestParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestParams.swift; sourceTree = ""; }; 5CAE246A2BC8043D00322AC6 /* APIContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIContainer.swift; sourceTree = ""; }; 5CAE246C2BC804DA00322AC6 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + 5CC28F872BF4A60F00089EC7 /* Member.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Member.swift; sourceTree = ""; }; + 5CC28F892BF4BFE200089EC7 /* VoiceManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VoiceManager.swift; sourceTree = ""; }; + 5CC28F8B2BF5AC8900089EC7 /* StorageCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageCollectionViewCell.swift; sourceTree = ""; }; + 5CC28F8D2BF5BEDF00089EC7 /* LoadingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingView.swift; sourceTree = ""; }; + 5CC28F8F2BF5C02F00089EC7 /* LoadingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingViewController.swift; sourceTree = ""; }; 5CC4BADC2B68E3DD00A2D259 /* NickNameSetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NickNameSetViewController.swift; sourceTree = ""; }; 5CC889BE2BCD0EDB0074FFA2 /* DataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataManager.swift; sourceTree = ""; }; 5CC889C02BCD0F060074FFA2 /* SecureDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureDataManager.swift; sourceTree = ""; }; 5CE3A12D2BEFD453001BF120 /* MainTemplateCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTemplateCollectionViewCell.swift; sourceTree = ""; }; + 5CE3A18F2BF0C6CA001BF120 /* S3Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = S3Configuration.swift; sourceTree = ""; }; + 5CF617832BF328FD00CCB297 /* Voice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Voice.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -115,21 +180,68 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 5CE3A17B2BF0A804001BF120 /* AWSSES in Frameworks */, 5CAE24532BC7FFCD00322AC6 /* KakaoSDKCertCore in Frameworks */, + 5CE3A16D2BF0A804001BF120 /* AWSLogs in Frameworks */, + 5CE3A16F2BF0A804001BF120 /* AWSMachineLearning in Frameworks */, + 5CE3A1472BF0A804001BF120 /* AWSComprehend in Frameworks */, + 5CE3A18B2BF0A804001BF120 /* AWSTranslate in Frameworks */, + 5CE3A1332BF0A804001BF120 /* AWSAppleSignIn in Frameworks */, + 5CE3A1572BF0A804001BF120 /* AWSGoogleSignIn in Frameworks */, + 5CE3A1632BF0A804001BF120 /* AWSKinesisVideoSignaling in Frameworks */, + 5CE3A1832BF0A804001BF120 /* AWSSimpleDB in Frameworks */, + 5CE3A1812BF0A804001BF120 /* AWSSageMakerRuntime in Frameworks */, 5CAE244D2BC7FFCD00322AC6 /* KakaoSDK in Frameworks */, + 5CE3A14F2BF0A804001BF120 /* AWSDynamoDB in Frameworks */, + 5CE3A1732BF0A804001BF120 /* AWSPinpoint in Frameworks */, + 5CE3A15B2BF0A804001BF120 /* AWSKMS in Frameworks */, + 5CE3A18D2BF0A804001BF120 /* AWSUserPoolsSignIn in Frameworks */, + 5CE3A1892BF0A804001BF120 /* AWSTranscribeStreaming in Frameworks */, + 5CE3A15F2BF0A804001BF120 /* AWSKinesisVideo in Frameworks */, + 5CE3A1452BF0A804001BF120 /* AWSCognitoIdentityProviderASF in Frameworks */, + 5CE3A1532BF0A804001BF120 /* AWSElasticLoadBalancing in Frameworks */, + 5CE3A14B2BF0A804001BF120 /* AWSConnectParticipant in Frameworks */, + 5CE3A17F2BF0A804001BF120 /* AWSSQS in Frameworks */, + 5CE3A1552BF0A804001BF120 /* AWSFacebookSignIn in Frameworks */, + 5CE3A1672BF0A804001BF120 /* AWSLambda in Frameworks */, 5CAB5B642B610F5500B2AF59 /* Alamofire in Frameworks */, + 5CE3A1372BF0A804001BF120 /* AWSAuthUI in Frameworks */, 5CAE24722BC91C0400322AC6 /* GoogleSignInSwift in Frameworks */, + 5CE3A1792BF0A804001BF120 /* AWSS3 in Frameworks */, + 5CE3A14D2BF0A804001BF120 /* AWSCore in Frameworks */, + 5CE3A13F2BF0A804001BF120 /* AWSCloudWatch in Frameworks */, + 5CE3A1852BF0A804001BF120 /* AWSTextract in Frameworks */, 5CAE24612BC7FFCD00322AC6 /* KakaoSDKTemplate in Frameworks */, 5CAE24552BC7FFCD00322AC6 /* KakaoSDKCommon in Frameworks */, 5CAE245F2BC7FFCD00322AC6 /* KakaoSDKTalk in Frameworks */, + 5CE3A1712BF0A804001BF120 /* AWSMobileClientXCF in Frameworks */, 5CAE245B2BC7FFCD00322AC6 /* KakaoSDKNavi in Frameworks */, 5CAE244F2BC7FFCD00322AC6 /* KakaoSDKAuth in Frameworks */, + 5CE3A1612BF0A804001BF120 /* AWSKinesisVideoArchivedMedia in Frameworks */, 5CAE24572BC7FFCD00322AC6 /* KakaoSDKFriend in Frameworks */, + 5CE3A13D2BF0A804001BF120 /* AWSChimeSDKMessaging in Frameworks */, + 5CE3A1872BF0A804001BF120 /* AWSTranscribe in Frameworks */, + 5CE3A16B2BF0A804001BF120 /* AWSLocationXCF in Frameworks */, + 5CE3A1392BF0A804001BF120 /* AWSAutoScaling in Frameworks */, 5CAE24702BC91C0400322AC6 /* GoogleSignIn in Frameworks */, + 5CE3A1512BF0A804001BF120 /* AWSEC2 in Frameworks */, 5CAE24512BC7FFCD00322AC6 /* KakaoSDKCert in Frameworks */, 5CAE245D2BC7FFCD00322AC6 /* KakaoSDKShare in Frameworks */, + 5CE3A15D2BF0A804001BF120 /* AWSKinesis in Frameworks */, + 5CE3A13B2BF0A804001BF120 /* AWSChimeSDKIdentity in Frameworks */, + 5CE3A1352BF0A804001BF120 /* AWSAuthCore in Frameworks */, + 5CE3A1692BF0A804001BF120 /* AWSLex in Frameworks */, + 5CE3A1312BF0A804001BF120 /* AWSAPIGateway in Frameworks */, 5CAB5B672B6110F600B2AF59 /* SnapKit in Frameworks */, + 5CE3A1752BF0A804001BF120 /* AWSPolly in Frameworks */, 5CAE24632BC7FFCD00322AC6 /* KakaoSDKUser in Frameworks */, + 5CE3A1592BF0A804001BF120 /* AWSIoT in Frameworks */, + 5CE3A1772BF0A804001BF120 /* AWSRekognition in Frameworks */, + 5CE3A1432BF0A804001BF120 /* AWSCognitoIdentityProvider in Frameworks */, + 5CE3A17D2BF0A804001BF120 /* AWSSNS in Frameworks */, + 5CE3A1652BF0A804001BF120 /* AWSKinesisVideoWebRTCStorage in Frameworks */, + 5CE3A1492BF0A804001BF120 /* AWSConnect in Frameworks */, + 5CE3A1412BF0A804001BF120 /* AWSCognitoAuth in Frameworks */, 5CAE24592BC7FFCD00322AC6 /* KakaoSDKFriendCore in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -180,6 +292,8 @@ isa = PBXGroup; children = ( 5C382FD52B95E4BC003DD4C6 /* VoiceSelectViewController.swift */, + 5CC28F8D2BF5BEDF00089EC7 /* LoadingView.swift */, + 5CC28F8F2BF5C02F00089EC7 /* LoadingViewController.swift */, 5C382FDA2B95E654003DD4C6 /* VoiceTableViewCell.swift */, ); path = Voice; @@ -188,7 +302,10 @@ 5C382FDC2B96EF37003DD4C6 /* Models */ = { isa = PBXGroup; children = ( - 5C382FDD2B96EF8E003DD4C6 /* Scene.swift */, + 5CC28F862BF4A60100089EC7 /* PackingItem */, + 5CF617872BF32B3400CCB297 /* Text */, + 5CF617852BF32B1900CCB297 /* Scene */, + 5CF617862BF32B2200CCB297 /* Vocie */, ); path = Models; sourceTree = ""; @@ -223,6 +340,7 @@ 5C4BB46B2B7B55280045FB28 /* HomeViewController.swift */, 5C4BB46D2B7B55540045FB28 /* MemesViewController.swift */, 5C4BB46F2B7B55720045FB28 /* StorageViewController.swift */, + 5CC28F8B2BF5AC8900089EC7 /* StorageCollectionViewCell.swift */, 5C4BB4652B7B4FAC0045FB28 /* Meme-generate */, 5C25F66E2B74A3EB0038F9AD /* Login */, ); @@ -277,6 +395,7 @@ 5CC889BC2BCD0EA30074FFA2 /* Sources */, 5CAB5B4E2B610EA600B2AF59 /* AppDelegate.swift */, 5CAB5B502B610EA600B2AF59 /* SceneDelegate.swift */, + 5CE3A18E2BF0A82A001BF120 /* AWS */, 5C8C1C0A2BC6A1A100E44813 /* Networking */, 5C83D3592B68DAA500F34F0B /* View */, 5CAB5B5C2B610EA800B2AF59 /* Info.plist */, @@ -312,6 +431,14 @@ path = Resources; sourceTree = ""; }; + 5CC28F862BF4A60100089EC7 /* PackingItem */ = { + isa = PBXGroup; + children = ( + 5CC28F872BF4A60F00089EC7 /* Member.swift */, + ); + path = PackingItem; + sourceTree = ""; + }; 5CC889BC2BCD0EA30074FFA2 /* Sources */ = { isa = PBXGroup; children = ( @@ -329,6 +456,40 @@ path = Common; sourceTree = ""; }; + 5CE3A18E2BF0A82A001BF120 /* AWS */ = { + isa = PBXGroup; + children = ( + 5CE3A18F2BF0C6CA001BF120 /* S3Configuration.swift */, + 5C2B97452C20200A000EE2A1 /* Environment.swift */, + 5C2B97472C2022CA000EE2A1 /* S3Manager.swift */, + ); + path = AWS; + sourceTree = ""; + }; + 5CF617852BF32B1900CCB297 /* Scene */ = { + isa = PBXGroup; + children = ( + 5C382FDD2B96EF8E003DD4C6 /* Scene.swift */, + ); + path = Scene; + sourceTree = ""; + }; + 5CF617862BF32B2200CCB297 /* Vocie */ = { + isa = PBXGroup; + children = ( + 5CF617832BF328FD00CCB297 /* Voice.swift */, + 5CC28F892BF4BFE200089EC7 /* VoiceManager.swift */, + ); + path = Vocie; + sourceTree = ""; + }; + 5CF617872BF32B3400CCB297 /* Text */ = { + isa = PBXGroup; + children = ( + ); + path = Text; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -362,6 +523,53 @@ 5CAE24622BC7FFCD00322AC6 /* KakaoSDKUser */, 5CAE246F2BC91C0400322AC6 /* GoogleSignIn */, 5CAE24712BC91C0400322AC6 /* GoogleSignInSwift */, + 5CE3A1302BF0A804001BF120 /* AWSAPIGateway */, + 5CE3A1322BF0A804001BF120 /* AWSAppleSignIn */, + 5CE3A1342BF0A804001BF120 /* AWSAuthCore */, + 5CE3A1362BF0A804001BF120 /* AWSAuthUI */, + 5CE3A1382BF0A804001BF120 /* AWSAutoScaling */, + 5CE3A13A2BF0A804001BF120 /* AWSChimeSDKIdentity */, + 5CE3A13C2BF0A804001BF120 /* AWSChimeSDKMessaging */, + 5CE3A13E2BF0A804001BF120 /* AWSCloudWatch */, + 5CE3A1402BF0A804001BF120 /* AWSCognitoAuth */, + 5CE3A1422BF0A804001BF120 /* AWSCognitoIdentityProvider */, + 5CE3A1442BF0A804001BF120 /* AWSCognitoIdentityProviderASF */, + 5CE3A1462BF0A804001BF120 /* AWSComprehend */, + 5CE3A1482BF0A804001BF120 /* AWSConnect */, + 5CE3A14A2BF0A804001BF120 /* AWSConnectParticipant */, + 5CE3A14C2BF0A804001BF120 /* AWSCore */, + 5CE3A14E2BF0A804001BF120 /* AWSDynamoDB */, + 5CE3A1502BF0A804001BF120 /* AWSEC2 */, + 5CE3A1522BF0A804001BF120 /* AWSElasticLoadBalancing */, + 5CE3A1542BF0A804001BF120 /* AWSFacebookSignIn */, + 5CE3A1562BF0A804001BF120 /* AWSGoogleSignIn */, + 5CE3A1582BF0A804001BF120 /* AWSIoT */, + 5CE3A15A2BF0A804001BF120 /* AWSKMS */, + 5CE3A15C2BF0A804001BF120 /* AWSKinesis */, + 5CE3A15E2BF0A804001BF120 /* AWSKinesisVideo */, + 5CE3A1602BF0A804001BF120 /* AWSKinesisVideoArchivedMedia */, + 5CE3A1622BF0A804001BF120 /* AWSKinesisVideoSignaling */, + 5CE3A1642BF0A804001BF120 /* AWSKinesisVideoWebRTCStorage */, + 5CE3A1662BF0A804001BF120 /* AWSLambda */, + 5CE3A1682BF0A804001BF120 /* AWSLex */, + 5CE3A16A2BF0A804001BF120 /* AWSLocationXCF */, + 5CE3A16C2BF0A804001BF120 /* AWSLogs */, + 5CE3A16E2BF0A804001BF120 /* AWSMachineLearning */, + 5CE3A1702BF0A804001BF120 /* AWSMobileClientXCF */, + 5CE3A1722BF0A804001BF120 /* AWSPinpoint */, + 5CE3A1742BF0A804001BF120 /* AWSPolly */, + 5CE3A1762BF0A804001BF120 /* AWSRekognition */, + 5CE3A1782BF0A804001BF120 /* AWSS3 */, + 5CE3A17A2BF0A804001BF120 /* AWSSES */, + 5CE3A17C2BF0A804001BF120 /* AWSSNS */, + 5CE3A17E2BF0A804001BF120 /* AWSSQS */, + 5CE3A1802BF0A804001BF120 /* AWSSageMakerRuntime */, + 5CE3A1822BF0A804001BF120 /* AWSSimpleDB */, + 5CE3A1842BF0A804001BF120 /* AWSTextract */, + 5CE3A1862BF0A804001BF120 /* AWSTranscribe */, + 5CE3A1882BF0A804001BF120 /* AWSTranscribeStreaming */, + 5CE3A18A2BF0A804001BF120 /* AWSTranslate */, + 5CE3A18C2BF0A804001BF120 /* AWSUserPoolsSignIn */, ); productName = Memetory; productReference = 5CAB5B4B2B610EA600B2AF59 /* Memetory.app */; @@ -396,6 +604,7 @@ 5CAB5B652B6110F500B2AF59 /* XCRemoteSwiftPackageReference "SnapKit" */, 5CAE244B2BC7FFCD00322AC6 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */, 5CAE246E2BC91C0400322AC6 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */, + 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */, ); productRefGroup = 5CAB5B4C2B610EA600B2AF59 /* Products */; projectDirPath = ""; @@ -429,33 +638,42 @@ 5CE3A12E2BEFD453001BF120 /* MainTemplateCollectionViewCell.swift in Sources */, 5C4BB4722B7B58AF0045FB28 /* GenerateViewController.swift in Sources */, 5CC889BF2BCD0EDB0074FFA2 /* DataManager.swift in Sources */, + 5C2B97482C2022CA000EE2A1 /* S3Manager.swift in Sources */, 5CC889C12BCD0F060074FFA2 /* SecureDataManager.swift in Sources */, 5C83D35B2B68DABC00F34F0B /* LoginViewController.swift in Sources */, 5C25F6712B74A4280038F9AD /* OnboardingViewController.swift in Sources */, + 5CE3A1902BF0C6CA001BF120 /* S3Configuration.swift in Sources */, 5C382FD22B95B093003DD4C6 /* TemplateCollectionViewCell.swift in Sources */, 5C382FD62B95E4BC003DD4C6 /* VoiceSelectViewController.swift in Sources */, 5C4BB4702B7B55720045FB28 /* StorageViewController.swift in Sources */, 5CAE24672BC803EA00322AC6 /* HeaderType.swift in Sources */, 5C25F6752B74AA6B0038F9AD /* OnboardingDataModel.swift in Sources */, + 5CC28F8A2BF4BFE200089EC7 /* VoiceManager.swift in Sources */, 5CAE246B2BC8043E00322AC6 /* APIContainer.swift in Sources */, + 5CC28F8C2BF5AC8900089EC7 /* StorageCollectionViewCell.swift in Sources */, 5CAE246D2BC804DA00322AC6 /* Constants.swift in Sources */, 5CC4BADD2B68E3DD00A2D259 /* NickNameSetViewController.swift in Sources */, 5C4BB4682B7B501F0045FB28 /* TabBarViewController.swift in Sources */, 5CAB5B532B610EA600B2AF59 /* ViewController.swift in Sources */, 5CAB5B4F2B610EA600B2AF59 /* AppDelegate.swift in Sources */, 5C382FDB2B95E654003DD4C6 /* VoiceTableViewCell.swift in Sources */, + 5CC28F882BF4A60F00089EC7 /* Member.swift in Sources */, 5C4BB46C2B7B55280045FB28 /* HomeViewController.swift in Sources */, 5C25F6732B74A6530038F9AD /* OnboardingCollectionViewCell.swift in Sources */, 5C382FD42B95E49D003DD4C6 /* CheckViewController.swift in Sources */, 5C8C1C132BC6A21400E44813 /* AuthService.swift in Sources */, + 5CC28F8E2BF5BEDF00089EC7 /* LoadingView.swift in Sources */, 5CAB5B512B610EA600B2AF59 /* SceneDelegate.swift in Sources */, 5C4BB46E2B7B55540045FB28 /* MemesViewController.swift in Sources */, 5CAE24652BC803A600322AC6 /* APIEventLogger.swift in Sources */, 5C8C1C112BC6A20500E44813 /* AuthRouter.swift in Sources */, 5CAE24692BC8040900322AC6 /* RequestParams.swift in Sources */, 5C4BB4742B7B5E300045FB28 /* TemplateTableViewCell.swift in Sources */, + 5C2B97462C20200A000EE2A1 /* Environment.swift in Sources */, 5CAE24342BC7EFAA00322AC6 /* BaseService.swift in Sources */, 5C4BB4762B7B62D30045FB28 /* TemSelectViewController.swift in Sources */, + 5CC28F902BF5C02F00089EC7 /* LoadingViewController.swift in Sources */, + 5CF617842BF328FD00CCB297 /* Voice.swift in Sources */, 5C9FE6062BE3DD020037EFCD /* Encodable+Extension.swift in Sources */, 5C382FDE2B96EF8E003DD4C6 /* Scene.swift in Sources */, 5CAE24322BC7EF9D00322AC6 /* BaseRouter.swift in Sources */, @@ -605,11 +823,14 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = Memetory/Memetory.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = D8KHM6Q8SA; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Memetory/Info.plist; + INFOPLIST_KEY_NSMicrophoneUsageDescription = "Use mic to record"; + INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "\"사진 라이브러리에 접근하여 음성 파일을 선택합니다.\""; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIMainStoryboardFile = Main; @@ -633,11 +854,14 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = Memetory/Memetory.entitlements; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = D8KHM6Q8SA; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Memetory/Info.plist; + INFOPLIST_KEY_NSMicrophoneUsageDescription = "Use mic to record"; + INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "\"사진 라이브러리에 접근하여 음성 파일을 선택합니다.\""; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIMainStoryboardFile = Main; @@ -712,6 +936,14 @@ minimumVersion = 7.1.0; }; }; + 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/aws-amplify/aws-sdk-ios-spm"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.36.1; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -795,6 +1027,241 @@ package = 5CAE246E2BC91C0400322AC6 /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */; productName = GoogleSignInSwift; }; + 5CE3A1302BF0A804001BF120 /* AWSAPIGateway */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSAPIGateway; + }; + 5CE3A1322BF0A804001BF120 /* AWSAppleSignIn */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSAppleSignIn; + }; + 5CE3A1342BF0A804001BF120 /* AWSAuthCore */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSAuthCore; + }; + 5CE3A1362BF0A804001BF120 /* AWSAuthUI */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSAuthUI; + }; + 5CE3A1382BF0A804001BF120 /* AWSAutoScaling */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSAutoScaling; + }; + 5CE3A13A2BF0A804001BF120 /* AWSChimeSDKIdentity */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSChimeSDKIdentity; + }; + 5CE3A13C2BF0A804001BF120 /* AWSChimeSDKMessaging */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSChimeSDKMessaging; + }; + 5CE3A13E2BF0A804001BF120 /* AWSCloudWatch */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSCloudWatch; + }; + 5CE3A1402BF0A804001BF120 /* AWSCognitoAuth */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSCognitoAuth; + }; + 5CE3A1422BF0A804001BF120 /* AWSCognitoIdentityProvider */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSCognitoIdentityProvider; + }; + 5CE3A1442BF0A804001BF120 /* AWSCognitoIdentityProviderASF */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSCognitoIdentityProviderASF; + }; + 5CE3A1462BF0A804001BF120 /* AWSComprehend */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSComprehend; + }; + 5CE3A1482BF0A804001BF120 /* AWSConnect */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSConnect; + }; + 5CE3A14A2BF0A804001BF120 /* AWSConnectParticipant */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSConnectParticipant; + }; + 5CE3A14C2BF0A804001BF120 /* AWSCore */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSCore; + }; + 5CE3A14E2BF0A804001BF120 /* AWSDynamoDB */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSDynamoDB; + }; + 5CE3A1502BF0A804001BF120 /* AWSEC2 */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSEC2; + }; + 5CE3A1522BF0A804001BF120 /* AWSElasticLoadBalancing */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSElasticLoadBalancing; + }; + 5CE3A1542BF0A804001BF120 /* AWSFacebookSignIn */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSFacebookSignIn; + }; + 5CE3A1562BF0A804001BF120 /* AWSGoogleSignIn */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSGoogleSignIn; + }; + 5CE3A1582BF0A804001BF120 /* AWSIoT */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSIoT; + }; + 5CE3A15A2BF0A804001BF120 /* AWSKMS */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSKMS; + }; + 5CE3A15C2BF0A804001BF120 /* AWSKinesis */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSKinesis; + }; + 5CE3A15E2BF0A804001BF120 /* AWSKinesisVideo */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSKinesisVideo; + }; + 5CE3A1602BF0A804001BF120 /* AWSKinesisVideoArchivedMedia */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSKinesisVideoArchivedMedia; + }; + 5CE3A1622BF0A804001BF120 /* AWSKinesisVideoSignaling */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSKinesisVideoSignaling; + }; + 5CE3A1642BF0A804001BF120 /* AWSKinesisVideoWebRTCStorage */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSKinesisVideoWebRTCStorage; + }; + 5CE3A1662BF0A804001BF120 /* AWSLambda */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSLambda; + }; + 5CE3A1682BF0A804001BF120 /* AWSLex */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSLex; + }; + 5CE3A16A2BF0A804001BF120 /* AWSLocationXCF */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSLocationXCF; + }; + 5CE3A16C2BF0A804001BF120 /* AWSLogs */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSLogs; + }; + 5CE3A16E2BF0A804001BF120 /* AWSMachineLearning */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSMachineLearning; + }; + 5CE3A1702BF0A804001BF120 /* AWSMobileClientXCF */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSMobileClientXCF; + }; + 5CE3A1722BF0A804001BF120 /* AWSPinpoint */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSPinpoint; + }; + 5CE3A1742BF0A804001BF120 /* AWSPolly */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSPolly; + }; + 5CE3A1762BF0A804001BF120 /* AWSRekognition */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSRekognition; + }; + 5CE3A1782BF0A804001BF120 /* AWSS3 */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSS3; + }; + 5CE3A17A2BF0A804001BF120 /* AWSSES */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSSES; + }; + 5CE3A17C2BF0A804001BF120 /* AWSSNS */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSSNS; + }; + 5CE3A17E2BF0A804001BF120 /* AWSSQS */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSSQS; + }; + 5CE3A1802BF0A804001BF120 /* AWSSageMakerRuntime */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSSageMakerRuntime; + }; + 5CE3A1822BF0A804001BF120 /* AWSSimpleDB */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSSimpleDB; + }; + 5CE3A1842BF0A804001BF120 /* AWSTextract */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSTextract; + }; + 5CE3A1862BF0A804001BF120 /* AWSTranscribe */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSTranscribe; + }; + 5CE3A1882BF0A804001BF120 /* AWSTranscribeStreaming */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSTranscribeStreaming; + }; + 5CE3A18A2BF0A804001BF120 /* AWSTranslate */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSTranslate; + }; + 5CE3A18C2BF0A804001BF120 /* AWSUserPoolsSignIn */ = { + isa = XCSwiftPackageProductDependency; + package = 5CE3A12F2BF0A804001BF120 /* XCRemoteSwiftPackageReference "aws-sdk-ios-spm" */; + productName = AWSUserPoolsSignIn; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 5CAB5B432B610EA600B2AF59 /* Project object */; From 3e5579d81d7a9eebee367d239dd0ff34d8db605e Mon Sep 17 00:00:00 2001 From: SEUNG E <105618997+SeungEEE@users.noreply.github.com> Date: Tue, 18 Jun 2024 15:44:50 +0900 Subject: [PATCH 20/20] =?UTF-8?q?[#86]=20VoiceManager=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Models/Vocie/VoiceManager.swift | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 iOS/Memetory/Memetory/View/Meme-generate/Models/Vocie/VoiceManager.swift diff --git a/iOS/Memetory/Memetory/View/Meme-generate/Models/Vocie/VoiceManager.swift b/iOS/Memetory/Memetory/View/Meme-generate/Models/Vocie/VoiceManager.swift new file mode 100644 index 00000000..6fe2c3eb --- /dev/null +++ b/iOS/Memetory/Memetory/View/Meme-generate/Models/Vocie/VoiceManager.swift @@ -0,0 +1,49 @@ +// +// VoiceManager.swift +// Memetory +// +// Created by 이승진 on 2024/05/15. +// + +import UIKit +import AVFoundation +import AWSS3 +import AWSCore + +class VoiceManager { + + let S3BucketName = "memetory" + + func uploadVoiceToS3(_ voiceURL: URL, completion: @escaping (String?) -> Void) { + + + // S3 전송 유틸리티 구성 + let transferUtility = AWSS3TransferUtility.default() + + // 파일 이름을 위한 날짜 포맷 설정 + let currentDate = Date() + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyyMMddhhmmssSSS" + let fileName = dateFormatter.string(from: currentDate) + ".m4a" // 음성 파일 형식에 따라 확장자를 변경할 수 있습니다. + + // 음성 파일 업로드 + transferUtility.uploadFile( + voiceURL, + bucket: S3BucketName, + key: fileName, // 음성 파일 이름 설정 + contentType: "audio/m4a", // 음성 파일 타입에 따라 적절한 MIME 타입을 설정해야 합니다. + expression: nil, + completionHandler: { (task, error) in + if let error = error { + print("Error uploading voice: \(error.localizedDescription)") + completion(nil) + } else { + print("Voice uploaded successfully!") + // 업로드 성공 시 업로드된 음성 파일의 URL을 반환합니다. + let voiceURLString = "https://\(self.S3BucketName).s3.amazonaws.com/\(fileName)" + completion(voiceURLString) + } + } + ) + } +}