Skip to content

Commit

Permalink
Merge pull request #48 from 42Box/dev/40-share-extension-추가/#40
Browse files Browse the repository at this point in the history
feat: share extension 추가/#40
  • Loading branch information
JH713 authored Feb 22, 2024
2 parents eb71f14 + d5e3693 commit d6784c4
Show file tree
Hide file tree
Showing 6 changed files with 361 additions and 9 deletions.
58 changes: 50 additions & 8 deletions Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,18 @@ protocol ProjectFactory {
class iBoxFactory: ProjectFactory {
let projectName: String = "iBox"
let bundleId: String = "com.box42.iBox"
let iosVersion: String = "15.0"

let dependencies: [TargetDependency] = [
.external(name: "SnapKit"),
.target(name: "iBoxShareExtension")
]

let iBoxShareExtensionDependencies: [TargetDependency] = [
.external(name: "SnapKit")
]

let infoPlist: [String: Plist.Value] = [
private let appInfoPlist: [String: Plist.Value] = [
"ITSAppUsesNonExemptEncryption": false,
"CFBundleName": "iBox",
"CFBundleShortVersionString": "1.2.1",
Expand All @@ -35,23 +41,59 @@ class iBoxFactory: ProjectFactory {
],
]
]
],
"CFBundleURLTypes": [
[
"CFBundleURLName": "com.url.iBox",
"CFBundleURLSchemes": ["iBox"],
"CFBundleTypeRole": "Editor"
]
],
]

private let shareExtensionInfoPlist: [String: Plist.Value] = [
"CFBundleDisplayName": "iBox.Share",
"CFBundleShortVersionString": "1.0",
"CFBundleVersion": "1",
"NSExtension": [
"NSExtensionAttributes": [
"NSExtensionActivationRule": [
"NSExtensionActivationSupportsWebPageWithMaxCount" : 1,
"NSExtensionActivationSupportsWebURLWithMaxCount" : 1
]
],
"NSExtensionPointIdentifier": "com.apple.share-services",
"NSExtensionPrincipalClass": "$(PRODUCT_MODULE_NAME).CustomShareViewController"
]
]

func generateTarget() -> [ProjectDescription.Target] {[
Target(
func generateTarget() -> [ProjectDescription.Target] {
let appTarget = Target(
name: projectName,
destinations: .iOS,
platform: .iOS,
product: .app,
bundleId: bundleId,
deploymentTargets: .iOS("15.0"),
infoPlist: .extendingDefault(with: infoPlist),
deploymentTarget: .iOS(targetVersion: iosVersion, devices: [.iphone]),
infoPlist: .extendingDefault(with: appInfoPlist),
sources: ["\(projectName)/Sources/**"],
resources: "\(projectName)/Resources/**",
dependencies: dependencies
)
]}


let shareExtensionTarget = Target(
name: "\(projectName)ShareExtension",
platform: .iOS,
product: .appExtension,
bundleId: "\(bundleId).ShareExtension",
deploymentTarget: .iOS(targetVersion: iosVersion, devices: [.iphone]),
infoPlist: .extendingDefault(with: shareExtensionInfoPlist),
sources: ["ShareExtension/**"],
resources: [],
dependencies: iBoxShareExtensionDependencies
)

return [appTarget, shareExtensionTarget]
}

}

Expand Down
130 changes: 130 additions & 0 deletions ShareExtension/ShareViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
//
// ShareViewController.swift
// iBoxWebShareExtension
//
// Created by Chan on 2/8/24.
//

import UIKit
import Social
import UniformTypeIdentifiers
import SnapKit

@objc(CustomShareViewController)
class CustomShareViewController: UIViewController {

var backgroundView = ShareExtensionBackGroundView()
var dataURL: String = ""

override func viewDidLoad() {
super.viewDidLoad()
configureUI()
extractSharedURL()
}

func configureUI() {
self.view.addSubview(backgroundView)
backgroundView.delegate = self
backgroundView.snp.makeConstraints { make in
make.trailing.leading.equalToSuperview().inset(20)
make.center.equalToSuperview()
make.height.equalTo(200)
}
}

func getAppGroupUserDefaults() {
let defaults = UserDefaults(suiteName: "group.com.iBox")
if let data = defaults?.string(forKey: "share") {
print("ShareViewController: URL 가져오기: \(data)")
}
}

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)
}

// MARK: IBAction

@IBAction func cancel() {
self.hideExtensionWithCompletionHandler(completion: { _ in
self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil)
})
}

@IBAction func save() {
if dataURL != "" {
let sharedData = dataURL
print(sharedData)
} else {
print("저장에 실패하였습니다.")
}

self.hideExtensionWithCompletionHandler(completion: { _ in
self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil)
})
}


@objc func openURL(_ url: URL) -> Bool {
self.hideExtensionWithCompletionHandler(completion: { _ in
self.extensionContext?.completeRequest(returningItems: nil, completionHandler: nil)
})

var responder: UIResponder? = self
while responder != nil {
if let application = responder as? UIApplication {
return application.perform(#selector(openURL(_:)), with: url) != nil
}
responder = responder?.next
}
return false
}

func extractSharedURL() {
guard let extensionItem = extensionContext?.inputItems.first as? NSExtensionItem else { return }

for attachment in extensionItem.attachments ?? [] {
if attachment.hasItemConformingToTypeIdentifier(UTType.url.identifier) {
attachment.loadItem(forTypeIdentifier: UTType.url.identifier, options: nil) { [weak self] (data, error) in
DispatchQueue.main.async {
if let url = data as? URL, error == nil {
self?.dataURL = url.absoluteString
self?.backgroundView.updateLinkLabel(with: url.absoluteString)
print("Shared URL: \(url.absoluteString)")
} else {
print("Failed to retrieve URL: \(String(describing: error))")
}
}
}
break
}
}
}
}

extension CustomShareViewController: ShareExtensionBackGroundViewDelegate {

func didTapCancel() {
cancel()
}

func didTapSave() {
save()
}

func didTapOpenApp() {
let sharedData = dataURL
let url = URL(string: "iBox://\(sharedData)")!

if openURL(url) {
print("iBox 앱이 성공적으로 열렸습니다.")
} else {
print("iBox 앱을 열 수 없습니다.")
}

print(url)
}

}
148 changes: 148 additions & 0 deletions ShareExtension/View/ShareExtensionBackGroundView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
//
// BackGroundView.swift
// iBox
//
// Created by Chan on 2/19/24.
//

import UIKit

import SnapKit

protocol ShareExtensionBackGroundViewDelegate: AnyObject {
func didTapCancel()
func didTapSave()
func didTapOpenApp()
}

class ShareExtensionBackGroundView: UIView {

// MARK: - Properties

weak var delegate: ShareExtensionBackGroundViewDelegate?

// MARK: - UI Components

lazy var label: UILabel = {
let label = UILabel()
label.text = "이 링크를 iBox 앱에서 여시겠습니까?"
label.font = .systemFont(ofSize: 17)
label.textColor = .label
return label
}()

lazy var linkLabel: UILabel = {
let label = UILabel()
label.textColor = .label
label.numberOfLines = 3
label.lineBreakMode = .byTruncatingTail
return label
}()

lazy var cancelButton: UIButton = {
let button = UIButton()
button.configuration = .plain()
button.configuration?.attributedTitle = .init(
"Cancel",
attributes: .init([.font: UIFont.systemFont(ofSize: 14)])
)
return button
}()

lazy var saveButton: UIButton = {
let button = UIButton()
button.configuration = .plain()
button.configuration?.attributedTitle = .init(
"Save",
attributes: .init([.font: UIFont.boldSystemFont(ofSize: 14)])
)
return button
}()

lazy var openAppButton: UIButton = {
let button = UIButton()
button.configuration = .plain()
button.configuration?.attributedTitle = .init(
"Open",
attributes: .init([.font: UIFont.boldSystemFont(ofSize: 14)])
)
return button
}()

// MARK: - Initializer

override init(frame: CGRect) {
super.init(frame: frame)

setupHierarchy()
setupLayout()
setupButtonAction()
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

// MARK: - Setup Methods

private func setupHierarchy() {
addSubview(label)
addSubview(linkLabel)
addSubview(cancelButton)
addSubview(saveButton)
addSubview(openAppButton)
}

private func setupLayout() {
backgroundColor = .systemBackground
clipsToBounds = true
layer.cornerRadius = 10

label.snp.makeConstraints {
$0.top.leading.equalToSuperview().inset(20)
}

linkLabel.snp.makeConstraints {
$0.top.equalTo(label.snp.bottom).offset(10)
$0.leading.trailing.equalToSuperview().inset(20)
}

cancelButton.snp.makeConstraints {
$0.trailing.equalTo(saveButton.snp.leading).offset(-20)
$0.centerY.equalTo(openAppButton.snp.centerY)
}

saveButton.snp.makeConstraints {
$0.trailing.equalTo(openAppButton.snp.leading).offset(-20)
$0.centerY.equalTo(openAppButton.snp.centerY)
}

openAppButton.snp.makeConstraints {
$0.trailing.bottom.equalToSuperview().inset(20)
}
}

private func setupButtonAction() {
cancelButton.addTarget(self, action: #selector(cancelButtonTapped), for: .touchUpInside)
saveButton.addTarget(self, action: #selector(saveButtonTapped), for: .touchUpInside)
openAppButton.addTarget(self, action: #selector(openAppButtonTapped), for: .touchUpInside)
}

func updateLinkLabel(with text: String) {
linkLabel.text = text
}

// MARK: - Actions

@objc func cancelButtonTapped() {
delegate?.didTapCancel()
}

@objc func saveButtonTapped() {
delegate?.didTapSave()
}

@objc func openAppButtonTapped() {
delegate?.didTapOpenApp()
}
}
4 changes: 3 additions & 1 deletion Tuist/Dependencies.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ import ProjectDescription

let spm = SwiftPackageManagerDependencies([
.remote(url: "https://github.com/SnapKit/SnapKit.git", requirement: .upToNextMinor(from: "5.0.1"))
])
],
productTypes: ["SnapKit": .framework]
)

let dependencies = Dependencies(
swiftPackageManager: spm,
Expand Down
Loading

0 comments on commit d6784c4

Please sign in to comment.