iOS 에서 파일다운로드를 해서 다른 앱이나 Airdrop 등으로 공유하는 방향으로 구현해 보려고 한다
WebKit 의 WKNavigationDelegate를 상속받아 navigationResponse 에서 mimeType이 있을때
파일 다운로드를 구현하는 방식으로 하면 되겠다
우선 mimeType 구조체를 만들고 다운로드로 인식할 값을 init 해준다
struct MimeType {
var type:String
var fileExtension:String
}
...
var mimeTypes:[MimeType] = []
...
mimeTypes = [MimeType(type: "ms-excel", fileExtension: "xls")
,MimeType(type: "pdf", fileExtension: "pdf")
,MimeType(type: "PDF", fileExtension: "PDF")
,MimeType(type: "application/octet-stream", fileExtension: "PDF")
,MimeType(type: "application/x-msdownload", fileExtension: "")]
MimeType에서 type은 웹에서 뿌려지는 mimeType을 넣어주고 fileExtenstion은 해당 타입의 확장자명을 추후에 만들어줄때 사용한다.
WKNavigationDelegate 를 상속받아 WebView의 Delegate 연결
webView.navigationDelegate = self
오버라이드된
webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) 구현 하자
func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse,
decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
if let mimeType = navigationResponse.response.mimeType {
if isMimeTypeConfigured(mimeType) {
if let url = navigationResponse.response.url {
var fileName = getDefaultFileName(forMimeType: mimeType)
if let name = getFileNameFromResponse(navigationResponse.response) {
let nName = name as NSString
fileName = nName.removingPercentEncoding ?? name
fileName = fileName.replacingOccurrences(of: "\";", with: "")
fileName = fileName.replacingOccurrences(of: "\"", with: "")
}
downloadData(fromURL: url, fileName: fileName) { success, destinationURL in
if success, let destinationURL = destinationURL {
self.fileDownloadedAtURL(url: destinationURL)
}
}
decisionHandler(.cancel)
return
}
}
}
decisionHandler(.allow)
}
private func isMimeTypeConfigured(_ mimeType:String) -> Bool {
for record in self.mimeTypes {
if mimeType.contains(record.type) {
return true
}
}
return false
}
private func getDefaultFileName(forMimeType mimeType:String) -> String {
for record in self.mimeTypes {
if mimeType.contains(record.type) {
return "default." + record.fileExtension
}
}
return "default"
}
private func getFileNameFromResponse(_ response:URLResponse) -> String? {
if let httpResponse = response as? HTTPURLResponse {
let headers = httpResponse.allHeaderFields
if let disposition = headers["Content-Disposition"] as? String {
let components = disposition.components(separatedBy: "filename=")
if components.count > 1 {
return components[1]
}
}
}
return nil
}
private func downloadData(fromURL url:URL,
fileName:String,
completion:@escaping (Bool, URL?) -> Void) {
webView.configuration.websiteDataStore.httpCookieStore.getAllCookies() { cookies in
let session = URLSession.shared
session.configuration.httpCookieStorage?.setCookies(cookies, for: url, mainDocumentURL: nil)
let task = session.downloadTask(with: url) { localURL, urlResponse, error in
if let localURL = localURL {
let destinationURL = self.moveDownloadedFile(url: localURL, fileName: fileName)
completion(true, destinationURL)
}
else {
completion(false, nil)
}
}
task.resume()
}
}
private func moveDownloadedFile(url:URL, fileName:String) -> URL {
let tempDir = NSTemporaryDirectory()
let destinationPath = tempDir + fileName
let destinationURL = URL(fileURLWithPath: destinationPath)
try? FileManager.default.removeItem(at: destinationURL)
try? FileManager.default.moveItem(at: url, to: destinationURL)
return destinationURL
}
func fileDownloadedAtURL(url: URL) {
DispatchQueue.main.async {
let activityVC = UIActivityViewController(activityItems: [url], applicationActivities: nil)
activityVC.popoverPresentationController?.sourceView = self.view
activityVC.popoverPresentationController?.sourceRect = self.view.frame
activityVC.popoverPresentationController?.barButtonItem = self.navigationItem.rightBarButtonItem
self.present(activityVC, animated: true, completion: nil)
}
}
navigationResponse.response.mimeType 에서 처음 설정한 값이 있으면 파일 이름을 추출하고
url을 통해서 파일을 다운로드 받는 구조이다
downloadData 함수를 통해서 세션과 쿠키를 연동시키고 downloadTask를 실행하고
moveDownloadFile을 통해서 파일을 다운로드하게 된다
다운로드가 된 파일을 filedownloadedAtURL을 통해서 공유를 하게 되는 그러한 방식이다
'iOS' 카테고리의 다른 글
[iOS] Xcode 14.3 아카이브 시 이슈 발생 (PhaseScriptExecution failed with a nonzero exit code) (0) | 2023.06.15 |
---|---|
[iOS,Swift] App과 Web 통신하기 (Webkit MessageHandlers) (0) | 2023.06.15 |
[iOS,Swift] 스트리밍 URL로 비디오영상 재생하기 (0) | 2023.05.08 |
[iOS,Swift] URL 인코딩 하기 (PercentEncoding) (0) | 2023.04.24 |