├── .github
└── workflows
│ └── build_and_test.yml
├── .gitignore
├── .jazzy.json
├── DocumentationBuilder
└── main.swift
├── Examples
├── PLRelationalBasics.playground
│ ├── Contents.swift
│ ├── contents.xcplayground
│ ├── playground.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── timeline.xctimeline
├── PLRelationalExamples.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ └── xcshareddata
│ │ └── xcschemes
│ │ ├── TodoApp-SwiftUI-iOS.xcscheme
│ │ └── TodoApp-SwiftUI-macOS.xcscheme
└── TodoApp-SwiftUI
│ ├── Resources
│ ├── iOS
│ │ ├── Assets.xcassets
│ │ │ ├── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ │ └── Contents.json
│ │ ├── Base.lproj
│ │ │ └── LaunchScreen.storyboard
│ │ └── Preview Content
│ │ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ └── macOS
│ │ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ ├── Contents.json
│ │ └── Trash.imageset
│ │ │ ├── Contents.json
│ │ │ ├── trash.png
│ │ │ └── trash@2x.png
│ │ ├── Base.lproj
│ │ └── Main.storyboard
│ │ └── Preview Content
│ │ └── Preview Assets.xcassets
│ │ └── Contents.json
│ ├── Sources
│ ├── Shared
│ │ ├── Identifiers.swift
│ │ └── Model.swift
│ ├── iOS
│ │ ├── AppDelegate.swift
│ │ ├── CheckButton.swift
│ │ ├── ChecklistItemView.swift
│ │ ├── ChecklistItemViewModel.swift
│ │ ├── ChecklistView.swift
│ │ ├── ChecklistViewModel.swift
│ │ ├── ContentView.swift
│ │ ├── ContentViewModel.swift
│ │ ├── DetailView.swift
│ │ ├── DetailViewModel.swift
│ │ ├── SceneDelegate.swift
│ │ ├── TagItemView.swift
│ │ ├── TagItemViewModel.swift
│ │ ├── TagsView.swift
│ │ ├── TagsViewModel.swift
│ │ └── TextView.swift
│ └── macOS
│ │ ├── AppDelegate.swift
│ │ ├── ChecklistItemView.swift
│ │ ├── ChecklistItemViewModel.swift
│ │ ├── ChecklistView.swift
│ │ ├── ChecklistViewModel.swift
│ │ ├── ContentView.swift
│ │ ├── ContentViewModel.swift
│ │ ├── DetailView.swift
│ │ ├── DetailViewModel.swift
│ │ ├── EphemeralComboBox.swift
│ │ └── TextView.swift
│ └── Support
│ ├── iOS
│ └── Info.plist
│ └── macOS
│ ├── App.entitlements
│ └── Info.plist
├── LICENSE
├── Legacy
├── Examples
│ ├── BindableControlsApp
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ │ └── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ ├── Base.lproj
│ │ │ └── MainMenu.xib
│ │ └── Info.plist
│ ├── BindableControlsAppUITests
│ │ ├── BindableControlsAppUITests.swift
│ │ └── Info.plist
│ ├── HelloWorldApp
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ │ └── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ ├── Base.lproj
│ │ │ └── MainMenu.xib
│ │ ├── Info.plist
│ │ └── ViewModel.swift
│ ├── HelloWorldAppTests
│ │ ├── HelloWorldAppTests.swift
│ │ └── Info.plist
│ ├── HelloWorldAppUITests
│ │ ├── HelloWorldAppUITests.swift
│ │ └── Info.plist
│ ├── MiniVisualizer
│ │ ├── AppDelegate.swift
│ │ ├── ArrowView.swift
│ │ ├── Assets.xcassets
│ │ │ └── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ ├── Base.lproj
│ │ │ └── MainMenu.xib
│ │ ├── Info.plist
│ │ ├── NSBezierPath+CGPath.swift
│ │ ├── RelationView.swift
│ │ ├── StageView.swift
│ │ └── ViewModel.swift
│ ├── RelationChangeApp
│ │ ├── AppDelegate.swift
│ │ ├── ArrowView.swift
│ │ ├── Assets.xcassets
│ │ │ └── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ ├── Base.lproj
│ │ │ └── MainMenu.xib
│ │ ├── Info.plist
│ │ ├── NSBezierPath+CGPath.swift
│ │ ├── RelationView.swift
│ │ ├── ScrollingTextView.swift
│ │ ├── TextView.swift
│ │ └── ViewModel.swift
│ ├── SearchApp
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ │ └── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ ├── Base.lproj
│ │ │ └── MainMenu.xib
│ │ ├── Info.plist
│ │ ├── ResultsListView.swift
│ │ ├── SearchResult.swift
│ │ └── ViewModel.swift
│ ├── SearchAppTests
│ │ ├── Info.plist
│ │ └── SearchAppTests.swift
│ ├── SearchAppUITests
│ │ ├── Info.plist
│ │ └── SearchAppUITests.swift
│ ├── TodoApp
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ │ └── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ ├── Base.lproj
│ │ │ ├── ChecklistView.xib
│ │ │ ├── DetailView.xib
│ │ │ └── MainMenu.xib
│ │ ├── ChecklistView.swift
│ │ ├── ChecklistViewModel.swift
│ │ ├── DetailView.swift
│ │ ├── DetailViewModel.swift
│ │ ├── Identifiers.swift
│ │ ├── Info.plist
│ │ ├── Model.swift
│ │ ├── README.markdown
│ │ ├── trash.png
│ │ └── trash@2x.png
│ ├── TodoClientApp
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ │ └── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ ├── Base.lproj
│ │ │ ├── ChecklistView.xib
│ │ │ ├── DetailView.xib
│ │ │ └── MainMenu.xib
│ │ ├── ChecklistView.swift
│ │ ├── ChecklistViewModel.swift
│ │ ├── DetailView.swift
│ │ ├── DetailViewModel.swift
│ │ ├── Identifiers.swift
│ │ ├── Info.plist
│ │ ├── Model.swift
│ │ ├── trash.png
│ │ └── trash@2x.png
│ ├── Visualizer
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ │ └── AppIcon.appiconset
│ │ │ │ └── Contents.json
│ │ ├── AsyncState.swift
│ │ ├── Base.lproj
│ │ │ ├── Document.xib
│ │ │ └── MainMenu.xib
│ │ ├── Box.swift
│ │ ├── DocDatabase.swift
│ │ ├── DocDatabaseError.swift
│ │ ├── DocModel.swift
│ │ ├── DocObject.swift
│ │ ├── DocOutlineModel.swift
│ │ ├── DocOutlineView.swift
│ │ ├── Document.swift
│ │ ├── EditorView.swift
│ │ ├── Environment.swift
│ │ ├── HistoryItem.swift
│ │ ├── Identifiers.swift
│ │ ├── Info.plist
│ │ ├── ItemType.swift
│ │ ├── NSColor255.swift
│ │ ├── NSControlSwiftAction.swift
│ │ ├── ObjectAttachment.swift
│ │ ├── OutlineRowView.swift
│ │ ├── RelationModel.swift
│ │ ├── RelationViewModel.swift
│ │ ├── ScrollView.swift
│ │ ├── SidebarModel.swift
│ │ ├── SidebarView.swift
│ │ └── VisualizerColors.swift
│ ├── VisualizerTests
│ │ ├── Info.plist
│ │ └── VisualizerTests.swift
│ └── VisualizerUITests
│ │ ├── Info.plist
│ │ └── VisualizerUITests.swift
├── PLBindableControls
│ └── Sources
│ │ ├── AppKit
│ │ ├── BackgroundView.swift
│ │ ├── Button.swift
│ │ ├── Checkbox.swift
│ │ ├── ColorPanel.swift
│ │ ├── ColorPickerView.swift
│ │ ├── ComboBox.swift
│ │ ├── ContextMenu.swift
│ │ ├── EmphemeralComboBox.swift
│ │ ├── EphemeralTextField.swift
│ │ ├── ExtOutlineView.swift
│ │ ├── ImageView.swift
│ │ ├── Label.swift
│ │ ├── ListView.swift
│ │ ├── MenuItem.swift
│ │ ├── PopUpButton.swift
│ │ ├── ProgressIndicator.swift
│ │ ├── SectionedTreeView.swift
│ │ ├── SegmentedControl.swift
│ │ ├── StepperView.swift
│ │ ├── TableView.swift
│ │ ├── TextField.swift
│ │ ├── TextView.swift
│ │ └── TreeView.swift
│ │ ├── Info.plist
│ │ ├── Shared
│ │ ├── Bindable.swift
│ │ ├── CheckState.swift
│ │ ├── Color.swift
│ │ ├── Image.swift
│ │ └── TextProperty.swift
│ │ └── UIKit
│ │ ├── BarButtonItem.swift
│ │ ├── ImageView.swift
│ │ ├── Label.swift
│ │ ├── ListView.swift
│ │ ├── SectionedTreeView.swift
│ │ ├── Slider.swift
│ │ ├── Switch.swift
│ │ ├── TextField.swift
│ │ └── TreeView.swift
├── PLRelationalBinding
│ ├── Sources
│ │ ├── ArrayBinarySearch.swift
│ │ ├── ArrayProperty.swift
│ │ ├── ArraySafeIndex.swift
│ │ ├── AsyncProperty.swift
│ │ ├── AsyncPropertyOperations.swift
│ │ ├── ChangeHandler.swift
│ │ ├── CollectionElement.swift
│ │ ├── CommonValue.swift
│ │ ├── Info.plist
│ │ ├── Property.swift
│ │ ├── PropertyOperations.swift
│ │ ├── RelationArrayProperty.swift
│ │ ├── RelationAsyncProperty.swift
│ │ ├── RelationChangeParts.swift
│ │ ├── RelationExtractTyped.swift
│ │ ├── RelationMutation.swift
│ │ ├── RelationSignal.swift
│ │ ├── RelationTreeProperty.swift
│ │ ├── RelationValuePlistable.swift
│ │ ├── RowCollectionElement.swift
│ │ ├── Signal.swift
│ │ ├── SignalOperations.swift
│ │ ├── TreeProperty.swift
│ │ ├── UndoManager.swift
│ │ └── UndoableDatabase.swift
│ └── Tests
│ │ ├── ArrayBinarySearchTests.swift
│ │ ├── AsyncPropertyOperationsTests.swift
│ │ ├── AsyncPropertyTests.swift
│ │ ├── BindingTestCase.swift
│ │ ├── ChangeHandlerTests.swift
│ │ ├── Info.plist
│ │ ├── PropertyOperationsTests.swift
│ │ ├── PropertyTests.swift
│ │ ├── RelationArrayPropertyTests.swift
│ │ ├── RelationAsyncPropertyTests.swift
│ │ ├── RelationChangePartsTests.swift
│ │ ├── RelationExtractTypedTests.swift
│ │ ├── RelationMutationTests.swift
│ │ ├── RelationSignalTests.swift
│ │ ├── RelationTreePropertyTests.swift
│ │ ├── SignalOperationsTests.swift
│ │ └── UndoableDatabaseTests.swift
└── PLRelationalLegacy.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
│ └── xcshareddata
│ └── xcschemes
│ ├── BindableControlsApp.xcscheme
│ ├── HelloWorldApp.xcscheme
│ ├── MiniVisualizer.xcscheme
│ ├── PLBindableControls-iOS.xcscheme
│ ├── PLBindableControls-macOS.xcscheme
│ ├── PLRelationalBinding-iOS.xcscheme
│ ├── PLRelationalBinding-macOS.xcscheme
│ ├── RelationChangeApp.xcscheme
│ ├── SearchApp.xcscheme
│ ├── TodoApp.xcscheme
│ └── TodoClientApp.xcscheme
├── PLRelational
├── Modules
│ ├── SQLiteTokenizerAPI.h
│ ├── module-sqlite3-include.h
│ └── module.modulemap
├── PLRelational.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── xcshareddata
│ │ └── xcschemes
│ │ ├── Documentation.xcscheme
│ │ ├── DocumentationBuilder.xcscheme
│ │ ├── PLRelational-iOS.xcscheme
│ │ └── PLRelational-macOS.xcscheme
├── Sources
│ ├── Async.swift
│ ├── AsyncManager.swift
│ ├── AsyncManagerObservation.swift
│ ├── AsyncRunloopHack.swift
│ ├── BinaryOperator.swift
│ ├── BookmarkableGraph.swift
│ ├── CachingRelation.swift
│ ├── ChangeLoggingDatabase.swift
│ ├── ChangeLoggingRelation.swift
│ ├── CollectionConvenience.swift
│ ├── ConcreteRelation.swift
│ ├── ConsistentPRNG.swift
│ ├── Crypto.swift
│ ├── DJBHash.swift
│ ├── DataCodec.swift
│ ├── DeallocObservation.swift
│ ├── Debug.swift
│ ├── DictionaryConvenience.swift
│ ├── DictionaryCreation.swift
│ ├── DispatchContext.swift
│ ├── ErrorConvenience.swift
│ ├── GeneratorConcat.swift
│ ├── IndexedSet.swift
│ ├── Info.plist
│ ├── InlineMutableData.swift
│ ├── IntermediateRelation.swift
│ ├── InternedUTF8String.swift
│ ├── MemoryTableDatabase.swift
│ ├── MemoryTableRelation.swift
│ ├── MirrorChildren.swift
│ ├── MutableBox.swift
│ ├── MutableRelation.swift
│ ├── MutableRelationCascadingDelete.swift
│ ├── Mutex.swift
│ ├── NSURLConvenience.swift
│ ├── NegativeSet.swift
│ ├── ObjectCasting.swift
│ ├── ObjectDictionary.swift
│ ├── ObjectMap.swift
│ ├── ObjectSet.swift
│ ├── ObserverSet.swift
│ ├── OptionalFlatten.swift
│ ├── PerThreadInstance.swift
│ ├── PlistDatabase.swift
│ ├── PlistDirectoryRelation.swift
│ ├── PlistFileRelation.swift
│ ├── Promise.swift
│ ├── QueryOptimizer.swift
│ ├── QueryPlanner.swift
│ ├── QueryPlannerDump.swift
│ ├── QueryRunner.swift
│ ├── RWLock.swift
│ ├── Relation.swift
│ ├── RelationActivityLogging.swift
│ ├── RelationChange.swift
│ ├── RelationDefaultChangeObserverImplementation.swift
│ ├── RelationDerivative.swift
│ ├── RelationDifferentiator.swift
│ ├── RelationDump.swift
│ ├── RelationObserver.swift
│ ├── RelationOperators.swift
│ ├── RelationParts.swift
│ ├── RelationRecursiveSelect.swift
│ ├── RelationTextIndex.swift
│ ├── RelationValue.swift
│ ├── RemovableSet.swift
│ ├── Result.swift
│ ├── Row.swift
│ ├── RowPlist.swift
│ ├── SQLiteDatabase.swift
│ ├── SQLiteRelation.swift
│ ├── SelectExpression.swift
│ ├── SelectExpressionAnalysis.swift
│ ├── SelectExpressionOperators.swift
│ ├── SequenceFind.swift
│ ├── SetPowerSet.swift
│ ├── SetSubtraction.swift
│ ├── SimpleDatabase.swift
│ ├── SmallInlineArray.swift
│ ├── StoredDatabase.swift
│ ├── StoredRelation.swift
│ ├── StringConvenience.swift
│ ├── StringPad.swift
│ ├── TransactionalDatabase.swift
│ ├── UnaryOperator.swift
│ ├── UnsafePointerReadWrite.swift
│ └── ValueWithDestructor.swift
├── Tests
│ ├── AsyncManagerTests.swift
│ ├── BookmarkableGraphTests.swift
│ ├── ChangeLoggingDatabaseTests.swift
│ ├── ChangeLoggingRelationTests.swift
│ ├── DBTestCase.swift
│ ├── Info.plist
│ ├── InlineMutableDataTests.swift
│ ├── InternedUTF8StringTests.swift
│ ├── ObjectMapTests.swift
│ ├── PlistDatabaseTests.swift
│ ├── PlistDirectoryRelationTests.swift
│ ├── PlistFileRelationTests.swift
│ ├── QueryOptimizerTests.swift
│ ├── QueryRunnerTests.swift
│ ├── RelationDifferentiatorTests.swift
│ ├── RelationObservationTests.swift
│ ├── RelationRecursiveSelectTests.swift
│ ├── RelationTests.swift
│ ├── RelationTextIndexTests.swift
│ ├── SQLiteDatabaseTests.swift
│ └── TransactionalDatabaseTests.swift
└── samplesummarize.swift
├── PLRelationalCombine
├── PLRelationalCombine.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── xcshareddata
│ │ └── xcschemes
│ │ ├── PLRelationalCombine-iOS.xcscheme
│ │ └── PLRelationalCombine-macOS.xcscheme
├── Sources
│ ├── ArrayBinarySearch.swift
│ ├── CancellableBag.swift
│ ├── Info.plist
│ ├── LogError.swift
│ ├── RelationArrayReduce.swift
│ ├── RelationChangePublisher.swift
│ ├── RelationChangeSummary.swift
│ ├── RelationMutation.swift
│ ├── RelationValuePublisher.swift
│ ├── TwoWay.swift
│ ├── UndoManager.swift
│ ├── UndoableDatabase.swift
│ ├── WeakBind.swift
│ └── WeakTwoWayBind.swift
└── Tests
│ ├── ArrayBinarySearchTests.swift
│ ├── CombineTestCase.swift
│ ├── Info.plist
│ ├── RelationArrayReduceTests.swift
│ ├── RelationChangePublisherTests.swift
│ ├── RelationChangeSummaryTests.swift
│ ├── RelationMutationTests.swift
│ ├── RelationValuePublisherTests.swift
│ ├── TwoWayTests.swift
│ ├── UndoableDatabaseTests.swift
│ ├── WeakBindTests.swift
│ └── WeakTwoWayBindTests.swift
└── README.md
/.github/workflows/build_and_test.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | # Run this on pushes to `master`, or when a pull request is opened against `master`
4 | on:
5 | push:
6 | branches:
7 | - master
8 | pull_request:
9 | branches:
10 | - master
11 |
12 | jobs:
13 | test:
14 |
15 | name: Test on ${{ matrix.name }}
16 | runs-on: macOS-latest
17 |
18 | strategy:
19 | fail-fast: false
20 | matrix:
21 | # GitHub Actions requires a single row to be added to the build matrix.
22 | # For more info:
23 | # https://help.github.com/en/articles/workflow-syntax-for-github-actions
24 | # This approach based on:
25 | # https://github.com/DaanDeMeyer/reproc/blob/master/.github/workflows/main.yml
26 | name: [
27 | macOS,
28 | iOS
29 | ]
30 |
31 | include:
32 | - name: macOS
33 | destination: 'platform=macOS'
34 |
35 | - name: iOS
36 | destination: 'platform=iOS Simulator,name=iPhone 11,OS=13.1'
37 |
38 | steps:
39 | - name: Checkout
40 | uses: actions/checkout@v1
41 |
42 | - name: Select Xcode 11.1
43 | run: sudo xcode-select -switch /Applications/Xcode_11.1.app
44 |
45 | - name: PLRelational
46 | run: |
47 | cd PLRelational
48 | set -o pipefail && xcodebuild clean test -scheme PLRelational-${{ matrix.name }} -destination "${{ matrix.destination }}" | xcpretty
49 |
50 | - name: PLRelationalCombine
51 | run: |
52 | cd PLRelationalCombine
53 | set -o pipefail && xcodebuild clean test -scheme PLRelationalCombine-${{ matrix.name }} -destination "${{ matrix.destination }}" | xcpretty
54 |
55 | - name: Examples > TodoApp-SwiftUI
56 | run: |
57 | cd Examples
58 | set -o pipefail && xcodebuild clean build -scheme TodoApp-SwiftUI-${{ matrix.name }} -destination "${{ matrix.destination }}" | xcpretty
59 |
60 | - name: Legacy > PLRelationalBinding
61 | run: |
62 | cd Legacy
63 | set -o pipefail && xcodebuild clean test -scheme PLRelationalBinding-${{ matrix.name }} -destination "${{ matrix.destination }}" | xcpretty
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData/
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata/
19 |
20 | ## Other
21 | *.moved-aside
22 | *.xccheckout
23 | *.xcscmblueprint.DS_Store
24 | .DS_Store
25 |
--------------------------------------------------------------------------------
/Examples/PLRelationalBasics.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Examples/PLRelationalBasics.playground/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Examples/PLRelationalBasics.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Examples/PLRelationalExamples.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Examples/PLRelationalExamples.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Resources/iOS/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Resources/iOS/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Resources/iOS/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Resources/macOS/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Resources/macOS/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Resources/macOS/Assets.xcassets/Trash.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "trash.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "trash@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Resources/macOS/Assets.xcassets/Trash.imageset/trash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plausiblelabs/plrelational/d904ea052b4e2b2db720733d3e63089c7d99e49b/Examples/TodoApp-SwiftUI/Resources/macOS/Assets.xcassets/Trash.imageset/trash.png
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Resources/macOS/Assets.xcassets/Trash.imageset/trash@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plausiblelabs/plrelational/d904ea052b4e2b2db720733d3e63089c7d99e49b/Examples/TodoApp-SwiftUI/Resources/macOS/Assets.xcassets/Trash.imageset/trash@2x.png
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Resources/macOS/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Sources/Shared/Identifiers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 | import PLRelational
8 |
9 | /// Base class for our identifier value types. All of our identifiers use UUIDs
10 | /// under the hood and we have conveniences for conversion to/from `RelationValue`.
11 | class BaseID {
12 | fileprivate let uuid: String
13 |
14 | init() {
15 | self.uuid = UUID().uuidString
16 | }
17 |
18 | init(_ stringValue: String) {
19 | self.uuid = stringValue
20 | }
21 |
22 | init(_ relationValue: RelationValue) {
23 | self.uuid = relationValue.get()!
24 | }
25 |
26 | var relationValue: RelationValue {
27 | return uuid.relationValue
28 | }
29 | }
30 |
31 | /// Identifier type for rows in the `Item` relation.
32 | class ItemID: BaseID, Equatable, Hashable {
33 | /// Shorthand for extracting an `ItemID` from an `Item` row.
34 | convenience init(_ row: Row) {
35 | self.init(row[Item.id])
36 | }
37 |
38 | static func ==(lhs: ItemID, rhs: ItemID) -> Bool {
39 | return lhs.uuid == rhs.uuid
40 | }
41 |
42 | func hash(into hasher: inout Hasher) {
43 | hasher.combine(uuid)
44 | }
45 | }
46 |
47 | /// Identifier type for rows in the `Tag` relation.
48 | class TagID: BaseID, Equatable, Hashable {
49 | /// Shorthand for extracting a `TagID` from an `Tag` row.
50 | convenience init(_ row: Row) {
51 | self.init(row[Tag.id])
52 | }
53 |
54 | static func ==(lhs: TagID, rhs: TagID) -> Bool {
55 | return lhs.uuid == rhs.uuid
56 | }
57 |
58 | func hash(into hasher: inout Hasher) {
59 | hasher.combine(uuid)
60 | }
61 | }
62 |
63 | /// Conforming to this protocol allows us to use `ItemID` and `TagID` directly
64 | /// in row initializers and in select expressions without having to explicitly
65 | /// convert to `RelationValue`.
66 | extension BaseID: SelectExpressionConstantValue {}
67 |
68 | extension BaseID: CustomStringConvertible {
69 | var description: String {
70 | return "\(String(describing: type(of: self)))(\(uuid))"
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Sources/iOS/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import UIKit
7 |
8 | @UIApplicationMain
9 | class AppDelegate: UIResponder, UIApplicationDelegate {
10 |
11 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
12 | return true
13 | }
14 |
15 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
16 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
17 | }
18 |
19 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Sources/iOS/CheckButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import SwiftUI
7 |
8 | struct CheckButton: View {
9 |
10 | let isOn: Binding
11 |
12 | var body: some View {
13 | let checkName = isOn.wrappedValue ?
14 | "checkmark.circle" : "circle"
15 | return Image(systemName: checkName)
16 | .imageScale(.large)
17 | .frame(minWidth: 28, maxHeight: .infinity)
18 | .onTapGesture { self.isOn.wrappedValue.toggle() }
19 | }
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Sources/iOS/ChecklistItemView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import SwiftUI
7 |
8 | struct ChecklistItemView: View {
9 |
10 | static let rowHeight: CGFloat = 34
11 |
12 | @ObservedObject private var model: ChecklistItemViewModel
13 |
14 | init(model: ChecklistItemViewModel) {
15 | self.model = model
16 | }
17 |
18 | var body: some View {
19 | HStack(alignment: .center) {
20 | CheckButton(isOn: $model.completed)
21 | VStack(alignment: .leading) {
22 | Text(model.title)
23 | .lineLimit(1)
24 | .frame(maxWidth: .infinity, alignment: .leading)
25 |
26 | if !model.tags.isEmpty {
27 | Text(model.tags)
28 | .lineLimit(1)
29 | .font(.system(size: 11))
30 | .foregroundColor(Color.secondary)
31 | .frame(maxWidth: .infinity, alignment: .leading)
32 | }
33 | }
34 | .frame(maxWidth: .infinity, alignment: .topLeading)
35 | }
36 | .frame(minHeight: ChecklistItemView.rowHeight)
37 | }
38 | }
39 |
40 | struct ChecklistItemView_Previews: PreviewProvider {
41 | static var previews: some View {
42 | let (model, itemIds) = modelForPreviewWithIds()
43 | let viewModel = ChecklistItemViewModel(model: model, item: ChecklistItem(id: itemIds[0], title: "Item 1", created: "", completed: ""))
44 | return ChecklistItemView(model: viewModel)
45 | .padding()
46 | .previewLayout(.fixed(width: 300, height: ChecklistItemView.rowHeight))
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Sources/iOS/ChecklistItemViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Combine
7 | import SwiftUI
8 | import PLRelational
9 | import PLRelationalCombine
10 |
11 | final class ChecklistItemViewModel: ElementViewModel, Identifiable, ObservableObject {
12 |
13 | private let model: Model
14 |
15 | var item: ChecklistItem {
16 | didSet {
17 | // REQ-4
18 | // Keep the list item title up to date. Note that we could have set up
19 | // a binding in `init` below similar to what we do for the tags label,
20 | // but using `didSet` here demonstrates another approach. Since the
21 | // `reduce(to:)` will see an update every time the title gets changed
22 | // by the detail view, and since it already sets the updated view model's
23 | // `item`, we can just update `title` here.
24 | self.title = item.title
25 | }
26 | }
27 | var element: ChecklistItem { item }
28 | var id: ItemID { item.id }
29 |
30 | @TwoWay(onSet: .commit) var completed: Bool = false
31 | @Published var title: String
32 | @Published var tags: String
33 |
34 | private var cancellableBag = CancellableBag()
35 |
36 | init(model: Model, item: ChecklistItem) {
37 | self.model = model
38 | self.item = item
39 | self.title = item.title
40 | self.tags = ""
41 |
42 | // REQ-3
43 | // Each to-do item should have a checkbox showing its completion status.
44 | // This is a two-way property that is backed by UndoableDatabase.
45 | model.items
46 | .select(Item.id *== id)
47 | .project(Item.completed)
48 | .bind(to: \._completed, on: self, strategy: model.itemCompleted())
49 | .store(in: &cancellableBag)
50 |
51 | // REQ-5
52 | // Each to-do item should have a string containing the
53 | // list of tags for that item.
54 | self.model
55 | .tagsString(for: id)
56 | .bind(to: \.tags, on: self)
57 | .store(in: &cancellableBag)
58 | }
59 |
60 | deinit {
61 | cancellableBag.cancel()
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Sources/iOS/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import SwiftUI
7 |
8 | struct ContentView: View {
9 |
10 | @ObservedObject private var model: ContentViewModel
11 |
12 | init(model: ContentViewModel) {
13 | self.model = model
14 | }
15 |
16 | var body: some View {
17 | NavigationView {
18 | ChecklistView(model: model.checklistViewModel)
19 | .navigationBarTitle("To Do", displayMode: .inline)
20 | // TODO: On iPad, the following would be the preferred way
21 | // of having a split master/detail view, but for now we'll
22 | // focus on iPhone
23 | // DetailView(model: model.checklistViewModel.detailViewModel)
24 | }
25 | }
26 | }
27 |
28 | struct ContentView_Previews: PreviewProvider {
29 | static var previews: some View {
30 | let model = modelForPreview()
31 | let viewModel = ContentViewModel(model: model)
32 | return Group {
33 | ContentView(model: viewModel)
34 | .previewDevice(PreviewDevice(rawValue: "iPhone 8"))
35 | .previewDisplayName("iPhone 8")
36 |
37 | // ContentView(model: viewModel)
38 | // .previewDevice(PreviewDevice(rawValue: "iPad Pro (9.7-inch)"))
39 | // .previewDisplayName("iPad Pro")
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Sources/iOS/ContentViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Combine
7 | import SwiftUI
8 | import PLRelational
9 | import PLRelationalCombine
10 |
11 | final class ContentViewModel: ObservableObject {
12 |
13 | private let model: Model
14 |
15 | let checklistViewModel: ChecklistViewModel
16 |
17 | private var cancellableBag = CancellableBag()
18 |
19 | init(model: Model) {
20 | self.model = model
21 | self.checklistViewModel = ChecklistViewModel(model: model)
22 | }
23 |
24 | deinit {
25 | cancellableBag.cancel()
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Sources/iOS/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import UIKit
7 | import SwiftUI
8 | import PLRelationalCombine
9 |
10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
11 |
12 | var window: UIWindow?
13 | var contentViewModel: ContentViewModel!
14 |
15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
16 | // Create the SwiftUI view that provides the window contents
17 | // TODO: Specify path for persistence
18 | let undoManager = PLRelationalCombine.UndoManager()
19 | let model = Model(undoManager: undoManager, path: nil)
20 | if !model.dbAlreadyExisted {
21 | _ = model.addDefaultData()
22 | }
23 | contentViewModel = ContentViewModel(model: model)
24 | let contentView = ContentView(model: contentViewModel)
25 |
26 | // Use a UIHostingController as window root view controller
27 | if let windowScene = scene as? UIWindowScene {
28 | let window = UIWindow(windowScene: windowScene)
29 | window.rootViewController = UIHostingController(rootView: contentView)
30 | self.window = window
31 | window.makeKeyAndVisible()
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Sources/iOS/TagItemView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import SwiftUI
7 |
8 | struct TagItemView: View {
9 |
10 | static let rowHeight: CGFloat = 34
11 |
12 | @ObservedObject private var model: TagItemViewModel
13 | private let action: () -> Void
14 |
15 | init(model: TagItemViewModel, action: @escaping () -> Void) {
16 | self.model = model
17 | self.action = action
18 | }
19 |
20 | var body: some View {
21 | HStack(alignment: .center) {
22 | ZStack {
23 | if model.tagItem.itemID != nil {
24 | Image(systemName: "checkmark")
25 | } else {
26 | EmptyView()
27 | }
28 | }
29 | .frame(width: 20, height: 20)
30 |
31 | Button(action: self.action) {
32 | Text(model.tagItem.name)
33 | .lineLimit(1)
34 | .frame(maxWidth: .infinity, alignment: .leading)
35 | }
36 | }
37 | .frame(minHeight: ChecklistItemView.rowHeight, alignment: .leading)
38 | }
39 | }
40 |
41 | struct TagItemView_Previews: PreviewProvider {
42 | static var previews: some View {
43 | return List {
44 | TagItemView(model: TagItemViewModel(tagItem: TagItem(id: TagID("1"), name: String(repeating: "Tag 1 ", count: 20), itemID: ItemID("1"))), action: {})
45 | TagItemView(model: TagItemViewModel(tagItem: TagItem(id: TagID("2"), name: "Tag 2", itemID: nil)), action: {})
46 | }
47 | .environment(\.defaultMinListRowHeight, TagItemView.rowHeight)
48 | .padding()
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Sources/iOS/TagItemViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Combine
7 | import SwiftUI
8 | import PLRelational
9 | import PLRelationalCombine
10 |
11 | struct TagItem: Identifiable {
12 | let id: TagID
13 | let name: String
14 | let itemID: ItemID?
15 |
16 | init(id: TagID, name: String, itemID: ItemID?) {
17 | self.id = id
18 | self.name = name
19 | self.itemID = itemID
20 | }
21 |
22 | init(row: Row) {
23 | self.id = TagID(row[Tag.id])
24 | self.name = row[Tag.name].get()!
25 | if let itemID = row[Item.id].get() as String? {
26 | self.itemID = ItemID(itemID)
27 | } else {
28 | self.itemID = nil
29 | }
30 | }
31 | }
32 |
33 | final class TagItemViewModel: ElementViewModel, Identifiable, ObservableObject {
34 |
35 | var tagItem: TagItem
36 | var element: TagItem { tagItem }
37 | var id: TagID { tagItem.id }
38 |
39 | init(tagItem: TagItem) {
40 | self.tagItem = tagItem
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Sources/iOS/TagsView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import SwiftUI
7 |
8 | struct TagsView: View {
9 |
10 | @ObservedObject private var model: TagsViewModel
11 |
12 | init(model: TagsViewModel) {
13 | self.model = model
14 | }
15 |
16 | var body: some View {
17 | VStack(spacing: 0) {
18 | TextField("Add a tag", text: $model.newTagName, onCommit: {
19 | self.model.addNewTagToSelectedItem()
20 | })
21 | .autocapitalization(.none)
22 | .padding()
23 |
24 | Divider()
25 |
26 | List {
27 | ForEach(self.model.tagItemViewModels) { tagItemViewModel in
28 | TagItemView(model: tagItemViewModel, action: {
29 | self.model.toggleApplied(tagItemViewModel.tagItem)
30 | })
31 | .frame(minWidth: 0, maxWidth: .infinity)
32 | }
33 | }
34 | .environment(\.defaultMinListRowHeight, TagItemView.rowHeight)
35 | }
36 | }
37 | }
38 |
39 | struct TagsView_Previews: PreviewProvider {
40 | static var previews: some View {
41 | let model = modelForPreview()
42 | return TagsView(model: TagsViewModel(model: model))
43 | .padding()
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Sources/iOS/TextView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | // Based on:
7 | // https://stackoverflow.com/a/57853937
8 |
9 | import SwiftUI
10 |
11 | /// A wrapper around UITextView to allow it to be used in SwiftUI.
12 | struct TextView: UIViewRepresentable {
13 |
14 | @Binding var text: String
15 |
16 | var onCommit: () -> Void = {}
17 |
18 | func makeCoordinator() -> Coordinator {
19 | Coordinator(self)
20 | }
21 |
22 | func makeUIView(context: Context) -> UITextView {
23 | let uiTextView = UITextView()
24 | uiTextView.delegate = context.coordinator
25 |
26 | uiTextView.font = UIFont.systemFont(ofSize: 16)
27 | uiTextView.isScrollEnabled = true
28 | uiTextView.isEditable = true
29 | uiTextView.isUserInteractionEnabled = true
30 |
31 | // XXX: Remove all padding
32 | uiTextView.textContainerInset = .zero
33 | uiTextView.textContainer.lineFragmentPadding = 0
34 |
35 | return uiTextView
36 | }
37 |
38 | func updateUIView(_ uiView: UITextView, context: Context) {
39 | uiView.text = text
40 | }
41 |
42 | class Coordinator : NSObject, UITextViewDelegate {
43 | var parent: TextView
44 |
45 | init(_ uiTextView: TextView) {
46 | self.parent = uiTextView
47 | }
48 |
49 | func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
50 | return true
51 | }
52 |
53 | func textViewShouldEndEditing(_ textView: UITextView) -> Bool {
54 | self.parent.text = textView.text
55 | self.parent.onCommit()
56 | return true
57 | }
58 | }
59 | }
60 |
61 | #if DEBUG
62 | struct TextView_Previews: PreviewProvider {
63 | static var previews: some View {
64 | VStack {
65 | TextView(text: .constant("hello\nthere"))
66 | }
67 | .padding()
68 | .previewLayout(.fixed(width: 300, height: 200))
69 | }
70 | }
71 | #endif
72 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Sources/macOS/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Cocoa
7 | import SwiftUI
8 | import PLRelational
9 | import PLRelationalCombine
10 |
11 | @NSApplicationMain
12 | class AppDelegate: NSObject, NSApplicationDelegate {
13 |
14 | var window: NSWindow!
15 |
16 | private var undoManager: PLRelationalCombine.UndoManager!
17 | private var model: Model!
18 | private var contentViewModel: ContentViewModel!
19 |
20 | func applicationDidFinishLaunching(_ aNotification: Notification) {
21 | // Prepare the undo manager
22 | undoManager = UndoManager()
23 |
24 | // Initialize our model
25 | model = Model(undoManager: undoManager, path: "/tmp/TodoApp-SwiftUI.db")
26 | if !model.dbAlreadyExisted {
27 | _ = model.addDefaultData()
28 | }
29 |
30 | // Create the SwiftUI view that provides the window contents
31 | contentViewModel = ContentViewModel(model: model)
32 | let contentView = ContentView(model: contentViewModel)
33 |
34 | // Create the window and set the content view
35 | window = NSWindow(
36 | contentRect: NSRect(x: 0, y: 0, width: 640, height: 440),
37 | styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
38 | backing: .buffered, defer: false)
39 | window.delegate = self
40 | window.center()
41 | window.setFrameAutosaveName("Main Window")
42 | window.contentView = NSHostingView(rootView: contentView)
43 | window.makeKeyAndOrderFront(nil)
44 | }
45 |
46 | func applicationWillTerminate(_ aNotification: Notification) {
47 | }
48 | }
49 |
50 | extension AppDelegate: NSWindowDelegate {
51 | func windowWillReturnUndoManager(_ window: NSWindow) -> Foundation.UndoManager? {
52 | return undoManager.native
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Sources/macOS/ChecklistItemView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import SwiftUI
7 |
8 | struct ChecklistItemView: View {
9 |
10 | @ObservedObject private var model: ChecklistItemViewModel
11 |
12 | init(model: ChecklistItemViewModel) {
13 | self.model = model
14 | }
15 |
16 | var body: some View {
17 | HStack(alignment: .center) {
18 | Toggle(isOn: $model.completed) {
19 | Text(" ")
20 | }
21 | Text(model.title)
22 | Spacer()
23 | Text(model.tags)
24 | .font(.system(size: 11))
25 | .foregroundColor(Color.secondary)
26 | }
27 | }
28 | }
29 |
30 | struct ChecklistItemView_Previews: PreviewProvider {
31 | static var previews: some View {
32 | let model = modelForPreview()
33 | let viewModel = ChecklistItemViewModel(model: model, item: ChecklistItem(id: ItemID("1"), title: "Item 1", created: "", completed: ""))
34 | return ChecklistItemView(model: viewModel)
35 | .padding()
36 | .previewLayout(.fixed(width: 300, height: 30))
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Sources/macOS/ChecklistItemViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Combine
7 | import SwiftUI
8 | import PLRelational
9 | import PLRelationalCombine
10 |
11 | final class ChecklistItemViewModel: ElementViewModel, Identifiable, ObservableObject {
12 |
13 | private let model: Model
14 |
15 | var item: ChecklistItem {
16 | didSet {
17 | // REQ-4
18 | // Keep the list item title up to date. Note that we could have set up
19 | // a binding in `init` below similar to what we do for the tags label,
20 | // but using `didSet` here demonstrates another approach. Since the
21 | // `reduce(to:)` will see an update every time the title gets changed
22 | // by the detail view, and since it already sets the updated view model's
23 | // `item`, we can just update `title` here.
24 | self.title = item.title
25 | }
26 | }
27 | var element: ChecklistItem { item }
28 | var id: ItemID { item.id }
29 |
30 | @TwoWay(onSet: .commit) var completed: Bool = false
31 | @Published var title: String
32 | @Published var tags: String
33 |
34 | private var cancellableBag = CancellableBag()
35 |
36 | init(model: Model, item: ChecklistItem) {
37 | self.model = model
38 | self.item = item
39 | self.title = item.title
40 | self.tags = ""
41 |
42 | // REQ-3
43 | // Each to-do item should have a checkbox showing its completion status.
44 | // This is a two-way property that is backed by UndoableDatabase.
45 | model.items
46 | .select(Item.id *== id)
47 | .project(Item.completed)
48 | .bind(to: \._completed, on: self, strategy: model.itemCompleted())
49 | .store(in: &cancellableBag)
50 |
51 | // REQ-5
52 | // Each to-do item should have a string containing the
53 | // list of tags for that item.
54 | self.model
55 | .tagsString(for: id)
56 | .bind(to: \.tags, on: self)
57 | .store(in: &cancellableBag)
58 | }
59 |
60 | deinit {
61 | cancellableBag.cancel()
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Sources/macOS/ChecklistView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import SwiftUI
7 |
8 | struct ChecklistView: View {
9 |
10 | @ObservedObject private var model: ChecklistViewModel
11 |
12 | init(model: ChecklistViewModel) {
13 | self.model = model
14 | }
15 |
16 | var body: some View {
17 | // Disable animation for initial load
18 | let animation: Animation?
19 | if model.hasDisplayedItems {
20 | animation = .default
21 | } else {
22 | if model.itemViewModels.count > 0 {
23 | model.hasDisplayedItems = true
24 | }
25 | animation = .none
26 | }
27 |
28 | return VStack {
29 | TextField("Add a to-do", text: $model.newItemTitle, onCommit: {
30 | self.model.addNewItem()
31 | })
32 | .padding(.bottom, 10)
33 |
34 | List(selection: $model.selectedItem) {
35 | ForEach(self.model.itemViewModels) { itemViewModel in
36 | ChecklistItemView(model: itemViewModel)
37 | .frame(minWidth: 0, maxWidth: .infinity)
38 | .animation(.none)
39 | }
40 | }
41 | .animation(animation)
42 | .environment(\.defaultMinListRowHeight, 30)
43 | }
44 | }
45 | }
46 |
47 | struct ChecklistView_Previews: PreviewProvider {
48 | static var previews: some View {
49 | let model = modelForPreview()
50 | let viewModel = ChecklistViewModel(model: model)
51 | return ChecklistView(model: viewModel)
52 | .padding()
53 | .previewLayout(.fixed(width: 300, height: 400))
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Sources/macOS/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import SwiftUI
7 |
8 | struct ContentView: View {
9 |
10 | @ObservedObject private var model: ContentViewModel
11 |
12 | init(model: ContentViewModel) {
13 | self.model = model
14 | }
15 |
16 | var body: some View {
17 | HStack(spacing: 20) {
18 | ChecklistView(model: model.checklistViewModel)
19 | .frame(minWidth: 290, minHeight: 400)
20 |
21 | ZStack {
22 | if model.hasSelection {
23 | DetailView(model: model.detailViewModel)
24 | .padding()
25 | .background(Color(white: 0.25))
26 | .cornerRadius(8)
27 | } else {
28 | Text("No Selection")
29 | .font(.system(size: 19))
30 | .foregroundColor(Color.secondary)
31 | .frame(maxWidth: .infinity)
32 | }
33 | }
34 | .frame(minWidth: 290, minHeight: 400)
35 | }
36 | .padding()
37 | }
38 | }
39 |
40 | struct ContentView_Previews: PreviewProvider {
41 | static var previews: some View {
42 | let model = modelForPreview()
43 | let viewModel = ContentViewModel(model: model)
44 | return ContentView(model: viewModel)
45 | .previewLayout(.fixed(width: 600, height: 440))
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Sources/macOS/ContentViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Combine
7 | import SwiftUI
8 | import PLRelational
9 | import PLRelationalCombine
10 |
11 | final class ContentViewModel: ObservableObject {
12 |
13 | private let model: Model
14 |
15 | let checklistViewModel: ChecklistViewModel
16 | let detailViewModel: DetailViewModel
17 |
18 | @Published var hasSelection: Bool = false
19 |
20 | private var cancellableBag = CancellableBag()
21 |
22 | init(model: Model) {
23 | self.model = model
24 | self.checklistViewModel = ChecklistViewModel(model: model)
25 | self.detailViewModel = DetailViewModel(model: model)
26 |
27 | // Set a flag when an item is selected in the list of to-do items.
28 | model.selectedItems
29 | .nonEmpty()
30 | .replaceError(with: false)
31 | .bind(to: \.hasSelection, on: self)
32 | .store(in: &cancellableBag)
33 | }
34 |
35 | deinit {
36 | cancellableBag.cancel()
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Sources/macOS/DetailView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import SwiftUI
7 |
8 | struct DetailView: View {
9 |
10 | @ObservedObject private var model: DetailViewModel
11 |
12 | init(model: DetailViewModel) {
13 | self.model = model
14 | }
15 |
16 | var body: some View {
17 | VStack(alignment: .leading) {
18 | HStack(alignment: .center) {
19 | Toggle(isOn: $model.itemCompleted) {
20 | Text(" ")
21 | }
22 | TextField("", text: $model.itemTitle, onCommit: { self.model.commitItemTitle() })
23 | }
24 | .padding(.bottom)
25 |
26 | EphemeralComboBox(
27 | placeholder: "Assign a tag",
28 | items: $model.availableTags,
29 | onCommitString: { self.model.addNewTagToSelectedItem(name: $0) },
30 | onItemSelected: { self.model.addExistingTagToSelectedItem(tagID: $0.id) }
31 | )
32 |
33 | List {
34 | ForEach(model.itemTags, id: \.self) { tag in
35 | Text(tag)
36 | }
37 | }
38 | .padding(.bottom)
39 |
40 | Text("Notes")
41 | TextView(text: $model.itemNotes, onCommit: { self.model.commitItemNotes() })
42 | .padding(.bottom)
43 |
44 | HStack {
45 | Text(model.createdOn)
46 | Spacer()
47 | Button(action: model.deleteSelectedItem) {
48 | Image("Trash")
49 | }.buttonStyle(BorderlessButtonStyle())
50 | }
51 | }
52 | }
53 | }
54 |
55 | struct DetailView_Previews: PreviewProvider {
56 | static var previews: some View {
57 | let model = modelForPreview()
58 | let viewModel = DetailViewModel(model: model)
59 | return DetailView(model: viewModel)
60 | .padding()
61 | .previewLayout(.fixed(width: 300, height: 400))
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Support/iOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 | UISceneConfigurations
28 |
29 | UIWindowSceneSessionRoleApplication
30 |
31 |
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneDelegateClassName
35 | $(PRODUCT_MODULE_NAME).SceneDelegate
36 |
37 |
38 |
39 |
40 | UILaunchStoryboardName
41 | LaunchScreen
42 | UIRequiredDeviceCapabilities
43 |
44 | armv7
45 |
46 | UISupportedInterfaceOrientations
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationLandscapeLeft
50 | UIInterfaceOrientationLandscapeRight
51 |
52 | UISupportedInterfaceOrientations~ipad
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationPortraitUpsideDown
56 | UIInterfaceOrientationLandscapeLeft
57 | UIInterfaceOrientationLandscapeRight
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Support/macOS/App.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | com.apple.security.app-sandbox
6 |
7 | com.apple.security.files.user-selected.read-only
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Examples/TodoApp-SwiftUI/Support/macOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | Copyright © 2019 Plausible Labs Cooperative, Inc. All rights reserved.
27 | NSMainStoryboardFile
28 | Main
29 | NSPrincipalClass
30 | NSApplication
31 | NSSupportsAutomaticTermination
32 |
33 | NSSupportsSuddenTermination
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2016-2017 Plausible Labs Cooperative, Inc.
2 | All rights reserved.
3 |
4 | Permission is hereby granted, free of charge,
5 | to any person obtaining a copy of this software and associated documentation
6 | files (the "Software"), to deal in the Software without restriction,
7 | including without limitation the rights to use, copy, modify, merge, publish,
8 | distribute, sublicense, and/or sell copies of the Software, and to permit
9 | persons to whom the Software is furnished to do so, subject to the following
10 | conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Legacy/Examples/BindableControlsApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/Legacy/Examples/BindableControlsApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSMinimumSystemVersion
26 | $(MACOSX_DEPLOYMENT_TARGET)
27 | NSHumanReadableCopyright
28 | Copyright © 2016 Plausible Labs Cooperative, Inc. All rights reserved.
29 | NSMainNibFile
30 | MainMenu
31 | NSPrincipalClass
32 | NSApplication
33 |
34 |
35 |
--------------------------------------------------------------------------------
/Legacy/Examples/BindableControlsAppUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Legacy/Examples/HelloWorldApp/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Cocoa
7 | import PLRelational
8 | import PLRelationalBinding
9 | import PLBindableControls
10 |
11 | @NSApplicationMain
12 | class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
13 |
14 | @IBOutlet weak var window: NSWindow!
15 | @IBOutlet var outlineView: ExtOutlineView!
16 | @IBOutlet var empNameLabel: Label!
17 | @IBOutlet var empDeptLabel: Label!
18 |
19 | private var undoManager: PLRelationalBinding.UndoManager!
20 | private var model: ViewModel!
21 | private var listView: ListView!
22 |
23 | func applicationDidFinishLaunching(_ aNotification: Notification) {
24 | window.delegate = self
25 |
26 | // Prepare the undo manager
27 | undoManager = UndoManager()
28 |
29 | // Bind the views to the view model
30 | model = ViewModel(undoManager: undoManager)
31 |
32 | listView = ListView(model: model.employeesListModel, outlineView: outlineView)
33 | listView.selection <~> model.employeesListSelection
34 | listView.configureCell = { view, row in
35 | let rowID: Int64 = row["id"].get()!
36 | let initialValue: String? = row["first_name"].get()
37 | let nameProperty = self.model.employeeName(for: rowID, initialValue: initialValue)
38 | let textField = view.textField as! TextField
39 | textField.string <~> nameProperty
40 | }
41 |
42 | empNameLabel.string <~ model.selectedEmployeeName
43 | empDeptLabel.string <~ model.selectedEmployeeDepartment
44 | }
45 |
46 | func windowWillReturnUndoManager(_ window: NSWindow) -> Foundation.UndoManager? {
47 | return undoManager.native
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Legacy/Examples/HelloWorldApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/Legacy/Examples/HelloWorldApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | Copyright © 2017 Plausible Labs Cooperative, Inc. All rights reserved.
27 | NSMainNibFile
28 | MainMenu
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Legacy/Examples/HelloWorldAppTests/HelloWorldAppTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import XCTest
7 | @testable import HelloWorldApp
8 |
9 | class HelloWorldAppTests: XCTestCase {
10 | }
11 |
--------------------------------------------------------------------------------
/Legacy/Examples/HelloWorldAppTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Legacy/Examples/HelloWorldAppUITests/HelloWorldAppUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import XCTest
7 |
8 | class HelloWorldAppUITests: XCTestCase {
9 | }
10 |
--------------------------------------------------------------------------------
/Legacy/Examples/HelloWorldAppUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Legacy/Examples/MiniVisualizer/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/Legacy/Examples/MiniVisualizer/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | Copyright © 2017 Plausible Labs Cooperative, Inc. All rights reserved.
27 | NSMainNibFile
28 | MainMenu
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Legacy/Examples/MiniVisualizer/NSBezierPath+CGPath.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import AppKit
7 |
8 | extension NSBezierPath {
9 |
10 | public var cgPath: CGPath {
11 | let path = CGMutablePath()
12 | var points = [CGPoint](repeating: .zero, count: 3)
13 |
14 | for i in 0 ..< self.elementCount {
15 | let type = self.element(at: i, associatedPoints: &points)
16 | switch type {
17 | case .moveToBezierPathElement:
18 | path.move(to: points[0])
19 | case .lineToBezierPathElement:
20 | path.addLine(to: points[0])
21 | case .curveToBezierPathElement:
22 | path.addCurve(to: points[2], control1: points[0], control2: points[1])
23 | case .closePathBezierPathElement:
24 | path.closeSubpath()
25 | }
26 | }
27 |
28 | return path
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Legacy/Examples/MiniVisualizer/ViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 | import PLRelational
8 | import PLRelationalBinding
9 | import PLBindableControls
10 |
11 | enum Fruit {
12 | static let id = Attribute("id")
13 | static let name = Attribute("name")
14 | static let quantity = Attribute("quantity")
15 | }
16 |
17 | class ViewModel {
18 |
19 | private let db: TransactionalDatabase
20 | let fruits: TransactionalRelation
21 |
22 | private var observerRemovals: [ObserverRemoval] = []
23 |
24 | init() {
25 | // Prepare the stored relations
26 | let memoryDB = MemoryTableDatabase()
27 | let db = TransactionalDatabase(memoryDB)
28 | func createRelation(_ name: String, _ scheme: Scheme) -> TransactionalRelation {
29 | _ = memoryDB.createRelation(name, scheme: scheme)
30 | return db[name]
31 | }
32 |
33 | fruits = createRelation(
34 | "fruit",
35 | [Fruit.id, Fruit.name, Fruit.quantity])
36 |
37 | self.db = db
38 |
39 | fruits.asyncAdd([Fruit.id: 1, Fruit.name: "Apple", Fruit.quantity: 5])
40 | fruits.asyncAdd([Fruit.id: 2, Fruit.name: "Banana", Fruit.quantity: 7])
41 | fruits.asyncAdd([Fruit.id: 3, Fruit.name: "Cherry", Fruit.quantity: 42])
42 | }
43 |
44 | deinit {
45 | observerRemovals.forEach{ $0() }
46 | }
47 |
48 | lazy var fruitsProperty: ArrayProperty = {
49 | return self.fruits.arrayProperty(idAttr: Fruit.id, orderAttr: Fruit.id)
50 | }()
51 | }
52 |
--------------------------------------------------------------------------------
/Legacy/Examples/RelationChangeApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/Legacy/Examples/RelationChangeApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | Copyright © 2017 Plausible Labs Cooperative, Inc. All rights reserved.
27 | NSMainNibFile
28 | MainMenu
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Legacy/Examples/RelationChangeApp/NSBezierPath+CGPath.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import AppKit
7 |
8 | extension NSBezierPath {
9 |
10 | public var cgPath: CGPath {
11 | let path = CGMutablePath()
12 | var points = [CGPoint](repeating: .zero, count: 3)
13 |
14 | for i in 0 ..< self.elementCount {
15 | let type = self.element(at: i, associatedPoints: &points)
16 | switch type {
17 | case .moveTo:
18 | path.move(to: points[0])
19 | case .lineTo:
20 | path.addLine(to: points[0])
21 | case .curveTo:
22 | path.addCurve(to: points[2], control1: points[0], control2: points[1])
23 | case .closePath:
24 | path.closeSubpath()
25 | }
26 | }
27 |
28 | return path
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Legacy/Examples/RelationChangeApp/TextView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Cocoa
7 | import PLRelationalBinding
8 | import PLBindableControls
9 |
10 | class TextView: NSTextView {
11 |
12 | lazy var text: BindableProperty = WriteOnlyProperty(set: { [weak self] value, _ in
13 | self?.string = value
14 | })
15 | }
16 |
--------------------------------------------------------------------------------
/Legacy/Examples/SearchApp/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Cocoa
7 | import PLRelational
8 | import PLRelationalBinding
9 | import PLBindableControls
10 |
11 | @NSApplicationMain
12 | class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
13 |
14 | @IBOutlet weak var window: NSWindow!
15 | @IBOutlet var queryField: TextField!
16 | @IBOutlet var outlineView: ExtOutlineView!
17 | @IBOutlet var noResultsLabel: Label!
18 | @IBOutlet var personNameLabel: Label!
19 | @IBOutlet var personBioLabel: Label!
20 |
21 | private var undoManager: PLRelationalBinding.UndoManager!
22 | private var model: ViewModel!
23 | private var resultsListView: ListView!
24 |
25 | func applicationDidFinishLaunching(_ aNotification: Notification) {
26 | // XXX: We're not ready for dark mode yet
27 | if #available(macOS 10.14, *) {
28 | NSApp.appearance = NSAppearance(named: .aqua)
29 | }
30 |
31 | window.delegate = self
32 | queryField.deliverTransientChanges = true
33 |
34 | // Prepare the undo manager
35 | undoManager = UndoManager()
36 |
37 | // Bind the views to the view model
38 | model = ViewModel(undoManager: undoManager)
39 | model.queryString <~ queryField.string
40 |
41 | resultsListView = ResultsListView(model: model.resultsListModel, outlineView: outlineView)
42 | resultsListView.reloadCellOnUpdate = true
43 | resultsListView.selection <~> model.resultsListSelection
44 | resultsListView.configureCell = { view, row in
45 | view.textField?.attributedStringValue = SearchResult.highlightedString(from: row)
46 | }
47 |
48 | noResultsLabel.visible <~ not(model.hasResults)
49 | personNameLabel.string <~ model.selectedPersonName
50 | personBioLabel.string <~ model.selectedPersonBio
51 | }
52 |
53 | func windowWillReturnUndoManager(_ window: NSWindow) -> Foundation.UndoManager? {
54 | return undoManager.native
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Legacy/Examples/SearchApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/Legacy/Examples/SearchApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSMinimumSystemVersion
26 | $(MACOSX_DEPLOYMENT_TARGET)
27 | NSHumanReadableCopyright
28 | Copyright © 2016 Plausible Labs Cooperative, Inc. All rights reserved.
29 | NSMainNibFile
30 | MainMenu
31 | NSPrincipalClass
32 | NSApplication
33 |
34 |
35 |
--------------------------------------------------------------------------------
/Legacy/Examples/SearchApp/ResultsListView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import AppKit
7 | import PLRelationalBinding
8 | import PLBindableControls
9 |
10 | /// Normally it would not be necessary to subclass ListView, but we do that here just to customize the row selection
11 | /// color to make the search results a bit more readable.
12 | class ResultsListView: ListView {
13 |
14 | override func outlineView(_ outlineView: NSOutlineView, rowViewForItem item: Any) -> NSTableRowView? {
15 | let identifier = NSUserInterfaceItemIdentifier("RowView")
16 | if let rowView = outlineView.makeView(withIdentifier: identifier, owner: self) {
17 | return rowView as? NSTableRowView
18 | } else {
19 | let rowView = OutlineRowView(frame: NSZeroRect, rowHeight: outlineView.rowHeight)
20 | rowView.identifier = identifier
21 | return rowView
22 | }
23 | }
24 | }
25 |
26 | private class OutlineRowView: NSTableRowView {
27 | let rowHeight: CGFloat
28 |
29 | init(frame: NSRect, rowHeight: CGFloat) {
30 | self.rowHeight = rowHeight
31 | super.init(frame: frame)
32 | }
33 |
34 | required init?(coder: NSCoder) {
35 | fatalError("init(coder:) has not been implemented")
36 | }
37 |
38 | override func drawSelection(in dirtyRect: NSRect) {
39 | var selectionRect = self.bounds
40 | selectionRect.origin.y = self.bounds.height - rowHeight
41 | selectionRect.size.height = rowHeight
42 | let color = NSColor(red: 178.0/255.0, green: 223.0/255.0, blue: 255.0/255.0, alpha: 1.0)
43 | color.setFill()
44 | selectionRect.fill()
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Legacy/Examples/SearchAppTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Legacy/Examples/SearchAppTests/SearchAppTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import XCTest
7 | @testable import SearchApp
8 |
9 | class SearchAppTests: XCTestCase {
10 |
11 | override func setUp() {
12 | super.setUp()
13 | // Put setup code here. This method is called before the invocation of each test method in the class.
14 | }
15 |
16 | override func tearDown() {
17 | // Put teardown code here. This method is called after the invocation of each test method in the class.
18 | super.tearDown()
19 | }
20 |
21 | func testExample() {
22 | // This is an example of a functional test case.
23 | // Use XCTAssert and related functions to verify your tests produce the correct results.
24 | }
25 |
26 | // func testPerformanceExample() {
27 | // // This is an example of a performance test case.
28 | // self.measure {
29 | // // Put the code you want to measure the time of here.
30 | // }
31 | // }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/Legacy/Examples/SearchAppUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Legacy/Examples/SearchAppUITests/SearchAppUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import XCTest
7 |
8 | class SearchAppUITests: XCTestCase {
9 |
10 | override func setUp() {
11 | super.setUp()
12 |
13 | // Put setup code here. This method is called before the invocation of each test method in the class.
14 |
15 | // In UI tests it is usually best to stop immediately when a failure occurs.
16 | continueAfterFailure = false
17 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
18 | XCUIApplication().launch()
19 |
20 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
21 | }
22 |
23 | override func tearDown() {
24 | // Put teardown code here. This method is called after the invocation of each test method in the class.
25 | super.tearDown()
26 | }
27 |
28 | func testExample() {
29 | // Use recording to get started writing UI tests.
30 | // Use XCTAssert and related functions to verify your tests produce the correct results.
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/Legacy/Examples/TodoApp/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Cocoa
7 | import PLRelational
8 | import PLRelationalBinding
9 | import PLBindableControls
10 |
11 | @NSApplicationMain
12 | class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
13 |
14 | @IBOutlet weak var window: NSWindow!
15 | @IBOutlet weak var listContainer: NSView!
16 | @IBOutlet weak var detailContainer: NSView!
17 | @IBOutlet weak var noSelectionLabel: Label!
18 |
19 | private var undoManager: PLRelationalBinding.UndoManager!
20 |
21 | private var checklistView: ChecklistView!
22 | private var detailView: DetailView!
23 |
24 | private var model: Model!
25 |
26 | func applicationDidFinishLaunching(_ aNotification: Notification) {
27 | // XXX: We're not ready for dark mode yet
28 | if #available(macOS 10.14, *) {
29 | NSApp.appearance = NSAppearance(named: .aqua)
30 | }
31 |
32 | window.delegate = self
33 |
34 | // Prepare the undo manager
35 | undoManager = UndoManager()
36 |
37 | // Initialize our model
38 | model = Model(undoManager: undoManager)
39 |
40 | // Add the checklist view to the left side
41 | let checklistViewModel = ChecklistViewModel(model: model)
42 | checklistView = ChecklistView(frame: listContainer.bounds, model: checklistViewModel)
43 | listContainer.addSubview(checklistView)
44 |
45 | // Add the detail view to the left side
46 | let detailViewModel = DetailViewModel(model: model)
47 | detailView = DetailView(frame: detailContainer.bounds, model: detailViewModel)
48 | detailContainer.addSubview(detailView)
49 |
50 | // REQ-6
51 | // Toggle the "No Selection" label and detail view depending
52 | // on the selection state
53 | detailView.visible <~ model.hasSelection
54 | noSelectionLabel.visible <~ not(model.hasSelection)
55 | }
56 |
57 | func windowWillReturnUndoManager(_ window: NSWindow) -> Foundation.UndoManager? {
58 | return undoManager.native
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/Legacy/Examples/TodoApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/Legacy/Examples/TodoApp/Identifiers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 | import PLRelational
8 |
9 | /// Base class for our identifier value types. All of our identifiers use UUIDs
10 | /// under the hood and we have conveniences for conversion to/from `RelationValue`.
11 | class BaseID {
12 | private let uuid: String
13 |
14 | init() {
15 | self.uuid = UUID().uuidString
16 | }
17 |
18 | init(_ stringValue: String) {
19 | self.uuid = stringValue
20 | }
21 |
22 | init(_ relationValue: RelationValue) {
23 | self.uuid = relationValue.get()!
24 | }
25 |
26 | var relationValue: RelationValue {
27 | return uuid.relationValue
28 | }
29 | }
30 |
31 | /// Identifier type for rows in the `Item` relation.
32 | class ItemID: BaseID {
33 | /// Shorthand for extracting an `ItemID` from an `Item` row.
34 | convenience init(_ row: Row) {
35 | self.init(row[Item.id])
36 | }
37 | }
38 |
39 | /// Identifier type for rows in the `Tag` relation.
40 | class TagID: BaseID {
41 | /// Shorthand for extracting a `TagID` from an `Tag` row.
42 | convenience init(_ row: Row) {
43 | self.init(row[Tag.id])
44 | }
45 | }
46 |
47 | /// Conforming to this protocol allows us to use `ItemID` and `TagID` directly
48 | /// in row initializers and in select expressions without having to explicitly
49 | /// convert to `RelationValue`.
50 | extension BaseID: SelectExpressionConstantValue {}
51 |
--------------------------------------------------------------------------------
/Legacy/Examples/TodoApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | Copyright © 2017 Plausible Labs Cooperative, Inc. All rights reserved.
27 | NSMainNibFile
28 | MainMenu
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Legacy/Examples/TodoApp/trash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plausiblelabs/plrelational/d904ea052b4e2b2db720733d3e63089c7d99e49b/Legacy/Examples/TodoApp/trash.png
--------------------------------------------------------------------------------
/Legacy/Examples/TodoApp/trash@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plausiblelabs/plrelational/d904ea052b4e2b2db720733d3e63089c7d99e49b/Legacy/Examples/TodoApp/trash@2x.png
--------------------------------------------------------------------------------
/Legacy/Examples/TodoClientApp/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Cocoa
7 | import PLRelational
8 | import PLRelationalBinding
9 | import PLBindableControls
10 |
11 | @NSApplicationMain
12 | class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
13 |
14 | @IBOutlet weak var window: NSWindow!
15 | @IBOutlet weak var listContainer: NSView!
16 | @IBOutlet weak var detailContainer: NSView!
17 | @IBOutlet weak var noSelectionLabel: Label!
18 |
19 | private var undoManager: PLRelationalBinding.UndoManager!
20 |
21 | private var checklistView: ChecklistView!
22 | private var detailView: DetailView!
23 |
24 | private var model: Model!
25 |
26 | func applicationDidFinishLaunching(_ aNotification: Notification) {
27 | window.delegate = self
28 |
29 | // Prepare the undo manager
30 | undoManager = UndoManager()
31 |
32 | // Initialize our model
33 | model = Model(undoManager: undoManager)
34 |
35 | // Add the checklist view to the left side
36 | let checklistViewModel = ChecklistViewModel(model: model)
37 | checklistView = ChecklistView(frame: listContainer.bounds, model: checklistViewModel)
38 | listContainer.addSubview(checklistView)
39 |
40 | // Add the detail view to the left side
41 | let detailViewModel = DetailViewModel(model: model)
42 | detailView = DetailView(frame: detailContainer.bounds, model: detailViewModel)
43 | detailContainer.addSubview(detailView)
44 |
45 | // Toggle the "No Selection" label and detail view depending
46 | // on the selection state
47 | detailView.visible <~ model.hasSelection
48 | noSelectionLabel.visible <~ not(model.hasSelection)
49 | }
50 |
51 | func windowWillReturnUndoManager(_ window: NSWindow) -> Foundation.UndoManager? {
52 | return undoManager.native
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Legacy/Examples/TodoClientApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/Legacy/Examples/TodoClientApp/Identifiers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 | import PLRelational
8 |
9 | /// Base class for our identifier value types. All of our identifiers use UUIDs
10 | /// under the hood and we have conveniences for conversion to/from `RelationValue`.
11 | class BaseID {
12 | private let uuid: String
13 |
14 | init() {
15 | self.uuid = UUID().uuidString
16 | }
17 |
18 | init(_ stringValue: String) {
19 | self.uuid = stringValue
20 | }
21 |
22 | init(_ relationValue: RelationValue) {
23 | self.uuid = relationValue.get()!
24 | }
25 |
26 | var relationValue: RelationValue {
27 | return uuid.relationValue
28 | }
29 | }
30 |
31 | /// Identifier type for rows in the `Item` relation.
32 | class ItemID: BaseID {
33 | /// Shorthand for extracting an `ItemID` from an `Item` row.
34 | convenience init(_ row: Row) {
35 | self.init(row[Item.id])
36 | }
37 | }
38 |
39 | /// Identifier type for rows in the `Tag` relation.
40 | class TagID: BaseID {
41 | /// Shorthand for extracting a `TagID` from an `Tag` row.
42 | convenience init(_ row: Row) {
43 | self.init(row[Tag.id])
44 | }
45 | }
46 |
47 | /// Conforming to this protocol allows us to use `ItemID` and `TagID` directly
48 | /// in row initializers and in select expressions without having to explicitly
49 | /// convert to `RelationValue`.
50 | extension BaseID: SelectExpressionConstantValue {}
51 |
--------------------------------------------------------------------------------
/Legacy/Examples/TodoClientApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSMinimumSystemVersion
24 | $(MACOSX_DEPLOYMENT_TARGET)
25 | NSHumanReadableCopyright
26 | Copyright © 2017 Plausible Labs Cooperative, Inc. All rights reserved.
27 | NSMainNibFile
28 | MainMenu
29 | NSPrincipalClass
30 | NSApplication
31 |
32 |
33 |
--------------------------------------------------------------------------------
/Legacy/Examples/TodoClientApp/trash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plausiblelabs/plrelational/d904ea052b4e2b2db720733d3e63089c7d99e49b/Legacy/Examples/TodoClientApp/trash.png
--------------------------------------------------------------------------------
/Legacy/Examples/TodoClientApp/trash@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/plausiblelabs/plrelational/d904ea052b4e2b2db720733d3e63089c7d99e49b/Legacy/Examples/TodoClientApp/trash@2x.png
--------------------------------------------------------------------------------
/Legacy/Examples/Visualizer/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Cocoa
7 |
8 | @NSApplicationMain
9 | class AppDelegate: NSObject, NSApplicationDelegate {
10 |
11 | func applicationDidFinishLaunching(_ aNotification: Notification) {
12 | }
13 |
14 | func applicationWillTerminate(_ aNotification: Notification) {
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Legacy/Examples/Visualizer/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/Legacy/Examples/Visualizer/AsyncState.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | enum AsyncState {
7 | case idle(T)
8 | case loading
9 | }
10 |
--------------------------------------------------------------------------------
/Legacy/Examples/Visualizer/Box.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2015 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | class Box {
7 | let value: T
8 |
9 | init(_ value: T) {
10 | self.value = value
11 | }
12 |
13 | static func open(_ obj: AnyObject?) -> T? {
14 | return (obj as? Box)?.value
15 | }
16 | }
17 |
18 | extension Box: CustomStringConvertible {
19 | var description: String {
20 | return "Box<\(T.self)>(\(value))"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Legacy/Examples/Visualizer/DocDatabaseError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 | import PLRelational
8 |
9 | enum DocDatabaseError: Error, CustomStringConvertible { case
10 | createFailed(underlying: RelationError),
11 | openFailed(underlying: RelationError),
12 | saveFailed(underlying: RelationError),
13 | replaceFailed(underlying: NSError)
14 |
15 | var description: String {
16 | return "DocDatabaseError(\(reason))"
17 | }
18 |
19 | var reason: String {
20 | switch self {
21 | case let .createFailed(e):
22 | return "Creation failed: \(e)"
23 | case let .openFailed(e):
24 | return "Open failed: \(e)"
25 | case let .saveFailed(e):
26 | return "Save failed: \(e)"
27 | case let .replaceFailed(e):
28 | return "Failed to replace file: \(e)"
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Legacy/Examples/Visualizer/DocObject.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | /// Represents an item that exists in the document outline.
7 | struct DocObject {
8 | let docItemID: DocItemID
9 | let objectID: ObjectID
10 | let type: ItemType
11 |
12 | /// Create a DocObject whose docItemID and objectID have the same underlying value.
13 | init(_ type: ItemType) {
14 | self.docItemID = DocItemID()
15 | self.objectID = ObjectID(docItemID.stringValue)
16 | self.type = type
17 | }
18 |
19 | /// Create a DocObject that uses the given identifiers.
20 | init(docItemID: DocItemID, objectID: ObjectID, type: ItemType) {
21 | self.docItemID = docItemID
22 | self.objectID = objectID
23 | self.type = type
24 | }
25 | }
26 |
27 | extension DocObject: Equatable {}
28 | func ==(a: DocObject, b: DocObject) -> Bool {
29 | return a.docItemID == b.docItemID
30 | }
31 |
--------------------------------------------------------------------------------
/Legacy/Examples/Visualizer/DocOutlineView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Cocoa
7 | import PLRelationalBinding
8 | import PLBindableControls
9 |
10 | class DocOutlineView {
11 |
12 | private let treeView: SectionedTreeView
13 |
14 | init(model: DocOutlineModel, outlineView: NSOutlineView) {
15 | treeView = SectionedTreeView(model: model, outlineView: outlineView)
16 | treeView.animateChanges = true
17 | treeView.autoExpand = true
18 | treeView.rowView = { frame, rowHeight in
19 | return OutlineRowView(frame: frame, rowHeight: rowHeight)
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Legacy/Examples/Visualizer/Environment.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2015 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 | import SystemConfiguration
8 |
9 | final class Environment {
10 |
11 | static func computerName() -> String {
12 | if let cfstring = SCDynamicStoreCopyComputerName(nil, nil) {
13 | return (cfstring as NSString) as String
14 | } else {
15 | return "Unknown"
16 | }
17 | }
18 |
19 | static func fullUserName() -> String {
20 | return NSFullUserName()
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Legacy/Examples/Visualizer/HistoryItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 | import PLRelational
8 |
9 | struct HistoryItem {
10 | let id: HistoryItemID
11 | let tabID: TabID
12 | let outlinePath: DocOutlinePath
13 | let position: Int64
14 | }
15 |
16 | extension HistoryItem: Equatable {}
17 | func ==(a: HistoryItem, b: HistoryItem) -> Bool {
18 | return a.id == b.id
19 | }
20 |
21 | extension HistoryItem: Hashable {
22 | func hash(into hasher: inout Hasher) {
23 | hasher.combine(id)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Legacy/Examples/Visualizer/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDocumentTypes
8 |
9 |
10 | CFBundleTypeExtensions
11 |
12 | plrv
13 |
14 | CFBundleTypeIconFile
15 |
16 | CFBundleTypeName
17 | DocumentType
18 | CFBundleTypeOSTypes
19 |
20 | ????
21 |
22 | CFBundleTypeRole
23 | Editor
24 | NSDocumentClass
25 | $(PRODUCT_MODULE_NAME).Document
26 |
27 |
28 | CFBundleExecutable
29 | $(EXECUTABLE_NAME)
30 | CFBundleIconFile
31 |
32 | CFBundleIdentifier
33 | $(PRODUCT_BUNDLE_IDENTIFIER)
34 | CFBundleInfoDictionaryVersion
35 | 6.0
36 | CFBundleName
37 | $(PRODUCT_NAME)
38 | CFBundlePackageType
39 | APPL
40 | CFBundleShortVersionString
41 | 1.0
42 | CFBundleVersion
43 | 1
44 | LSMinimumSystemVersion
45 | $(MACOSX_DEPLOYMENT_TARGET)
46 | NSHumanReadableCopyright
47 | Copyright © 2016 Plausible Labs Cooperative, Inc. All rights reserved.
48 | NSMainNibFile
49 | MainMenu
50 | NSPrincipalClass
51 | NSApplication
52 |
53 |
54 |
--------------------------------------------------------------------------------
/Legacy/Examples/Visualizer/ItemType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 | import PLRelational
8 |
9 | enum ItemType: Int64 { case
10 | section = 0,
11 | group = 1,
12 | storedRelation = 2,
13 | sharedRelation = 3,
14 | privateRelation = 4
15 |
16 | init?(_ value: RelationValue) {
17 | self.init(rawValue: value.get()!)!
18 | }
19 |
20 | init?(_ row: Row) {
21 | self.init(row["type"])
22 | }
23 |
24 | var name: String {
25 | switch self {
26 | case .section: return "Section"
27 | case .group: return "Group"
28 | case .storedRelation: return "Stored Relation"
29 | case .sharedRelation: return "Shared Relation"
30 | case .privateRelation: return "Private Relation"
31 | }
32 | }
33 |
34 | var isGroupType: Bool {
35 | switch self {
36 | case .section, .group:
37 | return true
38 | default:
39 | return false
40 | }
41 | }
42 |
43 | var isObjectType: Bool {
44 | return !isGroupType
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Legacy/Examples/Visualizer/NSColor255.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import AppKit
7 |
8 | extension NSColor {
9 | /// Initialize a color with 255-based component values.
10 | convenience init(r: Int, g: Int, b: Int, a: Int = 255) {
11 | func convert(_ component: Int) -> CGFloat {
12 | return CGFloat(component) / 255.0
13 | }
14 | self.init(calibratedRed: convert(r), green: convert(g), blue: convert(b), alpha: convert(a))
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Legacy/Examples/Visualizer/NSControlSwiftAction.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2015 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import AppKit
7 |
8 |
9 | private class ActionTrampoline: NSObject {
10 | var actionFunc: (NSControl) -> Void
11 |
12 | init(action: @escaping (NSControl) -> Void) {
13 | self.actionFunc = action
14 | }
15 |
16 | @objc func action(_ sender: NSControl) {
17 | actionFunc(sender)
18 | }
19 | }
20 |
21 | protocol NSActionSettingExtensionProtocol: class {
22 | var target: AnyObject? { get set }
23 | var action: Selector? { get set }
24 | }
25 |
26 | extension NSActionSettingExtensionProtocol {
27 | @discardableResult func setAction(_ action: @escaping (Self) -> Void) -> Self {
28 | let trampoline = ActionTrampoline(action: { sender in
29 | action(sender as! Self)
30 | })
31 | _ = attach(trampoline, to: self)
32 | self.target = trampoline
33 | self.action = #selector(ActionTrampoline.action(_:))
34 |
35 | return self
36 | }
37 | }
38 |
39 | extension NSControl: NSActionSettingExtensionProtocol {}
40 | extension NSMenuItem: NSActionSettingExtensionProtocol {}
41 |
--------------------------------------------------------------------------------
/Legacy/Examples/Visualizer/ObjectAttachment.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2015 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 |
8 | private var associatedObjectKey = UnsafeMutablePointer.allocate(capacity: 1)
9 |
10 | /// Attach an object to another object. This can be used for turning weak/unowned
11 | /// properties into de-facto strong properties by attaching the target of the
12 | /// property to the object that contains the property. Good for standalone delegates
13 | /// and action targets.
14 | ///
15 | /// - parameter object: The target object whose lifetime will be extended.
16 | /// - parameter to: The source object to attach to.
17 | /// - returns: `object`, for easier chaining of calls.
18 | func attach(_ object: T, to: AnyObject) -> T{
19 | objc_sync_enter(to)
20 |
21 | let array = objc_getAssociatedObject(to, associatedObjectKey) as? NSArray ?? []
22 | let newArray = array.adding(object)
23 | objc_setAssociatedObject(to, associatedObjectKey, newArray, .OBJC_ASSOCIATION_RETAIN)
24 |
25 | objc_sync_exit(to)
26 |
27 | return object
28 | }
29 |
--------------------------------------------------------------------------------
/Legacy/Examples/Visualizer/OutlineRowView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Cocoa
7 |
8 | class OutlineRowView: NSTableRowView {
9 | let rowHeight: CGFloat
10 |
11 | init(frame: NSRect, rowHeight: CGFloat) {
12 | self.rowHeight = rowHeight
13 | super.init(frame: frame)
14 | }
15 |
16 | required init?(coder: NSCoder) {
17 | fatalError("init(coder:) has not been implemented")
18 | }
19 |
20 | override func drawSelection(in dirtyRect: NSRect) {
21 | var selectionRect = self.bounds
22 | selectionRect.origin.y = self.bounds.height - rowHeight
23 | selectionRect.size.height = rowHeight
24 | let color = self.isEmphasized ? VisualizerColors.strongHighlight : VisualizerColors.weakHighlight
25 | color.setFill()
26 | selectionRect.fill()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Legacy/Examples/Visualizer/RelationViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 | import PLRelational
8 |
9 | struct RelationViewModel {
10 |
11 | let rootID: ObjectID
12 | let models: [ObjectID: RelationModel]
13 |
14 | // init(rootID: ObjectID, models: [ObjectID: RelationModel]) {
15 | // // TODO
16 | // }
17 | }
18 |
--------------------------------------------------------------------------------
/Legacy/Examples/Visualizer/ScrollView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Cocoa
7 | import PLRelationalBinding
8 |
9 | open class ScrollView: NSScrollView {
10 |
11 | open lazy var visible: BindableProperty = WriteOnlyProperty(set: { [weak self] value, _ in
12 | self?.isHidden = !value
13 | })
14 | }
15 |
--------------------------------------------------------------------------------
/Legacy/Examples/Visualizer/SidebarModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 | import PLRelational
8 | import PLRelationalBinding
9 |
10 | class SidebarModel {
11 |
12 | let db: DocDatabase
13 | private let selectedObjects: Relation
14 |
15 | /// - Parameters:
16 | /// - db: The database.
17 | /// - selectedObjects: Relation with scheme [id, type, name].
18 | init(db: DocDatabase, selectedObjects: Relation) {
19 | precondition(selectedObjects.scheme == DB.Object.scheme)
20 |
21 | self.db = db
22 | self.selectedObjects = selectedObjects
23 | }
24 |
25 | lazy var itemSelected: AsyncReadableProperty = {
26 | return self.selectedObjects.nonEmpty.property()
27 | }()
28 |
29 | lazy var itemNotSelected: AsyncReadableProperty = {
30 | return self.selectedObjects.empty.property()
31 | }()
32 | }
33 |
--------------------------------------------------------------------------------
/Legacy/Examples/Visualizer/VisualizerColors.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2015 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Cocoa
7 |
8 | class VisualizerColors {
9 | static let strongHighlight = NSColor(srgbRed: 113.0/255.0, green: 130.0/255.0, blue: 226.0/255.0, alpha: 1.0)
10 | static let weakHighlight = NSColor(srgbRed: 213.0/255.0, green: 230.0/255.0, blue: 249.0/255.0, alpha: 1.0)
11 | }
12 |
--------------------------------------------------------------------------------
/Legacy/Examples/VisualizerTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Legacy/Examples/VisualizerTests/VisualizerTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import XCTest
7 | @testable import Visualizer
8 |
9 | class VisualizerTests: XCTestCase {
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/Legacy/Examples/VisualizerUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Legacy/Examples/VisualizerUITests/VisualizerUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import XCTest
7 |
8 | class VisualizerUITests: XCTestCase {
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/Legacy/PLBindableControls/Sources/AppKit/BackgroundView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Cocoa
7 | import PLRelationalBinding
8 |
9 | open class BackgroundView: NSView {
10 |
11 | public lazy var visible: BindableProperty = WriteOnlyProperty(set: { [weak self] value, _ in
12 | self?.isHidden = !value
13 | })
14 |
15 | public var backgroundColor: NSColor?
16 |
17 | open override func draw(_ dirtyRect: NSRect) {
18 | super.draw(dirtyRect)
19 |
20 | if let bg = backgroundColor {
21 | bg.setFill()
22 | NSBezierPath.fill(dirtyRect)
23 | }
24 | }
25 |
26 | open override var isFlipped: Bool {
27 | return true
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Legacy/PLBindableControls/Sources/AppKit/Button.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Cocoa
7 | import PLRelationalBinding
8 |
9 | open class Button: NSButton {
10 |
11 | public private(set) lazy var visible: BindableProperty = WriteOnlyProperty(set: { [weak self] value, _ in
12 | self?.isHidden = !value
13 | })
14 |
15 | public private(set) lazy var disabled: BindableProperty = WriteOnlyProperty(set: { [unowned self] value, _ in
16 | self.isEnabled = !value
17 | })
18 |
19 | public private(set) lazy var string: BindableProperty = WriteOnlyProperty(set: { [weak self] value, _ in
20 | self?.title = value
21 | })
22 |
23 | private let _clicks = SourceSignal<()>()
24 | public var clicks: Signal<()> { return _clicks }
25 |
26 | public override init(frame: NSRect) {
27 | super.init(frame: frame)
28 | target = self
29 | action = #selector(buttonClicked(_:))
30 | }
31 |
32 | public required init?(coder: NSCoder) {
33 | super.init(coder: coder)
34 | target = self
35 | action = #selector(buttonClicked(_:))
36 | }
37 |
38 | @objc func buttonClicked(_ sender: Button) {
39 | _clicks.notifyAction()
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Legacy/PLBindableControls/Sources/AppKit/ContextMenu.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Cocoa
7 |
8 | public struct ContextMenu {
9 |
10 | // TODO: Remove this in favor of MenuItem
11 | public enum Item { case
12 | titled(title: String, enabled: Bool, action: () -> Void),
13 | separator
14 | }
15 |
16 | public let items: [Item]
17 |
18 | public init(items: [Item]) {
19 | self.items = items
20 | }
21 | }
22 |
23 | extension ContextMenu {
24 |
25 | public var nsmenu: NSMenu {
26 | let menu = NSMenu()
27 | menu.autoenablesItems = false
28 |
29 | for item in items {
30 | let nsitem: NSMenuItem
31 | switch item {
32 | case let .titled(title, enabled, action):
33 | nsitem = ClosureMenuItem(title: title, actionClosure: action, keyEquivalent: "")
34 | nsitem.isEnabled = enabled
35 | break
36 | case .separator:
37 | nsitem = NSMenuItem.separator()
38 | }
39 | menu.addItem(nsitem)
40 | }
41 |
42 | return menu
43 | }
44 | }
45 |
46 | private class ClosureMenuItem: NSMenuItem {
47 |
48 | private var actionClosure: () -> Void
49 |
50 | init(title: String, actionClosure: @escaping () -> Void, keyEquivalent: String) {
51 | self.actionClosure = actionClosure
52 | super.init(title: title, action: #selector(ClosureMenuItem.action(_:)), keyEquivalent: keyEquivalent)
53 | self.target = self
54 | }
55 |
56 | required init(coder aDecoder: NSCoder) {
57 | fatalError("init(coder:) has not been implemented")
58 | }
59 |
60 | @objc func action(_ sender: NSMenuItem) {
61 | self.actionClosure()
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Legacy/PLBindableControls/Sources/AppKit/EphemeralTextField.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Cocoa
7 | import PLRelationalBinding
8 |
9 | /// A simplified variant of `TextField` that clears the text field whenever new text is committed
10 | /// with the enter key.
11 | open class EphemeralTextField: NSTextField {
12 |
13 | private let _strings = SourceSignal()
14 | public var strings: Signal { return _strings }
15 |
16 | public override init(frame: NSRect) {
17 | super.init(frame: frame)
18 |
19 | target = self
20 | action = #selector(stringCommitted(_:))
21 | }
22 |
23 | public required init?(coder: NSCoder) {
24 | super.init(coder: coder)
25 |
26 | target = self
27 | action = #selector(stringCommitted(_:))
28 | }
29 |
30 | @objc func stringCommitted(_ sender: NSTextField) {
31 | if !self.stringValue.isEmpty {
32 | self._strings.notifyValueChanging(self.stringValue)
33 | self.stringValue = ""
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Legacy/PLBindableControls/Sources/AppKit/ExtOutlineView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Cocoa
7 |
8 | public protocol ExtOutlineViewDelegate: NSOutlineViewDelegate {
9 | func outlineView(_ outlineView: NSOutlineView, menuForItem item: Any) -> NSMenu?
10 | }
11 |
12 | open class ExtOutlineView: NSOutlineView {
13 |
14 | open override func validateProposedFirstResponder(_ responder: NSResponder, for event: NSEvent?) -> Bool {
15 | // XXX: The following prevents the text field from becoming first responder if it is right-clicked
16 | // (which should instead cause the context menu to be shown)
17 | if let event = event {
18 | if event.type == .rightMouseDown || (event.type == .leftMouseDown && event.modifierFlags.contains(.control)) {
19 | return false
20 | } else {
21 | return super.validateProposedFirstResponder(responder, for: event)
22 | }
23 | } else {
24 | return super.validateProposedFirstResponder(responder, for: event)
25 | }
26 | }
27 |
28 | open override func menu(for event: NSEvent) -> NSMenu? {
29 | // Notify the delegate if a context menu is requested for an item
30 | let point = self.convert(event.locationInWindow, from: nil)
31 | let row = self.row(at: point)
32 | let item = self.item(atRow: row)
33 | if item == nil {
34 | return nil
35 | }
36 | return (self.delegate as! ExtOutlineViewDelegate).outlineView(self, menuForItem: item!)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Legacy/PLBindableControls/Sources/AppKit/ImageView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Cocoa
7 | import PLRelationalBinding
8 |
9 | open class ImageView: NSImageView {
10 |
11 | public lazy var img: BindableProperty = WriteOnlyProperty(set: { [unowned self] value, _ in
12 | self.image = value.nsimage
13 | })
14 |
15 | public override init(frame: NSRect) {
16 | super.init(frame: frame)
17 | }
18 |
19 | public required init?(coder: NSCoder) {
20 | super.init(coder: coder)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Legacy/PLBindableControls/Sources/AppKit/Label.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Cocoa
7 | import PLRelationalBinding
8 |
9 | open class Label: NSTextField {
10 | public override init(frame frameRect: NSRect) {
11 | super.init(frame: frameRect)
12 | setup()
13 | }
14 |
15 | public required init?(coder: NSCoder) {
16 | super.init(coder: coder)
17 | setup()
18 | }
19 |
20 | public private(set) lazy var string: BindableProperty = WriteOnlyProperty(set: { [weak self] value, _ in
21 | self?.stringValue = value
22 | })
23 |
24 | public private(set) lazy var attributedString: BindableProperty = WriteOnlyProperty(set: { [weak self] value, _ in
25 | self?.attributedStringValue = value
26 | })
27 |
28 | public private(set) lazy var bindable_textColor: BindableProperty = WriteOnlyProperty(set: { [weak self] value, _ in
29 | self?.textColor = value
30 | })
31 |
32 | public private(set) lazy var visible: BindableProperty = WriteOnlyProperty(set: { [weak self] value, _ in
33 | self?.isHidden = !value
34 | })
35 | }
36 |
37 | extension Label {
38 | fileprivate func setup() {
39 | self.drawsBackground = false
40 | self.isBezeled = false
41 | self.isEditable = false
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Legacy/PLBindableControls/Sources/AppKit/ProgressIndicator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Cocoa
7 | import PLRelationalBinding
8 |
9 | open class ProgressIndicator: NSProgressIndicator {
10 |
11 | public lazy var visible: BindableProperty = WriteOnlyProperty(set: { [weak self] value, _ in
12 | self?.isHidden = !value
13 | if value {
14 | self?.startAnimation(nil)
15 | } else {
16 | self?.stopAnimation(nil)
17 | }
18 | })
19 | }
20 |
--------------------------------------------------------------------------------
/Legacy/PLBindableControls/Sources/AppKit/TextView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Cocoa
7 | import PLRelationalBinding
8 |
9 | open class TextView: NSTextView, NSTextViewDelegate {
10 |
11 | private lazy var _text: ExternalValueProperty = ExternalValueProperty(
12 | get: { [unowned self] in
13 | self.string
14 | },
15 | set: { [unowned self] value, _ in
16 | self.string = value
17 | // XXX: Without the following sometimes part of the text will disappear, not sure why yet
18 | self.layoutManager?.invalidateLayout(forCharacterRange: NSMakeRange(0, value.count), actualCharacterRange: nil)
19 | }
20 | )
21 | public var text: ReadWriteProperty { return _text }
22 |
23 | private var previousString: String?
24 |
25 | public override init(frame: NSRect) {
26 | super.init(frame: frame)
27 | configure()
28 | }
29 |
30 | public required init?(coder: NSCoder) {
31 | super.init(coder: coder)
32 | configure()
33 | }
34 |
35 | private func configure() {
36 | // XXX: Keep built-in undo support disabled until we decide how to make it play
37 | // nicely with our own undo stuff
38 | self.allowsUndo = false
39 | self.delegate = self
40 | }
41 |
42 | open func textDidBeginEditing(_ notification: Notification) {
43 | previousString = self.string
44 | }
45 |
46 | open func textDidEndEditing(_ notification: Notification) {
47 | if let previousString = previousString {
48 | if self.string != previousString {
49 | _text.changed(transient: false)
50 | }
51 | }
52 | previousString = nil
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Legacy/PLBindableControls/Sources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Legacy/PLBindableControls/Sources/Shared/Bindable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 |
8 | // The following is based on ReactiveSwift's ReactiveExtensionProvider protocol and Reactive proxy concepts.
9 |
10 | /// A provider of binding extensions.
11 | public protocol BindingExtensionsProvider: class {
12 | }
13 |
14 | /// A proxy that hosts binding extensions of `Base`.
15 | public struct Bindable {
16 | /// The `Base` instance used to invoke the extensions.
17 | public let base: Base
18 |
19 | fileprivate init(_ base: Base) {
20 | self.base = base
21 | }
22 | }
23 |
24 | extension BindingExtensionsProvider {
25 | /// A proxy that hosts binding extensions for `self`.
26 | public var bindable: Bindable {
27 | return Bindable(self)
28 | }
29 |
30 | /// A proxy that hosts static binding extensions for the type of `self`.
31 | public static var bindable: Bindable.Type {
32 | return Bindable.self
33 | }
34 | }
35 |
36 | // TODO: Eventually we should move Bindable and BindingExtensionsProvider to PLRelationalBinding, and then define
37 | // this NSObject extension in PLBindableControls.
38 | extension NSObject: BindingExtensionsProvider {
39 | }
40 |
--------------------------------------------------------------------------------
/Legacy/PLBindableControls/Sources/Shared/CheckState.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | #if os(macOS)
7 | import Cocoa
8 | #endif
9 | import PLRelationalBinding
10 |
11 | public enum CheckState: String { case
12 | on = "On",
13 | off = "Off",
14 | mixed = "Mixed"
15 |
16 | public init(_ boolValue: Bool?) {
17 | switch boolValue {
18 | case nil:
19 | self = .mixed
20 | case .some(false):
21 | self = .off
22 | case .some(true):
23 | self = .on
24 | }
25 | }
26 |
27 | public init(commonValue: CommonValue) {
28 | switch commonValue {
29 | case .none:
30 | self = .off
31 | case .one(let b):
32 | self = b ? .on : .off
33 | case .multi:
34 | self = .mixed
35 | }
36 | }
37 |
38 | #if os(macOS)
39 | public init(_ nsValue: NSControl.StateValue) {
40 | switch nsValue {
41 | case .mixed:
42 | self = .mixed
43 | case .off:
44 | self = .off
45 | case .on:
46 | self = .on
47 | default:
48 | preconditionFailure("Must be one of NSControl.StateValue.{mixed, off, on}")
49 | }
50 | }
51 | #endif
52 |
53 | public var boolValue: Bool {
54 | switch self {
55 | case .on:
56 | return true
57 | case .off:
58 | return false
59 | case .mixed:
60 | preconditionFailure("Cannot represent mixed state as a boolean")
61 | }
62 | }
63 |
64 | public var commonValue: CommonValue {
65 | switch self {
66 | case .on:
67 | return .one(true)
68 | case .off:
69 | return .one(false)
70 | case .mixed:
71 | return .multi
72 | }
73 | }
74 |
75 | #if os(macOS)
76 | /// The Cocoa-defined value that corresponds to this CheckState (NSOnState, NSOffState, or NSMixedState).
77 | public var nsValue: NSControl.StateValue {
78 | switch self {
79 | case .on:
80 | return .on
81 | case .off:
82 | return .off
83 | case .mixed:
84 | return .mixed
85 | }
86 | }
87 | #endif
88 | }
89 |
--------------------------------------------------------------------------------
/Legacy/PLBindableControls/Sources/Shared/Image.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | #if os(macOS)
7 | import AppKit
8 | #else
9 | import UIKit
10 | #endif
11 |
12 | /// This serves as an insulation layer to avoid having platform-specific (i.e., AppKit or UIKit)
13 | /// dependencies in the model, which should be platform-independent.
14 | public class Image {
15 |
16 | #if os(macOS)
17 | public let nsimage: NSImage
18 |
19 | public init(_ nsimage: NSImage) {
20 | self.nsimage = nsimage
21 | }
22 |
23 | public init?(named name: String) {
24 | guard let nsimage = NSImage(named: name) else { return nil }
25 | self.nsimage = nsimage
26 | }
27 | #else
28 | public let uiimage: UIImage
29 |
30 | public init(_ uiimage: UIImage) {
31 | self.uiimage = uiimage
32 | }
33 |
34 | public init?(named name: String) {
35 | guard let uiimage = UIImage(named: name) else { return nil }
36 | self.uiimage = uiimage
37 | }
38 | #endif
39 | }
40 |
--------------------------------------------------------------------------------
/Legacy/PLBindableControls/Sources/Shared/TextProperty.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import PLRelationalBinding
7 |
8 | /// A sum type that holds either a read-only or read-write String-typed property, used for
9 | /// binding the content of a TextField or similar control.
10 | public enum TextProperty { case
11 | readOnly(ReadableProperty),
12 | readWrite(ReadWriteProperty),
13 | asyncReadOnly(AsyncReadableProperty),
14 | asyncReadWrite(AsyncReadWriteProperty),
15 | readOnlyOpt(ReadableProperty),
16 | readWriteOpt(ReadWriteProperty),
17 | asyncReadOnlyOpt(AsyncReadableProperty),
18 | asyncReadWriteOpt(AsyncReadWriteProperty)
19 |
20 | var editable: Bool {
21 | switch self {
22 | case .readOnly, .asyncReadOnly, .readOnlyOpt, .asyncReadOnlyOpt:
23 | return false
24 | case .readWrite, .asyncReadWrite, .readWriteOpt, .asyncReadWriteOpt:
25 | return true
26 | }
27 | }
28 | }
29 |
30 | #if os(macOS)
31 | extension TextField {
32 | public func bind(_ property: TextProperty?) {
33 | string.unbindAll()
34 | optString.unbindAll()
35 | if let property = property {
36 | switch property {
37 | case .readOnly(let text):
38 | string <~ text
39 | case .readWrite(let text):
40 | string <~> text
41 | case .asyncReadOnly(let text):
42 | string <~ text
43 | case .asyncReadWrite(let text):
44 | string <~> text
45 | case .readOnlyOpt(let text):
46 | optString <~ text
47 | case .readWriteOpt(let text):
48 | optString <~> text
49 | case .asyncReadOnlyOpt(let text):
50 | optString <~ text
51 | case .asyncReadWriteOpt(let text):
52 | optString <~> text
53 | }
54 | isEditable = property.editable
55 | } else {
56 | isEditable = false
57 | }
58 | }
59 | }
60 | #endif
61 |
--------------------------------------------------------------------------------
/Legacy/PLBindableControls/Sources/UIKit/BarButtonItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import UIKit
7 | import PLRelationalBinding
8 |
9 | // TODO: Convert this to Bindable extension
10 | open class BarButtonItem: UIBarButtonItem {
11 |
12 | public lazy var bindable_enabled: BindableProperty = {
13 | return WriteOnlyProperty(set: { [weak self] value, _ in
14 | self?.isEnabled = value
15 | })
16 | }()
17 |
18 | private let _bindable_clicks = SourceSignal<()>()
19 | public var bindable_clicks: Signal<()> { return _bindable_clicks }
20 |
21 | public override init() {
22 | super.init()
23 | target = self
24 | action = #selector(buttonClicked(_:))
25 | }
26 |
27 | public required init?(coder: NSCoder) {
28 | super.init(coder: coder)
29 | target = self
30 | action = #selector(buttonClicked(_:))
31 | }
32 |
33 | @objc func buttonClicked(_ sender: BarButtonItem) {
34 | _bindable_clicks.notifyAction()
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Legacy/PLBindableControls/Sources/UIKit/ImageView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import UIKit
7 | import PLRelationalBinding
8 |
9 | extension Bindable where Base: UIImageView {
10 |
11 | public var image: BindableProperty {
12 | return WriteOnlyProperty(set: { [weak base = self.base] value, _ in
13 | base?.image = value.uiimage
14 | })
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Legacy/PLBindableControls/Sources/UIKit/Slider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import UIKit
7 | import PLRelationalBinding
8 |
9 | open class Slider: UISlider {
10 |
11 | private lazy var changeHandler: ChangeHandler = ChangeHandler(
12 | onLock: { [weak self] in
13 | guard let strongSelf = self else { return }
14 | strongSelf.isUserInteractionEnabled = false
15 | },
16 | onUnlock: { [weak self] in
17 | guard let strongSelf = self else { return }
18 | strongSelf.isUserInteractionEnabled = true
19 | }
20 | )
21 |
22 | private lazy var _bindable_value: ExternalValueProperty = ExternalValueProperty(
23 | get: { [weak self] in
24 | self?.value ?? 0.0
25 | },
26 | set: { [weak self] newValue, _ in
27 | self?.value = newValue
28 | },
29 | changeHandler: self.changeHandler
30 | )
31 | public var bindable_value: ReadWriteProperty { return _bindable_value }
32 |
33 | public override init(frame: CGRect) {
34 | super.init(frame: frame)
35 | self.addTarget(self, action: #selector(updatedState(_:)), for: .valueChanged)
36 | }
37 |
38 | public required init?(coder: NSCoder) {
39 | super.init(coder: coder)
40 | self.addTarget(self, action: #selector(updatedState(_:)), for: .valueChanged)
41 | }
42 |
43 | @objc func updatedState(_ sender: Slider) {
44 | // TODO: Use transient: true if isContinuous==true and the value is still changing?
45 | _bindable_value.changed(transient: false)
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Legacy/PLBindableControls/Sources/UIKit/Switch.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import UIKit
7 | import PLRelationalBinding
8 |
9 | open class Switch: UISwitch {
10 |
11 | private lazy var changeHandler: ChangeHandler = ChangeHandler(
12 | onLock: { [weak self] in
13 | guard let strongSelf = self else { return }
14 | strongSelf.isUserInteractionEnabled = false
15 | },
16 | onUnlock: { [weak self] in
17 | guard let strongSelf = self else { return }
18 | strongSelf.isUserInteractionEnabled = true
19 | }
20 | )
21 |
22 | private lazy var _bindable_on: ExternalValueProperty = ExternalValueProperty(
23 | get: { [weak self] in
24 | self?.isOn ?? false
25 | },
26 | set: { [weak self] newValue, _ in
27 | self?.setOn(newValue, animated: false)
28 | },
29 | changeHandler: self.changeHandler
30 | )
31 | public var bindable_on: ReadWriteProperty { return _bindable_on }
32 |
33 | public override init(frame: CGRect) {
34 | super.init(frame: frame)
35 | self.addTarget(self, action: #selector(updatedState(_:)), for: .valueChanged)
36 | }
37 |
38 | public required init?(coder: NSCoder) {
39 | super.init(coder: coder)
40 | self.addTarget(self, action: #selector(updatedState(_:)), for: .valueChanged)
41 | }
42 |
43 | @objc func updatedState(_ sender: Switch) {
44 | _bindable_on.changed(transient: false)
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Legacy/PLBindableControls/Sources/UIKit/TextField.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import UIKit
7 | import PLRelationalBinding
8 |
9 | open class TextField: UITextField, UITextFieldDelegate {
10 |
11 | private lazy var _bindable_text: ExternalValueProperty = ExternalValueProperty(
12 | get: {
13 | self.text ?? ""
14 | },
15 | set: { value, _ in
16 | self.text = value
17 | }
18 | )
19 | public var bindable_text: ReadWriteProperty { return _bindable_text }
20 |
21 | /// Whether to deliver transient changes. If `true` a transient change will be delivered via the
22 | /// `string` property on each keystroke. If `false` (the default), no transient changes will be
23 | /// delivered, and only a single commit change will be delivered when the user is done editing.
24 | public var deliverTransientChanges: Bool = false
25 |
26 | private var previousCommittedValue: String?
27 |
28 | public override init(frame: CGRect) {
29 | super.init(frame: frame)
30 | self.configure()
31 | }
32 |
33 | public required init?(coder: NSCoder) {
34 | super.init(coder: coder)
35 | self.configure()
36 | }
37 |
38 | private func configure() {
39 | self.delegate = self
40 | self.addTarget(self, action: #selector(textFieldChanged(_:)), for: .editingChanged)
41 | }
42 |
43 | open func textFieldDidBeginEditing(_ textField: UITextField) {
44 | _bindable_text.exclusiveMode = true
45 | previousCommittedValue = self.text
46 | }
47 |
48 | @objc func textFieldChanged(_ sender: UITextField) {
49 | if deliverTransientChanges {
50 | _bindable_text.changed(transient: true)
51 | }
52 | }
53 |
54 | open func textFieldShouldReturn(_ textField: UITextField) -> Bool {
55 | return true
56 | }
57 |
58 | open func textFieldDidEndEditing(_ textField: UITextField, reason: UITextField.DidEndEditingReason) {
59 | if self.text != previousCommittedValue {
60 | _bindable_text.changed(transient: false)
61 | }
62 | _bindable_text.exclusiveMode = false
63 | previousCommittedValue = nil
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Legacy/PLRelationalBinding/Sources/ArrayBinarySearch.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | extension Array {
7 | func binarySearch(_ lessThan: (Element) -> Bool) -> Int {
8 | return self[0..(_ element: Element, _ f: (Element) -> T, _ compare: (T, T) -> Bool) -> Int where T: Comparable {
12 | let val = f(element)
13 |
14 | let index = binarySearch({ compare(f($0), val) })
15 |
16 | if index < count {
17 | insert(element, at: index)
18 | } else {
19 | append(element)
20 | }
21 |
22 | return index
23 | }
24 | }
25 |
26 | extension Array where Element: Comparable {
27 | func binarySearch(_ element: Element) -> Int {
28 | return self.binarySearch({ $0 < element })
29 | }
30 | }
31 |
32 | extension ArraySlice {
33 | func binarySearch(_ lessThan: (Element) -> Bool) -> Int {
34 | if count == 0 { return startIndex }
35 | let mid = startIndex + ((endIndex - startIndex) / 2)
36 | if lessThan(self[mid]) {
37 | return self[(mid + 1).. Element? {
8 | return (index >= 0 && index < self.count)
9 | ? self[index]
10 | : nil
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/Legacy/PLRelationalBinding/Sources/CollectionElement.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 |
8 | /// :nodoc: Elided from docs to reduce clutter for now; part of "official" API but may be reworked in the near future
9 | public protocol CollectionElement: class {
10 | associatedtype ID: Hashable, Plistable
11 | associatedtype Data
12 |
13 | var id: ID { get }
14 | var data: Data { get set }
15 | }
16 |
--------------------------------------------------------------------------------
/Legacy/PLRelationalBinding/Sources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Legacy/PLRelationalBinding/Sources/RelationAsyncProperty.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import PLRelational
7 |
8 | public struct RelationMutationConfig {
9 | public let snapshot: () -> TransactionalDatabaseSnapshot
10 | public let update: (_ newValue: T) -> Void
11 | public let commit: (_ before: TransactionalDatabaseSnapshot, _ newValue: T) -> Void
12 |
13 | public init(
14 | snapshot: @escaping () -> TransactionalDatabaseSnapshot,
15 | update: @escaping (_ newValue: T) -> Void,
16 | commit: @escaping (_ before: TransactionalDatabaseSnapshot, _ newValue: T) -> Void)
17 | {
18 | self.snapshot = snapshot
19 | self.update = update
20 | self.commit = commit
21 | }
22 | }
23 |
24 | private class RelationAsyncReadWriteProperty: AsyncReadWriteProperty {
25 | private let mutator: RelationMutationConfig
26 | private var before: TransactionalDatabaseSnapshot?
27 |
28 | init(signal: Signal, mutator: RelationMutationConfig) {
29 | self.mutator = mutator
30 |
31 | super.init(signal: signal)
32 | }
33 |
34 | fileprivate override func setValue(_ value: T, _ metadata: ChangeMetadata) {
35 | if before == nil {
36 | before = mutator.snapshot()
37 | }
38 |
39 | // Note: We don't set `mutableValue` here; instead we wait to receive the change from the
40 | // relation in our signal observer and then update `mutableValue` there
41 | if metadata.transient {
42 | mutator.update(value)
43 | } else {
44 | mutator.commit(before!, value)
45 | before = nil
46 | }
47 | }
48 | }
49 |
50 | extension SignalType {
51 |
52 | // MARK: Relation / AsyncReadWriteProperty convenience
53 |
54 | /// Lifts this signal into an AsyncReadWriteProperty that writes values back to a relation via the given mutator.
55 | public func property(mutator: RelationMutationConfig) -> AsyncReadWriteProperty {
56 | return RelationAsyncReadWriteProperty(signal: signal, mutator: mutator)
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Legacy/PLRelationalBinding/Sources/RelationValuePlistable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 | import PLRelational
8 |
9 | /// :nodoc: Elided from docs to reduce clutter for now; part of "official" API but may be reworked in the near future
10 | public protocol Plistable {
11 | func toPlist() -> AnyObject
12 | static func fromPlist(_ obj: AnyObject) -> Self?
13 | }
14 |
15 | /// :nodoc: Elided from docs to reduce clutter for now; part of "official" API but may be reworked in the near future
16 | extension RelationValue: Plistable {
17 | public func toPlist() -> AnyObject {
18 | switch self {
19 | case let .integer(value):
20 | return ["type": "integer", "value": value.description] as NSDictionary
21 | case let .text(value):
22 | return ["type": "text", "value": value] as NSDictionary
23 | default:
24 | // TODO: Support other types
25 | return [:] as NSDictionary
26 | }
27 | }
28 |
29 | public static func fromPlist(_ obj: AnyObject) -> RelationValue? {
30 | if let plist = obj as? [String: String] {
31 | if let type = plist["type"], let stringValue = plist["value"] {
32 | switch type {
33 | case "integer":
34 | if let v = Int64(stringValue) {
35 | return RelationValue(v)
36 | } else {
37 | return nil
38 | }
39 | case "text":
40 | return RelationValue(stringValue)
41 | default:
42 | return nil
43 | }
44 | } else {
45 | return nil
46 | }
47 | } else {
48 | return nil
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Legacy/PLRelationalBinding/Sources/RowCollectionElement.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 | import PLRelational
8 |
9 | /// :nodoc: Elided from docs to reduce clutter for now; part of "official" API but may be reworked in the near future
10 | public class RowCollectionElement: CollectionElement {
11 | public typealias ID = RelationValue
12 | public typealias Data = Row
13 |
14 | public let id: RelationValue
15 | public var data: Row
16 | public let tag: AnyObject?
17 |
18 | init(id: RelationValue, data: Row, tag: AnyObject?) {
19 | self.id = id
20 | self.data = data
21 | self.tag = tag
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Legacy/PLRelationalBinding/Tests/ArrayBinarySearchTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import XCTest
7 | @testable import PLRelationalBinding
8 |
9 | class ArrayBinarySearchTests: XCTestCase {
10 |
11 | func testBinarySearch() {
12 | XCTAssertEqual([].binarySearch(42), 0)
13 | XCTAssertEqual([0, 2, 4, 6].binarySearch(-1), 0)
14 | XCTAssertEqual([0, 2, 4, 6].binarySearch(0), 0)
15 | XCTAssertEqual([0, 2, 4, 6].binarySearch(1), 1)
16 | XCTAssertEqual([0, 2, 4, 6].binarySearch(2), 1)
17 | XCTAssertEqual([0, 2, 4, 6].binarySearch(3), 2)
18 | XCTAssertEqual([0, 2, 4, 6].binarySearch(4), 2)
19 | XCTAssertEqual([0, 2, 4, 6].binarySearch(5), 3)
20 | XCTAssertEqual([0, 2, 4, 6].binarySearch(6), 3)
21 | XCTAssertEqual([0, 2, 4, 6].binarySearch(7), 4)
22 | XCTAssertEqual([0, 2, 4, 6].binarySearch(8), 4)
23 | }
24 |
25 | func testInsertSorted() {
26 |
27 | func insert(_ v: Int, _ vs: [Int], _ expected: [Int], _ expectedIndex: Int) {
28 | var mutvs = vs
29 | let index = mutvs.insertSorted(v, { $0 }, <)
30 | XCTAssertEqual(mutvs, expected)
31 | XCTAssertEqual(index, expectedIndex)
32 | }
33 |
34 | insert(42, [], [42], 0)
35 | insert(-1, [0, 2, 4, 6], [-1, 0, 2, 4, 6], 0)
36 | insert( 0, [0, 2, 4, 6], [0, 0, 2, 4, 6], 0)
37 | insert( 1, [0, 2, 4, 6], [0, 1, 2, 4, 6], 1)
38 | insert( 2, [0, 2, 4, 6], [0, 2, 2, 4, 6], 1)
39 | insert( 3, [0, 2, 4, 6], [0, 2, 3, 4, 6], 2)
40 | insert( 4, [0, 2, 4, 6], [0, 2, 4, 4, 6], 2)
41 | insert( 5, [0, 2, 4, 6], [0, 2, 4, 5, 6], 3)
42 | insert( 6, [0, 2, 4, 6], [0, 2, 4, 6, 6], 3)
43 | insert( 7, [0, 2, 4, 6], [0, 2, 4, 6, 7], 4)
44 | insert( 8, [0, 2, 4, 6], [0, 2, 4, 6, 8], 4)
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/Legacy/PLRelationalBinding/Tests/ChangeHandlerTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import XCTest
7 | @testable import PLRelationalBinding
8 |
9 | class ChangeHandlerTests: BindingTestCase {
10 |
11 | func testBasic() {
12 | var lockCount = 0
13 | var unlockCount = 0
14 |
15 | let handler = ChangeHandler(onLock: { lockCount += 1 }, onUnlock: { unlockCount += 1 })
16 | XCTAssertEqual(lockCount, 0)
17 | XCTAssertEqual(unlockCount, 0)
18 |
19 | handler.willChange()
20 | XCTAssertEqual(lockCount, 1)
21 | XCTAssertEqual(unlockCount, 0)
22 |
23 | handler.willChange()
24 | XCTAssertEqual(lockCount, 1)
25 | XCTAssertEqual(unlockCount, 0)
26 |
27 | handler.didChange()
28 | XCTAssertEqual(lockCount, 1)
29 | XCTAssertEqual(unlockCount, 0)
30 |
31 | handler.didChange()
32 | XCTAssertEqual(lockCount, 1)
33 | XCTAssertEqual(unlockCount, 1)
34 |
35 | handler.incrementCount(3)
36 | XCTAssertEqual(lockCount, 2)
37 | XCTAssertEqual(unlockCount, 1)
38 |
39 | handler.didChange()
40 | XCTAssertEqual(lockCount, 2)
41 | XCTAssertEqual(unlockCount, 1)
42 |
43 | handler.decrementCount(2)
44 | XCTAssertEqual(lockCount, 2)
45 | XCTAssertEqual(unlockCount, 2)
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Legacy/PLRelationalBinding/Tests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Legacy/PLRelationalLegacy.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Legacy/PLRelationalLegacy.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Legacy/PLRelationalLegacy.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/PLRelational/Modules/module-sqlite3-include.h:
--------------------------------------------------------------------------------
1 |
2 | // modulemaps don't automatically pull in SDK-relative system headers.
3 | // But if we indirect through a header file, they do!
4 | // So have the modulemap include this, which includes the system header.
5 | #include
6 |
7 | // We also need SQLite's tokenizer declarations, which the standard header does not include.
8 | #include "SQLiteTokenizerAPI.h"
9 |
--------------------------------------------------------------------------------
/PLRelational/Modules/module.modulemap:
--------------------------------------------------------------------------------
1 | module sqlite3 [system] {
2 | header "module-sqlite3-include.h"
3 | link "sqlite3"
4 | export *
5 | }
6 |
--------------------------------------------------------------------------------
/PLRelational/PLRelational.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/PLRelational/PLRelational.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/PLRelational/PLRelational.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/PLRelational/Sources/ConsistentPRNG.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Darwin
7 |
8 | /// :nodoc: Implementation detail (will be made non-public eventually)
9 | /// A PRNG that produces consistent output on each run. Obviously, don't use this for anything
10 | /// where randomness is relevant to security. Intended for pseudorandom test cases that can be
11 | /// replicated on each run.
12 | public struct ConsistentPRNG {
13 | private var state: [UInt16] = Array(repeating: 0, count: 3)
14 |
15 | public mutating func next() -> Int {
16 | return nrand48(&state)
17 | }
18 |
19 | public mutating func next(_ limit: Int) -> Int {
20 | return next() % limit
21 | }
22 |
23 | var max: Int {
24 | return 0x7fffffff
25 | }
26 | }
27 |
28 | private let nrand48: @convention(c) (UnsafeMutablePointer) -> Int = loadFunc("nrand48")
29 |
30 | private func loadFunc(_ name: String) -> T {
31 | let RTLD_DEFAULT = UnsafeMutableRawPointer(bitPattern: -2)
32 | let address = dlsym(RTLD_DEFAULT, name)
33 | return unsafeBitCast(address, to: T.self)
34 | }
35 |
--------------------------------------------------------------------------------
/PLRelational/Sources/Crypto.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import CommonCrypto
7 |
8 | /// :nodoc: Implementation detail (will be made non-public eventually)
9 | public func SHA256(_ data: [UInt8]) -> [UInt8] {
10 | var output: [UInt8] = Array(repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
11 | CC_SHA256(data, CC_LONG(data.count), &output)
12 | return output
13 | }
14 |
15 | /// :nodoc: Implementation detail (will be made non-public eventually)
16 | public func hexString(_ data: [UInt8], uppercase: Bool) -> String {
17 | struct Static {
18 | static let hexCharsUpper: [Character] = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"]
19 | static let hexCharsLower: [Character] = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"]
20 | }
21 |
22 | let chars = uppercase ? Static.hexCharsUpper : Static.hexCharsLower
23 |
24 | var output = String()
25 | output.reserveCapacity(data.count * 2)
26 |
27 | for byte in data {
28 | let upperNibble = byte >> 4
29 | let lowerNibble = byte & 0xf
30 |
31 | for nibble in [upperNibble, lowerNibble] {
32 | output.append(chars[Int(nibble)])
33 | }
34 | }
35 |
36 | return output
37 | }
38 |
--------------------------------------------------------------------------------
/PLRelational/Sources/DJBHash.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | /// :nodoc: Implementation detail (will be made non-public eventually)
7 | /// An implementation of the DJB hash function, adapted from http://stackoverflow.com/questions/31438210/how-to-implement-the-hashable-protocol-in-swift-for-an-int-array-a-custom-strin
8 | public struct DJBHash {
9 | public private(set) var value: Int = 5381
10 |
11 | public mutating func combine(_ new: Int) {
12 | value = (value << 5) &+ value &+ new
13 | }
14 |
15 | public init() {}
16 |
17 | public static func hash(values: S) -> Int where S.Iterator.Element == Int {
18 | var hash = DJBHash()
19 | for value in values {
20 | hash.combine(value)
21 | }
22 | return hash.value
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/PLRelational/Sources/DataCodec.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 |
8 |
9 | public protocol DataCodec {
10 | func encode(_ data: Data) -> Result
11 | func decode(_ data: Data) -> Result
12 | }
13 |
--------------------------------------------------------------------------------
/PLRelational/Sources/DeallocObservation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 |
8 | /// :nodoc: Implementation detail (will be made non-public eventually)
9 | /// Observe the deallocation of an object.
10 | ///
11 | /// - parameter target: The object to observe.
12 | /// - parameter f: The function to call when `target` is deallocated.
13 | /// - returns: A removal function. Call this to remove the observation before `target` has been deallocated.
14 | public func ObserveDeallocation(_ target: AnyObject, _ f: @escaping () -> Void) -> (() -> Void) {
15 | return mutex.locked({
16 | let callOnDeinit: CallOnDeinit
17 | if let obj = objc_getAssociatedObject(target, key!) as? CallOnDeinit {
18 | callOnDeinit = obj
19 | } else {
20 | callOnDeinit = CallOnDeinit()
21 | objc_setAssociatedObject(target, key!, callOnDeinit, .OBJC_ASSOCIATION_RETAIN)
22 | }
23 |
24 | let counter = callOnDeinit.counter
25 | callOnDeinit.counter += 1
26 |
27 | callOnDeinit.calls[counter] = f
28 |
29 | return { [weak callOnDeinit] in
30 | mutex.locked({
31 | _ = callOnDeinit?.calls.removeValue(forKey: counter)
32 | })
33 | }
34 | })
35 | }
36 |
37 | private class CallOnDeinit {
38 | var counter: UInt64 = 0
39 | var calls: [UInt64: () -> Void] = [:]
40 |
41 | deinit {
42 | for (_, call) in calls {
43 | call()
44 | }
45 | }
46 | }
47 |
48 | private let mutex = Mutex()
49 | private let key = malloc(1)
50 |
--------------------------------------------------------------------------------
/PLRelational/Sources/Debug.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 |
8 |
9 | /// :nodoc: Implementation detail (will be made non-public eventually)
10 | public func debugLog(_ items: Any..., file: String = #file, line: Int = #line) {
11 | let strings = items.map({ String(describing: $0) })
12 | let fullString = strings.joined(separator: " ")
13 | let filename = URL(fileURLWithPath: file).lastPathComponent
14 | NSLog("%@:%ld: %@", filename, line, fullString)
15 | }
16 |
17 | /// :nodoc: Implementation detail (will be made non-public eventually)
18 | public func pointerString(_ obj: AnyObject) -> String {
19 | return String(format: "%p", unsafeBitCast(obj, to: Int.self))
20 | }
21 |
22 | /// :nodoc: Implementation detail (will be made non-public eventually)
23 | public func pointerAndClassString(_ obj: AnyObject) -> String {
24 | return "<\(type(of: obj)): \(pointerString(obj))>"
25 | }
26 |
--------------------------------------------------------------------------------
/PLRelational/Sources/DictionaryConvenience.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | /// :nodoc: Implementation detail (will be made non-public eventually)
7 | extension Dictionary where Value: Hashable {
8 | public var inverted: [Value: Key] {
9 | return Dictionary(self.map({ ($1, $0) }))
10 | }
11 | }
12 |
13 | /// :nodoc: Implementation detail (will be made non-public eventually)
14 | extension Dictionary {
15 | public mutating func getOrCreate(_ key: Key, defaultValue: @autoclosure () -> Value) -> Value {
16 | if let value = self[key] {
17 | return value
18 | } else {
19 | let new = defaultValue()
20 | self[key] = new
21 | return new
22 | }
23 | }
24 |
25 | public subscript(key: Key, defaultValue defaultValue: @autoclosure () -> Value) -> Value {
26 | mutating get {
27 | return getOrCreate(key, defaultValue: defaultValue())
28 | }
29 | set {
30 | self[key] = newValue
31 | }
32 | }
33 | }
34 |
35 | /// :nodoc: Implementation detail (will be made non-public eventually)
36 | /// Combine a dictionary and some collection of key/value pairs, which may be a second dictionary.
37 | /// Any keys that exist in both will have the value from the second parameter in the result.
38 | public func +(a: [K: V], b: Seq) -> [K: V] where Seq.Iterator.Element == (K, V) {
39 | var result = a
40 | for (k, v) in b {
41 | result[k] = v
42 | }
43 | return result
44 | }
45 |
46 | /// :nodoc: Implementation detail (will be made non-public eventually)
47 | extension Dictionary {
48 | /// Initialize a dictionary with an array of key/value pairs.
49 | public init(_ pairs: [(Key, Value)]) {
50 | self.init(minimumCapacity: pairs.count)
51 |
52 | for (k, v) in pairs {
53 | self[k] = v
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/PLRelational/Sources/DictionaryCreation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | /// :nodoc: Implementation detail (will be made non-public eventually)
7 | public extension Dictionary {
8 | init(_ seq: S) where S.Iterator.Element == (Key, Value) {
9 | self.init()
10 | for (k, v) in seq {
11 | self[k] = v
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/PLRelational/Sources/ErrorConvenience.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 |
8 | extension Error {
9 | var isFileNotFound: Bool {
10 | // NSData throws NSFileReadNoSuchFileError when the file doesn't exist. It doesn't seem to be documented
11 | // but given that it's an official Cocoa constant it seems safe enough.
12 | let codes = [NSFileNoSuchFileError, NSFileReadNoSuchFileError]
13 | return (self as NSError).domain == NSCocoaErrorDomain && codes.contains((self as NSError).code)
14 | }
15 | }
16 |
17 | extension NSError {
18 | static var fileNotFound: NSError {
19 | return NSError(domain: NSCocoaErrorDomain, code: NSFileReadNoSuchFileError, userInfo: nil)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/PLRelational/Sources/GeneratorConcat.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | /// :nodoc: Implementation detail (will be made non-public eventually)
7 | extension IteratorProtocol {
8 | public func concat(_ other: OtherGen) -> ConcatGenerator {
9 | return ConcatGenerator(self, other)
10 | }
11 | }
12 |
13 | /// :nodoc: Implementation detail (will be made non-public eventually)
14 | /// A GeneratorType which concatenates two other generators. It will produce all elements from
15 | /// the first generator, then all elements from the second.
16 | public struct ConcatGenerator: IteratorProtocol where G1.Element == G2.Element {
17 | var g1: G1?
18 | var g2: G2?
19 |
20 | public init(_ g1: G1, _ g2: G2) {
21 | self.g1 = g1
22 | self.g2 = g2
23 | }
24 |
25 | public mutating func next() -> G1.Element? {
26 | if g1 != nil {
27 | if let element = g1?.next() {
28 | return element
29 | } else {
30 | g1 = nil
31 | }
32 | }
33 | // NOTE: not else if, because we want to fall through after the g1 = nil above.
34 | if g2 != nil {
35 | if let element = g2?.next() {
36 | return element
37 | } else {
38 | g2 = nil
39 | }
40 | }
41 |
42 | return nil
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/PLRelational/Sources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/PLRelational/Sources/MemoryTableDatabase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 |
8 | /// A minimal implementation of the `StoredDatabase` protocol that allows `MemoryTableRelation` to be used
9 | /// with `TransactionalDatabase`.
10 | public class MemoryTableDatabase: StoredDatabase {
11 |
12 | private var relations: Mutexed<[String: MemoryTableRelation]>
13 |
14 | public init(relations: [String: MemoryTableRelation] = [:]) {
15 | relations.forEach({ _ = $1.setDebugName($0) })
16 | self.relations = Mutexed(relations)
17 | }
18 |
19 | public subscript(name: String) -> StoredRelation? {
20 | return storedRelation(forName: name)
21 | }
22 |
23 | public func storedRelation(forName name: String) -> StoredRelation? {
24 | return relations.withValue({ $0[name] })
25 | }
26 |
27 | public func createRelation(_ name: String, scheme: Scheme) -> MemoryTableRelation {
28 | let relation = MemoryTableRelation(scheme: scheme)
29 | _ = relation.setDebugName(name)
30 | relations.withMutableValue({ $0[name] = relation })
31 | return relation
32 | }
33 |
34 | public func transaction(_ transactionFunction: () -> (Return, TransactionResult)) -> Result {
35 | return .Ok(transactionFunction().0)
36 | }
37 |
38 | public func resultNeedsRetry(_ result: Result) -> Bool {
39 | return false
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/PLRelational/Sources/MirrorChildren.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | extension Mirror {
7 | var mirrorsIncludingSupertypes: [Mirror] {
8 | var result = [self]
9 | while let next = result.last?.superclassMirror {
10 | result.append(next)
11 | }
12 | return result
13 | }
14 |
15 | var childrenIncludingSupertypes: Children {
16 | let all = mirrorsIncludingSupertypes.flatMap({ $0.children })
17 | return AnyCollection(all)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/PLRelational/Sources/MutableBox.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 |
7 | /// A really simple generic class that boxes a mutable value.
8 | /// The intent is to allow for in-place mutations of value-typed collections in situations
9 | /// where Swift isn't smart enough to figure out how otherwise.
10 | class MutableBox {
11 | var value: T
12 |
13 | init(_ initialValue: T) {
14 | value = initialValue
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/PLRelational/Sources/MutableRelation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | public protocol MutableRelation: class, Relation {
7 | func add(_ row: Row) -> Result
8 | func delete(_ query: SelectExpression) -> Result
9 | }
10 |
11 | public protocol MutableSelectRelation: class, Relation {
12 | var selectExpression: SelectExpression { get set }
13 | }
14 |
--------------------------------------------------------------------------------
/PLRelational/Sources/Mutex.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 |
7 | import Foundation
8 |
9 | /// :nodoc: Implementation detail (will be made non-public eventually)
10 | /// A simple mutex with a Swifty API. Note: despite being a struct, it acts like a reference type.
11 | public struct Mutex {
12 | fileprivate let lock = NSLock()
13 |
14 | /// Call the given function with the lock locked, automatically unlocking before returning or throwing.
15 | public func locked(_ f: () throws -> T) rethrows -> T {
16 | lock.lock()
17 | defer { lock.unlock() }
18 | return try f()
19 | }
20 | }
21 |
22 | /// :nodoc: Implementation detail (will be made non-public eventually)
23 | /// A wrapper that holds a value and a mutex, and allows accessing that value with the mutex held.
24 | /// When the wrapped type is a reference type, this will act like a reference type. When the
25 | /// wrapped type is a value type, this will act weirdly, with the mutex being shared among copies
26 | /// but the value being separate in each copy. This struct really shouldn't be copied, just have one.
27 | public struct Mutexed {
28 | fileprivate let mutex = Mutex()
29 |
30 | fileprivate var value: T
31 |
32 | public init(_ value: T) {
33 | self.value = value
34 | }
35 |
36 | /// Call the given function with the lock locked, passing in the value so it can be used while locked.
37 | public func withValue(_ f: (T) throws -> Result) rethrows -> Result {
38 | return try mutex.locked({
39 | return try f(value)
40 | })
41 | }
42 |
43 | /// Call the given function with the lock locked, passing in the value as inout so it can be used or
44 | /// mutated while locked.
45 | public mutating func withMutableValue(_ f: (inout T) throws -> Result) rethrows -> Result {
46 | return try mutex.locked({
47 | return try f(&value)
48 | })
49 | }
50 |
51 | /// Fetch the wrapped value. Obviously, only use this when it's safe to use (but not necessarily fetch)
52 | /// the value without locking, like with value types.
53 | public func get() -> T {
54 | return withValue({ $0 })
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/PLRelational/Sources/NSURLConvenience.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 |
8 |
9 | /// :nodoc: Implementation detail (will be made non-public eventually)
10 | extension URL {
11 | public var isDirectory: Result {
12 | do {
13 | let values = try resourceValues(forKeys: [.isDirectoryKey])
14 | return .Ok(values.isDirectory ?? false)
15 | } catch let error as NSError {
16 | return .Err(error)
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/PLRelational/Sources/NegativeSet.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | /// :nodoc: Implementation detail (will be made non-public eventually)
7 | /// A Set which tracks objects removed from an empty set, conceptually as an object
8 | /// with a count of -1. Removing an object and then adding it results in the set
9 | /// not containing that object. It only supports added and removed, not arbitrary
10 | /// counts.
11 | public struct NegativeSet {
12 | /// The values which have been added to the set.
13 | public fileprivate(set) var added: Set = []
14 |
15 | /// The values which have been removed from the set.
16 | public fileprivate(set) var removed: Set = []
17 |
18 | /// Create a new set.
19 | public init() {}
20 |
21 | /// Add all elements in `set` to this set.
22 | public mutating func unionInPlace(_ set: Set) {
23 | let new = set.fastSubtracting(removed)
24 | added.formUnion(new)
25 | removed.fastSubtract(set)
26 | }
27 |
28 | /// Remove all elements in `set` from this set.
29 | public mutating func subtractInPlace(_ set: Set) {
30 | let gone = set.fastSubtracting(added)
31 | removed.formUnion(gone)
32 | added.fastSubtract(set)
33 | }
34 |
35 | /// Clear this set by resetting `added` and `removed` to empty sets.
36 | public mutating func removeAll() {
37 | added.removeAll()
38 | removed.removeAll()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/PLRelational/Sources/ObjectCasting.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 |
7 | func asObject(_ value: Any) -> AnyObject? {
8 | if type(of: value) is AnyObject.Type {
9 | return value as AnyObject
10 | } else {
11 | return nil
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/PLRelational/Sources/ObjectDictionary.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | /// Like Dictionary, but the keys use object identity rather than value equality.
7 | struct ObjectDictionary: Sequence, ExpressibleByDictionaryLiteral {
8 | fileprivate var dict: Dictionary, Value>
9 |
10 | init(_ seq: S) where S.Iterator.Element == (Key, Value) {
11 | dict = Dictionary(seq.map({ (ObjectSetWrapper(object: $0), $1) }))
12 | }
13 |
14 | init(dictionaryLiteral elements: (Key, Value)...) {
15 | self.init(elements)
16 | }
17 |
18 | func makeIterator() -> AnyIterator<(Key, Value)> {
19 | let gen = dict.lazy.map({ ($0.object, $1) }).makeIterator()
20 | return AnyIterator(gen)
21 | }
22 |
23 | subscript(key: Key) -> Value? {
24 | get {
25 | return dict[ObjectSetWrapper(object: key)]
26 | }
27 | set {
28 | dict[ObjectSetWrapper(object: key)] = newValue
29 | }
30 | }
31 |
32 | subscript(key: Key, defaultValue defaultValue: @autoclosure () -> Value) -> Value {
33 | mutating get {
34 | return getOrCreate(key, defaultValue: defaultValue())
35 | }
36 | set {
37 | self[key] = newValue
38 | }
39 | }
40 |
41 | mutating func getOrCreate(_ key: Key, defaultValue: @autoclosure () -> Value) -> Value {
42 | if let value = self[key] {
43 | return value
44 | } else {
45 | let new = defaultValue()
46 | self[key] = new
47 | return new
48 | }
49 | }
50 |
51 | var keys: [Key] {
52 | return dict.keys.map({ $0.object })
53 | }
54 |
55 | var isEmpty: Bool {
56 | return dict.isEmpty
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/PLRelational/Sources/ObjectSet.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | /// :nodoc: Implementation detail (will be made non-public eventually)
7 | /// Like a Set, but based on object identity rather than value equality.
8 | public struct ObjectSet: Sequence {
9 | fileprivate var set: Set>
10 |
11 | public init(_ elements: [T]) {
12 | set = Set(elements.map(ObjectSetWrapper.init))
13 | }
14 |
15 | public func makeIterator() -> AnyIterator {
16 | let gen = set.lazy.map({ $0.object }).makeIterator()
17 | return AnyIterator(gen)
18 | }
19 |
20 | public var isEmpty: Bool {
21 | return set.isEmpty
22 | }
23 |
24 | public mutating func insert(_ obj: T) {
25 | set.insert(ObjectSetWrapper(object: obj))
26 | }
27 |
28 | public mutating func remove(_ obj: T) {
29 | set.remove(ObjectSetWrapper(object: obj))
30 | }
31 |
32 | public func contains(_ obj: T) -> Bool {
33 | return set.contains(ObjectSetWrapper(object: obj))
34 | }
35 |
36 | public var any: T? {
37 | return set.first?.object
38 | }
39 |
40 | public mutating func removeFirst() -> T {
41 | return set.removeFirst().object
42 | }
43 | }
44 |
45 | struct ObjectSetWrapper: Hashable {
46 | var object: T
47 |
48 | func hash(into hasher: inout Hasher) {
49 | hasher.combine(ObjectIdentifier(object))
50 | }
51 | }
52 |
53 | func ==(a: ObjectSetWrapper, b: ObjectSetWrapper) -> Bool {
54 | return a.object === b.object
55 | }
56 |
57 | /// :nodoc: Implementation detail (will be made non-public eventually)
58 | extension ObjectSet: ExpressibleByArrayLiteral {
59 | public init(arrayLiteral elements: T...) {
60 | self.init(elements)
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/PLRelational/Sources/OptionalFlatten.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 |
7 | /// :nodoc: Implementation detail (will be made non-public eventually)
8 | /// Collapse a double optional into a single optional, transforming .some(nil) into nil.
9 | public func flatten(_ doubleOptional: T??) -> T? {
10 | switch doubleOptional {
11 | case .none, .some(.none): return nil
12 | case .some(.some(let wrapped)): return wrapped
13 | }
14 | }
15 |
16 | /// :nodoc: Implementation detail (will be made non-public eventually)
17 | /// Collapse two optionals into an optional tuple with non-optional components.
18 | /// If either parameter is nil, the return value is nil.
19 | public func flatten(_ t: T?, _ u: U?) -> (T, U)? {
20 | if let t = t, let u = u {
21 | return (t, u)
22 | } else {
23 | return nil
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/PLRelational/Sources/PerThreadInstance.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 |
8 |
9 | /// :nodoc: Implementation detail (will be made non-public eventually)
10 | public protocol PerThreadInstance: class {
11 | static var currentInstance: Self { get }
12 |
13 | init()
14 | }
15 |
16 | /// :nodoc: Implementation detail (will be made non-public eventually)
17 | extension PerThreadInstance {
18 | public static var currentInstance: Self {
19 | let key = NSValue(nonretainedObject: self)
20 | if let instance = Thread.current.threadDictionary[key] as? Self {
21 | return instance
22 | } else {
23 | let instance = self.init()
24 | Thread.current.threadDictionary[key] = instance
25 | return instance
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/PLRelational/Sources/Promise.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 |
8 | /// :nodoc: Implementation detail (will be made non-public eventually)
9 | /// A simple implementation of a promise. You can set a value and wait for a value to be set.
10 | /// Thread safe, obviously.
11 | public class Promise {
12 | private let condition = NSCondition()
13 | private var value: T? = nil
14 |
15 | public init() {}
16 |
17 | public func fulfill(_ value: T) {
18 | condition.lock()
19 | precondition(self.value == nil)
20 | self.value = value
21 | condition.broadcast()
22 | condition.unlock()
23 | }
24 |
25 | public func get() -> T {
26 | condition.lock()
27 | while true {
28 | if let value = self.value {
29 | condition.unlock()
30 | return value
31 | } else {
32 | condition.wait()
33 | }
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/PLRelational/Sources/RWLock.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Darwin
7 |
8 |
9 | /// A reader-writer lock. Wraps pthread_rwlock.
10 | class RWLock {
11 | var lock: UnsafeMutablePointer
12 |
13 | init() {
14 | lock = UnsafeMutablePointer.allocate(capacity: 1)
15 | let err = pthread_rwlock_init(lock, nil)
16 | if err != 0 {
17 | fatalError("pthread_rwlock_init returned error \(err): \(String(cString: strerror(err)) )")
18 | }
19 | }
20 |
21 | deinit {
22 | let err = pthread_rwlock_destroy(lock)
23 | if err != 0 {
24 | fatalError("pthread_rwlock_destroy returned error \(err): \(String(cString: strerror(err)) )")
25 | }
26 | lock.deallocate()
27 | }
28 |
29 | func readLock() {
30 | pthread_rwlock_rdlock(lock)
31 | }
32 |
33 | func writeLock() {
34 | pthread_rwlock_wrlock(lock)
35 | }
36 |
37 | func unlock() {
38 | pthread_rwlock_unlock(lock)
39 | }
40 |
41 | func read(_ f: () -> T) -> T {
42 | readLock()
43 | defer { unlock() }
44 | return f()
45 | }
46 |
47 | func write(_ f: () -> T) -> T {
48 | writeLock()
49 | defer { unlock() }
50 | return f()
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/PLRelational/Sources/RelationChange.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | public struct RelationChange {
7 | public let added: Relation?
8 | public let removed: Relation?
9 |
10 | public init(added: Relation?, removed: Relation?) {
11 | self.added = added
12 | self.removed = removed
13 | }
14 | }
15 |
16 | extension RelationChange {
17 | public func copy() -> Result {
18 | let copiedAdded = added.map(ConcreteRelation.copyRelation)
19 | let copiedRemoved = removed.map(ConcreteRelation.copyRelation)
20 |
21 | return
22 | hoistOptional(copiedAdded)
23 | .combine(hoistOptional(copiedRemoved))
24 | .map({ RelationChange(added: $0, removed: $1) })
25 | }
26 | }
27 |
28 | extension RelationChange: CustomStringConvertible {
29 | public var description: String {
30 | func stringForRelation(_ r: Relation?) -> String {
31 | guard let r = r else { return "∅" }
32 | if r.isEmpty.ok ?? false { return "∅" }
33 | return "\n\(r.description)\n"
34 | }
35 | return "RelationChange added: \(stringForRelation(added)) removed: \(stringForRelation(removed))"
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/PLRelational/Sources/RelationObserver.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | /// :nodoc: Implementation detail (will be made non-public eventually); also related to synchronous APIs, which are de-emphasized
7 | public protocol RelationObserver {
8 | func transactionBegan()
9 | func relationChanged(_ relation: Relation, change: RelationChange)
10 | func transactionEnded()
11 | }
12 |
13 | class WeakRelationObserverProxy: RelationObserver {
14 | fileprivate weak var target: (RelationObserver & AnyObject)?
15 | fileprivate var targetRemoval: () -> Void = { fatalError("Proxy deallocated, but target removal function never set.") }
16 | fileprivate var relationRemoval: () -> Void = { fatalError("Observer method called, but relation removal function never set.") }
17 |
18 | init(target: RelationObserver & AnyObject) {
19 | self.target = target
20 | targetRemoval = ObserveDeallocation(target, { [weak self] in
21 | self?.relationRemoval()
22 | })
23 | }
24 |
25 | func registerOn(_ observee: Relation, kinds: [RelationObservationKind]) {
26 | relationRemoval = observee.addChangeObserver(self, kinds: kinds)
27 | }
28 |
29 | func transactionBegan() {
30 | getTargetOrRemove()?.transactionBegan()
31 | }
32 |
33 | func relationChanged(_ relation: Relation, change: RelationChange) {
34 | getTargetOrRemove()?.relationChanged(relation, change: change)
35 | }
36 |
37 | func transactionEnded() {
38 | getTargetOrRemove()?.transactionEnded()
39 | }
40 |
41 | fileprivate func getTargetOrRemove() -> RelationObserver? {
42 | if let target = target {
43 | return target
44 | } else {
45 | relationRemoval()
46 | return nil
47 | }
48 | }
49 | }
50 |
51 | struct SimpleRelationObserverProxy: RelationObserver {
52 | var f: (RelationChange) -> Void
53 | func transactionBegan() {}
54 | func relationChanged(_ relation: Relation, change: RelationChange) {
55 | f(change)
56 | }
57 | func transactionEnded() {}
58 | }
59 |
--------------------------------------------------------------------------------
/PLRelational/Sources/RelationOperators.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | infix operator ∩ : AdditionPrecedence
7 |
8 | /// Union two possibly-nil relations together. The result is nil if both inputs are nil.
9 | /// We use + instead of ∪ because it's easier to type and can still be understood.
10 | public func +(lhs: Relation?, rhs: Relation?) -> Relation? {
11 | switch (lhs, rhs) {
12 | case let (.some(lhs), .some(rhs)):
13 | return lhs.union(rhs)
14 | case let (.some(lhs), .none):
15 | return lhs
16 | case let (.none, .some(rhs)):
17 | return rhs
18 | case (.none, .none):
19 | return nil
20 | }
21 | }
22 |
23 | /// Subtract two possibly-nil relations. The result is nil if the lhs is nil, the lhs
24 | /// is returned if the rhs is nil, and the difference is returned if both exist.
25 | public func -(lhs: Relation?, rhs: Relation?) -> Relation? {
26 | if let lhs = lhs, let rhs = rhs {
27 | return lhs.difference(rhs)
28 | } else if let lhs = lhs {
29 | return lhs
30 | } else {
31 | return nil
32 | }
33 | }
34 |
35 | /// Intersect two possibly-nil relations. The result is nil if either operand is nil.
36 | /// I couldn't come up with a nice ASCII version of this operator, so we get the real
37 | /// untypeable thing. Use copy/paste or the character viewer.
38 | public func ∩(lhs: Relation?, rhs: Relation?) -> Relation? {
39 | if let lhs = lhs, let rhs = rhs {
40 | return lhs.intersection(rhs)
41 | } else {
42 | return nil
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/PLRelational/Sources/RemovableSet.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | /// :nodoc: Implementation detail (will be made non-public eventually)
7 | /// A set of arbitrary objects which can be iterated and can later be removed.
8 | /// Ideal for observer callbacks.
9 | public class RemovableSet: Sequence {
10 | private var nextNumber: UInt64 = 0
11 | private var contents: [UInt64: T] = [:]
12 |
13 | public init() {}
14 |
15 | /// Add a value to the set. Returns a remover which, when called, removes the value from the set.
16 | public func add(_ value: T) -> RemovableSetRemover {
17 | let number = nextNumber
18 | nextNumber += 1
19 |
20 | contents[number] = value
21 | return {
22 | self.contents.removeValue(forKey: number)
23 | }
24 | }
25 |
26 | public func makeIterator() -> Dictionary.Values.Iterator {
27 | return contents.values.makeIterator()
28 | }
29 | }
30 |
31 | /// :nodoc: Implementation detail (will be made non-public eventually)
32 | /// A set of observer functions which take some parameter and return Void.
33 | /// The same as RemovableSet aside from constraining the content type and
34 | /// providing a notify method to make it easier to call the observers.
35 | public class RemovableFunctionSet: RemovableSet<(Params) -> Void> {
36 | public func notify(_ params: Params) {
37 | for f in self {
38 | f(params)
39 | }
40 | }
41 | }
42 |
43 | public typealias RemovableSetRemover = () -> Void
44 |
--------------------------------------------------------------------------------
/PLRelational/Sources/SelectExpressionAnalysis.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | extension SelectExpression {
7 | /// Perform some basic simplifications of the expression. This short-circuits logical operators
8 | /// with a constant on one side and immediately evaluates operators with constant operands.
9 | func shallowSimplify() -> SelectExpression {
10 | switch self {
11 | case let op as SelectExpressionUnaryOperator:
12 | if let expr = op.expr as? Bool {
13 | return !expr
14 | }
15 | return self
16 |
17 | case let op as SelectExpressionBinaryOperator:
18 | switch (op.op, op.lhs, op.rhs) {
19 | case let (op, lhs as SelectExpressionConstantValue, rhs as SelectExpressionConstantValue):
20 | return op.evaluate(lhs.relationValue, rhs.relationValue)
21 |
22 | case let (_ as AndComparator, true, rhs):
23 | return rhs
24 | case (_ as AndComparator, false, _):
25 | return false
26 | case let (_ as AndComparator, lhs, true):
27 | return lhs
28 | case (_ as AndComparator, _, false):
29 | return false
30 |
31 | case let (_ as OrComparator, false, rhs):
32 | return rhs
33 | case (_ as OrComparator, true, _):
34 | return true
35 | case let (_ as OrComparator, lhs, false):
36 | return lhs
37 | case (_ as OrComparator, _, true):
38 | return true
39 |
40 | default:
41 | return self
42 | }
43 |
44 | default:
45 | return self
46 | }
47 | }
48 |
49 | /// Walk the entire expression, calling shallowSimplify as we go to simplify as much as possible.
50 | func deepSimplify() -> SelectExpression {
51 | return mapTree({ $0.shallowSimplify() })
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/PLRelational/Sources/SequenceFind.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | extension Sequence {
7 | func find(_ predicate: (Iterator.Element) -> Bool) -> Iterator.Element? {
8 | for element in self {
9 | if predicate(element) {
10 | return element
11 | }
12 | }
13 | return nil
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/PLRelational/Sources/SetPowerSet.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | extension Set {
7 | var powerSet: Set> {
8 | if count == 0 { return [[]] }
9 | let first = self.first!
10 | let rest = self.subtracting([first])
11 | let restPowerSet = rest.powerSet
12 | return restPowerSet.union(restPowerSet.map({ $0.union([first]) }))
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/PLRelational/Sources/SetSubtraction.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | /// :nodoc: Implementation detail (will be made non-public eventually)
7 | extension Set {
8 | /// As of Xcode 8.3/Swift 3.1, the implementation of Set's `subtract` is kind of dumb:
9 | ///
10 | /// for item in other {
11 | /// remove(item)
12 | /// }
13 | ///
14 | /// That means that subtracting a large set from a small set is unnecessarily slow.
15 | /// This reimplements subtraction doing the same thing, but iterating over the smaller
16 | /// of the two sets.
17 | public mutating func fastSubtract(_ other: Set) {
18 | if other.count > self.count {
19 | self = Set(self.lazy.filter({ !other.contains($0) }))
20 | } else {
21 | for item in other {
22 | self.remove(item)
23 | }
24 | }
25 | }
26 |
27 | /// A non-mutating wrapper around fastSubtract. Just creates a mutable copy and mutates it
28 | /// using fastSubtract.
29 | public func fastSubtracting(_ other: Set) -> Set {
30 | var result = self
31 | result.fastSubtract(other)
32 | return result
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/PLRelational/Sources/StoredDatabase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 |
8 | public enum TransactionResult {
9 | case commit
10 | case rollback
11 | case retry
12 | }
13 |
14 | public protocol StoredDatabase {
15 | func storedRelation(forName name: String) -> StoredRelation?
16 |
17 | func transaction(_ transactionFunction: () -> (Return, TransactionResult)) -> Result
18 | func resultNeedsRetry(_ result: Result) -> Bool
19 | }
20 |
--------------------------------------------------------------------------------
/PLRelational/Sources/StoredRelation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 |
8 | public protocol StoredRelation: MutableRelation {
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/PLRelational/Sources/StringConvenience.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 |
8 | extension String {
9 | func numericLessThan(_ other: String) -> Bool {
10 | return compare(other, options: .numeric, range: nil, locale: nil) == .orderedAscending
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/PLRelational/Sources/StringPad.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | extension String {
7 | func pad(to length: Int, with character: Character, after: Bool = true) -> String {
8 | var new = self
9 | while new.count < length {
10 | new.insert(character, at: after ? new.endIndex : new.startIndex)
11 | }
12 | return new
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/PLRelational/Sources/UnaryOperator.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | /// :nodoc: Elided from docs to reduce clutter for now; part of "official" API but may be reworked in the near future
7 | public protocol UnaryOperator {
8 | func evaluate(_ value: RelationValue) -> RelationValue
9 | }
10 |
11 | /// :nodoc: Elided from docs to reduce clutter for now; part of "official" API but may be reworked in the near future
12 | public struct NotOperator: UnaryOperator {
13 | public init() {}
14 |
15 | public func evaluate(_ value: RelationValue) -> RelationValue {
16 | return .integer(value.boolValue ? 0 : 1)
17 | }
18 | }
19 |
20 | /// :nodoc: Elided from docs to reduce clutter for now; part of "official" API but may be reworked in the near future
21 | extension NotOperator: CustomStringConvertible {
22 | public var description: String {
23 | return "!"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/PLRelational/Sources/UnsafePointerReadWrite.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 |
7 | import Darwin
8 |
9 | extension UnsafeRawPointer {
10 | func unalignedLoad(fromByteOffset: Int) -> T {
11 | var output: T = 0
12 | memcpy(&output, self + fromByteOffset, MemoryLayout.size)
13 | return output
14 | }
15 | }
16 |
17 | extension UnsafePointer {
18 | func unalignedLoad(fromByteOffset: Int) -> T {
19 | return UnsafeRawPointer(self).unalignedLoad(fromByteOffset: fromByteOffset)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/PLRelational/Sources/ValueWithDestructor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | class ValueWithDestructor {
7 | var value: T
8 | let destructor: (T) -> Void
9 |
10 | init(value: T, destructor: @escaping (T) -> Void) {
11 | self.value = value
12 | self.destructor = destructor
13 | }
14 |
15 | deinit {
16 | destructor(value)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/PLRelational/Tests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/PLRelational/Tests/InlineMutableDataTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import XCTest
7 | @testable import PLRelational
8 |
9 | class InlineMutableDataTests: XCTestCase {
10 | func testBigData() {
11 | var bigArray: [UInt8] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
12 | bigArray += bigArray
13 | bigArray += bigArray
14 | bigArray += bigArray
15 | bigArray += bigArray
16 | bigArray += bigArray
17 | bigArray += bigArray
18 | bigArray += bigArray
19 | bigArray += bigArray
20 | bigArray += bigArray
21 | bigArray += bigArray
22 |
23 | func assertContents(_ data: InlineMutableData) {
24 | XCTAssertEqual(data.length, bigArray.count)
25 | data.withUnsafeMutablePointerToElements({
26 | XCTAssertTrue(memcmp($0, bigArray, data.length) == 0)
27 | })
28 | }
29 |
30 | var a = InlineMutableData.make(10)
31 | a = InlineMutableData.append(a, pointer: bigArray, length: bigArray.count)
32 | assertContents(a)
33 |
34 | var b = InlineMutableData.make(bigArray.count)
35 | b = InlineMutableData.append(b, pointer: bigArray, length: bigArray.count)
36 | assertContents(b)
37 |
38 | var c = InlineMutableData.make(10)
39 | for elt in bigArray {
40 | var elt = elt
41 | c = InlineMutableData.append(c, pointer: &elt, length: 1)
42 | }
43 | assertContents(c)
44 |
45 | var d = InlineMutableData.make(10)
46 | for elt in bigArray {
47 | var elt = elt
48 | d = InlineMutableData.append(d, pointer: &elt, length: 1)
49 | }
50 | assertContents(d)
51 |
52 | XCTAssertEqual(a, b)
53 | XCTAssertEqual(a, c)
54 | XCTAssertEqual(a, d)
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/PLRelational/Tests/InternedUTF8StringTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import XCTest
7 | import PLRelational
8 |
9 | class InternedUTF8StringTests: XCTestCase {
10 | func testAll() {
11 | let abc = InternedUTF8String.get("abc")
12 | XCTAssertEqual(abc, InternedUTF8String.get("abc"))
13 |
14 | let def = InternedUTF8String.get("def")
15 | XCTAssertEqual(def, InternedUTF8String.get("def"))
16 |
17 | XCTAssertNotEqual(abc, def)
18 | XCTAssertTrue(abc < def)
19 | XCTAssertFalse(abc > def)
20 | XCTAssertEqual(abc.string, "abc")
21 | XCTAssertEqual(def.string, "def")
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/PLRelational/Tests/QueryRunnerTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2016 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import XCTest
7 | @testable import PLRelational
8 |
9 | class QueryRunnerTests: XCTestCase {
10 | func testLargerTree() {
11 | let a = MakeRelation(["number", "pilot", "equipment"])
12 | let b = MakeRelation(["number", "pilot", "equipment"], [1, "Jones", "777"])
13 | let c = MakeRelation(["number", "pilot", "equipment"])
14 | let d = MakeRelation(["number", "pilot", "equipment"], [2, "Smith", "787"])
15 | let e = MakeRelation(["number", "pilot", "equipment"])
16 | let f = MakeRelation(["number", "pilot", "equipment"], [1, "Jones", "777"])
17 | let g = MakeRelation(["number", "pilot", "equipment"], [3, "Johnson", "797"])
18 | let h = MakeRelation(["number", "pilot", "equipment"])
19 | let i = MakeRelation(["number", "pilot", "equipment"], [1, "Jones", "777"])
20 | let j = MakeRelation(["number", "pilot", "equipment"], [2, "Smith", "787"])
21 |
22 | let bc = b.difference(c)
23 | let abc = a.union(bc)
24 | let ef = e.difference(f)
25 | let def = d.difference(ef)
26 | let abcdef = abc.union(def)
27 | let hi = h.difference(i)
28 | let hij = hi.difference(j)
29 | let ghij = g.difference(hij)
30 | let abcdefghij = abcdef.union(ghij)
31 |
32 | AssertEqual(abcdefghij,
33 | MakeRelation(
34 | ["number", "pilot", "equipment"],
35 | [1, "Jones", "777"],
36 | [2, "Smith", "787"],
37 | [3, "Johnson", "797"]))
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/PLRelational/Tests/RelationDifferentiatorTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2017 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import XCTest
7 | @testable import PLRelational
8 |
9 | class RelationDifferentiatorTests: XCTestCase {
10 | func testEquijoinDerivative() {
11 | let a = MakeRelation(["id", "text"],
12 | [1, "new"],
13 | [2, "blah blah blah"])
14 | .setDebugName("a")
15 | let b = MakeRelation(["id", "title"],
16 | [1, "NEW"],
17 | [2, "Blah Blah Blah"])
18 | .setDebugName("b")
19 |
20 | let joined = a.equijoin(b, matching: ["id": "id"])
21 | .setDebugName("joined")
22 |
23 | let differentiator = RelationDifferentiator(relation: joined)
24 | let derivative = differentiator.computeDerivative()
25 |
26 | derivative.addChange(RelationChange(
27 | added: MakeRelation(["id", "text"], [1, "new"]),
28 | removed: MakeRelation(["id", "text"], [1, "old"])), toVariable: a)
29 |
30 | derivative.addChange(RelationChange(
31 | added: MakeRelation(["id", "title"], [1, "NEW"]),
32 | removed: MakeRelation(["id", "title"], [1, "OLD"])), toVariable: b)
33 |
34 | AssertEqual(derivative.change.added, MakeRelation(["id", "text", "title"], [1, "new", "NEW"]))
35 | AssertEqual(derivative.change.removed, MakeRelation(["id", "text", "title"], [1, "old", "OLD"]))
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/PLRelationalCombine/PLRelationalCombine.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/PLRelationalCombine/PLRelationalCombine.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/PLRelationalCombine/PLRelationalCombine.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/PLRelationalCombine/Sources/CancellableBag.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Combine
7 |
8 | /// Alias for a set of cancellables, with a `cancel` convenience method that
9 | /// allows for one-shot cancellation.
10 | public typealias CancellableBag = Set
11 |
12 | extension Set where Element: Cancellable {
13 | /// Cancel each cancellable in this set.
14 | public func cancel() {
15 | self.forEach{ $0.cancel() }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/PLRelationalCombine/Sources/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/PLRelationalCombine/Sources/RelationChangeSummary.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import PLRelational
7 |
8 | /// A container that summarizes a change made to a `Relation`, breaking down the change
9 | /// into distinct added, updated, and deleted items.
10 | public struct RelationChangeSummary {
11 | public let added: [T]
12 | public let updated: [T]
13 | public let deleted: [T]
14 |
15 | public var isEmpty: Bool {
16 | return added.isEmpty && updated.isEmpty && deleted.isEmpty
17 | }
18 | }
19 |
20 | extension NegativeSet where T == Row {
21 |
22 | func summary(idAttr: Attribute, _ mapFunc: (Row) -> V) -> RelationChangeSummary {
23 | // First gather the rows that are being deleted (or updated)
24 | var deletedRows = Set(self.removed)
25 |
26 | var addedItems: [V] = []
27 | var updatedItems: [V] = []
28 | for row in self.added {
29 | let id = row[idAttr]
30 | if let index = deletedRows.firstIndex(where: { $0[idAttr] == id }) {
31 | // A row with this identifier appears in both sets, so it must've been updated; the added set
32 | // will contain the new row contents
33 | updatedItems.append(mapFunc(row))
34 |
35 | // We can be sure this item isn't being removed, so remove it from the set of deleted items
36 | deletedRows.remove(at: index)
37 | } else {
38 | // This is a newly added row
39 | addedItems.append(mapFunc(row))
40 | }
41 | }
42 |
43 | let deletedItems: [V] = Array(deletedRows.map(mapFunc))
44 |
45 | return RelationChangeSummary(added: addedItems, updated: updatedItems, deleted: deletedItems)
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/PLRelationalCombine/Sources/UndoableDatabase.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import Foundation
7 | import PLRelational
8 |
9 | /// TODO: Docs
10 | public class UndoableDatabase {
11 |
12 | private let db: TransactionalDatabase
13 | private let undoManager: UndoManager
14 |
15 | public init(db: TransactionalDatabase, undoManager: UndoManager) {
16 | self.db = db
17 | self.undoManager = undoManager
18 | }
19 |
20 | /// TODO: Docs
21 | public func performUndoableAction(_ name: String, before: TransactionalDatabaseSnapshot? = nil, _ transactionFunc: @escaping () -> Void) {
22 | let deltaPromise = Promise()
23 |
24 | var actualBefore: TransactionalDatabaseSnapshot!
25 | if let before = before {
26 | actualBefore = before
27 | } else {
28 | AsyncManager.currentInstance.registerCheckpoint({
29 | actualBefore = self.db.takeSnapshot()
30 | })
31 | }
32 | transactionFunc()
33 | AsyncManager.currentInstance.registerCheckpoint({
34 | let after = self.db.takeSnapshot()
35 | let delta = self.db.computeDelta(from: actualBefore, to: after)
36 | deltaPromise.fulfill(delta)
37 | })
38 |
39 | undoManager.registerChange(
40 | name: name,
41 | perform: false,
42 | forward: {
43 | self.db.asyncApply(delta: deltaPromise.get())
44 | },
45 | backward: {
46 | self.db.asyncApply(delta: deltaPromise.get().reversed)
47 | }
48 | )
49 | }
50 |
51 | /// TODO: Docs
52 | public func takeSnapshot() -> TransactionalDatabaseSnapshot {
53 | return self.db.takeSnapshot()
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/PLRelationalCombine/Tests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/PLRelationalCombine/Tests/WeakBindTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import XCTest
7 | import Combine
8 | import PLRelational
9 | @testable import PLRelationalCombine
10 |
11 | class WeakBindTests: CombineTestCase {
12 |
13 | func testLifetime() {
14 |
15 | class TestObject {
16 | var value: String = ""
17 | private var cancellableBag = CancellableBag()
18 |
19 | init(names: Relation) {
20 | // This is a long-lived publisher that will continue to observe
21 | // the underlying relation until it is cancelled (and it will be
22 | // cancelled once there are no more references to this
23 | // TestObject instance)
24 | names
25 | .oneString()
26 | .replaceError(with: "")
27 | .bind(to: \.value, on: self)
28 | .store(in: &cancellableBag)
29 | }
30 |
31 | deinit {
32 | cancellableBag.cancel()
33 | }
34 | }
35 |
36 | let names = MakeRelation(["name"], ["fred"])
37 | var obj: TestObject? = TestObject(names: names)
38 | weak var weakObj = obj
39 |
40 | awaitIdle()
41 | XCTAssertEqual(weakObj?.value, "fred")
42 |
43 | // Verify that `bind` holds the given object weakly
44 | obj = nil
45 | XCTAssertNil(weakObj)
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/PLRelationalCombine/Tests/WeakTwoWayBindTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright (c) 2019 Plausible Labs Cooperative, Inc.
3 | // All rights reserved.
4 | //
5 |
6 | import XCTest
7 | import Combine
8 | import PLRelational
9 | @testable import PLRelationalCombine
10 |
11 | class WeakTwoWayBindTests: CombineTestCase {
12 |
13 | func testLifetime() {
14 |
15 | class TestObject: ObservableObject {
16 | @TwoWay var value: String = ""
17 | private var cancellableBag = CancellableBag()
18 |
19 | init(names: Relation) {
20 | // This is a long-lived publisher that will continue to observe
21 | // the underlying relation until it is cancelled (and it will be
22 | // cancelled once there are no more references to this
23 | // TestObject instance)
24 | names
25 | .bind(to: \._value, on: self, strategy: oneString)
26 | .store(in: &cancellableBag)
27 | }
28 |
29 | deinit {
30 | cancellableBag.cancel()
31 | }
32 | }
33 |
34 | let names = MakeRelation(["name"], ["fred"])
35 | var obj: TestObject? = TestObject(names: names)
36 | weak var weakObj = obj
37 |
38 | awaitIdle()
39 | XCTAssertEqual(weakObj?.value, "fred")
40 |
41 | // Verify that `bind` holds the given object weakly
42 | obj = nil
43 | XCTAssertNil(weakObj)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------