Skip to content

Commit 4366056

Browse files
committed
first commit
1 parent 30da2a5 commit 4366056

File tree

15 files changed

+585
-25
lines changed

15 files changed

+585
-25
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
.DS_Store
22
/.build
33
/Packages
4+
/*.xcodeproj
45
xcuserdata/
56
DerivedData/
6-
.swiftpm/configuration/registries.json
7+
.swiftpm/config/registries.json
78
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
89
.netrc

Package.swift

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,29 @@
1-
// swift-tools-version: 5.10
1+
// swift-tools-version: 5.8
22
// The swift-tools-version declares the minimum version of Swift required to build this package.
33

44
import PackageDescription
55

66
let package = Package(
7-
name: "sharelink-for-swiftui",
7+
name: "sharelink-swiftui",
8+
platforms: [.iOS(.v14)],
89
products: [
9-
// Products define the executables and libraries a package produces, making them visible to other packages.
10+
// Products define the executables and libraries a package produces, and make them visible to other packages.
1011
.library(
11-
name: "sharelink-for-swiftui",
12-
targets: ["sharelink-for-swiftui"]),
12+
name: "sharelink-swiftui",
13+
targets: ["sharelink-swiftui"]),
14+
],
15+
dependencies: [
16+
// Dependencies declare other packages that this package depends on.
17+
// .package(url: /* package url */, from: "1.0.0"),
1318
],
1419
targets: [
15-
// Targets are the basic building blocks of a package, defining a module or a test suite.
16-
// Targets can depend on other targets in this package and products from dependencies.
20+
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
21+
// Targets can depend on other targets in this package, and on products in packages this package depends on.
1722
.target(
18-
name: "sharelink-for-swiftui"),
23+
name: "sharelink-swiftui",
24+
dependencies: []),
1925
.testTarget(
20-
name: "sharelink-for-swiftuiTests",
21-
dependencies: ["sharelink-for-swiftui"]),
26+
name: "sharelink-swiftuiTests",
27+
dependencies: ["sharelink-swiftui"]),
2228
]
2329
)

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# sharelink-swiftui
2+
3+
A description of this package.

Sources/sharelink-for-swiftui/sharelink_for_swiftui.swift

Lines changed: 0 additions & 2 deletions
This file was deleted.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import UIKit
2+
import SwiftUI
3+
import LinkPresentation
4+
import CoreLocation
5+
import Contacts
6+
7+
public struct ShareLinkButton<Label: View>: View {
8+
9+
@State private var isPresented: Bool = false
10+
11+
private let data: [Any]
12+
13+
private let label: () -> Label
14+
15+
private let applicationActivities: [UIActivity]?
16+
17+
private let excludedActivityTypes: [UIActivity.ActivityType]?
18+
19+
// Initializer for Transportable items using the builder internally
20+
public init<T: Transportable>(
21+
item: T,
22+
icon: UIImage? = nil,
23+
title: String? = nil,
24+
printAction: Bool = true,
25+
applicationActivities: [UIActivity]? = nil,
26+
excludedActivityTypes: [UIActivity.ActivityType]? = nil,
27+
@ViewBuilder label: @escaping () -> Label
28+
) {
29+
let builder = TransportableItemBuilder(item: item)
30+
.setIcon(icon)
31+
.setTitle(title)
32+
.setPrintAction(printAction)
33+
34+
self.data = builder.build()
35+
self.applicationActivities = applicationActivities
36+
self.excludedActivityTypes = excludedActivityTypes
37+
self.label = label
38+
}
39+
40+
// Initializer for UIActivityItemSource items
41+
public init(
42+
itemSource: UIActivityItemSource,
43+
applicationActivities: [UIActivity]? = nil,
44+
excludedActivityTypes: [UIActivity.ActivityType]? = nil,
45+
@ViewBuilder label: @escaping () -> Label
46+
) {
47+
self.data = [itemSource]
48+
self.applicationActivities = applicationActivities
49+
self.excludedActivityTypes = excludedActivityTypes
50+
self.label = label
51+
}
52+
53+
public var body: some View {
54+
Button(action: {
55+
isPresented = true
56+
}, label: label)
57+
.sheet(isPresented: $isPresented) {
58+
ShareLinkView(
59+
data: data,
60+
applicationActivities: applicationActivities,
61+
excludedActivityTypes: excludedActivityTypes
62+
)
63+
}
64+
}
65+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//
2+
// ShareLinkError.swift
3+
//
4+
//
5+
// Created by Igor on 01.07.24.
6+
//
7+
8+
import Foundation
9+
10+
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//
2+
// Transportable.swift
3+
//
4+
// Created by Igor on 01.07.24.
5+
//
6+
7+
import UIKit
8+
import CoreLocation
9+
10+
/// Protocol `Transportable` inherits `CustomStringConvertible` for providing textual descriptions of objects.
11+
public protocol Transportable: CustomStringConvertible {}
12+
13+
extension String: Transportable {
14+
/// The description of a string is the string itself.
15+
public var description: String { self }
16+
}
17+
18+
extension URL: Transportable {
19+
/// The description of a URL is its absolute string representation.
20+
public var description: String { self.absoluteString }
21+
}
22+
23+
extension UIImage: Transportable {
24+
/// Property to extract the named parameter from the image description.
25+
var namedParameter: String? {
26+
/// Define the pattern to match the named parameter.
27+
let pattern = "named\\(\\w*: (\\w+)\\)"
28+
29+
/// Create a regular expression to match the pattern.
30+
if let regex = try? NSRegularExpression(pattern: pattern, options: []) {
31+
let nsRange = NSRange(description.startIndex..<description.endIndex, in: description)
32+
if let match = regex.firstMatch(in: description, options: [], range: nsRange) {
33+
if let range = Range(match.range(at: 1), in: description) {
34+
return String(description[range])
35+
}
36+
}
37+
}
38+
return nil
39+
}
40+
}
41+
42+
extension Data: Transportable {
43+
/// The description of data is its string representation, or "Data object" if conversion fails.
44+
public var description: String {
45+
return String(data: self, encoding: .utf8) ?? "Data object"
46+
}
47+
}
48+
49+
extension NSAttributedString: Transportable {}
50+
51+
extension CLLocation: Transportable {}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//
2+
// ShareLinkView.swift
3+
//
4+
// Created by Igor Shel on 29.10.2023.
5+
//
6+
7+
import SwiftUI
8+
import UIKit
9+
10+
/// A SwiftUI view that wraps a `UIActivityViewController` to share data.
11+
struct ShareLinkView: UIViewControllerRepresentable {
12+
13+
/// The data to be shared. It can include items like text, images, URLs, etc.
14+
let data: [Any]
15+
16+
/// Optional custom activities to include in the share sheet.
17+
let applicationActivities: [UIActivity]?
18+
19+
/// Optional activity types to exclude from the share sheet.
20+
let excludedActivityTypes: [UIActivity.ActivityType]?
21+
22+
/// Initializes a new `ShareLinkView`.
23+
///
24+
/// - Parameters:
25+
/// - data: The data to be shared.
26+
/// - applicationActivities: Optional custom activities to include in the share sheet.
27+
/// - excludedActivityTypes: Optional activity types to exclude from the share sheet.
28+
init(
29+
data: [Any],
30+
applicationActivities: [UIActivity]? = nil,
31+
excludedActivityTypes: [UIActivity.ActivityType]? = nil
32+
) {
33+
self.data = data
34+
self.applicationActivities = applicationActivities
35+
self.excludedActivityTypes = excludedActivityTypes
36+
}
37+
38+
/// Creates the view controller to be presented.
39+
///
40+
/// - Parameter context: The context in which the view is being created.
41+
/// - Returns: An `ActivitySheetController` configured with the provided data, application activities, and excluded activity types.
42+
public func makeUIViewController(context: Context) -> ActivitySheetController {
43+
let controller = ActivitySheetController(activityItems: data, applicationActivities: applicationActivities)
44+
controller.excludedActivityTypes = excludedActivityTypes
45+
controller.modalPresentationStyle = .popover
46+
return controller
47+
}
48+
49+
/// Updates the view controller with new data.
50+
///
51+
/// - Parameters:
52+
/// - uiViewController: The view controller to be updated.
53+
/// - context: The context in which the view is being updated.
54+
public func updateUIViewController(_ uiViewController: ActivitySheetController, context: Context) {}
55+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//
2+
// HalfSheetActivityController.swift
3+
//
4+
//
5+
// Created by Igor Shel on 29.10.2023.
6+
//
7+
8+
import UIKit
9+
10+
/// A custom `UIActivityViewController` subclass that customizes the presentation of the activity sheet.
11+
class ActivitySheetController: UIActivityViewController {
12+
13+
deinit {
14+
#if DEBUG
15+
print("deinit ActivitySheetController")
16+
#endif
17+
}
18+
19+
/// Called just before the view controller's view is added to a view hierarchy.
20+
///
21+
/// - Parameter animated: If true, the view is being added to the window using an animation.
22+
public override func viewWillAppear(_ animated: Bool) {
23+
super.viewWillAppear(animated)
24+
if #available(iOS 15.0, *) {
25+
/// Presentation for iOS 15 and later
26+
if let presentation = sheetPresentationController {
27+
presentation.detents = [.medium(), .large()]
28+
presentation.prefersGrabberVisible = true
29+
}
30+
} else {
31+
/// Support for iOS 14
32+
modalPresentationStyle = .overFullScreen
33+
}
34+
}
35+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//
2+
// TransportableItem.swift
3+
//
4+
// Created by Igor Shel on 29.10.2023.
5+
6+
import UIKit
7+
import LinkPresentation
8+
9+
/// A class that conforms to `UIActivityItemSource` to provide transportable items for sharing activities.
10+
final class TransportableItem<T: Transportable>: NSObject, UIActivityItemSource {
11+
/// The item to be shared, conforming to the `Transportable` protocol.
12+
private let item: T
13+
14+
/// An optional title for the item.
15+
private let title: String?
16+
17+
/// An optional icon for the item.
18+
private let icon: UIImage?
19+
20+
/// Initializes a new transportable item with optional title and icon.
21+
///
22+
/// - Parameters:
23+
/// - item: The item to be shared, conforming to the `Transportable` protocol.
24+
/// - icon: An optional icon for the item.
25+
/// - title: An optional title for the item.
26+
init(item: T, icon: UIImage? = nil, title: String? = nil) {
27+
self.item = item
28+
self.title = title
29+
self.icon = icon
30+
super.init()
31+
}
32+
33+
/// Provides a placeholder item for the activity view controller.
34+
///
35+
/// - Parameter activityViewController: The `UIActivityViewController` requesting the placeholder item.
36+
/// - Returns: The item to be shared.
37+
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
38+
return item
39+
}
40+
41+
/// Provides the actual item to be shared for the specified activity type.
42+
///
43+
/// - Parameters:
44+
/// - activityViewController: The `UIActivityViewController` requesting the item.
45+
/// - activityType: The type of activity requesting the item.
46+
/// - Returns: The item to be shared.
47+
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? {
48+
return item
49+
}
50+
51+
/// Provides metadata for the link being shared.
52+
///
53+
/// - Parameter activityViewController: The `UIActivityViewController` requesting the metadata.
54+
/// - Returns: The `LPLinkMetadata` object containing metadata about the item.
55+
func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? {
56+
let metadata = LPLinkMetadata()
57+
58+
// Set icon if available
59+
if let icon = icon {
60+
metadata.iconProvider = NSItemProvider(object: icon)
61+
} else if let icon = item as? UIImage {
62+
metadata.iconProvider = NSItemProvider(object: icon)
63+
}
64+
65+
// Set image if available
66+
if let image = item as? UIImage {
67+
metadata.imageProvider = NSItemProvider(object: image)
68+
}
69+
70+
// Set title if available
71+
if let title = title {
72+
metadata.title = title
73+
}
74+
75+
// Set item URL if the item is a URL
76+
if let urlString = item as? String, let itemURL = URL(string: urlString) {
77+
metadata.url = itemURL
78+
} else if let itemURL = item as? URL {
79+
metadata.url = itemURL
80+
}
81+
82+
return metadata
83+
}
84+
}

0 commit comments

Comments
 (0)