[
  {
    "path": ".gitignore",
    "content": "# macOS\n#\n*.DS_Store\n\n# Xcode\n#\n\n## Build generated\nbuild/\nDerivedData\n\n## Various settings\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!default.perspectivev3\nxcuserdata\n\n## Other\n*.xccheckout\n*.moved-aside\n*.xcuserstate\n*.xcscmblueprint\n\n## Obj-C/Swift specific\n*.hmap"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"Dependencies/Roxas\"]\n\tpath = Dependencies/Roxas\n\turl = https://github.com/rileytestut/Roxas.git\n"
  },
  {
    "path": "Clip/AppDelegate.swift",
    "content": "//\n//  AppDelegate.swift\n//  Clip\n//\n//  Created by Riley Testut on 6/10/19.\n//  Copyright © 2019 Riley Testut. All rights reserved.\n//\n\nimport UIKit\nimport UserNotifications\n\nimport ClipKit\nimport Roxas\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n\n    var window: UIWindow?\n\n    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool\n    {\n        // Override point for customization after application launch.\n        print(RoxasVersionNumber)\n        \n        self.window?.tintColor = .clipPink\n        \n        UserDefaults.shared.registerAppDefaults()\n        \n        func printError<T>(from result: Result<T, Error>, title: String)\n        {\n            guard let error = result.error else { return }\n            print(title, error)\n        }\n        \n        DatabaseManager.shared.prepare() { printError(from: $0, title: \"Database Error:\") }\n        \n        ApplicationMonitor.shared.start()\n        \n        self.registerForNotifications()\n        \n        return true\n    }\n\n    func applicationWillResignActive(_ application: UIApplication) {\n        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.\n        // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.\n    }\n\n    func applicationDidEnterBackground(_ application: UIApplication)\n    {\n        #if targetEnvironment(simulator)\n        // Audio extension hack to access pasteboard doesn't work in simulator, so for testing just start background task.\n        RSTBeginBackgroundTask(\"com.rileytestut.Clip.simulatorBackgroundTask\")\n        #endif\n        \n        DatabaseManager.shared.purge()\n    }\n\n    func applicationWillEnterForeground(_ application: UIApplication)\n    {\n        DatabaseManager.shared.refresh()\n    }\n\n    func applicationDidBecomeActive(_ application: UIApplication) {\n        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.\n    }\n\n    func applicationWillTerminate(_ application: UIApplication) {\n        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.\n    }\n}\n\nextension AppDelegate: UNUserNotificationCenterDelegate\n{\n    private func registerForNotifications()\n    {\n        let category = UNNotificationCategory(identifier: UNNotificationCategory.clipboardReaderIdentifier, actions: [], intentIdentifiers: [])\n        UNUserNotificationCenter.current().setNotificationCategories([category])\n        \n        UNUserNotificationCenter.current().delegate = self\n        UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (success, error) in\n        }\n    }\n    \n    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)\n    {\n        completionHandler(.alert)\n    }\n    \n    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void)\n    {\n        guard response.notification.request.content.categoryIdentifier == UNNotificationCategory.clipboardReaderIdentifier else { return }\n        guard response.actionIdentifier == UNNotificationDefaultActionIdentifier else { return }\n        \n        let location = ApplicationMonitor.shared.locationManager.location\n        \n        // Delay until next run loop so UIPasteboard no longer returns nil items due to being in background.\n        DispatchQueue.main.async {\n            DatabaseManager.shared.savePasteboard(location: location) { (result) in\n                switch result\n                {\n                case .success: break\n                case .failure(PasteboardError.duplicateItem): break\n                    \n                case .failure(let error):\n                    DispatchQueue.main.async {\n                        let alertController = UIAlertController(title: NSLocalizedString(\"Failed to Save Clipboard\", comment: \"\"), message: error.localizedDescription, preferredStyle: .alert)\n                        alertController.addAction(.ok)\n                        self.window?.rootViewController?.present(alertController, animated: true, completion: nil)\n                    }\n                }\n                \n                print(\"Save clipboard with result:\", result)\n                \n                completionHandler()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Clip/ApplicationMonitor.swift",
    "content": "//\n//  ApplicationMonitor.swift\n//  Clip\n//\n//  Created by Riley Testut on 6/27/19.\n//  Copyright © 2019 Riley Testut. All rights reserved.\n//\n\nimport UIKit\nimport AVFoundation\nimport UserNotifications\nimport Combine\n\nprivate enum UserNotification: String\n{\n    case appStoppedRunning = \"com.rileytestut.Clip.AppStoppedRunning\"\n}\n\nprivate extension CFNotificationName\n{\n    static let altstoreRequestAppState: CFNotificationName = CFNotificationName(\"com.altstore.RequestAppState.com.rileytestut.Clip\" as CFString)\n    static let altstoreAppIsRunning: CFNotificationName = CFNotificationName(\"com.altstore.AppState.Running.com.rileytestut.Clip\" as CFString)\n}\n\nprivate let ReceivedApplicationState: @convention(c) (CFNotificationCenter?, UnsafeMutableRawPointer?, CFNotificationName?, UnsafeRawPointer?, CFDictionary?) -> Void =\n{ (center, observer, name, object, userInfo) in\n    ApplicationMonitor.shared.receivedApplicationStateRequest()\n}\n\nclass ApplicationMonitor\n{\n    static let shared = ApplicationMonitor()\n    \n    let pasteboardMonitor = PasteboardMonitor()\n    let locationManager = LocationManager()\n    \n    private(set) var isMonitoring = false\n    \n    private var backgroundTaskID: UIBackgroundTaskIdentifier?\n}\n\nextension ApplicationMonitor\n{\n    func start()\n    {\n        guard !self.isMonitoring else { return }\n        self.isMonitoring = true\n        \n        self.cancelApplicationQuitNotification() // Cancel any notifications from a previous launch.\n        self.scheduleApplicationQuitNotification()\n        \n        self.pasteboardMonitor.start() { (result) in\n            switch result\n            {\n            case .success:\n                self.locationManager.start()\n                self.registerForNotifications()\n                \n            case .failure(let error):\n                self.isMonitoring = false\n                self.sendNotification(title: NSLocalizedString(\"Failed to Monitor Clipboard\", comment: \"\"), message: error.localizedDescription)\n            }\n        }\n    }\n}\n\nprivate extension ApplicationMonitor\n{\n    func registerForNotifications()\n    {\n        let center = CFNotificationCenterGetDarwinNotifyCenter()\n        CFNotificationCenterAddObserver(center, nil, ReceivedApplicationState, CFNotificationName.altstoreRequestAppState.rawValue, nil, .deliverImmediately)\n    }\n    \n    func scheduleApplicationQuitNotification()\n    {\n        let delay = 5 as TimeInterval\n        \n        let content = UNMutableNotificationContent()\n        content.title = NSLocalizedString(\"App Stopped Running\", comment: \"\")\n        content.body = NSLocalizedString(\"Tap this notification to resume monitoring your clipboard.\", comment: \"\")\n        \n        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: delay + 1, repeats: false)\n        \n        let request = UNNotificationRequest(identifier: UserNotification.appStoppedRunning.rawValue, content: content, trigger: trigger)\n        UNUserNotificationCenter.current().add(request)\n        \n        DispatchQueue.global().asyncAfter(deadline: .now() + delay) {\n            // If app is still running at this point, we schedule another notification with same identifier.\n            // This prevents the currently scheduled notification from displaying, and starts another countdown timer.\n            self.scheduleApplicationQuitNotification()\n        }\n    }\n    \n    func cancelApplicationQuitNotification()\n    {\n        UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [UserNotification.appStoppedRunning.rawValue])\n    }\n    \n    func sendNotification(title: String, message: String)\n    {\n        let content = UNMutableNotificationContent()\n        content.title = title\n        content.body = message\n        \n        let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)\n        UNUserNotificationCenter.current().add(request)\n    }\n}\n\nprivate extension ApplicationMonitor\n{\n    func receivedApplicationStateRequest()\n    {\n        guard UIApplication.shared.applicationState != .background else { return }\n        \n        let center = CFNotificationCenterGetDarwinNotifyCenter()\n        CFNotificationCenterPostNotification(center!, CFNotificationName(CFNotificationName.altstoreAppIsRunning.rawValue), nil, nil, true)\n    }\n}\n"
  },
  {
    "path": "Clip/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"13122.16\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"13104.12\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"667\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <viewLayoutGuide key=\"safeArea\" id=\"6Tk-OE-BBY\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "Clip/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"32700.99.1234\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"BHr-Pz-XEL\">\n    <device id=\"retina4_7\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"22684\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--Tab Bar Controller-->\n        <scene sceneID=\"G8a-px-lfx\">\n            <objects>\n                <tabBarController id=\"BHr-Pz-XEL\" sceneMemberID=\"viewController\">\n                    <tabBar key=\"tabBar\" contentMode=\"scaleToFill\" insetsLayoutMarginsFromSafeArea=\"NO\" id=\"wXw-wp-kuj\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"393\" height=\"49\"/>\n                        <autoresizingMask key=\"autoresizingMask\"/>\n                        <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                    </tabBar>\n                    <connections>\n                        <segue destination=\"Z9q-vE-dSj\" kind=\"relationship\" relationship=\"viewControllers\" id=\"PoF-Yv-BIK\"/>\n                        <segue destination=\"kpG-iJ-80X\" kind=\"relationship\" relationship=\"viewControllers\" id=\"AMM-Dn-Cgz\"/>\n                    </connections>\n                </tabBarController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"MAl-QG-WpX\" userLabel=\"First Responder\" customClass=\"UIResponder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"866\" y=\"466\"/>\n        </scene>\n        <!--List-->\n        <scene sceneID=\"u6D-9E-L2J\">\n            <objects>\n                <navigationController automaticallyAdjustsScrollViewInsets=\"NO\" id=\"Z9q-vE-dSj\" customClass=\"ForwardingNavigationController\" customModule=\"Clip\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <tabBarItem key=\"tabBarItem\" title=\"List\" id=\"PKr-yJ-H3B\"/>\n                    <toolbarItems/>\n                    <navigationBar key=\"navigationBar\" contentMode=\"scaleToFill\" insetsLayoutMarginsFromSafeArea=\"NO\" barStyle=\"black\" translucent=\"NO\" largeTitles=\"YES\" id=\"mZf-DU-Jdd\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"20\" width=\"375\" height=\"96\"/>\n                        <autoresizingMask key=\"autoresizingMask\"/>\n                    </navigationBar>\n                    <nil name=\"viewControllers\"/>\n                    <connections>\n                        <segue destination=\"fd0-Ck-KzX\" kind=\"relationship\" relationship=\"rootViewController\" id=\"cU1-pG-cNA\"/>\n                    </connections>\n                </navigationController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"sSM-ur-Bbi\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"1889\" y=\"116\"/>\n        </scene>\n        <!--Clip-->\n        <scene sceneID=\"05K-82-Num\">\n            <objects>\n                <tableViewController id=\"fd0-Ck-KzX\" customClass=\"HistoryViewController\" customModule=\"Clip\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <tableView key=\"view\" clipsSubviews=\"YES\" contentMode=\"scaleToFill\" alwaysBounceVertical=\"YES\" indicatorStyle=\"white\" dataMode=\"prototypes\" style=\"plain\" separatorStyle=\"none\" rowHeight=\"-1\" estimatedRowHeight=\"-1\" sectionHeaderHeight=\"28\" sectionFooterHeight=\"28\" id=\"y1F-1W-Tvt\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"551\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                        <sections/>\n                        <connections>\n                            <outlet property=\"dataSource\" destination=\"fd0-Ck-KzX\" id=\"ggy-BL-GCf\"/>\n                            <outlet property=\"delegate\" destination=\"fd0-Ck-KzX\" id=\"7Z3-Si-ipA\"/>\n                        </connections>\n                    </tableView>\n                    <navigationItem key=\"navigationItem\" title=\"Clip\" id=\"KPM-hY-RHG\">\n                        <barButtonItem key=\"rightBarButtonItem\" image=\"Settings\" id=\"IaP-Ad-9rj\">\n                            <color key=\"tintColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                            <connections>\n                                <segue destination=\"beq-wr-z37\" kind=\"popoverPresentation\" identifier=\"showSettings\" popoverAnchorBarButtonItem=\"IaP-Ad-9rj\" id=\"fMU-im-zh7\">\n                                    <popoverArrowDirection key=\"popoverArrowDirection\" up=\"YES\" down=\"YES\" left=\"YES\" right=\"YES\"/>\n                                </segue>\n                            </connections>\n                        </barButtonItem>\n                    </navigationItem>\n                </tableViewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"NTc-eU-WIU\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"2684\" y=\"115.59220389805098\"/>\n        </scene>\n        <!--Map-->\n        <scene sceneID=\"8yd-00-beM\">\n            <objects>\n                <hostingController id=\"kpG-iJ-80X\" customClass=\"ClippingsMapViewController\" customModule=\"Clip\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <tabBarItem key=\"tabBarItem\" title=\"Map\" id=\"zZh-lg-wou\"/>\n                    <navigationItem key=\"navigationItem\" id=\"JSY-Sx-Vfe\"/>\n                </hostingController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"1yu-Vg-29s\" userLabel=\"First Responder\" customClass=\"UIResponder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"1889\" y=\"841\"/>\n        </scene>\n        <!--Settings-->\n        <scene sceneID=\"eeF-sn-zd0\">\n            <objects>\n                <tableViewController id=\"xUa-Vn-Yd7\" customClass=\"SettingsViewController\" customModule=\"Clip\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <tableView key=\"view\" clipsSubviews=\"YES\" contentMode=\"scaleToFill\" alwaysBounceVertical=\"YES\" dataMode=\"static\" style=\"insetGrouped\" separatorStyle=\"default\" rowHeight=\"-1\" estimatedRowHeight=\"-1\" sectionHeaderHeight=\"18\" sectionFooterHeight=\"18\" id=\"1BY-e2-gP7\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"647\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <sections>\n                            <tableViewSection headerTitle=\"Keep Last…\" id=\"ZjM-l8-6S4\">\n                                <cells>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" accessoryType=\"checkmark\" indentationWidth=\"10\" textLabel=\"qhf-EU-e1i\" style=\"IBUITableViewCellStyleDefault\" id=\"bNo-Xt-G8X\">\n                                        <rect key=\"frame\" x=\"16\" y=\"55.5\" width=\"343\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"bNo-Xt-G8X\" id=\"F7x-pq-uZ0\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"303\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"10 items\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"qhf-EU-e1i\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"0.0\" width=\"279\" height=\"43.5\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" textLabel=\"GzS-pv-T1w\" style=\"IBUITableViewCellStyleDefault\" id=\"4Is-z7-ZoX\">\n                                        <rect key=\"frame\" x=\"16\" y=\"99\" width=\"343\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"4Is-z7-ZoX\" id=\"gQq-EX-uys\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"343\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"25 items\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"GzS-pv-T1w\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"0.0\" width=\"311\" height=\"43.5\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" textLabel=\"8Oa-Oe-o26\" style=\"IBUITableViewCellStyleDefault\" id=\"vHz-Pg-Ux0\">\n                                        <rect key=\"frame\" x=\"16\" y=\"142.5\" width=\"343\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"vHz-Pg-Ux0\" id=\"Drd-Is-Cyc\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"343\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"50 items\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"8Oa-Oe-o26\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"0.0\" width=\"311\" height=\"43.5\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" textLabel=\"vAN-CH-YFA\" style=\"IBUITableViewCellStyleDefault\" id=\"wqN-bH-xb8\">\n                                        <rect key=\"frame\" x=\"16\" y=\"186\" width=\"343\" height=\"43.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"wqN-bH-xb8\" id=\"pJK-hK-lUa\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"343\" height=\"43.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <label opaque=\"NO\" multipleTouchEnabled=\"YES\" contentMode=\"left\" insetsLayoutMarginsFromSafeArea=\"NO\" text=\"100 items\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" id=\"vAN-CH-YFA\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"0.0\" width=\"311\" height=\"43.5\"/>\n                                                    <autoresizingMask key=\"autoresizingMask\"/>\n                                                    <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                    <nil key=\"textColor\"/>\n                                                    <nil key=\"highlightedColor\"/>\n                                                </label>\n                                            </subviews>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                </cells>\n                            </tableViewSection>\n                            <tableViewSection id=\"1k6-PD-u1u\">\n                                <cells>\n                                    <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"default\" indentationWidth=\"10\" id=\"v6L-1Q-SY8\">\n                                        <rect key=\"frame\" x=\"16\" y=\"265.5\" width=\"343\" height=\"44.5\"/>\n                                        <autoresizingMask key=\"autoresizingMask\"/>\n                                        <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"v6L-1Q-SY8\" id=\"918-t4-tuC\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"343\" height=\"44.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\"/>\n                                            <subviews>\n                                                <stackView opaque=\"NO\" contentMode=\"scaleToFill\" distribution=\"equalSpacing\" alignment=\"center\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"c8N-qF-oa2\">\n                                                    <rect key=\"frame\" x=\"16\" y=\"0.0\" width=\"311\" height=\"44.5\"/>\n                                                    <subviews>\n                                                        <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"Show Location Icons\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"lvN-fC-K1r\">\n                                                            <rect key=\"frame\" x=\"0.0\" y=\"12\" width=\"157\" height=\"20.5\"/>\n                                                            <fontDescription key=\"fontDescription\" type=\"system\" pointSize=\"17\"/>\n                                                            <nil key=\"textColor\"/>\n                                                            <nil key=\"highlightedColor\"/>\n                                                        </label>\n                                                        <switch opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" contentHorizontalAlignment=\"center\" contentVerticalAlignment=\"center\" on=\"YES\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"L7B-Cw-is3\">\n                                                            <rect key=\"frame\" x=\"262\" y=\"7\" width=\"51\" height=\"31\"/>\n                                                            <connections>\n                                                                <action selector=\"toggleShowLocationIcon:\" destination=\"xUa-Vn-Yd7\" eventType=\"primaryActionTriggered\" id=\"TDD-d7-RwX\"/>\n                                                            </connections>\n                                                        </switch>\n                                                    </subviews>\n                                                    <constraints>\n                                                        <constraint firstAttribute=\"height\" constant=\"44\" id=\"WXO-l1-u2I\"/>\n                                                    </constraints>\n                                                </stackView>\n                                            </subviews>\n                                            <constraints>\n                                                <constraint firstAttribute=\"trailingMargin\" secondItem=\"c8N-qF-oa2\" secondAttribute=\"trailing\" id=\"bvY-Pi-5oN\"/>\n                                                <constraint firstAttribute=\"bottom\" secondItem=\"c8N-qF-oa2\" secondAttribute=\"bottom\" id=\"eda-9O-roS\"/>\n                                                <constraint firstItem=\"c8N-qF-oa2\" firstAttribute=\"top\" secondItem=\"918-t4-tuC\" secondAttribute=\"top\" id=\"ov6-hC-Lfm\"/>\n                                                <constraint firstItem=\"c8N-qF-oa2\" firstAttribute=\"leading\" secondItem=\"918-t4-tuC\" secondAttribute=\"leadingMargin\" id=\"rD7-x9-bDs\"/>\n                                            </constraints>\n                                        </tableViewCellContentView>\n                                    </tableViewCell>\n                                </cells>\n                            </tableViewSection>\n                        </sections>\n                        <connections>\n                            <outlet property=\"dataSource\" destination=\"xUa-Vn-Yd7\" id=\"yxE-Kd-hqj\"/>\n                            <outlet property=\"delegate\" destination=\"xUa-Vn-Yd7\" id=\"hEA-vX-Riy\"/>\n                        </connections>\n                    </tableView>\n                    <navigationItem key=\"navigationItem\" title=\"Settings\" id=\"HV4-i9-lJF\"/>\n                    <connections>\n                        <outlet property=\"showLocationIconSwitch\" destination=\"L7B-Cw-is3\" id=\"FJc-lY-5r9\"/>\n                    </connections>\n                </tableViewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"ohN-Gn-92v\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"4353\" y=\"-621\"/>\n        </scene>\n        <!--Navigation Controller-->\n        <scene sceneID=\"hWp-2p-Rtv\">\n            <objects>\n                <navigationController automaticallyAdjustsScrollViewInsets=\"NO\" id=\"beq-wr-z37\" sceneMemberID=\"viewController\">\n                    <toolbarItems/>\n                    <navigationBar key=\"navigationBar\" contentMode=\"scaleToFill\" insetsLayoutMarginsFromSafeArea=\"NO\" id=\"FwF-Od-Ej5\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"375\" height=\"56\"/>\n                        <autoresizingMask key=\"autoresizingMask\"/>\n                    </navigationBar>\n                    <nil name=\"viewControllers\"/>\n                    <connections>\n                        <segue destination=\"xUa-Vn-Yd7\" kind=\"relationship\" relationship=\"rootViewController\" id=\"YfP-Ns-wyi\"/>\n                    </connections>\n                </navigationController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"jel-yU-YIB\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"3480.8000000000002\" y=\"-621.13943028485767\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"Settings\" width=\"22\" height=\"22\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "Clip/Clip.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.application-groups</key>\n\t<array>\n\t\t<string>group.com.rileytestut.Clip</string>\n\t</array>\n\t<key>inter-app-audio</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "Clip/Components/ForwardingNavigationController.swift",
    "content": "//\n//  ForwardingNavigationController.swift\n//  AltStore\n//\n//  Created by Riley Testut on 10/24/19.\n//  Copyright © 2019 Riley Testut. All rights reserved.\n//\n\nimport UIKit\n\nclass ForwardingNavigationController: UINavigationController\n{\n    override var childForStatusBarStyle: UIViewController? {\n        return self.topViewController\n    }\n    \n    override var childForStatusBarHidden: UIViewController? {\n        return self.topViewController\n    }\n}\n"
  },
  {
    "path": "Clip/Components/GradientView.swift",
    "content": "//\n//  GradientView.swift\n//  Clip\n//\n//  Created by Riley Testut on 7/27/19.\n//  Copyright © 2019 Riley Testut. All rights reserved.\n//\n\nimport UIKit\n\nclass GradientView: UIView\n{\n    var colors: [UIColor] = [] {\n        didSet {\n            self.gradientLayer.colors = self.colors.map { $0.cgColor }\n        }\n    }\n    override class var layerClass: AnyClass {\n        return CAGradientLayer.self\n    }\n    \n    private var gradientLayer: CAGradientLayer {\n        return self.layer as! CAGradientLayer\n    }\n    \n    override init(frame: CGRect)\n    {\n        super.init(frame: frame)\n    }\n    \n    required init?(coder aDecoder: NSCoder) {\n        fatalError(\"init(coder:) has not been implemented\")\n    }\n}\n"
  },
  {
    "path": "Clip/Extensions/PasteboardItem+ActivityItemSource.swift",
    "content": "//\n//  PasteboardItem+ActivityItemSource.swift\n//  Clip\n//\n//  Created by Riley Testut on 6/14/19.\n//  Copyright © 2019 Riley Testut. All rights reserved.\n//\n\nimport MobileCoreServices\n\nimport ClipKit\n\nextension PasteboardItem: UIActivityItemSource\n{\n    public func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any\n    {\n        guard let representation = self.preferredRepresentation else { return NSNull() }\n        \n        switch representation.type\n        {\n        case .text: return \"\"\n        case .attributedText: return NSAttributedString(string: \"\")\n        case .url: return URL(string: \"http://apple.com\")!\n        case .image: return Data()\n        }\n    }\n    \n    public func activityViewController(_ activityViewController: UIActivityViewController, dataTypeIdentifierForActivityType activityType: UIActivity.ActivityType?) -> String\n    {\n        return self.preferredRepresentation?.uti ?? kUTTypeImage as String\n    }\n    \n    public func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any?\n    {\n        guard let representation = self.preferredRepresentation else { return nil }\n        \n        if activityType == UIActivity.ActivityType.copyToPasteboard\n        {\n            let itemProvider = NSItemProvider()            \n            \n            for representation in self.representations\n            {\n                itemProvider.registerItem(forTypeIdentifier: representation.uti) { (completionHandler, expectedClass, options) in\n                    completionHandler?(representation.pasteboardValue as? NSSecureCoding, nil)\n                }\n            }\n\n            // Add our own UTI representation to distinguish from other copies.\n            itemProvider.registerItem(forTypeIdentifier: UTI.clipping) { (completionHandler, expectedClass, options) in\n                completionHandler?([:] as NSDictionary, nil)\n            }\n            \n            return itemProvider\n        }\n        else\n        {\n            // _Don't_ return pasteboard value, instead return the logical object value.\n            // This way, inline data such as text won't accidentally appear as attachments in some share extensions.\n            return representation.value\n        }\n    }\n}\n"
  },
  {
    "path": "Clip/Extensions/UIDevice+Vibration.swift",
    "content": "//\n//  UIDevice+Vibration.swift\n//  DeltaCore\n//\n//  Created by Riley Testut on 11/28/16.\n//  Copyright © 2016 Riley Testut. All rights reserved.\n//\n\nimport UIKit\nimport AudioToolbox\n\npublic extension UIDevice\n{\n    enum FeedbackSupportLevel: Int\n    {\n        case unsupported // iPhone 6 or earlier, or non-iPhone (e.g. iPad)\n        case basic // iPhone 6s\n        case feedbackGenerator // iPhone 7 and later\n    }\n}\n\npublic extension UIDevice\n{\n    var feedbackSupportLevel: FeedbackSupportLevel\n    {\n        guard let rawValue = self.value(forKey: \"_feedbackSupportLevel\") as? Int else { return .unsupported }\n        \n        let feedbackSupportLevel = FeedbackSupportLevel(rawValue: rawValue)\n        return feedbackSupportLevel ?? .feedbackGenerator // We'll assume raw values greater than 2 still support UIFeedbackGenerator ¯\\_(ツ)_/¯\n    }\n    \n    var isVibrationSupported: Bool {\n        #if (arch(i386) || arch(x86_64))\n            // Return false for iOS simulator\n            return false\n        #else\n            // All iPhones support some form of vibration, and potentially future non-iPhone devices will support taptic feedback\n            return (self.model.hasPrefix(\"iPhone\")) || self.feedbackSupportLevel != .unsupported\n        #endif\n    }\n    \n    func vibrate()\n    {\n        guard self.isVibrationSupported else { return }\n        \n        switch self.feedbackSupportLevel\n        {\n        case .unsupported: break\n        case .basic, .feedbackGenerator: AudioServicesPlaySystemSound(1519) // \"peek\" vibration\n        }\n    }\n}\n"
  },
  {
    "path": "Clip/History/ClippingTableViewCell.swift",
    "content": "//\n//  ClippingTableViewCell.swift\n//  Clip\n//\n//  Created by Riley Testut on 6/13/19.\n//  Copyright © 2019 Riley Testut. All rights reserved.\n//\n\nimport UIKit\n\n@objc(ClippingTableViewCell)\nclass ClippingTableViewCell: UITableViewCell\n{\n    @IBOutlet var clippingView: UIView!\n    \n    @IBOutlet var titleLabel: UILabel!\n    @IBOutlet var dateLabel: UILabel!\n    @IBOutlet var contentLabel: UILabel!\n    @IBOutlet var contentImageView: UIImageView!\n    @IBOutlet var locationButton: UIButton!\n    \n    @IBOutlet var bottomConstraint: NSLayoutConstraint!\n    \n    override func awakeFromNib()\n    {\n        super.awakeFromNib()\n        \n        self.clippingView.layer.cornerRadius = 10\n        self.clippingView.layer.masksToBounds = true\n        \n        self.contentImageView.layer.cornerRadius = 10\n        self.contentImageView.layer.masksToBounds = true\n    }\n}\n"
  },
  {
    "path": "Clip/History/ClippingTableViewCell.xib",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"3.0\" toolsVersion=\"32700.99.1234\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\">\n    <device id=\"retina6_1\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"22684\"/>\n        <capability name=\"Image references\" minToolsVersion=\"12.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <objects>\n        <placeholder placeholderIdentifier=\"IBFilesOwner\" id=\"-1\" userLabel=\"File's Owner\"/>\n        <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"-2\" customClass=\"UIResponder\"/>\n        <tableViewCell clipsSubviews=\"YES\" contentMode=\"scaleToFill\" preservesSuperviewLayoutMargins=\"YES\" selectionStyle=\"none\" indentationWidth=\"10\" reuseIdentifier=\"Cell\" rowHeight=\"120\" id=\"Y0r-KE-oyf\" customClass=\"ClippingTableViewCell\">\n            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"400\"/>\n            <autoresizingMask key=\"autoresizingMask\"/>\n            <tableViewCellContentView key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" preservesSuperviewLayoutMargins=\"YES\" insetsLayoutMarginsFromSafeArea=\"NO\" tableViewCell=\"Y0r-KE-oyf\" id=\"DfT-qA-KZm\">\n                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"400\"/>\n                <autoresizingMask key=\"autoresizingMask\"/>\n                <subviews>\n                    <view contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"3fg-c6-MSF\">\n                        <rect key=\"frame\" x=\"16\" y=\"11.5\" width=\"288\" height=\"377.5\"/>\n                        <subviews>\n                            <visualEffectView opaque=\"NO\" contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"QV9-Vd-SCE\">\n                                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"288\" height=\"377.5\"/>\n                                <view key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" insetsLayoutMarginsFromSafeArea=\"NO\" id=\"Kkk-LW-I0M\">\n                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"288\" height=\"377.5\"/>\n                                    <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                                </view>\n                                <blurEffect style=\"extraLight\"/>\n                            </visualEffectView>\n                            <stackView opaque=\"NO\" contentMode=\"scaleToFill\" axis=\"vertical\" spacing=\"8\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"FsE-hm-xaq\">\n                                <rect key=\"frame\" x=\"12\" y=\"12\" width=\"264\" height=\"353.5\"/>\n                                <subviews>\n                                    <visualEffectView opaque=\"NO\" contentMode=\"scaleToFill\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"8Ov-eX-yBP\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"264\" height=\"55.5\"/>\n                                        <view key=\"contentView\" opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" insetsLayoutMarginsFromSafeArea=\"NO\" id=\"d93-97-hlG\">\n                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"264\" height=\"55.5\"/>\n                                            <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                                            <subviews>\n                                                <stackView opaque=\"NO\" contentMode=\"scaleToFill\" verticalCompressionResistancePriority=\"1000\" distribution=\"equalSpacing\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"yda-cG-JjY\">\n                                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"264\" height=\"55.5\"/>\n                                                    <subviews>\n                                                        <stackView opaque=\"NO\" contentMode=\"scaleToFill\" spacing=\"8\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"WG8-q4-I7L\">\n                                                            <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"87\" height=\"55.5\"/>\n                                                            <subviews>\n                                                                <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" verticalCompressionResistancePriority=\"1000\" text=\"Image\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"c2s-SZ-q8A\">\n                                                                    <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"49\" height=\"55.5\"/>\n                                                                    <fontDescription key=\"fontDescription\" style=\"UICTFontTextStyleHeadline\"/>\n                                                                    <nil key=\"textColor\"/>\n                                                                    <nil key=\"highlightedColor\"/>\n                                                                </label>\n                                                                <button opaque=\"NO\" contentMode=\"scaleToFill\" contentHorizontalAlignment=\"trailing\" contentVerticalAlignment=\"center\" buttonType=\"system\" lineBreakMode=\"middleTruncation\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"q7j-Nd-a75\">\n                                                                    <rect key=\"frame\" x=\"57\" y=\"0.0\" width=\"30\" height=\"55.5\"/>\n                                                                    <constraints>\n                                                                        <constraint firstAttribute=\"width\" constant=\"30\" id=\"7de-mh-FyQ\"/>\n                                                                    </constraints>\n                                                                    <state key=\"normal\" title=\"Button\"/>\n                                                                    <buttonConfiguration key=\"configuration\" style=\"plain\">\n                                                                        <imageReference key=\"image\" image=\"location.circle.fill\" catalog=\"system\" symbolScale=\"medium\"/>\n                                                                    </buttonConfiguration>\n                                                                </button>\n                                                            </subviews>\n                                                        </stackView>\n                                                        <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" text=\"3 mins ago\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"CHm-ya-B0P\">\n                                                            <rect key=\"frame\" x=\"202\" y=\"0.0\" width=\"62\" height=\"55.5\"/>\n                                                            <fontDescription key=\"fontDescription\" style=\"UICTFontTextStyleCaption1\"/>\n                                                            <color key=\"textColor\" white=\"0.66666666669999997\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                                                            <nil key=\"highlightedColor\"/>\n                                                        </label>\n                                                    </subviews>\n                                                </stackView>\n                                            </subviews>\n                                            <constraints>\n                                                <constraint firstItem=\"yda-cG-JjY\" firstAttribute=\"leading\" secondItem=\"d93-97-hlG\" secondAttribute=\"leading\" id=\"EPt-Lm-sq4\"/>\n                                                <constraint firstAttribute=\"bottom\" secondItem=\"yda-cG-JjY\" secondAttribute=\"bottom\" id=\"HGN-cR-IAW\"/>\n                                                <constraint firstAttribute=\"trailing\" secondItem=\"yda-cG-JjY\" secondAttribute=\"trailing\" id=\"VkD-pJ-Lk9\"/>\n                                                <constraint firstItem=\"yda-cG-JjY\" firstAttribute=\"top\" secondItem=\"d93-97-hlG\" secondAttribute=\"top\" id=\"ySK-Bk-v31\"/>\n                                            </constraints>\n                                        </view>\n                                        <vibrancyEffect>\n                                            <blurEffect style=\"extraLight\"/>\n                                        </vibrancyEffect>\n                                    </visualEffectView>\n                                    <stackView opaque=\"NO\" contentMode=\"scaleToFill\" verticalHuggingPriority=\"249\" verticalCompressionResistancePriority=\"1000\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"Bdm-xD-0S9\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"63.5\" width=\"264\" height=\"18\"/>\n                                        <subviews>\n                                            <label opaque=\"NO\" userInteractionEnabled=\"NO\" contentMode=\"left\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" verticalCompressionResistancePriority=\"1000\" text=\"Label\" textAlignment=\"natural\" lineBreakMode=\"tailTruncation\" numberOfLines=\"0\" baselineAdjustment=\"alignBaselines\" adjustsFontSizeToFit=\"NO\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"4Ic-XD-ZD7\">\n                                                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"264\" height=\"18\"/>\n                                                <fontDescription key=\"fontDescription\" style=\"UICTFontTextStyleSubhead\"/>\n                                                <nil key=\"textColor\"/>\n                                                <nil key=\"highlightedColor\"/>\n                                            </label>\n                                        </subviews>\n                                    </stackView>\n                                    <imageView clipsSubviews=\"YES\" userInteractionEnabled=\"NO\" contentMode=\"scaleAspectFill\" horizontalHuggingPriority=\"251\" verticalHuggingPriority=\"251\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"A9I-Tq-Vqw\">\n                                        <rect key=\"frame\" x=\"0.0\" y=\"89.5\" width=\"264\" height=\"264\"/>\n                                        <constraints>\n                                            <constraint firstAttribute=\"width\" secondItem=\"A9I-Tq-Vqw\" secondAttribute=\"height\" priority=\"750\" id=\"zZQ-fM-Uhm\"/>\n                                        </constraints>\n                                    </imageView>\n                                </subviews>\n                            </stackView>\n                        </subviews>\n                        <constraints>\n                            <constraint firstItem=\"FsE-hm-xaq\" firstAttribute=\"leading\" secondItem=\"3fg-c6-MSF\" secondAttribute=\"leadingMargin\" id=\"CG3-U5-r1W\"/>\n                            <constraint firstItem=\"QV9-Vd-SCE\" firstAttribute=\"top\" secondItem=\"3fg-c6-MSF\" secondAttribute=\"top\" id=\"ctH-Vw-i8Q\"/>\n                            <constraint firstItem=\"FsE-hm-xaq\" firstAttribute=\"top\" secondItem=\"3fg-c6-MSF\" secondAttribute=\"topMargin\" id=\"dyA-Io-Xbt\"/>\n                            <constraint firstAttribute=\"bottom\" secondItem=\"QV9-Vd-SCE\" secondAttribute=\"bottom\" id=\"erp-NN-Eyi\"/>\n                            <constraint firstAttribute=\"bottomMargin\" secondItem=\"FsE-hm-xaq\" secondAttribute=\"bottom\" id=\"sOz-vw-Nax\"/>\n                            <constraint firstItem=\"QV9-Vd-SCE\" firstAttribute=\"leading\" secondItem=\"3fg-c6-MSF\" secondAttribute=\"leading\" id=\"x7p-sz-27U\"/>\n                            <constraint firstAttribute=\"trailing\" secondItem=\"QV9-Vd-SCE\" secondAttribute=\"trailing\" id=\"yHS-Ie-9XV\"/>\n                            <constraint firstAttribute=\"trailingMargin\" secondItem=\"FsE-hm-xaq\" secondAttribute=\"trailing\" id=\"zHj-dc-JPb\"/>\n                        </constraints>\n                        <edgeInsets key=\"layoutMargins\" top=\"12\" left=\"12\" bottom=\"12\" right=\"12\"/>\n                    </view>\n                </subviews>\n                <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n                <constraints>\n                    <constraint firstItem=\"3fg-c6-MSF\" firstAttribute=\"bottom\" secondItem=\"DfT-qA-KZm\" secondAttribute=\"bottomMargin\" id=\"LRr-HV-NWu\"/>\n                    <constraint firstItem=\"3fg-c6-MSF\" firstAttribute=\"top\" secondItem=\"DfT-qA-KZm\" secondAttribute=\"topMargin\" constant=\"0.5\" id=\"QXR-pu-N1i\"/>\n                    <constraint firstAttribute=\"trailingMargin\" secondItem=\"3fg-c6-MSF\" secondAttribute=\"trailing\" id=\"hmx-Ud-7ia\"/>\n                    <constraint firstItem=\"3fg-c6-MSF\" firstAttribute=\"leading\" secondItem=\"DfT-qA-KZm\" secondAttribute=\"leadingMargin\" id=\"xnv-np-5co\"/>\n                </constraints>\n            </tableViewCellContentView>\n            <color key=\"backgroundColor\" white=\"0.0\" alpha=\"0.0\" colorSpace=\"custom\" customColorSpace=\"genericGamma22GrayColorSpace\"/>\n            <connections>\n                <outlet property=\"bottomConstraint\" destination=\"LRr-HV-NWu\" id=\"biA-rf-uaC\"/>\n                <outlet property=\"clippingView\" destination=\"3fg-c6-MSF\" id=\"NJy-JL-YgY\"/>\n                <outlet property=\"contentImageView\" destination=\"A9I-Tq-Vqw\" id=\"9Tg-3N-7pg\"/>\n                <outlet property=\"contentLabel\" destination=\"4Ic-XD-ZD7\" id=\"woG-Jk-cKt\"/>\n                <outlet property=\"dateLabel\" destination=\"CHm-ya-B0P\" id=\"y9q-KA-fPp\"/>\n                <outlet property=\"locationButton\" destination=\"q7j-Nd-a75\" id=\"bzV-bQ-Zin\"/>\n                <outlet property=\"titleLabel\" destination=\"c2s-SZ-q8A\" id=\"yge-vn-euB\"/>\n            </connections>\n            <point key=\"canvasLocation\" x=\"-271\" y=\"-11\"/>\n        </tableViewCell>\n    </objects>\n    <resources>\n        <image name=\"location.circle.fill\" catalog=\"system\" width=\"128\" height=\"123\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "Clip/History/HistoryViewController.swift",
    "content": "//\n//  HistoryViewController.swift\n//  Clip\n//\n//  Created by Riley Testut on 6/10/19.\n//  Copyright © 2019 Riley Testut. All rights reserved.\n//\n\nimport UIKit\nimport MobileCoreServices\nimport Combine\nimport CoreLocation\nimport Contacts\n\nimport ClipKit\nimport Roxas\n\nclass HistoryViewController: UITableViewController\n{\n    private var dataSource: RSTFetchedResultsTableViewPrefetchingDataSource<PasteboardItem, UIImage>!\n    \n    private let _undoManager = UndoManager()\n    \n    private var prototypeCell: ClippingTableViewCell!\n    private var navigationBarMaskView: UIView!\n    private var navigationBarGradientView: GradientView!\n    \n    private var didAddInitialLayoutConstraints = false\n    private var cachedHeights = [NSManagedObjectID: CGFloat]()\n    \n    private weak var selectedItem: PasteboardItem?\n    private var updateTimer: Timer?\n    private var fetchLimitSettingObservation: NSKeyValueObservation?\n    private var cancellables = Set<AnyCancellable>()\n    \n    private lazy var dateComponentsFormatter: DateComponentsFormatter = {\n        let formatter = DateComponentsFormatter()\n        formatter.unitsStyle = .abbreviated\n        formatter.maximumUnitCount = 1\n        formatter.allowedUnits = [.second, .minute, .hour, .day]\n        return formatter\n    }()\n    \n    override var undoManager: UndoManager? {\n        return _undoManager\n    }\n    \n    override var canBecomeFirstResponder: Bool {\n        return true\n    }\n    \n    override var preferredStatusBarStyle: UIStatusBarStyle {\n        return .lightContent\n    }\n    \n    override func viewDidLoad()\n    {\n        super.viewDidLoad()\n        \n        self.subscribe()\n        \n        self.extendedLayoutIncludesOpaqueBars = true\n        \n        self.tableView.backgroundView = self.makeGradientView()\n        \n        self.updateDataSource()\n        \n        self.tableView.contentInset.top = 8\n        self.tableView.estimatedRowHeight = 0\n        \n        self.prototypeCell = ClippingTableViewCell.instantiate(with: ClippingTableViewCell.nib!)\n        self.tableView.register(ClippingTableViewCell.nib, forCellReuseIdentifier: RSTCellContentGenericCellIdentifier)\n        \n        DatabaseManager.shared.persistentContainer.viewContext.undoManager = self.undoManager\n        \n        NotificationCenter.default.addObserver(self, selector: #selector(HistoryViewController.didEnterBackground(_:)), name: UIApplication.didEnterBackgroundNotification, object: nil)\n        NotificationCenter.default.addObserver(self, selector: #selector(HistoryViewController.willEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)\n        NotificationCenter.default.addObserver(self, selector: #selector(HistoryViewController.settingsDidChange(_:)), name: SettingsViewController.settingsDidChangeNotification, object: nil)\n        \n        self.fetchLimitSettingObservation = UserDefaults.shared.observe(\\.historyLimit) { [weak self] (defaults, change) in\n            self?.updateDataSource()\n        }\n        \n        self.navigationBarGradientView = self.makeGradientView()\n        self.navigationBarGradientView.translatesAutoresizingMaskIntoConstraints = false\n        \n        self.navigationBarMaskView = UIView()\n        self.navigationBarMaskView.clipsToBounds = true\n        self.navigationBarMaskView.translatesAutoresizingMaskIntoConstraints = false\n        self.navigationBarMaskView.addSubview(self.navigationBarGradientView)\n        \n        if let navigationBar = self.navigationController?.navigationBar\n        {\n            if #available(iOS 13.0, *)\n            {\n                let attributes: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor.white]\n                \n                let standardAppearance = navigationBar.standardAppearance\n                standardAppearance.configureWithOpaqueBackground()\n                standardAppearance.backgroundColor = .clipLightPink\n                standardAppearance.titleTextAttributes = attributes\n                standardAppearance.largeTitleTextAttributes = attributes\n                standardAppearance.shadowImage = nil\n                \n                let scrollEdgeAppearance = navigationBar.scrollEdgeAppearance\n                scrollEdgeAppearance?.configureWithTransparentBackground()\n                scrollEdgeAppearance?.titleTextAttributes = attributes\n                scrollEdgeAppearance?.largeTitleTextAttributes = attributes\n            }\n            else\n            {\n                navigationBar.shadowImage = UIImage()\n                navigationBar.setBackgroundImage(nil, for: .default)\n                navigationBar.insertSubview(self.navigationBarMaskView, at: 1)\n            }\n        }\n        \n        if let tabBar = self.navigationController?.tabBarController?.tabBar\n        {\n            let appearance = tabBar.standardAppearance\n            tabBar.scrollEdgeAppearance = appearance\n        }\n        \n        self.navigationController?.tabBarItem.image = UIImage(systemName: \"list.bullet\")\n        \n        self.startUpdating()\n    }\n    \n    override func viewDidAppear(_ animated: Bool)\n    {\n        super.viewDidAppear(animated)\n        \n        self.becomeFirstResponder()\n    }\n    \n    override func viewWillDisappear(_ animated: Bool)\n    {\n        super.viewWillDisappear(animated)\n        \n        self.resignFirstResponder()\n    }\n    \n    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)\n    {\n        super.viewWillTransition(to: size, with: coordinator)\n        \n        self.cachedHeights.removeAll()\n    }\n    \n    override func viewDidLayoutSubviews()\n    {\n        super.viewDidLayoutSubviews()\n        \n        if #available(iOS 13.0, *) {}\n        else\n        {\n            if let navigationBar = self.navigationController?.navigationBar, !self.didAddInitialLayoutConstraints\n            {\n                self.didAddInitialLayoutConstraints = true\n                \n                NSLayoutConstraint.activate([self.navigationBarGradientView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),\n                                             self.navigationBarGradientView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),\n                                             self.navigationBarGradientView.topAnchor.constraint(equalTo: self.view.topAnchor),\n                                             self.navigationBarGradientView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor)])\n                \n                NSLayoutConstraint.activate([self.navigationBarMaskView.leadingAnchor.constraint(equalTo: navigationBar.leadingAnchor),\n                                             self.navigationBarMaskView.trailingAnchor.constraint(equalTo: navigationBar.trailingAnchor),\n                                             self.navigationBarMaskView.topAnchor.constraint(equalTo: self.view.topAnchor),\n                                             self.navigationBarMaskView.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor)])\n            }\n        }\n    }\n    \n    override func prepare(for segue: UIStoryboardSegue, sender: Any?)\n    {\n        guard segue.identifier == \"showSettings\" else { return }\n        guard let sender = sender as? UIBarButtonItem else { return }\n        \n        let navigationController = segue.destination as! UINavigationController\n        \n        let settingsViewController = navigationController.viewControllers[0] as! SettingsViewController\n        settingsViewController.view.layoutIfNeeded()\n        \n        navigationController.preferredContentSize = CGSize(width: 375, height: settingsViewController.tableView.contentSize.height)\n        \n        navigationController.popoverPresentationController?.delegate = self\n        navigationController.popoverPresentationController?.barButtonItem = sender\n    }\n}\n\nextension HistoryViewController\n{\n    override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool\n    {\n        let supportedActions = [#selector(UIResponderStandardEditActions.copy(_:)), #selector(UIResponderStandardEditActions.delete(_:)), #selector(HistoryViewController._share(_:))]\n        \n        let isSupported = supportedActions.contains(action)\n        return isSupported\n    }\n    \n    @objc override func copy(_ sender: Any?)\n    {\n        guard let item = self.selectedItem else { return }\n        \n        UIPasteboard.general.copy(item)\n    }\n    \n    @objc override func delete(_ sender: Any?)\n    {\n        guard let item = self.selectedItem else { return }\n        \n        // Use the main view context so we can undo this operation easily.\n        // Saving a context can mess with its undo history, so we only save main context when we enter background.\n        item.isMarkedForDeletion = true\n    }\n    \n    @objc func _share(_ sender: Any?)\n    {\n        guard let item = self.selectedItem, let indexPath = self.dataSource.fetchedResultsController.indexPath(forObject: item) else { return }\n        \n        let cell = self.tableView.cellForRow(at: indexPath)\n        \n        let activityViewController = UIActivityViewController(activityItems: [item], applicationActivities: nil)\n        activityViewController.popoverPresentationController?.sourceItem = cell\n        self.present(activityViewController, animated: true, completion: nil)\n    }\n}\n\nprivate extension HistoryViewController\n{\n    func subscribe()\n    {\n        //TODO: Uncomment once we can tell user to enable location for background execution.\n        //ApplicationMonitor.shared.locationManager.$status\n        //    .receive(on: RunLoop.main)\n        //    .compactMap { $0?.error }\n        //    .sink { self.present($0) }\n        //    .store(in: &self.cancellables)\n    }\n    \n    func makeDataSource() -> RSTFetchedResultsTableViewPrefetchingDataSource<PasteboardItem, UIImage>\n    {\n        let fetchRequest = PasteboardItem.historyFetchRequest()\n        fetchRequest.returnsObjectsAsFaults = false\n        fetchRequest.relationshipKeyPathsForPrefetching = [#keyPath(PasteboardItem.preferredRepresentation)]\n        \n        let dataSource = RSTFetchedResultsTableViewPrefetchingDataSource<PasteboardItem, UIImage>(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.persistentContainer.viewContext)\n        dataSource.cellConfigurationHandler = { [weak self] (cell, item, indexPath) in\n            let cell = cell as! ClippingTableViewCell\n            cell.contentLabel.isHidden = false\n            cell.contentImageView.isHidden = true\n            \n            self?.updateDate(for: cell, item: item)\n            \n            if let representation = item.preferredRepresentation\n            {\n                cell.titleLabel.text = representation.type.localizedName\n                \n                switch representation.type\n                {\n                case .text: cell.contentLabel.text = representation.stringValue\n                case .attributedText: cell.contentLabel.text = representation.attributedStringValue?.string\n                case .url: cell.contentLabel.text = representation.urlValue?.absoluteString\n                case .image:\n                    cell.contentLabel.isHidden = true\n                    cell.contentImageView.isHidden = false\n                    cell.contentImageView.isIndicatingActivity = true\n                }\n            }\n            else\n            {\n                cell.titleLabel.text = NSLocalizedString(\"Unknown\", comment: \"\")\n                cell.contentLabel.isHidden = true\n            }\n            \n            if UserDefaults.shared.showLocationIcon\n            {\n                cell.locationButton.isHidden = (item.location == nil)\n                cell.locationButton.addTarget(self, action: #selector(HistoryViewController.showLocation(_:)), for: .primaryActionTriggered)\n            }\n            else\n            {\n                cell.locationButton.isHidden = true\n            }\n            \n            if indexPath.row < UserDefaults.shared.historyLimit.rawValue\n            {\n                cell.bottomConstraint.isActive = true\n            }\n            else\n            {\n                // Make it not active so we can collapse the cell to a height of 0 without auto layout errors.\n                cell.bottomConstraint.isActive = false\n            }\n        }\n        \n        dataSource.prefetchHandler = { (item, indexPath, completionHandler) in\n            guard let representation = item.preferredRepresentation, representation.type == .image else { return nil }\n            \n            return RSTBlockOperation() { (operation) in\n                guard let image = representation.imageValue?.resizing(toFill: CGSize(width: 500, height: 500)) else { return completionHandler(nil, nil) }\n                completionHandler(image, nil)\n            }\n        }\n        \n        dataSource.prefetchCompletionHandler = { (cell, image, indexPath, error) in\n            DispatchQueue.main.async {\n                let cell = cell as! ClippingTableViewCell\n\n                if let image = image\n                {\n                    cell.contentImageView.image = image\n                }\n                else\n                {\n                    cell.contentImageView.image = nil\n                }\n\n                cell.contentImageView.isIndicatingActivity = false\n            }\n        }\n        \n        let placeholderView = RSTPlaceholderView()\n        placeholderView.textLabel.text = NSLocalizedString(\"No Clippings\", comment: \"\")\n        placeholderView.textLabel.textColor = .white\n        placeholderView.detailTextLabel.text = NSLocalizedString(\"Items that you've copied to the clipboard will appear here.\", comment: \"\")\n        placeholderView.detailTextLabel.textColor = .white\n        \n        let vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: UIBlurEffect(style: .dark)))\n        vibrancyView.contentView.addSubview(placeholderView, pinningEdgesWith: .zero)\n        \n        let gradientView = self.makeGradientView()\n        gradientView.addSubview(vibrancyView, pinningEdgesWith: .zero)\n        dataSource.placeholderView = gradientView\n        \n        return dataSource\n    }\n    \n    func makeGradientView() -> GradientView\n    {\n        let gradientView = GradientView()\n        gradientView.colors = [.clipLightPink, .clipPink]\n        return gradientView\n    }\n    \n    func updateDataSource()\n    {\n        self.stopUpdating()\n        \n        self.dataSource = self.makeDataSource()\n        self.tableView.dataSource = self.dataSource\n        self.tableView.prefetchDataSource = self.dataSource\n        self.tableView.reloadData()\n        \n        self.startUpdating()\n    }\n    \n    func updateDate(for cell: ClippingTableViewCell, item: PasteboardItem)\n    {\n        if Date().timeIntervalSince(item.date) < 2\n        {\n            cell.dateLabel.text = NSLocalizedString(\"now\", comment: \"\")\n        }\n        else\n        {\n            cell.dateLabel.text = self.dateComponentsFormatter.string(from: item.date, to: Date())\n        }\n    }\n    \n    func showMenu(at indexPath: IndexPath)\n    {\n        guard let cell = self.tableView.cellForRow(at: indexPath) as? ClippingTableViewCell else { return }\n        \n        let item = self.dataSource.item(at: indexPath)\n        self.selectedItem = item\n        \n        let targetRect = cell.clippingView.frame\n        \n        self.becomeFirstResponder()\n        \n        UIMenuController.shared.setTargetRect(targetRect, in: cell)\n        UIMenuController.shared.setMenuVisible(true, animated: true)\n    }\n    \n    @objc func showLocation(_ sender: UIButton)\n    {\n        let point = self.view.convert(sender.center, from: sender.superview!)\n        guard let indexPath = self.tableView.indexPathForRow(at: point) else { return }\n        \n        let item = self.dataSource.item(at: indexPath)\n        guard let location = item.location else { return }\n        \n        let geocoder = CLGeocoder()\n        geocoder.reverseGeocodeLocation(location) { (placemarks, error) in\n            DispatchQueue.main.async {\n                let title: String\n                let message: String?\n                \n                if let placemarks, let placemark = placemarks.first,\n                   let postalAddress = placemark.postalAddress?.mutableCopy() as? CNMutablePostalAddress\n                {\n                    // The location isn't precise, so don't pretend that it is by showing street address.\n                    postalAddress.street = \"\"\n                    postalAddress.subLocality = \"\"\n                    \n                    let formatter = CNPostalAddressFormatter()\n                    \n                    if let sublocality = placemark.subLocality\n                    {\n                        title = sublocality + \"\\n\" + formatter.string(from: postalAddress)\n                    }\n                    else\n                    {\n                        title = formatter.string(from: postalAddress)\n                    }\n\n                    message = nil\n                }\n                else if let error\n                {\n                    title = NSLocalizedString(\"Unable to Look Up Location\", comment: \"\")\n                    message = error.localizedDescription + \"\\n\\n\" + \"\\(location.coordinate.latitude), \\(location.coordinate.longitude)\"\n                }\n                else\n                {\n                    title = \"\\(location.coordinate.latitude), \\(location.coordinate.longitude)\"\n                    message = nil\n                }\n                \n                let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)\n                alertController.addAction(.ok)\n                self.present(alertController, animated: true)\n            }\n        }\n    }\n    \n    func startUpdating()\n    {\n        self.stopUpdating()\n        \n        self.updateTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] (timer) in\n            guard let self = self else { return }\n            \n            for indexPath in self.tableView.indexPathsForVisibleRows ?? []\n            {\n                guard let cell = self.tableView.cellForRow(at: indexPath) as? ClippingTableViewCell else { continue }\n                \n                let item = self.dataSource.item(at: indexPath)\n                self.updateDate(for: cell, item: item)\n            }\n        }\n    }\n    \n    func stopUpdating()\n    {\n        self.updateTimer?.invalidate()\n        self.updateTimer = nil\n    }\n}\n\nprivate extension HistoryViewController\n{\n    func present(_ error: Error)\n    {\n        let nsError = error as NSError\n        \n        let alertController = UIAlertController(title: nsError.localizedFailureReason ?? nsError.localizedDescription,\n                                                message: nsError.localizedRecoverySuggestion, preferredStyle: .alert)\n        \n        if let recoverableError = error as? RecoverableError, !recoverableError.recoveryOptions.isEmpty\n        {\n            alertController.addAction(.cancel)\n            \n            for (index, title) in zip(0..., recoverableError.recoveryOptions)\n            {\n                let action = UIAlertAction(title: title, style: .default) { (action) in\n                    recoverableError.attemptRecovery(optionIndex: index) { (success) in\n                        print(\"Recovered from error with success:\", success)\n                    }\n                }\n                alertController.addAction(action)\n            }\n        }\n        else\n        {\n            alertController.addAction(.ok)\n        }\n        \n        self.present(alertController, animated: true, completion: nil)\n    }\n}\n\nprivate extension HistoryViewController\n{\n    @objc func didEnterBackground(_ notification: Notification)\n    {\n        // Save any pending changes to disk.\n        if DatabaseManager.shared.persistentContainer.viewContext.hasChanges\n        {\n            do\n            {\n                try DatabaseManager.shared.persistentContainer.viewContext.save()\n            }\n            catch\n            {\n                print(\"Failed to save view context.\", error)\n            }\n        }\n        \n        self.undoManager?.removeAllActions()\n        \n        self.stopUpdating()\n    }\n    \n    @objc func willEnterForeground(_ notification: Notification)\n    {\n        self.startUpdating()\n    }\n    \n    @objc func settingsDidChange(_ notification: Notification)\n    {\n        self.tableView.reloadData()\n    }\n    \n    @IBAction func unwindToHistoryViewController(_ segue: UIStoryboardSegue)\n    {\n    }\n}\n\nextension HistoryViewController\n{\n    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat\n    {\n        // It's far *far* easier to simply set row height to 0 for cells beyond history limit\n        // than to actually limit fetched results to the correct number live (with insertions and deletions).\n        guard indexPath.row < UserDefaults.shared.historyLimit.rawValue else { return 0.0 }\n        \n        let item = self.dataSource.item(at: indexPath)\n        \n        if let height = self.cachedHeights[item.objectID]\n        {\n            return height\n        }\n        \n        let portraitScreenHeight = UIScreen.main.coordinateSpace.convert(UIScreen.main.bounds, to: UIScreen.main.fixedCoordinateSpace).height\n        let maximumHeight: CGFloat\n        \n        if item.preferredRepresentation?.type == .image\n        {\n            maximumHeight = portraitScreenHeight / 2\n        }\n        else\n        {\n            maximumHeight = portraitScreenHeight / 3\n        }\n        \n        let widthConstraint = self.prototypeCell.contentView.widthAnchor.constraint(equalToConstant: tableView.bounds.width)\n        let heightConstraint = self.prototypeCell.contentView.heightAnchor.constraint(lessThanOrEqualToConstant: maximumHeight)\n        NSLayoutConstraint.activate([widthConstraint, heightConstraint])\n        defer { NSLayoutConstraint.deactivate([widthConstraint, heightConstraint]) }\n        \n        self.dataSource.cellConfigurationHandler(self.prototypeCell, item, indexPath)\n        \n        let size = self.prototypeCell.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)\n        self.cachedHeights[item.objectID] = size.height\n        return size.height\n    }\n    \n    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)\n    {\n        self.showMenu(at: indexPath)\n    }\n}\n\nextension HistoryViewController: UIPopoverPresentationControllerDelegate\n{\n    func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle\n    {\n        return .none\n    }\n}\n"
  },
  {
    "path": "Clip/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>ALTAppGroups</key>\n\t<array>\n\t\t<string>group.com.rileytestut.Clip</string>\n\t</array>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>Clip</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(MARKETING_VERSION)</string>\n\t<key>CFBundleURLTypes</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>CFBundleTypeRole</key>\n\t\t\t<string>Editor</string>\n\t\t\t<key>CFBundleURLName</key>\n\t\t\t<string>Clip General</string>\n\t\t\t<key>CFBundleURLSchemes</key>\n\t\t\t<array>\n\t\t\t\t<string>clip</string>\n\t\t\t</array>\n\t\t</dict>\n\t</array>\n\t<key>CFBundleVersion</key>\n\t<string>$(CURRENT_PROJECT_VERSION)</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>\n\t<string>Clip requires “Always” location permission to automatically tag clippings with your location. Your location data never leaves this device.</string>\n\t<key>NSLocationDefaultAccuracyReduced</key>\n\t<true/>\n\t<key>NSLocationWhenInUseUsageDescription</key>\n\t<string>Clip requires location permissions to automatically tag clippings with your location. Your location data never leaves this device.</string>\n\t<key>UIApplicationSceneManifest</key>\n\t<dict>\n\t\t<key>UIApplicationSupportsMultipleScenes</key>\n\t\t<true/>\n\t\t<key>UISceneConfigurations</key>\n\t\t<dict>\n\t\t\t<key>UIWindowSceneSessionRoleApplication</key>\n\t\t\t<array>\n\t\t\t\t<dict>\n\t\t\t\t\t<key>UILaunchStoryboardName</key>\n\t\t\t\t\t<string>LaunchScreen</string>\n\t\t\t\t\t<key>UISceneConfigurationName</key>\n\t\t\t\t\t<string>Default Configuration</string>\n\t\t\t\t\t<key>UISceneDelegateClassName</key>\n\t\t\t\t\t<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>\n\t\t\t\t\t<key>UISceneStoryboardFile</key>\n\t\t\t\t\t<string>Main</string>\n\t\t\t\t</dict>\n\t\t\t</array>\n\t\t</dict>\n\t</dict>\n\t<key>UIBackgroundModes</key>\n\t<array>\n\t\t<string>location</string>\n\t</array>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UIRequiredDeviceCapabilities</key>\n\t<array>\n\t\t<string>armv7</string>\n\t</array>\n\t<key>UIStatusBarStyle</key>\n\t<string>UIStatusBarStyleLightContent</string>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UIUserInterfaceStyle</key>\n\t<string>Light</string>\n\t<key>UTExportedTypeDeclarations</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>UTTypeConformsTo</key>\n\t\t\t<array>\n\t\t\t\t<string>public.data</string>\n\t\t\t</array>\n\t\t\t<key>UTTypeDescription</key>\n\t\t\t<string>Clipping</string>\n\t\t\t<key>UTTypeIconFiles</key>\n\t\t\t<array/>\n\t\t\t<key>UTTypeIdentifier</key>\n\t\t\t<string>com.rileytestut.Clip.Clipping</string>\n\t\t</dict>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "Clip/Map View/ClippingSheet.swift",
    "content": "//\n//  ClippingSheet.swift\n//  Clip\n//\n//  Created by Riley Testut on 3/20/24.\n//  Copyright © 2024 Riley Testut. All rights reserved.\n//\n\nimport SwiftUI\n\nimport ClipKit\n\n@available(iOS 16.4, *)\nstruct ClippingSheet: View\n{\n    var pasteboardItem: PasteboardItem\n    \n    @Environment(\\.dismiss)\n    private var dismiss\n    \n    var body: some View {\n        NavigationStack {\n            ClippingCell(pasteboardItem: pasteboardItem)\n                .padding()\n                .toolbar {\n                    ToolbarItem(placement: .confirmationAction) {\n                        Button(\"Copy\", action: copy)\n                    }\n                    \n                    ToolbarItem(placement: .cancellationAction) {\n                        Button(\"Cancel\", action: cancel)\n                    }\n                }\n        }\n        .presentationBackground(Material.regular)\n        .presentationDetents([.fraction(0.33)])\n        .tint(.init(uiColor: .clipPink))\n    }\n}\n\n@available(iOS 16.4, *)\nprivate extension ClippingSheet\n{\n    func copy()\n    {\n        let center = CFNotificationCenterGetDarwinNotifyCenter()\n        CFNotificationCenterPostNotification(center, .ignoreNextPasteboardChange, nil, nil, true)\n        \n        UIPasteboard.general.copy(self.pasteboardItem)\n        \n        self.dismiss()\n    }\n    \n    func cancel()\n    {\n        self.dismiss()\n    }\n}\n"
  },
  {
    "path": "Clip/Map View/ClippingsMapView.swift",
    "content": "//\n//  HistoryMapView.swift\n//  Clip\n//\n//  Created by Riley Testut on 3/20/24.\n//  Copyright © 2024 Riley Testut. All rights reserved.\n//\n\nimport MapKit\nimport UIKit\nimport SwiftUI\n\nimport ClipKit\n\n@available(iOS 17, *)\nclass ClippingsMapViewController: UIHostingController<AnyView>\n{\n    @MainActor \n    required dynamic init?(coder aDecoder: NSCoder) {\n        let view = AnyView(erasing: ClippingsMapView().environment(\\.managedObjectContext, DatabaseManager.shared.persistentContainer.viewContext))\n        super.init(coder: aDecoder, rootView: view)\n        \n        self.tabBarItem.image = UIImage(systemName: \"map\")\n    }\n}\n\n@MainActor @available(iOS 17, *)\nstruct ClippingsMapView: View\n{\n    @FetchRequest(fetchRequest: PasteboardItem.historyFetchRequest())\n    private var pasteboardItems: FetchedResults<PasteboardItem>\n    \n    @State\n    private var selectedItem: PasteboardItem?\n    \n    var body: some View {\n        Map(selection: $selectedItem) {\n            // Must use \\.self as keypath for selection to work\n            ForEach(pasteboardItems, id: \\.self) { pasteboardItem in\n                if let location = pasteboardItem.location\n                {\n                    Marker(pasteboardItem.date.formatted(), systemImage: \"paperclip\", coordinate: location.coordinate)\n                }\n            }\n        }\n        .sheet(item: $selectedItem) { pasteboardItem in\n            ClippingSheet(pasteboardItem: pasteboardItem)\n        }\n    }\n}\n"
  },
  {
    "path": "Clip/Pasteboard/LocationManager.swift",
    "content": "//\n//  LocationManager.swift\n//  Clip\n//\n//  Created by Riley Testut on 11/6/20.\n//  Copyright © 2020 Riley Testut. All rights reserved.\n//\n\nimport CoreLocation\nimport Combine\nimport UIKit\n\nextension LocationManager\n{\n    typealias Status = Result<Void, Swift.Error>\n    \n    enum Error: LocalizedError, RecoverableError\n    {\n        case requiresAlwaysAuthorization\n        \n        var failureReason: String? {\n            switch self\n            {\n            case .requiresAlwaysAuthorization: return NSLocalizedString(\"Clip requires “Always” location permission.\", comment: \"\")\n            }\n        }\n        \n        var recoverySuggestion: String? {\n            switch self\n            {\n            case .requiresAlwaysAuthorization: return NSLocalizedString(\"Please grant Clip “Always” location permission in Settings so it can run in the background indefinitely.\", comment: \"\")\n            }\n        }\n        \n        var recoveryOptions: [String] {\n            switch self\n            {\n            case .requiresAlwaysAuthorization: return [NSLocalizedString(\"Open Settings\", comment: \"\")]\n            }\n        }\n        \n        func attemptRecovery(optionIndex recoveryOptionIndex: Int) -> Bool\n        {\n            return false\n        }\n        \n        func attemptRecovery(optionIndex recoveryOptionIndex: Int, resultHandler handler: @escaping (Bool) -> Void)\n        {\n            switch self\n            {\n            case .requiresAlwaysAuthorization:\n                let openURL = URL(string: UIApplication.openSettingsURLString)!\n                UIApplication.shared.open(openURL, options: [:], completionHandler: handler)\n            }\n        }\n    }\n}\n\nclass LocationManager: NSObject, ObservableObject\n{\n    @PublishedPipeline({ removeDuplicatesPipeline($0) })\n    var status: Status? = nil\n    \n    var location: CLLocation? {\n        return self.locationManager.location\n    }\n\n    private let locationManager: CLLocationManager\n    \n    override init()\n    {\n        self.locationManager = CLLocationManager()\n        self.locationManager.distanceFilter = CLLocationDistanceMax\n        self.locationManager.pausesLocationUpdatesAutomatically = false\n        self.locationManager.allowsBackgroundLocationUpdates = true\n        \n        if #available(iOS 14.0, *)\n        {\n            self.locationManager.desiredAccuracy = kCLLocationAccuracyReduced\n        }\n        else\n        {\n            self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers\n        }\n        \n        super.init()\n        \n        self.locationManager.delegate = self\n    }\n    \n    func start()\n    {\n        switch self.status\n        {\n        case .success: return\n        case .failure, nil: break\n        }\n        \n        if CLLocationManager.authorizationStatus() == .notDetermined || CLLocationManager.authorizationStatus() == .authorizedWhenInUse\n        {\n            self.locationManager.requestAlwaysAuthorization()\n            return\n        }\n        \n        self.locationManager.startUpdatingLocation()\n    }\n    \n    func stop()\n    {\n        self.locationManager.stopUpdatingLocation()\n        self.status = nil\n    }\n}\n\nprivate extension LocationManager\n{\n    static func removeDuplicatesPipeline<T: Publisher>(_ publisher: T) -> AnyPublisher<Status?, Never>\n    where T.Output == Status?, T.Failure == Never\n    {\n        return publisher\n            .removeDuplicates { (a, b) in\n                switch (a, b)\n                {\n                case (nil, nil), (.success(()), .success(())): return true\n                case (.failure(let errorA as NSError), .failure(let errorB as NSError)): return errorA.domain == errorB.domain && errorA.code == errorB.code\n                case (nil, _), (.success, _), (.failure, _): return false\n                }\n            }\n            .eraseToAnyPublisher()\n    }\n}\n\nextension LocationManager: CLLocationManagerDelegate\n{\n    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus)\n    {\n        switch status\n        {\n        case .notDetermined: break\n        case .restricted, .denied, .authorizedWhenInUse: self.status = .failure(Error.requiresAlwaysAuthorization)\n        case .authorizedAlways: self.start()\n        @unknown default: break\n        }\n    }\n    \n    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])\n    {\n        self.status = .success(())\n    }\n    \n    func locationManager(_ manager: CLLocationManager, didFailWithError error: Swift.Error)\n    {\n        if let error = error as? CLError\n        {\n            guard error.code != .denied else {\n                self.status = .failure(Error.requiresAlwaysAuthorization)\n                return\n            }\n        }\n        \n        self.status = .failure(error)\n    }\n}\n"
  },
  {
    "path": "Clip/Pasteboard/PasteboardMonitor.swift",
    "content": "//\n//  PasteboardMonitor.swift\n//  Clip\n//\n//  Created by Riley Testut on 6/11/19.\n//  Copyright © 2019 Riley Testut. All rights reserved.\n//\n\nimport UIKit\nimport AVFoundation\nimport UserNotifications\nimport CoreLocation\n\nimport ClipKit\nimport Roxas\n\nprivate let PasteboardMonitorDidChangePasteboard: @convention(c) (CFNotificationCenter?, UnsafeMutableRawPointer?, CFNotificationName?, UnsafeRawPointer?, CFDictionary?) -> Void =\n{ (center, observer, name, object, userInfo) in\n    ApplicationMonitor.shared.pasteboardMonitor.didChangePasteboard()\n}\n\nprivate let PasteboardMonitorIgnoreNextPasteboardChange: @convention(c) (CFNotificationCenter?, UnsafeMutableRawPointer?, CFNotificationName?, UnsafeRawPointer?, CFDictionary?) -> Void =\n{ (center, observer, name, object, userInfo) in\n    ApplicationMonitor.shared.pasteboardMonitor.ignoreNextPasteboardChange = true\n}\n\nclass PasteboardMonitor\n{\n    private(set) var isStarted = false\n    fileprivate var ignoreNextPasteboardChange = false\n    \n    private let feedbackGenerator = UINotificationFeedbackGenerator()\n}\n\nextension PasteboardMonitor\n{\n    func start(completionHandler: @escaping (Result<Void, Error>) -> Void)\n    {\n        guard !self.isStarted else { return }\n        self.isStarted = true\n                \n        self.registerForNotifications()\n        completionHandler(.success(()))\n    }\n}\n\nprivate extension PasteboardMonitor\n{\n    func registerForNotifications()\n    {\n        let center = CFNotificationCenterGetDarwinNotifyCenter()\n        CFNotificationCenterAddObserver(center, nil, PasteboardMonitorDidChangePasteboard, CFNotificationName.didChangePasteboard.rawValue, nil, .deliverImmediately)\n        CFNotificationCenterAddObserver(center, nil, PasteboardMonitorIgnoreNextPasteboardChange, CFNotificationName.ignoreNextPasteboardChange.rawValue, nil, .deliverImmediately)\n        \n        #if !targetEnvironment(simulator)\n        let beginListeningSelector = [\"Notifications\", \"Change\", \"Pasteboard\", \"To\", \"Listening\", \"begin\"].reversed().joined()\n        \n        let className = [\"Connection\", \"Server\", \"PB\"].reversed().joined()\n        \n        let PBServerConnection = NSClassFromString(className) as AnyObject\n        _ = PBServerConnection.perform(NSSelectorFromString(beginListeningSelector))\n        #endif\n        \n        let changedNotification = [\"changed\", \"pasteboard\", \"apple\", \"com\"].reversed().joined(separator: \".\")\n        NotificationCenter.default.addObserver(self, selector: #selector(PasteboardMonitor.pasteboardDidUpdate), name: Notification.Name(changedNotification), object: nil)\n    }\n    \n    @objc func pasteboardDidUpdate()\n    {\n        guard !self.ignoreNextPasteboardChange else {\n            self.ignoreNextPasteboardChange = false\n            return\n        }\n        \n        DispatchQueue.main.async {\n            if UIApplication.shared.applicationState != .background\n            {\n                // Don't present notifications for items copied from within Clip.\n                guard !UIPasteboard.general.contains(pasteboardTypes: [UTI.clipping]) else { return }\n            }\n            \n            UNUserNotificationCenter.current().getNotificationSettings { (settings) in\n                if settings.soundSetting == .enabled\n                {\n                    UIDevice.current.vibrate()\n                }\n            }            \n            \n            let content = UNMutableNotificationContent()\n            content.categoryIdentifier = UNNotificationCategory.clipboardReaderIdentifier\n            content.title = NSLocalizedString(\"Clipboard Changed\", comment: \"\")\n            content.body = NSLocalizedString(\"Swipe down to save to Clip.\", comment: \"\")\n            \n            if let location = ApplicationMonitor.shared.locationManager.location\n            {\n                content.userInfo = [\n                    UNNotification.latitudeUserInfoKey: location.coordinate.latitude,\n                    UNNotification.longitudeUserInfoKey: location.coordinate.longitude\n                ]\n            }\n            \n            let request = UNNotificationRequest(identifier: \"ClipboardChanged\", content: content, trigger: nil)\n            UNUserNotificationCenter.current().add(request) { (error) in\n                if let error = error {\n                    print(error)\n                }\n            }\n        }\n    }\n}\n\nprivate extension PasteboardMonitor\n{\n    func didChangePasteboard()\n    {\n        DatabaseManager.shared.refresh()\n        \n        UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [\"ClipboardChanged\"])\n    }\n}\n"
  },
  {
    "path": "Clip/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"filename\" : \"Clip1024.png\",\n      \"idiom\" : \"universal\",\n      \"platform\" : \"ios\",\n      \"size\" : \"1024x1024\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Clip/Resources/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}"
  },
  {
    "path": "Clip/Resources/Assets.xcassets/Settings.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"gear_1.pdf\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  },\n  \"properties\" : {\n    \"template-rendering-intent\" : \"template\"\n  }\n}"
  },
  {
    "path": "Clip/SceneDelegate.swift",
    "content": "//\n//  SceneDelegate.swift\n//  Clip\n//\n//  Created by Riley Testut on 10/30/20.\n//  Copyright © 2020 Riley Testut. All rights reserved.\n//\n\nimport UIKit\nimport ClipKit\n\nimport Roxas\n\nclass SceneDelegate: UIResponder, UIWindowSceneDelegate\n{\n    var window: UIWindow?\n\n    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)\n    {\n        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.\n        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.\n        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).\n        guard let _ = (scene as? UIWindowScene) else { return }\n        \n        if let context = connectionOptions.urlContexts.first\n        {\n            self.open(context)\n        }\n    }\n\n    func sceneWillEnterForeground(_ scene: UIScene)\n    {\n        // Called as the scene transitions from the background to the foreground.\n        // Use this method to undo the changes made on entering the background.\n        \n        guard DatabaseManager.shared.isStarted else { return }\n        \n        DatabaseManager.shared.refresh()\n        \n        guard !UIPasteboard.general.hasImages else {\n            // Our duplicate detection does not work for images,\n            // so don't automatically save images upon returning to foreground.\n            return\n        }\n        \n        let location = ApplicationMonitor.shared.locationManager.location\n        DatabaseManager.shared.savePasteboard(location: location) { (result) in\n            do\n            {\n                try result.get()\n                print(\"Saved clipboard upon returning to foreground!\")\n            }\n            catch PasteboardError.noItem, PasteboardError.duplicateItem\n            {\n                // Ignore\n            }\n            catch\n            {\n                print(\"Failed to save clipboard upon returning to app.\")\n            }\n        }\n    }\n    \n    func sceneDidEnterBackground(_ scene: UIScene)\n    {\n        // Called as the scene transitions from the foreground to the background.\n        // Use this method to save data, release shared resources, and store enough scene-specific state information\n        // to restore the scene back to its current state.\n        \n        #if targetEnvironment(simulator)\n        // Audio extension hack to access pasteboard doesn't work in simulator, so for testing just start background task.\n        RSTBeginBackgroundTask(\"com.rileytestut.Clip.simulatorBackgroundTask\")\n        #endif\n        \n        DatabaseManager.shared.purge()\n    }\n    \n    func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>)\n    {\n        guard let context = URLContexts.first else { return }\n        self.open(context)\n    }\n}\n\nprivate extension SceneDelegate\n{\n    func open(_ context: UIOpenURLContext)\n    {\n        guard context.url.scheme?.lowercased() == \"clip\" && context.url.host?.lowercased() == \"settings\" else { return }\n        \n        let openURL = URL(string: UIApplication.openSettingsURLString)!\n        UIApplication.shared.open(openURL, options: [:], completionHandler: nil)\n    }\n}\n\n"
  },
  {
    "path": "Clip/Settings/SettingsViewController.swift",
    "content": "//\n//  SettingsViewController.swift\n//  Clip\n//\n//  Created by Riley Testut on 6/14/19.\n//  Copyright © 2019 Riley Testut. All rights reserved.\n//\n\nimport UIKit\n\nimport ClipKit\n\nextension SettingsViewController\n{\n    private enum Section: CaseIterable\n    {\n        case historyLimit\n        case location\n    }\n    \n    static let settingsDidChangeNotification: Notification.Name = Notification.Name(\"SettingsDidChangeNotification\")\n}\n\nclass SettingsViewController: UITableViewController\n{\n    @IBOutlet private var showLocationIconSwitch: UISwitch!\n    \n    override func viewDidLoad()\n    {\n        super.viewDidLoad()\n        \n        self.showLocationIconSwitch.isOn = UserDefaults.shared.showLocationIcon\n    }\n}\n\nprivate extension SettingsViewController\n{\n    @IBAction func toggleShowLocationIcon(_ sender: UISwitch)\n    {\n        UserDefaults.shared.showLocationIcon = sender.isOn\n        \n        NotificationCenter.default.post(name: SettingsViewController.settingsDidChangeNotification, object: nil)\n    }\n}\n\nextension SettingsViewController\n{\n    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell\n    {\n        let cell = super.tableView(tableView, cellForRowAt: indexPath)\n        \n        switch Section.allCases[indexPath.section]\n        {\n        case .historyLimit:\n            let limit = HistoryLimit.allCases[indexPath.row]\n            cell.accessoryType = (limit == UserDefaults.shared.historyLimit) ? .checkmark : .none\n            \n        case .location: break\n        }\n        \n        return cell\n    }\n    \n    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)\n    {\n        guard Section.allCases[indexPath.section] == .historyLimit else { return }\n        \n        let historyLimit = HistoryLimit.allCases[indexPath.row]\n        UserDefaults.shared.historyLimit = historyLimit\n        \n        tableView.reloadData()\n        \n        self.dismiss(animated: true, completion: nil)\n    }\n}\n"
  },
  {
    "path": "Clip/Types/PublishedPipeline.swift",
    "content": "//\n//  PublishedPipeline.swift\n//  Clip\n//\n//  Created by Riley Testut on 12/4/20.\n//  Copyright © 2020 Riley Testut. All rights reserved.\n//\n\nimport Combine\n\n@propertyWrapper\nclass PublishedPipeline<Value, Pipeline: Publisher>\n{\n    @Published\n    var wrappedValue: Value\n    \n    var projectedValue: AnyPublisher<Pipeline.Output, Pipeline.Failure> {\n        return self.pipeline(self.$wrappedValue.eraseToAnyPublisher()).eraseToAnyPublisher()\n    }\n    \n    private let pipeline: (AnyPublisher<Value, Never>) -> Pipeline\n    \n    init(wrappedValue: Value, _ pipeline: @escaping (AnyPublisher<Value, Never>) -> Pipeline)\n    {\n        self.wrappedValue = wrappedValue\n        self.pipeline = pipeline\n    }\n}\n"
  },
  {
    "path": "Clip.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\tBF0BDE5122B4414A00E1419D /* UTI.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0BDE5022B4414A00E1419D /* UTI.swift */; };\n\t\tBF0BDE5622B456A600E1419D /* PasteboardItem+ActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0BDE5522B456A600E1419D /* PasteboardItem+ActivityItemSource.swift */; };\n\t\tBF0BDE5A22B4771300E1419D /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0BDE5922B4771300E1419D /* SettingsViewController.swift */; };\n\t\tBF0BDE5E22B47D4900E1419D /* UserDefaults+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0BDE5722B4757100E1419D /* UserDefaults+App.swift */; };\n\t\tBF1D5C7A22B029E70062F474 /* Roxas.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF1D5C7522B029DC0062F474 /* Roxas.framework */; platformFilter = ios; };\n\t\tBF1D5C7B22B029E70062F474 /* Roxas.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF1D5C7522B029DC0062F474 /* Roxas.framework */; platformFilter = ios; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };\n\t\tBF1D5C7F22B02BF30062F474 /* PasteboardMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1D5C7E22B02BF30062F474 /* PasteboardMonitor.swift */; };\n\t\tBF235BC1247D8B5300CCFCB0 /* UIPasteboard+PasteboardItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF235BC0247D8B5300CCFCB0 /* UIPasteboard+PasteboardItem.swift */; };\n\t\tBF50E7D922C2BF010070E17B /* Bundle+AppGroups.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF50E7D822C2BF010070E17B /* Bundle+AppGroups.swift */; };\n\t\tBF770E6522BC7688002A40FE /* UIDevice+Vibration.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF770E6122BC767A002A40FE /* UIDevice+Vibration.swift */; };\n\t\tBF7B9EE122B81C980042C873 /* Int+Bytes.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF7B9EE022B81C980042C873 /* Int+Bytes.swift */; };\n\t\tBF7E6FE3247C4FCD0058F4D4 /* KeyboardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF7E6FE2247C4FCD0058F4D4 /* KeyboardViewController.swift */; };\n\t\tBF7E6FE7247C4FCD0058F4D4 /* ClipBoard.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = BF7E6FE0247C4FCD0058F4D4 /* ClipBoard.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };\n\t\tBF8F76F3254CC0E0005AF18B /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8F76F2254CC0E0005AF18B /* SceneDelegate.swift */; };\n\t\tBFA2DF60247DD21F00E31E4D /* Keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF7E6FF0247C50540058F4D4 /* Keyboard.swift */; };\n\t\tBFA2DF61247DD23800E31E4D /* ClippingCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF7E6FF2247C518D0058F4D4 /* ClippingCell.swift */; };\n\t\tBFA2DF62247DD23C00E31E4D /* Blur.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0AB807247C5D1F0090B43B /* Blur.swift */; };\n\t\tBFA2DF65247DD2B200E31E4D /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFA2DF64247DD2B200E31E4D /* Colors.xcassets */; };\n\t\tBFA2DF69247DD31500E31E4D /* UIColor+Clip.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA2DF68247DD31500E31E4D /* UIColor+Clip.swift */; };\n\t\tBFA2DF6B247DE47900E31E4D /* Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA2DF6A247DE47900E31E4D /* Preview.swift */; };\n\t\tBFA714BA22C53F1700EE7236 /* ApplicationMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA714B922C53F1700EE7236 /* ApplicationMonitor.swift */; };\n\t\tBFAC49D322B2EDA80011E7C4 /* ClippingTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFAC49D222B2EDA80011E7C4 /* ClippingTableViewCell.xib */; };\n\t\tBFAC49D522B2EDB20011E7C4 /* ClippingTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFAC49D422B2EDB20011E7C4 /* ClippingTableViewCell.swift */; };\n\t\tBFAC49E022B40EC90011E7C4 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BFAC49DF22B40EC90011E7C4 /* AudioToolbox.framework */; };\n\t\tBFAD2C4A257AD4D600FF9532 /* PublishedPipeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFAD2C49257AD4D600FF9532 /* PublishedPipeline.swift */; };\n\t\tBFAD46C92490588800451D6F /* UIInputView+Click.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFAD46C82490588800451D6F /* UIInputView+Click.swift */; };\n\t\tBFAD46CB249058DE00451D6F /* SwitchKeyboardButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFAD46CA249058DE00451D6F /* SwitchKeyboardButton.swift */; };\n\t\tBFC176FD2399BC4F0058AC51 /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF69A53F2395D44600CF838A /* UserNotifications.framework */; };\n\t\tBFC176FE2399BC4F0058AC51 /* UserNotificationsUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF69A5412395D44700CF838A /* UserNotificationsUI.framework */; };\n\t\tBFC177012399BC4F0058AC51 /* NotificationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC177002399BC4F0058AC51 /* NotificationViewController.swift */; };\n\t\tBFC177042399BC4F0058AC51 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFC177022399BC4F0058AC51 /* MainInterface.storyboard */; };\n\t\tBFC177082399BC4F0058AC51 /* ClipboardReader.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = BFC176FC2399BC4F0058AC51 /* ClipboardReader.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };\n\t\tBFC1F39D22AF0D0E003AC21A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC1F39C22AF0D0E003AC21A /* AppDelegate.swift */; };\n\t\tBFC1F39F22AF0D0E003AC21A /* HistoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC1F39E22AF0D0E003AC21A /* HistoryViewController.swift */; };\n\t\tBFC1F3A222AF0D0E003AC21A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFC1F3A022AF0D0E003AC21A /* Main.storyboard */; };\n\t\tBFC1F3A422AF0D0F003AC21A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFC1F3A322AF0D0F003AC21A /* Assets.xcassets */; };\n\t\tBFC1F3A722AF0D0F003AC21A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFC1F3A522AF0D0F003AC21A /* LaunchScreen.storyboard */; };\n\t\tBFC9E65422B1A22700974663 /* ClipKit.h in Headers */ = {isa = PBXBuildFile; fileRef = BFC9E65222B1A22700974663 /* ClipKit.h */; settings = {ATTRIBUTES = (Public, ); }; };\n\t\tBFC9E65722B1A22700974663 /* ClipKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BFC9E65022B1A22700974663 /* ClipKit.framework */; platformFilter = ios; };\n\t\tBFC9E65822B1A22700974663 /* ClipKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BFC9E65022B1A22700974663 /* ClipKit.framework */; platformFilter = ios; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };\n\t\tBFC9E66222B1B03900974663 /* DatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1D5D4622B0869C0062F474 /* DatabaseManager.swift */; };\n\t\tBFC9E66322B1B03C00974663 /* PasteboardItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1D5D4A22B094DB0062F474 /* PasteboardItem.swift */; };\n\t\tBFC9E66422B1B03C00974663 /* PasteboardItemRepresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1D5D4822B088F40062F474 /* PasteboardItemRepresentation.swift */; };\n\t\tBFC9E66522B1B04000974663 /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = BF1D5D4322B0867C0062F474 /* Model.xcdatamodeld */; };\n\t\tBFC9E66622B1B1E600974663 /* Roxas.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF1D5C7522B029DC0062F474 /* Roxas.framework */; };\n\t\tBFC9E67222B2C4C500974663 /* Result+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC9E67122B2C4C400974663 /* Result+Conveniences.swift */; };\n\t\tBFC9E67C22B2CA4400974663 /* CFNotification+PasteboardListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC9E67B22B2CA4400974663 /* CFNotification+PasteboardListener.swift */; };\n\t\tBFDB5B1E22EF7B5000F74113 /* GradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB5B1D22EF7B5000F74113 /* GradientView.swift */; };\n\t\tBFEF44AE2398693300095A92 /* ForwardingNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFEF44AB2398693300095A92 /* ForwardingNavigationController.swift */; };\n\t\tBFF9E4932555F4F40052B1B2 /* LocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF9E4922555F4F40052B1B2 /* LocationManager.swift */; };\n\t\tD534428E2BAB202700E133EE /* ClippingsMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D534428D2BAB202700E133EE /* ClippingsMapView.swift */; };\n\t\tD5D375152BAB330200213D84 /* ClippingSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D375142BAB330200213D84 /* ClippingSheet.swift */; };\n\t\tD5D375192BAB4DF800213D84 /* UNNotification+Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D375182BAB4DF800213D84 /* UNNotification+Keys.swift */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\tBF1D5C7422B029DC0062F474 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BF1D5C6E22B029DB0062F474 /* Roxas.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = BFADAFF819AE7BB70050CF31;\n\t\t\tremoteInfo = Roxas;\n\t\t};\n\t\tBF1D5C7622B029DC0062F474 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BF1D5C6E22B029DB0062F474 /* Roxas.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = BF8624801BB742E700C12EEE;\n\t\t\tremoteInfo = RoxasTV;\n\t\t};\n\t\tBF1D5C7822B029DC0062F474 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BF1D5C6E22B029DB0062F474 /* Roxas.xcodeproj */;\n\t\t\tproxyType = 2;\n\t\t\tremoteGlobalIDString = BFADB00319AE7BB80050CF31;\n\t\t\tremoteInfo = RoxasTests;\n\t\t};\n\t\tBF1D5C7C22B029E80062F474 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BF1D5C6E22B029DB0062F474 /* Roxas.xcodeproj */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BFADAFF719AE7BB70050CF31;\n\t\t\tremoteInfo = Roxas;\n\t\t};\n\t\tBF7E6FE5247C4FCD0058F4D4 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BFC1F39122AF0D0E003AC21A /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BF7E6FDF247C4FCD0058F4D4;\n\t\t\tremoteInfo = ClipBoard;\n\t\t};\n\t\tBFAD46CC24905E9400451D6F /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BFC1F39122AF0D0E003AC21A /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BFC9E64F22B1A22700974663;\n\t\t\tremoteInfo = ClipKit;\n\t\t};\n\t\tBFC177062399BC4F0058AC51 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BFC1F39122AF0D0E003AC21A /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BFC176FB2399BC4F0058AC51;\n\t\t\tremoteInfo = ClipboardReader;\n\t\t};\n\t\tBFC9E65522B1A22700974663 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = BFC1F39122AF0D0E003AC21A /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = BFC9E64F22B1A22700974663;\n\t\t\tremoteInfo = ClipKit;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\tBF1D5C6D22B029190062F474 /* Embed Frameworks */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t\tBF1D5C7B22B029E70062F474 /* Roxas.framework in Embed Frameworks */,\n\t\t\t\tBFC9E65822B1A22700974663 /* ClipKit.framework in Embed Frameworks */,\n\t\t\t);\n\t\t\tname = \"Embed Frameworks\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBF1D5C9C22B042870062F474 /* Embed App Extensions */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 13;\n\t\t\tfiles = (\n\t\t\t\tBFC177082399BC4F0058AC51 /* ClipboardReader.appex in Embed App Extensions */,\n\t\t\t\tBF7E6FE7247C4FCD0058F4D4 /* ClipBoard.appex in Embed App Extensions */,\n\t\t\t);\n\t\t\tname = \"Embed App Extensions\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\tBF0AB807247C5D1F0090B43B /* Blur.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Blur.swift; sourceTree = \"<group>\"; };\n\t\tBF0BDE5022B4414A00E1419D /* UTI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTI.swift; sourceTree = \"<group>\"; };\n\t\tBF0BDE5522B456A600E1419D /* PasteboardItem+ActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"PasteboardItem+ActivityItemSource.swift\"; sourceTree = \"<group>\"; };\n\t\tBF0BDE5722B4757100E1419D /* UserDefaults+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"UserDefaults+App.swift\"; sourceTree = \"<group>\"; };\n\t\tBF0BDE5922B4771300E1419D /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = \"<group>\"; };\n\t\tBF1D5C6E22B029DB0062F474 /* Roxas.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = \"wrapper.pb-project\"; name = Roxas.xcodeproj; path = Dependencies/Roxas/Roxas.xcodeproj; sourceTree = \"<group>\"; };\n\t\tBF1D5C7E22B02BF30062F474 /* PasteboardMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteboardMonitor.swift; sourceTree = \"<group>\"; };\n\t\tBF1D5D4422B0867C0062F474 /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = \"<group>\"; };\n\t\tBF1D5D4622B0869C0062F474 /* DatabaseManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseManager.swift; sourceTree = \"<group>\"; };\n\t\tBF1D5D4822B088F40062F474 /* PasteboardItemRepresentation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteboardItemRepresentation.swift; sourceTree = \"<group>\"; };\n\t\tBF1D5D4A22B094DB0062F474 /* PasteboardItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteboardItem.swift; sourceTree = \"<group>\"; };\n\t\tBF235BC0247D8B5300CCFCB0 /* UIPasteboard+PasteboardItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"UIPasteboard+PasteboardItem.swift\"; sourceTree = \"<group>\"; };\n\t\tBF50E7D822C2BF010070E17B /* Bundle+AppGroups.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"Bundle+AppGroups.swift\"; sourceTree = \"<group>\"; };\n\t\tBF69A53F2395D44600CF838A /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; };\n\t\tBF69A5412395D44700CF838A /* UserNotificationsUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotificationsUI.framework; path = System/Library/Frameworks/UserNotificationsUI.framework; sourceTree = SDKROOT; };\n\t\tBF746541247C900100F66F3B /* ClipBoard.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ClipBoard.entitlements; sourceTree = \"<group>\"; };\n\t\tBF770E6122BC767A002A40FE /* UIDevice+Vibration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = \"UIDevice+Vibration.swift\"; sourceTree = \"<group>\"; };\n\t\tBF7B9EE022B81C980042C873 /* Int+Bytes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"Int+Bytes.swift\"; sourceTree = \"<group>\"; };\n\t\tBF7E6FE0247C4FCD0058F4D4 /* ClipBoard.appex */ = {isa = PBXFileReference; explicitFileType = \"wrapper.app-extension\"; includeInIndex = 0; path = ClipBoard.appex; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBF7E6FE2247C4FCD0058F4D4 /* KeyboardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardViewController.swift; sourceTree = \"<group>\"; };\n\t\tBF7E6FE4247C4FCD0058F4D4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tBF7E6FF0247C50540058F4D4 /* Keyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keyboard.swift; sourceTree = \"<group>\"; };\n\t\tBF7E6FF2247C518D0058F4D4 /* ClippingCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClippingCell.swift; sourceTree = \"<group>\"; };\n\t\tBF8F76F2254CC0E0005AF18B /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = \"<group>\"; };\n\t\tBFA2DF64247DD2B200E31E4D /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = \"<group>\"; };\n\t\tBFA2DF68247DD31500E31E4D /* UIColor+Clip.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = \"UIColor+Clip.swift\"; sourceTree = \"<group>\"; };\n\t\tBFA2DF6A247DE47900E31E4D /* Preview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preview.swift; sourceTree = \"<group>\"; };\n\t\tBFA714B922C53F1700EE7236 /* ApplicationMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationMonitor.swift; sourceTree = \"<group>\"; };\n\t\tBFAC49D222B2EDA80011E7C4 /* ClippingTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ClippingTableViewCell.xib; sourceTree = \"<group>\"; };\n\t\tBFAC49D422B2EDB20011E7C4 /* ClippingTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClippingTableViewCell.swift; sourceTree = \"<group>\"; };\n\t\tBFAC49DF22B40EC90011E7C4 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };\n\t\tBFAD2C49257AD4D600FF9532 /* PublishedPipeline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublishedPipeline.swift; sourceTree = \"<group>\"; };\n\t\tBFAD46C82490588800451D6F /* UIInputView+Click.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"UIInputView+Click.swift\"; sourceTree = \"<group>\"; };\n\t\tBFAD46CA249058DE00451D6F /* SwitchKeyboardButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchKeyboardButton.swift; sourceTree = \"<group>\"; };\n\t\tBFC176FC2399BC4F0058AC51 /* ClipboardReader.appex */ = {isa = PBXFileReference; explicitFileType = \"wrapper.app-extension\"; includeInIndex = 0; path = ClipboardReader.appex; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBFC177002399BC4F0058AC51 /* NotificationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationViewController.swift; sourceTree = \"<group>\"; };\n\t\tBFC177032399BC4F0058AC51 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = \"<group>\"; };\n\t\tBFC177052399BC4F0058AC51 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tBFC1770C2399BCF90058AC51 /* ClipboardReader.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ClipboardReader.entitlements; sourceTree = \"<group>\"; };\n\t\tBFC1F39922AF0D0E003AC21A /* Clip.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Clip.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBFC1F39C22AF0D0E003AC21A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\tBFC1F39E22AF0D0E003AC21A /* HistoryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryViewController.swift; sourceTree = \"<group>\"; };\n\t\tBFC1F3A122AF0D0E003AC21A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\tBFC1F3A322AF0D0F003AC21A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\tBFC1F3A622AF0D0F003AC21A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\tBFC1F3A822AF0D0F003AC21A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tBFC9E62C22B18D5900974663 /* Clip.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Clip.entitlements; sourceTree = \"<group>\"; };\n\t\tBFC9E65022B1A22700974663 /* ClipKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ClipKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tBFC9E65222B1A22700974663 /* ClipKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ClipKit.h; sourceTree = \"<group>\"; };\n\t\tBFC9E65322B1A22700974663 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tBFC9E67122B2C4C400974663 /* Result+Conveniences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = \"Result+Conveniences.swift\"; sourceTree = \"<group>\"; };\n\t\tBFC9E67B22B2CA4400974663 /* CFNotification+PasteboardListener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"CFNotification+PasteboardListener.swift\"; sourceTree = \"<group>\"; };\n\t\tBFDB5B1D22EF7B5000F74113 /* GradientView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GradientView.swift; sourceTree = \"<group>\"; };\n\t\tBFEF44AB2398693300095A92 /* ForwardingNavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForwardingNavigationController.swift; sourceTree = \"<group>\"; };\n\t\tBFF9E4922555F4F40052B1B2 /* LocationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationManager.swift; sourceTree = \"<group>\"; };\n\t\tD534428D2BAB202700E133EE /* ClippingsMapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClippingsMapView.swift; sourceTree = \"<group>\"; };\n\t\tD5D375142BAB330200213D84 /* ClippingSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClippingSheet.swift; sourceTree = \"<group>\"; };\n\t\tD5D375182BAB4DF800213D84 /* UNNotification+Keys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = \"UNNotification+Keys.swift\"; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\tBF7E6FDD247C4FCD0058F4D4 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBFC176F92399BC4F0058AC51 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBFC176FE2399BC4F0058AC51 /* UserNotificationsUI.framework in Frameworks */,\n\t\t\t\tBFC176FD2399BC4F0058AC51 /* UserNotifications.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBFC1F39622AF0D0E003AC21A /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBF1D5C7A22B029E70062F474 /* Roxas.framework in Frameworks */,\n\t\t\t\tBFC9E65722B1A22700974663 /* ClipKit.framework in Frameworks */,\n\t\t\t\tBFAC49E022B40EC90011E7C4 /* AudioToolbox.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBFC9E64D22B1A22700974663 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBFC9E66622B1B1E600974663 /* Roxas.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\tBF0BDE5422B4568A00E1419D /* Extensions */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBF0BDE5522B456A600E1419D /* PasteboardItem+ActivityItemSource.swift */,\n\t\t\t\tBF770E6122BC767A002A40FE /* UIDevice+Vibration.swift */,\n\t\t\t);\n\t\t\tpath = Extensions;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBF0BDE6622B4871400E1419D /* Settings */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBF0BDE5922B4771300E1419D /* SettingsViewController.swift */,\n\t\t\t);\n\t\t\tpath = Settings;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBF1D5C6722B029090062F474 /* Dependencies */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBF1D5C6E22B029DB0062F474 /* Roxas.xcodeproj */,\n\t\t\t);\n\t\t\tname = Dependencies;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBF1D5C6F22B029DB0062F474 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBF1D5C7522B029DC0062F474 /* Roxas.framework */,\n\t\t\t\tBF1D5C7722B029DC0062F474 /* Roxas.framework */,\n\t\t\t\tBF1D5C7922B029DC0062F474 /* RoxasTests.xctest */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBF1D5C8122B038030062F474 /* Resources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBFC1F3A322AF0D0F003AC21A /* Assets.xcassets */,\n\t\t\t);\n\t\t\tpath = Resources;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBF1D5C8522B038FE0062F474 /* Supporting Files */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBFC1F3A522AF0D0F003AC21A /* LaunchScreen.storyboard */,\n\t\t\t\tBFC1F3A822AF0D0F003AC21A /* Info.plist */,\n\t\t\t);\n\t\t\tname = \"Supporting Files\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBF1D5CDF22B063E00062F474 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBFAC49DF22B40EC90011E7C4 /* AudioToolbox.framework */,\n\t\t\t\tBF69A53F2395D44600CF838A /* UserNotifications.framework */,\n\t\t\t\tBF69A5412395D44700CF838A /* UserNotificationsUI.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBF1D5D4122B083210062F474 /* Pasteboard */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBF1D5C7E22B02BF30062F474 /* PasteboardMonitor.swift */,\n\t\t\t\tBFF9E4922555F4F40052B1B2 /* LocationManager.swift */,\n\t\t\t);\n\t\t\tpath = Pasteboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBF7E6FE1247C4FCD0058F4D4 /* ClipBoard */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBF746541247C900100F66F3B /* ClipBoard.entitlements */,\n\t\t\t\tBF7E6FE2247C4FCD0058F4D4 /* KeyboardViewController.swift */,\n\t\t\t\tBF7E6FE4247C4FCD0058F4D4 /* Info.plist */,\n\t\t\t);\n\t\t\tpath = ClipBoard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBF7E6FEF247C50480058F4D4 /* SwiftUI */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBFA2DF6A247DE47900E31E4D /* Preview.swift */,\n\t\t\t\tBF7E6FF0247C50540058F4D4 /* Keyboard.swift */,\n\t\t\t\tBF7E6FF2247C518D0058F4D4 /* ClippingCell.swift */,\n\t\t\t\tBFAD46CA249058DE00451D6F /* SwitchKeyboardButton.swift */,\n\t\t\t\tBF0AB807247C5D1F0090B43B /* Blur.swift */,\n\t\t\t);\n\t\t\tpath = SwiftUI;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBFA2DF63247DD2A500E31E4D /* Resources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBFA2DF64247DD2B200E31E4D /* Colors.xcassets */,\n\t\t\t);\n\t\t\tpath = Resources;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBFAC49D722B2EF170011E7C4 /* History */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBFC1F39E22AF0D0E003AC21A /* HistoryViewController.swift */,\n\t\t\t\tBFAC49D422B2EDB20011E7C4 /* ClippingTableViewCell.swift */,\n\t\t\t\tBFAC49D222B2EDA80011E7C4 /* ClippingTableViewCell.xib */,\n\t\t\t);\n\t\t\tpath = History;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBFAD2C48257AD4CB00FF9532 /* Types */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBFAD2C49257AD4D600FF9532 /* PublishedPipeline.swift */,\n\t\t\t);\n\t\t\tpath = Types;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBFC176FF2399BC4F0058AC51 /* ClipboardReader */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBFC1770C2399BCF90058AC51 /* ClipboardReader.entitlements */,\n\t\t\t\tBFC177002399BC4F0058AC51 /* NotificationViewController.swift */,\n\t\t\t\tBFC177022399BC4F0058AC51 /* MainInterface.storyboard */,\n\t\t\t\tBFC177052399BC4F0058AC51 /* Info.plist */,\n\t\t\t);\n\t\t\tpath = ClipboardReader;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBFC1F39022AF0D0E003AC21A = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBFC1F39B22AF0D0E003AC21A /* Clip */,\n\t\t\t\tBFC9E65122B1A22700974663 /* ClipKit */,\n\t\t\t\tBFC176FF2399BC4F0058AC51 /* ClipboardReader */,\n\t\t\t\tBF7E6FE1247C4FCD0058F4D4 /* ClipBoard */,\n\t\t\t\tBF1D5C6722B029090062F474 /* Dependencies */,\n\t\t\t\tBFC1F39A22AF0D0E003AC21A /* Products */,\n\t\t\t\tBF1D5CDF22B063E00062F474 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBFC1F39A22AF0D0E003AC21A /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBFC1F39922AF0D0E003AC21A /* Clip.app */,\n\t\t\t\tBFC9E65022B1A22700974663 /* ClipKit.framework */,\n\t\t\t\tBFC176FC2399BC4F0058AC51 /* ClipboardReader.appex */,\n\t\t\t\tBF7E6FE0247C4FCD0058F4D4 /* ClipBoard.appex */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBFC1F39B22AF0D0E003AC21A /* Clip */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBFC9E62C22B18D5900974663 /* Clip.entitlements */,\n\t\t\t\tBFC1F39C22AF0D0E003AC21A /* AppDelegate.swift */,\n\t\t\t\tBF8F76F2254CC0E0005AF18B /* SceneDelegate.swift */,\n\t\t\t\tBFA714B922C53F1700EE7236 /* ApplicationMonitor.swift */,\n\t\t\t\tBFC1F3A022AF0D0E003AC21A /* Main.storyboard */,\n\t\t\t\tBFAC49D722B2EF170011E7C4 /* History */,\n\t\t\t\tD53442912BAB202A00E133EE /* Map View */,\n\t\t\t\tBF0BDE6622B4871400E1419D /* Settings */,\n\t\t\t\tBF1D5D4122B083210062F474 /* Pasteboard */,\n\t\t\t\tBFDB5B2322EF8F9600F74113 /* Components */,\n\t\t\t\tBFAD2C48257AD4CB00FF9532 /* Types */,\n\t\t\t\tBF0BDE5422B4568A00E1419D /* Extensions */,\n\t\t\t\tBF1D5C8122B038030062F474 /* Resources */,\n\t\t\t\tBF1D5C8522B038FE0062F474 /* Supporting Files */,\n\t\t\t);\n\t\t\tpath = Clip;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBFC9E65122B1A22700974663 /* ClipKit */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBFC9E65222B1A22700974663 /* ClipKit.h */,\n\t\t\t\tBF0BDE5022B4414A00E1419D /* UTI.swift */,\n\t\t\t\tBF7E6FEF247C50480058F4D4 /* SwiftUI */,\n\t\t\t\tBFC9E67022B1DE0A00974663 /* Database */,\n\t\t\t\tBFC9E67322B2C6C500974663 /* Extensions */,\n\t\t\t\tBFA2DF63247DD2A500E31E4D /* Resources */,\n\t\t\t\tBFC9E65322B1A22700974663 /* Info.plist */,\n\t\t\t);\n\t\t\tpath = ClipKit;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBFC9E67022B1DE0A00974663 /* Database */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBF1D5D4622B0869C0062F474 /* DatabaseManager.swift */,\n\t\t\t\tBFC9E67A22B2C85C00974663 /* Model */,\n\t\t\t);\n\t\t\tpath = Database;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBFC9E67322B2C6C500974663 /* Extensions */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBFC9E67B22B2CA4400974663 /* CFNotification+PasteboardListener.swift */,\n\t\t\t\tBFC9E67122B2C4C400974663 /* Result+Conveniences.swift */,\n\t\t\t\tBF0BDE5722B4757100E1419D /* UserDefaults+App.swift */,\n\t\t\t\tBF7B9EE022B81C980042C873 /* Int+Bytes.swift */,\n\t\t\t\tBF50E7D822C2BF010070E17B /* Bundle+AppGroups.swift */,\n\t\t\t\tBF235BC0247D8B5300CCFCB0 /* UIPasteboard+PasteboardItem.swift */,\n\t\t\t\tBFA2DF68247DD31500E31E4D /* UIColor+Clip.swift */,\n\t\t\t\tBFAD46C82490588800451D6F /* UIInputView+Click.swift */,\n\t\t\t\tD5D375182BAB4DF800213D84 /* UNNotification+Keys.swift */,\n\t\t\t);\n\t\t\tpath = Extensions;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBFC9E67A22B2C85C00974663 /* Model */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBF1D5D4322B0867C0062F474 /* Model.xcdatamodeld */,\n\t\t\t\tBF1D5D4A22B094DB0062F474 /* PasteboardItem.swift */,\n\t\t\t\tBF1D5D4822B088F40062F474 /* PasteboardItemRepresentation.swift */,\n\t\t\t);\n\t\t\tpath = Model;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBFDB5B2322EF8F9600F74113 /* Components */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBFDB5B1D22EF7B5000F74113 /* GradientView.swift */,\n\t\t\t\tBFEF44AB2398693300095A92 /* ForwardingNavigationController.swift */,\n\t\t\t);\n\t\t\tpath = Components;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD53442912BAB202A00E133EE /* Map View */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD534428D2BAB202700E133EE /* ClippingsMapView.swift */,\n\t\t\t\tD5D375142BAB330200213D84 /* ClippingSheet.swift */,\n\t\t\t);\n\t\t\tpath = \"Map View\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXHeadersBuildPhase section */\n\t\tBFC9E64B22B1A22700974663 /* Headers */ = {\n\t\t\tisa = PBXHeadersBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBFC9E65422B1A22700974663 /* ClipKit.h in Headers */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXHeadersBuildPhase section */\n\n/* Begin PBXNativeTarget section */\n\t\tBF7E6FDF247C4FCD0058F4D4 /* ClipBoard */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BF7E6FEC247C4FCD0058F4D4 /* Build configuration list for PBXNativeTarget \"ClipBoard\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBF7E6FDC247C4FCD0058F4D4 /* Sources */,\n\t\t\t\tBF7E6FDD247C4FCD0058F4D4 /* Frameworks */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tBFAD46CD24905E9400451D6F /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = ClipBoard;\n\t\t\tproductName = ClipBoard;\n\t\t\tproductReference = BF7E6FE0247C4FCD0058F4D4 /* ClipBoard.appex */;\n\t\t\tproductType = \"com.apple.product-type.app-extension\";\n\t\t};\n\t\tBFC176FB2399BC4F0058AC51 /* ClipboardReader */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BFC177092399BC4F0058AC51 /* Build configuration list for PBXNativeTarget \"ClipboardReader\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBFC176F82399BC4F0058AC51 /* Sources */,\n\t\t\t\tBFC176F92399BC4F0058AC51 /* Frameworks */,\n\t\t\t\tBFC176FA2399BC4F0058AC51 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = ClipboardReader;\n\t\t\tproductName = ClipboardReader;\n\t\t\tproductReference = BFC176FC2399BC4F0058AC51 /* ClipboardReader.appex */;\n\t\t\tproductType = \"com.apple.product-type.app-extension\";\n\t\t};\n\t\tBFC1F39822AF0D0E003AC21A /* Clip */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BFC1F3AB22AF0D0F003AC21A /* Build configuration list for PBXNativeTarget \"Clip\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBFC1F39522AF0D0E003AC21A /* Sources */,\n\t\t\t\tBFC1F39622AF0D0E003AC21A /* Frameworks */,\n\t\t\t\tBFC1F39722AF0D0E003AC21A /* Resources */,\n\t\t\t\tBF1D5C6D22B029190062F474 /* Embed Frameworks */,\n\t\t\t\tBF1D5C9C22B042870062F474 /* Embed App Extensions */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\tBF1D5C7D22B029E80062F474 /* PBXTargetDependency */,\n\t\t\t\tBFC9E65622B1A22700974663 /* PBXTargetDependency */,\n\t\t\t\tBFC177072399BC4F0058AC51 /* PBXTargetDependency */,\n\t\t\t\tBF7E6FE6247C4FCD0058F4D4 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = Clip;\n\t\t\tproductName = ClipboardManager;\n\t\t\tproductReference = BFC1F39922AF0D0E003AC21A /* Clip.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\tBFC9E64F22B1A22700974663 /* ClipKit */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = BFC9E65922B1A22700974663 /* Build configuration list for PBXNativeTarget \"ClipKit\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tBFC9E64B22B1A22700974663 /* Headers */,\n\t\t\t\tBFC9E64C22B1A22700974663 /* Sources */,\n\t\t\t\tBFC9E64D22B1A22700974663 /* Frameworks */,\n\t\t\t\tBFC9E64E22B1A22700974663 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = ClipKit;\n\t\t\tproductName = ClipKit;\n\t\t\tproductReference = BFC9E65022B1A22700974663 /* ClipKit.framework */;\n\t\t\tproductType = \"com.apple.product-type.framework\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\tBFC1F39122AF0D0E003AC21A /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 1150;\n\t\t\t\tLastUpgradeCheck = 1020;\n\t\t\t\tORGANIZATIONNAME = \"Riley Testut\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\tBF7E6FDF247C4FCD0058F4D4 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 11.5;\n\t\t\t\t\t};\n\t\t\t\t\tBFC176FB2399BC4F0058AC51 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 11.1;\n\t\t\t\t\t};\n\t\t\t\t\tBFC1F39822AF0D0E003AC21A = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 10.2.1;\n\t\t\t\t\t\tLastSwiftMigration = 1120;\n\t\t\t\t\t\tSystemCapabilities = {\n\t\t\t\t\t\t\tcom.apple.ApplicationGroups.iOS = {\n\t\t\t\t\t\t\t\tenabled = 1;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tcom.apple.BackgroundModes = {\n\t\t\t\t\t\t\t\tenabled = 1;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tcom.apple.InterAppAudio = {\n\t\t\t\t\t\t\t\tenabled = 1;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\t\t\t\t\tBFC9E64F22B1A22700974663 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 10.2.1;\n\t\t\t\t\t\tLastSwiftMigration = 1020;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = BFC1F39422AF0D0E003AC21A /* Build configuration list for PBXProject \"Clip\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = BFC1F39022AF0D0E003AC21A;\n\t\t\tproductRefGroup = BFC1F39A22AF0D0E003AC21A /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectReferences = (\n\t\t\t\t{\n\t\t\t\t\tProductGroup = BF1D5C6F22B029DB0062F474 /* Products */;\n\t\t\t\t\tProjectRef = BF1D5C6E22B029DB0062F474 /* Roxas.xcodeproj */;\n\t\t\t\t},\n\t\t\t);\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\tBFC1F39822AF0D0E003AC21A /* Clip */,\n\t\t\t\tBFC9E64F22B1A22700974663 /* ClipKit */,\n\t\t\t\tBFC176FB2399BC4F0058AC51 /* ClipboardReader */,\n\t\t\t\tBF7E6FDF247C4FCD0058F4D4 /* ClipBoard */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXReferenceProxy section */\n\t\tBF1D5C7522B029DC0062F474 /* Roxas.framework */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = wrapper.framework;\n\t\t\tpath = Roxas.framework;\n\t\t\tremoteRef = BF1D5C7422B029DC0062F474 /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\tBF1D5C7722B029DC0062F474 /* Roxas.framework */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = wrapper.framework;\n\t\t\tpath = Roxas.framework;\n\t\t\tremoteRef = BF1D5C7622B029DC0062F474 /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n\t\tBF1D5C7922B029DC0062F474 /* RoxasTests.xctest */ = {\n\t\t\tisa = PBXReferenceProxy;\n\t\t\tfileType = wrapper.cfbundle;\n\t\t\tpath = RoxasTests.xctest;\n\t\t\tremoteRef = BF1D5C7822B029DC0062F474 /* PBXContainerItemProxy */;\n\t\t\tsourceTree = BUILT_PRODUCTS_DIR;\n\t\t};\n/* End PBXReferenceProxy section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\tBFC176FA2399BC4F0058AC51 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBFC177042399BC4F0058AC51 /* MainInterface.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBFC1F39722AF0D0E003AC21A /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBFC1F3A722AF0D0F003AC21A /* LaunchScreen.storyboard in Resources */,\n\t\t\t\tBFAC49D322B2EDA80011E7C4 /* ClippingTableViewCell.xib in Resources */,\n\t\t\t\tBFC1F3A422AF0D0F003AC21A /* Assets.xcassets in Resources */,\n\t\t\t\tBFC1F3A222AF0D0E003AC21A /* Main.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBFC9E64E22B1A22700974663 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBFA2DF65247DD2B200E31E4D /* Colors.xcassets in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\tBF7E6FDC247C4FCD0058F4D4 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBF7E6FE3247C4FCD0058F4D4 /* KeyboardViewController.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBFC176F82399BC4F0058AC51 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBFC177012399BC4F0058AC51 /* NotificationViewController.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBFC1F39522AF0D0E003AC21A /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBF1D5C7F22B02BF30062F474 /* PasteboardMonitor.swift in Sources */,\n\t\t\t\tBFA714BA22C53F1700EE7236 /* ApplicationMonitor.swift in Sources */,\n\t\t\t\tBFDB5B1E22EF7B5000F74113 /* GradientView.swift in Sources */,\n\t\t\t\tBFC1F39F22AF0D0E003AC21A /* HistoryViewController.swift in Sources */,\n\t\t\t\tBFF9E4932555F4F40052B1B2 /* LocationManager.swift in Sources */,\n\t\t\t\tD5D375152BAB330200213D84 /* ClippingSheet.swift in Sources */,\n\t\t\t\tBF8F76F3254CC0E0005AF18B /* SceneDelegate.swift in Sources */,\n\t\t\t\tD534428E2BAB202700E133EE /* ClippingsMapView.swift in Sources */,\n\t\t\t\tBF770E6522BC7688002A40FE /* UIDevice+Vibration.swift in Sources */,\n\t\t\t\tBFC1F39D22AF0D0E003AC21A /* AppDelegate.swift in Sources */,\n\t\t\t\tBFEF44AE2398693300095A92 /* ForwardingNavigationController.swift in Sources */,\n\t\t\t\tBF0BDE5622B456A600E1419D /* PasteboardItem+ActivityItemSource.swift in Sources */,\n\t\t\t\tBFAC49D522B2EDB20011E7C4 /* ClippingTableViewCell.swift in Sources */,\n\t\t\t\tBFAD2C4A257AD4D600FF9532 /* PublishedPipeline.swift in Sources */,\n\t\t\t\tBF0BDE5A22B4771300E1419D /* SettingsViewController.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tBFC9E64C22B1A22700974663 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tBFA2DF69247DD31500E31E4D /* UIColor+Clip.swift in Sources */,\n\t\t\t\tBF0BDE5E22B47D4900E1419D /* UserDefaults+App.swift in Sources */,\n\t\t\t\tBFC9E67C22B2CA4400974663 /* CFNotification+PasteboardListener.swift in Sources */,\n\t\t\t\tBFC9E66222B1B03900974663 /* DatabaseManager.swift in Sources */,\n\t\t\t\tBF0BDE5122B4414A00E1419D /* UTI.swift in Sources */,\n\t\t\t\tBF235BC1247D8B5300CCFCB0 /* UIPasteboard+PasteboardItem.swift in Sources */,\n\t\t\t\tBFAD46C92490588800451D6F /* UIInputView+Click.swift in Sources */,\n\t\t\t\tBFA2DF61247DD23800E31E4D /* ClippingCell.swift in Sources */,\n\t\t\t\tBFC9E66422B1B03C00974663 /* PasteboardItemRepresentation.swift in Sources */,\n\t\t\t\tBFC9E66522B1B04000974663 /* Model.xcdatamodeld in Sources */,\n\t\t\t\tBFAD46CB249058DE00451D6F /* SwitchKeyboardButton.swift in Sources */,\n\t\t\t\tBFC9E67222B2C4C500974663 /* Result+Conveniences.swift in Sources */,\n\t\t\t\tBFA2DF6B247DE47900E31E4D /* Preview.swift in Sources */,\n\t\t\t\tBFA2DF62247DD23C00E31E4D /* Blur.swift in Sources */,\n\t\t\t\tBF7B9EE122B81C980042C873 /* Int+Bytes.swift in Sources */,\n\t\t\t\tBFC9E66322B1B03C00974663 /* PasteboardItem.swift in Sources */,\n\t\t\t\tD5D375192BAB4DF800213D84 /* UNNotification+Keys.swift in Sources */,\n\t\t\t\tBFA2DF60247DD21F00E31E4D /* Keyboard.swift in Sources */,\n\t\t\t\tBF50E7D922C2BF010070E17B /* Bundle+AppGroups.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\tBF1D5C7D22B029E80062F474 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\tname = Roxas;\n\t\t\tplatformFilter = ios;\n\t\t\ttargetProxy = BF1D5C7C22B029E80062F474 /* PBXContainerItemProxy */;\n\t\t};\n\t\tBF7E6FE6247C4FCD0058F4D4 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BF7E6FDF247C4FCD0058F4D4 /* ClipBoard */;\n\t\t\ttargetProxy = BF7E6FE5247C4FCD0058F4D4 /* PBXContainerItemProxy */;\n\t\t};\n\t\tBFAD46CD24905E9400451D6F /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BFC9E64F22B1A22700974663 /* ClipKit */;\n\t\t\ttargetProxy = BFAD46CC24905E9400451D6F /* PBXContainerItemProxy */;\n\t\t};\n\t\tBFC177072399BC4F0058AC51 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = BFC176FB2399BC4F0058AC51 /* ClipboardReader */;\n\t\t\ttargetProxy = BFC177062399BC4F0058AC51 /* PBXContainerItemProxy */;\n\t\t};\n\t\tBFC9E65622B1A22700974663 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\tplatformFilter = ios;\n\t\t\ttarget = BFC9E64F22B1A22700974663 /* ClipKit */;\n\t\t\ttargetProxy = BFC9E65522B1A22700974663 /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin PBXVariantGroup section */\n\t\tBFC177022399BC4F0058AC51 /* MainInterface.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tBFC177032399BC4F0058AC51 /* Base */,\n\t\t\t);\n\t\t\tname = MainInterface.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBFC1F3A022AF0D0E003AC21A /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tBFC1F3A122AF0D0E003AC21A /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tBFC1F3A522AF0D0F003AC21A /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\tBFC1F3A622AF0D0F003AC21A /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\tBF7E6FE8247C4FCD0058F4D4 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = ClipBoard/ClipBoard.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = 6XVY5G3U44;\n\t\t\t\tINFOPLIST_FILE = ClipBoard/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 16.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@executable_path/../../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.Clip.ClipBoard;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tBF7E6FE9247C4FCD0058F4D4 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = ClipBoard/ClipBoard.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = 6XVY5G3U44;\n\t\t\t\tINFOPLIST_FILE = ClipBoard/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 16.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@executable_path/../../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.Clip.ClipBoard;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBFC1770A2399BC4F0058AC51 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = ClipboardReader/ClipboardReader.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = 6XVY5G3U44;\n\t\t\t\tINFOPLIST_FILE = ClipboardReader/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 16.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@executable_path/../../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.Clip.ClipboardReader;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = 1;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tBFC1770B2399BC4F0058AC51 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = ClipboardReader/ClipboardReader.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = 6XVY5G3U44;\n\t\t\t\tINFOPLIST_FILE = ClipboardReader/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 16.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@executable_path/../../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.Clip.ClipboardReader;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = 1;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBFC1F3A922AF0D0F003AC21A /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tCURRENT_PROJECT_VERSION = 14;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tMARKETING_VERSION = 1.2;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tBFC1F3AA22AF0D0F003AC21A /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tCURRENT_PROJECT_VERSION = 14;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 13.0;\n\t\t\t\tMARKETING_VERSION = 1.2;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBFC1F3AC22AF0D0F003AC21A /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Clip/Clip.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = 6XVY5G3U44;\n\t\t\t\tINFOPLIST_FILE = Clip/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 17.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.Clip;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSUPPORTED_PLATFORMS = \"iphoneos iphonesimulator\";\n\t\t\t\tSUPPORTS_MACCATALYST = NO;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tBFC1F3AD22AF0D0F003AC21A /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Clip/Clip.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEVELOPMENT_TEAM = 6XVY5G3U44;\n\t\t\t\tINFOPLIST_FILE = Clip/Info.plist;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 17.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.Clip;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSUPPORTED_PLATFORMS = \"iphoneos iphonesimulator\";\n\t\t\t\tSUPPORTS_MACCATALYST = NO;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tBFC9E65A22B1A22700974663 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tAPPLICATION_EXTENSION_API_ONLY = YES;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_TEAM = 6XVY5G3U44;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tINFOPLIST_FILE = ClipKit/Info.plist;\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 16.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.ClipKit;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\tBFC9E65B22B1A22700974663 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tAPPLICATION_EXTENSION_API_ONLY = YES;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tDEFINES_MODULE = YES;\n\t\t\t\tDEVELOPMENT_TEAM = 6XVY5G3U44;\n\t\t\t\tDYLIB_COMPATIBILITY_VERSION = 1;\n\t\t\t\tDYLIB_CURRENT_VERSION = 1;\n\t\t\t\tDYLIB_INSTALL_NAME_BASE = \"@rpath\";\n\t\t\t\tINFOPLIST_FILE = ClipKit/Info.plist;\n\t\t\t\tINSTALL_PATH = \"$(LOCAL_LIBRARY_DIR)/Frameworks\";\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 16.0;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t\t\"@loader_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.ClipKit;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME:c99extidentifier)\";\n\t\t\t\tSKIP_INSTALL = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t\tVERSION_INFO_PREFIX = \"\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\tBF7E6FEC247C4FCD0058F4D4 /* Build configuration list for PBXNativeTarget \"ClipBoard\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBF7E6FE8247C4FCD0058F4D4 /* Debug */,\n\t\t\t\tBF7E6FE9247C4FCD0058F4D4 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBFC177092399BC4F0058AC51 /* Build configuration list for PBXNativeTarget \"ClipboardReader\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBFC1770A2399BC4F0058AC51 /* Debug */,\n\t\t\t\tBFC1770B2399BC4F0058AC51 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBFC1F39422AF0D0E003AC21A /* Build configuration list for PBXProject \"Clip\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBFC1F3A922AF0D0F003AC21A /* Debug */,\n\t\t\t\tBFC1F3AA22AF0D0F003AC21A /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBFC1F3AB22AF0D0F003AC21A /* Build configuration list for PBXNativeTarget \"Clip\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBFC1F3AC22AF0D0F003AC21A /* Debug */,\n\t\t\t\tBFC1F3AD22AF0D0F003AC21A /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\tBFC9E65922B1A22700974663 /* Build configuration list for PBXNativeTarget \"ClipKit\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\tBFC9E65A22B1A22700974663 /* Debug */,\n\t\t\t\tBFC9E65B22B1A22700974663 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\n/* Begin XCVersionGroup section */\n\t\tBF1D5D4322B0867C0062F474 /* Model.xcdatamodeld */ = {\n\t\t\tisa = XCVersionGroup;\n\t\t\tchildren = (\n\t\t\t\tBF1D5D4422B0867C0062F474 /* Model.xcdatamodel */,\n\t\t\t);\n\t\t\tcurrentVersion = BF1D5D4422B0867C0062F474 /* Model.xcdatamodel */;\n\t\t\tpath = Model.xcdatamodeld;\n\t\t\tsourceTree = \"<group>\";\n\t\t\tversionGroupType = wrapper.xcdatamodel;\n\t\t};\n/* End XCVersionGroup section */\n\t};\n\trootObject = BFC1F39122AF0D0E003AC21A /* Project object */;\n}\n"
  },
  {
    "path": "Clip.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:/Users/Riley/Library/Mobile Documents/com~apple~CloudDocs/Documents/Developer/Projects/Apps/Production/ClipboardManager/Clip.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "Clip.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "Clip.xcodeproj/xcshareddata/xcschemes/Clip.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1020\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"NO\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"BFC1F39822AF0D0E003AC21A\"\n               BuildableName = \"Clip.app\"\n               BlueprintName = \"Clip\"\n               ReferencedContainer = \"container:Clip.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"BFC1F39822AF0D0E003AC21A\"\n            BuildableName = \"Clip.app\"\n            BlueprintName = \"Clip\"\n            ReferencedContainer = \"container:Clip.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      migratedStopOnEveryIssue = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"BFC1F39822AF0D0E003AC21A\"\n            BuildableName = \"Clip.app\"\n            BlueprintName = \"Clip\"\n            ReferencedContainer = \"container:Clip.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n      <EnvironmentVariables>\n         <EnvironmentVariable\n            key = \"CGBITMAP_CONTEXT_LOG_ERRORS\"\n            value = \"\"\n            isEnabled = \"YES\">\n         </EnvironmentVariable>\n      </EnvironmentVariables>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"BFC1F39822AF0D0E003AC21A\"\n            BuildableName = \"Clip.app\"\n            BlueprintName = \"Clip\"\n            ReferencedContainer = \"container:Clip.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "Clip.xcodeproj/xcshareddata/xcschemes/ClipKit.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1110\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"BFC9E64F22B1A22700974663\"\n               BuildableName = \"ClipKit.framework\"\n               BlueprintName = \"ClipKit\"\n               ReferencedContainer = \"container:Clip.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"BFC9E64F22B1A22700974663\"\n            BuildableName = \"ClipKit.framework\"\n            BlueprintName = \"ClipKit\"\n            ReferencedContainer = \"container:Clip.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "Clip.xcodeproj/xcshareddata/xcschemes/ClipboardReader.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1110\"\n   wasCreatedForAppExtension = \"YES\"\n   version = \"2.0\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"BF2A236A2399BA8E0059E3E8\"\n               BuildableName = \"ClipboardReader.appex\"\n               BlueprintName = \"ClipboardReader\"\n               ReferencedContainer = \"container:Clip.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"BFC1F39822AF0D0E003AC21A\"\n               BuildableName = \"Clip.app\"\n               BlueprintName = \"Clip\"\n               ReferencedContainer = \"container:Clip.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"\"\n      selectedLauncherIdentifier = \"Xcode.IDEFoundation.Launcher.PosixSpawn\"\n      launchStyle = \"0\"\n      askForAppToLaunch = \"Yes\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\"\n      launchAutomaticallySubstyle = \"2\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"BFC1F39822AF0D0E003AC21A\"\n            BuildableName = \"Clip.app\"\n            BlueprintName = \"Clip\"\n            ReferencedContainer = \"container:Clip.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      launchAutomaticallySubstyle = \"2\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"BFC1F39822AF0D0E003AC21A\"\n            BuildableName = \"Clip.app\"\n            BlueprintName = \"Clip\"\n            ReferencedContainer = \"container:Clip.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "ClipBoard/ClipBoard.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.application-groups</key>\n\t<array>\n\t\t<string>group.com.rileytestut.Clip</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "ClipBoard/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>ALTAppGroups</key>\n\t<array>\n\t\t<string>group.com.rileytestut.Clip</string>\n\t</array>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>ClipBoard</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(MARKETING_VERSION)</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>NSExtension</key>\n\t<dict>\n\t\t<key>NSExtensionAttributes</key>\n\t\t<dict>\n\t\t\t<key>IsASCIICapable</key>\n\t\t\t<false/>\n\t\t\t<key>PrefersRightToLeft</key>\n\t\t\t<false/>\n\t\t\t<key>PrimaryLanguage</key>\n\t\t\t<string>en-US</string>\n\t\t\t<key>RequestsOpenAccess</key>\n\t\t\t<true/>\n\t\t</dict>\n\t\t<key>NSExtensionPointIdentifier</key>\n\t\t<string>com.apple.keyboard-service</string>\n\t\t<key>NSExtensionPrincipalClass</key>\n\t\t<string>$(PRODUCT_MODULE_NAME).KeyboardViewController</string>\n\t</dict>\n</dict>\n</plist>\n"
  },
  {
    "path": "ClipBoard/KeyboardViewController.swift",
    "content": "//\n//  KeyboardViewController.swift\n//  ClipBoard\n//\n//  Created by Riley Testut on 5/25/20.\n//  Copyright © 2020 Riley Testut. All rights reserved.\n//\n\nimport UIKit\nimport SwiftUI\nimport Roxas\n\nimport ClipKit\n\nclass KeyboardViewController: UIInputViewController\n{\n    private var hostingViewController: UIHostingController<AnyView>!\n    private var heightConstraint: NSLayoutConstraint!\n    \n    override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)\n    {\n        super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)\n        \n        if DatabaseManager.shared.persistentContainer.persistentStoreCoordinator.persistentStores.isEmpty\n        {\n            if !self.hasFullAccess\n            {\n                // Use temporary in-memory store if we don't have full access.\n                let inMemoryStoreDescription = NSPersistentStoreDescription()\n                inMemoryStoreDescription.type = NSInMemoryStoreType\n                DatabaseManager.shared.persistentContainer.persistentStoreDescriptions = [inMemoryStoreDescription]\n            }\n            \n            DatabaseManager.shared.persistentContainer.shouldAddStoresAsynchronously = false\n            DatabaseManager.shared.prepare { (result) in\n                switch result\n                {\n                case .failure(let error): print(\"Failed to prepare database:\", error)\n                case .success: break\n                }\n            }\n        }\n    }\n    \n    required init?(coder: NSCoder) {\n        fatalError(\"init(coder:) has not been implemented\")\n    }\n    \n    override func viewDidLoad()\n    {\n        super.viewDidLoad()\n        \n        self.inputView?.allowsSelfSizing = true\n        \n        let rootView = Keyboard(inputViewController: self)\n            .environment(\\.managedObjectContext, DatabaseManager.shared.persistentContainer.viewContext)\n        \n        self.hostingViewController = UIHostingController(rootView: AnyView(rootView))\n        self.hostingViewController.view.backgroundColor = .clear\n        \n        self.addChild(self.hostingViewController)\n        self.inputView?.addSubview(self.hostingViewController.view, pinningEdgesWith: .zero)\n        self.hostingViewController.didMove(toParent: self)\n        \n        self.view.setNeedsUpdateConstraints()\n    }\n\n    override func updateViewConstraints()\n    {\n        super.updateViewConstraints()\n                \n        if self.heightConstraint == nil\n        {\n            for constraint in self.view.constraintsAffectingLayout(for: .vertical)\n            {\n                // UIKit embeds height constraint, even if allowsSelfSizing is true.\n                // Must set to non-required priority, or else it will conflict with\n                // our own self-sizing constraint (annoyingly).\n                constraint.priority = .defaultHigh\n            }\n            \n            self.heightConstraint = self.view.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height / 2)\n            self.heightConstraint.isActive = true\n        }\n    }\n    \n    override func viewWillLayoutSubviews()\n    {\n        if let heightConstraint = self.heightConstraint\n        {\n            heightConstraint.constant = UIScreen.main.bounds.height / 2\n        }\n        \n        super.viewWillLayoutSubviews()\n    }\n}\n"
  },
  {
    "path": "ClipKit/ClipKit.h",
    "content": "//\n//  ClipKit.h\n//  ClipKit\n//\n//  Created by Riley Testut on 6/12/19.\n//  Copyright © 2019 Riley Testut. All rights reserved.\n//\n\n#import <UIKit/UIKit.h>\n\n//! Project version number for ClipKit.\nFOUNDATION_EXPORT double ClipKitVersionNumber;\n\n//! Project version string for ClipKit.\nFOUNDATION_EXPORT const unsigned char ClipKitVersionString[];\n\n// In this header, you should import all the public headers of your framework using statements like #import <ClipKit/PublicHeader.h>\n"
  },
  {
    "path": "ClipKit/Database/DatabaseManager.swift",
    "content": "//\n//  DatabaseManager.swift\n//  Clip\n//\n//  Created by Riley Testut on 6/11/19.\n//  Copyright © 2019 Riley Testut. All rights reserved.\n//\n\nimport CoreData\nimport CoreLocation\n\nimport Roxas\n\nprivate extension UserDefaults\n{\n    @NSManaged var previousHistoryToken: Data?\n}\n\npublic enum PasteboardError: LocalizedError\n{\n    case unsupportedImageFormat\n    case unsupportedItem\n    case noItem\n    case duplicateItem\n    \n    public var errorDescription: String? {\n        switch self\n        {\n        case .unsupportedImageFormat: return NSLocalizedString(\"Unsupported image format.\", comment: \"\")\n        case .unsupportedItem: return NSLocalizedString(\"Unsupported clipboard item.\", comment: \"\")\n        case .noItem: return NSLocalizedString(\"No clipboard item.\", comment: \"\")\n        case .duplicateItem: return NSLocalizedString(\"Duplicate item.\", comment: \"\")\n        }\n    }\n}\n\nprivate class PersistentContainer: RSTPersistentContainer\n{\n    override class func defaultDirectoryURL() -> URL\n    {\n        guard let appGroup = Bundle.main.appGroups.first else { return super.defaultDirectoryURL() }\n        \n        let sharedDirectoryURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup)!\n        \n        let databaseDirectoryURL = sharedDirectoryURL.appendingPathComponent(\"Database\")\n        try? FileManager.default.createDirectory(at: databaseDirectoryURL, withIntermediateDirectories: true, attributes: nil)\n        \n        print(\"Database URL:\", databaseDirectoryURL)\n        return databaseDirectoryURL\n    }\n}\n\npublic class DatabaseManager\n{\n    public static let shared = DatabaseManager()\n    \n    public let persistentContainer: RSTPersistentContainer = PersistentContainer(name: \"Model\", bundle: Bundle(for: DatabaseManager.self))\n    \n    public private(set) var isStarted = false\n    \n    private var prepareCompletionHandlers = [(Result<Void, Error>) -> Void]()\n    private let dispatchQueue = DispatchQueue(label: \"com.rileytestut.Clip.DatabaseManager\")\n    \n    private var previousHistoryToken: NSPersistentHistoryToken? {\n        set {\n            guard let value = newValue else {\n                UserDefaults.shared.previousHistoryToken = nil\n                return\n            }\n            \n            let data = try? NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: true)\n            UserDefaults.shared.previousHistoryToken = data\n        }\n        get {\n            guard let data = UserDefaults.shared.previousHistoryToken else { return nil }\n            \n            let token = try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSPersistentHistoryToken.self, from: data)\n            return token\n        }\n    }\n    \n    private init()\n    {\n    }\n    \n    public func prepare(completionHandler: @escaping (Result<Void, Error>) -> Void)\n    {\n        func finish(_ result: Result<Void, Error>)\n        {\n            self.dispatchQueue.async {\n                switch result\n                {\n                case .success: self.isStarted = true\n                case .failure: break\n                }\n                \n                self.prepareCompletionHandlers.forEach { $0(result) }\n                self.prepareCompletionHandlers.removeAll()\n            }\n        }\n        \n        self.dispatchQueue.async {\n            self.prepareCompletionHandlers.append(completionHandler)\n            guard self.prepareCompletionHandlers.count == 1 else { return }\n            \n            guard !self.isStarted else { return finish(.success(())) }\n            \n            self.persistentContainer.persistentStoreDescriptions.first?.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)\n            \n            self.persistentContainer.loadPersistentStores { (description, error) in\n                let result = Result(description, error).map { _ in () }\n                finish(result)\n                \n                self.purge()\n            }\n        }\n    }\n    \n    public func refresh()\n    {\n        DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in\n            let fetchRequest = NSPersistentHistoryChangeRequest.fetchHistory(after: self.previousHistoryToken)\n            \n            do\n            {\n                guard\n                    let result = try context.execute(fetchRequest) as? NSPersistentHistoryResult,\n                    let transactions = result.result as? [NSPersistentHistoryTransaction]\n                else { return }\n                \n                DispatchQueue.main.async {\n                    \n                    self.persistentContainer.viewContext.undoManager?.disableUndoRegistration()\n                    defer { self.persistentContainer.viewContext.undoManager?.enableUndoRegistration() }\n                    \n                    for transaction in transactions\n                    {\n                        self.persistentContainer.viewContext.mergeChanges(fromContextDidSave: transaction.objectIDNotification())\n                    }\n                    \n                    if let token = transactions.last?.token\n                    {\n                        self.previousHistoryToken = token\n                    }\n                }\n            }\n            catch let error as CocoaError where error.code.rawValue == NSPersistentHistoryTokenExpiredError\n            {\n                self.previousHistoryToken = nil\n                self.refresh()\n            }\n            catch\n            {\n                print(\"Failed to fetch change history.\", error)\n            }\n        }\n    }\n    \n    public func purge()\n    {\n        // In-memory contexts don't support history tracking.\n        guard let description = DatabaseManager.shared.persistentContainer.persistentStoreDescriptions.first, description.type != NSInMemoryStoreType else { return }\n        \n        DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in\n            if let token = self.previousHistoryToken\n            {\n                let deleteHistoryRequest = NSPersistentHistoryChangeRequest.deleteHistory(before: token)\n                \n                do { try context.execute(deleteHistoryRequest) }\n                catch { print(\"Failed to delete persistent distory.\", error) }\n            }\n            \n            do\n            {\n                let fetchRequest = PasteboardItem.historyFetchRequest() as! NSFetchRequest<NSManagedObjectID>\n                fetchRequest.resultType = .managedObjectIDResultType\n                \n                let objectIDs = try context.fetch(fetchRequest)\n                \n                let deletionFetchRequest = PasteboardItem.fetchRequest() as NSFetchRequest<NSFetchRequestResult>\n                deletionFetchRequest.predicate = NSPredicate(format: \"NOT (SELF IN %@)\", objectIDs)\n                \n                let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: deletionFetchRequest)\n                batchDeleteRequest.resultType = .resultTypeObjectIDs\n                \n                guard\n                    let result = try context.execute(batchDeleteRequest) as? NSBatchDeleteResult,\n                    let deletedObjectIDs = result.result as? [NSManagedObjectID]\n                else { return }\n                \n                let changes = [NSDeletedObjectsKey: deletedObjectIDs]\n                \n                DispatchQueue.main.async {\n                    NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [DatabaseManager.shared.persistentContainer.viewContext])\n                }\n            }\n            catch\n            {\n                print(\"Failed to delete pasteboard items.\", error)\n            }\n        }\n    }\n}\n\npublic extension DatabaseManager\n{\n    func savePasteboard(location: CLLocation?, completionHandler: @escaping (Result<Void, Error>) -> Void)\n    {\n        do\n        {\n            guard !UIPasteboard.general.hasColors else {\n                throw PasteboardError.unsupportedItem // Accessing UIPasteboard.items causes crash as of iOS 12.3 if it contains a UIColor.\n            }\n            \n            print(\"Did update pasteboard!\")\n            \n            guard let itemProvider = UIPasteboard.general.itemProviders.first else { throw PasteboardError.noItem }\n            guard !itemProvider.registeredTypeIdentifiers.contains(UTI.clipping) else { throw PasteboardError.duplicateItem } // Ignore copies that we made from the app.\n            \n            let context = DatabaseManager.shared.persistentContainer.newBackgroundContext()\n            PasteboardItemRepresentation.representations(for: itemProvider, in: context) { (representations) in\n                do\n                {\n                    guard let pasteboardItem = PasteboardItem(representations: representations, context: context) else { throw PasteboardError.noItem }\n                    pasteboardItem.location = location\n                    \n                    print(pasteboardItem)\n                    \n                    let fetchRequest = PasteboardItem.fetchRequest() as NSFetchRequest<PasteboardItem>\n                    fetchRequest.predicate = NSPredicate(format: \"%K == NO\", #keyPath(PasteboardItem.isMarkedForDeletion))\n                    fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \\PasteboardItem.date, ascending: false)]\n                    fetchRequest.relationshipKeyPathsForPrefetching = [\"representations\"]\n                    fetchRequest.includesPendingChanges = false\n                    fetchRequest.fetchLimit = 1\n                    \n                    if let previousItem = try context.fetch(fetchRequest).first\n                    {\n                        let representations = pasteboardItem.representations.reduce(into: [:], { ($0[$1.type] = $1.value as? NSObject) })\n                        let previousRepresentations = previousItem.representations.reduce(into: [:], { ($0[$1.type] = $1.value as? NSObject) })\n\n                        guard representations != previousRepresentations else {\n                            throw PasteboardError.duplicateItem\n                        }\n                    }\n                    \n                    guard let _ = pasteboardItem.preferredRepresentation else { throw PasteboardError.unsupportedItem }\n                    \n                    context.transactionAuthor = Bundle.main.bundleIdentifier\n                    try context.save()\n                    \n                    let center = CFNotificationCenterGetDarwinNotifyCenter()\n                    CFNotificationCenterPostNotification(center, .didChangePasteboard, nil, nil, true)\n                    \n                    DispatchQueue.main.async {\n                        completionHandler(.success(()))\n                    }\n                }\n                catch\n                {\n                    DispatchQueue.main.async {\n                        print(\"Failed to handle pasteboard item.\", error)\n                        completionHandler(.failure(error))\n                    }\n                }\n            }\n        }\n        catch\n        {\n            completionHandler(.failure(error))\n        }\n    }\n}\n"
  },
  {
    "path": "ClipKit/Database/Model/Model.xcdatamodeld/Model.xcdatamodel/contents",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<model type=\"com.apple.IDECoreDataModeler.DataModel\" documentVersion=\"1.0\" lastSavedToolsVersion=\"22757\" systemVersion=\"23E5211a\" minimumToolsVersion=\"Automatic\" sourceLanguage=\"Swift\" userDefinedModelVersionIdentifier=\"\">\n    <entity name=\"PasteboardItem\" representedClassName=\"PasteboardItem\" syncable=\"YES\">\n        <attribute name=\"date\" optional=\"YES\" attributeType=\"Date\" usesScalarValueType=\"NO\"/>\n        <attribute name=\"isMarkedForDeletion\" attributeType=\"Boolean\" defaultValueString=\"NO\" usesScalarValueType=\"YES\"/>\n        <attribute name=\"latitude\" optional=\"YES\" attributeType=\"Double\" defaultValueString=\"0.0\" usesScalarValueType=\"NO\"/>\n        <attribute name=\"longitude\" optional=\"YES\" attributeType=\"Double\" defaultValueString=\"0.0\" usesScalarValueType=\"NO\"/>\n        <relationship name=\"preferredRepresentation\" optional=\"YES\" maxCount=\"1\" deletionRule=\"Nullify\" destinationEntity=\"PasteboardItemRepresentation\" inverseName=\"preferringItem\" inverseEntity=\"PasteboardItemRepresentation\"/>\n        <relationship name=\"representations\" toMany=\"YES\" deletionRule=\"Cascade\" ordered=\"YES\" destinationEntity=\"PasteboardItemRepresentation\" inverseName=\"item\" inverseEntity=\"PasteboardItemRepresentation\"/>\n    </entity>\n    <entity name=\"PasteboardItemRepresentation\" representedClassName=\"PasteboardItemRepresentation\" syncable=\"YES\">\n        <attribute name=\"data\" optional=\"YES\" attributeType=\"Binary\" allowsExternalBinaryDataStorage=\"YES\"/>\n        <attribute name=\"string\" optional=\"YES\" attributeType=\"String\"/>\n        <attribute name=\"type\" attributeType=\"Integer 16\" defaultValueString=\"0\" usesScalarValueType=\"YES\"/>\n        <attribute name=\"url\" optional=\"YES\" attributeType=\"URI\"/>\n        <attribute name=\"uti\" optional=\"YES\" attributeType=\"String\"/>\n        <relationship name=\"item\" optional=\"YES\" maxCount=\"1\" deletionRule=\"Nullify\" destinationEntity=\"PasteboardItem\" inverseName=\"representations\" inverseEntity=\"PasteboardItem\"/>\n        <relationship name=\"preferringItem\" optional=\"YES\" maxCount=\"1\" deletionRule=\"Nullify\" destinationEntity=\"PasteboardItem\" inverseName=\"preferredRepresentation\" inverseEntity=\"PasteboardItem\"/>\n    </entity>\n</model>"
  },
  {
    "path": "ClipKit/Database/Model/PasteboardItem.swift",
    "content": "//\n//  PasteboardItem.swift\n//  Clip\n//\n//  Created by Riley Testut on 6/11/19.\n//  Copyright © 2019 Riley Testut. All rights reserved.\n//\n\nimport CoreData\nimport MobileCoreServices\nimport CoreLocation\n\nprivate extension PasteboardItemRepresentation.RepresentationType\n{\n    var priority: Int {\n        switch self\n        {\n        case .attributedText: return 0\n        case .text: return 1\n        case .url: return 2\n        case .image: return 3\n        }\n    }\n}\n\n@objc(PasteboardItem)\npublic class PasteboardItem: NSManagedObject, Identifiable\n{\n    /* Properties */\n    @NSManaged public private(set) var date: Date\n    @NSManaged public var isMarkedForDeletion: Bool\n    \n    public var location: CLLocation? {\n        get {\n            guard let latitude, let longitude else { return nil }\n            \n            let coordinate = CLLocation(latitude: latitude.doubleValue, longitude: longitude.doubleValue)\n            return coordinate\n        }\n        set {\n            self.latitude = newValue?.coordinate.latitude as? NSNumber\n            self.longitude = newValue?.coordinate.longitude as? NSNumber\n        }\n    }\n    @NSManaged private var latitude: NSNumber?\n    @NSManaged private var longitude: NSNumber?\n    \n    /* Relationships */\n    @nonobjc public var representations: [PasteboardItemRepresentation] {\n        return self._representations.array as! [PasteboardItemRepresentation]\n    }\n    @NSManaged @objc(representations) private var _representations: NSOrderedSet\n    \n    @NSManaged public var preferredRepresentation: PasteboardItemRepresentation?\n    \n    private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)\n    {\n        super.init(entity: entity, insertInto: context)\n    }\n    \n    public init?(representations: [PasteboardItemRepresentation], context: NSManagedObjectContext)\n    {\n        guard !representations.isEmpty else { return nil }\n        \n        super.init(entity: PasteboardItem.entity(), insertInto: context)\n        \n        self._representations = NSOrderedSet(array: representations)\n        \n        let prioritizedRepresentationTypes = PasteboardItemRepresentation.RepresentationType.allCases.sorted { $0.priority > $1.priority }\n        for type in prioritizedRepresentationTypes\n        {\n            guard let representation = representations.first(where: { $0.type == type }) else { continue }\n            \n            self.preferredRepresentation = representation\n            break\n        }\n    }\n    \n    override public func awakeFromInsert()\n    {\n        super.awakeFromInsert()\n        \n        self.date = Date()\n    }\n}\n\npublic extension PasteboardItem\n{\n    @nonobjc class func fetchRequest() -> NSFetchRequest<PasteboardItem>\n    {\n        return NSFetchRequest<PasteboardItem>(entityName: \"PasteboardItem\")\n    }\n    \n    class func historyFetchRequest() -> NSFetchRequest<PasteboardItem>\n    {\n        let fetchRequest = PasteboardItem.fetchRequest() as NSFetchRequest<PasteboardItem>\n        fetchRequest.predicate = NSPredicate(format: \"%K == NO\", #keyPath(PasteboardItem.isMarkedForDeletion))\n        fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \\PasteboardItem.date, ascending: false)]\n        fetchRequest.fetchLimit = UserDefaults.shared.historyLimit.rawValue\n        return fetchRequest\n    }\n}\n\n// SwiftUI\nextension PasteboardItem\n{\n    class func make(item: NSItemProviderWriting, date: Date = Date(), context: NSManagedObjectContext) -> PasteboardItem\n    {\n        let itemProvider = NSItemProvider(object: item)\n        let semaphore = DispatchSemaphore(value: 0)\n        \n        let childContext = DatabaseManager.shared.persistentContainer.newBackgroundContext()\n        var objectID: NSManagedObjectID!\n        \n        PasteboardItemRepresentation.representations(for: itemProvider, in: childContext) { (representations) in\n            let item = PasteboardItem(representations: representations, context: childContext)!\n            item.date = date\n            \n            try! childContext.obtainPermanentIDs(for: [item])\n            objectID = item.objectID\n            \n            try! childContext.save()\n            semaphore.signal()\n        }\n        semaphore.wait()\n                \n        let pasteboardItem = context.object(with: objectID) as! PasteboardItem\n        return pasteboardItem\n    }\n}\n"
  },
  {
    "path": "ClipKit/Database/Model/PasteboardItemRepresentation.swift",
    "content": "//\n//  PasteboardItemRepresentation.swift\n//  Clip\n//\n//  Created by Riley Testut on 6/11/19.\n//  Copyright © 2019 Riley Testut. All rights reserved.\n//\n\nimport UIKit\nimport CoreData\nimport MobileCoreServices\n\nextension PasteboardItemRepresentation\n{\n    @objc public enum RepresentationType: Int16, CaseIterable\n    {\n        case text\n        case attributedText\n        case url\n        case image\n        \n        public var localizedName: String {\n            switch self\n            {\n            case .text: return NSLocalizedString(\"Text\", comment: \"\")\n            case .attributedText: return NSLocalizedString(\"Text\", comment: \"\")\n            case .url: return NSLocalizedString(\"URL\", comment: \"\")\n            case .image: return NSLocalizedString(\"Image\", comment: \"\")\n            }\n        }\n    }\n}\n\n@objc(PasteboardItemRepresentation)\npublic class PasteboardItemRepresentation: NSManagedObject\n{\n    /* Properties */\n    @NSManaged public private(set) var uti: String\n    @NSManaged public private(set) var type: RepresentationType\n    \n    @NSManaged private var data: Data?\n    @NSManaged private var string: String?\n    @NSManaged private var url: URL?\n    \n    /* Relationships */\n    @NSManaged public var item: PasteboardItem?\n    @NSManaged private var preferringItem: PasteboardItem? // Inverse of PasteboardItem.preferredRepresentation.\n    \n    private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)\n    {\n        super.init(entity: entity, insertInto: context)\n    }\n    \n    private init(uti: String, type: RepresentationType, context: NSManagedObjectContext)\n    {\n        super.init(entity: PasteboardItemRepresentation.entity(), insertInto: context)\n        self.uti = uti\n        self.type = type\n    }\n    \n    private convenience init(uti: String, text: String, context: NSManagedObjectContext)\n    {\n        self.init(uti: uti, type: .text, context: context)\n        self.string = text\n    }\n    \n    private convenience init(uti: String, data: Data, type: RepresentationType, context: NSManagedObjectContext)\n    {\n        self.init(uti: uti, type: type, context: context)\n        self.data = data\n    }\n    \n    private convenience init(uti: String, url: URL, context: NSManagedObjectContext)\n    {\n        self.init(uti: uti, type: .url, context: context)\n        self.url = url\n    }\n    \n    public static func representations(for itemProvider: NSItemProvider, in context: NSManagedObjectContext, completionHandler: @escaping ([PasteboardItemRepresentation]) -> Void)\n    {\n        var representations = [PasteboardItemRepresentation]()\n        \n        let dispatchGroup = DispatchGroup()\n        \n        let supportedTextUTIs = [kUTTypeUTF8PlainText, kUTTypePlainText, kUTTypeText]\n        if let uti = supportedTextUTIs.first(where: { itemProvider.hasItemConformingToTypeIdentifier($0 as String) }), itemProvider.canLoadObject(ofClass: NSString.self)\n        {\n            dispatchGroup.enter()\n            \n            itemProvider.loadObject(ofClass: NSString.self) { (text, error) in\n                context.perform {\n                    switch Result(text, error)\n                    {\n                    case .failure(let error): print(error)\n                    case .success(let text):\n                        let representation = PasteboardItemRepresentation(uti: uti as String, text: text as! String, context: context)\n                        representations.append(representation)\n                    }\n                    \n                    dispatchGroup.leave()\n                }\n            }\n        }\n        \n        let supportedAttributedTextUTIs = [kUTTypeRTF, kUTTypeHTML, kUTTypeFlatRTFD, kUTTypeRTFD]\n        if let uti = supportedAttributedTextUTIs.first(where: { itemProvider.hasItemConformingToTypeIdentifier($0 as String) }), itemProvider.canLoadObject(ofClass: NSAttributedString.self)\n        {\n            dispatchGroup.enter()\n            \n            itemProvider.loadDataRepresentation(forTypeIdentifier: uti as String) { (data, error) in\n                context.perform {\n                    switch Result(data, error)\n                    {\n                    case .failure(let error): print(error)\n                    case .success(let data):\n                        let representation = PasteboardItemRepresentation(uti: uti as String, data: data, type: .attributedText, context: context)\n                        representations.append(representation)\n                    }\n                    \n                    dispatchGroup.leave()\n                }\n            }\n        }\n        \n        let supportedImageUTIs = [kUTTypePNG, kUTTypeJPEG, kUTTypeImage]\n        if let uti = supportedImageUTIs.first(where: { itemProvider.hasItemConformingToTypeIdentifier($0 as String) }), itemProvider.canLoadObject(ofClass: UIImage.self)\n        {\n            dispatchGroup.enter()\n            \n            itemProvider.loadDataRepresentation(forTypeIdentifier: uti as String) { (data, error) in\n                context.perform {\n                    switch Result(data, error)\n                    {\n                    case .failure(let error): print(error)\n                    case .success(let data):\n                        guard data.count <= UserDefaults.shared.maximumClippingSize else { break }\n                        \n                        let representation = PasteboardItemRepresentation(uti: uti as String, data: data, type: .image, context: context)\n                        representations.append(representation)\n                    }\n                    \n                    dispatchGroup.leave()\n                }\n            }\n        }\n        \n        let supportedURLUTIs = [kUTTypeFileURL, kUTTypeURL]\n        if let uti = supportedURLUTIs.first(where: { itemProvider.hasItemConformingToTypeIdentifier($0 as String) }), itemProvider.canLoadObject(ofClass: NSURL.self)\n        {\n            dispatchGroup.enter()\n            \n            itemProvider.loadObject(ofClass: NSURL.self) { (url, error) in\n                context.perform {\n                    switch Result(url, error)\n                    {\n                    case .failure(let error as NSError) where error.domain == NSItemProvider.errorDomain && error.code == NSItemProvider.ErrorCode.unavailableCoercionError.rawValue:\n                        // Ignore, corrupted data.\n                        break\n                        \n                    case .failure(let error): print(\"Failed to load URL.\", error)\n                        \n                    case .success(let url):\n                        let representation = PasteboardItemRepresentation(uti: uti as String, url: url as! URL, context: context)\n                        representations.append(representation)\n                    }\n                    \n                    dispatchGroup.leave()\n                }\n            }\n        }\n        \n        dispatchGroup.notify(queue: .global()) {\n            context.perform {\n                let sortedRepresentations = representations.sorted(by: { (a, b) -> Bool in\n                    guard let indexA = itemProvider.registeredTypeIdentifiers.firstIndex(of: a.uti) else { return false }\n                    guard let indexB = itemProvider.registeredTypeIdentifiers.firstIndex(of: b.uti) else { return false }\n                    \n                    return indexA < indexB\n                })\n                \n                completionHandler(sortedRepresentations)\n            }\n        }\n    }\n}\n\nextension PasteboardItemRepresentation\n{\n    @nonobjc class func fetchRequest() -> NSFetchRequest<PasteboardItemRepresentation>\n    {\n        return NSFetchRequest<PasteboardItemRepresentation>(entityName: \"PasteboardItemRepresentation\")\n    }\n}\n\npublic extension PasteboardItemRepresentation\n{\n    var value: Any? {\n        switch self.type\n        {\n        case .text: return self.stringValue\n        case .attributedText: return self.attributedStringValue\n        case .url: return self.urlValue\n        case .image: return self.imageValue\n        }\n    }\n    \n    var pasteboardValue: Any? {\n        switch self.type\n        {\n        case .text: return self.string\n        case .attributedText: return self.data\n        case .url: return self.url\n        case .image: return self.data\n        }\n    }\n    \n    var stringValue: String? {\n        switch self.type\n        {\n        case .text: return self.string\n        case .attributedText: return self.attributedStringValue?.string\n        case .url: return self.urlValue?.absoluteString\n        case .image: return nil\n        }\n    }\n    \n    var imageValue: UIImage? {\n        guard let data = self.data, let image = UIImage(data: data) else { return nil }\n        return image\n    }\n    \n    var urlValue: URL? {\n        return self.url\n    }\n    \n    var dataValue: Data? {\n        return self.data\n    }\n    \n    var attributedStringValue: NSAttributedString? {\n        let type: NSAttributedString.DocumentType\n        \n        switch self.uti\n        {\n        case let uti as CFString where UTTypeConformsTo(uti, kUTTypeRTF): type = .rtf\n        case let uti as CFString where UTTypeConformsTo(uti, kUTTypeHTML): type = .html\n            \n        case let uti as CFString where UTTypeConformsTo(uti, kUTTypeRTFD): type = .rtfd\n        case let uti as CFString where UTTypeConformsTo(uti, kUTTypeFlatRTFD): type = .rtfd\n            \n        default: return nil\n        }\n        \n        guard let data = self.data ?? self.string?.data(using: .utf8) else { return nil }\n        \n        let attributedString = try? NSAttributedString(data: data, options: [.documentType : type], documentAttributes: nil)\n        return attributedString\n    }\n}\n"
  },
  {
    "path": "ClipKit/Extensions/Bundle+AppGroups.swift",
    "content": "//\n//  Bundle+AppGroups.swift\n//  ClipKit\n//\n//  Created by Riley Testut on 6/25/19.\n//  Copyright © 2019 Riley Testut. All rights reserved.\n//\n\nimport Foundation\n\npublic extension Bundle\n{\n    var appGroups: [String] {\n        let appGroups = self.object(forInfoDictionaryKey: \"ALTAppGroups\") as? [String]\n        return appGroups ?? []\n    }\n}\n"
  },
  {
    "path": "ClipKit/Extensions/CFNotification+PasteboardListener.swift",
    "content": "//\n//  CFNotification+PasteboardListener.swift\n//  ClipKit\n//\n//  Created by Riley Testut on 6/13/19.\n//  Copyright © 2019 Riley Testut. All rights reserved.\n//\n\nimport CoreFoundation\n\npublic extension CFNotificationName\n{\n    static let didChangePasteboard: CFNotificationName = CFNotificationName(\"com.rileytestut.Clip.DidChangePasteboard\" as CFString)\n    \n    static let ignoreNextPasteboardChange: CFNotificationName = CFNotificationName(\"com.rileytestut.Clip.IgnoreNextPasteboardChange\" as CFString)\n}\n"
  },
  {
    "path": "ClipKit/Extensions/Int+Bytes.swift",
    "content": "//\n//  Int+Bytes.swift\n//  ClipKit\n//\n//  Created by Riley Testut on 6/17/19.\n//  Copyright © 2019 Riley Testut. All rights reserved.\n//\n\nimport Foundation\n\npublic extension Int\n{\n    static var bytesPerMegabyte: Int {\n        return 1024 * 1024\n    }\n}\n"
  },
  {
    "path": "ClipKit/Extensions/Result+Conveniences.swift",
    "content": "//\n//  Result+Conveniences.swift\n//  AltStore\n//\n//  Created by Riley Testut on 5/22/19.\n//  Copyright © 2019 Riley Testut. All rights reserved.\n//\n\nimport Foundation\n\npublic extension Result\n{\n    var value: Success? {\n        switch self\n        {\n        case .success(let value): return value\n        case .failure: return nil\n        }\n    }\n    \n    var error: Failure? {\n        switch self\n        {\n        case .success: return nil\n        case .failure(let error): return error\n        }\n    }\n    \n    init(_ value: Success?, _ error: Failure?)\n    {\n        switch (value, error)\n        {\n        case (let value?, _): self = .success(value)\n        case (_, let error?): self = .failure(error)\n        case (nil, nil): preconditionFailure(\"Either value or error must be non-nil\")\n        }\n    }\n}\n\npublic extension Result where Success == Void\n{\n    init(_ success: Bool, _ error: Failure?)\n    {\n        if success\n        {\n            self = .success(())\n        }\n        else if let error = error\n        {\n            self = .failure(error)\n        }\n        else\n        {\n            preconditionFailure(\"Error must be non-nil if success is false\")\n        }\n    }\n}\n\npublic extension Result\n{\n    init<T, U>(_ values: (T?, U?), _ error: Failure?) where Success == (T, U)\n    {\n        if let value1 = values.0, let value2 = values.1\n        {\n            self = .success((value1, value2))\n        }\n        else if let error = error\n        {\n            self = .failure(error)\n        }\n        else\n        {\n            preconditionFailure(\"Error must be non-nil if either provided values are nil\")\n        }\n    }\n}\n"
  },
  {
    "path": "ClipKit/Extensions/UIColor+Clip.swift",
    "content": "//\n//  UIColor+Clip.swift\n//  Clip\n//\n//  Created by Riley Testut on 7/29/19.\n//  Copyright © 2019 Riley Testut. All rights reserved.\n//\n\nimport UIKit\n\npublic extension UIColor\n{\n    static let clipPink = UIColor(named: \"Pink\", in: Bundle(for: DatabaseManager.self), compatibleWith: nil)!\n    static let clipLightPink = UIColor(named: \"LightPink\", in: Bundle(for: DatabaseManager.self), compatibleWith: nil)!\n}\n"
  },
  {
    "path": "ClipKit/Extensions/UIInputView+Click.swift",
    "content": "//\n//  UIInputView+Click.swift\n//  ClipKit\n//\n//  Created by Riley Testut on 6/9/20.\n//  Copyright © 2020 Riley Testut. All rights reserved.\n//\n\nimport UIKit\n\nextension UIInputView: UIInputViewAudioFeedback\n{\n    public var enableInputClicksWhenVisible: Bool {\n        return true\n    }\n    \n    func playInputClick()\n    {\n        UIDevice.current.playInputClick()\n    }\n}\n"
  },
  {
    "path": "ClipKit/Extensions/UIPasteboard+PasteboardItem.swift",
    "content": "//\n//  UIPasteboard+PasteboardItem.swift\n//  ClipKit\n//\n//  Created by Riley Testut on 5/26/20.\n//  Copyright © 2020 Riley Testut. All rights reserved.\n//\n\nimport UIKit\n\npublic extension UIPasteboard\n{\n    func copy(_ pasteboardItem: PasteboardItem)\n    {\n        var representations = pasteboardItem.representations.reduce(into: [:]) { $0[$1.uti] = $1.pasteboardValue }\n        representations[UTI.clipping] = [:]\n        \n        self.setItems([representations], options: [:])\n    }\n}\n"
  },
  {
    "path": "ClipKit/Extensions/UNNotification+Keys.swift",
    "content": "//\n//  UNNotification+Keys.swift\n//  ClipKit\n//\n//  Created by Riley Testut on 3/20/24.\n//  Copyright © 2024 Riley Testut. All rights reserved.\n//\n\nimport UserNotifications\n\npublic extension UNNotification\n{\n    static let latitudeUserInfoKey: String = \"CLPLatitude\"\n    static let longitudeUserInfoKey: String = \"CLPLongitude\"\n    \n    static let errorMessageUserInfoKey: String = \"CLPErrorMessage\"\n}\n\npublic extension UNNotificationCategory\n{\n    static let clipboardReaderIdentifier = \"ClipboardReader\"\n}\n"
  },
  {
    "path": "ClipKit/Extensions/UserDefaults+App.swift",
    "content": "//\n//  UserDefaults+App.swift\n//  Clip\n//\n//  Created by Riley Testut on 6/14/19.\n//  Copyright © 2019 Riley Testut. All rights reserved.\n//\n\nimport Foundation\n\nimport Roxas\n\n@objc public enum HistoryLimit: Int, CaseIterable\n{\n    case _10 = 10\n    case _25 = 25\n    case _50 = 50\n    case _100 = 100\n}\n\npublic extension UserDefaults\n{\n    static let shared: UserDefaults = {\n        guard let appGroup = Bundle.main.appGroups.first else { return .standard }\n        \n        let sharedUserDefaults = UserDefaults(suiteName: appGroup)!\n        return sharedUserDefaults\n    }()\n    \n    @NSManaged var historyLimit: HistoryLimit\n    @NSManaged var maximumClippingSize: Int\n    @NSManaged var showLocationIcon: Bool\n}\n\npublic extension UserDefaults\n{\n    func registerAppDefaults()\n    {\n        self.register(defaults: [\n            #keyPath(UserDefaults.historyLimit): HistoryLimit._25.rawValue,\n            #keyPath(UserDefaults.maximumClippingSize): 10 * .bytesPerMegabyte,\n            #keyPath(UserDefaults.showLocationIcon): true\n        ])\n    }\n}\n"
  },
  {
    "path": "ClipKit/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>FMWK</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>1.0</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(CURRENT_PROJECT_VERSION)</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "ClipKit/Resources/Colors.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "ClipKit/Resources/Colors.xcassets/LightPink.colorset/Contents.json",
    "content": "{\n  \"colors\" : [\n    {\n      \"color\" : {\n        \"color-space\" : \"srgb\",\n        \"components\" : {\n          \"alpha\" : \"1.000\",\n          \"blue\" : \"0.404\",\n          \"green\" : \"0.404\",\n          \"red\" : \"0.988\"\n        }\n      },\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "ClipKit/Resources/Colors.xcassets/Pink.colorset/Contents.json",
    "content": "{\n  \"colors\" : [\n    {\n      \"color\" : {\n        \"color-space\" : \"srgb\",\n        \"components\" : {\n          \"alpha\" : \"1.000\",\n          \"blue\" : \"0.549\",\n          \"green\" : \"0.000\",\n          \"red\" : \"0.925\"\n        }\n      },\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "ClipKit/SwiftUI/Blur.swift",
    "content": "//\n//  Blur.swift\n//  ClipKit\n//\n//  Created by Riley Testut on 5/25/20.\n//  Copyright © 2020 Riley Testut. All rights reserved.\n//\n\nimport SwiftUI\nimport UIKit\n\nfileprivate struct BlurStyleKey: EnvironmentKey\n{\n    static let defaultValue: UIBlurEffect.Style = .regular\n}\n\npublic extension EnvironmentValues\n{\n    var blurStyle: UIBlurEffect.Style {\n        get { self[BlurStyleKey.self] }\n        set { self[BlurStyleKey.self] = newValue }\n    }\n}\n\npublic extension View\n{\n    func blurStyle(_ blurStyle: UIBlurEffect.Style) -> some View {\n        environment(\\.blurStyle, blurStyle)\n    }\n}\n\npublic struct Blur: View, UIViewRepresentable\n{\n    @Environment(\\.blurStyle) var blurStyle: UIBlurEffect.Style\n\n    public func makeUIView(context: Context) -> UIVisualEffectView\n    {\n        let visualEffectView = UIVisualEffectView(effect: nil)\n        updateUIView(visualEffectView, context: context)\n\n        return visualEffectView\n    }\n\n    public func updateUIView(_ uiView: UIVisualEffectView, context: Context)\n    {\n        let blurEffect = UIBlurEffect(style: self.blurStyle)\n        uiView.effect = blurEffect\n    }\n}\n\nstruct Blur_Previews: PreviewProvider\n{\n    static var previews: some View {\n        ZStack {\n            Color.blue\n            Blur()\n            Text(\"Hello World!\")\n        }\n        .colorScheme(.dark)\n        .previewLayout(.fixed(width: 300, height: 300))\n    }\n}\n"
  },
  {
    "path": "ClipKit/SwiftUI/ClippingCell.swift",
    "content": "//\n//  ClippingCell.swift\n//  ClipKit\n//\n//  Created by Riley Testut on 5/25/20.\n//  Copyright © 2020 Riley Testut. All rights reserved.\n//\n\nimport SwiftUI\nimport CoreData\nimport MobileCoreServices\n\nprivate extension Formatter\n{\n    static let clipFormatter: DateComponentsFormatter = {\n        let formatter = DateComponentsFormatter()\n        formatter.unitsStyle = .abbreviated\n        formatter.maximumUnitCount = 1\n        formatter.allowedUnits = [.second, .minute, .hour, .day]\n        return formatter\n    }()\n}\n\npublic struct ClippingCell: View\n{\n    @ObservedObject var pasteboardItem: PasteboardItem\n    \n    public init(pasteboardItem: PasteboardItem) \n    {\n        self.pasteboardItem = pasteboardItem\n    }\n        \n    public var body: some View {\n        let representation = self.pasteboardItem.preferredRepresentation\n        let dateString = Formatter.clipFormatter.string(from: self.pasteboardItem.date, to: Date())\n\n        return Group {\n            VStack(alignment: .leading, spacing: 8) {\n                HStack {\n                    Text(representation?.type.localizedName ?? \"Unknown\")\n                        .font(.headline)\n                    \n                    Spacer()\n                                            \n                    Text(dateString ?? \"\")\n                        .font(.caption)\n                }\n                .foregroundColor(Color(.clipPink))\n                \n                if representation?.stringValue != nil\n                {\n                    Text(representation!.stringValue!)\n                        .font(.subheadline)\n                        .lineLimit(6)\n                }\n            }\n            .padding(.horizontal, nil)\n            .padding(.vertical, 8)\n        }\n        .background(Color.white)\n        .colorScheme(.light)\n        .frame(minWidth: 0, maxWidth: .infinity)\n        .cornerRadius(10)\n    }\n}\n\nstruct ClippingCell_Previews: PreviewProvider\n{\n    static var previews: some View\n    {\n        Preview.prepare()\n        \n        let context = DatabaseManager.shared.persistentContainer.viewContext\n        let date = Date().addingTimeInterval(-1 * 60 * 60)\n        \n        let item = PasteboardItem.make(item: \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\" as NSString, date: date, context: context)\n        \n        return ClippingCell(pasteboardItem: item)\n            .background(Color(.clipPink))\n            .environment(\\.managedObjectContext, context)\n            .previewLayout(.sizeThatFits)\n    }\n}\n"
  },
  {
    "path": "ClipKit/SwiftUI/Keyboard.swift",
    "content": "//\n//  Keyboard.swift\n//  ClipKit\n//\n//  Created by Riley Testut on 5/25/20.\n//  Copyright © 2020 Riley Testut. All rights reserved.\n//\n\nimport SwiftUI\nimport CoreData\nimport UIKit\n\nimport Roxas\n\n@objc\nprivate protocol RSTApplication: AnyObject\n{\n    @objc(openURL:options:completionHandler:)\n    func open(_ url: URL, options: [UIApplication.OpenExternalURLOptionsKey : Any], completionHandler completion: (@MainActor @Sendable (Bool) -> Void)?)\n}\n\npublic struct Keyboard: View\n{\n    private let inputViewController: UIInputViewController?\n    private let needsInputModeSwitchKey: Bool\n    private let hasFullAccess: Bool\n    \n    @FetchRequest(fetchRequest: PasteboardItem.historyFetchRequest())\n    private var pasteboardItems: FetchedResults<PasteboardItem>\n    \n    @Environment(\\.horizontalSizeClass)\n    private var horizontalSizeClass\n    \n    public init(inputViewController: UIInputViewController?,\n                needsInputModeSwitchKey: Bool? = nil,\n                hasFullAccess: Bool? = nil)\n    {\n        self.inputViewController = inputViewController\n        self.needsInputModeSwitchKey = needsInputModeSwitchKey ?? inputViewController?.needsInputModeSwitchKey ?? false\n        self.hasFullAccess = hasFullAccess ?? inputViewController?.hasFullAccess ?? true\n    }\n        \n    public var body: some View {\n        ZStack(alignment: .bottomLeading) {\n            \n            if !self.hasFullAccess\n            {\n                VStack(spacing: 32) {\n                    VStack(spacing: 16) {\n                        Text(\"Full Access Disabled\")\n                            .font(.title)\n                        Text(\"Allow Full Access for this keyboard in Settings to access saved clippings.\")\n                            .font(.body)\n                    }\n                    \n                    Button(action: self.openSettings) {\n                        Text(\"Open Settings\")\n                            .font(Font(UIFont.preferredFont(forTextStyle: .title3)))\n                            .foregroundColor(Color(.clipPink))\n                    }\n                    \n                    Button(action: self.pasteUUID) {\n                        Text(\"Paste Random UUID\")\n                            .font(Font(UIFont.preferredFont(forTextStyle: .title3)))\n                            .foregroundColor(Color(.clipPink))\n                    }\n                }\n                .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)\n                .padding()\n            }\n            else if self.pasteboardItems.isEmpty\n            {\n                VStack(spacing: 16) {\n                    Text(\"No Clippings\")\n                        .font(.title)\n                    Text(\"Items that you've copied to the clipboard will appear here.\")\n                        .font(.body)\n                }\n                .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)\n                .padding()\n            }\n            else\n            {\n                let list = List(self.pasteboardItems, id: \\.objectID) { (pasteboardItem) in\n                    Button(action: { self.paste(pasteboardItem) }) {\n                        ClippingCell(pasteboardItem: pasteboardItem)\n                    }\n                    .buttonStyle(.plain)\n                    .foregroundStyle(.primary)\n                    .listRowBackground(Color.clear)\n                    .listRowSeparator(.hidden)\n                    .listRowInsets(makeInsets())\n                }\n                .padding(.top, 8) // iPadOS sometimes places List too close to toolbar, so add padding.\n                \n                if #available(iOS 16.4, *)\n                {\n                    list\n                        .scrollContentBackground(.hidden)\n                        .scrollBounceBehavior(.always, axes: .vertical)\n                }\n                else\n                {\n                    list\n                }\n            }\n            \n            if self.needsInputModeSwitchKey\n            {\n                let offset = (self.horizontalSizeClass == .regular) ? 16.0 : 8.0\n                \n                SwitchKeyboardButton(inputViewController: self.inputViewController,\n                                     tintColor: .clipPink,\n                                     configuration: .init(textStyle: .title2))\n                    .fixedSize()\n                    .padding(.horizontal, 4)\n                    .padding(.vertical, 8)\n                    .background(Blur())\n                    .blurStyle(.extraLight)\n                    .clipShape(Circle())\n                    .offset(x: offset, y: -offset)\n            }\n        }\n        .edgesIgnoringSafeArea(.all)\n        .onAppear {\n            UITableView.appearance().backgroundColor = .clear\n            UITableView.appearance().separatorStyle = .none\n            UITableView.appearance().contentInset = UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0)\n            \n            UITableViewCell.appearance().backgroundColor = .clear\n        }\n    }\n    \n    private func makeInsets() -> EdgeInsets\n    {\n        var insets = EdgeInsets()\n        insets.top = 8\n        insets.bottom = 8\n        return insets\n    }\n}\n\nprivate extension Keyboard\n{\n    func paste(_ pasteboardItem: PasteboardItem)\n    {\n        guard let text = pasteboardItem.preferredRepresentation?.stringValue else { return }\n        \n        let center = CFNotificationCenterGetDarwinNotifyCenter()\n        CFNotificationCenterPostNotification(center, .ignoreNextPasteboardChange, nil, nil, true)\n        \n        UIPasteboard.general.copy(pasteboardItem)\n        \n        self.paste(text)\n    }\n    \n    func pasteUUID()\n    {\n        let uuid = UUID().uuidString\n        self.paste(uuid)\n    }\n    \n    func paste(_ text: String)\n    {\n        self.inputViewController?.textDocumentProxy.insertText(text)\n        self.inputViewController?.inputView?.playInputClick()\n        \n        DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {\n            self.inputViewController?.advanceToNextInputMode()\n        }\n    }\n    \n    func openSettings()\n    {\n        // NSExtensionContext.openURL() can only be called from Today extensions.\n        // As a workaround, we can just call UIApplication.openURL(),\n        // but we can't call it directly because it's marked as unavailable for extensions.\n        guard\n            let application = (UIApplication.self as AnyObject).value(forKey: \"sharedApplication\") as? UIApplication\n        else { return }\n        \n        // UIApplication.openSettingsURLString doesn't work from keyboard extension,\n        // so instead we open Clip which will then open Settings.\n        let openURL = URL(string: \"clip://settings\")!\n        \n        let tempApp = unsafeBitCast(application, to: RSTApplication.self)\n        tempApp.open(openURL, options: [:], completionHandler: nil)\n    }\n}\n\nstruct Keyboard_Previews: PreviewProvider\n{\n    static var previews: some View\n    {\n        Preview.prepare()\n        \n        let context = DatabaseManager.shared.persistentContainer.viewContext\n        let date = Date().addingTimeInterval(-1 * 60 * 60)\n        \n        _ = PasteboardItem.make(item: \"Hello SwiftUI!\" as NSString, date: date, context: context)\n        _ = PasteboardItem.make(item: NSURL(string: \"https://rileytestut.com\")!, date: date, context: context)\n        _ = PasteboardItem.make(item: \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\" as NSString, date: date, context: context)\n                \n        return Group {\n            Keyboard(inputViewController: nil, needsInputModeSwitchKey: true)\n            Keyboard(inputViewController: nil, needsInputModeSwitchKey: true, hasFullAccess: false)\n        }\n        .background(Color(.lightGray))\n        .environment(\\.managedObjectContext, context)\n        .previewLayout(.fixed(width: 375, height: 500))\n    }\n}\n"
  },
  {
    "path": "ClipKit/SwiftUI/Preview.swift",
    "content": "//\n//  Preview.swift\n//  ClipKit\n//\n//  Created by Riley Testut on 5/26/20.\n//  Copyright © 2020 Riley Testut. All rights reserved.\n//\n\nimport Foundation\nimport CoreData\n\nstruct Preview\n{\n    static func prepare()\n    {\n        if DatabaseManager.shared.persistentContainer.persistentStoreCoordinator.persistentStores.isEmpty\n        {\n            let inMemoryStoreDescription = NSPersistentStoreDescription()\n            inMemoryStoreDescription.type = NSInMemoryStoreType\n            \n            DatabaseManager.shared.persistentContainer.persistentStoreDescriptions = [inMemoryStoreDescription]\n            DatabaseManager.shared.persistentContainer.shouldAddStoresAsynchronously = false\n            DatabaseManager.shared.prepare() { (result) in\n                print(\"Database Result:\", result)\n            }\n        }\n        \n        // Manually call initialize() since it isn't normally called when previewing 🤷‍♂️\n        UserDefaults.initialize()\n    }\n}\n"
  },
  {
    "path": "ClipKit/SwiftUI/SwitchKeyboardButton.swift",
    "content": "//\n//  SwitchKeyboardButton.swift\n//  ClipKit\n//\n//  Created by Riley Testut on 6/9/20.\n//  Copyright © 2020 Riley Testut. All rights reserved.\n//\n\nimport SwiftUI\n\nstruct SwitchKeyboardButton: UIViewRepresentable\n{\n    var inputViewController: UIInputViewController?\n    \n    var tintColor: UIColor? = nil\n    var configuration: UIImage.SymbolConfiguration? = nil\n    \n    func makeUIView(context: Context) -> UIButton\n    {\n        let button = UIButton(type: .system)\n        button.tintColor = self.tintColor\n        button.addTarget(self.inputViewController,\n                         action: #selector(UIInputViewController.handleInputModeList(from:with:)),\n                         for: .allTouchEvents)\n        \n        button.setImage(UIImage(systemName: \"globe\", withConfiguration: self.configuration), for: .normal)\n        button.sizeToFit()\n        \n        return button\n    }\n    \n    func updateUIView(_ button: UIButton, context: Context)\n    {\n    }\n}\n\nstruct SwitchKeyboardButton_Previews: PreviewProvider\n{\n    static var previews: some View {\n        Group {\n            SwitchKeyboardButton(inputViewController: nil)\n                .fixedSize()\n            \n            SwitchKeyboardButton(inputViewController: nil,\n                                 tintColor: .clipPink,\n                                 configuration: .init(textStyle: .title1))\n                .fixedSize()\n        }\n        .previewLayout(.sizeThatFits)\n    }\n}\n"
  },
  {
    "path": "ClipKit/UTI.swift",
    "content": "//\n//  UTI.swift\n//  ClipKit\n//\n//  Created by Riley Testut on 6/14/19.\n//  Copyright © 2019 Riley Testut. All rights reserved.\n//\n\nimport Foundation\n\npublic struct UTI\n{\n    public static let clipping = \"com.rileytestut.Clip.Clipping\"\n}\n"
  },
  {
    "path": "ClipboardReader/Base.lproj/MainInterface.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"15400\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" useSafeAreas=\"YES\" colorMatched=\"YES\" initialViewController=\"M4Y-Lb-cyx\">\n    <device id=\"retina6_1\" orientation=\"portrait\" appearance=\"light\"/>\n    <dependencies>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"15404\"/>\n        <capability name=\"Safe area layout guides\" minToolsVersion=\"9.0\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <scenes>\n        <!--Notification View Controller-->\n        <scene sceneID=\"cwh-vc-ff4\">\n            <objects>\n                <viewController id=\"M4Y-Lb-cyx\" userLabel=\"Notification View Controller\" customClass=\"NotificationViewController\" customModule=\"ClipboardReader\" customModuleProvider=\"target\" sceneMemberID=\"viewController\">\n                    <view key=\"view\" contentMode=\"scaleToFill\" simulatedAppContext=\"notificationCenter\" id=\"S3S-Oj-5AN\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"320\" height=\"37\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <activityIndicatorView opaque=\"NO\" contentMode=\"scaleToFill\" horizontalHuggingPriority=\"750\" verticalHuggingPriority=\"750\" hidesWhenStopped=\"YES\" animating=\"YES\" style=\"medium\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"0Ub-qg-2xe\">\n                                <rect key=\"frame\" x=\"150\" y=\"8.5\" width=\"20\" height=\"20\"/>\n                            </activityIndicatorView>\n                        </subviews>\n                        <constraints>\n                            <constraint firstItem=\"0Ub-qg-2xe\" firstAttribute=\"centerX\" secondItem=\"S3S-Oj-5AN\" secondAttribute=\"centerX\" id=\"Icb-zr-fWa\"/>\n                            <constraint firstItem=\"0Ub-qg-2xe\" firstAttribute=\"centerY\" secondItem=\"S3S-Oj-5AN\" secondAttribute=\"centerY\" id=\"geV-jF-Mjw\"/>\n                        </constraints>\n                        <viewLayoutGuide key=\"safeArea\" id=\"2BE-c3-nQJ\"/>\n                    </view>\n                    <extendedEdge key=\"edgesForExtendedLayout\"/>\n                    <freeformSimulatedSizeMetrics key=\"simulatedDestinationMetrics\"/>\n                    <size key=\"freeformSize\" width=\"320\" height=\"37\"/>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"vXp-U4-Rya\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"139\" y=\"138\"/>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "ClipboardReader/ClipboardReader.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.application-groups</key>\n\t<array>\n\t\t<string>group.com.rileytestut.Clip</string>\n\t</array>\n</dict>\n</plist>\n"
  },
  {
    "path": "ClipboardReader/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>ALTAppGroups</key>\n\t<array>\n\t\t<string>group.com.rileytestut.Clip</string>\n\t</array>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>ClipboardReader</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(MARKETING_VERSION)</string>\n\t<key>CFBundleVersion</key>\n\t<string>1</string>\n\t<key>NSExtension</key>\n\t<dict>\n\t\t<key>NSExtensionAttributes</key>\n\t\t<dict>\n\t\t\t<key>UNNotificationExtensionCategory</key>\n\t\t\t<string>ClipboardReader</string>\n\t\t\t<key>UNNotificationExtensionDefaultContentHidden</key>\n\t\t\t<true/>\n\t\t\t<key>UNNotificationExtensionInitialContentSizeRatio</key>\n\t\t\t<real>0.14999999999999999</real>\n\t\t</dict>\n\t\t<key>NSExtensionMainStoryboard</key>\n\t\t<string>MainInterface</string>\n\t\t<key>NSExtensionPointIdentifier</key>\n\t\t<string>com.apple.usernotifications.content-extension</string>\n\t</dict>\n</dict>\n</plist>\n"
  },
  {
    "path": "ClipboardReader/NotificationViewController.swift",
    "content": "//\n//  NotificationViewController.swift\n//  NotificationClipboard\n//\n//  Created by Riley Testut on 12/2/19.\n//  Copyright © 2019 Riley Testut. All rights reserved.\n//\n\nimport UIKit\nimport UserNotifications\nimport UserNotificationsUI\nimport CoreLocation\n\nimport ClipKit\nimport Roxas\n\nclass NotificationViewController: UIViewController, UNNotificationContentExtension\n{\n    @IBOutlet private var activityIndicatorView: UIActivityIndicatorView!\n    \n    private let preparationDispatchGroup = DispatchGroup()\n    private var databaseError: Swift.Error?\n    \n    required init?(coder: NSCoder)\n    {\n        super.init(coder: coder)\n        \n        UserDefaults.shared.registerAppDefaults()\n        \n        self.preparationDispatchGroup.enter()\n\n        DatabaseManager.shared.persistentContainer.shouldAddStoresAsynchronously = true\n        DatabaseManager.shared.prepare { (result) in\n            switch result\n            {\n            case .failure(let error): self.databaseError = error\n            case .success: break\n            }\n\n            self.preparationDispatchGroup.leave()\n        }\n    }\n    \n    func didReceive(_ notification: UNNotification)\n    {\n        guard notification.request.content.userInfo[UNNotification.errorMessageUserInfoKey] == nil else {\n            // This is an error notification, so just dismiss it if user interacts.\n            self.extensionContext?.dismissNotificationContentExtension()\n            return\n        }\n        \n        if let error = self.databaseError\n        {\n            self.finish(.failure(error))\n        }\n        else\n        {\n            let location: CLLocation?\n            \n            if let latitude = notification.request.content.userInfo[UNNotification.latitudeUserInfoKey] as? Double,\n               let longitude = notification.request.content.userInfo[UNNotification.longitudeUserInfoKey] as? Double\n            {\n                location = CLLocation(latitude: latitude, longitude: longitude)\n            }\n            else\n            {\n                location = nil\n            }\n            \n            self.preparationDispatchGroup.notify(queue: .main) {\n                DatabaseManager.shared.savePasteboard(location: location) { (result) in\n                    self.finish(result)\n                }\n            }\n        }\n    }\n}\n\nprivate extension NotificationViewController\n{\n    func finish(_ result: Result<Void, Swift.Error>)\n    {\n        // Can't dismiss extension too early or else we can't read clipboard.\n        self.extensionContext?.dismissNotificationContentExtension()\n        \n        switch result\n        {\n        case .success: break\n        case .failure(PasteboardError.duplicateItem): break\n        case .failure(let error):\n            let content = UNMutableNotificationContent()\n            content.title = NSLocalizedString(\"Failed to Save Clipboard\", comment: \"\")\n            content.body = error.localizedDescription\n            content.categoryIdentifier = UNNotificationCategory.clipboardReaderIdentifier\n            content.userInfo[UNNotification.errorMessageUserInfoKey] = error.localizedDescription\n            \n            let request = UNNotificationRequest(identifier: \"SaveError\", content: content, trigger: nil)\n            UNUserNotificationCenter.current().add(request) { (error) in\n                if let error = error {\n                    print(error)\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "README.md",
    "content": "# Clip\n\n> Clip is a clipboard manager for iOS that can monitor your clipboard indefinitely in the background — no jailbreak required.\n\n[![Swift Version](https://img.shields.io/badge/swift-5.0-orange.svg)](https://swift.org/)\n[![License: Unlicense](https://img.shields.io/badge/license-Unlicense-blue.svg)](http://unlicense.org/)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)\n\nClip is a simple clipboard manager for iOS. Unlike other clipboard managers available in the App Store, Clip is able to monitor your clipboard indefinitely in the background. This is accomplished through a combination of hacks and workarounds, none of which would pass App Store review. For that reason, Clip is only available to download through [AltStore](https://github.com/rileytestut/AltStore) — my alternative app store for non-jailbroken devices — or by compiling the source code yourself.\n\n<p align=\"middle\">\n<img title=\"Clip Main Screen\" src=\"https://user-images.githubusercontent.com/705880/63391950-34286600-c37a-11e9-965f-832efe3da507.png\" width=\"375\"> \n</p>\n\n<p></p>\n\n## Features\n- Runs silently in the background, monitoring your clipboard the whole time.\n- Save text, URLs, and images copied to the clipboard.\n- Copy, delete, and share clippings.\n- Customizable history limit.\n\n## Requirements\n- Xcode 11\n- iOS 13\n- Swift 5+\n\n## Project Overview\n\nAll things considered, Clip is a very simple app. The core app target can be mentally divided up into UI and logic, while each additional target serves a specific role.\n\n### App UI\nThe entire UI is implemented with just two view controllers:\n\n**HistoryViewController**  \nThe main screen of Clip. A relatively straightforward `UITableViewController` subclass that fetches recent clippings from Clip’s persistent store and displays them in a table view.\n\n**SettingsViewController**  \nThe settings screen for Clip. Another `UITableViewController` subclass that displays all Clip settings in a list, but is presented as a popover due to limited number of settings.\n\n### App Logic\nThe app logic for Clip is relatively straightforward. Most is self-explanatory, but there are two classes that serve particularly important roles:\n\n**PasteboardMonitor**  \nAs you might have guessed from the name, this class is in charge of listening for changes to the clipboard. Since `UIPasteboardChangedNotification` is only received when the app is in the foreground, this class uses the private `Pasteboard.framework` to start sending system-wide Darwin notifications whenever the clipboard’s contents change. Once a change is detected, PasteboardMonitor presents a local notification that can be expanded by the user to save their clipboard to Clip.\n\n**ApplicationMonitor**  \nThis class manages the lifecycle of Clip. Specifically, it is in charge of playing a silent audio clip on loop so Clip can run indefinitely in the background, as well as presenting a local notification whenever Clip stops running (for whatever reason).\n\n### ClipKit\nClipKit is a shared framework that includes common code between Clip, ClipboardReader, and ClipBoard. Notably, it contains all model + Core Data logic, so that Clip and each app extension can access the same persistent store with all clippings.\n\n### ClipboardReader\nClipboardReader is a Notification Content app extension used to read the clipboard while Clip is running in the background. When Clip detects a change to the clipboard, it will present a local notification. If this notification is expanded, ClipboardReader will be launched and save the contents of the clipboard to disk before dismissing the now-expanded notification.\n\n<p align=\"middle\">\n<img title=\"ClipboardReader\" src=\"https://user-images.githubusercontent.com/705880/84835557-b4dedf80-afe8-11ea-94c5-52731b3ac15c.gif\"><br>\n<em>ClipboardReader in action.</em>\n</p>\n\n### ClipBoard\nClipBoard is a Custom Keyboard app extension that provides quick access to your recent clippings when editing text. This feature is still being worked on, so it is only available in beta versions of Clip for now.\n\n### Roxas\nRoxas is my internal framework used across all my iOS projects, developed to simplify a variety of common tasks used in iOS development. For more info, check the [Roxas repo](https://github.com/rileytestut/roxas).\n\n## Compilation Instructions\nClip is very straightforward to compile and run if you're already an iOS developer. To compile Clip:\n\n1. Clone the repository \n\t``` \n\thttps://github.com/rileytestut/Clip.git\n\t```\n2. Update submodules: \n\t```\n\tcd Clip \n\tgit submodule update --init --recursive\n\t```\n3. Open `Clip.xcworkspace` and select the Clip project in the project navigator. On the `Signing & Capabilities` tab, change the team from `Yvette Testut` to your own account.\n4. Build + run app! 🎉\n\n## Licensing\n\nUnlike my other projects, Clip uses no 3rd party dependencies. This gives me complete freedom to choose the license I want, so I’m choosing to **release the complete Clip source code into the public domain**. You can view the complete “unlicense” [here](https://github.com/rileytestut/Clip/blob/master/UNLICENSE), but the gist is:\n\n> Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means.\n\n## Contact Me\n\n* Email: riley@rileytestut.com\n* Twitter: [@rileytestut](https://twitter.com/rileytestut)\n"
  },
  {
    "path": "UNLICENSE",
    "content": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, compile, sell, or\ndistribute this software, either in source code form or as a compiled\nbinary, for any purpose, commercial or non-commercial, and by any\nmeans.\n\nIn jurisdictions that recognize copyright laws, the author or authors\nof this software dedicate any and all copyright interest in the\nsoftware to the public domain. We make this dedication for the benefit\nof the public at large and to the detriment of our heirs and\nsuccessors. We intend this dedication to be an overt act of\nrelinquishment in perpetuity of all present and future rights to this\nsoftware under copyright law.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\nIN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR\nOTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,\nARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\nOTHER DEALINGS IN THE SOFTWARE.\n\nFor more information, please refer to <https://unlicense.org>\n"
  }
]