Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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