ios

[ios-Swift] Share Extension 사용하기

POKY_0908 2021. 1. 27. 10:49

 

 

이번 글에서는 Share Extension을 커스텀 뷰로 꾸며 사용하는 글입니다.

 

 

 

1. 설정

 

ShareExtensionExample 프로젝트를 생성한 후

 

File -> Target -> Share Extension을 추가해 주도록 합니다.

 

 

 

 

Extension -> App 간 데이터 사용은 동일한 그룹을 사용해야 데이터를 공유할 수 있는 것으로 알고 있어 그룹 설정을 먼저 하도록 하겠습니다.

 

프로젝트의 Capacapabilities -> App Grouops를 선택 후 그룹을 설정해 주도록 합니다.

 

그룹명은  group.Bundle identifier.Share를 사용했습니다.

 

 

 

TARGETS -> 프로젝트에서 추가한 그룹을  셰어 익스텐션에서 체크해주도록 합니다.

 

이제 그룹 설정이 완료되었습니다. 

 

 

 

이제 우리는 커스텀 뷰에서 어떤 데이터를 받을 것 인지를 설정해야 하는데 Extension의 info.plist 에서 설정하겠습니다.

 

 

NSExtensionActivationRule -> Dictionary로 변경 후 키와 밸류를 설정합니다.

 

NSExtensionActivationSupportsWebURLWithMaxCount : 허용할 HTTP URL  최대 수 

NSExtensionActivationSupportsImageWithMaxCount : 허용할 IMAGE  최대 수

 

NSExtensionActivationRule의 키 값은 이곳에서 확인해 보시면 됩니다.

 

 

 

 

 

 

2. 소스

 

MainInterface.stroryboard에서 오브젝트들을 추가 및 ShareViewController에서 SLComposeServiceViewController -> UIViewController로 변경했습니다.

 

import UIKit
import Social

class ShareViewController: UIViewController {

    override func viewDidLoad() {
    
    }
}

 

 

이제 앱이 아닌 익스텐션을 실행시켜 사파리 브라우저를 실행시키면 아래와 같이 커스텀 뷰가 나타나는 것을 확인할 수 있습니다.

 

 

 

 

커스텀 뷰의 버튼 및 동작을 추가하도록 하겠습니다.

 

import UIKit
import Social
import MobileCoreServices

class ShareViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var textView: UITextView!
    
    override func viewDidLoad() {
        let extensionItems = extensionContext?.inputItems as! [NSExtensionItem]
        
        for extensionItem in extensionItems {
            if let itemProviders = extensionItem.attachments as? [NSItemProvider] {
                for itemProvider in itemProviders {
                    // 해당 객체가 있는지 식별
                    if itemProvider.hasItemConformingToTypeIdentifier(kUTTypeImage as String) {
                        itemProvider.loadItem(forTypeIdentifier: kUTTypeImage as String, options: nil, completionHandler: { result, error in
                            var image: UIImage?
                            if result is UIImage {
                                image = result as? UIImage
                            }
                            
                            if result is URL {
                                let data = try? Data(contentsOf: result as! URL)
                                image = UIImage(data: data!)!
                            }
                            
                            if result is Data {
                                image = UIImage(data: result as! Data)!
                            }
                            
                            DispatchQueue.main.async {
                                if let image = image {
                                    self.imageView.image = image
                                }
                            }
                        })
                    }
                    
                    if itemProvider.hasItemConformingToTypeIdentifier(kUTTypeURL as String) {
                        itemProvider.loadItem(forTypeIdentifier: kUTTypeURL as String, options: nil, completionHandler: { result, error in
                            let data = NSData.init(contentsOf:result as! URL)
                            DispatchQueue.main.async {
                                if let urlStr = result {
                                    self.textView.text = "\(urlStr)"
                                }
                            }
                        })
                    }
                }
            }
        }
    }
}

kUTTypeImage 외 다른 타입도 많으니 필요하신 걸 찾아서 변경 후 사용하면 됩니다.

 

kUTTypeImage as string는 "public.image"와 동일합니다. 

 

// 익스텐션의 버튼 이벤트

@IBAction func btnSend(_ sender: UIButton) {
    // 처리후 종료
    if let userDefaults = UserDefaults(suiteName: "위에서 설정한 그룹") {
        if let image = imageView.image {
            userDefaults.set(image.pngData(), forKey: "image")
        }
        
        if let text = textView.text {
            userDefaults.set(text, forKey: "text")
        }
    }
    
    self.hideExtensionWithCompletionHandler(completion: { _ in
        self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil)
    })
}
    
@IBAction func btnDismiss(_ sender: UIButton) {
    self.hideExtensionWithCompletionHandler(completion: { _ in
        self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil)
    })
}

func hideExtensionWithCompletionHandler(completion: @escaping (Bool) -> Void) {
    UIView.animate(withDuration: 0.3, animations: {
        self.navigationController?.view.transform = CGAffineTransform(translationX: 0, y:self.navigationController!.view.frame.size.height)
    }, completion: completion)
}

익스텐션 뷰를 닫으려고 해도 그냥 dismiss를 사용하면 뷰가 사라지지 않아 hideExtensionWithCompletionHandler를 사용하도록 합니다.

 

 

 

//앱 뷰 컨트롤러

import UIKit

class ViewController: UIViewController {
    override func viewDidLoad() {
        if let userDefaults = UserDefaults(suiteName: "group.com.fostep.Share") {
            if let data = userDefaults.data(forKey: "image") {
            imageView.image = UIImage(data: data)
        }

        if let text = userDefaults.string(forKey: "text") {
            textLabel.text = text
        }
    }
}

 

 

익스텐션과 앱과의 데이터 전송은 UserDefaults를 사용해 저장 후 앱을 실행 UserDefaults를 확인하는 방식을 사용합니다.

 

UserDefaults를 사용하지 않아도 되며 익스텐션에서 서버에 전송 후 받는 방법을 사용하셔도 될 겁니다.