Showing preview only (249K chars total). Download the full file or copy to clipboard to get everything.
Repository: rileytestut/Clip
Branch: main
Commit: fc0c861b82e1
Files: 64
Total size: 230.4 KB
Directory structure:
gitextract_7pamx728/
├── .gitignore
├── .gitmodules
├── Clip/
│ ├── AppDelegate.swift
│ ├── ApplicationMonitor.swift
│ ├── Base.lproj/
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── Clip.entitlements
│ ├── Components/
│ │ ├── ForwardingNavigationController.swift
│ │ └── GradientView.swift
│ ├── Extensions/
│ │ ├── PasteboardItem+ActivityItemSource.swift
│ │ └── UIDevice+Vibration.swift
│ ├── History/
│ │ ├── ClippingTableViewCell.swift
│ │ ├── ClippingTableViewCell.xib
│ │ └── HistoryViewController.swift
│ ├── Info.plist
│ ├── Map View/
│ │ ├── ClippingSheet.swift
│ │ └── ClippingsMapView.swift
│ ├── Pasteboard/
│ │ ├── LocationManager.swift
│ │ └── PasteboardMonitor.swift
│ ├── Resources/
│ │ └── Assets.xcassets/
│ │ ├── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ └── Settings.imageset/
│ │ └── Contents.json
│ ├── SceneDelegate.swift
│ ├── Settings/
│ │ └── SettingsViewController.swift
│ └── Types/
│ └── PublishedPipeline.swift
├── Clip.xcodeproj/
│ ├── project.pbxproj
│ ├── project.xcworkspace/
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata/
│ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata/
│ └── xcschemes/
│ ├── Clip.xcscheme
│ ├── ClipKit.xcscheme
│ └── ClipboardReader.xcscheme
├── ClipBoard/
│ ├── ClipBoard.entitlements
│ ├── Info.plist
│ └── KeyboardViewController.swift
├── ClipKit/
│ ├── ClipKit.h
│ ├── Database/
│ │ ├── DatabaseManager.swift
│ │ └── Model/
│ │ ├── Model.xcdatamodeld/
│ │ │ └── Model.xcdatamodel/
│ │ │ └── contents
│ │ ├── PasteboardItem.swift
│ │ └── PasteboardItemRepresentation.swift
│ ├── Extensions/
│ │ ├── Bundle+AppGroups.swift
│ │ ├── CFNotification+PasteboardListener.swift
│ │ ├── Int+Bytes.swift
│ │ ├── Result+Conveniences.swift
│ │ ├── UIColor+Clip.swift
│ │ ├── UIInputView+Click.swift
│ │ ├── UIPasteboard+PasteboardItem.swift
│ │ ├── UNNotification+Keys.swift
│ │ └── UserDefaults+App.swift
│ ├── Info.plist
│ ├── Resources/
│ │ └── Colors.xcassets/
│ │ ├── Contents.json
│ │ ├── LightPink.colorset/
│ │ │ └── Contents.json
│ │ └── Pink.colorset/
│ │ └── Contents.json
│ ├── SwiftUI/
│ │ ├── Blur.swift
│ │ ├── ClippingCell.swift
│ │ ├── Keyboard.swift
│ │ ├── Preview.swift
│ │ └── SwitchKeyboardButton.swift
│ └── UTI.swift
├── ClipboardReader/
│ ├── Base.lproj/
│ │ └── MainInterface.storyboard
│ ├── ClipboardReader.entitlements
│ ├── Info.plist
│ └── NotificationViewController.swift
├── README.md
└── UNLICENSE
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# macOS
#
*.DS_Store
# Xcode
#
## Build generated
build/
DerivedData
## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
## Other
*.xccheckout
*.moved-aside
*.xcuserstate
*.xcscmblueprint
## Obj-C/Swift specific
*.hmap
================================================
FILE: .gitmodules
================================================
[submodule "Dependencies/Roxas"]
path = Dependencies/Roxas
url = https://github.com/rileytestut/Roxas.git
================================================
FILE: Clip/AppDelegate.swift
================================================
//
// AppDelegate.swift
// Clip
//
// Created by Riley Testut on 6/10/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import UIKit
import UserNotifications
import ClipKit
import Roxas
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
{
// Override point for customization after application launch.
print(RoxasVersionNumber)
self.window?.tintColor = .clipPink
UserDefaults.shared.registerAppDefaults()
func printError<T>(from result: Result<T, Error>, title: String)
{
guard let error = result.error else { return }
print(title, error)
}
DatabaseManager.shared.prepare() { printError(from: $0, title: "Database Error:") }
ApplicationMonitor.shared.start()
self.registerForNotifications()
return true
}
func applicationWillResignActive(_ application: UIApplication) {
// 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.
// Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
}
func applicationDidEnterBackground(_ application: UIApplication)
{
#if targetEnvironment(simulator)
// Audio extension hack to access pasteboard doesn't work in simulator, so for testing just start background task.
RSTBeginBackgroundTask("com.rileytestut.Clip.simulatorBackgroundTask")
#endif
DatabaseManager.shared.purge()
}
func applicationWillEnterForeground(_ application: UIApplication)
{
DatabaseManager.shared.refresh()
}
func applicationDidBecomeActive(_ application: UIApplication) {
// 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.
}
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
extension AppDelegate: UNUserNotificationCenterDelegate
{
private func registerForNotifications()
{
let category = UNNotificationCategory(identifier: UNNotificationCategory.clipboardReaderIdentifier, actions: [], intentIdentifiers: [])
UNUserNotificationCenter.current().setNotificationCategories([category])
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (success, error) in
}
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
{
completionHandler(.alert)
}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void)
{
guard response.notification.request.content.categoryIdentifier == UNNotificationCategory.clipboardReaderIdentifier else { return }
guard response.actionIdentifier == UNNotificationDefaultActionIdentifier else { return }
let location = ApplicationMonitor.shared.locationManager.location
// Delay until next run loop so UIPasteboard no longer returns nil items due to being in background.
DispatchQueue.main.async {
DatabaseManager.shared.savePasteboard(location: location) { (result) in
switch result
{
case .success: break
case .failure(PasteboardError.duplicateItem): break
case .failure(let error):
DispatchQueue.main.async {
let alertController = UIAlertController(title: NSLocalizedString("Failed to Save Clipboard", comment: ""), message: error.localizedDescription, preferredStyle: .alert)
alertController.addAction(.ok)
self.window?.rootViewController?.present(alertController, animated: true, completion: nil)
}
}
print("Save clipboard with result:", result)
completionHandler()
}
}
}
}
================================================
FILE: Clip/ApplicationMonitor.swift
================================================
//
// ApplicationMonitor.swift
// Clip
//
// Created by Riley Testut on 6/27/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import UIKit
import AVFoundation
import UserNotifications
import Combine
private enum UserNotification: String
{
case appStoppedRunning = "com.rileytestut.Clip.AppStoppedRunning"
}
private extension CFNotificationName
{
static let altstoreRequestAppState: CFNotificationName = CFNotificationName("com.altstore.RequestAppState.com.rileytestut.Clip" as CFString)
static let altstoreAppIsRunning: CFNotificationName = CFNotificationName("com.altstore.AppState.Running.com.rileytestut.Clip" as CFString)
}
private let ReceivedApplicationState: @convention(c) (CFNotificationCenter?, UnsafeMutableRawPointer?, CFNotificationName?, UnsafeRawPointer?, CFDictionary?) -> Void =
{ (center, observer, name, object, userInfo) in
ApplicationMonitor.shared.receivedApplicationStateRequest()
}
class ApplicationMonitor
{
static let shared = ApplicationMonitor()
let pasteboardMonitor = PasteboardMonitor()
let locationManager = LocationManager()
private(set) var isMonitoring = false
private var backgroundTaskID: UIBackgroundTaskIdentifier?
}
extension ApplicationMonitor
{
func start()
{
guard !self.isMonitoring else { return }
self.isMonitoring = true
self.cancelApplicationQuitNotification() // Cancel any notifications from a previous launch.
self.scheduleApplicationQuitNotification()
self.pasteboardMonitor.start() { (result) in
switch result
{
case .success:
self.locationManager.start()
self.registerForNotifications()
case .failure(let error):
self.isMonitoring = false
self.sendNotification(title: NSLocalizedString("Failed to Monitor Clipboard", comment: ""), message: error.localizedDescription)
}
}
}
}
private extension ApplicationMonitor
{
func registerForNotifications()
{
let center = CFNotificationCenterGetDarwinNotifyCenter()
CFNotificationCenterAddObserver(center, nil, ReceivedApplicationState, CFNotificationName.altstoreRequestAppState.rawValue, nil, .deliverImmediately)
}
func scheduleApplicationQuitNotification()
{
let delay = 5 as TimeInterval
let content = UNMutableNotificationContent()
content.title = NSLocalizedString("App Stopped Running", comment: "")
content.body = NSLocalizedString("Tap this notification to resume monitoring your clipboard.", comment: "")
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: delay + 1, repeats: false)
let request = UNNotificationRequest(identifier: UserNotification.appStoppedRunning.rawValue, content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request)
DispatchQueue.global().asyncAfter(deadline: .now() + delay) {
// If app is still running at this point, we schedule another notification with same identifier.
// This prevents the currently scheduled notification from displaying, and starts another countdown timer.
self.scheduleApplicationQuitNotification()
}
}
func cancelApplicationQuitNotification()
{
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: [UserNotification.appStoppedRunning.rawValue])
}
func sendNotification(title: String, message: String)
{
let content = UNMutableNotificationContent()
content.title = title
content.body = message
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
UNUserNotificationCenter.current().add(request)
}
}
private extension ApplicationMonitor
{
func receivedApplicationStateRequest()
{
guard UIApplication.shared.applicationState != .background else { return }
let center = CFNotificationCenterGetDarwinNotifyCenter()
CFNotificationCenterPostNotification(center!, CFNotificationName(CFNotificationName.altstoreAppIsRunning.rawValue), nil, nil, true)
}
}
================================================
FILE: Clip/Base.lproj/LaunchScreen.storyboard
================================================
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>
================================================
FILE: Clip/Base.lproj/Main.storyboard
================================================
<?xml version="1.0" encoding="UTF-8"?>
<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">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Tab Bar Controller-->
<scene sceneID="G8a-px-lfx">
<objects>
<tabBarController id="BHr-Pz-XEL" sceneMemberID="viewController">
<tabBar key="tabBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="wXw-wp-kuj">
<rect key="frame" x="0.0" y="0.0" width="393" height="49"/>
<autoresizingMask key="autoresizingMask"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</tabBar>
<connections>
<segue destination="Z9q-vE-dSj" kind="relationship" relationship="viewControllers" id="PoF-Yv-BIK"/>
<segue destination="kpG-iJ-80X" kind="relationship" relationship="viewControllers" id="AMM-Dn-Cgz"/>
</connections>
</tabBarController>
<placeholder placeholderIdentifier="IBFirstResponder" id="MAl-QG-WpX" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="866" y="466"/>
</scene>
<!--List-->
<scene sceneID="u6D-9E-L2J">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="Z9q-vE-dSj" customClass="ForwardingNavigationController" customModule="Clip" customModuleProvider="target" sceneMemberID="viewController">
<tabBarItem key="tabBarItem" title="List" id="PKr-yJ-H3B"/>
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" barStyle="black" translucent="NO" largeTitles="YES" id="mZf-DU-Jdd">
<rect key="frame" x="0.0" y="20" width="375" height="96"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="fd0-Ck-KzX" kind="relationship" relationship="rootViewController" id="cU1-pG-cNA"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="sSM-ur-Bbi" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1889" y="116"/>
</scene>
<!--Clip-->
<scene sceneID="05K-82-Num">
<objects>
<tableViewController id="fd0-Ck-KzX" customClass="HistoryViewController" customModule="Clip" customModuleProvider="target" sceneMemberID="viewController">
<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">
<rect key="frame" x="0.0" y="0.0" width="375" height="551"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<sections/>
<connections>
<outlet property="dataSource" destination="fd0-Ck-KzX" id="ggy-BL-GCf"/>
<outlet property="delegate" destination="fd0-Ck-KzX" id="7Z3-Si-ipA"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Clip" id="KPM-hY-RHG">
<barButtonItem key="rightBarButtonItem" image="Settings" id="IaP-Ad-9rj">
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<connections>
<segue destination="beq-wr-z37" kind="popoverPresentation" identifier="showSettings" popoverAnchorBarButtonItem="IaP-Ad-9rj" id="fMU-im-zh7">
<popoverArrowDirection key="popoverArrowDirection" up="YES" down="YES" left="YES" right="YES"/>
</segue>
</connections>
</barButtonItem>
</navigationItem>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="NTc-eU-WIU" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="2684" y="115.59220389805098"/>
</scene>
<!--Map-->
<scene sceneID="8yd-00-beM">
<objects>
<hostingController id="kpG-iJ-80X" customClass="ClippingsMapViewController" customModule="Clip" customModuleProvider="target" sceneMemberID="viewController">
<tabBarItem key="tabBarItem" title="Map" id="zZh-lg-wou"/>
<navigationItem key="navigationItem" id="JSY-Sx-Vfe"/>
</hostingController>
<placeholder placeholderIdentifier="IBFirstResponder" id="1yu-Vg-29s" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1889" y="841"/>
</scene>
<!--Settings-->
<scene sceneID="eeF-sn-zd0">
<objects>
<tableViewController id="xUa-Vn-Yd7" customClass="SettingsViewController" customModule="Clip" customModuleProvider="target" sceneMemberID="viewController">
<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">
<rect key="frame" x="0.0" y="0.0" width="375" height="647"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<sections>
<tableViewSection headerTitle="Keep Last…" id="ZjM-l8-6S4">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="checkmark" indentationWidth="10" textLabel="qhf-EU-e1i" style="IBUITableViewCellStyleDefault" id="bNo-Xt-G8X">
<rect key="frame" x="16" y="55.5" width="343" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="bNo-Xt-G8X" id="F7x-pq-uZ0">
<rect key="frame" x="0.0" y="0.0" width="303" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="10 items" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="qhf-EU-e1i">
<rect key="frame" x="16" y="0.0" width="279" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="GzS-pv-T1w" style="IBUITableViewCellStyleDefault" id="4Is-z7-ZoX">
<rect key="frame" x="16" y="99" width="343" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="4Is-z7-ZoX" id="gQq-EX-uys">
<rect key="frame" x="0.0" y="0.0" width="343" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="25 items" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="GzS-pv-T1w">
<rect key="frame" x="16" y="0.0" width="311" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="8Oa-Oe-o26" style="IBUITableViewCellStyleDefault" id="vHz-Pg-Ux0">
<rect key="frame" x="16" y="142.5" width="343" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="vHz-Pg-Ux0" id="Drd-Is-Cyc">
<rect key="frame" x="0.0" y="0.0" width="343" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="50 items" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="8Oa-Oe-o26">
<rect key="frame" x="16" y="0.0" width="311" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" textLabel="vAN-CH-YFA" style="IBUITableViewCellStyleDefault" id="wqN-bH-xb8">
<rect key="frame" x="16" y="186" width="343" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="wqN-bH-xb8" id="pJK-hK-lUa">
<rect key="frame" x="0.0" y="0.0" width="343" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="100 items" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="vAN-CH-YFA">
<rect key="frame" x="16" y="0.0" width="311" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
<tableViewSection id="1k6-PD-u1u">
<cells>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" id="v6L-1Q-SY8">
<rect key="frame" x="16" y="265.5" width="343" height="44.5"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="v6L-1Q-SY8" id="918-t4-tuC">
<rect key="frame" x="0.0" y="0.0" width="343" height="44.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" distribution="equalSpacing" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="c8N-qF-oa2">
<rect key="frame" x="16" y="0.0" width="311" height="44.5"/>
<subviews>
<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">
<rect key="frame" x="0.0" y="12" width="157" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="L7B-Cw-is3">
<rect key="frame" x="262" y="7" width="51" height="31"/>
<connections>
<action selector="toggleShowLocationIcon:" destination="xUa-Vn-Yd7" eventType="primaryActionTriggered" id="TDD-d7-RwX"/>
</connections>
</switch>
</subviews>
<constraints>
<constraint firstAttribute="height" constant="44" id="WXO-l1-u2I"/>
</constraints>
</stackView>
</subviews>
<constraints>
<constraint firstAttribute="trailingMargin" secondItem="c8N-qF-oa2" secondAttribute="trailing" id="bvY-Pi-5oN"/>
<constraint firstAttribute="bottom" secondItem="c8N-qF-oa2" secondAttribute="bottom" id="eda-9O-roS"/>
<constraint firstItem="c8N-qF-oa2" firstAttribute="top" secondItem="918-t4-tuC" secondAttribute="top" id="ov6-hC-Lfm"/>
<constraint firstItem="c8N-qF-oa2" firstAttribute="leading" secondItem="918-t4-tuC" secondAttribute="leadingMargin" id="rD7-x9-bDs"/>
</constraints>
</tableViewCellContentView>
</tableViewCell>
</cells>
</tableViewSection>
</sections>
<connections>
<outlet property="dataSource" destination="xUa-Vn-Yd7" id="yxE-Kd-hqj"/>
<outlet property="delegate" destination="xUa-Vn-Yd7" id="hEA-vX-Riy"/>
</connections>
</tableView>
<navigationItem key="navigationItem" title="Settings" id="HV4-i9-lJF"/>
<connections>
<outlet property="showLocationIconSwitch" destination="L7B-Cw-is3" id="FJc-lY-5r9"/>
</connections>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="ohN-Gn-92v" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="4353" y="-621"/>
</scene>
<!--Navigation Controller-->
<scene sceneID="hWp-2p-Rtv">
<objects>
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="beq-wr-z37" sceneMemberID="viewController">
<toolbarItems/>
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="FwF-Od-Ej5">
<rect key="frame" x="0.0" y="0.0" width="375" height="56"/>
<autoresizingMask key="autoresizingMask"/>
</navigationBar>
<nil name="viewControllers"/>
<connections>
<segue destination="xUa-Vn-Yd7" kind="relationship" relationship="rootViewController" id="YfP-Ns-wyi"/>
</connections>
</navigationController>
<placeholder placeholderIdentifier="IBFirstResponder" id="jel-yU-YIB" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3480.8000000000002" y="-621.13943028485767"/>
</scene>
</scenes>
<resources>
<image name="Settings" width="22" height="22"/>
</resources>
</document>
================================================
FILE: Clip/Clip.entitlements
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.rileytestut.Clip</string>
</array>
<key>inter-app-audio</key>
<true/>
</dict>
</plist>
================================================
FILE: Clip/Components/ForwardingNavigationController.swift
================================================
//
// ForwardingNavigationController.swift
// AltStore
//
// Created by Riley Testut on 10/24/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import UIKit
class ForwardingNavigationController: UINavigationController
{
override var childForStatusBarStyle: UIViewController? {
return self.topViewController
}
override var childForStatusBarHidden: UIViewController? {
return self.topViewController
}
}
================================================
FILE: Clip/Components/GradientView.swift
================================================
//
// GradientView.swift
// Clip
//
// Created by Riley Testut on 7/27/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import UIKit
class GradientView: UIView
{
var colors: [UIColor] = [] {
didSet {
self.gradientLayer.colors = self.colors.map { $0.cgColor }
}
}
override class var layerClass: AnyClass {
return CAGradientLayer.self
}
private var gradientLayer: CAGradientLayer {
return self.layer as! CAGradientLayer
}
override init(frame: CGRect)
{
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
================================================
FILE: Clip/Extensions/PasteboardItem+ActivityItemSource.swift
================================================
//
// PasteboardItem+ActivityItemSource.swift
// Clip
//
// Created by Riley Testut on 6/14/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import MobileCoreServices
import ClipKit
extension PasteboardItem: UIActivityItemSource
{
public func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any
{
guard let representation = self.preferredRepresentation else { return NSNull() }
switch representation.type
{
case .text: return ""
case .attributedText: return NSAttributedString(string: "")
case .url: return URL(string: "http://apple.com")!
case .image: return Data()
}
}
public func activityViewController(_ activityViewController: UIActivityViewController, dataTypeIdentifierForActivityType activityType: UIActivity.ActivityType?) -> String
{
return self.preferredRepresentation?.uti ?? kUTTypeImage as String
}
public func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any?
{
guard let representation = self.preferredRepresentation else { return nil }
if activityType == UIActivity.ActivityType.copyToPasteboard
{
let itemProvider = NSItemProvider()
for representation in self.representations
{
itemProvider.registerItem(forTypeIdentifier: representation.uti) { (completionHandler, expectedClass, options) in
completionHandler?(representation.pasteboardValue as? NSSecureCoding, nil)
}
}
// Add our own UTI representation to distinguish from other copies.
itemProvider.registerItem(forTypeIdentifier: UTI.clipping) { (completionHandler, expectedClass, options) in
completionHandler?([:] as NSDictionary, nil)
}
return itemProvider
}
else
{
// _Don't_ return pasteboard value, instead return the logical object value.
// This way, inline data such as text won't accidentally appear as attachments in some share extensions.
return representation.value
}
}
}
================================================
FILE: Clip/Extensions/UIDevice+Vibration.swift
================================================
//
// UIDevice+Vibration.swift
// DeltaCore
//
// Created by Riley Testut on 11/28/16.
// Copyright © 2016 Riley Testut. All rights reserved.
//
import UIKit
import AudioToolbox
public extension UIDevice
{
enum FeedbackSupportLevel: Int
{
case unsupported // iPhone 6 or earlier, or non-iPhone (e.g. iPad)
case basic // iPhone 6s
case feedbackGenerator // iPhone 7 and later
}
}
public extension UIDevice
{
var feedbackSupportLevel: FeedbackSupportLevel
{
guard let rawValue = self.value(forKey: "_feedbackSupportLevel") as? Int else { return .unsupported }
let feedbackSupportLevel = FeedbackSupportLevel(rawValue: rawValue)
return feedbackSupportLevel ?? .feedbackGenerator // We'll assume raw values greater than 2 still support UIFeedbackGenerator ¯\_(ツ)_/¯
}
var isVibrationSupported: Bool {
#if (arch(i386) || arch(x86_64))
// Return false for iOS simulator
return false
#else
// All iPhones support some form of vibration, and potentially future non-iPhone devices will support taptic feedback
return (self.model.hasPrefix("iPhone")) || self.feedbackSupportLevel != .unsupported
#endif
}
func vibrate()
{
guard self.isVibrationSupported else { return }
switch self.feedbackSupportLevel
{
case .unsupported: break
case .basic, .feedbackGenerator: AudioServicesPlaySystemSound(1519) // "peek" vibration
}
}
}
================================================
FILE: Clip/History/ClippingTableViewCell.swift
================================================
//
// ClippingTableViewCell.swift
// Clip
//
// Created by Riley Testut on 6/13/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import UIKit
@objc(ClippingTableViewCell)
class ClippingTableViewCell: UITableViewCell
{
@IBOutlet var clippingView: UIView!
@IBOutlet var titleLabel: UILabel!
@IBOutlet var dateLabel: UILabel!
@IBOutlet var contentLabel: UILabel!
@IBOutlet var contentImageView: UIImageView!
@IBOutlet var locationButton: UIButton!
@IBOutlet var bottomConstraint: NSLayoutConstraint!
override func awakeFromNib()
{
super.awakeFromNib()
self.clippingView.layer.cornerRadius = 10
self.clippingView.layer.masksToBounds = true
self.contentImageView.layer.cornerRadius = 10
self.contentImageView.layer.masksToBounds = true
}
}
================================================
FILE: Clip/History/ClippingTableViewCell.xib
================================================
<?xml version="1.0" encoding="UTF-8"?>
<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">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
<capability name="Image references" minToolsVersion="12.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="none" indentationWidth="10" reuseIdentifier="Cell" rowHeight="120" id="Y0r-KE-oyf" customClass="ClippingTableViewCell">
<rect key="frame" x="0.0" y="0.0" width="320" height="400"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Y0r-KE-oyf" id="DfT-qA-KZm">
<rect key="frame" x="0.0" y="0.0" width="320" height="400"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3fg-c6-MSF">
<rect key="frame" x="16" y="11.5" width="288" height="377.5"/>
<subviews>
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QV9-Vd-SCE">
<rect key="frame" x="0.0" y="0.0" width="288" height="377.5"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="Kkk-LW-I0M">
<rect key="frame" x="0.0" y="0.0" width="288" height="377.5"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</view>
<blurEffect style="extraLight"/>
</visualEffectView>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="FsE-hm-xaq">
<rect key="frame" x="12" y="12" width="264" height="353.5"/>
<subviews>
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="8Ov-eX-yBP">
<rect key="frame" x="0.0" y="0.0" width="264" height="55.5"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="d93-97-hlG">
<rect key="frame" x="0.0" y="0.0" width="264" height="55.5"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" verticalCompressionResistancePriority="1000" distribution="equalSpacing" translatesAutoresizingMaskIntoConstraints="NO" id="yda-cG-JjY">
<rect key="frame" x="0.0" y="0.0" width="264" height="55.5"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="WG8-q4-I7L">
<rect key="frame" x="0.0" y="0.0" width="87" height="55.5"/>
<subviews>
<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">
<rect key="frame" x="0.0" y="0.0" width="49" height="55.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleHeadline"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="trailing" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="q7j-Nd-a75">
<rect key="frame" x="57" y="0.0" width="30" height="55.5"/>
<constraints>
<constraint firstAttribute="width" constant="30" id="7de-mh-FyQ"/>
</constraints>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="plain">
<imageReference key="image" image="location.circle.fill" catalog="system" symbolScale="medium"/>
</buttonConfiguration>
</button>
</subviews>
</stackView>
<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">
<rect key="frame" x="202" y="0.0" width="62" height="55.5"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleCaption1"/>
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</stackView>
</subviews>
<constraints>
<constraint firstItem="yda-cG-JjY" firstAttribute="leading" secondItem="d93-97-hlG" secondAttribute="leading" id="EPt-Lm-sq4"/>
<constraint firstAttribute="bottom" secondItem="yda-cG-JjY" secondAttribute="bottom" id="HGN-cR-IAW"/>
<constraint firstAttribute="trailing" secondItem="yda-cG-JjY" secondAttribute="trailing" id="VkD-pJ-Lk9"/>
<constraint firstItem="yda-cG-JjY" firstAttribute="top" secondItem="d93-97-hlG" secondAttribute="top" id="ySK-Bk-v31"/>
</constraints>
</view>
<vibrancyEffect>
<blurEffect style="extraLight"/>
</vibrancyEffect>
</visualEffectView>
<stackView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="249" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="Bdm-xD-0S9">
<rect key="frame" x="0.0" y="63.5" width="264" height="18"/>
<subviews>
<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">
<rect key="frame" x="0.0" y="0.0" width="264" height="18"/>
<fontDescription key="fontDescription" style="UICTFontTextStyleSubhead"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</stackView>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="A9I-Tq-Vqw">
<rect key="frame" x="0.0" y="89.5" width="264" height="264"/>
<constraints>
<constraint firstAttribute="width" secondItem="A9I-Tq-Vqw" secondAttribute="height" priority="750" id="zZQ-fM-Uhm"/>
</constraints>
</imageView>
</subviews>
</stackView>
</subviews>
<constraints>
<constraint firstItem="FsE-hm-xaq" firstAttribute="leading" secondItem="3fg-c6-MSF" secondAttribute="leadingMargin" id="CG3-U5-r1W"/>
<constraint firstItem="QV9-Vd-SCE" firstAttribute="top" secondItem="3fg-c6-MSF" secondAttribute="top" id="ctH-Vw-i8Q"/>
<constraint firstItem="FsE-hm-xaq" firstAttribute="top" secondItem="3fg-c6-MSF" secondAttribute="topMargin" id="dyA-Io-Xbt"/>
<constraint firstAttribute="bottom" secondItem="QV9-Vd-SCE" secondAttribute="bottom" id="erp-NN-Eyi"/>
<constraint firstAttribute="bottomMargin" secondItem="FsE-hm-xaq" secondAttribute="bottom" id="sOz-vw-Nax"/>
<constraint firstItem="QV9-Vd-SCE" firstAttribute="leading" secondItem="3fg-c6-MSF" secondAttribute="leading" id="x7p-sz-27U"/>
<constraint firstAttribute="trailing" secondItem="QV9-Vd-SCE" secondAttribute="trailing" id="yHS-Ie-9XV"/>
<constraint firstAttribute="trailingMargin" secondItem="FsE-hm-xaq" secondAttribute="trailing" id="zHj-dc-JPb"/>
</constraints>
<edgeInsets key="layoutMargins" top="12" left="12" bottom="12" right="12"/>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="3fg-c6-MSF" firstAttribute="bottom" secondItem="DfT-qA-KZm" secondAttribute="bottomMargin" id="LRr-HV-NWu"/>
<constraint firstItem="3fg-c6-MSF" firstAttribute="top" secondItem="DfT-qA-KZm" secondAttribute="topMargin" constant="0.5" id="QXR-pu-N1i"/>
<constraint firstAttribute="trailingMargin" secondItem="3fg-c6-MSF" secondAttribute="trailing" id="hmx-Ud-7ia"/>
<constraint firstItem="3fg-c6-MSF" firstAttribute="leading" secondItem="DfT-qA-KZm" secondAttribute="leadingMargin" id="xnv-np-5co"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<connections>
<outlet property="bottomConstraint" destination="LRr-HV-NWu" id="biA-rf-uaC"/>
<outlet property="clippingView" destination="3fg-c6-MSF" id="NJy-JL-YgY"/>
<outlet property="contentImageView" destination="A9I-Tq-Vqw" id="9Tg-3N-7pg"/>
<outlet property="contentLabel" destination="4Ic-XD-ZD7" id="woG-Jk-cKt"/>
<outlet property="dateLabel" destination="CHm-ya-B0P" id="y9q-KA-fPp"/>
<outlet property="locationButton" destination="q7j-Nd-a75" id="bzV-bQ-Zin"/>
<outlet property="titleLabel" destination="c2s-SZ-q8A" id="yge-vn-euB"/>
</connections>
<point key="canvasLocation" x="-271" y="-11"/>
</tableViewCell>
</objects>
<resources>
<image name="location.circle.fill" catalog="system" width="128" height="123"/>
</resources>
</document>
================================================
FILE: Clip/History/HistoryViewController.swift
================================================
//
// HistoryViewController.swift
// Clip
//
// Created by Riley Testut on 6/10/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import UIKit
import MobileCoreServices
import Combine
import CoreLocation
import Contacts
import ClipKit
import Roxas
class HistoryViewController: UITableViewController
{
private var dataSource: RSTFetchedResultsTableViewPrefetchingDataSource<PasteboardItem, UIImage>!
private let _undoManager = UndoManager()
private var prototypeCell: ClippingTableViewCell!
private var navigationBarMaskView: UIView!
private var navigationBarGradientView: GradientView!
private var didAddInitialLayoutConstraints = false
private var cachedHeights = [NSManagedObjectID: CGFloat]()
private weak var selectedItem: PasteboardItem?
private var updateTimer: Timer?
private var fetchLimitSettingObservation: NSKeyValueObservation?
private var cancellables = Set<AnyCancellable>()
private lazy var dateComponentsFormatter: DateComponentsFormatter = {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .abbreviated
formatter.maximumUnitCount = 1
formatter.allowedUnits = [.second, .minute, .hour, .day]
return formatter
}()
override var undoManager: UndoManager? {
return _undoManager
}
override var canBecomeFirstResponder: Bool {
return true
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
override func viewDidLoad()
{
super.viewDidLoad()
self.subscribe()
self.extendedLayoutIncludesOpaqueBars = true
self.tableView.backgroundView = self.makeGradientView()
self.updateDataSource()
self.tableView.contentInset.top = 8
self.tableView.estimatedRowHeight = 0
self.prototypeCell = ClippingTableViewCell.instantiate(with: ClippingTableViewCell.nib!)
self.tableView.register(ClippingTableViewCell.nib, forCellReuseIdentifier: RSTCellContentGenericCellIdentifier)
DatabaseManager.shared.persistentContainer.viewContext.undoManager = self.undoManager
NotificationCenter.default.addObserver(self, selector: #selector(HistoryViewController.didEnterBackground(_:)), name: UIApplication.didEnterBackgroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(HistoryViewController.willEnterForeground(_:)), name: UIApplication.willEnterForegroundNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(HistoryViewController.settingsDidChange(_:)), name: SettingsViewController.settingsDidChangeNotification, object: nil)
self.fetchLimitSettingObservation = UserDefaults.shared.observe(\.historyLimit) { [weak self] (defaults, change) in
self?.updateDataSource()
}
self.navigationBarGradientView = self.makeGradientView()
self.navigationBarGradientView.translatesAutoresizingMaskIntoConstraints = false
self.navigationBarMaskView = UIView()
self.navigationBarMaskView.clipsToBounds = true
self.navigationBarMaskView.translatesAutoresizingMaskIntoConstraints = false
self.navigationBarMaskView.addSubview(self.navigationBarGradientView)
if let navigationBar = self.navigationController?.navigationBar
{
if #available(iOS 13.0, *)
{
let attributes: [NSAttributedString.Key: Any] = [.foregroundColor: UIColor.white]
let standardAppearance = navigationBar.standardAppearance
standardAppearance.configureWithOpaqueBackground()
standardAppearance.backgroundColor = .clipLightPink
standardAppearance.titleTextAttributes = attributes
standardAppearance.largeTitleTextAttributes = attributes
standardAppearance.shadowImage = nil
let scrollEdgeAppearance = navigationBar.scrollEdgeAppearance
scrollEdgeAppearance?.configureWithTransparentBackground()
scrollEdgeAppearance?.titleTextAttributes = attributes
scrollEdgeAppearance?.largeTitleTextAttributes = attributes
}
else
{
navigationBar.shadowImage = UIImage()
navigationBar.setBackgroundImage(nil, for: .default)
navigationBar.insertSubview(self.navigationBarMaskView, at: 1)
}
}
if let tabBar = self.navigationController?.tabBarController?.tabBar
{
let appearance = tabBar.standardAppearance
tabBar.scrollEdgeAppearance = appearance
}
self.navigationController?.tabBarItem.image = UIImage(systemName: "list.bullet")
self.startUpdating()
}
override func viewDidAppear(_ animated: Bool)
{
super.viewDidAppear(animated)
self.becomeFirstResponder()
}
override func viewWillDisappear(_ animated: Bool)
{
super.viewWillDisappear(animated)
self.resignFirstResponder()
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)
{
super.viewWillTransition(to: size, with: coordinator)
self.cachedHeights.removeAll()
}
override func viewDidLayoutSubviews()
{
super.viewDidLayoutSubviews()
if #available(iOS 13.0, *) {}
else
{
if let navigationBar = self.navigationController?.navigationBar, !self.didAddInitialLayoutConstraints
{
self.didAddInitialLayoutConstraints = true
NSLayoutConstraint.activate([self.navigationBarGradientView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
self.navigationBarGradientView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
self.navigationBarGradientView.topAnchor.constraint(equalTo: self.view.topAnchor),
self.navigationBarGradientView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor)])
NSLayoutConstraint.activate([self.navigationBarMaskView.leadingAnchor.constraint(equalTo: navigationBar.leadingAnchor),
self.navigationBarMaskView.trailingAnchor.constraint(equalTo: navigationBar.trailingAnchor),
self.navigationBarMaskView.topAnchor.constraint(equalTo: self.view.topAnchor),
self.navigationBarMaskView.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor)])
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
guard segue.identifier == "showSettings" else { return }
guard let sender = sender as? UIBarButtonItem else { return }
let navigationController = segue.destination as! UINavigationController
let settingsViewController = navigationController.viewControllers[0] as! SettingsViewController
settingsViewController.view.layoutIfNeeded()
navigationController.preferredContentSize = CGSize(width: 375, height: settingsViewController.tableView.contentSize.height)
navigationController.popoverPresentationController?.delegate = self
navigationController.popoverPresentationController?.barButtonItem = sender
}
}
extension HistoryViewController
{
override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool
{
let supportedActions = [#selector(UIResponderStandardEditActions.copy(_:)), #selector(UIResponderStandardEditActions.delete(_:)), #selector(HistoryViewController._share(_:))]
let isSupported = supportedActions.contains(action)
return isSupported
}
@objc override func copy(_ sender: Any?)
{
guard let item = self.selectedItem else { return }
UIPasteboard.general.copy(item)
}
@objc override func delete(_ sender: Any?)
{
guard let item = self.selectedItem else { return }
// Use the main view context so we can undo this operation easily.
// Saving a context can mess with its undo history, so we only save main context when we enter background.
item.isMarkedForDeletion = true
}
@objc func _share(_ sender: Any?)
{
guard let item = self.selectedItem, let indexPath = self.dataSource.fetchedResultsController.indexPath(forObject: item) else { return }
let cell = self.tableView.cellForRow(at: indexPath)
let activityViewController = UIActivityViewController(activityItems: [item], applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceItem = cell
self.present(activityViewController, animated: true, completion: nil)
}
}
private extension HistoryViewController
{
func subscribe()
{
//TODO: Uncomment once we can tell user to enable location for background execution.
//ApplicationMonitor.shared.locationManager.$status
// .receive(on: RunLoop.main)
// .compactMap { $0?.error }
// .sink { self.present($0) }
// .store(in: &self.cancellables)
}
func makeDataSource() -> RSTFetchedResultsTableViewPrefetchingDataSource<PasteboardItem, UIImage>
{
let fetchRequest = PasteboardItem.historyFetchRequest()
fetchRequest.returnsObjectsAsFaults = false
fetchRequest.relationshipKeyPathsForPrefetching = [#keyPath(PasteboardItem.preferredRepresentation)]
let dataSource = RSTFetchedResultsTableViewPrefetchingDataSource<PasteboardItem, UIImage>(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.persistentContainer.viewContext)
dataSource.cellConfigurationHandler = { [weak self] (cell, item, indexPath) in
let cell = cell as! ClippingTableViewCell
cell.contentLabel.isHidden = false
cell.contentImageView.isHidden = true
self?.updateDate(for: cell, item: item)
if let representation = item.preferredRepresentation
{
cell.titleLabel.text = representation.type.localizedName
switch representation.type
{
case .text: cell.contentLabel.text = representation.stringValue
case .attributedText: cell.contentLabel.text = representation.attributedStringValue?.string
case .url: cell.contentLabel.text = representation.urlValue?.absoluteString
case .image:
cell.contentLabel.isHidden = true
cell.contentImageView.isHidden = false
cell.contentImageView.isIndicatingActivity = true
}
}
else
{
cell.titleLabel.text = NSLocalizedString("Unknown", comment: "")
cell.contentLabel.isHidden = true
}
if UserDefaults.shared.showLocationIcon
{
cell.locationButton.isHidden = (item.location == nil)
cell.locationButton.addTarget(self, action: #selector(HistoryViewController.showLocation(_:)), for: .primaryActionTriggered)
}
else
{
cell.locationButton.isHidden = true
}
if indexPath.row < UserDefaults.shared.historyLimit.rawValue
{
cell.bottomConstraint.isActive = true
}
else
{
// Make it not active so we can collapse the cell to a height of 0 without auto layout errors.
cell.bottomConstraint.isActive = false
}
}
dataSource.prefetchHandler = { (item, indexPath, completionHandler) in
guard let representation = item.preferredRepresentation, representation.type == .image else { return nil }
return RSTBlockOperation() { (operation) in
guard let image = representation.imageValue?.resizing(toFill: CGSize(width: 500, height: 500)) else { return completionHandler(nil, nil) }
completionHandler(image, nil)
}
}
dataSource.prefetchCompletionHandler = { (cell, image, indexPath, error) in
DispatchQueue.main.async {
let cell = cell as! ClippingTableViewCell
if let image = image
{
cell.contentImageView.image = image
}
else
{
cell.contentImageView.image = nil
}
cell.contentImageView.isIndicatingActivity = false
}
}
let placeholderView = RSTPlaceholderView()
placeholderView.textLabel.text = NSLocalizedString("No Clippings", comment: "")
placeholderView.textLabel.textColor = .white
placeholderView.detailTextLabel.text = NSLocalizedString("Items that you've copied to the clipboard will appear here.", comment: "")
placeholderView.detailTextLabel.textColor = .white
let vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: UIBlurEffect(style: .dark)))
vibrancyView.contentView.addSubview(placeholderView, pinningEdgesWith: .zero)
let gradientView = self.makeGradientView()
gradientView.addSubview(vibrancyView, pinningEdgesWith: .zero)
dataSource.placeholderView = gradientView
return dataSource
}
func makeGradientView() -> GradientView
{
let gradientView = GradientView()
gradientView.colors = [.clipLightPink, .clipPink]
return gradientView
}
func updateDataSource()
{
self.stopUpdating()
self.dataSource = self.makeDataSource()
self.tableView.dataSource = self.dataSource
self.tableView.prefetchDataSource = self.dataSource
self.tableView.reloadData()
self.startUpdating()
}
func updateDate(for cell: ClippingTableViewCell, item: PasteboardItem)
{
if Date().timeIntervalSince(item.date) < 2
{
cell.dateLabel.text = NSLocalizedString("now", comment: "")
}
else
{
cell.dateLabel.text = self.dateComponentsFormatter.string(from: item.date, to: Date())
}
}
func showMenu(at indexPath: IndexPath)
{
guard let cell = self.tableView.cellForRow(at: indexPath) as? ClippingTableViewCell else { return }
let item = self.dataSource.item(at: indexPath)
self.selectedItem = item
let targetRect = cell.clippingView.frame
self.becomeFirstResponder()
UIMenuController.shared.setTargetRect(targetRect, in: cell)
UIMenuController.shared.setMenuVisible(true, animated: true)
}
@objc func showLocation(_ sender: UIButton)
{
let point = self.view.convert(sender.center, from: sender.superview!)
guard let indexPath = self.tableView.indexPathForRow(at: point) else { return }
let item = self.dataSource.item(at: indexPath)
guard let location = item.location else { return }
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(location) { (placemarks, error) in
DispatchQueue.main.async {
let title: String
let message: String?
if let placemarks, let placemark = placemarks.first,
let postalAddress = placemark.postalAddress?.mutableCopy() as? CNMutablePostalAddress
{
// The location isn't precise, so don't pretend that it is by showing street address.
postalAddress.street = ""
postalAddress.subLocality = ""
let formatter = CNPostalAddressFormatter()
if let sublocality = placemark.subLocality
{
title = sublocality + "\n" + formatter.string(from: postalAddress)
}
else
{
title = formatter.string(from: postalAddress)
}
message = nil
}
else if let error
{
title = NSLocalizedString("Unable to Look Up Location", comment: "")
message = error.localizedDescription + "\n\n" + "\(location.coordinate.latitude), \(location.coordinate.longitude)"
}
else
{
title = "\(location.coordinate.latitude), \(location.coordinate.longitude)"
message = nil
}
let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
alertController.addAction(.ok)
self.present(alertController, animated: true)
}
}
}
func startUpdating()
{
self.stopUpdating()
self.updateTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] (timer) in
guard let self = self else { return }
for indexPath in self.tableView.indexPathsForVisibleRows ?? []
{
guard let cell = self.tableView.cellForRow(at: indexPath) as? ClippingTableViewCell else { continue }
let item = self.dataSource.item(at: indexPath)
self.updateDate(for: cell, item: item)
}
}
}
func stopUpdating()
{
self.updateTimer?.invalidate()
self.updateTimer = nil
}
}
private extension HistoryViewController
{
func present(_ error: Error)
{
let nsError = error as NSError
let alertController = UIAlertController(title: nsError.localizedFailureReason ?? nsError.localizedDescription,
message: nsError.localizedRecoverySuggestion, preferredStyle: .alert)
if let recoverableError = error as? RecoverableError, !recoverableError.recoveryOptions.isEmpty
{
alertController.addAction(.cancel)
for (index, title) in zip(0..., recoverableError.recoveryOptions)
{
let action = UIAlertAction(title: title, style: .default) { (action) in
recoverableError.attemptRecovery(optionIndex: index) { (success) in
print("Recovered from error with success:", success)
}
}
alertController.addAction(action)
}
}
else
{
alertController.addAction(.ok)
}
self.present(alertController, animated: true, completion: nil)
}
}
private extension HistoryViewController
{
@objc func didEnterBackground(_ notification: Notification)
{
// Save any pending changes to disk.
if DatabaseManager.shared.persistentContainer.viewContext.hasChanges
{
do
{
try DatabaseManager.shared.persistentContainer.viewContext.save()
}
catch
{
print("Failed to save view context.", error)
}
}
self.undoManager?.removeAllActions()
self.stopUpdating()
}
@objc func willEnterForeground(_ notification: Notification)
{
self.startUpdating()
}
@objc func settingsDidChange(_ notification: Notification)
{
self.tableView.reloadData()
}
@IBAction func unwindToHistoryViewController(_ segue: UIStoryboardSegue)
{
}
}
extension HistoryViewController
{
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
{
// It's far *far* easier to simply set row height to 0 for cells beyond history limit
// than to actually limit fetched results to the correct number live (with insertions and deletions).
guard indexPath.row < UserDefaults.shared.historyLimit.rawValue else { return 0.0 }
let item = self.dataSource.item(at: indexPath)
if let height = self.cachedHeights[item.objectID]
{
return height
}
let portraitScreenHeight = UIScreen.main.coordinateSpace.convert(UIScreen.main.bounds, to: UIScreen.main.fixedCoordinateSpace).height
let maximumHeight: CGFloat
if item.preferredRepresentation?.type == .image
{
maximumHeight = portraitScreenHeight / 2
}
else
{
maximumHeight = portraitScreenHeight / 3
}
let widthConstraint = self.prototypeCell.contentView.widthAnchor.constraint(equalToConstant: tableView.bounds.width)
let heightConstraint = self.prototypeCell.contentView.heightAnchor.constraint(lessThanOrEqualToConstant: maximumHeight)
NSLayoutConstraint.activate([widthConstraint, heightConstraint])
defer { NSLayoutConstraint.deactivate([widthConstraint, heightConstraint]) }
self.dataSource.cellConfigurationHandler(self.prototypeCell, item, indexPath)
let size = self.prototypeCell.contentView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize)
self.cachedHeights[item.objectID] = size.height
return size.height
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
self.showMenu(at: indexPath)
}
}
extension HistoryViewController: UIPopoverPresentationControllerDelegate
{
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle
{
return .none
}
}
================================================
FILE: Clip/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ALTAppGroups</key>
<array>
<string>group.com.rileytestut.Clip</string>
</array>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Clip</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>Clip General</string>
<key>CFBundleURLSchemes</key>
<array>
<string>clip</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Clip requires “Always” location permission to automatically tag clippings with your location. Your location data never leaves this device.</string>
<key>NSLocationDefaultAccuracyReduced</key>
<true/>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Clip requires location permissions to automatically tag clippings with your location. Your location data never leaves this device.</string>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<true/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UIBackgroundModes</key>
<array>
<string>location</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIRequiredDeviceCapabilities</key>
<array>
<string>armv7</string>
</array>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIUserInterfaceStyle</key>
<string>Light</string>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeDescription</key>
<string>Clipping</string>
<key>UTTypeIconFiles</key>
<array/>
<key>UTTypeIdentifier</key>
<string>com.rileytestut.Clip.Clipping</string>
</dict>
</array>
</dict>
</plist>
================================================
FILE: Clip/Map View/ClippingSheet.swift
================================================
//
// ClippingSheet.swift
// Clip
//
// Created by Riley Testut on 3/20/24.
// Copyright © 2024 Riley Testut. All rights reserved.
//
import SwiftUI
import ClipKit
@available(iOS 16.4, *)
struct ClippingSheet: View
{
var pasteboardItem: PasteboardItem
@Environment(\.dismiss)
private var dismiss
var body: some View {
NavigationStack {
ClippingCell(pasteboardItem: pasteboardItem)
.padding()
.toolbar {
ToolbarItem(placement: .confirmationAction) {
Button("Copy", action: copy)
}
ToolbarItem(placement: .cancellationAction) {
Button("Cancel", action: cancel)
}
}
}
.presentationBackground(Material.regular)
.presentationDetents([.fraction(0.33)])
.tint(.init(uiColor: .clipPink))
}
}
@available(iOS 16.4, *)
private extension ClippingSheet
{
func copy()
{
let center = CFNotificationCenterGetDarwinNotifyCenter()
CFNotificationCenterPostNotification(center, .ignoreNextPasteboardChange, nil, nil, true)
UIPasteboard.general.copy(self.pasteboardItem)
self.dismiss()
}
func cancel()
{
self.dismiss()
}
}
================================================
FILE: Clip/Map View/ClippingsMapView.swift
================================================
//
// HistoryMapView.swift
// Clip
//
// Created by Riley Testut on 3/20/24.
// Copyright © 2024 Riley Testut. All rights reserved.
//
import MapKit
import UIKit
import SwiftUI
import ClipKit
@available(iOS 17, *)
class ClippingsMapViewController: UIHostingController<AnyView>
{
@MainActor
required dynamic init?(coder aDecoder: NSCoder) {
let view = AnyView(erasing: ClippingsMapView().environment(\.managedObjectContext, DatabaseManager.shared.persistentContainer.viewContext))
super.init(coder: aDecoder, rootView: view)
self.tabBarItem.image = UIImage(systemName: "map")
}
}
@MainActor @available(iOS 17, *)
struct ClippingsMapView: View
{
@FetchRequest(fetchRequest: PasteboardItem.historyFetchRequest())
private var pasteboardItems: FetchedResults<PasteboardItem>
@State
private var selectedItem: PasteboardItem?
var body: some View {
Map(selection: $selectedItem) {
// Must use \.self as keypath for selection to work
ForEach(pasteboardItems, id: \.self) { pasteboardItem in
if let location = pasteboardItem.location
{
Marker(pasteboardItem.date.formatted(), systemImage: "paperclip", coordinate: location.coordinate)
}
}
}
.sheet(item: $selectedItem) { pasteboardItem in
ClippingSheet(pasteboardItem: pasteboardItem)
}
}
}
================================================
FILE: Clip/Pasteboard/LocationManager.swift
================================================
//
// LocationManager.swift
// Clip
//
// Created by Riley Testut on 11/6/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
import CoreLocation
import Combine
import UIKit
extension LocationManager
{
typealias Status = Result<Void, Swift.Error>
enum Error: LocalizedError, RecoverableError
{
case requiresAlwaysAuthorization
var failureReason: String? {
switch self
{
case .requiresAlwaysAuthorization: return NSLocalizedString("Clip requires “Always” location permission.", comment: "")
}
}
var recoverySuggestion: String? {
switch self
{
case .requiresAlwaysAuthorization: return NSLocalizedString("Please grant Clip “Always” location permission in Settings so it can run in the background indefinitely.", comment: "")
}
}
var recoveryOptions: [String] {
switch self
{
case .requiresAlwaysAuthorization: return [NSLocalizedString("Open Settings", comment: "")]
}
}
func attemptRecovery(optionIndex recoveryOptionIndex: Int) -> Bool
{
return false
}
func attemptRecovery(optionIndex recoveryOptionIndex: Int, resultHandler handler: @escaping (Bool) -> Void)
{
switch self
{
case .requiresAlwaysAuthorization:
let openURL = URL(string: UIApplication.openSettingsURLString)!
UIApplication.shared.open(openURL, options: [:], completionHandler: handler)
}
}
}
}
class LocationManager: NSObject, ObservableObject
{
@PublishedPipeline({ removeDuplicatesPipeline($0) })
var status: Status? = nil
var location: CLLocation? {
return self.locationManager.location
}
private let locationManager: CLLocationManager
override init()
{
self.locationManager = CLLocationManager()
self.locationManager.distanceFilter = CLLocationDistanceMax
self.locationManager.pausesLocationUpdatesAutomatically = false
self.locationManager.allowsBackgroundLocationUpdates = true
if #available(iOS 14.0, *)
{
self.locationManager.desiredAccuracy = kCLLocationAccuracyReduced
}
else
{
self.locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
}
super.init()
self.locationManager.delegate = self
}
func start()
{
switch self.status
{
case .success: return
case .failure, nil: break
}
if CLLocationManager.authorizationStatus() == .notDetermined || CLLocationManager.authorizationStatus() == .authorizedWhenInUse
{
self.locationManager.requestAlwaysAuthorization()
return
}
self.locationManager.startUpdatingLocation()
}
func stop()
{
self.locationManager.stopUpdatingLocation()
self.status = nil
}
}
private extension LocationManager
{
static func removeDuplicatesPipeline<T: Publisher>(_ publisher: T) -> AnyPublisher<Status?, Never>
where T.Output == Status?, T.Failure == Never
{
return publisher
.removeDuplicates { (a, b) in
switch (a, b)
{
case (nil, nil), (.success(()), .success(())): return true
case (.failure(let errorA as NSError), .failure(let errorB as NSError)): return errorA.domain == errorB.domain && errorA.code == errorB.code
case (nil, _), (.success, _), (.failure, _): return false
}
}
.eraseToAnyPublisher()
}
}
extension LocationManager: CLLocationManagerDelegate
{
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus)
{
switch status
{
case .notDetermined: break
case .restricted, .denied, .authorizedWhenInUse: self.status = .failure(Error.requiresAlwaysAuthorization)
case .authorizedAlways: self.start()
@unknown default: break
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
{
self.status = .success(())
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Swift.Error)
{
if let error = error as? CLError
{
guard error.code != .denied else {
self.status = .failure(Error.requiresAlwaysAuthorization)
return
}
}
self.status = .failure(error)
}
}
================================================
FILE: Clip/Pasteboard/PasteboardMonitor.swift
================================================
//
// PasteboardMonitor.swift
// Clip
//
// Created by Riley Testut on 6/11/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import UIKit
import AVFoundation
import UserNotifications
import CoreLocation
import ClipKit
import Roxas
private let PasteboardMonitorDidChangePasteboard: @convention(c) (CFNotificationCenter?, UnsafeMutableRawPointer?, CFNotificationName?, UnsafeRawPointer?, CFDictionary?) -> Void =
{ (center, observer, name, object, userInfo) in
ApplicationMonitor.shared.pasteboardMonitor.didChangePasteboard()
}
private let PasteboardMonitorIgnoreNextPasteboardChange: @convention(c) (CFNotificationCenter?, UnsafeMutableRawPointer?, CFNotificationName?, UnsafeRawPointer?, CFDictionary?) -> Void =
{ (center, observer, name, object, userInfo) in
ApplicationMonitor.shared.pasteboardMonitor.ignoreNextPasteboardChange = true
}
class PasteboardMonitor
{
private(set) var isStarted = false
fileprivate var ignoreNextPasteboardChange = false
private let feedbackGenerator = UINotificationFeedbackGenerator()
}
extension PasteboardMonitor
{
func start(completionHandler: @escaping (Result<Void, Error>) -> Void)
{
guard !self.isStarted else { return }
self.isStarted = true
self.registerForNotifications()
completionHandler(.success(()))
}
}
private extension PasteboardMonitor
{
func registerForNotifications()
{
let center = CFNotificationCenterGetDarwinNotifyCenter()
CFNotificationCenterAddObserver(center, nil, PasteboardMonitorDidChangePasteboard, CFNotificationName.didChangePasteboard.rawValue, nil, .deliverImmediately)
CFNotificationCenterAddObserver(center, nil, PasteboardMonitorIgnoreNextPasteboardChange, CFNotificationName.ignoreNextPasteboardChange.rawValue, nil, .deliverImmediately)
#if !targetEnvironment(simulator)
let beginListeningSelector = ["Notifications", "Change", "Pasteboard", "To", "Listening", "begin"].reversed().joined()
let className = ["Connection", "Server", "PB"].reversed().joined()
let PBServerConnection = NSClassFromString(className) as AnyObject
_ = PBServerConnection.perform(NSSelectorFromString(beginListeningSelector))
#endif
let changedNotification = ["changed", "pasteboard", "apple", "com"].reversed().joined(separator: ".")
NotificationCenter.default.addObserver(self, selector: #selector(PasteboardMonitor.pasteboardDidUpdate), name: Notification.Name(changedNotification), object: nil)
}
@objc func pasteboardDidUpdate()
{
guard !self.ignoreNextPasteboardChange else {
self.ignoreNextPasteboardChange = false
return
}
DispatchQueue.main.async {
if UIApplication.shared.applicationState != .background
{
// Don't present notifications for items copied from within Clip.
guard !UIPasteboard.general.contains(pasteboardTypes: [UTI.clipping]) else { return }
}
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
if settings.soundSetting == .enabled
{
UIDevice.current.vibrate()
}
}
let content = UNMutableNotificationContent()
content.categoryIdentifier = UNNotificationCategory.clipboardReaderIdentifier
content.title = NSLocalizedString("Clipboard Changed", comment: "")
content.body = NSLocalizedString("Swipe down to save to Clip.", comment: "")
if let location = ApplicationMonitor.shared.locationManager.location
{
content.userInfo = [
UNNotification.latitudeUserInfoKey: location.coordinate.latitude,
UNNotification.longitudeUserInfoKey: location.coordinate.longitude
]
}
let request = UNNotificationRequest(identifier: "ClipboardChanged", content: content, trigger: nil)
UNUserNotificationCenter.current().add(request) { (error) in
if let error = error {
print(error)
}
}
}
}
}
private extension PasteboardMonitor
{
func didChangePasteboard()
{
DatabaseManager.shared.refresh()
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: ["ClipboardChanged"])
}
}
================================================
FILE: Clip/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"filename" : "Clip1024.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
================================================
FILE: Clip/Resources/Assets.xcassets/Contents.json
================================================
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: Clip/Resources/Assets.xcassets/Settings.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "gear_1.pdf"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
},
"properties" : {
"template-rendering-intent" : "template"
}
}
================================================
FILE: Clip/SceneDelegate.swift
================================================
//
// SceneDelegate.swift
// Clip
//
// Created by Riley Testut on 10/30/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
import UIKit
import ClipKit
import Roxas
class SceneDelegate: UIResponder, UIWindowSceneDelegate
{
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)
{
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let _ = (scene as? UIWindowScene) else { return }
if let context = connectionOptions.urlContexts.first
{
self.open(context)
}
}
func sceneWillEnterForeground(_ scene: UIScene)
{
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
guard DatabaseManager.shared.isStarted else { return }
DatabaseManager.shared.refresh()
guard !UIPasteboard.general.hasImages else {
// Our duplicate detection does not work for images,
// so don't automatically save images upon returning to foreground.
return
}
let location = ApplicationMonitor.shared.locationManager.location
DatabaseManager.shared.savePasteboard(location: location) { (result) in
do
{
try result.get()
print("Saved clipboard upon returning to foreground!")
}
catch PasteboardError.noItem, PasteboardError.duplicateItem
{
// Ignore
}
catch
{
print("Failed to save clipboard upon returning to app.")
}
}
}
func sceneDidEnterBackground(_ scene: UIScene)
{
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
#if targetEnvironment(simulator)
// Audio extension hack to access pasteboard doesn't work in simulator, so for testing just start background task.
RSTBeginBackgroundTask("com.rileytestut.Clip.simulatorBackgroundTask")
#endif
DatabaseManager.shared.purge()
}
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>)
{
guard let context = URLContexts.first else { return }
self.open(context)
}
}
private extension SceneDelegate
{
func open(_ context: UIOpenURLContext)
{
guard context.url.scheme?.lowercased() == "clip" && context.url.host?.lowercased() == "settings" else { return }
let openURL = URL(string: UIApplication.openSettingsURLString)!
UIApplication.shared.open(openURL, options: [:], completionHandler: nil)
}
}
================================================
FILE: Clip/Settings/SettingsViewController.swift
================================================
//
// SettingsViewController.swift
// Clip
//
// Created by Riley Testut on 6/14/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import UIKit
import ClipKit
extension SettingsViewController
{
private enum Section: CaseIterable
{
case historyLimit
case location
}
static let settingsDidChangeNotification: Notification.Name = Notification.Name("SettingsDidChangeNotification")
}
class SettingsViewController: UITableViewController
{
@IBOutlet private var showLocationIconSwitch: UISwitch!
override func viewDidLoad()
{
super.viewDidLoad()
self.showLocationIconSwitch.isOn = UserDefaults.shared.showLocationIcon
}
}
private extension SettingsViewController
{
@IBAction func toggleShowLocationIcon(_ sender: UISwitch)
{
UserDefaults.shared.showLocationIcon = sender.isOn
NotificationCenter.default.post(name: SettingsViewController.settingsDidChangeNotification, object: nil)
}
}
extension SettingsViewController
{
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = super.tableView(tableView, cellForRowAt: indexPath)
switch Section.allCases[indexPath.section]
{
case .historyLimit:
let limit = HistoryLimit.allCases[indexPath.row]
cell.accessoryType = (limit == UserDefaults.shared.historyLimit) ? .checkmark : .none
case .location: break
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath)
{
guard Section.allCases[indexPath.section] == .historyLimit else { return }
let historyLimit = HistoryLimit.allCases[indexPath.row]
UserDefaults.shared.historyLimit = historyLimit
tableView.reloadData()
self.dismiss(animated: true, completion: nil)
}
}
================================================
FILE: Clip/Types/PublishedPipeline.swift
================================================
//
// PublishedPipeline.swift
// Clip
//
// Created by Riley Testut on 12/4/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
import Combine
@propertyWrapper
class PublishedPipeline<Value, Pipeline: Publisher>
{
@Published
var wrappedValue: Value
var projectedValue: AnyPublisher<Pipeline.Output, Pipeline.Failure> {
return self.pipeline(self.$wrappedValue.eraseToAnyPublisher()).eraseToAnyPublisher()
}
private let pipeline: (AnyPublisher<Value, Never>) -> Pipeline
init(wrappedValue: Value, _ pipeline: @escaping (AnyPublisher<Value, Never>) -> Pipeline)
{
self.wrappedValue = wrappedValue
self.pipeline = pipeline
}
}
================================================
FILE: Clip.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
BF0BDE5122B4414A00E1419D /* UTI.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0BDE5022B4414A00E1419D /* UTI.swift */; };
BF0BDE5622B456A600E1419D /* PasteboardItem+ActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0BDE5522B456A600E1419D /* PasteboardItem+ActivityItemSource.swift */; };
BF0BDE5A22B4771300E1419D /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0BDE5922B4771300E1419D /* SettingsViewController.swift */; };
BF0BDE5E22B47D4900E1419D /* UserDefaults+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0BDE5722B4757100E1419D /* UserDefaults+App.swift */; };
BF1D5C7A22B029E70062F474 /* Roxas.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF1D5C7522B029DC0062F474 /* Roxas.framework */; platformFilter = ios; };
BF1D5C7B22B029E70062F474 /* Roxas.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF1D5C7522B029DC0062F474 /* Roxas.framework */; platformFilter = ios; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
BF1D5C7F22B02BF30062F474 /* PasteboardMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1D5C7E22B02BF30062F474 /* PasteboardMonitor.swift */; };
BF235BC1247D8B5300CCFCB0 /* UIPasteboard+PasteboardItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF235BC0247D8B5300CCFCB0 /* UIPasteboard+PasteboardItem.swift */; };
BF50E7D922C2BF010070E17B /* Bundle+AppGroups.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF50E7D822C2BF010070E17B /* Bundle+AppGroups.swift */; };
BF770E6522BC7688002A40FE /* UIDevice+Vibration.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF770E6122BC767A002A40FE /* UIDevice+Vibration.swift */; };
BF7B9EE122B81C980042C873 /* Int+Bytes.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF7B9EE022B81C980042C873 /* Int+Bytes.swift */; };
BF7E6FE3247C4FCD0058F4D4 /* KeyboardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF7E6FE2247C4FCD0058F4D4 /* KeyboardViewController.swift */; };
BF7E6FE7247C4FCD0058F4D4 /* ClipBoard.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = BF7E6FE0247C4FCD0058F4D4 /* ClipBoard.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
BF8F76F3254CC0E0005AF18B /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8F76F2254CC0E0005AF18B /* SceneDelegate.swift */; };
BFA2DF60247DD21F00E31E4D /* Keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF7E6FF0247C50540058F4D4 /* Keyboard.swift */; };
BFA2DF61247DD23800E31E4D /* ClippingCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF7E6FF2247C518D0058F4D4 /* ClippingCell.swift */; };
BFA2DF62247DD23C00E31E4D /* Blur.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF0AB807247C5D1F0090B43B /* Blur.swift */; };
BFA2DF65247DD2B200E31E4D /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFA2DF64247DD2B200E31E4D /* Colors.xcassets */; };
BFA2DF69247DD31500E31E4D /* UIColor+Clip.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA2DF68247DD31500E31E4D /* UIColor+Clip.swift */; };
BFA2DF6B247DE47900E31E4D /* Preview.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA2DF6A247DE47900E31E4D /* Preview.swift */; };
BFA714BA22C53F1700EE7236 /* ApplicationMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFA714B922C53F1700EE7236 /* ApplicationMonitor.swift */; };
BFAC49D322B2EDA80011E7C4 /* ClippingTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFAC49D222B2EDA80011E7C4 /* ClippingTableViewCell.xib */; };
BFAC49D522B2EDB20011E7C4 /* ClippingTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFAC49D422B2EDB20011E7C4 /* ClippingTableViewCell.swift */; };
BFAC49E022B40EC90011E7C4 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BFAC49DF22B40EC90011E7C4 /* AudioToolbox.framework */; };
BFAD2C4A257AD4D600FF9532 /* PublishedPipeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFAD2C49257AD4D600FF9532 /* PublishedPipeline.swift */; };
BFAD46C92490588800451D6F /* UIInputView+Click.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFAD46C82490588800451D6F /* UIInputView+Click.swift */; };
BFAD46CB249058DE00451D6F /* SwitchKeyboardButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFAD46CA249058DE00451D6F /* SwitchKeyboardButton.swift */; };
BFC176FD2399BC4F0058AC51 /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF69A53F2395D44600CF838A /* UserNotifications.framework */; };
BFC176FE2399BC4F0058AC51 /* UserNotificationsUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF69A5412395D44700CF838A /* UserNotificationsUI.framework */; };
BFC177012399BC4F0058AC51 /* NotificationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC177002399BC4F0058AC51 /* NotificationViewController.swift */; };
BFC177042399BC4F0058AC51 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFC177022399BC4F0058AC51 /* MainInterface.storyboard */; };
BFC177082399BC4F0058AC51 /* ClipboardReader.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = BFC176FC2399BC4F0058AC51 /* ClipboardReader.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
BFC1F39D22AF0D0E003AC21A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC1F39C22AF0D0E003AC21A /* AppDelegate.swift */; };
BFC1F39F22AF0D0E003AC21A /* HistoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC1F39E22AF0D0E003AC21A /* HistoryViewController.swift */; };
BFC1F3A222AF0D0E003AC21A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFC1F3A022AF0D0E003AC21A /* Main.storyboard */; };
BFC1F3A422AF0D0F003AC21A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFC1F3A322AF0D0F003AC21A /* Assets.xcassets */; };
BFC1F3A722AF0D0F003AC21A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFC1F3A522AF0D0F003AC21A /* LaunchScreen.storyboard */; };
BFC9E65422B1A22700974663 /* ClipKit.h in Headers */ = {isa = PBXBuildFile; fileRef = BFC9E65222B1A22700974663 /* ClipKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
BFC9E65722B1A22700974663 /* ClipKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BFC9E65022B1A22700974663 /* ClipKit.framework */; platformFilter = ios; };
BFC9E65822B1A22700974663 /* ClipKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BFC9E65022B1A22700974663 /* ClipKit.framework */; platformFilter = ios; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
BFC9E66222B1B03900974663 /* DatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1D5D4622B0869C0062F474 /* DatabaseManager.swift */; };
BFC9E66322B1B03C00974663 /* PasteboardItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1D5D4A22B094DB0062F474 /* PasteboardItem.swift */; };
BFC9E66422B1B03C00974663 /* PasteboardItemRepresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1D5D4822B088F40062F474 /* PasteboardItemRepresentation.swift */; };
BFC9E66522B1B04000974663 /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = BF1D5D4322B0867C0062F474 /* Model.xcdatamodeld */; };
BFC9E66622B1B1E600974663 /* Roxas.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF1D5C7522B029DC0062F474 /* Roxas.framework */; };
BFC9E67222B2C4C500974663 /* Result+Conveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC9E67122B2C4C400974663 /* Result+Conveniences.swift */; };
BFC9E67C22B2CA4400974663 /* CFNotification+PasteboardListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFC9E67B22B2CA4400974663 /* CFNotification+PasteboardListener.swift */; };
BFDB5B1E22EF7B5000F74113 /* GradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFDB5B1D22EF7B5000F74113 /* GradientView.swift */; };
BFEF44AE2398693300095A92 /* ForwardingNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFEF44AB2398693300095A92 /* ForwardingNavigationController.swift */; };
BFF9E4932555F4F40052B1B2 /* LocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF9E4922555F4F40052B1B2 /* LocationManager.swift */; };
D534428E2BAB202700E133EE /* ClippingsMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D534428D2BAB202700E133EE /* ClippingsMapView.swift */; };
D5D375152BAB330200213D84 /* ClippingSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D375142BAB330200213D84 /* ClippingSheet.swift */; };
D5D375192BAB4DF800213D84 /* UNNotification+Keys.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5D375182BAB4DF800213D84 /* UNNotification+Keys.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
BF1D5C7422B029DC0062F474 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BF1D5C6E22B029DB0062F474 /* Roxas.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = BFADAFF819AE7BB70050CF31;
remoteInfo = Roxas;
};
BF1D5C7622B029DC0062F474 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BF1D5C6E22B029DB0062F474 /* Roxas.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = BF8624801BB742E700C12EEE;
remoteInfo = RoxasTV;
};
BF1D5C7822B029DC0062F474 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BF1D5C6E22B029DB0062F474 /* Roxas.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = BFADB00319AE7BB80050CF31;
remoteInfo = RoxasTests;
};
BF1D5C7C22B029E80062F474 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BF1D5C6E22B029DB0062F474 /* Roxas.xcodeproj */;
proxyType = 1;
remoteGlobalIDString = BFADAFF719AE7BB70050CF31;
remoteInfo = Roxas;
};
BF7E6FE5247C4FCD0058F4D4 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BFC1F39122AF0D0E003AC21A /* Project object */;
proxyType = 1;
remoteGlobalIDString = BF7E6FDF247C4FCD0058F4D4;
remoteInfo = ClipBoard;
};
BFAD46CC24905E9400451D6F /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BFC1F39122AF0D0E003AC21A /* Project object */;
proxyType = 1;
remoteGlobalIDString = BFC9E64F22B1A22700974663;
remoteInfo = ClipKit;
};
BFC177062399BC4F0058AC51 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BFC1F39122AF0D0E003AC21A /* Project object */;
proxyType = 1;
remoteGlobalIDString = BFC176FB2399BC4F0058AC51;
remoteInfo = ClipboardReader;
};
BFC9E65522B1A22700974663 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = BFC1F39122AF0D0E003AC21A /* Project object */;
proxyType = 1;
remoteGlobalIDString = BFC9E64F22B1A22700974663;
remoteInfo = ClipKit;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
BF1D5C6D22B029190062F474 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
BF1D5C7B22B029E70062F474 /* Roxas.framework in Embed Frameworks */,
BFC9E65822B1A22700974663 /* ClipKit.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
BF1D5C9C22B042870062F474 /* Embed App Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
BFC177082399BC4F0058AC51 /* ClipboardReader.appex in Embed App Extensions */,
BF7E6FE7247C4FCD0058F4D4 /* ClipBoard.appex in Embed App Extensions */,
);
name = "Embed App Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
BF0AB807247C5D1F0090B43B /* Blur.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Blur.swift; sourceTree = "<group>"; };
BF0BDE5022B4414A00E1419D /* UTI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UTI.swift; sourceTree = "<group>"; };
BF0BDE5522B456A600E1419D /* PasteboardItem+ActivityItemSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PasteboardItem+ActivityItemSource.swift"; sourceTree = "<group>"; };
BF0BDE5722B4757100E1419D /* UserDefaults+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefaults+App.swift"; sourceTree = "<group>"; };
BF0BDE5922B4771300E1419D /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
BF1D5C6E22B029DB0062F474 /* Roxas.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Roxas.xcodeproj; path = Dependencies/Roxas/Roxas.xcodeproj; sourceTree = "<group>"; };
BF1D5C7E22B02BF30062F474 /* PasteboardMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteboardMonitor.swift; sourceTree = "<group>"; };
BF1D5D4422B0867C0062F474 /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = "<group>"; };
BF1D5D4622B0869C0062F474 /* DatabaseManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseManager.swift; sourceTree = "<group>"; };
BF1D5D4822B088F40062F474 /* PasteboardItemRepresentation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteboardItemRepresentation.swift; sourceTree = "<group>"; };
BF1D5D4A22B094DB0062F474 /* PasteboardItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteboardItem.swift; sourceTree = "<group>"; };
BF235BC0247D8B5300CCFCB0 /* UIPasteboard+PasteboardItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIPasteboard+PasteboardItem.swift"; sourceTree = "<group>"; };
BF50E7D822C2BF010070E17B /* Bundle+AppGroups.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+AppGroups.swift"; sourceTree = "<group>"; };
BF69A53F2395D44600CF838A /* UserNotifications.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotifications.framework; path = System/Library/Frameworks/UserNotifications.framework; sourceTree = SDKROOT; };
BF69A5412395D44700CF838A /* UserNotificationsUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotificationsUI.framework; path = System/Library/Frameworks/UserNotificationsUI.framework; sourceTree = SDKROOT; };
BF746541247C900100F66F3B /* ClipBoard.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ClipBoard.entitlements; sourceTree = "<group>"; };
BF770E6122BC767A002A40FE /* UIDevice+Vibration.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIDevice+Vibration.swift"; sourceTree = "<group>"; };
BF7B9EE022B81C980042C873 /* Int+Bytes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Bytes.swift"; sourceTree = "<group>"; };
BF7E6FE0247C4FCD0058F4D4 /* ClipBoard.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ClipBoard.appex; sourceTree = BUILT_PRODUCTS_DIR; };
BF7E6FE2247C4FCD0058F4D4 /* KeyboardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardViewController.swift; sourceTree = "<group>"; };
BF7E6FE4247C4FCD0058F4D4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
BF7E6FF0247C50540058F4D4 /* Keyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Keyboard.swift; sourceTree = "<group>"; };
BF7E6FF2247C518D0058F4D4 /* ClippingCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClippingCell.swift; sourceTree = "<group>"; };
BF8F76F2254CC0E0005AF18B /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
BFA2DF64247DD2B200E31E4D /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = "<group>"; };
BFA2DF68247DD31500E31E4D /* UIColor+Clip.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Clip.swift"; sourceTree = "<group>"; };
BFA2DF6A247DE47900E31E4D /* Preview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Preview.swift; sourceTree = "<group>"; };
BFA714B922C53F1700EE7236 /* ApplicationMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationMonitor.swift; sourceTree = "<group>"; };
BFAC49D222B2EDA80011E7C4 /* ClippingTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ClippingTableViewCell.xib; sourceTree = "<group>"; };
BFAC49D422B2EDB20011E7C4 /* ClippingTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClippingTableViewCell.swift; sourceTree = "<group>"; };
BFAC49DF22B40EC90011E7C4 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; };
BFAD2C49257AD4D600FF9532 /* PublishedPipeline.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublishedPipeline.swift; sourceTree = "<group>"; };
BFAD46C82490588800451D6F /* UIInputView+Click.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIInputView+Click.swift"; sourceTree = "<group>"; };
BFAD46CA249058DE00451D6F /* SwitchKeyboardButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchKeyboardButton.swift; sourceTree = "<group>"; };
BFC176FC2399BC4F0058AC51 /* ClipboardReader.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = ClipboardReader.appex; sourceTree = BUILT_PRODUCTS_DIR; };
BFC177002399BC4F0058AC51 /* NotificationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationViewController.swift; sourceTree = "<group>"; };
BFC177032399BC4F0058AC51 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
BFC177052399BC4F0058AC51 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
BFC1770C2399BCF90058AC51 /* ClipboardReader.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ClipboardReader.entitlements; sourceTree = "<group>"; };
BFC1F39922AF0D0E003AC21A /* Clip.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Clip.app; sourceTree = BUILT_PRODUCTS_DIR; };
BFC1F39C22AF0D0E003AC21A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
BFC1F39E22AF0D0E003AC21A /* HistoryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryViewController.swift; sourceTree = "<group>"; };
BFC1F3A122AF0D0E003AC21A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
BFC1F3A322AF0D0F003AC21A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
BFC1F3A622AF0D0F003AC21A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
BFC1F3A822AF0D0F003AC21A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
BFC9E62C22B18D5900974663 /* Clip.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Clip.entitlements; sourceTree = "<group>"; };
BFC9E65022B1A22700974663 /* ClipKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ClipKit.framework; sourceTree = BUILT_PRODUCTS_DIR; };
BFC9E65222B1A22700974663 /* ClipKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ClipKit.h; sourceTree = "<group>"; };
BFC9E65322B1A22700974663 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
BFC9E67122B2C4C400974663 /* Result+Conveniences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Result+Conveniences.swift"; sourceTree = "<group>"; };
BFC9E67B22B2CA4400974663 /* CFNotification+PasteboardListener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CFNotification+PasteboardListener.swift"; sourceTree = "<group>"; };
BFDB5B1D22EF7B5000F74113 /* GradientView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GradientView.swift; sourceTree = "<group>"; };
BFEF44AB2398693300095A92 /* ForwardingNavigationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForwardingNavigationController.swift; sourceTree = "<group>"; };
BFF9E4922555F4F40052B1B2 /* LocationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationManager.swift; sourceTree = "<group>"; };
D534428D2BAB202700E133EE /* ClippingsMapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClippingsMapView.swift; sourceTree = "<group>"; };
D5D375142BAB330200213D84 /* ClippingSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClippingSheet.swift; sourceTree = "<group>"; };
D5D375182BAB4DF800213D84 /* UNNotification+Keys.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UNNotification+Keys.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
BF7E6FDD247C4FCD0058F4D4 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
BFC176F92399BC4F0058AC51 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
BFC176FE2399BC4F0058AC51 /* UserNotificationsUI.framework in Frameworks */,
BFC176FD2399BC4F0058AC51 /* UserNotifications.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
BFC1F39622AF0D0E003AC21A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
BF1D5C7A22B029E70062F474 /* Roxas.framework in Frameworks */,
BFC9E65722B1A22700974663 /* ClipKit.framework in Frameworks */,
BFAC49E022B40EC90011E7C4 /* AudioToolbox.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
BFC9E64D22B1A22700974663 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
BFC9E66622B1B1E600974663 /* Roxas.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
BF0BDE5422B4568A00E1419D /* Extensions */ = {
isa = PBXGroup;
children = (
BF0BDE5522B456A600E1419D /* PasteboardItem+ActivityItemSource.swift */,
BF770E6122BC767A002A40FE /* UIDevice+Vibration.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
BF0BDE6622B4871400E1419D /* Settings */ = {
isa = PBXGroup;
children = (
BF0BDE5922B4771300E1419D /* SettingsViewController.swift */,
);
path = Settings;
sourceTree = "<group>";
};
BF1D5C6722B029090062F474 /* Dependencies */ = {
isa = PBXGroup;
children = (
BF1D5C6E22B029DB0062F474 /* Roxas.xcodeproj */,
);
name = Dependencies;
sourceTree = "<group>";
};
BF1D5C6F22B029DB0062F474 /* Products */ = {
isa = PBXGroup;
children = (
BF1D5C7522B029DC0062F474 /* Roxas.framework */,
BF1D5C7722B029DC0062F474 /* Roxas.framework */,
BF1D5C7922B029DC0062F474 /* RoxasTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
BF1D5C8122B038030062F474 /* Resources */ = {
isa = PBXGroup;
children = (
BFC1F3A322AF0D0F003AC21A /* Assets.xcassets */,
);
path = Resources;
sourceTree = "<group>";
};
BF1D5C8522B038FE0062F474 /* Supporting Files */ = {
isa = PBXGroup;
children = (
BFC1F3A522AF0D0F003AC21A /* LaunchScreen.storyboard */,
BFC1F3A822AF0D0F003AC21A /* Info.plist */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
BF1D5CDF22B063E00062F474 /* Frameworks */ = {
isa = PBXGroup;
children = (
BFAC49DF22B40EC90011E7C4 /* AudioToolbox.framework */,
BF69A53F2395D44600CF838A /* UserNotifications.framework */,
BF69A5412395D44700CF838A /* UserNotificationsUI.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
BF1D5D4122B083210062F474 /* Pasteboard */ = {
isa = PBXGroup;
children = (
BF1D5C7E22B02BF30062F474 /* PasteboardMonitor.swift */,
BFF9E4922555F4F40052B1B2 /* LocationManager.swift */,
);
path = Pasteboard;
sourceTree = "<group>";
};
BF7E6FE1247C4FCD0058F4D4 /* ClipBoard */ = {
isa = PBXGroup;
children = (
BF746541247C900100F66F3B /* ClipBoard.entitlements */,
BF7E6FE2247C4FCD0058F4D4 /* KeyboardViewController.swift */,
BF7E6FE4247C4FCD0058F4D4 /* Info.plist */,
);
path = ClipBoard;
sourceTree = "<group>";
};
BF7E6FEF247C50480058F4D4 /* SwiftUI */ = {
isa = PBXGroup;
children = (
BFA2DF6A247DE47900E31E4D /* Preview.swift */,
BF7E6FF0247C50540058F4D4 /* Keyboard.swift */,
BF7E6FF2247C518D0058F4D4 /* ClippingCell.swift */,
BFAD46CA249058DE00451D6F /* SwitchKeyboardButton.swift */,
BF0AB807247C5D1F0090B43B /* Blur.swift */,
);
path = SwiftUI;
sourceTree = "<group>";
};
BFA2DF63247DD2A500E31E4D /* Resources */ = {
isa = PBXGroup;
children = (
BFA2DF64247DD2B200E31E4D /* Colors.xcassets */,
);
path = Resources;
sourceTree = "<group>";
};
BFAC49D722B2EF170011E7C4 /* History */ = {
isa = PBXGroup;
children = (
BFC1F39E22AF0D0E003AC21A /* HistoryViewController.swift */,
BFAC49D422B2EDB20011E7C4 /* ClippingTableViewCell.swift */,
BFAC49D222B2EDA80011E7C4 /* ClippingTableViewCell.xib */,
);
path = History;
sourceTree = "<group>";
};
BFAD2C48257AD4CB00FF9532 /* Types */ = {
isa = PBXGroup;
children = (
BFAD2C49257AD4D600FF9532 /* PublishedPipeline.swift */,
);
path = Types;
sourceTree = "<group>";
};
BFC176FF2399BC4F0058AC51 /* ClipboardReader */ = {
isa = PBXGroup;
children = (
BFC1770C2399BCF90058AC51 /* ClipboardReader.entitlements */,
BFC177002399BC4F0058AC51 /* NotificationViewController.swift */,
BFC177022399BC4F0058AC51 /* MainInterface.storyboard */,
BFC177052399BC4F0058AC51 /* Info.plist */,
);
path = ClipboardReader;
sourceTree = "<group>";
};
BFC1F39022AF0D0E003AC21A = {
isa = PBXGroup;
children = (
BFC1F39B22AF0D0E003AC21A /* Clip */,
BFC9E65122B1A22700974663 /* ClipKit */,
BFC176FF2399BC4F0058AC51 /* ClipboardReader */,
BF7E6FE1247C4FCD0058F4D4 /* ClipBoard */,
BF1D5C6722B029090062F474 /* Dependencies */,
BFC1F39A22AF0D0E003AC21A /* Products */,
BF1D5CDF22B063E00062F474 /* Frameworks */,
);
sourceTree = "<group>";
};
BFC1F39A22AF0D0E003AC21A /* Products */ = {
isa = PBXGroup;
children = (
BFC1F39922AF0D0E003AC21A /* Clip.app */,
BFC9E65022B1A22700974663 /* ClipKit.framework */,
BFC176FC2399BC4F0058AC51 /* ClipboardReader.appex */,
BF7E6FE0247C4FCD0058F4D4 /* ClipBoard.appex */,
);
name = Products;
sourceTree = "<group>";
};
BFC1F39B22AF0D0E003AC21A /* Clip */ = {
isa = PBXGroup;
children = (
BFC9E62C22B18D5900974663 /* Clip.entitlements */,
BFC1F39C22AF0D0E003AC21A /* AppDelegate.swift */,
BF8F76F2254CC0E0005AF18B /* SceneDelegate.swift */,
BFA714B922C53F1700EE7236 /* ApplicationMonitor.swift */,
BFC1F3A022AF0D0E003AC21A /* Main.storyboard */,
BFAC49D722B2EF170011E7C4 /* History */,
D53442912BAB202A00E133EE /* Map View */,
BF0BDE6622B4871400E1419D /* Settings */,
BF1D5D4122B083210062F474 /* Pasteboard */,
BFDB5B2322EF8F9600F74113 /* Components */,
BFAD2C48257AD4CB00FF9532 /* Types */,
BF0BDE5422B4568A00E1419D /* Extensions */,
BF1D5C8122B038030062F474 /* Resources */,
BF1D5C8522B038FE0062F474 /* Supporting Files */,
);
path = Clip;
sourceTree = "<group>";
};
BFC9E65122B1A22700974663 /* ClipKit */ = {
isa = PBXGroup;
children = (
BFC9E65222B1A22700974663 /* ClipKit.h */,
BF0BDE5022B4414A00E1419D /* UTI.swift */,
BF7E6FEF247C50480058F4D4 /* SwiftUI */,
BFC9E67022B1DE0A00974663 /* Database */,
BFC9E67322B2C6C500974663 /* Extensions */,
BFA2DF63247DD2A500E31E4D /* Resources */,
BFC9E65322B1A22700974663 /* Info.plist */,
);
path = ClipKit;
sourceTree = "<group>";
};
BFC9E67022B1DE0A00974663 /* Database */ = {
isa = PBXGroup;
children = (
BF1D5D4622B0869C0062F474 /* DatabaseManager.swift */,
BFC9E67A22B2C85C00974663 /* Model */,
);
path = Database;
sourceTree = "<group>";
};
BFC9E67322B2C6C500974663 /* Extensions */ = {
isa = PBXGroup;
children = (
BFC9E67B22B2CA4400974663 /* CFNotification+PasteboardListener.swift */,
BFC9E67122B2C4C400974663 /* Result+Conveniences.swift */,
BF0BDE5722B4757100E1419D /* UserDefaults+App.swift */,
BF7B9EE022B81C980042C873 /* Int+Bytes.swift */,
BF50E7D822C2BF010070E17B /* Bundle+AppGroups.swift */,
BF235BC0247D8B5300CCFCB0 /* UIPasteboard+PasteboardItem.swift */,
BFA2DF68247DD31500E31E4D /* UIColor+Clip.swift */,
BFAD46C82490588800451D6F /* UIInputView+Click.swift */,
D5D375182BAB4DF800213D84 /* UNNotification+Keys.swift */,
);
path = Extensions;
sourceTree = "<group>";
};
BFC9E67A22B2C85C00974663 /* Model */ = {
isa = PBXGroup;
children = (
BF1D5D4322B0867C0062F474 /* Model.xcdatamodeld */,
BF1D5D4A22B094DB0062F474 /* PasteboardItem.swift */,
BF1D5D4822B088F40062F474 /* PasteboardItemRepresentation.swift */,
);
path = Model;
sourceTree = "<group>";
};
BFDB5B2322EF8F9600F74113 /* Components */ = {
isa = PBXGroup;
children = (
BFDB5B1D22EF7B5000F74113 /* GradientView.swift */,
BFEF44AB2398693300095A92 /* ForwardingNavigationController.swift */,
);
path = Components;
sourceTree = "<group>";
};
D53442912BAB202A00E133EE /* Map View */ = {
isa = PBXGroup;
children = (
D534428D2BAB202700E133EE /* ClippingsMapView.swift */,
D5D375142BAB330200213D84 /* ClippingSheet.swift */,
);
path = "Map View";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
BFC9E64B22B1A22700974663 /* Headers */ = {
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
BFC9E65422B1A22700974663 /* ClipKit.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXHeadersBuildPhase section */
/* Begin PBXNativeTarget section */
BF7E6FDF247C4FCD0058F4D4 /* ClipBoard */ = {
isa = PBXNativeTarget;
buildConfigurationList = BF7E6FEC247C4FCD0058F4D4 /* Build configuration list for PBXNativeTarget "ClipBoard" */;
buildPhases = (
BF7E6FDC247C4FCD0058F4D4 /* Sources */,
BF7E6FDD247C4FCD0058F4D4 /* Frameworks */,
);
buildRules = (
);
dependencies = (
BFAD46CD24905E9400451D6F /* PBXTargetDependency */,
);
name = ClipBoard;
productName = ClipBoard;
productReference = BF7E6FE0247C4FCD0058F4D4 /* ClipBoard.appex */;
productType = "com.apple.product-type.app-extension";
};
BFC176FB2399BC4F0058AC51 /* ClipboardReader */ = {
isa = PBXNativeTarget;
buildConfigurationList = BFC177092399BC4F0058AC51 /* Build configuration list for PBXNativeTarget "ClipboardReader" */;
buildPhases = (
BFC176F82399BC4F0058AC51 /* Sources */,
BFC176F92399BC4F0058AC51 /* Frameworks */,
BFC176FA2399BC4F0058AC51 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = ClipboardReader;
productName = ClipboardReader;
productReference = BFC176FC2399BC4F0058AC51 /* ClipboardReader.appex */;
productType = "com.apple.product-type.app-extension";
};
BFC1F39822AF0D0E003AC21A /* Clip */ = {
isa = PBXNativeTarget;
buildConfigurationList = BFC1F3AB22AF0D0F003AC21A /* Build configuration list for PBXNativeTarget "Clip" */;
buildPhases = (
BFC1F39522AF0D0E003AC21A /* Sources */,
BFC1F39622AF0D0E003AC21A /* Frameworks */,
BFC1F39722AF0D0E003AC21A /* Resources */,
BF1D5C6D22B029190062F474 /* Embed Frameworks */,
BF1D5C9C22B042870062F474 /* Embed App Extensions */,
);
buildRules = (
);
dependencies = (
BF1D5C7D22B029E80062F474 /* PBXTargetDependency */,
BFC9E65622B1A22700974663 /* PBXTargetDependency */,
BFC177072399BC4F0058AC51 /* PBXTargetDependency */,
BF7E6FE6247C4FCD0058F4D4 /* PBXTargetDependency */,
);
name = Clip;
productName = ClipboardManager;
productReference = BFC1F39922AF0D0E003AC21A /* Clip.app */;
productType = "com.apple.product-type.application";
};
BFC9E64F22B1A22700974663 /* ClipKit */ = {
isa = PBXNativeTarget;
buildConfigurationList = BFC9E65922B1A22700974663 /* Build configuration list for PBXNativeTarget "ClipKit" */;
buildPhases = (
BFC9E64B22B1A22700974663 /* Headers */,
BFC9E64C22B1A22700974663 /* Sources */,
BFC9E64D22B1A22700974663 /* Frameworks */,
BFC9E64E22B1A22700974663 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = ClipKit;
productName = ClipKit;
productReference = BFC9E65022B1A22700974663 /* ClipKit.framework */;
productType = "com.apple.product-type.framework";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
BFC1F39122AF0D0E003AC21A /* Project object */ = {
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1150;
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = "Riley Testut";
TargetAttributes = {
BF7E6FDF247C4FCD0058F4D4 = {
CreatedOnToolsVersion = 11.5;
};
BFC176FB2399BC4F0058AC51 = {
CreatedOnToolsVersion = 11.1;
};
BFC1F39822AF0D0E003AC21A = {
CreatedOnToolsVersion = 10.2.1;
LastSwiftMigration = 1120;
SystemCapabilities = {
com.apple.ApplicationGroups.iOS = {
enabled = 1;
};
com.apple.BackgroundModes = {
enabled = 1;
};
com.apple.InterAppAudio = {
enabled = 1;
};
};
};
BFC9E64F22B1A22700974663 = {
CreatedOnToolsVersion = 10.2.1;
LastSwiftMigration = 1020;
};
};
};
buildConfigurationList = BFC1F39422AF0D0E003AC21A /* Build configuration list for PBXProject "Clip" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = BFC1F39022AF0D0E003AC21A;
productRefGroup = BFC1F39A22AF0D0E003AC21A /* Products */;
projectDirPath = "";
projectReferences = (
{
ProductGroup = BF1D5C6F22B029DB0062F474 /* Products */;
ProjectRef = BF1D5C6E22B029DB0062F474 /* Roxas.xcodeproj */;
},
);
projectRoot = "";
targets = (
BFC1F39822AF0D0E003AC21A /* Clip */,
BFC9E64F22B1A22700974663 /* ClipKit */,
BFC176FB2399BC4F0058AC51 /* ClipboardReader */,
BF7E6FDF247C4FCD0058F4D4 /* ClipBoard */,
);
};
/* End PBXProject section */
/* Begin PBXReferenceProxy section */
BF1D5C7522B029DC0062F474 /* Roxas.framework */ = {
isa = PBXReferenceProxy;
fileType = wrapper.framework;
path = Roxas.framework;
remoteRef = BF1D5C7422B029DC0062F474 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
BF1D5C7722B029DC0062F474 /* Roxas.framework */ = {
isa = PBXReferenceProxy;
fileType = wrapper.framework;
path = Roxas.framework;
remoteRef = BF1D5C7622B029DC0062F474 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
BF1D5C7922B029DC0062F474 /* RoxasTests.xctest */ = {
isa = PBXReferenceProxy;
fileType = wrapper.cfbundle;
path = RoxasTests.xctest;
remoteRef = BF1D5C7822B029DC0062F474 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
/* End PBXReferenceProxy section */
/* Begin PBXResourcesBuildPhase section */
BFC176FA2399BC4F0058AC51 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BFC177042399BC4F0058AC51 /* MainInterface.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
BFC1F39722AF0D0E003AC21A /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BFC1F3A722AF0D0F003AC21A /* LaunchScreen.storyboard in Resources */,
BFAC49D322B2EDA80011E7C4 /* ClippingTableViewCell.xib in Resources */,
BFC1F3A422AF0D0F003AC21A /* Assets.xcassets in Resources */,
BFC1F3A222AF0D0E003AC21A /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
BFC9E64E22B1A22700974663 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BFA2DF65247DD2B200E31E4D /* Colors.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
BF7E6FDC247C4FCD0058F4D4 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BF7E6FE3247C4FCD0058F4D4 /* KeyboardViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
BFC176F82399BC4F0058AC51 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BFC177012399BC4F0058AC51 /* NotificationViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
BFC1F39522AF0D0E003AC21A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BF1D5C7F22B02BF30062F474 /* PasteboardMonitor.swift in Sources */,
BFA714BA22C53F1700EE7236 /* ApplicationMonitor.swift in Sources */,
BFDB5B1E22EF7B5000F74113 /* GradientView.swift in Sources */,
BFC1F39F22AF0D0E003AC21A /* HistoryViewController.swift in Sources */,
BFF9E4932555F4F40052B1B2 /* LocationManager.swift in Sources */,
D5D375152BAB330200213D84 /* ClippingSheet.swift in Sources */,
BF8F76F3254CC0E0005AF18B /* SceneDelegate.swift in Sources */,
D534428E2BAB202700E133EE /* ClippingsMapView.swift in Sources */,
BF770E6522BC7688002A40FE /* UIDevice+Vibration.swift in Sources */,
BFC1F39D22AF0D0E003AC21A /* AppDelegate.swift in Sources */,
BFEF44AE2398693300095A92 /* ForwardingNavigationController.swift in Sources */,
BF0BDE5622B456A600E1419D /* PasteboardItem+ActivityItemSource.swift in Sources */,
BFAC49D522B2EDB20011E7C4 /* ClippingTableViewCell.swift in Sources */,
BFAD2C4A257AD4D600FF9532 /* PublishedPipeline.swift in Sources */,
BF0BDE5A22B4771300E1419D /* SettingsViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
BFC9E64C22B1A22700974663 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BFA2DF69247DD31500E31E4D /* UIColor+Clip.swift in Sources */,
BF0BDE5E22B47D4900E1419D /* UserDefaults+App.swift in Sources */,
BFC9E67C22B2CA4400974663 /* CFNotification+PasteboardListener.swift in Sources */,
BFC9E66222B1B03900974663 /* DatabaseManager.swift in Sources */,
BF0BDE5122B4414A00E1419D /* UTI.swift in Sources */,
BF235BC1247D8B5300CCFCB0 /* UIPasteboard+PasteboardItem.swift in Sources */,
BFAD46C92490588800451D6F /* UIInputView+Click.swift in Sources */,
BFA2DF61247DD23800E31E4D /* ClippingCell.swift in Sources */,
BFC9E66422B1B03C00974663 /* PasteboardItemRepresentation.swift in Sources */,
BFC9E66522B1B04000974663 /* Model.xcdatamodeld in Sources */,
BFAD46CB249058DE00451D6F /* SwitchKeyboardButton.swift in Sources */,
BFC9E67222B2C4C500974663 /* Result+Conveniences.swift in Sources */,
BFA2DF6B247DE47900E31E4D /* Preview.swift in Sources */,
BFA2DF62247DD23C00E31E4D /* Blur.swift in Sources */,
BF7B9EE122B81C980042C873 /* Int+Bytes.swift in Sources */,
BFC9E66322B1B03C00974663 /* PasteboardItem.swift in Sources */,
D5D375192BAB4DF800213D84 /* UNNotification+Keys.swift in Sources */,
BFA2DF60247DD21F00E31E4D /* Keyboard.swift in Sources */,
BF50E7D922C2BF010070E17B /* Bundle+AppGroups.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
BF1D5C7D22B029E80062F474 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
name = Roxas;
platformFilter = ios;
targetProxy = BF1D5C7C22B029E80062F474 /* PBXContainerItemProxy */;
};
BF7E6FE6247C4FCD0058F4D4 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = BF7E6FDF247C4FCD0058F4D4 /* ClipBoard */;
targetProxy = BF7E6FE5247C4FCD0058F4D4 /* PBXContainerItemProxy */;
};
BFAD46CD24905E9400451D6F /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = BFC9E64F22B1A22700974663 /* ClipKit */;
targetProxy = BFAD46CC24905E9400451D6F /* PBXContainerItemProxy */;
};
BFC177072399BC4F0058AC51 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = BFC176FB2399BC4F0058AC51 /* ClipboardReader */;
targetProxy = BFC177062399BC4F0058AC51 /* PBXContainerItemProxy */;
};
BFC9E65622B1A22700974663 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
platformFilter = ios;
target = BFC9E64F22B1A22700974663 /* ClipKit */;
targetProxy = BFC9E65522B1A22700974663 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
BFC177022399BC4F0058AC51 /* MainInterface.storyboard */ = {
isa = PBXVariantGroup;
children = (
BFC177032399BC4F0058AC51 /* Base */,
);
name = MainInterface.storyboard;
sourceTree = "<group>";
};
BFC1F3A022AF0D0E003AC21A /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
BFC1F3A122AF0D0E003AC21A /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
BFC1F3A522AF0D0F003AC21A /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
BFC1F3A622AF0D0F003AC21A /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
BF7E6FE8247C4FCD0058F4D4 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = ClipBoard/ClipBoard.entitlements;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 6XVY5G3U44;
INFOPLIST_FILE = ClipBoard/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.Clip.ClipBoard;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
BF7E6FE9247C4FCD0058F4D4 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = ClipBoard/ClipBoard.entitlements;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 6XVY5G3U44;
INFOPLIST_FILE = ClipBoard/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.Clip.ClipBoard;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
BFC1770A2399BC4F0058AC51 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = ClipboardReader/ClipboardReader.entitlements;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 6XVY5G3U44;
INFOPLIST_FILE = ClipboardReader/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.Clip.ClipboardReader;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
};
name = Debug;
};
BFC1770B2399BC4F0058AC51 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_ENTITLEMENTS = ClipboardReader/ClipboardReader.entitlements;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 6XVY5G3U44;
INFOPLIST_FILE = ClipboardReader/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.Clip.ClipboardReader;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
};
name = Release;
};
BFC1F3A922AF0D0F003AC21A /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 14;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.2;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
BFC1F3AA22AF0D0F003AC21A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
CODE_SIGN_IDENTITY = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 14;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.2;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
BFC1F3AC22AF0D0F003AC21A /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Clip/Clip.entitlements;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 6XVY5G3U44;
INFOPLIST_FILE = Clip/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.Clip;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
BFC1F3AD22AF0D0F003AC21A /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Clip/Clip.entitlements;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 6XVY5G3U44;
INFOPLIST_FILE = Clip/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.Clip;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
BFC9E65A22B1A22700974663 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 6XVY5G3U44;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = ClipKit/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.ClipKit;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Debug;
};
BFC9E65B22B1A22700974663 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = YES;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "";
CODE_SIGN_STYLE = Automatic;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 6XVY5G3U44;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
INFOPLIST_FILE = ClipKit/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.rileytestut.ClipKit;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SKIP_INSTALL = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
BF7E6FEC247C4FCD0058F4D4 /* Build configuration list for PBXNativeTarget "ClipBoard" */ = {
isa = XCConfigurationList;
buildConfigurations = (
BF7E6FE8247C4FCD0058F4D4 /* Debug */,
BF7E6FE9247C4FCD0058F4D4 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
BFC177092399BC4F0058AC51 /* Build configuration list for PBXNativeTarget "ClipboardReader" */ = {
isa = XCConfigurationList;
buildConfigurations = (
BFC1770A2399BC4F0058AC51 /* Debug */,
BFC1770B2399BC4F0058AC51 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
BFC1F39422AF0D0E003AC21A /* Build configuration list for PBXProject "Clip" */ = {
isa = XCConfigurationList;
buildConfigurations = (
BFC1F3A922AF0D0F003AC21A /* Debug */,
BFC1F3AA22AF0D0F003AC21A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
BFC1F3AB22AF0D0F003AC21A /* Build configuration list for PBXNativeTarget "Clip" */ = {
isa = XCConfigurationList;
buildConfigurations = (
BFC1F3AC22AF0D0F003AC21A /* Debug */,
BFC1F3AD22AF0D0F003AC21A /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
BFC9E65922B1A22700974663 /* Build configuration list for PBXNativeTarget "ClipKit" */ = {
isa = XCConfigurationList;
buildConfigurations = (
BFC9E65A22B1A22700974663 /* Debug */,
BFC9E65B22B1A22700974663 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCVersionGroup section */
BF1D5D4322B0867C0062F474 /* Model.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
BF1D5D4422B0867C0062F474 /* Model.xcdatamodel */,
);
currentVersion = BF1D5D4422B0867C0062F474 /* Model.xcdatamodel */;
path = Model.xcdatamodeld;
sourceTree = "<group>";
versionGroupType = wrapper.xcdatamodel;
};
/* End XCVersionGroup section */
};
rootObject = BFC1F39122AF0D0E003AC21A /* Project object */;
}
================================================
FILE: Clip.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:/Users/Riley/Library/Mobile Documents/com~apple~CloudDocs/Documents/Developer/Projects/Apps/Production/ClipboardManager/Clip.xcodeproj">
</FileRef>
</Workspace>
================================================
FILE: Clip.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
================================================
FILE: Clip.xcodeproj/xcshareddata/xcschemes/Clip.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BFC1F39822AF0D0E003AC21A"
BuildableName = "Clip.app"
BlueprintName = "Clip"
ReferencedContainer = "container:Clip.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BFC1F39822AF0D0E003AC21A"
BuildableName = "Clip.app"
BlueprintName = "Clip"
ReferencedContainer = "container:Clip.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
migratedStopOnEveryIssue = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BFC1F39822AF0D0E003AC21A"
BuildableName = "Clip.app"
BlueprintName = "Clip"
ReferencedContainer = "container:Clip.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "CGBITMAP_CONTEXT_LOG_ERRORS"
value = ""
isEnabled = "YES">
</EnvironmentVariable>
</EnvironmentVariables>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BFC1F39822AF0D0E003AC21A"
BuildableName = "Clip.app"
BlueprintName = "Clip"
ReferencedContainer = "container:Clip.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
================================================
FILE: Clip.xcodeproj/xcshareddata/xcschemes/ClipKit.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1110"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BFC9E64F22B1A22700974663"
BuildableName = "ClipKit.framework"
BlueprintName = "ClipKit"
ReferencedContainer = "container:Clip.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BFC9E64F22B1A22700974663"
BuildableName = "ClipKit.framework"
BlueprintName = "ClipKit"
ReferencedContainer = "container:Clip.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
================================================
FILE: Clip.xcodeproj/xcshareddata/xcschemes/ClipboardReader.xcscheme
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1110"
wasCreatedForAppExtension = "YES"
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BF2A236A2399BA8E0059E3E8"
BuildableName = "ClipboardReader.appex"
BlueprintName = "ClipboardReader"
ReferencedContainer = "container:Clip.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BFC1F39822AF0D0E003AC21A"
BuildableName = "Clip.app"
BlueprintName = "Clip"
ReferencedContainer = "container:Clip.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
askForAppToLaunch = "Yes"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BFC1F39822AF0D0E003AC21A"
BuildableName = "Clip.app"
BlueprintName = "Clip"
ReferencedContainer = "container:Clip.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BFC1F39822AF0D0E003AC21A"
BuildableName = "Clip.app"
BlueprintName = "Clip"
ReferencedContainer = "container:Clip.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
================================================
FILE: ClipBoard/ClipBoard.entitlements
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.com.rileytestut.Clip</string>
</array>
</dict>
</plist>
================================================
FILE: ClipBoard/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ALTAppGroups</key>
<array>
<string>group.com.rileytestut.Clip</string>
</array>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>ClipBoard</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>$(MARKETING_VERSION)</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>IsASCIICapable</key>
<false/>
<key>PrefersRightToLeft</key>
<false/>
<key>PrimaryLanguage</key>
<string>en-US</string>
<key>RequestsOpenAccess</key>
<true/>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.keyboard-service</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).KeyboardViewController</string>
</dict>
</dict>
</plist>
================================================
FILE: ClipBoard/KeyboardViewController.swift
================================================
//
// KeyboardViewController.swift
// ClipBoard
//
// Created by Riley Testut on 5/25/20.
// Copyright © 2020 Riley Testut. All rights reserved.
//
import UIKit
import SwiftUI
import Roxas
import ClipKit
class KeyboardViewController: UIInputViewController
{
private var hostingViewController: UIHostingController<AnyView>!
private var heightConstraint: NSLayoutConstraint!
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)
{
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
if DatabaseManager.shared.persistentContainer.persistentStoreCoordinator.persistentStores.isEmpty
{
if !self.hasFullAccess
{
// Use temporary in-memory store if we don't have full access.
let inMemoryStoreDescription = NSPersistentStoreDescription()
inMemoryStoreDescription.type = NSInMemoryStoreType
DatabaseManager.shared.persistentContainer.persistentStoreDescriptions = [inMemoryStoreDescription]
}
DatabaseManager.shared.persistentContainer.shouldAddStoresAsynchronously = false
DatabaseManager.shared.prepare { (result) in
switch result
{
case .failure(let error): print("Failed to prepare database:", error)
case .success: break
}
}
}
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad()
{
super.viewDidLoad()
self.inputView?.allowsSelfSizing = true
let rootView = Keyboard(inputViewController: self)
.environment(\.managedObjectContext, DatabaseManager.shared.persistentContainer.viewContext)
self.hostingViewController = UIHostingController(rootView: AnyView(rootView))
self.hostingViewController.view.backgroundColor = .clear
self.addChild(self.hostingViewController)
self.inputView?.addSubview(self.hostingViewController.view, pinningEdgesWith: .zero)
self.hostingViewController.didMove(toParent: self)
self.view.setNeedsUpdateConstraints()
}
override func updateViewConstraints()
{
super.updateViewConstraints()
if self.heightConstraint == nil
{
for constraint in self.view.constraintsAffectingLayout(for: .vertical)
{
// UIKit embeds height constraint, even if allowsSelfSizing is true.
// Must set to non-required priority, or else it will conflict with
// our own self-sizing constraint (annoyingly).
constraint.priority = .defaultHigh
}
self.heightConstraint = self.view.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height / 2)
self.heightConstraint.isActive = true
}
}
override func viewWillLayoutSubviews()
{
if let heightConstraint = self.heightConstraint
{
heightConstraint.constant = UIScreen.main.bounds.height / 2
}
super.viewWillLayoutSubviews()
}
}
================================================
FILE: ClipKit/ClipKit.h
================================================
//
// ClipKit.h
// ClipKit
//
// Created by Riley Testut on 6/12/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
#import <UIKit/UIKit.h>
//! Project version number for ClipKit.
FOUNDATION_EXPORT double ClipKitVersionNumber;
//! Project version string for ClipKit.
FOUNDATION_EXPORT const unsigned char ClipKitVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <ClipKit/PublicHeader.h>
================================================
FILE: ClipKit/Database/DatabaseManager.swift
================================================
//
// DatabaseManager.swift
// Clip
//
// Created by Riley Testut on 6/11/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import CoreData
import CoreLocation
import Roxas
private extension UserDefaults
{
@NSManaged var previousHistoryToken: Data?
}
public enum PasteboardError: LocalizedError
{
case unsupportedImageFormat
case unsupportedItem
case noItem
case duplicateItem
public var errorDescription: String? {
switch self
{
case .unsupportedImageFormat: return NSLocalizedString("Unsupported image format.", comment: "")
case .unsupportedItem: return NSLocalizedString("Unsupported clipboard item.", comment: "")
case .noItem: return NSLocalizedString("No clipboard item.", comment: "")
case .duplicateItem: return NSLocalizedString("Duplicate item.", comment: "")
}
}
}
private class PersistentContainer: RSTPersistentContainer
{
override class func defaultDirectoryURL() -> URL
{
guard let appGroup = Bundle.main.appGroups.first else { return super.defaultDirectoryURL() }
let sharedDirectoryURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroup)!
let databaseDirectoryURL = sharedDirectoryURL.appendingPathComponent("Database")
try? FileManager.default.createDirectory(at: databaseDirectoryURL, withIntermediateDirectories: true, attributes: nil)
print("Database URL:", databaseDirectoryURL)
return databaseDirectoryURL
}
}
public class DatabaseManager
{
public static let shared = DatabaseManager()
public let persistentContainer: RSTPersistentContainer = PersistentContainer(name: "Model", bundle: Bundle(for: DatabaseManager.self))
public private(set) var isStarted = false
private var prepareCompletionHandlers = [(Result<Void, Error>) -> Void]()
private let dispatchQueue = DispatchQueue(label: "com.rileytestut.Clip.DatabaseManager")
private var previousHistoryToken: NSPersistentHistoryToken? {
set {
guard let value = newValue else {
UserDefaults.shared.previousHistoryToken = nil
return
}
let data = try? NSKeyedArchiver.archivedData(withRootObject: value, requiringSecureCoding: true)
UserDefaults.shared.previousHistoryToken = data
}
get {
guard let data = UserDefaults.shared.previousHistoryToken else { return nil }
let token = try? NSKeyedUnarchiver.unarchivedObject(ofClass: NSPersistentHistoryToken.self, from: data)
return token
}
}
private init()
{
}
public func prepare(completionHandler: @escaping (Result<Void, Error>) -> Void)
{
func finish(_ result: Result<Void, Error>)
{
self.dispatchQueue.async {
switch result
{
case .success: self.isStarted = true
case .failure: break
}
self.prepareCompletionHandlers.forEach { $0(result) }
self.prepareCompletionHandlers.removeAll()
}
}
self.dispatchQueue.async {
self.prepareCompletionHandlers.append(completionHandler)
guard self.prepareCompletionHandlers.count == 1 else { return }
guard !self.isStarted else { return finish(.success(())) }
self.persistentContainer.persistentStoreDescriptions.first?.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
self.persistentContainer.loadPersistentStores { (description, error) in
let result = Result(description, error).map { _ in () }
finish(result)
self.purge()
}
}
}
public func refresh()
{
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
let fetchRequest = NSPersistentHistoryChangeRequest.fetchHistory(after: self.previousHistoryToken)
do
{
guard
let result = try context.execute(fetchRequest) as? NSPersistentHistoryResult,
let transactions = result.result as? [NSPersistentHistoryTransaction]
else { return }
DispatchQueue.main.async {
self.persistentContainer.viewContext.undoManager?.disableUndoRegistration()
defer { self.persistentContainer.viewContext.undoManager?.enableUndoRegistration() }
for transaction in transactions
{
self.persistentContainer.viewContext.mergeChanges(fromContextDidSave: transaction.objectIDNotification())
}
if let token = transactions.last?.token
{
self.previousHistoryToken = token
}
}
}
catch let error as CocoaError where error.code.rawValue == NSPersistentHistoryTokenExpiredError
{
self.previousHistoryToken = nil
self.refresh()
}
catch
{
print("Failed to fetch change history.", error)
}
}
}
public func purge()
{
// In-memory contexts don't support history tracking.
guard let description = DatabaseManager.shared.persistentContainer.persistentStoreDescriptions.first, description.type != NSInMemoryStoreType else { return }
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
if let token = self.previousHistoryToken
{
let deleteHistoryRequest = NSPersistentHistoryChangeRequest.deleteHistory(before: token)
do { try context.execute(deleteHistoryRequest) }
catch { print("Failed to delete persistent distory.", error) }
}
do
{
let fetchRequest = PasteboardItem.historyFetchRequest() as! NSFetchRequest<NSManagedObjectID>
fetchRequest.resultType = .managedObjectIDResultType
let objectIDs = try context.fetch(fetchRequest)
let deletionFetchRequest = PasteboardItem.fetchRequest() as NSFetchRequest<NSFetchRequestResult>
deletionFetchRequest.predicate = NSPredicate(format: "NOT (SELF IN %@)", objectIDs)
let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: deletionFetchRequest)
batchDeleteRequest.resultType = .resultTypeObjectIDs
guard
let result = try context.execute(batchDeleteRequest) as? NSBatchDeleteResult,
let deletedObjectIDs = result.result as? [NSManagedObjectID]
else { return }
let changes = [NSDeletedObjectsKey: deletedObjectIDs]
DispatchQueue.main.async {
NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [DatabaseManager.shared.persistentContainer.viewContext])
}
}
catch
{
print("Failed to delete pasteboard items.", error)
}
}
}
}
public extension DatabaseManager
{
func savePasteboard(location: CLLocation?, completionHandler: @escaping (Result<Void, Error>) -> Void)
{
do
{
guard !UIPasteboard.general.hasColors else {
throw PasteboardError.unsupportedItem // Accessing UIPasteboard.items causes crash as of iOS 12.3 if it contains a UIColor.
}
print("Did update pasteboard!")
guard let itemProvider = UIPasteboard.general.itemProviders.first else { throw PasteboardError.noItem }
guard !itemProvider.registeredTypeIdentifiers.contains(UTI.clipping) else { throw PasteboardError.duplicateItem } // Ignore copies that we made from the app.
let context = DatabaseManager.shared.persistentContainer.newBackgroundContext()
PasteboardItemRepresentation.representations(for: itemProvider, in: context) { (representations) in
do
{
guard let pasteboardItem = PasteboardItem(representations: representations, context: context) else { throw PasteboardError.noItem }
pasteboardItem.location = location
print(pasteboardItem)
let fetchRequest = PasteboardItem.fetchRequest() as NSFetchRequest<PasteboardItem>
fetchRequest.predicate = NSPredicate(format: "%K == NO", #keyPath(PasteboardItem.isMarkedForDeletion))
fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \PasteboardItem.date, ascending: false)]
fetchRequest.relationshipKeyPathsForPrefetching = ["representations"]
fetchRequest.includesPendingChanges = false
fetchRequest.fetchLimit = 1
if let previousItem = try context.fetch(fetchRequest).first
{
let representations = pasteboardItem.representations.reduce(into: [:], { ($0[$1.type] = $1.value as? NSObject) })
let previousRepresentations = previousItem.representations.reduce(into: [:], { ($0[$1.type] = $1.value as? NSObject) })
guard representations != previousRepresentations else {
throw PasteboardError.duplicateItem
}
}
guard let _ = pasteboardItem.preferredRepresentation else { throw PasteboardError.unsupportedItem }
context.transactionAuthor = Bundle.main.bundleIdentifier
try context.save()
let center = CFNotificationCenterGetDarwinNotifyCenter()
CFNotificationCenterPostNotification(center, .didChangePasteboard, nil, nil, true)
DispatchQueue.main.async {
completionHandler(.success(()))
}
}
catch
{
DispatchQueue.main.async {
print("Failed to handle pasteboard item.", error)
completionHandler(.failure(error))
}
}
}
}
catch
{
completionHandler(.failure(error))
}
}
}
================================================
FILE: ClipKit/Database/Model/Model.xcdatamodeld/Model.xcdatamodel/contents
================================================
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="22757" systemVersion="23E5211a" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
<entity name="PasteboardItem" representedClassName="PasteboardItem" syncable="YES">
<attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="isMarkedForDeletion" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
<attribute name="latitude" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="NO"/>
<attribute name="longitude" optional="YES" attributeType="Double" defaultValueString="0.0" usesScalarValueType="NO"/>
<relationship name="preferredRepresentation" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PasteboardItemRepresentation" inverseName="preferringItem" inverseEntity="PasteboardItemRepresentation"/>
<relationship name="representations" toMany="YES" deletionRule="Cascade" ordered="YES" destinationEntity="PasteboardItemRepresentation" inverseName="item" inverseEntity="PasteboardItemRepresentation"/>
</entity>
<entity name="PasteboardItemRepresentation" representedClassName="PasteboardItemRepresentation" syncable="YES">
<attribute name="data" optional="YES" attributeType="Binary" allowsExternalBinaryDataStorage="YES"/>
<attribute name="string" optional="YES" attributeType="String"/>
<attribute name="type" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="url" optional="YES" attributeType="URI"/>
<attribute name="uti" optional="YES" attributeType="String"/>
<relationship name="item" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PasteboardItem" inverseName="representations" inverseEntity="PasteboardItem"/>
<relationship name="preferringItem" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="PasteboardItem" inverseName="preferredRepresentation" inverseEntity="PasteboardItem"/>
</entity>
</model>
================================================
FILE: ClipKit/Database/Model/PasteboardItem.swift
================================================
//
// PasteboardItem.swift
// Clip
//
// Created by Riley Testut on 6/11/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import CoreData
import MobileCoreServices
import CoreLocation
private extension PasteboardItemRepresentation.RepresentationType
{
var priority: Int {
switch self
{
case .attributedText: return 0
case .text: return 1
case .url: return 2
case .image: return 3
}
}
}
@objc(PasteboardItem)
public class PasteboardItem: NSManagedObject, Identifiable
{
/* Properties */
@NSManaged public private(set) var date: Date
@NSManaged public var isMarkedForDeletion: Bool
public var location: CLLocation? {
get {
guard let latitude, let longitude else { return nil }
let coordinate = CLLocation(latitude: latitude.doubleValue, longitude: longitude.doubleValue)
return coordinate
}
set {
self.latitude = newValue?.coordinate.latitude as? NSNumber
self.longitude = newValue?.coordinate.longitude as? NSNumber
}
}
@NSManaged private var latitude: NSNumber?
@NSManaged private var longitude: NSNumber?
/* Relationships */
@nonobjc public var representations: [PasteboardItemRepresentation] {
return self._representations.array as! [PasteboardItemRepresentation]
}
@NSManaged @objc(representations) private var _representations: NSOrderedSet
@NSManaged public var preferredRepresentation: PasteboardItemRepresentation?
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)
{
super.init(entity: entity, insertInto: context)
}
public init?(representations: [PasteboardItemRepresentation], context: NSManagedObjectContext)
{
guard !representations.isEmpty else { return nil }
super.init(entity: PasteboardItem.entity(), insertInto: context)
self._representations = NSOrderedSet(array: representations)
let prioritizedRepresentationTypes = PasteboardItemRepresentation.RepresentationType.allCases.sorted { $0.priority > $1.priority }
for type in prioritizedRepresentationTypes
{
guard let representation = representations.first(where: { $0.type == type }) else { continue }
self.preferredRepresentation = representation
break
}
}
override public func awakeFromInsert()
{
super.awakeFromInsert()
self.date = Date()
}
}
public extension PasteboardItem
{
@nonobjc class func fetchRequest() -> NSFetchRequest<PasteboardItem>
{
return NSFetchRequest<PasteboardItem>(entityName: "PasteboardItem")
}
class func historyFetchRequest() -> NSFetchRequest<PasteboardItem>
{
let fetchRequest = PasteboardItem.fetchRequest() as NSFetchRequest<PasteboardItem>
fetchRequest.predicate = NSPredicate(format: "%K == NO", #keyPath(PasteboardItem.isMarkedForDeletion))
fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \PasteboardItem.date, ascending: false)]
fetchRequest.fetchLimit = UserDefaults.shared.historyLimit.rawValue
return fetchRequest
}
}
// SwiftUI
extension PasteboardItem
{
class func make(item: NSItemProviderWriting, date: Date = Date(), context: NSManagedObjectContext) -> PasteboardItem
{
let itemProvider = NSItemProvider(object: item)
let semaphore = DispatchSemaphore(value: 0)
let childContext = DatabaseManager.shared.persistentContainer.newBackgroundContext()
var objectID: NSManagedObjectID!
PasteboardItemRepresentation.representations(for: itemProvider, in: childContext) { (representations) in
let item = PasteboardItem(representations: representations, context: childContext)!
item.date = date
try! childContext.obtainPermanentIDs(for: [item])
objectID = item.objectID
try! childContext.save()
semaphore.signal()
}
semaphore.wait()
let pasteboardItem = context.object(with: objectID) as! PasteboardItem
return pasteboardItem
}
}
================================================
FILE: ClipKit/Database/Model/PasteboardItemRepresentation.swift
================================================
//
// PasteboardItemRepresentation.swift
// Clip
//
// Created by Riley Testut on 6/11/19.
// Copyright © 2019 Riley Testut. All rights reserved.
//
import UIKit
import CoreData
import MobileCoreServices
extension PasteboardItemRepresentation
{
@objc public enum RepresentationType: Int16, CaseIterable
{
case text
case attributedText
case url
case image
public var localizedName: String {
switch self
{
case .text: return NSLocalizedString("Text", comment: "")
case .attributedText: return NSLocalizedString("Text", comment: "")
case .url: return NSLocalizedString("URL", comment: "")
case .image: return NSLocalizedString("Image", comment: "")
}
}
}
}
@objc(PasteboardItemRepresentation)
public class PasteboardItemRepresentation: NSManagedObject
{
/* Properties */
@NSManaged public private(set) var uti: String
@NSManaged public private(set) var type: RepresentationType
@NSManaged private var data: Data?
@NSManaged private var string: String?
@NSManaged private var url: URL?
/* Relationships */
@NSManaged public var item: PasteboardItem?
@NSManaged private var preferringItem: PasteboardItem? // Inverse of PasteboardItem.preferredRepresentation.
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)
{
super.init(entity: entity, insertInto: context)
}
private init(uti: String, type: RepresentationType, context: NSManagedObjectContext)
{
super.init(entity: PasteboardItemRepresentation.entity(), insertInto: context)
self.uti = uti
self.type = type
}
private convenience init(uti: String, text: String, context: NSManagedObjectContext)
{
self.init(uti: uti, type: .text, context: context)
self.string = text
}
private convenience init(uti: String, data: Data, type: RepresentationType, context: NSManagedObjectContext)
{
self.init(uti: uti, type: type, context: context)
self.data = data
}
private convenience init(uti: String, url: URL, context: NSManagedObjectContext)
{
self.init(uti: uti, type: .url, context: context)
self.url = url
}
public static func representations(for itemProvider: NSItemProvider, in context: NSManagedObjectContext, completionHandler: @escaping ([PasteboardItemRepresentation]) -> Void)
{
var representations = [PasteboardItemRepresentation]()
let dispatchGroup = DispatchGroup()
let supportedTextUTIs = [kUTTypeUTF8PlainText, kUTTypePlainText, kUTTypeText]
if let uti = supportedTextUTIs.first(where: { itemProvider.hasItemConformingToTypeIdentifier($0 as String) }), itemProvider.canLoadObject(ofClass: NSString.self)
{
dispatchGroup.enter()
itemProvider.loadObject(ofClass: NSString.self) { (text, error) in
context.perform {
switch Result(text, error)
{
case .failure(let error): print(error)
case .success(let text):
let representation = PasteboardItemRepresentation(uti: uti as String, text: text as! String, context: context)
representations.append(representation)
}
dispatchGroup.leave()
}
}
}
let supportedAttributedTextUTIs = [kUTTypeRTF, kUTTypeHTML, kUTTypeFlatRTFD, kUTTypeRTFD]
if let uti = supportedAttributedTextUTIs.first(where: { itemProvider.hasItemConformingToTypeIdentifier($0 as String) }), itemProvider.canLoadObject(ofClass: NSAttributedString.self)
{
dispatchGroup.enter()
itemProvider.loadDataRepresentation(forTypeIdentifier: uti as String) { (data, error) in
context.perform {
switch Result(data, error)
{
case .failure(let error): print(error)
case .success(let data):
let representation = PasteboardItemRepresentation(uti: uti as String, data: data, type: .attributedText, context: context)
representations.append(representation)
}
dispatchGroup.leave()
}
}
}
let supportedImageUTIs = [kUTTypePNG, kUTTypeJPEG, kUTTypeImage]
if let uti = supportedImageUTIs.first(where: { itemProvider.hasItemConformingToTypeIdentifier($0 as String) }), itemProvider.canLoadObject(ofClass: UIImage.self)
{
dispatchGroup.enter()
itemProvider.loadDataRepresentation(forTypeIdentifier: uti as String) { (data, error) in
context.perform {
switch Result(data,
gitextract_7pamx728/ ├── .gitignore ├── .gitmodules ├── Clip/ │ ├── AppDelegate.swift │ ├── ApplicationMonitor.swift │ ├── Base.lproj/ │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Clip.entitlements │ ├── Components/ │ │ ├── ForwardingNavigationController.swift │ │ └── GradientView.swift │ ├── Extensions/ │ │ ├── PasteboardItem+ActivityItemSource.swift │ │ └── UIDevice+Vibration.swift │ ├── History/ │ │ ├── ClippingTableViewCell.swift │ │ ├── ClippingTableViewCell.xib │ │ └── HistoryViewController.swift │ ├── Info.plist │ ├── Map View/ │ │ ├── ClippingSheet.swift │ │ └── ClippingsMapView.swift │ ├── Pasteboard/ │ │ ├── LocationManager.swift │ │ └── PasteboardMonitor.swift │ ├── Resources/ │ │ └── Assets.xcassets/ │ │ ├── AppIcon.appiconset/ │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── Settings.imageset/ │ │ └── Contents.json │ ├── SceneDelegate.swift │ ├── Settings/ │ │ └── SettingsViewController.swift │ └── Types/ │ └── PublishedPipeline.swift ├── Clip.xcodeproj/ │ ├── project.pbxproj │ ├── project.xcworkspace/ │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata/ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata/ │ └── xcschemes/ │ ├── Clip.xcscheme │ ├── ClipKit.xcscheme │ └── ClipboardReader.xcscheme ├── ClipBoard/ │ ├── ClipBoard.entitlements │ ├── Info.plist │ └── KeyboardViewController.swift ├── ClipKit/ │ ├── ClipKit.h │ ├── Database/ │ │ ├── DatabaseManager.swift │ │ └── Model/ │ │ ├── Model.xcdatamodeld/ │ │ │ └── Model.xcdatamodel/ │ │ │ └── contents │ │ ├── PasteboardItem.swift │ │ └── PasteboardItemRepresentation.swift │ ├── Extensions/ │ │ ├── Bundle+AppGroups.swift │ │ ├── CFNotification+PasteboardListener.swift │ │ ├── Int+Bytes.swift │ │ ├── Result+Conveniences.swift │ │ ├── UIColor+Clip.swift │ │ ├── UIInputView+Click.swift │ │ ├── UIPasteboard+PasteboardItem.swift │ │ ├── UNNotification+Keys.swift │ │ └── UserDefaults+App.swift │ ├── Info.plist │ ├── Resources/ │ │ └── Colors.xcassets/ │ │ ├── Contents.json │ │ ├── LightPink.colorset/ │ │ │ └── Contents.json │ │ └── Pink.colorset/ │ │ └── Contents.json │ ├── SwiftUI/ │ │ ├── Blur.swift │ │ ├── ClippingCell.swift │ │ ├── Keyboard.swift │ │ ├── Preview.swift │ │ └── SwitchKeyboardButton.swift │ └── UTI.swift ├── ClipboardReader/ │ ├── Base.lproj/ │ │ └── MainInterface.storyboard │ ├── ClipboardReader.entitlements │ ├── Info.plist │ └── NotificationViewController.swift ├── README.md └── UNLICENSE
Condensed preview — 64 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (254K chars).
[
{
"path": ".gitignore",
"chars": 322,
"preview": "# 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*"
},
{
"path": ".gitmodules",
"chars": 108,
"preview": "[submodule \"Dependencies/Roxas\"]\n\tpath = Dependencies/Roxas\n\turl = https://github.com/rileytestut/Roxas.git\n"
},
{
"path": "Clip/AppDelegate.swift",
"chars": 4977,
"preview": "//\n// AppDelegate.swift\n// Clip\n//\n// Created by Riley Testut on 6/10/19.\n// Copyright © 2019 Riley Testut. All righ"
},
{
"path": "Clip/ApplicationMonitor.swift",
"chars": 4332,
"preview": "//\n// ApplicationMonitor.swift\n// Clip\n//\n// Created by Riley Testut on 6/27/19.\n// Copyright © 2019 Riley Testut. A"
},
{
"path": "Clip/Base.lproj/LaunchScreen.storyboard",
"chars": 1658,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard"
},
{
"path": "Clip/Base.lproj/Main.storyboard",
"chars": 22030,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
},
{
"path": "Clip/Clip.entitlements",
"chars": 340,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Clip/Components/ForwardingNavigationController.swift",
"chars": 456,
"preview": "//\n// ForwardingNavigationController.swift\n// AltStore\n//\n// Created by Riley Testut on 10/24/19.\n// Copyright © 201"
},
{
"path": "Clip/Components/GradientView.swift",
"chars": 714,
"preview": "//\n// GradientView.swift\n// Clip\n//\n// Created by Riley Testut on 7/27/19.\n// Copyright © 2019 Riley Testut. All rig"
},
{
"path": "Clip/Extensions/PasteboardItem+ActivityItemSource.swift",
"chars": 2346,
"preview": "//\n// PasteboardItem+ActivityItemSource.swift\n// Clip\n//\n// Created by Riley Testut on 6/14/19.\n// Copyright © 2019 "
},
{
"path": "Clip/Extensions/UIDevice+Vibration.swift",
"chars": 1561,
"preview": "//\n// UIDevice+Vibration.swift\n// DeltaCore\n//\n// Created by Riley Testut on 11/28/16.\n// Copyright © 2016 Riley Tes"
},
{
"path": "Clip/History/ClippingTableViewCell.swift",
"chars": 867,
"preview": "//\n// ClippingTableViewCell.swift\n// Clip\n//\n// Created by Riley Testut on 6/13/19.\n// Copyright © 2019 Riley Testut"
},
{
"path": "Clip/History/ClippingTableViewCell.xib",
"chars": 14612,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.XIB\" version=\"3.0\" toolsVe"
},
{
"path": "Clip/History/HistoryViewController.swift",
"chars": 22781,
"preview": "//\n// HistoryViewController.swift\n// Clip\n//\n// Created by Riley Testut on 6/10/19.\n// Copyright © 2019 Riley Testut"
},
{
"path": "Clip/Info.plist",
"chars": 3532,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Clip/Map View/ClippingSheet.swift",
"chars": 1376,
"preview": "//\n// ClippingSheet.swift\n// Clip\n//\n// Created by Riley Testut on 3/20/24.\n// Copyright © 2024 Riley Testut. All ri"
},
{
"path": "Clip/Map View/ClippingsMapView.swift",
"chars": 1467,
"preview": "//\n// HistoryMapView.swift\n// Clip\n//\n// Created by Riley Testut on 3/20/24.\n// Copyright © 2024 Riley Testut. All r"
},
{
"path": "Clip/Pasteboard/LocationManager.swift",
"chars": 4836,
"preview": "//\n// LocationManager.swift\n// Clip\n//\n// Created by Riley Testut on 11/6/20.\n// Copyright © 2020 Riley Testut. All "
},
{
"path": "Clip/Pasteboard/PasteboardMonitor.swift",
"chars": 4610,
"preview": "//\n// PasteboardMonitor.swift\n// Clip\n//\n// Created by Riley Testut on 6/11/19.\n// Copyright © 2019 Riley Testut. Al"
},
{
"path": "Clip/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json",
"chars": 212,
"preview": "{\n \"images\" : [\n {\n \"filename\" : \"Clip1024.png\",\n \"idiom\" : \"universal\",\n \"platform\" : \"ios\",\n \""
},
{
"path": "Clip/Resources/Assets.xcassets/Contents.json",
"chars": 62,
"preview": "{\n \"info\" : {\n \"version\" : 1,\n \"author\" : \"xcode\"\n }\n}"
},
{
"path": "Clip/Resources/Assets.xcassets/Settings.imageset/Contents.json",
"chars": 224,
"preview": "{\n \"images\" : [\n {\n \"idiom\" : \"universal\",\n \"filename\" : \"gear_1.pdf\"\n }\n ],\n \"info\" : {\n \"version"
},
{
"path": "Clip/SceneDelegate.swift",
"chars": 3339,
"preview": "//\n// SceneDelegate.swift\n// Clip\n//\n// Created by Riley Testut on 10/30/20.\n// Copyright © 2020 Riley Testut. All r"
},
{
"path": "Clip/Settings/SettingsViewController.swift",
"chars": 2011,
"preview": "//\n// SettingsViewController.swift\n// Clip\n//\n// Created by Riley Testut on 6/14/19.\n// Copyright © 2019 Riley Testu"
},
{
"path": "Clip/Types/PublishedPipeline.swift",
"chars": 710,
"preview": "//\n// PublishedPipeline.swift\n// Clip\n//\n// Created by Riley Testut on 12/4/20.\n// Copyright © 2020 Riley Testut. Al"
},
{
"path": "Clip.xcodeproj/project.pbxproj",
"chars": 56705,
"preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
},
{
"path": "Clip.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
"chars": 269,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n version = \"1.0\">\n <FileRef\n location = \"self:/Users/Riley/Li"
},
{
"path": "Clip.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
"chars": 238,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "Clip.xcodeproj/xcshareddata/xcschemes/Clip.xcscheme",
"chars": 3422,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"1020\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "Clip.xcodeproj/xcshareddata/xcschemes/ClipKit.xcscheme",
"chars": 2375,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"1110\"\n version = \"1.3\">\n <BuildAction\n "
},
{
"path": "Clip.xcodeproj/xcshareddata/xcschemes/ClipboardReader.xcscheme",
"chars": 3514,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n LastUpgradeVersion = \"1110\"\n wasCreatedForAppExtension = \"YES\"\n ve"
},
{
"path": "ClipBoard/ClipBoard.entitlements",
"chars": 303,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "ClipBoard/Info.plist",
"chars": 1365,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "ClipBoard/KeyboardViewController.swift",
"chars": 3304,
"preview": "//\n// KeyboardViewController.swift\n// ClipBoard\n//\n// Created by Riley Testut on 5/25/20.\n// Copyright © 2020 Riley "
},
{
"path": "ClipKit/ClipKit.h",
"chars": 481,
"preview": "//\n// ClipKit.h\n// ClipKit\n//\n// Created by Riley Testut on 6/12/19.\n// Copyright © 2019 Riley Testut. All rights re"
},
{
"path": "ClipKit/Database/DatabaseManager.swift",
"chars": 11154,
"preview": "//\n// DatabaseManager.swift\n// Clip\n//\n// Created by Riley Testut on 6/11/19.\n// Copyright © 2019 Riley Testut. All "
},
{
"path": "ClipKit/Database/Model/Model.xcdatamodeld/Model.xcdatamodel/contents",
"chars": 2237,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<model type=\"com.apple.IDECoreDataModeler.DataModel\" documentVer"
},
{
"path": "ClipKit/Database/Model/PasteboardItem.swift",
"chars": 4360,
"preview": "//\n// PasteboardItem.swift\n// Clip\n//\n// Created by Riley Testut on 6/11/19.\n// Copyright © 2019 Riley Testut. All r"
},
{
"path": "ClipKit/Database/Model/PasteboardItemRepresentation.swift",
"chars": 9721,
"preview": "//\n// PasteboardItemRepresentation.swift\n// Clip\n//\n// Created by Riley Testut on 6/11/19.\n// Copyright © 2019 Riley"
},
{
"path": "ClipKit/Extensions/Bundle+AppGroups.swift",
"chars": 346,
"preview": "//\n// Bundle+AppGroups.swift\n// ClipKit\n//\n// Created by Riley Testut on 6/25/19.\n// Copyright © 2019 Riley Testut. "
},
{
"path": "ClipKit/Extensions/CFNotification+PasteboardListener.swift",
"chars": 508,
"preview": "//\n// CFNotification+PasteboardListener.swift\n// ClipKit\n//\n// Created by Riley Testut on 6/13/19.\n// Copyright © 20"
},
{
"path": "ClipKit/Extensions/Int+Bytes.swift",
"chars": 254,
"preview": "//\n// Int+Bytes.swift\n// ClipKit\n//\n// Created by Riley Testut on 6/17/19.\n// Copyright © 2019 Riley Testut. All rig"
},
{
"path": "ClipKit/Extensions/Result+Conveniences.swift",
"chars": 1645,
"preview": "//\n// Result+Conveniences.swift\n// AltStore\n//\n// Created by Riley Testut on 5/22/19.\n// Copyright © 2019 Riley Test"
},
{
"path": "ClipKit/Extensions/UIColor+Clip.swift",
"chars": 411,
"preview": "//\n// UIColor+Clip.swift\n// Clip\n//\n// Created by Riley Testut on 7/29/19.\n// Copyright © 2019 Riley Testut. All rig"
},
{
"path": "ClipKit/Extensions/UIInputView+Click.swift",
"chars": 374,
"preview": "//\n// UIInputView+Click.swift\n// ClipKit\n//\n// Created by Riley Testut on 6/9/20.\n// Copyright © 2020 Riley Testut. "
},
{
"path": "ClipKit/Extensions/UIPasteboard+PasteboardItem.swift",
"chars": 487,
"preview": "//\n// UIPasteboard+PasteboardItem.swift\n// ClipKit\n//\n// Created by Riley Testut on 5/26/20.\n// Copyright © 2020 Ril"
},
{
"path": "ClipKit/Extensions/UNNotification+Keys.swift",
"chars": 508,
"preview": "//\n// UNNotification+Keys.swift\n// ClipKit\n//\n// Created by Riley Testut on 3/20/24.\n// Copyright © 2024 Riley Testu"
},
{
"path": "ClipKit/Extensions/UserDefaults+App.swift",
"chars": 1054,
"preview": "//\n// UserDefaults+App.swift\n// Clip\n//\n// Created by Riley Testut on 6/14/19.\n// Copyright © 2019 Riley Testut. All"
},
{
"path": "ClipKit/Info.plist",
"chars": 726,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "ClipKit/Resources/Colors.xcassets/Contents.json",
"chars": 63,
"preview": "{\n \"info\" : {\n \"author\" : \"xcode\",\n \"version\" : 1\n }\n}\n"
},
{
"path": "ClipKit/Resources/Colors.xcassets/LightPink.colorset/Contents.json",
"chars": 329,
"preview": "{\n \"colors\" : [\n {\n \"color\" : {\n \"color-space\" : \"srgb\",\n \"components\" : {\n \"alpha\" : \"1"
},
{
"path": "ClipKit/Resources/Colors.xcassets/Pink.colorset/Contents.json",
"chars": 329,
"preview": "{\n \"colors\" : [\n {\n \"color\" : {\n \"color-space\" : \"srgb\",\n \"components\" : {\n \"alpha\" : \"1"
},
{
"path": "ClipKit/SwiftUI/Blur.swift",
"chars": 1400,
"preview": "//\n// Blur.swift\n// ClipKit\n//\n// Created by Riley Testut on 5/25/20.\n// Copyright © 2020 Riley Testut. All rights r"
},
{
"path": "ClipKit/SwiftUI/ClippingCell.swift",
"chars": 2904,
"preview": "//\n// ClippingCell.swift\n// ClipKit\n//\n// Created by Riley Testut on 5/25/20.\n// Copyright © 2020 Riley Testut. All "
},
{
"path": "ClipKit/SwiftUI/Keyboard.swift",
"chars": 8287,
"preview": "//\n// Keyboard.swift\n// ClipKit\n//\n// Created by Riley Testut on 5/25/20.\n// Copyright © 2020 Riley Testut. All righ"
},
{
"path": "ClipKit/SwiftUI/Preview.swift",
"chars": 966,
"preview": "//\n// Preview.swift\n// ClipKit\n//\n// Created by Riley Testut on 5/26/20.\n// Copyright © 2020 Riley Testut. All right"
},
{
"path": "ClipKit/SwiftUI/SwitchKeyboardButton.swift",
"chars": 1453,
"preview": "//\n// SwitchKeyboardButton.swift\n// ClipKit\n//\n// Created by Riley Testut on 6/9/20.\n// Copyright © 2020 Riley Testu"
},
{
"path": "ClipKit/UTI.swift",
"chars": 238,
"preview": "//\n// UTI.swift\n// ClipKit\n//\n// Created by Riley Testut on 6/14/19.\n// Copyright © 2019 Riley Testut. All rights re"
},
{
"path": "ClipboardReader/Base.lproj/MainInterface.storyboard",
"chars": 2844,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
},
{
"path": "ClipboardReader/ClipboardReader.entitlements",
"chars": 303,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "ClipboardReader/Info.plist",
"chars": 1420,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
},
{
"path": "ClipboardReader/NotificationViewController.swift",
"chars": 3436,
"preview": "//\n// NotificationViewController.swift\n// NotificationClipboard\n//\n// Created by Riley Testut on 12/2/19.\n// Copyrig"
},
{
"path": "README.md",
"chars": 5479,
"preview": "# Clip\n\n> Clip is a clipboard manager for iOS that can monitor your clipboard indefinitely in the background — no jailbr"
},
{
"path": "UNLICENSE",
"chars": 1211,
"preview": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, c"
}
]
About this extraction
This page contains the full source code of the rileytestut/Clip GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 64 files (230.4 KB), approximately 59.4k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.