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