initial version
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
class ShareService {
|
||||
static let shared = ShareService()
|
||||
|
||||
private init() {}
|
||||
|
||||
func shareTextFile(content: String, fileName: String) {
|
||||
guard let viewController = ShareService.topViewController() else { return }
|
||||
|
||||
let tempURL = FileManager.default.temporaryDirectory
|
||||
.appendingPathComponent(fileName)
|
||||
|
||||
do {
|
||||
try content.write(to: tempURL, atomically: true, encoding: .utf8)
|
||||
|
||||
let activityVC = UIActivityViewController(
|
||||
activityItems: [tempURL],
|
||||
applicationActivities: nil
|
||||
)
|
||||
|
||||
if let popover = activityVC.popoverPresentationController {
|
||||
popover.sourceView = viewController.view
|
||||
popover.sourceRect = CGRect(
|
||||
x: viewController.view.bounds.midX,
|
||||
y: viewController.view.bounds.midY,
|
||||
width: 0,
|
||||
height: 0
|
||||
)
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
viewController.present(activityVC, animated: true)
|
||||
}
|
||||
} catch {
|
||||
print("Share file error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func shareCalendarEvent(
|
||||
title: String,
|
||||
notes: String,
|
||||
startDate: Date,
|
||||
durationMinutes: Int = 60
|
||||
) {
|
||||
let endDate = startDate.addingTimeInterval(TimeInterval(durationMinutes * 60))
|
||||
let icsContent = calendarICS(
|
||||
title: title,
|
||||
notes: notes,
|
||||
startDate: startDate,
|
||||
endDate: endDate
|
||||
)
|
||||
shareTextFile(content: icsContent, fileName: "PortfolioJournal-CheckIn.ics")
|
||||
}
|
||||
|
||||
private static func topViewController(
|
||||
base: UIViewController? = UIApplication.shared.connectedScenes
|
||||
.compactMap { $0 as? UIWindowScene }
|
||||
.flatMap { $0.windows }
|
||||
.first(where: { $0.isKeyWindow })?.rootViewController
|
||||
) -> UIViewController? {
|
||||
if let nav = base as? UINavigationController {
|
||||
return topViewController(base: nav.visibleViewController)
|
||||
}
|
||||
if let tab = base as? UITabBarController {
|
||||
return topViewController(base: tab.selectedViewController)
|
||||
}
|
||||
if let presented = base?.presentedViewController {
|
||||
return topViewController(base: presented)
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
private func calendarICS(
|
||||
title: String,
|
||||
notes: String,
|
||||
startDate: Date,
|
||||
endDate: Date
|
||||
) -> String {
|
||||
let uid = UUID().uuidString
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "yyyyMMdd'T'HHmmss'Z'"
|
||||
formatter.timeZone = TimeZone(secondsFromGMT: 0)
|
||||
let stamp = formatter.string(from: Date())
|
||||
let start = formatter.string(from: startDate)
|
||||
let end = formatter.string(from: endDate)
|
||||
|
||||
return """
|
||||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//PortfolioJournal//MonthlyCheckIn//EN
|
||||
BEGIN:VEVENT
|
||||
UID:\(uid)
|
||||
DTSTAMP:\(stamp)
|
||||
DTSTART:\(start)
|
||||
DTEND:\(end)
|
||||
SUMMARY:\(escapeICS(title))
|
||||
DESCRIPTION:\(escapeICS(notes))
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
||||
"""
|
||||
}
|
||||
|
||||
private func escapeICS(_ value: String) -> String {
|
||||
value
|
||||
.replacingOccurrences(of: "\\", with: "\\\\")
|
||||
.replacingOccurrences(of: "\n", with: "\\n")
|
||||
.replacingOccurrences(of: ";", with: "\\;")
|
||||
.replacingOccurrences(of: ",", with: "\\,")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user