diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e43b0f9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.DS_Store
diff --git a/interject/1024 copy.png b/interject/1024 copy.png
new file mode 100644
index 0000000..8f4e0bd
Binary files /dev/null and b/interject/1024 copy.png differ
diff --git a/interject/Interject! iMessage/Assets.xcassets/Contents.json b/interject/Interject! iMessage/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/interject/Interject! iMessage/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/1024_768.png b/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/1024_768.png
new file mode 100644
index 0000000..e90aa93
Binary files /dev/null and b/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/1024_768.png differ
diff --git a/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/120_90.png b/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/120_90.png
new file mode 100644
index 0000000..d17014d
Binary files /dev/null and b/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/120_90.png differ
diff --git a/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/134_100.png b/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/134_100.png
new file mode 100644
index 0000000..0d4b53f
Binary files /dev/null and b/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/134_100.png differ
diff --git a/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/148_110.png b/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/148_110.png
new file mode 100644
index 0000000..59f4736
Binary files /dev/null and b/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/148_110.png differ
diff --git a/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/180_135.png b/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/180_135.png
new file mode 100644
index 0000000..94354c6
Binary files /dev/null and b/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/180_135.png differ
diff --git a/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/54_40.png b/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/54_40.png
new file mode 100644
index 0000000..3e5cc2b
Binary files /dev/null and b/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/54_40.png differ
diff --git a/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/64_48.png b/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/64_48.png
new file mode 100644
index 0000000..fe7f539
Binary files /dev/null and b/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/64_48.png differ
diff --git a/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/81_60.png b/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/81_60.png
new file mode 100644
index 0000000..b340d03
Binary files /dev/null and b/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/81_60.png differ
diff --git a/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/96_72.png b/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/96_72.png
new file mode 100644
index 0000000..4ed59db
Binary files /dev/null and b/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/96_72.png differ
diff --git a/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/Contents.json b/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/Contents.json
new file mode 100644
index 0000000..9bc32c8
--- /dev/null
+++ b/interject/Interject! iMessage/Assets.xcassets/iMessage App Icon.stickersiconset/Contents.json
@@ -0,0 +1,67 @@
+{
+ "images" : [
+ {
+ "filename" : "120_90.png",
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "60x45"
+ },
+ {
+ "filename" : "180_135.png",
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "60x45"
+ },
+ {
+ "filename" : "134_100.png",
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "67x50"
+ },
+ {
+ "filename" : "148_110.png",
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "74x55"
+ },
+ {
+ "filename" : "54_40.png",
+ "idiom" : "universal",
+ "platform" : "ios",
+ "scale" : "2x",
+ "size" : "27x20"
+ },
+ {
+ "filename" : "81_60.png",
+ "idiom" : "universal",
+ "platform" : "ios",
+ "scale" : "3x",
+ "size" : "27x20"
+ },
+ {
+ "filename" : "64_48.png",
+ "idiom" : "universal",
+ "platform" : "ios",
+ "scale" : "2x",
+ "size" : "32x24"
+ },
+ {
+ "filename" : "96_72.png",
+ "idiom" : "universal",
+ "platform" : "ios",
+ "scale" : "3x",
+ "size" : "32x24"
+ },
+ {
+ "filename" : "1024_768.png",
+ "idiom" : "ios-marketing",
+ "platform" : "ios",
+ "scale" : "1x",
+ "size" : "1024x768"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/interject/Interject! iMessage/Info.plist b/interject/Interject! iMessage/Info.plist
new file mode 100644
index 0000000..52e0429
--- /dev/null
+++ b/interject/Interject! iMessage/Info.plist
@@ -0,0 +1,13 @@
+
+
+
+
+ NSExtension
+
+ NSExtensionMainStoryboard
+ MainInterface
+ NSExtensionPointIdentifier
+ com.apple.message-payload-provider
+
+
+
diff --git a/interject/Interject! iMessage/LaunchScreen.storyboard b/interject/Interject! iMessage/LaunchScreen.storyboard
new file mode 100644
index 0000000..f83f6fd
--- /dev/null
+++ b/interject/Interject! iMessage/LaunchScreen.storyboard
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/interject/Interject! iMessage/MainInterface.storyboard b/interject/Interject! iMessage/MainInterface.storyboard
new file mode 100644
index 0000000..326d211
--- /dev/null
+++ b/interject/Interject! iMessage/MainInterface.storyboard
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/interject/Interject! iMessage/MessagesViewController.swift b/interject/Interject! iMessage/MessagesViewController.swift
new file mode 100644
index 0000000..14d25f6
--- /dev/null
+++ b/interject/Interject! iMessage/MessagesViewController.swift
@@ -0,0 +1,70 @@
+//
+// MessagesViewController.swift
+// iMessage Interject
+//
+// Created by Rory Hinnen on 11/18/18.
+// Copyright © 2018 Rory Hinnen. All rights reserved.
+//
+
+import UIKit
+import Messages
+
+extension String {
+ var uppercaseFirst: String {
+ return prefix(1).uppercased() + dropFirst()
+ }
+}
+
+// This uses the Words Structure, it doesn't need History.
+class MessagesViewController: MSMessagesAppViewController {
+
+ // MARK: UI VARS
+ @IBOutlet weak var sfwLabel: UIBarButtonItem!
+ @IBOutlet weak var phraseLabel: UILabel!
+
+
+ // MARK: Local Vars
+ var sfw = true
+ var sentence = Sentence()
+
+
+ // MARK: functions
+ // first load, Let's explain what the need to do.
+ override func viewDidLoad() {
+ super.viewDidLoad()
+ phraseLabel.text = "Swipe down for new words, up to send."
+ }
+
+ // Toggle sfw
+ @IBAction func sfwToggle(_ sender: Any) {
+ if !sfw {
+ sfw = true
+ sfwLabel.title = "SFW"
+ updatePhraseLabel()
+ } else {
+ sfw = false
+ sfwLabel.title = "NSFW"
+ updatePhraseLabel()
+ }
+ }
+
+ // Let's get a new phrase
+ @IBAction func newPhrase(_ sender: Any) {
+ sentence = Sentence().new(current: sentence)
+ updatePhraseLabel()
+ }
+
+ // Send a phrase to messages
+ @IBAction func sendPhrase(_ sender: Any) {
+ let conversation = self.activeConversation
+ conversation?.insertText(phraseLabel.text!) { error in }
+ }
+
+ // Update the label
+ func updatePhraseLabel() {
+ phraseLabel.text =
+ sentence.word(.first, safe: sfw).uppercaseFirst + " " +
+ sentence.word(.second, safe: sfw).uppercaseFirst + " " +
+ sentence.word(.noun, safe: sfw).uppercaseFirst
+ }
+}
diff --git a/interject/Interject!.xcodeproj/project.pbxproj b/interject/Interject!.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..e91926e
--- /dev/null
+++ b/interject/Interject!.xcodeproj/project.pbxproj
@@ -0,0 +1,754 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 63;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 762C51A22BC33AE5000689D9 /* Tips.swift in Sources */ = {isa = PBXBuildFile; fileRef = 762C51A12BC33AE5000689D9 /* Tips.swift */; };
+ 762C51A42BCAD9BC000689D9 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 762C51A32BCAD9BC000689D9 /* StoreKit.framework */; };
+ 762C51A62BCADA1C000689D9 /* Products.storekit in Resources */ = {isa = PBXBuildFile; fileRef = 762C51A52BCADA1C000689D9 /* Products.storekit */; };
+ 762C51A82BCADCE7000689D9 /* Store.swift in Sources */ = {isa = PBXBuildFile; fileRef = 762C51A72BCADCE7000689D9 /* Store.swift */; };
+ 762C51AA2BCB4305000689D9 /* Products.plist in Resources */ = {isa = PBXBuildFile; fileRef = 762C51A92BCB4305000689D9 /* Products.plist */; };
+ 762C51AC2BCC5C06000689D9 /* ListCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 762C51AB2BCC5C06000689D9 /* ListCellView.swift */; };
+ 762C51AE2BCC9C52000689D9 /* BuyButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 762C51AD2BCC9C52000689D9 /* BuyButtonStyle.swift */; };
+ 76CBBDC22BD46EA10019C344 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 76CBBDC12BD46EA10019C344 /* PrivacyInfo.xcprivacy */; };
+ 981334CE2754530100E9E53A /* WordView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 981334CD2754530100E9E53A /* WordView.swift */; };
+ 9814700D286162030097E0A7 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9814700C286162030097E0A7 /* Utils.swift */; };
+ 98D0A2A927471FF80084F7F9 /* Interject_App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D0A2A827471FF80084F7F9 /* Interject_App.swift */; };
+ 98D0A2AB27471FF80084F7F9 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D0A2AA27471FF80084F7F9 /* ContentView.swift */; };
+ 98D0A2AD27471FF90084F7F9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 98D0A2AC27471FF90084F7F9 /* Assets.xcassets */; };
+ 98D0A2B027471FF90084F7F9 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 98D0A2AF27471FF90084F7F9 /* Preview Assets.xcassets */; };
+ 98D0A2BA27471FF90084F7F9 /* Interject_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D0A2B927471FF90084F7F9 /* Interject_Tests.swift */; };
+ 98D0A2C427471FF90084F7F9 /* Interject_UITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D0A2C327471FF90084F7F9 /* Interject_UITests.swift */; };
+ 98D0A2C627471FF90084F7F9 /* Interject_UITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D0A2C527471FF90084F7F9 /* Interject_UITestsLaunchTests.swift */; };
+ 98D0A2D8274720360084F7F9 /* SentenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D0A2D4274720360084F7F9 /* SentenceView.swift */; };
+ 98D0A2D9274720360084F7F9 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D0A2D5274720360084F7F9 /* SettingsView.swift */; };
+ 98D0A2DB274720360084F7F9 /* DefinitionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D0A2D7274720360084F7F9 /* DefinitionView.swift */; };
+ 98D0A2E1274721620084F7F9 /* Words.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D0A2DE274721620084F7F9 /* Words.swift */; };
+ 98D0A2E2274721620084F7F9 /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98D0A2DD274721620084F7F9 /* Settings.swift */; };
+ 98EA0F3728636B6400B4E136 /* ToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98EA0F3628636B6400B4E136 /* ToolbarView.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 98D0A2B627471FF90084F7F9 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 98D0A29D27471FF80084F7F9 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 98D0A2A427471FF80084F7F9;
+ remoteInfo = "Interject!";
+ };
+ 98D0A2C027471FF90084F7F9 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 98D0A29D27471FF80084F7F9 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 98D0A2A427471FF80084F7F9;
+ remoteInfo = "Interject!";
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 98EA0F4F2863963F00B4E136 /* Embed Foundation Extensions */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 13;
+ files = (
+ );
+ name = "Embed Foundation Extensions";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 760B5A742BDD538100ADE31A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+ 761F89D12BDF0AC1003D2DD2 /* MainInterface.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = MainInterface.storyboard; sourceTree = ""; };
+ 761F89D32BDF0ACC003D2DD2 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; };
+ 761F89D52BDF0AE3003D2DD2 /* MessagesViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MessagesViewController.swift; sourceTree = ""; };
+ 761F89D72BDF0AF3003D2DD2 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 762C51A12BC33AE5000689D9 /* Tips.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tips.swift; sourceTree = ""; };
+ 762C51A32BCAD9BC000689D9 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
+ 762C51A52BCADA1C000689D9 /* Products.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Products.storekit; sourceTree = ""; };
+ 762C51A72BCADCE7000689D9 /* Store.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Store.swift; sourceTree = ""; };
+ 762C51A92BCB4305000689D9 /* Products.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Products.plist; sourceTree = ""; };
+ 762C51AB2BCC5C06000689D9 /* ListCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCellView.swift; sourceTree = ""; };
+ 762C51AD2BCC9C52000689D9 /* BuyButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuyButtonStyle.swift; sourceTree = ""; };
+ 76C445E02BDD4DE30033B339 /* Messages.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Messages.framework; path = System/Library/Frameworks/Messages.framework; sourceTree = SDKROOT; };
+ 76CBBDC12BD46EA10019C344 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; };
+ 981334CD2754530100E9E53A /* WordView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordView.swift; sourceTree = ""; };
+ 9814700C286162030097E0A7 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; };
+ 98D0A2A527471FF80084F7F9 /* Interject!.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Interject!.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 98D0A2A827471FF80084F7F9 /* Interject_App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Interject_App.swift; sourceTree = ""; };
+ 98D0A2AA27471FF80084F7F9 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
+ 98D0A2AC27471FF90084F7F9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 98D0A2AF27471FF90084F7F9 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
+ 98D0A2B527471FF90084F7F9 /* Interject!Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Interject!Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 98D0A2B927471FF90084F7F9 /* Interject_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Interject_Tests.swift; sourceTree = ""; };
+ 98D0A2BF27471FF90084F7F9 /* Interject!UITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Interject!UITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 98D0A2C327471FF90084F7F9 /* Interject_UITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Interject_UITests.swift; sourceTree = ""; };
+ 98D0A2C527471FF90084F7F9 /* Interject_UITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Interject_UITestsLaunchTests.swift; sourceTree = ""; };
+ 98D0A2D4274720360084F7F9 /* SentenceView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SentenceView.swift; sourceTree = ""; };
+ 98D0A2D5274720360084F7F9 /* SettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; };
+ 98D0A2D7274720360084F7F9 /* DefinitionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefinitionView.swift; sourceTree = ""; };
+ 98D0A2DD274721620084F7F9 /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; };
+ 98D0A2DE274721620084F7F9 /* Words.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Words.swift; sourceTree = ""; };
+ 98EA0F3628636B6400B4E136 /* ToolbarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolbarView.swift; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 98D0A2A227471FF80084F7F9 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 762C51A42BCAD9BC000689D9 /* StoreKit.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 98D0A2B227471FF90084F7F9 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 98D0A2BC27471FF90084F7F9 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 760B5A6C2BDD537F00ADE31A /* Interject! iMessage */ = {
+ isa = PBXGroup;
+ children = (
+ 761F89D72BDF0AF3003D2DD2 /* Assets.xcassets */,
+ 761F89D52BDF0AE3003D2DD2 /* MessagesViewController.swift */,
+ 761F89D12BDF0AC1003D2DD2 /* MainInterface.storyboard */,
+ 761F89D32BDF0ACC003D2DD2 /* LaunchScreen.storyboard */,
+ 760B5A742BDD538100ADE31A /* Info.plist */,
+ );
+ path = "Interject! iMessage";
+ sourceTree = "";
+ };
+ 9814700B286161EC0097E0A7 /* Utils */ = {
+ isa = PBXGroup;
+ children = (
+ 9814700C286162030097E0A7 /* Utils.swift */,
+ );
+ path = Utils;
+ sourceTree = "";
+ };
+ 9879879A2865299F00E82AF6 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 762C51A32BCAD9BC000689D9 /* StoreKit.framework */,
+ 76C445E02BDD4DE30033B339 /* Messages.framework */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ 98D0A29C27471FF80084F7F9 = {
+ isa = PBXGroup;
+ children = (
+ 98D0A2A727471FF80084F7F9 /* Interject! */,
+ 98D0A2B827471FF90084F7F9 /* Interject!Tests */,
+ 98D0A2C227471FF90084F7F9 /* Interject!UITests */,
+ 760B5A6C2BDD537F00ADE31A /* Interject! iMessage */,
+ 9879879A2865299F00E82AF6 /* Frameworks */,
+ 98D0A2A627471FF80084F7F9 /* Products */,
+ );
+ sourceTree = "";
+ };
+ 98D0A2A627471FF80084F7F9 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 98D0A2A527471FF80084F7F9 /* Interject!.app */,
+ 98D0A2B527471FF90084F7F9 /* Interject!Tests.xctest */,
+ 98D0A2BF27471FF90084F7F9 /* Interject!UITests.xctest */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 98D0A2A727471FF80084F7F9 /* Interject! */ = {
+ isa = PBXGroup;
+ children = (
+ 98D0A2DC274720BF0084F7F9 /* View */,
+ 98D0A2E32747218C0084F7F9 /* Model */,
+ 98D0A2AC27471FF90084F7F9 /* Assets.xcassets */,
+ 9814700B286161EC0097E0A7 /* Utils */,
+ 98D0A2AE27471FF90084F7F9 /* Preview Content */,
+ 762C51A52BCADA1C000689D9 /* Products.storekit */,
+ 762C51A92BCB4305000689D9 /* Products.plist */,
+ 76CBBDC12BD46EA10019C344 /* PrivacyInfo.xcprivacy */,
+ );
+ path = "Interject!";
+ sourceTree = "";
+ };
+ 98D0A2AE27471FF90084F7F9 /* Preview Content */ = {
+ isa = PBXGroup;
+ children = (
+ 98D0A2AF27471FF90084F7F9 /* Preview Assets.xcassets */,
+ );
+ path = "Preview Content";
+ sourceTree = "";
+ };
+ 98D0A2B827471FF90084F7F9 /* Interject!Tests */ = {
+ isa = PBXGroup;
+ children = (
+ 98D0A2B927471FF90084F7F9 /* Interject_Tests.swift */,
+ );
+ path = "Interject!Tests";
+ sourceTree = "";
+ };
+ 98D0A2C227471FF90084F7F9 /* Interject!UITests */ = {
+ isa = PBXGroup;
+ children = (
+ 98D0A2C327471FF90084F7F9 /* Interject_UITests.swift */,
+ 98D0A2C527471FF90084F7F9 /* Interject_UITestsLaunchTests.swift */,
+ );
+ path = "Interject!UITests";
+ sourceTree = "";
+ };
+ 98D0A2DC274720BF0084F7F9 /* View */ = {
+ isa = PBXGroup;
+ children = (
+ 98D0A2A827471FF80084F7F9 /* Interject_App.swift */,
+ 98D0A2AA27471FF80084F7F9 /* ContentView.swift */,
+ 98D0A2D4274720360084F7F9 /* SentenceView.swift */,
+ 981334CD2754530100E9E53A /* WordView.swift */,
+ 98D0A2D7274720360084F7F9 /* DefinitionView.swift */,
+ 98D0A2D5274720360084F7F9 /* SettingsView.swift */,
+ 98EA0F3628636B6400B4E136 /* ToolbarView.swift */,
+ 762C51A12BC33AE5000689D9 /* Tips.swift */,
+ 762C51AB2BCC5C06000689D9 /* ListCellView.swift */,
+ 762C51AD2BCC9C52000689D9 /* BuyButtonStyle.swift */,
+ );
+ path = View;
+ sourceTree = "";
+ };
+ 98D0A2E32747218C0084F7F9 /* Model */ = {
+ isa = PBXGroup;
+ children = (
+ 98D0A2DD274721620084F7F9 /* Settings.swift */,
+ 98D0A2DE274721620084F7F9 /* Words.swift */,
+ 762C51A72BCADCE7000689D9 /* Store.swift */,
+ );
+ path = Model;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 98D0A2A427471FF80084F7F9 /* Interject! */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 98D0A2C927471FF90084F7F9 /* Build configuration list for PBXNativeTarget "Interject!" */;
+ buildPhases = (
+ 98D0A2A127471FF80084F7F9 /* Sources */,
+ 98D0A2A227471FF80084F7F9 /* Frameworks */,
+ 98D0A2A327471FF80084F7F9 /* Resources */,
+ 98EA0F4F2863963F00B4E136 /* Embed Foundation Extensions */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = "Interject!";
+ productName = "Interject!";
+ productReference = 98D0A2A527471FF80084F7F9 /* Interject!.app */;
+ productType = "com.apple.product-type.application";
+ };
+ 98D0A2B427471FF90084F7F9 /* Interject!Tests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 98D0A2CC27471FF90084F7F9 /* Build configuration list for PBXNativeTarget "Interject!Tests" */;
+ buildPhases = (
+ 98D0A2B127471FF90084F7F9 /* Sources */,
+ 98D0A2B227471FF90084F7F9 /* Frameworks */,
+ 98D0A2B327471FF90084F7F9 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 98D0A2B727471FF90084F7F9 /* PBXTargetDependency */,
+ );
+ name = "Interject!Tests";
+ productName = "Interject!Tests";
+ productReference = 98D0A2B527471FF90084F7F9 /* Interject!Tests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ 98D0A2BE27471FF90084F7F9 /* Interject!UITests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 98D0A2CF27471FF90084F7F9 /* Build configuration list for PBXNativeTarget "Interject!UITests" */;
+ buildPhases = (
+ 98D0A2BB27471FF90084F7F9 /* Sources */,
+ 98D0A2BC27471FF90084F7F9 /* Frameworks */,
+ 98D0A2BD27471FF90084F7F9 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 98D0A2C127471FF90084F7F9 /* PBXTargetDependency */,
+ );
+ name = "Interject!UITests";
+ productName = "Interject!UITests";
+ productReference = 98D0A2BF27471FF90084F7F9 /* Interject!UITests.xctest */;
+ productType = "com.apple.product-type.bundle.ui-testing";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 98D0A29D27471FF80084F7F9 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = 1;
+ LastSwiftUpdateCheck = 1530;
+ LastUpgradeCheck = 1530;
+ TargetAttributes = {
+ 98D0A2A427471FF80084F7F9 = {
+ CreatedOnToolsVersion = 13.1;
+ };
+ 98D0A2B427471FF90084F7F9 = {
+ CreatedOnToolsVersion = 13.1;
+ TestTargetID = 98D0A2A427471FF80084F7F9;
+ };
+ 98D0A2BE27471FF90084F7F9 = {
+ CreatedOnToolsVersion = 13.1;
+ TestTargetID = 98D0A2A427471FF80084F7F9;
+ };
+ };
+ };
+ buildConfigurationList = 98D0A2A027471FF80084F7F9 /* Build configuration list for PBXProject "Interject!" */;
+ compatibilityVersion = "Xcode 15.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 98D0A29C27471FF80084F7F9;
+ productRefGroup = 98D0A2A627471FF80084F7F9 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 98D0A2A427471FF80084F7F9 /* Interject! */,
+ 98D0A2B427471FF90084F7F9 /* Interject!Tests */,
+ 98D0A2BE27471FF90084F7F9 /* Interject!UITests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 98D0A2A327471FF80084F7F9 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 762C51AA2BCB4305000689D9 /* Products.plist in Resources */,
+ 98D0A2B027471FF90084F7F9 /* Preview Assets.xcassets in Resources */,
+ 98D0A2AD27471FF90084F7F9 /* Assets.xcassets in Resources */,
+ 76CBBDC22BD46EA10019C344 /* PrivacyInfo.xcprivacy in Resources */,
+ 762C51A62BCADA1C000689D9 /* Products.storekit in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 98D0A2B327471FF90084F7F9 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 98D0A2BD27471FF90084F7F9 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 98D0A2A127471FF80084F7F9 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 98D0A2AB27471FF80084F7F9 /* ContentView.swift in Sources */,
+ 762C51A82BCADCE7000689D9 /* Store.swift in Sources */,
+ 98D0A2D9274720360084F7F9 /* SettingsView.swift in Sources */,
+ 762C51AC2BCC5C06000689D9 /* ListCellView.swift in Sources */,
+ 762C51A22BC33AE5000689D9 /* Tips.swift in Sources */,
+ 9814700D286162030097E0A7 /* Utils.swift in Sources */,
+ 762C51AE2BCC9C52000689D9 /* BuyButtonStyle.swift in Sources */,
+ 98D0A2DB274720360084F7F9 /* DefinitionView.swift in Sources */,
+ 98D0A2A927471FF80084F7F9 /* Interject_App.swift in Sources */,
+ 981334CE2754530100E9E53A /* WordView.swift in Sources */,
+ 98EA0F3728636B6400B4E136 /* ToolbarView.swift in Sources */,
+ 98D0A2E1274721620084F7F9 /* Words.swift in Sources */,
+ 98D0A2E2274721620084F7F9 /* Settings.swift in Sources */,
+ 98D0A2D8274720360084F7F9 /* SentenceView.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 98D0A2B127471FF90084F7F9 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 98D0A2BA27471FF90084F7F9 /* Interject_Tests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 98D0A2BB27471FF90084F7F9 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 98D0A2C427471FF90084F7F9 /* Interject_UITests.swift in Sources */,
+ 98D0A2C627471FF90084F7F9 /* Interject_UITestsLaunchTests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 98D0A2B727471FF90084F7F9 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 98D0A2A427471FF80084F7F9 /* Interject! */;
+ targetProxy = 98D0A2B627471FF90084F7F9 /* PBXContainerItemProxy */;
+ };
+ 98D0A2C127471FF90084F7F9 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 98D0A2A427471FF80084F7F9 /* Interject! */;
+ targetProxy = 98D0A2C027471FF90084F7F9 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 98D0A2C727471FF90084F7F9 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ INFOPLIST_KEY_WKRunsIndependentlyOfCompanionApp = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 98D0A2C827471FF90084F7F9 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ INFOPLIST_KEY_WKRunsIndependentlyOfCompanionApp = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 98D0A2CA27471FF90084F7F9 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_ASSET_PATHS = "";
+ DEVELOPMENT_TEAM = 5P9XB49J6K;
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = "Interject--Info.plist";
+ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
+ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 3.04;
+ PRODUCT_BUNDLE_IDENTIFIER = "us.hinnen.Interject-";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 98D0A2CB27471FF90084F7F9 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_ASSET_PATHS = "";
+ DEVELOPMENT_TEAM = 5P9XB49J6K;
+ ENABLE_PREVIEWS = YES;
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_FILE = "Interject--Info.plist";
+ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
+ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
+ IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ MARKETING_VERSION = 3.04;
+ PRODUCT_BUNDLE_IDENTIFIER = "us.hinnen.Interject-";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = YES;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+ 98D0A2CD27471FF90084F7F9 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = 5P9XB49J6K;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.chartreusenecktie.Interject-Tests";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Interject!.app/Interject!";
+ };
+ name = Debug;
+ };
+ 98D0A2CE27471FF90084F7F9 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = 5P9XB49J6K;
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 15.0;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.chartreusenecktie.Interject-Tests";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Interject!.app/Interject!";
+ };
+ name = Release;
+ };
+ 98D0A2D027471FF90084F7F9 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = 5P9XB49J6K;
+ GENERATE_INFOPLIST_FILE = YES;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.chartreusenecktie.Interject-UITests";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_TARGET_NAME = "Interject!";
+ };
+ name = Debug;
+ };
+ 98D0A2D127471FF90084F7F9 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ DEVELOPMENT_TEAM = 5P9XB49J6K;
+ GENERATE_INFOPLIST_FILE = YES;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ "@loader_path/Frameworks",
+ );
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = "com.chartreusenecktie.Interject-UITests";
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_EMIT_LOC_STRINGS = NO;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_TARGET_NAME = "Interject!";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 98D0A2A027471FF80084F7F9 /* Build configuration list for PBXProject "Interject!" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 98D0A2C727471FF90084F7F9 /* Debug */,
+ 98D0A2C827471FF90084F7F9 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 98D0A2C927471FF90084F7F9 /* Build configuration list for PBXNativeTarget "Interject!" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 98D0A2CA27471FF90084F7F9 /* Debug */,
+ 98D0A2CB27471FF90084F7F9 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 98D0A2CC27471FF90084F7F9 /* Build configuration list for PBXNativeTarget "Interject!Tests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 98D0A2CD27471FF90084F7F9 /* Debug */,
+ 98D0A2CE27471FF90084F7F9 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 98D0A2CF27471FF90084F7F9 /* Build configuration list for PBXNativeTarget "Interject!UITests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 98D0A2D027471FF90084F7F9 /* Debug */,
+ 98D0A2D127471FF90084F7F9 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 98D0A29D27471FF80084F7F9 /* Project object */;
+}
diff --git a/interject/Interject!.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/interject/Interject!.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/interject/Interject!.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/interject/Interject!.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/interject/Interject!.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/interject/Interject!.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/interject/Interject!.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/interject/Interject!.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..f9b0d7c
--- /dev/null
+++ b/interject/Interject!.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/interject/Interject!.xcodeproj/project.xcworkspace/xcuserdata/rory.xcuserdatad/UserInterfaceState.xcuserstate b/interject/Interject!.xcodeproj/project.xcworkspace/xcuserdata/rory.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644
index 0000000..0751566
Binary files /dev/null and b/interject/Interject!.xcodeproj/project.xcworkspace/xcuserdata/rory.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/interject/Interject!.xcodeproj/project.xcworkspace/xcuserdata/rory.xcuserdatad/WorkspaceSettings.xcsettings b/interject/Interject!.xcodeproj/project.xcworkspace/xcuserdata/rory.xcuserdatad/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000..379adbe
--- /dev/null
+++ b/interject/Interject!.xcodeproj/project.xcworkspace/xcuserdata/rory.xcuserdatad/WorkspaceSettings.xcsettings
@@ -0,0 +1,18 @@
+
+
+
+
+ BuildLocationStyle
+ UseAppPreferences
+ CustomBuildLocationType
+ RelativeToDerivedData
+ DerivedDataLocationStyle
+ Default
+ IssueFilterStyle
+ ShowActiveSchemeOnly
+ LiveSourceIssuesEnabled
+
+ ShowSharedSchemesAutomaticallyEnabled
+
+
+
diff --git a/interject/Interject!.xcodeproj/project.xcworkspace/xcuserdata/roryhinnen.xcuserdatad/IDEFindNavigatorScopes.plist b/interject/Interject!.xcodeproj/project.xcworkspace/xcuserdata/roryhinnen.xcuserdatad/IDEFindNavigatorScopes.plist
new file mode 100644
index 0000000..5dd5da8
--- /dev/null
+++ b/interject/Interject!.xcodeproj/project.xcworkspace/xcuserdata/roryhinnen.xcuserdatad/IDEFindNavigatorScopes.plist
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/interject/Interject!.xcodeproj/project.xcworkspace/xcuserdata/roryhinnen.xcuserdatad/UserInterfaceState.xcuserstate b/interject/Interject!.xcodeproj/project.xcworkspace/xcuserdata/roryhinnen.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644
index 0000000..fe342c6
Binary files /dev/null and b/interject/Interject!.xcodeproj/project.xcworkspace/xcuserdata/roryhinnen.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/interject/Interject!.xcodeproj/xcshareddata/xcschemes/Interject!.xcscheme b/interject/Interject!.xcodeproj/xcshareddata/xcschemes/Interject!.xcscheme
new file mode 100644
index 0000000..5a165f5
--- /dev/null
+++ b/interject/Interject!.xcodeproj/xcshareddata/xcschemes/Interject!.xcscheme
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/interject/Interject!.xcodeproj/xcuserdata/rory.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/interject/Interject!.xcodeproj/xcuserdata/rory.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
new file mode 100644
index 0000000..bd6ce69
--- /dev/null
+++ b/interject/Interject!.xcodeproj/xcuserdata/rory.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -0,0 +1,6 @@
+
+
+
diff --git a/interject/Interject!.xcodeproj/xcuserdata/rory.xcuserdatad/xcschemes/xcschememanagement.plist b/interject/Interject!.xcodeproj/xcuserdata/rory.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..a3aaade
--- /dev/null
+++ b/interject/Interject!.xcodeproj/xcuserdata/rory.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,39 @@
+
+
+
+
+ SchemeUserState
+
+ Interject! iMessage MessagesExtension.xcscheme_^#shared#^_
+
+ orderHint
+ 1
+
+ Interject!.xcscheme_^#shared#^_
+
+ isShown
+
+ orderHint
+ 0
+
+
+ SuppressBuildableAutocreation
+
+ 98D0A2A427471FF80084F7F9
+
+ primary
+
+
+ 98D0A2B427471FF90084F7F9
+
+ primary
+
+
+ 98D0A2BE27471FF90084F7F9
+
+ primary
+
+
+
+
+
diff --git a/interject/Interject!.xcodeproj/xcuserdata/roryhinnen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/interject/Interject!.xcodeproj/xcuserdata/roryhinnen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
new file mode 100644
index 0000000..7abe87b
--- /dev/null
+++ b/interject/Interject!.xcodeproj/xcuserdata/roryhinnen.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
diff --git a/interject/Interject!.xcodeproj/xcuserdata/roryhinnen.xcuserdatad/xcschemes/xcschememanagement.plist b/interject/Interject!.xcodeproj/xcuserdata/roryhinnen.xcuserdatad/xcschemes/xcschememanagement.plist
new file mode 100644
index 0000000..0d910b2
--- /dev/null
+++ b/interject/Interject!.xcodeproj/xcuserdata/roryhinnen.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -0,0 +1,29 @@
+
+
+
+
+ SchemeUserState
+
+ Interject! iMessage MessagesExtension.xcscheme_^#shared#^_
+
+ orderHint
+ 1
+
+ Interject! iMessage.xcscheme_^#shared#^_
+
+ orderHint
+ 1
+
+ Interject! messages.xcscheme_^#shared#^_
+
+ orderHint
+ 1
+
+ Interject!.xcscheme_^#shared#^_
+
+ orderHint
+ 0
+
+
+
+
diff --git a/interject/Interject!/Assets.xcassets/AppIcon.appiconset/1024.png b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/1024.png
new file mode 100644
index 0000000..3fc3291
Binary files /dev/null and b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/1024.png differ
diff --git a/interject/Interject!/Assets.xcassets/AppIcon.appiconset/120-1.png b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/120-1.png
new file mode 100644
index 0000000..963cdb0
Binary files /dev/null and b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/120-1.png differ
diff --git a/interject/Interject!/Assets.xcassets/AppIcon.appiconset/120.png b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/120.png
new file mode 100644
index 0000000..963cdb0
Binary files /dev/null and b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/120.png differ
diff --git a/interject/Interject!/Assets.xcassets/AppIcon.appiconset/152.png b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/152.png
new file mode 100644
index 0000000..8752d91
Binary files /dev/null and b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/152.png differ
diff --git a/interject/Interject!/Assets.xcassets/AppIcon.appiconset/167.png b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/167.png
new file mode 100644
index 0000000..4ffca42
Binary files /dev/null and b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/167.png differ
diff --git a/interject/Interject!/Assets.xcassets/AppIcon.appiconset/180.png b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/180.png
new file mode 100644
index 0000000..ccd7d0a
Binary files /dev/null and b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/180.png differ
diff --git a/interject/Interject!/Assets.xcassets/AppIcon.appiconset/20.png b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/20.png
new file mode 100644
index 0000000..812bde8
Binary files /dev/null and b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/20.png differ
diff --git a/interject/Interject!/Assets.xcassets/AppIcon.appiconset/29.png b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/29.png
new file mode 100644
index 0000000..83bb805
Binary files /dev/null and b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/29.png differ
diff --git a/interject/Interject!/Assets.xcassets/AppIcon.appiconset/40-1.png b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/40-1.png
new file mode 100644
index 0000000..dd854d3
Binary files /dev/null and b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/40-1.png differ
diff --git a/interject/Interject!/Assets.xcassets/AppIcon.appiconset/40-2.png b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/40-2.png
new file mode 100644
index 0000000..dd854d3
Binary files /dev/null and b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/40-2.png differ
diff --git a/interject/Interject!/Assets.xcassets/AppIcon.appiconset/40.png b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/40.png
new file mode 100644
index 0000000..dd854d3
Binary files /dev/null and b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/40.png differ
diff --git a/interject/Interject!/Assets.xcassets/AppIcon.appiconset/58-1.png b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/58-1.png
new file mode 100644
index 0000000..14f13b7
Binary files /dev/null and b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/58-1.png differ
diff --git a/interject/Interject!/Assets.xcassets/AppIcon.appiconset/58.png b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/58.png
new file mode 100644
index 0000000..14f13b7
Binary files /dev/null and b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/58.png differ
diff --git a/interject/Interject!/Assets.xcassets/AppIcon.appiconset/60.png b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/60.png
new file mode 100644
index 0000000..8851627
Binary files /dev/null and b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/60.png differ
diff --git a/interject/Interject!/Assets.xcassets/AppIcon.appiconset/76.png b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/76.png
new file mode 100644
index 0000000..a1f1d15
Binary files /dev/null and b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/76.png differ
diff --git a/interject/Interject!/Assets.xcassets/AppIcon.appiconset/80-1.png b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/80-1.png
new file mode 100644
index 0000000..b4cff6f
Binary files /dev/null and b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/80-1.png differ
diff --git a/interject/Interject!/Assets.xcassets/AppIcon.appiconset/80.png b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/80.png
new file mode 100644
index 0000000..b4cff6f
Binary files /dev/null and b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/80.png differ
diff --git a/interject/Interject!/Assets.xcassets/AppIcon.appiconset/87.png b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/87.png
new file mode 100644
index 0000000..a4ba065
Binary files /dev/null and b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/87.png differ
diff --git a/interject/Interject!/Assets.xcassets/AppIcon.appiconset/Contents.json b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..e594077
--- /dev/null
+++ b/interject/Interject!/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,116 @@
+{
+ "images" : [
+ {
+ "filename" : "40.png",
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "filename" : "60.png",
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "20x20"
+ },
+ {
+ "filename" : "58.png",
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "filename" : "87.png",
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "29x29"
+ },
+ {
+ "filename" : "80.png",
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "filename" : "120.png",
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "40x40"
+ },
+ {
+ "filename" : "120-1.png",
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "60x60"
+ },
+ {
+ "filename" : "180.png",
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "60x60"
+ },
+ {
+ "filename" : "20.png",
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "20x20"
+ },
+ {
+ "filename" : "40-1.png",
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "filename" : "29.png",
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "29x29"
+ },
+ {
+ "filename" : "58-1.png",
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "filename" : "40-2.png",
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "40x40"
+ },
+ {
+ "filename" : "80-1.png",
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "filename" : "76.png",
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "76x76"
+ },
+ {
+ "filename" : "152.png",
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "76x76"
+ },
+ {
+ "filename" : "167.png",
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "83.5x83.5"
+ },
+ {
+ "filename" : "1024.png",
+ "idiom" : "ios-marketing",
+ "scale" : "1x",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/interject/Interject!/Assets.xcassets/Background.colorset/Contents.json b/interject/Interject!/Assets.xcassets/Background.colorset/Contents.json
new file mode 100644
index 0000000..0405470
--- /dev/null
+++ b/interject/Interject!/Assets.xcassets/Background.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "display-p3",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.953",
+ "green" : "0.953",
+ "red" : "0.953"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "display-p3",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.082",
+ "green" : "0.082",
+ "red" : "0.082"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/interject/Interject!/Assets.xcassets/Contents.json b/interject/Interject!/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/interject/Interject!/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/interject/Interject!/Assets.xcassets/Foreground.colorset/Contents.json b/interject/Interject!/Assets.xcassets/Foreground.colorset/Contents.json
new file mode 100644
index 0000000..964acbe
--- /dev/null
+++ b/interject/Interject!/Assets.xcassets/Foreground.colorset/Contents.json
@@ -0,0 +1,38 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "display-p3",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.000",
+ "green" : "0.077",
+ "red" : "0.396"
+ }
+ },
+ "idiom" : "universal"
+ },
+ {
+ "appearances" : [
+ {
+ "appearance" : "luminosity",
+ "value" : "dark"
+ }
+ ],
+ "color" : {
+ "color-space" : "display-p3",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.000",
+ "green" : "0.024",
+ "red" : "1.000"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/interject/Interject!/Assets.xcassets/PrivacyInfo.dataset/Contents.json b/interject/Interject!/Assets.xcassets/PrivacyInfo.dataset/Contents.json
new file mode 100644
index 0000000..12208b1
--- /dev/null
+++ b/interject/Interject!/Assets.xcassets/PrivacyInfo.dataset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "data" : [
+ {
+ "filename" : "PrivacyInfo.xcprivacy",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/interject/Interject!/Assets.xcassets/PrivacyInfo.dataset/PrivacyInfo.xcprivacy b/interject/Interject!/Assets.xcassets/PrivacyInfo.dataset/PrivacyInfo.xcprivacy
new file mode 100644
index 0000000..18dc8ed
--- /dev/null
+++ b/interject/Interject!/Assets.xcassets/PrivacyInfo.dataset/PrivacyInfo.xcprivacy
@@ -0,0 +1,25 @@
+
+
+
+
+ NSPrivacyTracking
+
+ NSPrivacyTrackingDomains
+
+ NSPrivacyAccessedAPITypes
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryUserDefaults
+
+
+ NSPrivacyAccessedAPITypeReasons
+
+ CA92.1
+
+
+
+ NSPrivacyCollectedDataTypes
+
+
+
diff --git a/interject/Interject!/Model/Settings.swift b/interject/Interject!/Model/Settings.swift
new file mode 100644
index 0000000..e936eb2
--- /dev/null
+++ b/interject/Interject!/Model/Settings.swift
@@ -0,0 +1,157 @@
+//
+// Settings.swift
+// Interject! 2
+//
+// Created by Rory Hinnen on 2/3/18.
+// Copyright © 2018 Rory Hinnen. All rights reserved.
+//
+
+import Foundation
+import SwiftUI
+
+// MARK: enum
+enum Fonts: String, CaseIterable, CustomStringConvertible {
+ case americanTypewriter = "American Typewriter"
+ case appleGothicNeo = "Apple SD Gothic Neo"
+ case arial = "Arial"
+ case avenir = "Avenir Medium"
+ case baskerville = "Baskerville"
+ case chalkboard = "Chalkboard"
+ case chalkDuster = "Chalkduster"
+ case charter = "Charter Roman"
+ case cochin = "Cochin"
+ case copperplate = "Copperplate"
+ case courier = "Courier"
+ // case damascus = "Damascus Medium"
+ case didot = "Didot"
+ case futura = "Futura Medium"
+ case georgia = "Georgia"
+ case gill = "Gill Sans"
+ case helvetica = "Helvetica"
+ case helveticaNeue = "Helvetica Neue"
+ case menlo = "Menlo Regular"
+ case optima = "Optima Regular"
+ case palatino = "Palatino"
+ // case rockwell = "Rockwell"
+ case thonburi = "Thonburi"
+ case timesNewRoman = "Times New Roman"
+ case trebuchet = "Trebuchet MS"
+ case verdana = "Verdana"
+
+ static func random() -> Fonts {
+ return Fonts.allCases.randomElement()!
+ }
+
+ var description: String {
+ get {
+ return self.rawValue
+ }
+ }
+}
+
+enum Appearance: Int, CaseIterable, Identifiable {
+ case light, dark, automatic
+
+ var id: Int { self.rawValue }
+
+ var name: String {
+ switch self {
+ case .light: return "Light"
+ case .dark: return "Dark"
+ case .automatic: return "Automatic"
+ }
+ }
+
+ var type: ColorScheme? {
+ switch self {
+ case .light: return ColorScheme.light
+ case .dark: return ColorScheme.dark
+ case .automatic: return nil
+ }
+ }
+}
+
+extension Settings {
+
+ convenience init() {
+ self.init(
+ phrases: [Sentence()],
+ sfw: true,
+ locked: Locked(),
+ font: Fonts.random(),
+ colorScheme: Appearance.automatic)
+ handleFirstTime()
+ }
+
+ // MARK: First time
+ // Is this the first time we've run?
+ func handleFirstTime() {
+ let userDefaults = UserDefaults.standard
+ let firstTime = userDefaults.bool(forKey: "FirstTime")
+ sfw = userDefaults.bool(forKey: "SFW")
+
+ if firstTime {
+ // Yes, it's the first time, so lets load up a new sentence
+ phrases = [new(current: Sentence())]
+ } else {
+ // Nope, we've been here before, let's get our history
+ load()
+ if phrases.first!.word(.first, safe: true) == "" {
+ phrases = [new(current: Sentence())]
+ }
+ }
+ //Ok, let's mark it so next time we know it's not the first time
+ userDefaults.set(false, forKey: "FirstTime")
+ userDefaults.synchronize()
+ }
+
+ // MARK: Save Utilities
+ // These are some untility funtions to make saving variables long term easier
+ // Find a place to store files for later reading
+ func documentsDirectory() -> URL {
+ let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
+ return paths[0]
+ }
+
+ // Let's name the path of the save file
+ func dataFilePath() -> URL {
+ return documentsDirectory().appendingPathComponent("Interject.plist")
+ }
+
+ // Let's save the history of Sentences we've seen, in case we need them later
+ func save() {
+ let encoder = PropertyListEncoder()
+
+ do {
+ let data = try encoder.encode(phrases)
+ try data.write(to: dataFilePath(), options: Data.WritingOptions.atomic)
+ } catch {
+ print("Error encoding item array!")
+ }
+ }
+
+ //MARK: Load Utilities
+ // Let's load the history of Sentences we've seen.
+ func load() {
+ let path = dataFilePath()
+
+ if let data = try? Data(contentsOf: path) {
+ let decoder = PropertyListDecoder()
+ do {
+ phrases = try decoder.decode([Sentence].self, from: data)
+ } catch {
+ print("Error decoding item array!")
+ }
+ }
+ }
+
+ // MARK: Defaults
+ // Let's set up our default User Vars
+ func registerDefaults() {
+ let dictionary: [String:Any] = ["FirstTime": true, "SFW": true, "font": "none" ]
+ UserDefaults.standard.register(defaults: dictionary)
+ }
+}
+
+
+
diff --git a/interject/Interject!/Model/Store.swift b/interject/Interject!/Model/Store.swift
new file mode 100644
index 0000000..af0cefb
--- /dev/null
+++ b/interject/Interject!/Model/Store.swift
@@ -0,0 +1,200 @@
+/*
+Abstract:
+The store class is responsible for requesting products from the App Store and starting purchases.
+*/
+
+import Foundation
+import StoreKit
+
+typealias Transaction = StoreKit.Transaction
+typealias RenewalInfo = StoreKit.Product.SubscriptionInfo.RenewalInfo
+typealias RenewalState = StoreKit.Product.SubscriptionInfo.RenewalState
+
+public enum StoreError: Error {
+ case failedVerification
+}
+
+class Store: ObservableObject {
+
+ @Published private(set) var tips: [Product]
+
+ @Published private(set) var purchasedTips: [Product] = []
+
+ var updateListenerTask: Task? = nil
+
+ private let productIdToEmoji: [String: String]
+
+ init() {
+ productIdToEmoji = Store.loadProductIdToEmojiData()
+
+ //Initialize empty products, and then do a product request asynchronously to fill them in.
+ tips = []
+
+ //Start a transaction listener as close to app launch as possible so you don't miss any transactions.
+ updateListenerTask = listenForTransactions()
+
+ Task {
+ //During store initialization, request products from the App Store.
+ await requestProducts()
+
+ //Deliver products that the customer purchases.
+ await updateCustomerProductStatus()
+ }
+ }
+
+ deinit {
+ updateListenerTask?.cancel()
+ }
+
+ static func loadProductIdToEmojiData() -> [String: String] {
+ guard let path = Bundle.main.path(forResource: "Products", ofType: "plist"),
+ let plist = FileManager.default.contents(atPath: path),
+ let data = try? PropertyListSerialization.propertyList(from: plist, format: nil) as? [String: String] else {
+ return [:]
+ }
+ return data
+ }
+
+ func listenForTransactions() -> Task {
+ return Task.detached {
+ //Iterate through any transactions that don't come from a direct call to `purchase()`.
+ for await result in Transaction.updates {
+ do {
+ let transaction = try self.checkVerified(result)
+
+ //Deliver products to the user.
+ await self.updateCustomerProductStatus()
+
+ //Always finish a transaction.
+ await transaction.finish()
+ } catch {
+ //StoreKit has a transaction that fails verification. Don't deliver content to the user.
+ print("Transaction failed verification")
+ }
+ }
+ }
+ }
+
+ @MainActor
+ func requestProducts() async {
+ do {
+ //Request products from the App Store using the identifiers that the Products.plist file defines.
+ let storeProducts = try await Product.products(for: productIdToEmoji.keys)
+
+ var newTips: [Product] = []
+
+ //Filter the products into categories based on their type.
+ for product in storeProducts {
+ switch product.type {
+ case .consumable:
+ break
+ case .nonConsumable:
+ newTips.append(product)
+ case .autoRenewable:
+ break
+ case .nonRenewable:
+ break
+ default:
+ //Ignore this product.
+ print("Unknown product")
+ }
+ }
+
+ //Sort each product category by price, lowest to highest, to update the store.
+ tips = sortByPrice(newTips)
+ } catch {
+ print("Failed product request from the App Store server: \(error)")
+ }
+ }
+
+ func purchase(_ product: Product) async throws -> Transaction? {
+ //Begin purchasing the `Product` the user selects.
+ let result = try await product.purchase()
+
+ switch result {
+ case .success(let verification):
+ //Check whether the transaction is verified. If it isn't,
+ //this function rethrows the verification error.
+ let transaction = try checkVerified(verification)
+
+ //The transaction is verified. Deliver content to the user.
+ await updateCustomerProductStatus()
+
+ //Always finish a transaction.
+ await transaction.finish()
+
+ return transaction
+ case .userCancelled, .pending:
+ return nil
+ default:
+ return nil
+ }
+ }
+
+ func isPurchased(_ product: Product) async throws -> Bool {
+ //Determine whether the user purchases a given product.
+ switch product.type {
+ case .nonRenewable:
+ break
+ case .nonConsumable:
+ return purchasedTips.contains(product)
+ case .autoRenewable:
+ break
+ default:
+ return false
+ }
+ return true
+ }
+
+ func checkVerified(_ result: VerificationResult) throws -> T {
+ //Check whether the JWS passes StoreKit verification.
+ switch result {
+ case .unverified:
+ //StoreKit parses the JWS, but it fails verification.
+ throw StoreError.failedVerification
+ case .verified(let safe):
+ //The result is verified. Return the unwrapped value.
+ return safe
+ }
+ }
+
+ @MainActor
+ func updateCustomerProductStatus() async {
+ var purchasedTips: [Product] = []
+
+ //Iterate through all of the user's purchased products.
+ for await result in Transaction.currentEntitlements {
+ do {
+ //Check whether the transaction is verified. If it isn’t, catch `failedVerification` error.
+ let transaction = try checkVerified(result)
+
+ //Check the `productType` of the transaction and get the corresponding product from the store.
+ switch transaction.productType {
+ case .nonConsumable:
+ if let tip = tips.first(where: { $0.id == transaction.productID }) {
+ purchasedTips.append(tip)
+ }
+ case .nonRenewable:
+ break
+ case .autoRenewable:
+ break
+ default:
+ break
+ }
+ } catch {
+ print()
+ }
+ }
+
+ //Update the store information with the purchased products.
+ self.purchasedTips = purchasedTips
+ }
+
+ func emoji(for productId: String) -> String {
+ return productIdToEmoji[productId]!
+ }
+
+ func sortByPrice(_ products: [Product]) -> [Product] {
+ products.sorted(by: { return $0.price < $1.price })
+ }
+}
diff --git a/interject/Interject!/Model/Words.swift b/interject/Interject!/Model/Words.swift
new file mode 100644
index 0000000..de291b0
--- /dev/null
+++ b/interject/Interject!/Model/Words.swift
@@ -0,0 +1,508 @@
+//
+// File.swift
+// Interject!
+//
+// Created by Rory Hinnen on 12/2/18.
+// Copyright © 2018 Rory Hinnen. All rights reserved.
+//
+import Foundation
+import SwiftUI
+
+// MARK: enum
+enum Place {
+ case first, second, noun
+
+ func size() -> CGFloat {
+ switch self {
+ case .first:
+ return 35.0
+ case .second:
+ return 55.0
+ case .noun:
+ return 75.0
+ }
+ }
+}
+
+struct Locked {
+ var adjective1: Bool = false
+ var adjective2: Bool = false
+ var noun: Bool = false
+
+ mutating func toggle(_ place: Place) {
+ switch(place) {
+ case .first:
+ self.adjective1.toggle()
+ case .second:
+ self.adjective2.toggle()
+ case .noun:
+ self.noun.toggle()
+ }
+ }
+
+ func isLocked(_ which: Place) -> Bool {
+ switch(which) {
+ case .first:
+ return self.adjective1
+ case .second:
+ return self.adjective2
+ case .noun:
+ return self.noun
+ }
+ }
+}
+
+/* func newSentence(current: Sentence) -> Sentence {
+ // find adjective one, which could be from the pool of first words or adjectives.
+ // remove the current words from the list of possible words
+ // pick from the list for just one word.
+ let firstList = Sentence.first + Sentence.adjectives
+ let adj1List = firstList.filter({$0.word(true) != current.adjective1.word(true)}).filter({$0.word(true) != current.adjective2.word(true)})
+ let firstAdj = adj1List[Int.random(in: 0...adj1List.count-1)]
+
+ // find adjective two, only from the pool of adjectives
+ // remove current words and adjective one from the pool
+ // pick from the list for just one word
+ let adj2List = Sentence.adjectives.filter({$0.word(true) != current.adjective1.word(true)}).filter({$0.word(true) != current.adjective2.word(true)}).filter({$0.word(true) != firstAdj.word(true)})
+ let adjective2 = adj2List[Int.random(in: 0...adj2List.count-1)]
+
+ // find the noun, only from the pool of nouns
+ // remove the current word from the pool
+ // select the new noun.
+ let nounList = Sentence.nouns.filter({$0.word(true) != current.noun.word(true)})
+ let noun = nounList[Int.random(in: 0...nounList.count-1)]
+
+ // make a new sentence
+ return(Sentence(adjective1: firstAdj, adjective2: adjective2, noun: noun))
+} */
+
+class Settings: ObservableObject {
+ @Published var phrases: [Sentence]
+ @Published var sfw: Bool
+ @Published var locked: Locked
+ @Published var font: Fonts
+ @Published var colorScheme: Appearance
+
+ init(phrases: [Sentence], sfw: Bool, locked: Locked, font: Fonts, colorScheme: Appearance) {
+ self.phrases = phrases
+ self.sfw = sfw
+ self.locked = locked
+ self.font = font
+ self.colorScheme = colorScheme
+ }
+
+ func add() {
+ let maxPhrase = 100
+ let newPhrase = new(current: self.phrases.first!)
+ if self.locked.adjective1 { newPhrase.adjective1 = self.phrases.first!.adjective1 }
+ if self.locked.adjective2 { newPhrase.adjective2 = self.phrases.first!.adjective2}
+ if self.locked.noun { newPhrase.noun = self.phrases.first!.noun }
+ self.phrases.insert(newPhrase, at: 0)
+ if self.phrases.count > maxPhrase {
+ self.phrases.remove(at: self.phrases.count-1)
+ }
+ save()
+ }
+
+ func remove() {
+ if self.phrases.count > 1 {
+ if self.locked.adjective1 {self.phrases[1].adjective1 = self.phrases[0].adjective1}
+ if self.locked.adjective2 {self.phrases[1].adjective2 = self.phrases[0].adjective2}
+ if self.locked.noun {self.phrases[1].noun = self.phrases[0].noun}
+ self.phrases.remove(at: 0)
+ }
+ save()
+ }
+
+ func new(current: Sentence) -> Sentence {
+ // find adjective one, which could be from the pool of first words or adjectives.
+ // remove the current words from the list of possible words
+ // pick from the list for just one word.
+ let firstList = Sentence.first + Sentence.adjectives
+ let adj1List = firstList.filter({$0.word(true)
+ != current.adjective1.word(true)}).filter({$0.word(true)
+ != current.adjective2.word(true)})
+ let firstAdj = adj1List[Int.random(in: 0...adj1List.count-1)]
+
+ // find adjective two, only from the pool of adjectives
+ // remove current words and adjective one from the pool
+ // pick from the list for just one word
+ let adj2List = Sentence.adjectives.filter({$0.word(true)
+ != current.adjective1.word(true)}).filter({$0.word(true)
+ != current.adjective2.word(true)}).filter({$0.word(true)
+ != firstAdj.word(true)})
+ let adjective2 = adj2List[Int.random(in: 0...adj2List.count-1)]
+
+ // find the noun, only from the pool of nouns
+ // remove the current word from the pool
+ // select the new noun.
+ let nounList = Sentence.nouns.filter({$0.word(true)
+ != current.noun.word(true)})
+ let noun = nounList[Int.random(in: 0...nounList.count-1)]
+
+ // make a new sentence
+ return(Sentence(adjective1: firstAdj, adjective2: adjective2, noun: noun))
+ }
+}
+
+// MARK: Entry
+struct Entry: Codable {
+ // The structure of the words themself.
+ // a word could be sfw or nsfw,
+ // and meanings may also be sfw or nsfw
+ // The words are private, and if you query Entry, you will need to
+ // provide a bool. The return will be the appropriate word or meaning.
+ fileprivate var nsfw, nsfwMeaning: String?
+ fileprivate var word, meaning: String
+
+ init(nsfw: String?, nsfwMeaning: String?, word: String, meaning: String) {
+ self.nsfw = nsfw
+ self.nsfwMeaning = nsfwMeaning
+ self.word = word
+ self.meaning = meaning
+ }
+
+ init() {
+ self.nsfw = nil
+ self.nsfwMeaning = nil
+ self.word = ""
+ self.meaning = ""
+ }
+
+ func word(_ isSfw: Bool) -> String {
+ return isSfw ? word : nsfw ?? word
+ }
+
+ func definition(_ isSfw: Bool) -> String {
+ return isSfw ? meaning : nsfwMeaning ?? meaning
+ }
+}
+
+// MARK: Sentence
+class Sentence: NSObject, Codable {
+ // The sentence. This is our primary structure.
+ // perhaps I should include Entry as part of this, rather than
+ // a selfcontained structure, but there are times when I want to
+ // hand around just an entry without the extra words
+ // (like when I'm handing off to meaning)
+ // There are two initializers.
+
+ fileprivate var adjective1: Entry
+ fileprivate var adjective2: Entry
+ fileprivate var noun: Entry
+
+ // This initializer will take three Entry vars and assign them into a sentence
+ init(adjective1: Entry, adjective2: Entry, noun: Entry) {
+ self.adjective1 = adjective1
+ self.adjective2 = adjective2
+ self.noun = noun
+ }
+
+ // This initializer will make an empty sentence
+ override init() {
+ adjective1 = Entry()
+ adjective2 = Entry()
+ noun = Entry()
+ }
+
+ func new(current: Sentence) -> Sentence {
+ // find adjective one, which could be from the pool of first words or adjectives.
+ // remove the current words from the list of possible words
+ // pick from the list for just one word.
+ let firstList = Sentence.first + Sentence.adjectives
+ let adj1List = firstList.filter({$0.word(true)
+ != current.adjective1.word(true)}).filter({$0.word(true)
+ != current.adjective2.word(true)})
+ let firstAdj = adj1List[Int.random(in: 0...adj1List.count-1)]
+
+ // find adjective two, only from the pool of adjectives
+ // remove current words and adjective one from the pool
+ // pick from the list for just one word
+ let adj2List = Sentence.adjectives.filter({$0.word(true)
+ != current.adjective1.word(true)}).filter({$0.word(true)
+ != current.adjective2.word(true)}).filter({$0.word(true)
+ != firstAdj.word(true)})
+ let adjective2 = adj2List[Int.random(in: 0...adj2List.count-1)]
+
+ // find the noun, only from the pool of nouns
+ // remove the current word from the pool
+ // select the new noun.
+ let nounList = Sentence.nouns.filter({$0.word(true)
+ != current.noun.word(true)})
+ let noun = nounList[Int.random(in: 0...nounList.count-1)]
+
+ // make a new sentence
+ return(Sentence(adjective1: firstAdj, adjective2: adjective2, noun: noun))
+ }
+
+ func word(_ place: Place, safe: Bool) -> String {
+ switch place {
+ case .first:
+ return adjective1.word(safe)
+ case .second:
+ return adjective2.word(safe)
+ case .noun:
+ return noun.word(safe)
+ }
+ }
+ func definition(_ place: Place, safe: Bool) -> String {
+ switch place {
+ case .first:
+ return adjective1.definition(safe)
+ case .second:
+ return adjective2.definition(safe)
+ case .noun:
+ return noun.definition(safe)
+ }
+
+ }
+}
+
+extension Sentence {
+ // MARK: Create the pool of words
+ // first are words that will only be in adjective one. There should be a better
+ // algorithm for sorting these, that will come soon.
+ // adjectives are words that can be in adjective one or two.
+ // nouns are words that can only be a noun
+ static var first = [
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "absolute", meaning: "The most extreme example"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "complete", meaning: "The most extreme example"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "utter", meaning: "The most extreme example")
+ ]
+
+ static var adjectives = [
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "artless", meaning: "Without craft or intelligence"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "base-court", meaning: "From the lower English court, common"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "bat-fowling", meaning: "An archaic method of bird hunting, like fishing from a barrel"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "bawdy", meaning: "Humorously indecent"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "bawheeded", meaning: "A person who speaks nonsense"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "beef-witted", meaning: "Brainless, a meat head"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "beetle-headed", meaning: "Witless"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "beslubbering", meaning: "Drooling"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "bloomin’", meaning: "For emphasis"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "boil-brained", meaning: "Filled with thoughts of pestilence and misery"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "bootless", meaning: "Common, or poor"),
+ Entry(nsfw: "pussy grabbing", nsfwMeaning: "A conman", word: "cat grabbing", meaning: "A man who respects women, and is always concerned for their well being"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "churlish", meaning: "Intentionally rude"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "clapper-clawed", meaning: "Brutish, all hands"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "clarted", meaning: "Mud covered"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "clatty", meaning: "Dirty, slovely, cluttered"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "clay-brained", meaning: "Rock head"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "clouted", meaning: "Senseless, reeling from being beat"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "cockered", meaning: "Spoiled"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "common-kissing", meaning: "Rogue, a rascal, grabs others without permission"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "craven", meaning: "Cowardly"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "crook-pated", meaning: "A thug, a thief who takes by violence"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "currish", meaning: "Like a feral dog, wild and snappish"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "daft", meaning: "Silly, foolish"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "dankish", meaning: "Dark, damp, humid"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "dismal-dreaming", meaning: "A person with foul and depressing dreams or goals"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "dissembling", meaning: "A rambling liar"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "dizzy-eyed", meaning: "Easily distracted"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "doaty", meaning: "A malingerer, one who obsesses over unimportant things"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "doddering", meaning: "Someone who rambles on, complaining of witchhunts and fake news"),
+ Entry(nsfw: "dog fucking", nsfwMeaning: nil, word: "dog loving", meaning: "Low standards"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "doghearted", meaning: "Inhuman, cruel"),
+ Entry(nsfw: "knobdobbing", nsfwMeaning: nil, word: "door knobbing", meaning: "Dickish"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "dread-bolted", meaning: "Frightning, terrifying"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "droning", meaning: "One who goes on and on, monotonous, boring and obsessive"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "earth-vexing", meaning: "Causes pain to all around"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "elf-skinned", meaning: "Shrunken, tiny, a mere nothing"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "errant", meaning: "One who wanders from what is right"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "fat-kidneyed", meaning: "Gross, clumsy"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "fawning", meaning: "Obsequious"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "fen-sucked", meaning: "Rising from the marshes"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "fly-bitten", meaning: "Marked by fly bites, wild and unkempt"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "fobbing", meaning: "A con man, a cheat"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "folly-fallen", meaning: "Stooping to foolishness"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "fool-born", meaning: "A second-generation fool"),
+ Entry(nsfw: "fucking", nsfwMeaning: "An old germanic word", word: "freaking", meaning: "A bad word"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "frothy", meaning: "Of little substance, foolish"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "froward", meaning: "A contrary person, difficult to deal with"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "fuddy", meaning: "Stuffy and old fashioned"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "full-gorged", meaning: "Well fed"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "glaikit", meaning: "Foolish, giddy"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "glakit", meaning: "Unsightly and stupid"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "gleeking", meaning: "Spitting"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "goatish", meaning: "Goatlike in manners"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "gommy", meaning: "Idiotic, mammering"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "gorbellied", meaning: "Corpulent"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "gormless", meaning: "Stupid or dull, lacking in gorm"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "guts-griping", meaning: "Belly aching"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "hackit", meaning: "Ugly"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "half-faced", meaning: "Imperfect, incomplete, defective"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "hasty-witted", meaning: "Shallow"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "hedge-born", meaning: "Of low birth"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "hell-hated", meaning: "Even despised by the Devil"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "howlin’", meaning: "Complaining, waling"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "idle-headed", meaning: "Stupid"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "ill-bred", meaning: "Lacking class"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "ill-nurtured", meaning: "Ill bred, raised common"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "impertinent", meaning: "Not respectful, rude"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "infectious", meaning: "Likely to spread infection"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "jakey", meaning: "A drunken tramp"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "jarring", meaning: "Incongruous, ill fitting"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "knotty-pated", meaning: "Easily distracted"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "lavvy-heided", meaning: "Toilet headed"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "loggerheaded", meaning: "Block headed"),
+ Entry(nsfw: nil, nsfwMeaning: "Orange, and pocked", word: "loofa-faced", meaning: "Pale and pocked"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "lumpish", meaning: "Roughly or crudely formed"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "malodorous", meaning: "Bad smelling"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "mammering", meaning: "To mumble endlessly without point"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "mangled", meaning: "Disfigured"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "mewling", meaning: "Whimpering"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "milk-livered", meaning: "Gutless, cowardly"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "mingin’", meaning: "Foul smelling"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "motley minded", meaning: "Foolish"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "naffy", meaning: "Self centered fop"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "onion-eyed", meaning: "One so ugly they make your eyes water"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "paunchy", meaning: "Pot bellied"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "pencil-necked", meaning: "Skinny, useless, annoying, weak"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "plume-plucked", meaning: "Humbled"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "pottle-deep", meaning: "Drunken, one who has emptied the dregs of his drink"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "pox-marked", meaning: "Pitted skin, marked with pox"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "pribbling", meaning: "A quibbler who argues just to argue"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "puking", meaning: "Nauseating"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "puny", meaning: "Small and weak"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "qualling", meaning: "Quail like, cowardly"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "rank", meaning: "Low or vile, stinking"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "reeky", meaning: "Stinky"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "reeling-ripe", meaning: "Smelly"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "roguish", meaning: "Loutish, and not in a good way"),
+ Entry(nsfw: "cock-juggling", nsfwMeaning: "A promiscous person", word: "rooster juggling", meaning: "A fowl manager"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "rude-growing", meaning: "Rough and wild"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "rump-fed", meaning: "Pampered"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "ruttish", meaning: "Salacious, lascivious"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "saucy", meaning: "Lascivious, salacious"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "scabby", meaning: "Covered in scars, ugly"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "shard borne", meaning: "Born in dung"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "sheep-biting", meaning: "A person who preys on the defenseless"),
+ Entry(nsfw: nil, nsfwMeaning: "Refering to the size of one's third leg", word: "small-handed", meaning: "An observation regarding one's manliness"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "spleeny", meaning: "Having a lot of spleen"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "spongy", meaning: "Soft headed"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "spur-galled", meaning: "Ridden hard, put away wet, worn out"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "straw-headed", meaning: "A numpty"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "surly", meaning: "One of the dwarfs"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "swag-bellied", meaning: "Well fed, a golf course owner"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "swollen", meaning: "Distended, fat, puffy"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "tardy-gaited", meaning: "Sluggish"),
+ Entry(nsfw: nil, nsfwMeaning: "As reported by Stormy Daniels", word: "three-inched", meaning: "An observation regarding one's manliness"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "tickle-brained", meaning: "Flighty, unreliable, probably a drunk"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "toad-spotted", meaning: "Spotted like a toad"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "toffee-nosed", meaning: "Having a soft, brown nose"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "tottering", meaning: "Doddering"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "unchin-snouted", meaning: "A face like a hedgehog"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "unmuzzled", meaning: "Unrestrained, obnoxiously capricious"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "vacuous", meaning: "Empty headed"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "vain", meaning: "Self obsessed"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "venomed", meaning: "Poisonous"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "villianous", meaning: "Evil"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "warped", meaning: "Bent, bad"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "wayward", meaning: "Errant"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "weapons-grade", meaning: "The most extreme example of"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "weather-bitten", meaning: "Scarred by a life outside"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "weedy", meaning: "Thin"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "witless", meaning: "Uhh.."),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "yeasty", meaning: "Pale and soft")
+ ]
+
+ static var nouns = [
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "apple john", meaning: "Withered"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "arse", meaning: "That thing you sit on"),
+ Entry(nsfw: "cocksplat", nsfwMeaning: "The money shot", word: "babysplat" , meaning: "Everywhere. Just everywhere"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "baggage", meaning: "A person you would rather not carry"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "bampot", meaning: "An idiot"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "barnacle", meaning: "A clingy person"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "bassa", meaning: "Bastard"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "bawbag", meaning: "An annoying person, a sack"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "bladder", meaning: "A bag of urine"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "boaby", meaning: "Penis"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "boar-pig", meaning: "A pig"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "bugbear", meaning: "A hobgoblin bogeyman"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "bull’s pizzle", meaning: "Pizzle from a bull"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "bum-ailey", meaning: "A bad cop"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "canker blossom", meaning: "A common person"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "clack-dish", meaning: "A beggars bowl"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "clawbaw", meaning: "A person who keeps their hands in thier pockets"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "clotpole", meaning: "A foolish person"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "cockwomble", meaning: "A foolish or obnoxious person"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "codpiece", meaning: "A flap of cloth covering one’s cod"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "coxcomb", meaning: "A dandy, vain and conceited"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "death-token", meaning: "A sign of impending death"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "dewberry", meaning: "A species of berry"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "diddy", meaning: "A tit"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "dobber", meaning: "A tool, a member"),
+ Entry(nsfw: "dog-fucker", nsfwMeaning: "A classless heel", word: "dog-lover", meaning: "An individual of low standards"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "doiley", meaning: "A foolish old person"),
+ Entry(nsfw: "dickweed", nsfwMeaning: nil, word: "dorkweed", meaning: "Not a swearword"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "dottard", meaning: "A decript senior as identified by the Leader of North Korea"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "doughnut", meaning: "A person with a hole in their head, witless"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "eejit", meaning: "An idiot"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "fannybaws", meaning: "A twat"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "flap-dragon", meaning: "A raisin floating in a lit glass of rum"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "flax-wench", meaning: "A prostitute"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "flirt gill", meaning: "A wanton person"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "foot-licker", meaning: "A syncophant"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "fud", meaning: "Female genitalia, but more likely a person who does blindingly stupid things"),
+ Entry(nsfw: "fucknugget", nsfwMeaning: "The consequences of sex", word: "fudge-nugget", meaning: "Mmmm. Fudge"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "fustilarian", meaning: "A scoundrel, but not a space smuggler"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "geek", meaning: "Not athletic, a programmer"),
+ Entry(nsfw: "shit-gibbon", nsfwMeaning: "A monkey who flings shit", word: "gibbon", meaning: "A monkey"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "giglet", meaning: "A lascivious girl"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "gobshite", meaning: "A natterer, one who doesn’t know how to shut"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "gowk", meaning: "An awkward or foolish person"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "gudgeon", meaning: "A foolish person, gullible"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "haggard", meaning: "A worn out person"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "harpy", meaning: "Virginia Woolf"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "hedge-pig", meaning: "Hedgehog, prickly"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "horn-beast", meaning: "Easily enraged, bestial"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "hugger mugger", meaning: "A pretender"),
+ Entry(nsfw: "jizztrumpet", nsfwMeaning: "Someone who plays a trumpet of jizz", word: "jazztrumpet", meaning: "Not Miles Davis"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "jobby", meaning: "Feces, junk"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "joithead", meaning: "A hothead"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "lewdster", meaning: "A lecher, a lewd person"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "lickspittle", meaning: "A weak-willed flatterer"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "lout", meaning: "An uncouth, aggressive man"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "maggot-pie", meaning: "A pie of maggots"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "malt-worm", meaning: "A drunkard"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "mammet", meaning: "A scarecrow"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "measle", meaning: "A leper"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "midden", meaning: "A trash heap, a dung hill"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "minnow", meaning: "A small fish in a big pond"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "miscreant", meaning: "A wicked, sinful person"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "moldwarp", meaning: "A mud thrower, a politician"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "moron", meaning: "Intentionally ignorant"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "mumble-news", meaning: "A gossiper, a tattler"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "nimrod", meaning: "A mighty hunter. Not what you expected, is it?"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "numpty", meaning: "A stupid, useless person"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "nut-hook", meaning: "A constable, an officer"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "nyaff", meaning: "A stupid, irritating person"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "pervert", meaning: "Deviant sexual standards"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "pigeon-egg", meaning: "A small useless thing"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "pignut", meaning: "An ugly tuber"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "plamf", meaning: "An underwear sniffer"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "plum", meaning: "A person who says or does stupid things"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "pumpion", meaning: "A person who makes a fool of themselves"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "puttock", meaning: "A greedy, rapacious person"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "ratsbane", meaning: "Rat poisioner"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "roaster", meaning: "A person who makes a fool of themselves"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "rocket", meaning: "An idiot"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "scunner", meaning: "Disgusting, unlikeable"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "scut", meaning: "A foolish or contemptible person"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "skainsmate", meaning: "A shipboard companion"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "strumpet", meaning: "A promiscuous person, a prostitute"),
+ Entry(nsfw: "thundercunt", nsfwMeaning:"Queefing genitalia", word: "thundercat", meaning: "A cartoon from the 70's"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "tube", meaning: "The cave where you conquer the fear"),
+ Entry(nsfw: "shit-tobogganist", nsfwMeaning: "A person who creates chaos just because they fucking love it", word: "turd-tobogganist", meaning: "A person who likes to create discord"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "twally-washer", meaning: "Someone who cleans twallies orally"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "twit", meaning: "A annoying, stupid person. Oliver St. John-Mollusc"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "twonk", meaning: "A stupid person"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "varlot", meaning: "A punk"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "vassal", meaning: "A subordinant, servile"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "wagtail", meaning: "A syncophant"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "walloper", meaning: "A male member"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "whalluper", meaning: "A male member"),
+ Entry(nsfw: nil, nsfwMeaning: nil, word: "whey-face", meaning: "Pale of face, sickly")
+ ]
+}
+
diff --git a/interject/Interject!/Preview Content/Preview Assets.xcassets/Contents.json b/interject/Interject!/Preview Content/Preview Assets.xcassets/Contents.json
new file mode 100644
index 0000000..73c0059
--- /dev/null
+++ b/interject/Interject!/Preview Content/Preview Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/interject/Interject!/PrivacyInfo.xcprivacy b/interject/Interject!/PrivacyInfo.xcprivacy
new file mode 100644
index 0000000..79bc9e2
--- /dev/null
+++ b/interject/Interject!/PrivacyInfo.xcprivacy
@@ -0,0 +1,17 @@
+
+
+
+
+ NSPrivacyAccessedAPITypes
+
+
+ NSPrivacyAccessedAPIType
+ NSPrivacyAccessedAPICategoryUserDefaults
+ NSPrivacyAccessedAPITypeReasons
+
+ CA92.1
+
+
+
+
+
diff --git a/interject/Interject!/Products.plist b/interject/Interject!/Products.plist
new file mode 100644
index 0000000..9627d38
--- /dev/null
+++ b/interject/Interject!/Products.plist
@@ -0,0 +1,10 @@
+
+
+
+
+ bigTip
+ 💵
+ smallTip
+ 🪙
+
+
diff --git a/interject/Interject!/Products.storekit b/interject/Interject!/Products.storekit
new file mode 100644
index 0000000..37a2d98
--- /dev/null
+++ b/interject/Interject!/Products.storekit
@@ -0,0 +1,97 @@
+{
+ "identifier" : "BDE0506D",
+ "nonRenewingSubscriptions" : [
+
+ ],
+ "products" : [
+ {
+ "displayPrice" : "0.99",
+ "familyShareable" : true,
+ "internalID" : "B5B65148",
+ "localizations" : [
+ {
+ "description" : "A little encouragement",
+ "displayName" : "little TIP",
+ "locale" : "en_US"
+ }
+ ],
+ "productID" : "smallTip",
+ "referenceName" : "smallTip",
+ "type" : "NonConsumable"
+ },
+ {
+ "displayPrice" : "4.99",
+ "familyShareable" : true,
+ "internalID" : "F22AF47F",
+ "localizations" : [
+ {
+ "description" : "A lot of encouragement",
+ "displayName" : "BIG tip",
+ "locale" : "en_US"
+ }
+ ],
+ "productID" : "bigTip",
+ "referenceName" : "bigTip",
+ "type" : "NonConsumable"
+ }
+ ],
+ "settings" : {
+ "_failTransactionsEnabled" : false,
+ "_locale" : "en_US",
+ "_storefront" : "USA",
+ "_storeKitErrors" : [
+ {
+ "current" : null,
+ "enabled" : false,
+ "name" : "Load Products"
+ },
+ {
+ "current" : null,
+ "enabled" : false,
+ "name" : "Purchase"
+ },
+ {
+ "current" : null,
+ "enabled" : false,
+ "name" : "Verification"
+ },
+ {
+ "current" : null,
+ "enabled" : false,
+ "name" : "App Store Sync"
+ },
+ {
+ "current" : null,
+ "enabled" : false,
+ "name" : "Subscription Status"
+ },
+ {
+ "current" : null,
+ "enabled" : false,
+ "name" : "App Transaction"
+ },
+ {
+ "current" : null,
+ "enabled" : false,
+ "name" : "Manage Subscriptions Sheet"
+ },
+ {
+ "current" : null,
+ "enabled" : false,
+ "name" : "Refund Request Sheet"
+ },
+ {
+ "current" : null,
+ "enabled" : false,
+ "name" : "Offer Code Redeem Sheet"
+ }
+ ]
+ },
+ "subscriptionGroups" : [
+
+ ],
+ "version" : {
+ "major" : 3,
+ "minor" : 0
+ }
+}
diff --git a/interject/Interject!/Utils/Utils.swift b/interject/Interject!/Utils/Utils.swift
new file mode 100644
index 0000000..44fc4c4
--- /dev/null
+++ b/interject/Interject!/Utils/Utils.swift
@@ -0,0 +1,62 @@
+//
+// Utils.swift
+// Interject!
+//
+// Created by Rory Hinnen on 22/6/20.
+//
+
+import Foundation
+import SwiftUI
+
+extension DragGesture.Value {
+ enum Direction {
+ case left
+ case right
+ case up
+ case down
+ case tap
+ }
+
+ func detectDirection(_ tolerance: Double = 12) -> Direction? {
+ if startLocation.x < location.x - tolerance * 6 { return .left }
+ if startLocation.x > location.x + tolerance * 6 { return .right }
+ if startLocation.y > location.y + tolerance { return .up }
+ if startLocation.y < location.y - tolerance { return .down }
+ return .tap
+ }
+}
+
+extension String {
+ func capitalizingFirstLetter() -> String {
+ return prefix(1).uppercased() + self.lowercased().dropFirst()
+ }
+
+ mutating func capitalizeFirstLetter() {
+ self = self.capitalizingFirstLetter()
+ }
+}
+
+extension CaseIterable where Self: Equatable {
+ mutating func next() {
+ let allCases = Self.allCases
+ // just a sanity check, as the possibility of a enum case to not be
+ // present in `allCases` is quite low
+ guard let selfIndex = allCases.firstIndex(of: self) else { return }
+ let nextIndex = Self.allCases.index(after: selfIndex)
+ self = allCases[nextIndex == allCases.endIndex ? allCases.startIndex : nextIndex]
+ }
+}
+
+public extension UIApplication {
+ func currentUIWindow() -> UIWindow? {
+ let connectedScenes = UIApplication.shared.connectedScenes
+ .filter { $0.activationState == .foregroundActive }
+ .compactMap { $0 as? UIWindowScene }
+
+ let window = connectedScenes.first?
+ .windows
+ .first { $0.isKeyWindow }
+
+ return window
+ }
+}
diff --git a/interject/Interject!/View/BuyButtonStyle.swift b/interject/Interject!/View/BuyButtonStyle.swift
new file mode 100644
index 0000000..4516932
--- /dev/null
+++ b/interject/Interject!/View/BuyButtonStyle.swift
@@ -0,0 +1,50 @@
+/*
+See LICENSE folder for this sample’s licensing information.
+
+Abstract:
+The style of the product buttons that display in the store view.
+*/
+
+import StoreKit
+import SwiftUI
+
+struct BuyButtonStyle: ButtonStyle {
+ let isPurchased: Bool
+
+ init(isPurchased: Bool = false) {
+ self.isPurchased = isPurchased
+ }
+
+ func makeBody(configuration: Self.Configuration) -> some View {
+ var bgColor: Color = isPurchased ? Color.green : Color.blue
+ bgColor = configuration.isPressed ? bgColor.opacity(0.7) : bgColor.opacity(1)
+
+ return configuration.label
+ .frame(width: 100)
+ .padding(10)
+ .background(bgColor)
+ .clipShape(RoundedRectangle(cornerRadius: 20, style: .continuous))
+ .scaleEffect(configuration.isPressed ? 0.9 : 1.0)
+ }
+}
+
+struct BuyButtonStyle_Previews: PreviewProvider {
+ static var previews: some View {
+ Group {
+ Button(action: {}) {
+ Text("Buy")
+ .foregroundColor(.white)
+ .bold()
+ }
+ .buttonStyle(BuyButtonStyle())
+ .previewDisplayName("Normal")
+
+ Button(action: {}) {
+ Image(systemName: "checkmark")
+ .foregroundColor(.white)
+ }
+ .buttonStyle(BuyButtonStyle(isPurchased: true))
+ .previewDisplayName("Purchased")
+ }
+ }
+}
diff --git a/interject/Interject!/View/ContentView.swift b/interject/Interject!/View/ContentView.swift
new file mode 100644
index 0000000..08ea7e9
--- /dev/null
+++ b/interject/Interject!/View/ContentView.swift
@@ -0,0 +1,60 @@
+//
+// ContentView.swift
+// Interject!
+//
+// Created by Rory Hinnen on 21/11/18.
+//
+
+import SwiftUI
+
+struct ContentView: View {
+ @EnvironmentObject var settings: Settings/Users/roryhinnen/Downloads/Dungeon Project
+ @EnvironmentObject var store: Store
+ @State var displaySettingsView: Bool = false
+ @State var preferredColorScheme: ColorScheme? = nil
+
+ var body: some View {
+ SentenceView()
+ .contentShape(Rectangle())
+ .padding()
+ .edgesIgnoringSafeArea(.all)
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
+ .contentShape(Rectangle())
+ .foregroundColor(Color("Foreground"))
+ .background(Color("Background"))
+ .gesture(DragGesture()
+ .onEnded { value in
+ let direction = value.detectDirection()
+ switch direction {
+ case .up:
+ settings.remove()
+ case .down:
+ settings.add()
+ default:
+ break
+ }
+ })
+ .toolbar {
+ ToolbarItemGroup(placement: .bottomBar) {
+ ToolbarView(displaySettingsView: $displaySettingsView)
+ .foregroundColor(Color("Foreground"))
+ }
+ }
+ .preferredColorScheme(settings.colorScheme.type)
+ .sheet(isPresented: $displaySettingsView) {
+ VStack {
+ SettingsView()
+ Button("Accept", action: {displaySettingsView.toggle()})
+ }
+ }
+ }
+}
+
+
+struct ContentView_Previews: PreviewProvider {
+ static var previews: some View {
+ ContentView()
+ .environmentObject(Settings())
+ .environmentObject(Store())
+ }
+}
diff --git a/interject/Interject!/View/DefinitionView.swift b/interject/Interject!/View/DefinitionView.swift
new file mode 100644
index 0000000..5acfc35
--- /dev/null
+++ b/interject/Interject!/View/DefinitionView.swift
@@ -0,0 +1,28 @@
+//
+// DefinitionView.swift
+// Interject! ui
+//
+// Created by Rory Hinnen on 21/11/14.
+//
+
+import SwiftUI
+
+struct DefinitionView: View {
+ @EnvironmentObject var settings: Settings
+ @State var word: Place
+
+ var body: some View {
+ let size = 50.0
+ Text(settings.phrases.first!.definition(word, safe: settings.sfw))
+ .font(.custom(settings.font.description, size: size))
+ .minimumScaleFactor(0.5)
+ .allowsTightening(true)
+ }
+}
+
+struct DefinitionView_Previews: PreviewProvider {
+ static var previews: some View {
+ DefinitionView(word: .noun)
+ .environmentObject(Settings())
+ }
+}
diff --git a/interject/Interject!/View/Interject_App.swift b/interject/Interject!/View/Interject_App.swift
new file mode 100644
index 0000000..f1ddb45
--- /dev/null
+++ b/interject/Interject!/View/Interject_App.swift
@@ -0,0 +1,23 @@
+//
+// Interject_App.swift
+// Interject!
+//
+// Created by Rory Hinnen on 21/11/18.
+//
+
+import SwiftUI
+
+
+@main
+struct Interject_App: App {
+ @StateObject var settings = Settings()
+ @StateObject var store = Store()
+
+ var body: some Scene {
+ WindowGroup {
+ ContentView()
+ .environmentObject(settings)
+ .environmentObject(store)
+ }
+ }
+}
diff --git a/interject/Interject!/View/ListCellView.swift b/interject/Interject!/View/ListCellView.swift
new file mode 100644
index 0000000..428010c
--- /dev/null
+++ b/interject/Interject!/View/ListCellView.swift
@@ -0,0 +1,78 @@
+//
+// ListCellView.swift
+// Interject!
+//
+// Created by Rory on 4/14/24.
+//
+
+import SwiftUI
+import StoreKit
+
+struct ListCellView: View {
+ @EnvironmentObject var store: Store
+ @State var isPurchased: Bool = false
+ @State var errorTitle = ""
+ @State var isShowingError: Bool = false
+
+ let product: Product
+
+ var emoji: String {
+ store.emoji(for: product.id)
+ }
+
+ init(product: Product, purchasingEnabled: Bool = true) {
+ self.product = product
+ }
+
+ var body: some View {
+ HStack {
+ Text(emoji)
+ .font(.system(size: 50))
+ Spacer()
+ Text(product.displayName)
+ Spacer()
+ buyButton
+ .buttonStyle(BuyButtonStyle(isPurchased: isPurchased))
+ .disabled(isPurchased)
+ .frame(width: 100, height: 50)
+ }
+ }
+
+ var buyButton: some View {
+ Button(action: {
+ Task {
+ await buy()
+ }
+ }) {
+ if isPurchased {
+ Text(Image(systemName: "checkmark"))
+ .bold()
+ .foregroundColor(.white)
+ } else {
+ Text(product.displayPrice)
+ .foregroundColor(.white)
+ .bold()
+ }
+ }
+ .onAppear {
+ Task {
+ isPurchased = (try? await store.isPurchased(product)) ?? false
+ }
+ }
+ }
+
+ func buy() async {
+ do {
+ if try await store.purchase(product) != nil {
+ withAnimation {
+ isPurchased = true
+ }
+ }
+ } catch StoreError.failedVerification {
+ errorTitle = "Your purchase could not be verified by the App Store."
+ isShowingError = true
+ } catch {
+ print("Failed purchase for \(product.id): \(error)")
+ }
+ }
+}
diff --git a/interject/Interject!/View/SentenceView.swift b/interject/Interject!/View/SentenceView.swift
new file mode 100644
index 0000000..e73bf07
--- /dev/null
+++ b/interject/Interject!/View/SentenceView.swift
@@ -0,0 +1,85 @@
+//
+// SentenceView.swift
+// Interject! ui
+//
+// Created by Rory Hinnen on 21/11/13.
+//
+
+import SwiftUI
+
+struct SentenceView: View {
+ @EnvironmentObject var settings: Settings
+ @State var presentDefinition = false
+ @State var orientation = UIDevice.current.orientation
+
+ let orientationChanged = NotificationCenter.default.publisher(for: UIDevice.orientationDidChangeNotification)
+ .makeConnectable()
+ .autoconnect()
+
+ var body: some View {
+ VStack(spacing: orientation.isLandscape ? 35 : 85)
+ {
+ WordView(whichWord: .first)
+ .gesture(DragGesture()
+ .onEnded({ value in
+ let direction = value.detectDirection()
+ switch direction {
+ case .left:
+ settings.locked.adjective1.toggle()
+ case .right:
+ settings.locked.adjective1.toggle()
+ case .up:
+ settings.remove();
+ case .down:
+ settings.add();
+ default:
+ break
+ }
+ }))
+ WordView(whichWord: .second)
+ .gesture(DragGesture()
+ .onEnded({ value in
+ let direction = value.detectDirection()
+ switch direction {
+ case .left:
+ settings.locked.adjective2.toggle()
+ case .right:
+ settings.locked.adjective2.toggle()
+ case .up:
+ settings.remove()
+ case .down:
+ settings.add()
+ default:
+ break
+ }
+ }))
+ WordView(whichWord: .noun)
+ .gesture(DragGesture()
+ .onEnded({ value in
+ let direction = value.detectDirection()
+ switch direction {
+ case .left:
+ settings.locked.noun.toggle()
+ case .right:
+ settings.locked.noun.toggle()
+ case .up:
+ settings.remove()
+ case.down:
+ settings.add()
+ default:
+ break
+ }
+ }))
+ }
+ .onReceive(orientationChanged) { _ in
+ self.orientation = UIDevice.current.orientation
+ }
+ }
+}
+
+struct SentenceView_Previews: PreviewProvider {
+ static var previews: some View {
+ SentenceView()
+ .environmentObject(Settings())
+ }
+}
diff --git a/interject/Interject!/View/SettingsView.swift b/interject/Interject!/View/SettingsView.swift
new file mode 100644
index 0000000..10d767d
--- /dev/null
+++ b/interject/Interject!/View/SettingsView.swift
@@ -0,0 +1,77 @@
+//
+// SettingsView.swift
+// Interject! ui
+//
+// Created by Rory Hinnen on 21/11/17.
+//
+
+import SwiftUI
+import StoreKit
+
+struct SettingsView: View {
+ @EnvironmentObject var settings: Settings
+ @EnvironmentObject var store: Store
+ let isPurchased = false;
+
+ let thanks = "Doghearted Eejit!"
+
+ var body: some View {
+ VStack{
+ Text("Settings")
+ .font(.title)
+ Spacer()
+ VStack{
+ Text("Theme")
+ Text(settings.colorScheme.name)
+ }
+ .font(.title2)
+ .gesture(TapGesture().onEnded(
+ {settings.colorScheme.next()}))
+ Spacer()
+ VStack{
+ Text("Font")
+ .font(.title2)
+ Text(settings.font.description)
+ .font(.custom(settings.font.description, size: 25))
+ }
+ .gesture(TapGesture().onEnded({settings.font.next()}))
+ Spacer()
+ VStack {
+ Text("If you enjoy this nonsense, encourage me...")
+ Tips()
+ .frame(height: 230)
+ if store.purchasedTips.isEmpty {
+ VStack {
+ Text("I already sent you a tip!!")
+ Button("Restore my purchase", action: {
+ Task {
+ try? await AppStore.sync()
+ }
+ })
+ .buttonStyle(BuyButtonStyle(isPurchased: isPurchased))
+ .disabled(isPurchased)
+ .frame(width: 400, height: 70)
+ .foregroundColor(.white)
+ .multilineTextAlignment(.center)
+ }
+ } else {
+ Text("Thanks for not being a ")
+ Text(thanks)
+ }
+ }
+ Spacer()
+ }
+ .foregroundColor(Color("Foreground"))
+ .background(Color("Background"))
+ .edgesIgnoringSafeArea(.all)
+ .preferredColorScheme(settings.colorScheme.type)
+ }
+}
+
+struct SettingsView_Previews: PreviewProvider {
+ static var previews: some View {
+ SettingsView()
+ .environmentObject(Settings())
+ .environmentObject(Store())
+ }
+}
diff --git a/interject/Interject!/View/Tips.swift b/interject/Interject!/View/Tips.swift
new file mode 100644
index 0000000..6ef08c7
--- /dev/null
+++ b/interject/Interject!/View/Tips.swift
@@ -0,0 +1,25 @@
+//
+// SwiftUIView.swift
+// Interject!
+//
+// Created by Rory on 4/7/24.
+//
+
+import SwiftUI
+import StoreKit
+
+struct Tips: View {
+ @EnvironmentObject var store: Store
+
+
+ var body: some View {
+ List(store.tips){ tip in
+ ListCellView(product: tip)
+ }
+ }
+}
+
+#Preview {
+ Tips()
+ .environmentObject(Store())
+}
diff --git a/interject/Interject!/View/ToolbarView.swift b/interject/Interject!/View/ToolbarView.swift
new file mode 100644
index 0000000..1955ddb
--- /dev/null
+++ b/interject/Interject!/View/ToolbarView.swift
@@ -0,0 +1,39 @@
+//
+// ToolbarView.swift
+// Interject!
+//
+// Created by Rory Martin Hinnen on 6/22/22.
+//
+
+import SwiftUI
+
+struct ToolbarView: View {
+ @EnvironmentObject var settings: Settings
+ @Binding var displaySettingsView: Bool
+
+ func actionSheet() {
+ let textString =
+ settings.phrases.first!.word(.first, safe: settings.sfw).capitalizingFirstLetter() + " " +
+ settings.phrases.first!.word(.second, safe: settings.sfw).capitalizingFirstLetter() + " " +
+ settings.phrases.first!.word(.noun, safe: settings.sfw).capitalizingFirstLetter()
+ let activityVC = UIActivityViewController(activityItems: [textString], applicationActivities: nil)
+ UIApplication.shared.currentUIWindow()?.rootViewController?.present(activityVC, animated: true, completion: nil)
+ }
+
+ var body: some View {
+ HStack(spacing: 25.0) {
+ Button(action: {displaySettingsView.toggle()})
+ {Image(systemName: "pencil.circle.fill")}
+ Button(action: {settings.sfw.toggle()})
+ {
+ settings.sfw ?
+ Image(systemName: "exclamationmark.shield") :
+ Image(systemName: "exclamationmark.shield.fill")
+ }
+ if UIDevice.current.userInterfaceIdiom != .pad {
+ Button(action: {actionSheet()})
+ {Image(systemName: "square.and.arrow.up.circle.fill")}
+ }
+ }
+ }
+}
diff --git a/interject/Interject!/View/WordView.swift b/interject/Interject!/View/WordView.swift
new file mode 100644
index 0000000..fe85d2e
--- /dev/null
+++ b/interject/Interject!/View/WordView.swift
@@ -0,0 +1,45 @@
+//
+// WordView.swift
+// Interject!
+//
+// Created by Rory Hinnen on 21/11/28.
+//
+
+import SwiftUI
+
+
+
+struct WordView: View {
+ @EnvironmentObject var settings: Settings
+ @State private var showDefinition = false
+ @State var whichWord: Place
+
+ var body: some View {
+ Text(settings.phrases.first!.word(whichWord, safe: settings.sfw).capitalizingFirstLetter())
+ .font(.custom(settings.font.description, size: whichWord.size()))
+ .fontWeight(settings.locked.isLocked(whichWord) ?
+ Font.Weight.bold : Font.Weight.regular)
+ .allowsTightening(true)
+ .minimumScaleFactor(0.5)
+ .lineLimit(1)
+ .gesture(TapGesture().onEnded {showDefinition.toggle()})
+ .sheet(isPresented: $showDefinition)
+ {
+ VStack {
+ DefinitionView(word: whichWord)
+ Button("Dismiss", action: {showDefinition.toggle()})
+ .padding(20)
+ }
+ }
+ }
+}
+
+
+struct WordView_Previews: PreviewProvider {
+ @Environment(\.colorScheme) var colorScheme
+
+ static var previews: some View {
+ WordView(whichWord: .noun)
+ .environmentObject(Settings())
+ }
+}
diff --git a/interject/Interject!/adjective.strings b/interject/Interject!/adjective.strings
new file mode 100644
index 0000000..de75ac0
--- /dev/null
+++ b/interject/Interject!/adjective.strings
@@ -0,0 +1,142 @@
+/*
+ adjective.strings
+ Interject!
+
+ Created by Rory Hinnen on 21/12/4.
+
+*/
+[
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "artless", meaning: "Without craft or intelligence"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "base-court", meaning: "From the lower English court, common"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "bat-fowling", meaning: "An archaic method of bird hunting, like fishing from a barrel"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "bawdy", meaning: "Humorously indecent"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "bawheeded", meaning: "A person who speaks nonsense"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "beef-witted", meaning: "Brainless, a meat head"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "beetle-headed", meaning: "Witless"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "beslubbering", meaning: "Drooling"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "bloomin’", meaning: "For emphasis"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "boil-brained", meaning: "Filled with thoughts of pestilence and misery"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "bootless", meaning: "Common, or poor"),
+ Entry(nsfw: "pussy grabbing", nsfwMeaning: "A conman", sfw: "cat grabbing", meaning: "A man who respects women, and is always concerned for their well being"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "churlish", meaning: "Intentionally rude"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "clapper-clawed", meaning: "Brutish, all hands"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "clarted", meaning: "Mud covered"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "clatty", meaning: "Dirty, slovely, cluttered"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "clay-brained", meaning: "Rock head"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "clouted", meaning: "Senseless, reeling from being beat"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "cockered", meaning: "Spoiled"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "common-kissing", meaning: "Rogue, a rascal, grabs others without permission"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "craven", meaning: "Cowardly"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "crook-pated", meaning: "A thug, a thief who takes by violence"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "currish", meaning: "Like a feral dog, wild and snappish"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "daft", meaning: "Silly, foolish"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "dankish", meaning: "Dark, damp, humid"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "dismal-dreaming", meaning: "A person with foul and depressing dreams or goals"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "dissembling", meaning: "A rambling liar"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "dizzy-eyed", meaning: "Easily distracted"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "doaty", meaning: "A malingerer, one who obsesses over unimportant things"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "doddering", meaning: "Someone who rambles on, complaining of witchhunts and fake news"),
+ Entry(nsfw: "dog fucking", nsfwMeaning: nil, sfw: "dog loving", meaning: "Low standards"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "doghearted", meaning: "Inhuman, cruel"),
+ Entry(nsfw: "knobdobbing", nsfwMeaning: nil, sfw: "door knobbing", meaning: "Dickish"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "dread-bolted", meaning: "Frightning, terrifying"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "droning", meaning: "One who goes on and on, monotonous, boring and obsessive"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "earth-vexing", meaning: "Causes pain to all around"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "elf-skinned", meaning: "Shrunken, tiny, a mere nothing"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "errant", meaning: "One who wanders from what is right"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "fat-kidneyed", meaning: "Gross, clumsy"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "fawning", meaning: "Obsequious"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "fen-sucked", meaning: "Rising from the marshes"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "fly-bitten", meaning: "Marked by fly bites, wild and unkempt"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "fobbing", meaning: "A con man, a cheat"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "folly-fallen", meaning: "Stooping to foolishness"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "fool-born", meaning: "A second-generation fool"),
+ Entry(nsfw: "fucking", nsfwMeaning: "An old germanic word", sfw: "freaking", meaning: "A bad word"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "frothy", meaning: "Of little substance, foolish"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "froward", meaning: "A contrary person, difficult to deal with"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "fuddy", meaning: "Stuffy and old fashioned"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "full-gorged", meaning: "Well fed"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "glaikit", meaning: "Foolish, giddy"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "glakit", meaning: "Unsightly and stupid"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "gleeking", meaning: "Spitting"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "goatish", meaning: "Goatlike in manners"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "gommy", meaning: "Idiotic, mammering"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "gorbellied", meaning: "Corpulent"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "gormless", meaning: "Stupid or dull, lacking in gorm"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "guts-griping", meaning: "Belly aching"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "hackit", meaning: "Ugly"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "half-faced", meaning: "Imperfect, incomplete, defective"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "hasty-witted", meaning: "Shallow"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "hedge-born", meaning: "Of low birth"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "hell-hated", meaning: "Even despised by the Devil"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "howlin’", meaning: "Complaining, waling"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "idle-headed", meaning: "Stupid"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "ill-bred", meaning: "Lacking class"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "ill-nurtured", meaning: "Ill bred, raised common"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "impertinent", meaning: "Not respectful, rude"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "infectious", meaning: "Likely to spread infection"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "jakey", meaning: "A drunken tramp"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "jarring", meaning: "Incongruous, ill fitting"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "knotty-pated", meaning: "Easily distracted"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "lavvy-heided", meaning: "Toilet headed"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "loggerheaded", meaning: "Block headed"),
+ Entry(nsfw: nil, nsfwMeaning: "Orange, and pocked", sfw: "loofa-faced", meaning: "Pale and pocked"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "lumpish", meaning: "Roughly or crudely formed"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "malodorous", meaning: "Bad smelling"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "mammering", meaning: "To mumble endlessly without point"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "mangled", meaning: "Disfigured"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "mewling", meaning: "Whimpering"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "milk-livered", meaning: "Gutless, cowardly"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "mingin’", meaning: "Foul smelling"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "motley minded", meaning: "Foolish"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "naffy", meaning: "Self centered fop"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "onion-eyed", meaning: "One so ugly they make your eyes water"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "paunchy", meaning: "Pot bellied"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "pencil-necked", meaning: "Skinny, useless, annoying, weak"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "plume-plucked", meaning: "Humbled"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "pottle-deep", meaning: "Drunken, one who has emptied the dregs of his drink"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "pox-marked", meaning: "Pitted skin, marked with pox"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "pribbling", meaning: "A quibbler who argues just to argue"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "puking", meaning: "Nauseating"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "puny", meaning: "Small and weak"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "qualling", meaning: "Quail like, cowardly"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "rank", meaning: "Low or vile, stinking"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "reeky", meaning: "Stinky"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "reeling-ripe", meaning: "Smelly"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "roguish", meaning: "Loutish, and not in a good way"),
+ Entry(nsfw: "cock-juggling", nsfwMeaning: "A promiscous person", sfw: "rooster juggling", meaning: "A fowl manager"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "rude-growing", meaning: "Rough and wild"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "rump-fed", meaning: "Pampered"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "ruttish", meaning: "Salacious, lascivious"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "saucy", meaning: "Lascivious, salacious"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "scabby", meaning: "Covered in scars, ugly"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "shard borne", meaning: "Born in dung"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "sheep-biting", meaning: "A person who preys on the defenseless"),
+ Entry(nsfw: nil, nsfwMeaning: "Refering to the size of one's third leg", sfw: "small-handed", meaning: "An observation regarding one's manliness"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "spleeny", meaning: "Having a lot of spleen"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "spongy", meaning: "Soft headed"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "spur-galled", meaning: "Ridden hard, put away wet, worn out"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "straw-headed", meaning: "A numpty"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "surly", meaning: "One of the dwarfs"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "swag-bellied", meaning: "Well fed, a golf course owner"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "swollen", meaning: "Distended, fat, puffy"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "tardy-gaited", meaning: "Sluggish"),
+ Entry(nsfw: nil, nsfwMeaning: "As reported by Stormy Daniels", sfw: "three-inched", meaning: "An observation regarding one's manliness"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "tickle-brained", meaning: "Flighty, unreliable, probably a drunk"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "toad-spotted", meaning: "Spotted like a toad"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "toffee-nosed", meaning: "Having a soft, brown nose"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "tottering", meaning: "Doddering"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "unchin-snouted", meaning: "A face like a hedgehog"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "unmuzzled", meaning: "Unrestrained, obnoxiously capricious"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "vacuous", meaning: "Empty headed"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "vain", meaning: "Self obsessed"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "venomed", meaning: "Poisonous"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "villianous", meaning: "Evil"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "warped", meaning: "Bent, bad"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "wayward", meaning: "Errant"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "weapons-grade", meaning: "The most extreme example of"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "weather-bitten", meaning: "Scarred by a life outside"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "weedy", meaning: "Thin"),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "witless", meaning: "Uhh.."),
+ Entry(nsfw: nil, nsfwMeaning: nil, sfw: "yeasty", meaning: "Pale and soft")
+]
diff --git a/interject/Interject!Tests/Interject_Tests.swift b/interject/Interject!Tests/Interject_Tests.swift
new file mode 100644
index 0000000..a0d4579
--- /dev/null
+++ b/interject/Interject!Tests/Interject_Tests.swift
@@ -0,0 +1,33 @@
+//
+// Interject_Tests.swift
+// Interject!Tests
+//
+// Created by Rory Hinnen on 21/11/18.
+//
+
+import XCTest
+@testable import Interject_
+
+class Interject_Tests: XCTestCase {
+
+ override func setUpWithError() throws {
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+ }
+
+ override func tearDownWithError() throws {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ }
+
+ func testExample() throws {
+ // This is an example of a functional test case.
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
+ }
+
+ func testPerformanceExample() throws {
+ // This is an example of a performance test case.
+ self.measure {
+ // Put the code you want to measure the time of here.
+ }
+ }
+
+}
diff --git a/interject/Interject!UITests/Interject_UITests.swift b/interject/Interject!UITests/Interject_UITests.swift
new file mode 100644
index 0000000..5cd1f34
--- /dev/null
+++ b/interject/Interject!UITests/Interject_UITests.swift
@@ -0,0 +1,42 @@
+//
+// Interject_UITests.swift
+// Interject!UITests
+//
+// Created by Rory Hinnen on 21/11/18.
+//
+
+import XCTest
+
+class Interject_UITests: XCTestCase {
+
+ override func setUpWithError() throws {
+ // Put setup code here. This method is called before the invocation of each test method in the class.
+
+ // In UI tests it is usually best to stop immediately when a failure occurs.
+ continueAfterFailure = false
+
+ // 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.
+ }
+
+ override func tearDownWithError() throws {
+ // Put teardown code here. This method is called after the invocation of each test method in the class.
+ }
+
+ func testExample() throws {
+ // UI tests must launch the application that they test.
+ let app = XCUIApplication()
+ app.launch()
+
+ // Use recording to get started writing UI tests.
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
+ }
+
+ func testLaunchPerformance() throws {
+ if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
+ // This measures how long it takes to launch your application.
+ measure(metrics: [XCTApplicationLaunchMetric()]) {
+ XCUIApplication().launch()
+ }
+ }
+ }
+}
diff --git a/interject/Interject!UITests/Interject_UITestsLaunchTests.swift b/interject/Interject!UITests/Interject_UITestsLaunchTests.swift
new file mode 100644
index 0000000..b5424ee
--- /dev/null
+++ b/interject/Interject!UITests/Interject_UITestsLaunchTests.swift
@@ -0,0 +1,32 @@
+//
+// Interject_UITestsLaunchTests.swift
+// Interject!UITests
+//
+// Created by Rory Hinnen on 21/11/18.
+//
+
+import XCTest
+
+class Interject_UITestsLaunchTests: XCTestCase {
+
+ override class var runsForEachTargetApplicationUIConfiguration: Bool {
+ true
+ }
+
+ override func setUpWithError() throws {
+ continueAfterFailure = false
+ }
+
+ func testLaunch() throws {
+ let app = XCUIApplication()
+ app.launch()
+
+ // Insert steps here to perform after app launch but before taking a screenshot,
+ // such as logging into a test account or navigating somewhere in the app
+
+ let attachment = XCTAttachment(screenshot: app.screenshot())
+ attachment.name = "Launch Screen"
+ attachment.lifetime = .keepAlways
+ add(attachment)
+ }
+}
diff --git a/interject/Interject--Info.plist b/interject/Interject--Info.plist
new file mode 100644
index 0000000..55d3f36
--- /dev/null
+++ b/interject/Interject--Info.plist
@@ -0,0 +1,10 @@
+
+
+
+
+ ITSAppUsesNonExemptEncryption
+
+ ITSEncryptionExportComplianceCode
+ YES
+
+
diff --git a/interject/interject.png b/interject/interject.png
new file mode 100644
index 0000000..e939f7d
Binary files /dev/null and b/interject/interject.png differ