โ๏ธ GOAL
๐ฉถ Step 1. ๋ฒํผ ํด๋ฆญ ์ ์ฌ์ง์จ๋ฒ(๋ผ์ด๋ธ๋ฌ๋ฆฌ)์ ์ ๊ทผ
1-1. ์ ํํ ์ด๋ฏธ์ง๋ฅผ imageView๋ก ๋ณด์ฌ์ค๋ค.
1-2. ์ ํํ ์ด๋ฏธ์ง ํน์ ์นด๋ฉ๋ผ๋ก ์ฐ์ ์ด๋ฏธ์ง๋ฅผ ์๋ฒ์ ์ ๋ก๋ํ๋ค.
๐ฉถ Step 2. ์๋ฒ ํต์ ํ response url๋ก imageView์ ๋ณด์ฌ์ค๋ค.
โ๏ธ SETTING
1. Info.plist ์์
privacy - Photo Library Usage Description, Privacy - Camera Usage Description ํญ๋ชฉ์ ์ถ๊ฐํ์ฌ ์ฌ์ง์จ๋ฒ, ์นด๋ฉ๋ผ ์ ๊ทผ์ ๋ํ ๊ถํ์ ํ๊ฐ + Description์ ์ฌ์ฉํ๋ ์ด์ ๋ฅผ ๊ธฐ์ฌํ๋ค.
์ด๋, ์ด์ ๋ ์นด๋ฉ๋ผ, ์จ๋ฒ ํ๊ฐ ์์ฒญ ์๋ฆผ ์ฐฝ์ ๋ฉ์์ง๋ก ๋์ค๋๋ฐ,
๋ถ์น์ ํ ๊ฒฝ์ฐ, ์ถํ ์ฑ์คํ ์ด์ ์ฑ ๋ฐฐํฌํ ๋, ๋ฆฌ์ ์ฌ์ ๊ฐ ๋ ์ ์๋ค. ์น์ + ์์ธํ๊ฒ ์ ์ ๊ฒ!
๐ฉถ Step 1. ๋ฒํผ ํด๋ฆญ ์ ์ฌ์ง์จ๋ฒ(๋ผ์ด๋ธ๋ฌ๋ฆฌ)์ ์ ๊ทผ
1. UIImagePickerController ์ธ์คํด์ค ์์ฑ
UIImagePickerController๋ ์ด๋ฏธ์ง๋ฅผ ์ ํํด์ ๊ฐ์ ธ์ค๋ ๊ธฐ๋ฅ์ ์ํํ๋ค.
ํ์ง๋ง, source type์ ๋ฐ๋ผ ์นด๋ฉ๋ผ๋ก ์ดฌ์ํ ์ฌ์ง, ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ง, ๋์์์ ๊ฐ์ ธ์ฌ ์ ์๋ค.
์ด UIImagePickerController๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์ ์๋ ๋ ๊ฐ์ ๋ธ๋ฆฌ๊ฒ์ดํธ๋ฅผ ์์๋ฐ๋๋ค.
- UIImagePickerControllerDelegate : ์ด๋ฏธ์ง๋ฅผ ์ ํํ๊ณ ์นด๋ฉ๋ผ๋ฅผ ์ฐ์์ ๋ ๋ค์ํ ๋์ ์ํ
- UINavigationControllerDelegate : ์จ๋ฒ ์ฌ์ง์ ์ ํํ์ ๋, ํ๋ฉด ์ ํ์ ๋ค๋น๊ฒ์ด์ ์ผ๋ก ์ด๋
UIImagePickerController ์ธ์คํด์ค์ delegate = self๋ก ์ค์ ํ๋ค.
*UIImagePickerControllerDelegate์ delegate ์์ฑ์ ์ ๋ ๊ฐ์ ํ๋กํ ์ฝ์ ๋ชจ๋ ๊ตฌํํด์ผ ํ๋ Protocol Composition์ผ๋ก ๋ ํ๋กํ ์ฝ์ ํจ๊ป ์จ์ค์ผ ํ๋ค!
class AddViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
@IBOutlet weak var itemImg: UIImageView!
let imgPickerController = UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
imgPickerController.delegate = self
}
}
2. ์จ๋ฒ ์ ๊ทผ ์ก์ ์ํ
๋ฒํผ ํด๋ฆญ ์ ์จ๋ฒ์ด ์ํ๋๋๋ก imagePickerController์ sourceType์ photoLibrary๋ก ์ค์
imagePickerController๋ UIViewController๋ฅผ ์์๋ฐ๋ ์ปจํธ๋กค๋ฌ์ด๊ธฐ ๋๋ฌธ์,
present ๋ฉ์๋๋ฅผ ์ด์ฉํด์ ์ปจํธ๋กค๋ฌ ๋ทฐ๋ฅผ ๋์์ค๋ค!
@IBAction func addImgAction(_ sender: Any) {
// ์ด๋ฏธ์ง ์์ค๋ก ์ฌ์ง ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ ํ
imgPickerController.sourceType = .photoLibrary
// ์ด๋ฏธ์ง ํผ์ปค ์ปจํธ๋กค๋ฌ ์คํ
self.present(imgPickerController, animated: true, completion: nil)
}
1-1. ์ ํํ ์ด๋ฏธ์ง๋ฅผ imageView๋ก ๋ณด์ฌ์ค๋ค.
UIImageView ๊ฐ์ฒด์ ์์ธ๋ ๋ณ์์ ์ ํํ ์ด๋ฏธ์ง๋ฅผ ๋ฃ์ด์ฃผ๋ ๋ฐฉ๋ฒ! ์ ๋ฐฉ๋ฒ์ผ๋ก ์จ๋ฒ์ ์ํํ์ผ๋ฉด, ์จ๋ฒ ๋ด์ ์ฌ์ฉํ ์ฌ์ง์ ์ ํํ ๊ฒ์ด๋ค. ๋จผ์ , ์ด ์ ํ์ ๊ฐ์งํ๋ ๋ฉ์๋์ ์ ํ์ ์ทจ์ํ์ ๋ ์ํํ๋ ์๋ ๋ ๋ฉ์๋๊ฐ ์๋ค.
- imagePickerController(_: didFinishPickingMediaWithInfo:) : ์ด๋ฏธ์ง ํผ์ปค ์ปจํธ๋กค๋ฌ์์ ์ด๋ฏธ์ง๋ฅผ ์ ํํ๊ฑฐ๋ ์นด๋ฉ๋ผ ์ดฌ์์ ์๋ฃํ์ ๋ ํธ์ถ๋๋ ๋ฉ์๋
- imagePickerControllerDidCancel : ์ด๋ฏธ์ง ํผ์ปค ์ปจํธ๋กค๋ฌ๊ฐ ์คํ๋ ํ ์ด๋ฏธ์ง ์ ํ ์์ด ๊ทธ๋ฅ ์ทจ์ํ์ ๋ ํธ์ถ๋๋ ๋ฉ์๋
๋๋ ํฌํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ(๊ฐค๋ฌ๋ฆฌ)์์ ์ด๋ฏธ์ง ์ ํ ํ ์ํ๋๋ didFinishPickingMediaWithInfo ๋ฉ์๋์์ ๊ฐ์ง๊ณ ์จ ์ด๋ฏธ์ง๋ฅผ UIImage๋ก ๋ณํ ํ photoImageView์ ์ด๋ฏธ์ง๋ก ๋ฃ์ด์ฃผ์๋ค.
์ด๋, infoKey ์ค์ ์ ๋ฐ๋ผ ์ํ๋ ๊ฐ์ ํตํด ์ด๋ฏธ์ง๋ฅผ ๋ฃ์ด์ค ์ ์๋ค.
- UIImagePickerController.infoKey.mediaType : ์ ๋ฌ๋ฐ์ ๋ฏธ๋์ด ํ์ ์ ๋ํ ์ ๋ณด
- UIImagePickerController.infoKey.originalImage : ์ ํํ ์ด๋ฏธ์ง์ ๋ํ ์๋ณธ ๋ฐ์ดํฐ
- UIImagePickerController.infoKey.editedImage : ์์ ๋ ์ด๋ฏธ์ง๋ฅผ ์ ๋ฌ
- UIImagePickerController.infoKey.cropRect : ํฌ๋กญ ํ ์ด๋ฏธ์ง์ผ ๊ฒฝ์ฐ, ํฌ๋กญ ๋ ์ด๋ฏธ์ง๋ฅผ ์ ๋ฌ
UIImageView ๋ณ์์ ์ด๋ฏธ์ง๋ฅผ ๋ฃ์ด์ฃผ์๊ณ , UI ์ฒ๋ฆฌ๋ผ GCD ์ ์ฉํด ์ฃผ์๋ค.
์ดํ, dismiss๋ฅผ ํตํด picker๋ฅผ ๋ซ๊ณ ์ด์ ํ๋ฉด์ผ๋ก ๋์๊ฐ๋ค.
์ด๋ฏธ์ง ์ ํ๊ณผ ๋์์ didFinishPickingMediaWithInfo ๋ฉ์๋๊ฐ ์ํ๋๋ ๊ฒ๊ณผ ์ ํํ ์ด๋ฏธ์ง๊ฐ ์ ์ถ๋ ฅ๋์๋ค.
์ด๋ ๊ฒ ์ ํํ ์ด๋ฏธ์ง๋ฅผ ๊ทธ๋๋ก ์ถ๋ ฅํ๋ ๊ฒ์ ๋งค์ฐ ์ฝ๋ค! ์ด์ ์ด ์ด๋ฏธ์ง๋ฅผ ์๋ฒ์ ์ฌ๋ ค๋ณด์.
1-2. ์ ํํ ์ด๋ฏธ์ง ํน์ ์นด๋ฉ๋ผ๋ก ์ฐ์ ์ด๋ฏธ์ง๋ฅผ ์๋ฒ์ ์ ๋ก๋ํ๋ค.(with. alamofire)
asset์ ์ ์ฅ๋ ์ด๋ฏธ์ง ๋๋ ์์ ๋ฐฉ๋ฒ์ผ๋ก ์ฌ๋ฆฐ ์ด๋ฏธ์ง๋ฅผ ์๋ฒ์ ์ ๋ก๋ํด ๋ณด์.
โญ๏ธ Alamofire-upload ์ฌ์ฉํ์ฌ ์ ํํ ์ฌ์ง์ ์๋ฒ์ ์ ๋ก๋ํ๊ธฐ
์ด๋ฏธ์ง๋ฅผ data๋ก ์ ํํด์ ๋ฉํฐํํธ๋ก ์ ์กํ๋ ๋ฐฉ๋ฒ์ผ๋ก
Content-Type ์ด multipart/form-data์ธ ํ์ ์ ํต์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ฌ์ด์ post๋ฅผ ๋ ๋ฆฌ๋ ๊ฑฐ๋ผ ์๊ฐํ๋ฉด ๋๋ค!
1. URL, Request Header ์์ฑํ๊ธฐ
HTTP message body์ ๋ค์ด๊ฐ๋ ๋ฐ์ดํฐ ํ์ ์ HTTP ํค๋์ ๋ช ์ํด ์ค๋ค.
์ด๋, ์ฌ์ฉํ๋ ํ๋๊ฐ Content-Type์ด๊ณ multipart ํ์ ์ผ๋ก ๊ฐ์ผ๋ก ํค๋์ ์ถ๊ฐํด์ค๋ค.
*MultiPart ์ด๋ฏธ์ง๋ฅผ data๋ก ์ ํํด์ ์ ์กํ๋ ๋ฐฉ์
// ์๋ฒ url
let url: String = "~~~"
let header: HTTPHeaders = [
"Content-Type" : "multipart/form-data"
]
2. requester body ๊ตฌ์ฑํ๊ธฐ
์๋ฒ API์ ๋ง๊ฒ ๋ง๋ body๋ก ๊ตฌ์ฑํด์ค๋ค. ์์ผ๋ฉด ์ ๋ฃ์ด์ค๋ ๋จ.
let parameters: [String : Any] = [:]
*ํ์ผ ์ ๋ก๋ ํ ๋, HTTP ๊ท์ฝ์ ๋ณด๋ฉด,
boundary์ ๋ฌธ์์ด ์ค ๋ง์ง๋ง **------WebKitFormBoundaryQGvWeNAiOE4g2VM5--** ๊ฐ์ ๋ค๋ฅธ ๊ฐ๊ณผ ๋ค๋ฅด๊ฒ --๊ฐ ๋ง์ง๋ง์ ๋ถ์๋๋ฐ, -- ๋ body์ ๋์ ์๋ฆฌ๋ ์๋ฏธ๋ฅผ ๊ฐ์ง๋ค.
URLSession์ผ๋ก ํ์ผ์ ๋ก๋๋ฅผ ๊ตฌํํ๋ฉด ์๋ ์์์ฒ๋ผ ์ง์ ๊ตฌํํด์ฃผ์ด์ผ ํ๋ค.
let boundary = "Boundary-\(UUID().uuidString)"
ํ์ง๋ง, alamofire์ ์ฌ์ฉํ๋ฉด, ์๋์ ๊ฐ์ด alamofire์ MultipartFormData ๊ตฌํํด์ฃผ๊ณ ์์ด ๊ตฌํํ ํ์๊ฐ ์๋ค.
3. multipartformdata์ ์ถ๊ฐํ ๋ฐ์ดํฐ ์ฒ๋ฆฌ
body ๊ฐ๊ณผ ์ ๋ก๋ํ ์ด๋ฏธ์ง๋ฅผ multipartformdata์ ์ถ๊ฐ ์ค๋ค.
body์ ๋ฃ์ด์ค์ผ ํ๋ ๋ฐ์ดํฐ๋ค์ ํ์ ์ Data ํ์ ์ด์ด์ผ ํ๋ค. ๋ฐ๋ผ์ ๋ฐ์ดํฐ๊ฐ String, int, double ๋ฑ์ ํ์ ์ด๋ฉด ๋ชจ๋ Data ํ์์ผ๋ก ๋ฐ๊ฟ์ ๋ณด๋ด์ผ ํ๋ค.
- ๊ธฐ๋ณธ ํ์
์ ๋ฐ์ดํฐ ์ฒ๋ฆฌ
- String: utf8๋ก ๋จผ์ ์ธ์ฝ๋ฉ ํ Data()๋ก ๋ณํ ex) Data(stringdata.utf8)
let content = "๋ด์ฉ"
multipartFormData.append(Data(content.utf8 ?? "".utf8), withName: "content")
- ์ด๋ฏธ์ง ๋ฐ์ดํฐ ์ฒ๋ฆฌ
- withName - key๊ฐ
- fileName - ์๋ฒ์ ์ ๋ก๋ํ ํ์ผ ์ด๋ฆ
- mimeType - ํ์ผ ํ์
- ์ฃผ์* fileName์ jpeg ๋๋ png ํ์ฅ์๋ฅผ ๊ผญ ๋ถ์ฌ์ค์ผ ํ๋ค.
- ex) fileName: image.jpeg , memeType: image/jpeg
AF.upload(multipartFormData: { MultipartFormData in
// body ์ถ๊ฐ
for (key, value) in parameters {
MultipartFormData.append("\(value)".data(using: .utf8)!, withName: key)
}
// UIImage ์ฒ๋ฆฌ
// img ์ถ๊ฐ data๋ก ํ์
๋ณ๊ฒฝ ํ์!
if let image = imageData?.pngData() {
MultipartFormData.append(image, withName: "file", fileName: "test.png", mimeType: "image/png")
}
...
*์ฃผ์*
๊ทธ๋ฐ๋ฐ, ์จ๋ฒ์์ ํน์ ์ดฌ์ํ ์ด๋ฏธ์ง ๋ฐ์ดํฐ๊ฐ ์ฉ๋์ด ๋๋ฌด ์ปค์ ์ ๋ก๋๊ฐ ๋์ง ์์๋ค.
์ค๊ฐ์ ์ด๋ฏธ์ง ์ฉ๋์ ์ค์ด๋ ์ฝ๋๋ฅผ ์ถ๊ฐํด ์ฃผ๋ ์ฑ๊ณต!
๊ธฐ์กด ๊ฐค๋ฌ๋ฆฌ์์ ๋ณด๋ธ ์๋ณธ ์ด๋ฏธ์ง ์ฌ์ด์ฆ๋ ๊ฐ๋ก์ธ๋ก 3000px ๋ด์ธ๋ก jpeg compression์ 1๋ก ํ๋ฉด 6MB ์ ๋, 0.5๋ก ํ๋ฉด 3MB ์ ๋ ๋์๋ค. compression์ ๋ ์ค์ด์๋ ํ์ง์ด ์ ์ข์์ง๊ณ ์ฌ์ฉํ ์ฌ์ง ํฌ๊ธฐ๊ฐ ์์์, resize๋ก ์ฉ๋์ ์ค์๋ค.
[๋ฆฌ์ฌ์ด์ฆ ์ฝ๋]
let resizedImage = resizeImage(image: image, newWidth: 300)
extension UIImage{
func resizeImage(image: UIImage, newWidth: CGFloat) -> UIImage {
let scale = newWidth / image.size.width // ์ ์ด๋ฏธ์ง ํ๋/์ถ์ ๋น์จ
let newHeight = image.size.height * scale
UIGraphicsBeginImageContext(CGSizeMake(newWidth, newHeight))
image.draw(in: CGRectMake(0, 0, newWidth, newHeight))
let newImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
}
[์๋ณธ์ฌ์ง ์ฉ๋]
imageData Foundation.Data? 9604841 bytes some
[๋ฆฌ์ฌ์ด์ฆ ํ ์ฉ๋]
imageData Foundation.Data? 102521 bytes some
4. response ์ฒ๋ฆฌ
์ ์์ ์ผ๋ก ์ ๋ก๋๋์ด response ๊ฐ์ ๋ฐ์๊ณ , json ๋์ฝ๋ฉ ์ฒ๋ฆฌ ํด์ฃผ์๋ค.
๐ฉถ Step 2. ์๋ฒ ํต์ ํ response url๋ก imageView์ ๋ณด์ฌ์ค๋ค.
ํต์ ์ ์ฑ๊ณตํ์ฌ ๋ฐ์ remote url๋ก ์ด๋ฏธ์ง๋ฅผ ๋ณด์ฌ์ฃผ์~
๋ฐฉ๋ฒ์ ํฌ๊ฒ Data(contentsOf: )๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ๊ณผ URLSession์ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ 2๊ฐ์ง ์๋๋ฐ, ์์ฃผ ๊ฐ๋จํ๋ค.(KingFisher์๋ ์ง์!)
*ํ ์คํธํ ์ ์๋ ์ด๋ฏธ์ง๊ฐ ์๋ remote url
: https://fastly.picsum.photos/id/8/5000/3333.jpg?hmac=OeG5ufhPYQBd6Rx1TAldAuF92lhCzAhKQKttGfawWuA
Sol 1. Data(contentsOf: ) ์ฌ์ฉํ๊ธฐ
์๋ ์ฝ๋์ ๊ฐ์ด ๋งค์ฐ ๊ฐ๋จํ๋ค.
let url = URL(string: "https://cdn.cocoacasts.com/cc00ceb0c6bff0d536f25454d50223875d5c79f1/above-the-clouds.jpg")!
if let data = try? Data(contentsOf: url) {
imageView.image = UIImage(data: data)
}
ํ์ง๋ง ์ด ๋ฐฉ๋ฒ์ ์คํ์ ๋์ง๋ง Synchronous URL loading ์ค๋ ๋ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
๋ฉ์ธ ์ค๋ ๋์์ ๋๊ธฐ url ๋ก๋ฉํ๋ฉด UI ์๋ต์ด ์์ ์๋ ์๋ค๊ณ ํ๋ค!
๊ทธ๋ผ ๋น๋๊ธฐ๋ก ๋ฐ๊ฟ์ฃผ์!
์ด๋ป๊ฒ? ์ ๋ฉ์์ง์์ ์๋ ค์ฃผ๋ ๊ฒ์ฒ๋ผ ๋น๋๊ธฐ ๋คํธ์ํฌ API์ธ URLSession ์ฌ์ฉํ๊ฑฐ๋
ํน์ ์๋์ ๊ฐ์ด GCD๋ฅผ ์ฌ์ฉํ์ฌ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋์์ ์์ ํ์!
let url = URL(string: "https://cdn.cocoacasts.com/cc00ceb0c6bff0d536f25454d50223875d5c79f1/above-the-clouds.jpg")!
// ๋น๋๊ธฐ ์ ์ฉ
DispatchQueue.global().async {
if let data = try? Data(contentsOf: url) {
// UI ์์
์ main ์์!
DispatchQueue.main.async {
self.imageView.image = UIImage(data: data)
}
}
}
Sol 2. URLSession.shared.dataTask(with: url) ์ฌ์ฉํ๊ธฐ
URLSession.shared.dataTask()์ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋์์ ๋์ํ๊ธฐ ๋๋ฌธ์ ๋ด๋ถ UI ๊ด๋ จ ์ฒ๋ฆฌ๋ ๋ฉ์ธ์ค๋ ๋๋ก ์ ํํด ์ฃผ์ด์ผ ํ๋ค.
if let url = URL(string: https://cdn.cocoacasts.com/cc00ceb0c6bff0d536f25454d50223875d5c79f1/above-the-clouds.jpg")! {
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
// UI๋ ๋ฉ์ธ ์ค๋ ๋์์ ์ฒ๋ฆฌ
DispatchQueue.main.async {
self.imgView.image = UIImage(data: data)
}
}
task.resume()
}
+ UIImageView์์ ์์ฃผ ๋ถ๋ฅด๋ฉด, extension์ผ๋ก ๋ง๋ค์ด์ค์ ํธ์ถํ๋ ๋ถ๋ถ ๊น๋ํ๊ฒ refac ํด์ฃผ๊ธฐ!
self.myPageImg.loadwithURLSession(url: URL(string:thumbnail)!)
extension UIImageView {
func loadwithURLSession(url: URL) {
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
DispatchQueue.main.async {
self.image = UIImage(data: data)
}
}
task.resume()
}
}
[์ ์ฒด ์ฝ๋]
์จ๋ฒ์์ ์ฌ์ง ์ ํ!
public func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
let ala = AlamofireManager()
let resizedImage = resizeImage(image: image, newWidth: 300)
ala.fileUpload(img: resizedImage, completion: { res in
let url = URL(string: res)
self.imgView.loadwithURLSession(url: url!)
})
self.dismiss(animated: true, completion: nil)
}
}
ํ์ผ ์ ๋ก๋ ํ response url ๊ฐ loadwithURLSession๋ก ๋๊ฒจ์ฃผ๋ฉด์ ์ด๋ฏธ์ง ์ถ๋ ฅ!
func fileUpload(img: UIImage, completion: @escaping (String)->Void) {
// ์๋ฒ url
let url: String = "http://3.39.17.117:80/archive-files"
// ํค๋ ๊ตฌ์ฑ : Content-Type ํ๋์ multipart ํ์
์ถ๊ฐ
let header: HTTPHeaders = [
"Accept" : "application/json, application/javascript, text/javascript, text/json",
"Content-Type" : "multipart/form-data"
]
let parameters: [String : Any] = [:]
let imageData = img.jpegData(compressionQuality: 1)
AF.upload(multipartFormData: { MultipartFormData in
// body ์ถ๊ฐ
for (key, value) in parameters {
MultipartFormData.append("\(value)".data(using: .utf8)!, withName: key)
}
// img ์ถ๊ฐ data๋ก ํ์
๋ณ๊ฒฝ ํ์!
if let image = imageData {
MultipartFormData.append(image, withName: "file", fileName: "test.jpeg", mimeType: "image/jpeg")
}
}, to: url, method: .post, headers: header)
.validate()
.responseDecodable(of: GetUrl.self, completionHandler: { response in
switch response.result{
case .success(let data):
print("url : \(data.url)")
completion(data.url)
case .failure(let error):
print(error)
}
})
}