diff --git a/.gitignore b/.gitignore index 95c4320..f3c8e33 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ /Packages /*.xcodeproj xcuserdata/ +.swiftpm/ diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 706eede..0000000 --- a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/Package.swift b/Package.swift index ca5cdcc..f9ba04e 100644 --- a/Package.swift +++ b/Package.swift @@ -6,23 +6,11 @@ import PackageDescription let package = Package( name: "Share", products: [ - // Products define the executables and libraries produced by a package, and make them visible to other packages. - .library( - name: "Share", - targets: ["Share"]), - ], - dependencies: [ - // Dependencies declare other packages that this package depends on. - // .package(url: /* package url */, from: "1.0.0"), + .library(name: "Share", targets: ["Share"]), ], + dependencies: [ ], targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages which this package depends on. - .target( - name: "Share", - dependencies: []), - .testTarget( - name: "ShareTests", - dependencies: ["Share"]), + .target(name: "Share", dependencies: []), + .testTarget(name: "ShareTests", dependencies: ["Share"]), ] ) diff --git a/Sources/Share/Share.swift b/Sources/Share/Share.swift index 6c3d50c..d6c563a 100644 --- a/Sources/Share/Share.swift +++ b/Sources/Share/Share.swift @@ -1,3 +1,51 @@ -struct Share { - var text = "Hello, World!" +#if os(macOS) +import Cocoa +#elseif os(iOS) +import UIKit +#endif + +public protocol Sharable { + var shareItems: [Any] { get } +} + +public protocol ShareDelegate { + func didChange(status: Share.Service.Status) +} + +public struct Share { } + +extension Share { + #if os(macOS) + public typealias Service = NSSharingService + #elseif os(iOS) + public typealias Service = UIActivity.ActivityType + #endif +} + +extension Share.Service { + public enum Status { + case willShare([Any], via: Share.Service) + case didShare([Any], via: Share.Service) + case error(Share.Service.Status.Error, sharing:[Any]) + } +} + +extension Share.Service.Status { + public enum Error { + case cancelled(Share.Service?) + case other(Swift.Error, with: Share.Service?) + + var service: Share.Service? { + switch self { + case .cancelled(let service): return service + case .other(_, with: let service): return service + } + } + } +} + +extension Sharable { + public func shareSheet(delegate: ShareDelegate? = nil) -> Share.Sheet { + return Share.Sheet(for: self, delegate: delegate) + } } diff --git a/Sources/Share/Sheet.swift b/Sources/Share/Sheet.swift new file mode 100644 index 0000000..b27b7fa --- /dev/null +++ b/Sources/Share/Sheet.swift @@ -0,0 +1,85 @@ +#if os(macOS) +import Cocoa + +extension Share { + public class Sheet: NSSharingServicePicker, NSSharingServicePickerDelegate, NSSharingServiceDelegate { + let sharable: Sharable + let serviceDelegate: ShareDelegate? + + private override init(items: [Any]) { fatalError("This is private") } + + public init(for sharable: Sharable, delegate: ShareDelegate? = nil) { + self.sharable = sharable + serviceDelegate = delegate + super.init(items: sharable.shareItems) + } + + // MARK:- NSSharingServicePickerDelegate + public func sharingServicePicker(_ sharingServicePicker: NSSharingServicePicker, didChoose service: NSSharingService?) { + guard service == nil + else { return } + + serviceDelegate?.didChange(status: .error(.cancelled(service), sharing: sharable.shareItems)) + } + + public func sharingServicePicker(_ sharingServicePicker: NSSharingServicePicker, delegateFor sharingService: NSSharingService) -> NSSharingServiceDelegate? { + return self + } + + // MARK:- NSSharingServiceDelegate + + public func sharingService(_ sharingService: NSSharingService, willShareItems items: [Any]) { + serviceDelegate?.didChange(status: .willShare(items, via: sharingService)) + } + + public func sharingService(_ sharingService: NSSharingService, didShareItems items: [Any]) { + serviceDelegate?.didChange(status: .didShare(items, via: sharingService)) + } + + public func sharingService(_ sharingService: NSSharingService, didFailToShareItems items: [Any], error: Error) { + serviceDelegate?.didChange(status: .error(.other(error, with: sharingService), sharing: items)) + } + } +} +#endif + +#if os(iOS) +import UIKit + +extension Share { + public class Sheet: UIActivityViewController { + let sharable: Sharable + let serviceDelegate: ShareDelegate? + + public init(for sharable: Sharable, delegate: ShareDelegate? = nil) { + self.sharable = sharable + serviceDelegate = delegate + + super.init(activityItems: sharable.shareItems, applicationActivities: nil) + + completionWithItemsHandler = {(activityType: UIActivity.ActivityType?, completed: Bool, returnedItems:[Any]?, error: Error?) in + guard let service = activityType + else { self.report(error: .cancelled(activityType)); return } + + if let error = error { + self.report(error: .other(error, with: service)); return + } + + if !completed { + self.report(.willShare(sharable.shareItems, via: service)) + } else { + self.report(.didShare(sharable.shareItems, via: service)) + } + } + } + + private func report(_ status: Share.Service.Status) { + serviceDelegate?.didChange(status: status) + } + + private func report(error: Share.Service.Status.Error) { + serviceDelegate?.didChange(status: .error(error, sharing: sharable.shareItems)) + } + } +} +#endif