diff --git a/CalmMeditation.xcodeproj/project.pbxproj b/CalmMeditation.xcodeproj/project.pbxproj new file mode 100644 index 0000000..41c968f --- /dev/null +++ b/CalmMeditation.xcodeproj/project.pbxproj @@ -0,0 +1,1218 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 520C84B021386BD500BB0E3D /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 524CD0F72129D40A00E6E311 /* Constants.swift */; }; + 520C84B12138744300BB0E3D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 524CD0DA2129CF3800E6E311 /* Assets.xcassets */; }; + 52377305213727F800E49221 /* WatchMeditation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52377304213727F800E49221 /* WatchMeditation.swift */; }; + 52377306213729EE00E49221 /* CustomClasses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 524CD0FD2129DC1800E6E311 /* CustomClasses.swift */; }; + 5237730721372A4200E49221 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 524CD0F72129D40A00E6E311 /* Constants.swift */; }; + 5237730821372A9100E49221 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 524CD0DA2129CF3800E6E311 /* Assets.xcassets */; }; + 523773122137316B00E49221 /* WatchMeditations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 523773112137316B00E49221 /* WatchMeditations.swift */; }; + 523773162137352100E49221 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 523773152137352100E49221 /* Assets.xcassets */; }; + 524CD0D42129CF3500E6E311 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 524CD0D32129CF3500E6E311 /* AppDelegate.swift */; }; + 524CD0D92129CF3500E6E311 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 524CD0D72129CF3500E6E311 /* Main.storyboard */; }; + 524CD0DB2129CF3800E6E311 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 524CD0DA2129CF3800E6E311 /* Assets.xcassets */; }; + 524CD0DE2129CF3800E6E311 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 524CD0DC2129CF3800E6E311 /* LaunchScreen.storyboard */; }; + 524CD0E92129CF3800E6E311 /* MeditationUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 524CD0E82129CF3800E6E311 /* MeditationUITests.swift */; }; + 524CD0F42129D3CE00E6E311 /* Meditation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 524CD0F32129D3CE00E6E311 /* Meditation.swift */; }; + 524CD0F62129D3D900E6E311 /* RunMeditation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 524CD0F52129D3D900E6E311 /* RunMeditation.swift */; }; + 524CD0F82129D40A00E6E311 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 524CD0F72129D40A00E6E311 /* Constants.swift */; }; + 524CD0FA2129D95B00E6E311 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 524CD0F92129D95B00E6E311 /* Utils.swift */; }; + 524CD0FC2129DAA900E6E311 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 524CD0FB2129DAA900E6E311 /* Extensions.swift */; }; + 524CD0FE2129DC1800E6E311 /* CustomClasses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 524CD0FD2129DC1800E6E311 /* CustomClasses.swift */; }; + 524CD1032129DC6500E6E311 /* GothamProMedium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 524CD0FF2129DC6300E6E311 /* GothamProMedium.ttf */; }; + 524CD1042129DC6500E6E311 /* GothamProBlack.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 524CD1002129DC6300E6E311 /* GothamProBlack.ttf */; }; + 524CD1052129DC6500E6E311 /* GothamProRegular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 524CD1012129DC6400E6E311 /* GothamProRegular.ttf */; }; + 524CD1062129DC6500E6E311 /* GothamProBold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 524CD1022129DC6400E6E311 /* GothamProBold.ttf */; }; + 524CD1162129E32A00E6E311 /* SnapshotHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 524CD1152129E32900E6E311 /* SnapshotHelper.swift */; }; + 524CD11F212B1CE600E6E311 /* NotificationCenter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 524CD11E212B1CE600E6E311 /* NotificationCenter.framework */; }; + 524CD122212B1CE600E6E311 /* TodayMeditations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 524CD121212B1CE600E6E311 /* TodayMeditations.swift */; }; + 524CD125212B1CE600E6E311 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 524CD123212B1CE600E6E311 /* MainInterface.storyboard */; }; + 524CD129212B1CE600E6E311 /* TodayExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 524CD11D212B1CE600E6E311 /* TodayExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 525830C62145439300CD8872 /* ChooseSound.swift in Sources */ = {isa = PBXBuildFile; fileRef = 525830C52145439300CD8872 /* ChooseSound.swift */; }; + 5260824C214703C2009CEDE3 /* Cave_Water_Dripping.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 5260823E214703BC009CEDE3 /* Cave_Water_Dripping.mp3 */; }; + 5260824D214703C2009CEDE3 /* Summer_Birds_Singing.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 5260823F214703BC009CEDE3 /* Summer_Birds_Singing.mp3 */; }; + 5260824E214703C2009CEDE3 /* Crackling_Fire.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 52608240214703BC009CEDE3 /* Crackling_Fire.mp3 */; }; + 5260824F214703C2009CEDE3 /* Majestic_Rain.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 52608241214703BD009CEDE3 /* Majestic_Rain.mp3 */; }; + 52608250214703C2009CEDE3 /* Thunderstorm.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 52608242214703BD009CEDE3 /* Thunderstorm.mp3 */; }; + 52608251214703C2009CEDE3 /* Jungle_Birds_And_Insects.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 52608243214703BD009CEDE3 /* Jungle_Birds_And_Insects.mp3 */; }; + 52608252214703C2009CEDE3 /* Jungle_River.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 52608244214703BE009CEDE3 /* Jungle_River.mp3 */; }; + 52608253214703C2009CEDE3 /* Waves_Breaking_Onto_Shore.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 52608245214703BE009CEDE3 /* Waves_Breaking_Onto_Shore.mp3 */; }; + 52608254214703C2009CEDE3 /* Hailstones.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 52608246214703BE009CEDE3 /* Hailstones.mp3 */; }; + 52608255214703C2009CEDE3 /* Running_Cave_Water.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 52608247214703BE009CEDE3 /* Running_Cave_Water.mp3 */; }; + 52608256214703C2009CEDE3 /* Rainforest_Ambience.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 52608248214703BF009CEDE3 /* Rainforest_Ambience.mp3 */; }; + 52608257214703C2009CEDE3 /* Rocky Shore Waves.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 52608249214703C0009CEDE3 /* Rocky Shore Waves.mp3 */; }; + 52608258214703C2009CEDE3 /* Nightime_Jungle.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 5260824A214703C1009CEDE3 /* Nightime_Jungle.mp3 */; }; + 52608259214703C2009CEDE3 /* Ocean_Waves.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 5260824B214703C1009CEDE3 /* Ocean_Waves.mp3 */; }; + 5260825A214703F0009CEDE3 /* Cave_Water_Dripping.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 5260823E214703BC009CEDE3 /* Cave_Water_Dripping.mp3 */; }; + 5260825B214703F0009CEDE3 /* Crackling_Fire.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 52608240214703BC009CEDE3 /* Crackling_Fire.mp3 */; }; + 5260825C214703F0009CEDE3 /* Hailstones.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 52608246214703BE009CEDE3 /* Hailstones.mp3 */; }; + 5260825D214703F0009CEDE3 /* Jungle_Birds_And_Insects.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 52608243214703BD009CEDE3 /* Jungle_Birds_And_Insects.mp3 */; }; + 5260825E214703F0009CEDE3 /* Jungle_River.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 52608244214703BE009CEDE3 /* Jungle_River.mp3 */; }; + 5260825F214703F0009CEDE3 /* Majestic_Rain.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 52608241214703BD009CEDE3 /* Majestic_Rain.mp3 */; }; + 52608260214703F0009CEDE3 /* Nightime_Jungle.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 5260824A214703C1009CEDE3 /* Nightime_Jungle.mp3 */; }; + 52608261214703F0009CEDE3 /* Ocean_Waves.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 5260824B214703C1009CEDE3 /* Ocean_Waves.mp3 */; }; + 52608262214703F0009CEDE3 /* Rainforest_Ambience.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 52608248214703BF009CEDE3 /* Rainforest_Ambience.mp3 */; }; + 52608263214703F0009CEDE3 /* Rocky Shore Waves.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 52608249214703C0009CEDE3 /* Rocky Shore Waves.mp3 */; }; + 52608264214703F0009CEDE3 /* Running_Cave_Water.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 52608247214703BE009CEDE3 /* Running_Cave_Water.mp3 */; }; + 52608265214703F0009CEDE3 /* Summer_Birds_Singing.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 5260823F214703BC009CEDE3 /* Summer_Birds_Singing.mp3 */; }; + 52608266214703F0009CEDE3 /* Thunderstorm.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 52608242214703BD009CEDE3 /* Thunderstorm.mp3 */; }; + 52608267214703F0009CEDE3 /* Waves_Breaking_Onto_Shore.mp3 in Resources */ = {isa = PBXBuildFile; fileRef = 52608245214703BE009CEDE3 /* Waves_Breaking_Onto_Shore.mp3 */; }; + 526472B6218C9D3400427299 /* Achievements.swift in Sources */ = {isa = PBXBuildFile; fileRef = 526472B5218C9D3400427299 /* Achievements.swift */; }; + 526472B8218C9DF500427299 /* ProgressMananger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 526472B7218C9DF500427299 /* ProgressMananger.swift */; }; + 52BC3C652133375F0093C029 /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 52BC3C632133375F0093C029 /* Interface.storyboard */; }; + 52BC3C6E213337610093C029 /* WatchKit Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 52BC3C6D213337610093C029 /* WatchKit Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 52BC3C75213337610093C029 /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52BC3C74213337610093C029 /* ExtensionDelegate.swift */; }; + 52BC3C7E213337620093C029 /* WatchKit.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 52BC3C612133375F0093C029 /* WatchKit.app */; }; + 52C703342142D95200AC839F /* Settings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52C703332142D95200AC839F /* Settings.swift */; }; + 52C7034621442B2900AC839F /* Purchase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52C7034521442B2900AC839F /* Purchase.swift */; }; + 52C7035021442B9800AC839F /* Upgrade.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52C7034F21442B9800AC839F /* Upgrade.swift */; }; + 52C7035621442D8B00AC839F /* Dialogs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52C7035521442D8B00AC839F /* Dialogs.swift */; }; + 670E52CFA16807ABC85935A0 /* Pods_Meditation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 520DE0B7213ADD9400BC4181 /* Pods_Meditation.framework */; }; + A79902F9924D5DF0629637E6 /* Pods_Meditation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9FAC9632EEA9A61F9D441314 /* Pods_Meditation.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 524CD0E52129CF3800E6E311 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 524CD0C82129CF3500E6E311 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 524CD0CF2129CF3500E6E311; + remoteInfo = Meditation; + }; + 524CD127212B1CE600E6E311 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 524CD0C82129CF3500E6E311 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 524CD11C212B1CE600E6E311; + remoteInfo = TodayExtension; + }; + 52BC3C6F213337610093C029 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 524CD0C82129CF3500E6E311 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 52BC3C6C213337610093C029; + remoteInfo = "WatchKit Extension"; + }; + 52BC3C7C213337620093C029 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 524CD0C82129CF3500E6E311 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 52BC3C602133375F0093C029; + remoteInfo = WatchKit; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 524CD12D212B1CE600E6E311 /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 524CD129212B1CE600E6E311 /* TodayExtension.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; + 52BC3C84213337620093C029 /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 52BC3C6E213337610093C029 /* WatchKit Extension.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; + 52BC3C86213337620093C029 /* Embed Watch Content */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "$(CONTENTS_FOLDER_PATH)/Watch"; + dstSubfolderSpec = 16; + files = ( + 52BC3C7E213337620093C029 /* WatchKit.app in Embed Watch Content */, + ); + name = "Embed Watch Content"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 4F0426F2A2D943613DDEDC6D /* Pods-Meditation.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Meditation.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Meditation/Pods-Meditation.debug.xcconfig"; sourceTree = ""; }; + 520DE0B7213ADD9400BC4181 /* Pods_Meditation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Pods_Meditation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 52377304213727F800E49221 /* WatchMeditation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchMeditation.swift; sourceTree = ""; }; + 523773112137316B00E49221 /* WatchMeditations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WatchMeditations.swift; sourceTree = ""; }; + 523773152137352100E49221 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 524CD0D02129CF3500E6E311 /* CalmMeditation.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CalmMeditation.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 524CD0D32129CF3500E6E311 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 524CD0D82129CF3500E6E311 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 524CD0DA2129CF3800E6E311 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 524CD0DD2129CF3800E6E311 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 524CD0E42129CF3800E6E311 /* CalmMeditationUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CalmMeditationUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 524CD0E82129CF3800E6E311 /* MeditationUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeditationUITests.swift; sourceTree = ""; }; + 524CD0EA2129CF3800E6E311 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 524CD0F32129D3CE00E6E311 /* Meditation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Meditation.swift; sourceTree = ""; }; + 524CD0F52129D3D900E6E311 /* RunMeditation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunMeditation.swift; sourceTree = ""; }; + 524CD0F72129D40A00E6E311 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + 524CD0F92129D95B00E6E311 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; + 524CD0FB2129DAA900E6E311 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; + 524CD0FD2129DC1800E6E311 /* CustomClasses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomClasses.swift; sourceTree = ""; }; + 524CD0FF2129DC6300E6E311 /* GothamProMedium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = GothamProMedium.ttf; sourceTree = ""; }; + 524CD1002129DC6300E6E311 /* GothamProBlack.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = GothamProBlack.ttf; sourceTree = ""; }; + 524CD1012129DC6400E6E311 /* GothamProRegular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = GothamProRegular.ttf; sourceTree = ""; }; + 524CD1022129DC6400E6E311 /* GothamProBold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = GothamProBold.ttf; sourceTree = ""; }; + 524CD1152129E32900E6E311 /* SnapshotHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SnapshotHelper.swift; sourceTree = ""; }; + 524CD11D212B1CE600E6E311 /* TodayExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = TodayExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 524CD11E212B1CE600E6E311 /* NotificationCenter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NotificationCenter.framework; path = System/Library/Frameworks/NotificationCenter.framework; sourceTree = SDKROOT; }; + 524CD121212B1CE600E6E311 /* TodayMeditations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodayMeditations.swift; sourceTree = ""; }; + 524CD124212B1CE600E6E311 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = ""; }; + 524CD126212B1CE600E6E311 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 525830C52145439300CD8872 /* ChooseSound.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChooseSound.swift; sourceTree = ""; }; + 5260823E214703BC009CEDE3 /* Cave_Water_Dripping.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = Cave_Water_Dripping.mp3; sourceTree = ""; }; + 5260823F214703BC009CEDE3 /* Summer_Birds_Singing.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = Summer_Birds_Singing.mp3; sourceTree = ""; }; + 52608240214703BC009CEDE3 /* Crackling_Fire.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = Crackling_Fire.mp3; sourceTree = ""; }; + 52608241214703BD009CEDE3 /* Majestic_Rain.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = Majestic_Rain.mp3; sourceTree = ""; }; + 52608242214703BD009CEDE3 /* Thunderstorm.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = Thunderstorm.mp3; sourceTree = ""; }; + 52608243214703BD009CEDE3 /* Jungle_Birds_And_Insects.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = Jungle_Birds_And_Insects.mp3; sourceTree = ""; }; + 52608244214703BE009CEDE3 /* Jungle_River.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = Jungle_River.mp3; sourceTree = ""; }; + 52608245214703BE009CEDE3 /* Waves_Breaking_Onto_Shore.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = Waves_Breaking_Onto_Shore.mp3; sourceTree = ""; }; + 52608246214703BE009CEDE3 /* Hailstones.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = Hailstones.mp3; sourceTree = ""; }; + 52608247214703BE009CEDE3 /* Running_Cave_Water.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = Running_Cave_Water.mp3; sourceTree = ""; }; + 52608248214703BF009CEDE3 /* Rainforest_Ambience.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = Rainforest_Ambience.mp3; sourceTree = ""; }; + 52608249214703C0009CEDE3 /* Rocky Shore Waves.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = "Rocky Shore Waves.mp3"; sourceTree = ""; }; + 5260824A214703C1009CEDE3 /* Nightime_Jungle.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = Nightime_Jungle.mp3; sourceTree = ""; }; + 5260824B214703C1009CEDE3 /* Ocean_Waves.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = Ocean_Waves.mp3; sourceTree = ""; }; + 526472B5218C9D3400427299 /* Achievements.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Achievements.swift; sourceTree = ""; }; + 526472B7218C9DF500427299 /* ProgressMananger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProgressMananger.swift; sourceTree = ""; }; + 52BC3C612133375F0093C029 /* WatchKit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WatchKit.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 52BC3C642133375F0093C029 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Interface.storyboard; sourceTree = ""; }; + 52BC3C68213337610093C029 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 52BC3C6D213337610093C029 /* WatchKit Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "WatchKit Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; + 52BC3C74213337610093C029 /* ExtensionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionDelegate.swift; sourceTree = ""; }; + 52BC3C7A213337620093C029 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 52C703332142D95200AC839F /* Settings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Settings.swift; sourceTree = ""; }; + 52C7034521442B2900AC839F /* Purchase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Purchase.swift; sourceTree = ""; }; + 52C7034F21442B9800AC839F /* Upgrade.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Upgrade.swift; sourceTree = ""; }; + 52C7035521442D8B00AC839F /* Dialogs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dialogs.swift; sourceTree = ""; }; + 52F45518212B1F3E005D5433 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 52F4551E21305A78005D5433 /* Meditation.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Meditation.entitlements; sourceTree = ""; }; + 9EAD0DEDA39A6812B91C782F /* Pods_MeditationUITests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MeditationUITests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 9FAC9632EEA9A61F9D441314 /* Pods_Meditation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Meditation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E49B285A854089E5C3CB5DAD /* Pods-Meditation.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Meditation.release.xcconfig"; path = "Pods/Target Support Files/Pods-Meditation/Pods-Meditation.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 524CD0CD2129CF3500E6E311 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A79902F9924D5DF0629637E6 /* Pods_Meditation.framework in Frameworks */, + 670E52CFA16807ABC85935A0 /* Pods_Meditation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 524CD0E12129CF3800E6E311 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 524CD11A212B1CE600E6E311 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 524CD11F212B1CE600E6E311 /* NotificationCenter.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 52BC3C6A213337610093C029 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 83BC46B4CC014D4513515AE9 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 520C84AF21386B7E00BB0E3D /* Resources */ = { + isa = PBXGroup; + children = ( + 52BC3C7A213337620093C029 /* Info.plist */, + 52BC3C74213337610093C029 /* ExtensionDelegate.swift */, + ); + path = Resources; + sourceTree = ""; + }; + 524CD0C72129CF3500E6E311 = { + isa = PBXGroup; + children = ( + 524CD0D22129CF3500E6E311 /* Meditation */, + 524CD120212B1CE600E6E311 /* TodayExtension */, + 52BC3C622133375F0093C029 /* WatchKit */, + 52BC3C71213337610093C029 /* WatchKit Extension */, + 524CD0E72129CF3800E6E311 /* MeditationUITests */, + 524CD0D12129CF3500E6E311 /* Products */, + B16DBEBDE55E6D37006338E7 /* Pods */, + AF309B85C6FCB580A97A74FD /* Frameworks */, + ); + sourceTree = ""; + }; + 524CD0D12129CF3500E6E311 /* Products */ = { + isa = PBXGroup; + children = ( + 524CD0D02129CF3500E6E311 /* CalmMeditation.app */, + 524CD0E42129CF3800E6E311 /* CalmMeditationUITests.xctest */, + 524CD11D212B1CE600E6E311 /* TodayExtension.appex */, + 52BC3C612133375F0093C029 /* WatchKit.app */, + 52BC3C6D213337610093C029 /* WatchKit Extension.appex */, + ); + name = Products; + sourceTree = ""; + }; + 524CD0D22129CF3500E6E311 /* Meditation */ = { + isa = PBXGroup; + children = ( + 52F4551E21305A78005D5433 /* Meditation.entitlements */, + 524CD117212B1AA500E6E311 /* Resources */, + 524CD118212B1AF300E6E311 /* Utils */, + 52C7034A21442B5900AC839F /* Purchase */, + 524CD0D72129CF3500E6E311 /* Main.storyboard */, + 524CD0DA2129CF3800E6E311 /* Assets.xcassets */, + 524CD0D32129CF3500E6E311 /* AppDelegate.swift */, + 526472BA218E05E300427299 /* Meditation */, + 526472B9218E05AB00427299 /* Achievements */, + 52C703332142D95200AC839F /* Settings.swift */, + ); + path = Meditation; + sourceTree = ""; + }; + 524CD0E72129CF3800E6E311 /* MeditationUITests */ = { + isa = PBXGroup; + children = ( + 524CD0E82129CF3800E6E311 /* MeditationUITests.swift */, + 524CD1152129E32900E6E311 /* SnapshotHelper.swift */, + 524CD0EA2129CF3800E6E311 /* Info.plist */, + ); + path = MeditationUITests; + sourceTree = ""; + }; + 524CD1072129DCA100E6E311 /* Fonts */ = { + isa = PBXGroup; + children = ( + 524CD1002129DC6300E6E311 /* GothamProBlack.ttf */, + 524CD1022129DC6400E6E311 /* GothamProBold.ttf */, + 524CD0FF2129DC6300E6E311 /* GothamProMedium.ttf */, + 524CD1012129DC6400E6E311 /* GothamProRegular.ttf */, + ); + path = Fonts; + sourceTree = ""; + }; + 524CD1142129DCB500E6E311 /* Sounds */ = { + isa = PBXGroup; + children = ( + 5260823E214703BC009CEDE3 /* Cave_Water_Dripping.mp3 */, + 52608240214703BC009CEDE3 /* Crackling_Fire.mp3 */, + 52608246214703BE009CEDE3 /* Hailstones.mp3 */, + 52608243214703BD009CEDE3 /* Jungle_Birds_And_Insects.mp3 */, + 52608244214703BE009CEDE3 /* Jungle_River.mp3 */, + 52608241214703BD009CEDE3 /* Majestic_Rain.mp3 */, + 5260824A214703C1009CEDE3 /* Nightime_Jungle.mp3 */, + 5260824B214703C1009CEDE3 /* Ocean_Waves.mp3 */, + 52608248214703BF009CEDE3 /* Rainforest_Ambience.mp3 */, + 52608249214703C0009CEDE3 /* Rocky Shore Waves.mp3 */, + 52608247214703BE009CEDE3 /* Running_Cave_Water.mp3 */, + 5260823F214703BC009CEDE3 /* Summer_Birds_Singing.mp3 */, + 52608242214703BD009CEDE3 /* Thunderstorm.mp3 */, + 52608245214703BE009CEDE3 /* Waves_Breaking_Onto_Shore.mp3 */, + ); + path = Sounds; + sourceTree = ""; + }; + 524CD117212B1AA500E6E311 /* Resources */ = { + isa = PBXGroup; + children = ( + 524CD1072129DCA100E6E311 /* Fonts */, + 524CD1142129DCB500E6E311 /* Sounds */, + 52F45518212B1F3E005D5433 /* Info.plist */, + 524CD0DC2129CF3800E6E311 /* LaunchScreen.storyboard */, + ); + path = Resources; + sourceTree = ""; + }; + 524CD118212B1AF300E6E311 /* Utils */ = { + isa = PBXGroup; + children = ( + 524CD0F72129D40A00E6E311 /* Constants.swift */, + 524CD0FD2129DC1800E6E311 /* CustomClasses.swift */, + 52C7035521442D8B00AC839F /* Dialogs.swift */, + 524CD0FB2129DAA900E6E311 /* Extensions.swift */, + 524CD0F92129D95B00E6E311 /* Utils.swift */, + ); + path = Utils; + sourceTree = ""; + }; + 524CD120212B1CE600E6E311 /* TodayExtension */ = { + isa = PBXGroup; + children = ( + 524CD121212B1CE600E6E311 /* TodayMeditations.swift */, + 524CD123212B1CE600E6E311 /* MainInterface.storyboard */, + 524CD126212B1CE600E6E311 /* Info.plist */, + ); + path = TodayExtension; + sourceTree = ""; + }; + 526472B9218E05AB00427299 /* Achievements */ = { + isa = PBXGroup; + children = ( + 526472B5218C9D3400427299 /* Achievements.swift */, + 526472B7218C9DF500427299 /* ProgressMananger.swift */, + ); + path = Achievements; + sourceTree = ""; + }; + 526472BA218E05E300427299 /* Meditation */ = { + isa = PBXGroup; + children = ( + 524CD0F32129D3CE00E6E311 /* Meditation.swift */, + 524CD0F52129D3D900E6E311 /* RunMeditation.swift */, + 525830C52145439300CD8872 /* ChooseSound.swift */, + ); + path = Meditation; + sourceTree = ""; + }; + 52BC3C622133375F0093C029 /* WatchKit */ = { + isa = PBXGroup; + children = ( + 52BC3C632133375F0093C029 /* Interface.storyboard */, + 523773152137352100E49221 /* Assets.xcassets */, + 52BC3C68213337610093C029 /* Info.plist */, + ); + path = WatchKit; + sourceTree = ""; + }; + 52BC3C71213337610093C029 /* WatchKit Extension */ = { + isa = PBXGroup; + children = ( + 520C84AF21386B7E00BB0E3D /* Resources */, + 523773112137316B00E49221 /* WatchMeditations.swift */, + 52377304213727F800E49221 /* WatchMeditation.swift */, + ); + path = "WatchKit Extension"; + sourceTree = ""; + }; + 52C7034A21442B5900AC839F /* Purchase */ = { + isa = PBXGroup; + children = ( + 52C7034521442B2900AC839F /* Purchase.swift */, + 52C7034F21442B9800AC839F /* Upgrade.swift */, + ); + path = Purchase; + sourceTree = ""; + }; + AF309B85C6FCB580A97A74FD /* Frameworks */ = { + isa = PBXGroup; + children = ( + 520DE0B7213ADD9400BC4181 /* Pods_Meditation.framework */, + 9FAC9632EEA9A61F9D441314 /* Pods_Meditation.framework */, + 9EAD0DEDA39A6812B91C782F /* Pods_MeditationUITests.framework */, + 524CD11E212B1CE600E6E311 /* NotificationCenter.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + B16DBEBDE55E6D37006338E7 /* Pods */ = { + isa = PBXGroup; + children = ( + 4F0426F2A2D943613DDEDC6D /* Pods-Meditation.debug.xcconfig */, + E49B285A854089E5C3CB5DAD /* Pods-Meditation.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 524CD0CF2129CF3500E6E311 /* CalmMeditation */ = { + isa = PBXNativeTarget; + buildConfigurationList = 524CD0ED2129CF3800E6E311 /* Build configuration list for PBXNativeTarget "CalmMeditation" */; + buildPhases = ( + 18BEF87A40C23EB19424A6CB /* [CP] Check Pods Manifest.lock */, + 524CD0CC2129CF3500E6E311 /* Sources */, + 524CD0CD2129CF3500E6E311 /* Frameworks */, + 524CD0CE2129CF3500E6E311 /* Resources */, + CB1C45ACAB25D908CD0A6412 /* [CP] Embed Pods Frameworks */, + 524CD12D212B1CE600E6E311 /* Embed App Extensions */, + 52BC3C86213337620093C029 /* Embed Watch Content */, + ); + buildRules = ( + ); + dependencies = ( + 524CD128212B1CE600E6E311 /* PBXTargetDependency */, + 52BC3C7D213337620093C029 /* PBXTargetDependency */, + ); + name = CalmMeditation; + productName = Meditation; + productReference = 524CD0D02129CF3500E6E311 /* CalmMeditation.app */; + productType = "com.apple.product-type.application"; + }; + 524CD0E32129CF3800E6E311 /* CalmMeditationUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 524CD0F02129CF3800E6E311 /* Build configuration list for PBXNativeTarget "CalmMeditationUITests" */; + buildPhases = ( + 524CD0E02129CF3800E6E311 /* Sources */, + 524CD0E12129CF3800E6E311 /* Frameworks */, + 524CD0E22129CF3800E6E311 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 524CD0E62129CF3800E6E311 /* PBXTargetDependency */, + ); + name = CalmMeditationUITests; + productName = MeditationUITests; + productReference = 524CD0E42129CF3800E6E311 /* CalmMeditationUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; + 524CD11C212B1CE600E6E311 /* TodayExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 524CD12A212B1CE600E6E311 /* Build configuration list for PBXNativeTarget "TodayExtension" */; + buildPhases = ( + 524CD119212B1CE600E6E311 /* Sources */, + 524CD11A212B1CE600E6E311 /* Frameworks */, + 524CD11B212B1CE600E6E311 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TodayExtension; + productName = TodayExtension; + productReference = 524CD11D212B1CE600E6E311 /* TodayExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; + 52BC3C602133375F0093C029 /* WatchKit */ = { + isa = PBXNativeTarget; + buildConfigurationList = 52BC3C85213337620093C029 /* Build configuration list for PBXNativeTarget "WatchKit" */; + buildPhases = ( + 52BC3C5F2133375F0093C029 /* Resources */, + 52BC3C84213337620093C029 /* Embed App Extensions */, + 83BC46B4CC014D4513515AE9 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 52BC3C70213337610093C029 /* PBXTargetDependency */, + ); + name = WatchKit; + productName = WatchKit; + productReference = 52BC3C612133375F0093C029 /* WatchKit.app */; + productType = "com.apple.product-type.application.watchapp2"; + }; + 52BC3C6C213337610093C029 /* WatchKit Extension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 52BC3C83213337620093C029 /* Build configuration list for PBXNativeTarget "WatchKit Extension" */; + buildPhases = ( + 52BC3C69213337610093C029 /* Sources */, + 52BC3C6A213337610093C029 /* Frameworks */, + 52BC3C6B213337610093C029 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "WatchKit Extension"; + productName = "WatchKit Extension"; + productReference = 52BC3C6D213337610093C029 /* WatchKit Extension.appex */; + productType = "com.apple.product-type.watchkit2-extension"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 524CD0C82129CF3500E6E311 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0940; + LastUpgradeCheck = 0940; + ORGANIZATIONNAME = "Joey Tawadrous"; + TargetAttributes = { + 524CD0CF2129CF3500E6E311 = { + CreatedOnToolsVersion = 9.4; + SystemCapabilities = { + com.apple.ApplicationGroups.iOS = { + enabled = 1; + }; + }; + }; + 524CD0E32129CF3800E6E311 = { + CreatedOnToolsVersion = 9.4; + TestTargetID = 524CD0CF2129CF3500E6E311; + }; + 524CD11C212B1CE600E6E311 = { + CreatedOnToolsVersion = 9.4; + }; + 52BC3C602133375F0093C029 = { + CreatedOnToolsVersion = 9.4; + }; + 52BC3C6C213337610093C029 = { + CreatedOnToolsVersion = 9.4; + }; + }; + }; + buildConfigurationList = 524CD0CB2129CF3500E6E311 /* Build configuration list for PBXProject "CalmMeditation" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 524CD0C72129CF3500E6E311; + productRefGroup = 524CD0D12129CF3500E6E311 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 524CD0CF2129CF3500E6E311 /* CalmMeditation */, + 524CD0E32129CF3800E6E311 /* CalmMeditationUITests */, + 524CD11C212B1CE600E6E311 /* TodayExtension */, + 52BC3C602133375F0093C029 /* WatchKit */, + 52BC3C6C213337610093C029 /* WatchKit Extension */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 524CD0CE2129CF3500E6E311 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 52608253214703C2009CEDE3 /* Waves_Breaking_Onto_Shore.mp3 in Resources */, + 52608252214703C2009CEDE3 /* Jungle_River.mp3 in Resources */, + 524CD0DE2129CF3800E6E311 /* LaunchScreen.storyboard in Resources */, + 5260824E214703C2009CEDE3 /* Crackling_Fire.mp3 in Resources */, + 524CD1032129DC6500E6E311 /* GothamProMedium.ttf in Resources */, + 5260824C214703C2009CEDE3 /* Cave_Water_Dripping.mp3 in Resources */, + 52608258214703C2009CEDE3 /* Nightime_Jungle.mp3 in Resources */, + 52608254214703C2009CEDE3 /* Hailstones.mp3 in Resources */, + 524CD1062129DC6500E6E311 /* GothamProBold.ttf in Resources */, + 52608250214703C2009CEDE3 /* Thunderstorm.mp3 in Resources */, + 52608259214703C2009CEDE3 /* Ocean_Waves.mp3 in Resources */, + 52608255214703C2009CEDE3 /* Running_Cave_Water.mp3 in Resources */, + 52608257214703C2009CEDE3 /* Rocky Shore Waves.mp3 in Resources */, + 5260824D214703C2009CEDE3 /* Summer_Birds_Singing.mp3 in Resources */, + 524CD1042129DC6500E6E311 /* GothamProBlack.ttf in Resources */, + 524CD0DB2129CF3800E6E311 /* Assets.xcassets in Resources */, + 524CD0D92129CF3500E6E311 /* Main.storyboard in Resources */, + 5260824F214703C2009CEDE3 /* Majestic_Rain.mp3 in Resources */, + 52608256214703C2009CEDE3 /* Rainforest_Ambience.mp3 in Resources */, + 524CD1052129DC6500E6E311 /* GothamProRegular.ttf in Resources */, + 52608251214703C2009CEDE3 /* Jungle_Birds_And_Insects.mp3 in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 524CD0E22129CF3800E6E311 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 524CD11B212B1CE600E6E311 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5237730821372A9100E49221 /* Assets.xcassets in Resources */, + 524CD125212B1CE600E6E311 /* MainInterface.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 52BC3C5F2133375F0093C029 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 523773162137352100E49221 /* Assets.xcassets in Resources */, + 52BC3C652133375F0093C029 /* Interface.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 52BC3C6B213337610093C029 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5260825A214703F0009CEDE3 /* Cave_Water_Dripping.mp3 in Resources */, + 5260825B214703F0009CEDE3 /* Crackling_Fire.mp3 in Resources */, + 5260825C214703F0009CEDE3 /* Hailstones.mp3 in Resources */, + 5260825D214703F0009CEDE3 /* Jungle_Birds_And_Insects.mp3 in Resources */, + 5260825E214703F0009CEDE3 /* Jungle_River.mp3 in Resources */, + 5260825F214703F0009CEDE3 /* Majestic_Rain.mp3 in Resources */, + 52608260214703F0009CEDE3 /* Nightime_Jungle.mp3 in Resources */, + 52608261214703F0009CEDE3 /* Ocean_Waves.mp3 in Resources */, + 52608262214703F0009CEDE3 /* Rainforest_Ambience.mp3 in Resources */, + 52608263214703F0009CEDE3 /* Rocky Shore Waves.mp3 in Resources */, + 52608264214703F0009CEDE3 /* Running_Cave_Water.mp3 in Resources */, + 52608265214703F0009CEDE3 /* Summer_Birds_Singing.mp3 in Resources */, + 52608266214703F0009CEDE3 /* Thunderstorm.mp3 in Resources */, + 52608267214703F0009CEDE3 /* Waves_Breaking_Onto_Shore.mp3 in Resources */, + 520C84B12138744300BB0E3D /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 18BEF87A40C23EB19424A6CB /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Meditation-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + CB1C45ACAB25D908CD0A6412 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${SRCROOT}/Pods/Target Support Files/Pods-Meditation/Pods-Meditation-frameworks.sh", + "${BUILT_PRODUCTS_DIR}/FontAwesome.swift/FontAwesome_swift.framework", + "${BUILT_PRODUCTS_DIR}/KYCircularProgress/KYCircularProgress.framework", + "${BUILT_PRODUCTS_DIR}/SCLAlertView/SCLAlertView.framework", + "${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework", + "${BUILT_PRODUCTS_DIR}/SwiftySound/SwiftySound.framework", + "${BUILT_PRODUCTS_DIR}/SwiftyStoreKit/SwiftyStoreKit.framework", + "${BUILT_PRODUCTS_DIR}/paper-onboarding/paper_onboarding.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FontAwesome_swift.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/KYCircularProgress.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SCLAlertView.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftySound.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyStoreKit.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/paper_onboarding.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Meditation/Pods-Meditation-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 524CD0CC2129CF3500E6E311 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 524CD0F82129D40A00E6E311 /* Constants.swift in Sources */, + 524CD0F42129D3CE00E6E311 /* Meditation.swift in Sources */, + 52C703342142D95200AC839F /* Settings.swift in Sources */, + 524CD0F62129D3D900E6E311 /* RunMeditation.swift in Sources */, + 524CD0D42129CF3500E6E311 /* AppDelegate.swift in Sources */, + 52C7034621442B2900AC839F /* Purchase.swift in Sources */, + 52C7035021442B9800AC839F /* Upgrade.swift in Sources */, + 524CD0FA2129D95B00E6E311 /* Utils.swift in Sources */, + 52C7035621442D8B00AC839F /* Dialogs.swift in Sources */, + 526472B6218C9D3400427299 /* Achievements.swift in Sources */, + 524CD0FC2129DAA900E6E311 /* Extensions.swift in Sources */, + 526472B8218C9DF500427299 /* ProgressMananger.swift in Sources */, + 524CD0FE2129DC1800E6E311 /* CustomClasses.swift in Sources */, + 525830C62145439300CD8872 /* ChooseSound.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 524CD0E02129CF3800E6E311 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 524CD0E92129CF3800E6E311 /* MeditationUITests.swift in Sources */, + 524CD1162129E32A00E6E311 /* SnapshotHelper.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 524CD119212B1CE600E6E311 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5237730721372A4200E49221 /* Constants.swift in Sources */, + 52377306213729EE00E49221 /* CustomClasses.swift in Sources */, + 524CD122212B1CE600E6E311 /* TodayMeditations.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 52BC3C69213337610093C029 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 520C84B021386BD500BB0E3D /* Constants.swift in Sources */, + 52BC3C75213337610093C029 /* ExtensionDelegate.swift in Sources */, + 52377305213727F800E49221 /* WatchMeditation.swift in Sources */, + 523773122137316B00E49221 /* WatchMeditations.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 524CD0E62129CF3800E6E311 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 524CD0CF2129CF3500E6E311 /* CalmMeditation */; + targetProxy = 524CD0E52129CF3800E6E311 /* PBXContainerItemProxy */; + }; + 524CD128212B1CE600E6E311 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 524CD11C212B1CE600E6E311 /* TodayExtension */; + targetProxy = 524CD127212B1CE600E6E311 /* PBXContainerItemProxy */; + }; + 52BC3C70213337610093C029 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 52BC3C6C213337610093C029 /* WatchKit Extension */; + targetProxy = 52BC3C6F213337610093C029 /* PBXContainerItemProxy */; + }; + 52BC3C7D213337620093C029 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 52BC3C602133375F0093C029 /* WatchKit */; + targetProxy = 52BC3C7C213337620093C029 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 524CD0D72129CF3500E6E311 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 524CD0D82129CF3500E6E311 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 524CD0DC2129CF3800E6E311 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 524CD0DD2129CF3800E6E311 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; + 524CD123212B1CE600E6E311 /* MainInterface.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 524CD124212B1CE600E6E311 /* Base */, + ); + name = MainInterface.storyboard; + sourceTree = ""; + }; + 52BC3C632133375F0093C029 /* Interface.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 52BC3C642133375F0093C029 /* Base */, + ); + name = Interface.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 524CD0EB2129CF3800E6E311 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 524CD0EC2129CF3800E6E311 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 524CD0EE2129CF3800E6E311 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4F0426F2A2D943613DDEDC6D /* Pods-Meditation.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Meditation/Meditation.entitlements; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = P5Z7RL76WF; + INFOPLIST_FILE = Meditation/Resources/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.joeyt.meditation; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 524CD0EF2129CF3800E6E311 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E49B285A854089E5C3CB5DAD /* Pods-Meditation.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_ENTITLEMENTS = Meditation/Meditation.entitlements; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = P5Z7RL76WF; + INFOPLIST_FILE = Meditation/Resources/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.joeyt.meditation; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 524CD0F12129CF3800E6E311 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = P5Z7RL76WF; + INFOPLIST_FILE = MeditationUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.joeyt.MeditationUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = Meditation; + }; + name = Debug; + }; + 524CD0F22129CF3800E6E311 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = P5Z7RL76WF; + INFOPLIST_FILE = MeditationUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.joeyt.MeditationUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = Meditation; + }; + name = Release; + }; + 524CD12B212B1CE600E6E311 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = P5Z7RL76WF; + INFOPLIST_FILE = TodayExtension/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.joeyt.meditation.todayExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 524CD12C212B1CE600E6E311 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = P5Z7RL76WF; + INFOPLIST_FILE = TodayExtension/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.joeyt.meditation.todayExtension; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 52BC3C7F213337620093C029 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = P5Z7RL76WF; + IBSC_MODULE = WatchKit_Extension; + INFOPLIST_FILE = WatchKit/Info.plist; + PRODUCT_BUNDLE_IDENTIFIER = com.joeyt.meditation.watchkitapp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 4.3; + }; + name = Debug; + }; + 52BC3C80213337620093C029 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = P5Z7RL76WF; + IBSC_MODULE = WatchKit_Extension; + INFOPLIST_FILE = WatchKit/Info.plist; + PRODUCT_BUNDLE_IDENTIFIER = com.joeyt.meditation.watchkitapp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 4.3; + }; + name = Release; + }; + 52BC3C81213337620093C029 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = P5Z7RL76WF; + INFOPLIST_FILE = "WatchKit Extension/Resources/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.joeyt.meditation.watchkitapp.watchkitextension; + PRODUCT_NAME = "${TARGET_NAME}"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 4.3; + }; + name = Debug; + }; + 52BC3C82213337620093C029 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = P5Z7RL76WF; + INFOPLIST_FILE = "WatchKit Extension/Resources/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.joeyt.meditation.watchkitapp.watchkitextension; + PRODUCT_NAME = "${TARGET_NAME}"; + SDKROOT = watchos; + SKIP_INSTALL = YES; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = 4; + WATCHOS_DEPLOYMENT_TARGET = 4.3; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 524CD0CB2129CF3500E6E311 /* Build configuration list for PBXProject "CalmMeditation" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 524CD0EB2129CF3800E6E311 /* Debug */, + 524CD0EC2129CF3800E6E311 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 524CD0ED2129CF3800E6E311 /* Build configuration list for PBXNativeTarget "CalmMeditation" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 524CD0EE2129CF3800E6E311 /* Debug */, + 524CD0EF2129CF3800E6E311 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 524CD0F02129CF3800E6E311 /* Build configuration list for PBXNativeTarget "CalmMeditationUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 524CD0F12129CF3800E6E311 /* Debug */, + 524CD0F22129CF3800E6E311 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 524CD12A212B1CE600E6E311 /* Build configuration list for PBXNativeTarget "TodayExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 524CD12B212B1CE600E6E311 /* Debug */, + 524CD12C212B1CE600E6E311 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 52BC3C83213337620093C029 /* Build configuration list for PBXNativeTarget "WatchKit Extension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 52BC3C81213337620093C029 /* Debug */, + 52BC3C82213337620093C029 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 52BC3C85213337620093C029 /* Build configuration list for PBXNativeTarget "WatchKit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 52BC3C7F213337620093C029 /* Debug */, + 52BC3C80213337620093C029 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 524CD0C82129CF3500E6E311 /* Project object */; +} diff --git a/CalmMeditation.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/CalmMeditation.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..3af52d1 --- /dev/null +++ b/CalmMeditation.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/CalmMeditation.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/CalmMeditation.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/CalmMeditation.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/CalmMeditation.xcodeproj/project.xcworkspace/xcuserdata/joeytawadrous.xcuserdatad/UserInterfaceState.xcuserstate b/CalmMeditation.xcodeproj/project.xcworkspace/xcuserdata/joeytawadrous.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..54dc4ec Binary files /dev/null and b/CalmMeditation.xcodeproj/project.xcworkspace/xcuserdata/joeytawadrous.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/CalmMeditation.xcodeproj/xcshareddata/xcschemes/CalmMeditation.xcscheme b/CalmMeditation.xcodeproj/xcshareddata/xcschemes/CalmMeditation.xcscheme new file mode 100644 index 0000000..c48fb41 --- /dev/null +++ b/CalmMeditation.xcodeproj/xcshareddata/xcschemes/CalmMeditation.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CalmMeditation.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/Meditation.xcscheme b/CalmMeditation.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/Meditation.xcscheme new file mode 100644 index 0000000..0e93307 --- /dev/null +++ b/CalmMeditation.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/Meditation.xcscheme @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CalmMeditation.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/MeditationUITests.xcscheme b/CalmMeditation.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/MeditationUITests.xcscheme new file mode 100644 index 0000000..0586424 --- /dev/null +++ b/CalmMeditation.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/MeditationUITests.xcscheme @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CalmMeditation.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/TodayExtension.xcscheme b/CalmMeditation.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/TodayExtension.xcscheme new file mode 100644 index 0000000..edf5b7c --- /dev/null +++ b/CalmMeditation.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/TodayExtension.xcscheme @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CalmMeditation.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/WatchKit Extension.xcscheme b/CalmMeditation.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/WatchKit Extension.xcscheme new file mode 100644 index 0000000..2e9627d --- /dev/null +++ b/CalmMeditation.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/WatchKit Extension.xcscheme @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CalmMeditation.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/WatchKit.xcscheme b/CalmMeditation.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/WatchKit.xcscheme new file mode 100644 index 0000000..4089339 --- /dev/null +++ b/CalmMeditation.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/WatchKit.xcscheme @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CalmMeditation.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/xcschememanagement.plist b/CalmMeditation.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..6dbdbaa --- /dev/null +++ b/CalmMeditation.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,69 @@ + + + + + SchemeUserState + + CalmMeditation.xcscheme_^#shared#^_ + + orderHint + 14 + + Meditation.xcscheme + + orderHint + 1 + + MeditationUITests.xcscheme + + orderHint + 0 + + TodayExtension.xcscheme + + orderHint + 2 + + WatchKit Extension.xcscheme + + isShown + + orderHint + 4 + + WatchKit.xcscheme + + orderHint + 3 + + + SuppressBuildableAutocreation + + 524CD0CF2129CF3500E6E311 + + primary + + + 524CD0E32129CF3800E6E311 + + primary + + + 524CD11C212B1CE600E6E311 + + primary + + + 52BC3C602133375F0093C029 + + primary + + + 52BC3C6C213337610093C029 + + primary + + + + + diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..7a118b4 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source "https://rubygems.org" + +gem "fastlane" diff --git a/Meditation.xcworkspace/contents.xcworkspacedata b/Meditation.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..d0fdd8c --- /dev/null +++ b/Meditation.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Meditation.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Meditation.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Meditation.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Meditation.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Meditation.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..3ddf867 --- /dev/null +++ b/Meditation.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + BuildSystemType + Latest + + diff --git a/Meditation.xcworkspace/xcuserdata/joeytawadrous.xcuserdatad/UserInterfaceState.xcuserstate b/Meditation.xcworkspace/xcuserdata/joeytawadrous.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..887a484 Binary files /dev/null and b/Meditation.xcworkspace/xcuserdata/joeytawadrous.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Meditation.xcworkspace/xcuserdata/joeytawadrous.xcuserdatad/WorkspaceSettings.xcsettings b/Meditation.xcworkspace/xcuserdata/joeytawadrous.xcuserdatad/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..f25782d --- /dev/null +++ b/Meditation.xcworkspace/xcuserdata/joeytawadrous.xcuserdatad/WorkspaceSettings.xcsettings @@ -0,0 +1,18 @@ + + + + + BuildLocationStyle + UseAppPreferences + CustomBuildLocationType + RelativeToDerivedData + DerivedDataLocationStyle + Default + EnabledFullIndexStoreVisibility + + IssueFilterStyle + ShowActiveSchemeOnly + LiveSourceIssuesEnabled + + + diff --git a/Meditation.xcworkspace/xcuserdata/joeytawadrous.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Meditation.xcworkspace/xcuserdata/joeytawadrous.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..e1b1ce8 --- /dev/null +++ b/Meditation.xcworkspace/xcuserdata/joeytawadrous.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Meditation/Achievements/Achievements.swift b/Meditation/Achievements/Achievements.swift new file mode 100644 index 0000000..ed8e050 --- /dev/null +++ b/Meditation/Achievements/Achievements.swift @@ -0,0 +1,207 @@ +import UIKit +import KYCircularProgress + + +class Achievements: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout { + + @IBOutlet var collectionView: UICollectionView! + @IBOutlet var topView: UIView? + @IBOutlet var backButton: UIBarButtonItem! + @IBOutlet var infoButton: UIBarButtonItem! + + // Points + private var levelsLabel: UILabel? + private var pointsLabel: UILabel? + private var circularPointsView: KYCircularProgress! + private var circularPointsValue: Double = 0.0 + private var circlePoints: Double = 0.0 + + + + /* MARK: Initialising + /////////////////////////////////////////// */ + override func viewDidLoad() { + // Refresh here once so the user is not waiting (as they would be in viewDidAppear) + collectionView.reloadData() + Utils.createFontAwesomeBarButton(button: backButton, icon: .arrowLeft, style: .solid) + Utils.createFontAwesomeBarButton(button: infoButton, icon: .info, style: .solid) + } + + + override func viewDidAppear(_ animated: Bool) { + // User may have achieved something since last visit! + collectionView.reloadData() + } + + override func viewWillLayoutSubviews() { + // Remove all previous views from topView + // Needed for orientation changes + for subview in topView!.subviews { + subview.removeFromSuperview() + } + + // Reset progress values (needed for orientation changes) + circularPointsValue = 0.0 + + initPointsView() + } + + + override var prefersStatusBarHidden: Bool { + return true + } + + + + /* MARK: Actions + /////////////////////////////////////////// */ + @IBAction func backButtonPressed() { + Utils.presentView(self, viewName: Constants.Views.MEDITATION) + } + + @IBAction func infoButtonPressed() { + Dialogs.showInfoDialog(title: Constants.Strings.ACHIEVEMENTS_INFO_DIALOG_TITLE, subTitle: Constants.Strings.ACHIEVEMENTS_INFO_DIALOG_SUBTITLE) + } + + + + /* MARK: Points Functionality + /////////////////////////////////////////// */ + func initPointsView() { + circularPointsView = createProgressCircle(x: (UIScreen.main.bounds.width / 2) - 50, color: Constants.Colors.PRIMARY_PURPLE) + topView?.addSubview(circularPointsView) + + levelsLabel = createProgressLabel(center: circularPointsView.center) + topView?.addSubview(levelsLabel!) + + pointsLabel = createProgressLabel(center: CGPoint(x: circularPointsView.center.x, y: circularPointsView.bounds.height + 19)) + topView?.addSubview(pointsLabel!) + + if Utils.bool(key: "FASTLANE_SNAPSHOT") { + levelsLabel?.text = "Level 5" + pointsLabel?.text = "Total Points: 78" + circlePoints = 80.0 + } + else { + calculatePointsAndLevel() + } + + // Set points circle load speed relative to total points + if(circlePoints < 0.3) { + Timer.scheduledTimer(timeInterval: 0.010, target: self, selector: #selector(updatePoints), userInfo: nil, repeats: true) + } + else if(circlePoints > 0.3 && circlePoints < 0.7) { + Timer.scheduledTimer(timeInterval: 0.005, target: self, selector: #selector(updatePoints), userInfo: nil, repeats: true) + } + else { + Timer.scheduledTimer(timeInterval: 0.0025, target: self, selector: #selector(updatePoints), userInfo: nil, repeats: true) + } + } + + func calculatePointsAndLevel() { + let points = Utils.int(key: Constants.Defaults.APP_DATA_TOTAL_POINTS) + levelsLabel?.text = Constants.Strings.LEVEL + String(ProgressManager.getLevel(points: points)) + pointsLabel?.text = Constants.Strings.POINTS_CIRCULAR_VIEW_DESCRIPTION_LABEL + String(points) + circlePoints = ProgressManager.getLevelProgress(points: points) + } + + @objc private func updatePoints() { + if(circularPointsValue <= circlePoints) { + circularPointsValue = circularPointsValue + 0.001 + circularPointsView.progress = circularPointsValue + } + } + + + + /* MARK: Points UI + /////////////////////////////////////////// */ + func createProgressLabel(center: CGPoint) -> UILabel { + let label = UILabel(frame: CGRect(x: 0, y: 0, width: 140, height: 20)) + label.center = center + label.textColor = UIColor(hex: Constants.Colors.PRIMARY_TEXT_GRAY) + label.textAlignment = .center + label.font = UIFont.GothamProMedium(size: 15.0) + return label + } + + func createProgressCircle(x: CGFloat, color: String) -> KYCircularProgress { + let progressView = KYCircularProgress(frame: CGRect(x: x, y: 5, width: 100, height: 100), showGuide: true) + return Utils.createProgressView(progressView: progressView, color: color, guideColor: "fff") + } + + + + /* MARK: Collection View + /////////////////////////////////////////// */ + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return Constants.Achievements.ACHIEVEMENTS_ALL.count + } + + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "achievementCollectionViewCell", for: indexPath) as! AchievementCollectionViewCell + + let achievement = Constants.Achievements.ACHIEVEMENTS_ALL[indexPath.row] as [String] + let image = UIImage(named: achievement[2]) + + cell.titleLabel.text = achievement[3] + + if Utils.bool(key: "FASTLANE_SNAPSHOT") { + cell.iconImageView.image = image + cell.titleLabel.textColor = UIColor.black + Utils.createHexagonImageView(imageView: cell.containerImageView, lineWidth: 5, lineColor: UIColor(hex: achievement[1])) + } + else { + if !ProgressManager.isAchievementReached(name: achievement[0]) { + let color = UIColor(hex: "fff") + cell.iconImageView.image = image?.maskWithColor(color: color) + cell.titleLabel.textColor = color + Utils.createHexagonImageView(imageView: cell.containerImageView, lineWidth: 5, lineColor: color) + } + else { + cell.iconImageView.image = image + cell.titleLabel.textColor = UIColor(hex: Constants.Colors.PRIMARY_TEXT_GRAY) + Utils.createHexagonImageView(imageView: cell.containerImageView, lineWidth: 5, lineColor: UIColor(hex: achievement[1])) + } + } + + return cell + } + + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + let achievement = Constants.Achievements.ACHIEVEMENTS_ALL[indexPath.row] as [String] + let achievementReached = ProgressManager.isAchievementReached(name: achievement[0]) + Dialogs.showAchievementDialog(view: self, reached: achievementReached, achievement: achievement) + } + + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + let width = collectionView.bounds.width / 3.0 + return CGSize(width: width, height: width + 20) + } + + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { + return UIEdgeInsets.zero + } + + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return 0 + } + + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return 0 + } +} + + + +class AchievementCollectionViewCell: UICollectionViewCell { + @IBOutlet var containerImageView: UIImageView! + @IBOutlet var iconImageView: UIImageView! + @IBOutlet var titleLabel: UILabel! +} diff --git a/Meditation/Achievements/ProgressMananger.swift b/Meditation/Achievements/ProgressMananger.swift new file mode 100644 index 0000000..9a14c1b --- /dev/null +++ b/Meditation/Achievements/ProgressMananger.swift @@ -0,0 +1,112 @@ +import UIKit +import SwiftyJSON + + +class ProgressManager { + + class func getAchievements() -> [Any] { + var achievements: [Any] = [] + + if Utils.appData()[Constants.Defaults.APP_DATA_ACHIEVEMENTS] != JSON.null { + achievements = Utils.appData()[Constants.Defaults.APP_DATA_ACHIEVEMENTS].arrayObject! + } + + return achievements + } + + + class func isAchievementReached(name: String) -> Bool { + let achievements = getAchievements() + + for achievement in achievements { + if (achievement as! [String: String])["name"]! == name { + return true + } + } + + return false + } + + + class func checkAchivementReached(view: UIViewController, achievement: [String]) { + var achievementReached = false + + for userAchievement in getAchievements() { + if JSON(userAchievement)["name"].string! == achievement[0] { + achievementReached = true + } + } + + if !achievementReached { setAchievementReached(view: view, achievement: achievement) } + } + + + class func checkAndSetAchievementReached(view: UIViewController, type: String) { + if type == Constants.Achievements.POINTS_TYPE { + for achievement in Constants.Achievements.ACHIEVEMENTS_POINTS { + let value = Int(achievement[0].components(separatedBy: "p")[0]) + if Utils.int(key: Constants.Defaults.APP_DATA_TOTAL_POINTS) >= value! { + checkAchivementReached(view: view, achievement: achievement) + } + } + } + else if type == Constants.Achievements.SESSIONS_TYPE { + for achievement in Constants.Achievements.ACHIEVEMENTS_SESSIONS { + let value = Int(achievement[0].components(separatedBy: "s")[0]) + if Utils.int(key: Constants.Defaults.APP_DATA_TOTAL_SESSIONS) >= value! { + checkAchivementReached(view: view, achievement: achievement) + } + } + } + else if type == Constants.Achievements.TIME_MEDITATING_TYPE { + for achievement in Constants.Achievements.ACHIEVEMENTS_TIME_MEDITATING { + let value = Int(achievement[0].components(separatedBy: "t")[0]) + if Utils.int(key: Constants.Defaults.APP_DATA_TOTAL_TIME_MEDITATING) >= value! { + checkAchivementReached(view: view, achievement: achievement) + } + } + } + } + + + class func setAchievementReached(view: UIViewController, achievement: [String]) { + var achievements = getAchievements() + + let newAchievement: Any = [ + "name": achievement[0], + "date": Utils.getCurrentDateString() + ] + + achievements.append(newAchievement) + Utils.setAppData(key: Constants.Defaults.APP_DATA_ACHIEVEMENTS, json: JSON(achievements)) + + Dialogs.showAchievementDialog(view: view, reached: true, achievement: achievement) + } + + + class func getLevel(points: Int) -> Int { + var i = 0 + for levelThreshold in Constants.Strings.LEVEL_THRESHOLDS { + if levelThreshold > points { + return i + 1 + } + i = i + 1 + } + return 0 + } + + + class func getLevelProgress(points: Int) -> Double { + return Double(Double(points) / Double(Constants.Strings.LEVEL_THRESHOLDS[getLevel(points: points)])) + } + + + class func shouldLevelUp(points: Int) -> Bool { + for levelThreshold in Constants.Strings.LEVEL_THRESHOLDS { + if levelThreshold > points && levelThreshold <= (points + 3) { + return true + } + } + return false + } +} diff --git a/Meditation/AppDelegate.swift b/Meditation/AppDelegate.swift new file mode 100644 index 0000000..3706e9a --- /dev/null +++ b/Meditation/AppDelegate.swift @@ -0,0 +1,38 @@ +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + + // MARK: Initialising + /////////////////////////////////////////// */ + func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool { + + // Purchases + Purchase.supportStorePurchase() + Purchase.completeTransactions() +// Purchase.verifyReceiptCheck() + + if (url.scheme != nil) { + let selectedMeditation = url.scheme?.split(separator: "n") // e.g. Meditation0 + Utils.set(key: Constants.Defaults.SELECTED_MEDITATION, value: Int(selectedMeditation![1])!) + + let storyboard = UIStoryboard(name: "Main", bundle: nil) + let viewController = storyboard.instantiateViewController(withIdentifier: "Meditation") + self.window?.rootViewController = viewController + } + + return true + } + + func applicationWillEnterForeground(_ application: UIApplication) { + // Purchase.restorePurchases(view: self.inputViewController!, showDialog: false) +// Purchase.verifyReceiptCheck() + } + + func applicationDidFinishLaunching(_ application: UIApplication) { + UINavigationBar.appearance().titleTextAttributes = [NSAttributedStringKey.foregroundColor : UIColor(hex: Constants.Colors.PRIMARY_PURPLE)] + } +} diff --git a/Meditation/Assets.xcassets/1.imageset/Contents.json b/Meditation/Assets.xcassets/1.imageset/Contents.json new file mode 100644 index 0000000..349c3b1 --- /dev/null +++ b/Meditation/Assets.xcassets/1.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "adventure-beautiful-boardwalk-235734.jpg", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/1.imageset/adventure-beautiful-boardwalk-235734.jpg b/Meditation/Assets.xcassets/1.imageset/adventure-beautiful-boardwalk-235734.jpg new file mode 100644 index 0000000..9130429 Binary files /dev/null and b/Meditation/Assets.xcassets/1.imageset/adventure-beautiful-boardwalk-235734.jpg differ diff --git a/Meditation/Assets.xcassets/2.imageset/Contents.json b/Meditation/Assets.xcassets/2.imageset/Contents.json new file mode 100644 index 0000000..e8ee0b6 --- /dev/null +++ b/Meditation/Assets.xcassets/2.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "alps-background-environment-1054181.jpg", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/2.imageset/alps-background-environment-1054181.jpg b/Meditation/Assets.xcassets/2.imageset/alps-background-environment-1054181.jpg new file mode 100644 index 0000000..2232483 Binary files /dev/null and b/Meditation/Assets.xcassets/2.imageset/alps-background-environment-1054181.jpg differ diff --git a/Meditation/Assets.xcassets/3.imageset/Contents.json b/Meditation/Assets.xcassets/3.imageset/Contents.json new file mode 100644 index 0000000..2a40aa1 --- /dev/null +++ b/Meditation/Assets.xcassets/3.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "background-background-image-clouds-1054289.jpg", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/3.imageset/background-background-image-clouds-1054289.jpg b/Meditation/Assets.xcassets/3.imageset/background-background-image-clouds-1054289.jpg new file mode 100644 index 0000000..9d6e731 Binary files /dev/null and b/Meditation/Assets.xcassets/3.imageset/background-background-image-clouds-1054289.jpg differ diff --git a/Meditation/Assets.xcassets/4.imageset/Contents.json b/Meditation/Assets.xcassets/4.imageset/Contents.json new file mode 100644 index 0000000..2a5f5ed --- /dev/null +++ b/Meditation/Assets.xcassets/4.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "beach-dawn-dusk-129458.jpg", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/4.imageset/beach-dawn-dusk-129458.jpg b/Meditation/Assets.xcassets/4.imageset/beach-dawn-dusk-129458.jpg new file mode 100644 index 0000000..25c44f2 Binary files /dev/null and b/Meditation/Assets.xcassets/4.imageset/beach-dawn-dusk-129458.jpg differ diff --git a/Meditation/Assets.xcassets/5.imageset/Contents.json b/Meditation/Assets.xcassets/5.imageset/Contents.json new file mode 100644 index 0000000..6b55e3d --- /dev/null +++ b/Meditation/Assets.xcassets/5.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "beach-dawn-dusk-129975.jpg", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/5.imageset/beach-dawn-dusk-129975.jpg b/Meditation/Assets.xcassets/5.imageset/beach-dawn-dusk-129975.jpg new file mode 100644 index 0000000..7846dc8 Binary files /dev/null and b/Meditation/Assets.xcassets/5.imageset/beach-dawn-dusk-129975.jpg differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Contents.json b/Meditation/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100755 index 0000000..98be84a --- /dev/null +++ b/Meditation/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,182 @@ +{ + "images":[ + { + "idiom":"iphone", + "size":"20x20", + "scale":"2x", + "filename":"Icon-App-20x20@2x.png" + }, + { + "idiom":"iphone", + "size":"20x20", + "scale":"3x", + "filename":"Icon-App-20x20@3x.png" + }, + { + "idiom":"iphone", + "size":"29x29", + "scale":"1x", + "filename":"Icon-App-29x29@1x.png" + }, + { + "idiom":"iphone", + "size":"29x29", + "scale":"2x", + "filename":"Icon-App-29x29@2x.png" + }, + { + "idiom":"iphone", + "size":"29x29", + "scale":"3x", + "filename":"Icon-App-29x29@3x.png" + }, + { + "idiom":"iphone", + "size":"40x40", + "scale":"1x", + "filename":"Icon-App-40x40@1x.png" + }, + { + "idiom":"iphone", + "size":"40x40", + "scale":"2x", + "filename":"Icon-App-40x40@2x.png" + }, + { + "idiom":"iphone", + "size":"40x40", + "scale":"3x", + "filename":"Icon-App-40x40@3x.png" + }, + { + "idiom":"iphone", + "size":"57x57", + "scale":"1x", + "filename":"Icon-App-57x57@1x.png" + }, + { + "idiom":"iphone", + "size":"57x57", + "scale":"2x", + "filename":"Icon-App-57x57@2x.png" + }, + { + "idiom":"iphone", + "size":"60x60", + "scale":"1x", + "filename":"Icon-App-60x60@1x.png" + }, + { + "idiom":"iphone", + "size":"60x60", + "scale":"2x", + "filename":"Icon-App-60x60@2x.png" + }, + { + "idiom":"iphone", + "size":"60x60", + "scale":"3x", + "filename":"Icon-App-60x60@3x.png" + }, + { + "idiom":"iphone", + "size":"76x76", + "scale":"1x", + "filename":"Icon-App-76x76@1x.png" + }, + { + "idiom":"ipad", + "size":"20x20", + "scale":"1x", + "filename":"Icon-App-20x20@1x.png" + }, + { + "idiom":"ipad", + "size":"20x20", + "scale":"2x", + "filename":"Icon-App-20x20@2x.png" + }, + { + "idiom":"ipad", + "size":"29x29", + "scale":"1x", + "filename":"Icon-App-29x29@1x.png" + }, + { + "idiom":"ipad", + "size":"29x29", + "scale":"2x", + "filename":"Icon-App-29x29@2x.png" + }, + { + "idiom":"ipad", + "size":"40x40", + "scale":"1x", + "filename":"Icon-App-40x40@1x.png" + }, + { + "idiom":"ipad", + "size":"40x40", + "scale":"2x", + "filename":"Icon-App-40x40@2x.png" + }, + { + "size" : "50x50", + "idiom" : "ipad", + "filename" : "Icon-Small-50x50@1x.png", + "scale" : "1x" + }, + { + "size" : "50x50", + "idiom" : "ipad", + "filename" : "Icon-Small-50x50@2x.png", + "scale" : "2x" + }, + { + "idiom":"ipad", + "size":"72x72", + "scale":"1x", + "filename":"Icon-App-72x72@1x.png" + }, + { + "idiom":"ipad", + "size":"72x72", + "scale":"2x", + "filename":"Icon-App-72x72@2x.png" + }, + { + "idiom":"ipad", + "size":"76x76", + "scale":"1x", + "filename":"Icon-App-76x76@1x.png" + }, + { + "idiom":"ipad", + "size":"76x76", + "scale":"2x", + "filename":"Icon-App-76x76@2x.png" + }, + { + "idiom":"ipad", + "size":"76x76", + "scale":"3x", + "filename":"Icon-App-76x76@3x.png" + }, + { + "idiom":"ipad", + "size":"83.5x83.5", + "scale":"2x", + "filename":"Icon-App-83.5x83.5@2x.png" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "ItunesArtwork@2x.png", + "scale" : "1x" + } + ], + "info":{ + "version":1, + "author":"makeappicon" + } +} diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000..32ce105 Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000..5a1ea68 Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000..bba2b81 Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000..77ac218 Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000..9eefe24 Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000..195a858 Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000..5a1ea68 Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000..4331ca3 Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000..6db4e69 Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png new file mode 100644 index 0000000..04fe8fe Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png new file mode 100644 index 0000000..41b3953 Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@1x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@1x.png new file mode 100644 index 0000000..bba2b81 Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@1x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000..6db4e69 Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000..2fd57be Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png new file mode 100644 index 0000000..ecdb966 Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png new file mode 100644 index 0000000..e4f319a Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000..8a3ba65 Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000..5f5bdc2 Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@3x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@3x.png new file mode 100644 index 0000000..185f4b5 Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@3x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000..d0cb8fe Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png new file mode 100644 index 0000000..21d8bdc Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@1x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png new file mode 100644 index 0000000..23aff71 Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/Icon-Small-50x50@2x.png differ diff --git a/Meditation/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png b/Meditation/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png new file mode 100644 index 0000000..2d800bc Binary files /dev/null and b/Meditation/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png differ diff --git a/Meditation/Assets.xcassets/Contents.json b/Meditation/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Meditation/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/Contents.json b/Meditation/Assets.xcassets/Icons/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/aim.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/aim.imageset/Contents.json new file mode 100644 index 0000000..1095c88 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/aim.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "aim.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/aim.imageset/aim.png b/Meditation/Assets.xcassets/Icons/aim.imageset/aim.png new file mode 100644 index 0000000..9959d41 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/aim.imageset/aim.png differ diff --git a/Meditation/Assets.xcassets/Icons/analyze.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/analyze.imageset/Contents.json new file mode 100644 index 0000000..7fd3a39 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/analyze.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "analyze.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/analyze.imageset/analyze.png b/Meditation/Assets.xcassets/Icons/analyze.imageset/analyze.png new file mode 100644 index 0000000..95c9194 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/analyze.imageset/analyze.png differ diff --git a/Meditation/Assets.xcassets/Icons/app.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/app.imageset/Contents.json new file mode 100644 index 0000000..217ccdd --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/app.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "app.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/app.imageset/app.png b/Meditation/Assets.xcassets/Icons/app.imageset/app.png new file mode 100644 index 0000000..3a00313 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/app.imageset/app.png differ diff --git a/Meditation/Assets.xcassets/Icons/apps.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/apps.imageset/Contents.json new file mode 100644 index 0000000..ebf5d8b --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/apps.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "apps.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/apps.imageset/apps.png b/Meditation/Assets.xcassets/Icons/apps.imageset/apps.png new file mode 100644 index 0000000..1b3ce48 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/apps.imageset/apps.png differ diff --git a/Meditation/Assets.xcassets/Icons/article.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/article.imageset/Contents.json new file mode 100644 index 0000000..145813e --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/article.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "article.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/article.imageset/article.png b/Meditation/Assets.xcassets/Icons/article.imageset/article.png new file mode 100644 index 0000000..0b22086 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/article.imageset/article.png differ diff --git a/Meditation/Assets.xcassets/Icons/atom.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/atom.imageset/Contents.json new file mode 100644 index 0000000..cde368d --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/atom.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "atom.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/atom.imageset/atom.png b/Meditation/Assets.xcassets/Icons/atom.imageset/atom.png new file mode 100644 index 0000000..f2dc9d7 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/atom.imageset/atom.png differ diff --git a/Meditation/Assets.xcassets/Icons/balance.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/balance.imageset/Contents.json new file mode 100644 index 0000000..d021b9b --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/balance.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "balance.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/balance.imageset/balance.png b/Meditation/Assets.xcassets/Icons/balance.imageset/balance.png new file mode 100644 index 0000000..38eeddf Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/balance.imageset/balance.png differ diff --git a/Meditation/Assets.xcassets/Icons/bank.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/bank.imageset/Contents.json new file mode 100644 index 0000000..4046ed4 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/bank.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "bank.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/bank.imageset/bank.png b/Meditation/Assets.xcassets/Icons/bank.imageset/bank.png new file mode 100644 index 0000000..0a92b77 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/bank.imageset/bank.png differ diff --git a/Meditation/Assets.xcassets/Icons/basketball.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/basketball.imageset/Contents.json new file mode 100644 index 0000000..f2e9610 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/basketball.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "basketball.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/basketball.imageset/basketball.png b/Meditation/Assets.xcassets/Icons/basketball.imageset/basketball.png new file mode 100644 index 0000000..0e75921 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/basketball.imageset/basketball.png differ diff --git a/Meditation/Assets.xcassets/Icons/battery_charging.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/battery_charging.imageset/Contents.json new file mode 100644 index 0000000..5e2ef91 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/battery_charging.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "battery_charging.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/battery_charging.imageset/battery_charging.png b/Meditation/Assets.xcassets/Icons/battery_charging.imageset/battery_charging.png new file mode 100644 index 0000000..c681d2e Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/battery_charging.imageset/battery_charging.png differ diff --git a/Meditation/Assets.xcassets/Icons/best_product.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/best_product.imageset/Contents.json new file mode 100644 index 0000000..d6af73b --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/best_product.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "best_product.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/best_product.imageset/best_product.png b/Meditation/Assets.xcassets/Icons/best_product.imageset/best_product.png new file mode 100644 index 0000000..d7acc6f Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/best_product.imageset/best_product.png differ diff --git a/Meditation/Assets.xcassets/Icons/brightness.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/brightness.imageset/Contents.json new file mode 100644 index 0000000..3e46a8c --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/brightness.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "brightness.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/brightness.imageset/brightness.png b/Meditation/Assets.xcassets/Icons/brightness.imageset/brightness.png new file mode 100644 index 0000000..5e20057 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/brightness.imageset/brightness.png differ diff --git a/Meditation/Assets.xcassets/Icons/bulb.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/bulb.imageset/Contents.json new file mode 100644 index 0000000..8870412 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/bulb.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "bulb.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/bulb.imageset/bulb.png b/Meditation/Assets.xcassets/Icons/bulb.imageset/bulb.png new file mode 100644 index 0000000..7d49d0d Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/bulb.imageset/bulb.png differ diff --git a/Meditation/Assets.xcassets/Icons/calculator.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/calculator.imageset/Contents.json new file mode 100644 index 0000000..7969903 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/calculator.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "calculator.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/calculator.imageset/calculator.png b/Meditation/Assets.xcassets/Icons/calculator.imageset/calculator.png new file mode 100644 index 0000000..578d245 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/calculator.imageset/calculator.png differ diff --git a/Meditation/Assets.xcassets/Icons/camera.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/camera.imageset/Contents.json new file mode 100644 index 0000000..1518837 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/camera.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "camera.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/camera.imageset/camera.png b/Meditation/Assets.xcassets/Icons/camera.imageset/camera.png new file mode 100644 index 0000000..5abe52e Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/camera.imageset/camera.png differ diff --git a/Meditation/Assets.xcassets/Icons/certificate.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/certificate.imageset/Contents.json new file mode 100644 index 0000000..ee82ddb --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/certificate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "certificate.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/certificate.imageset/certificate.png b/Meditation/Assets.xcassets/Icons/certificate.imageset/certificate.png new file mode 100644 index 0000000..a16236f Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/certificate.imageset/certificate.png differ diff --git a/Meditation/Assets.xcassets/Icons/checkmark.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/checkmark.imageset/Contents.json new file mode 100644 index 0000000..07764d4 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/checkmark.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "checkmark.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/checkmark.imageset/checkmark.png b/Meditation/Assets.xcassets/Icons/checkmark.imageset/checkmark.png new file mode 100644 index 0000000..8ce49af Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/checkmark.imageset/checkmark.png differ diff --git a/Meditation/Assets.xcassets/Icons/cloud.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/cloud.imageset/Contents.json new file mode 100644 index 0000000..3868788 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/cloud.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "cloud.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/cloud.imageset/cloud.png b/Meditation/Assets.xcassets/Icons/cloud.imageset/cloud.png new file mode 100644 index 0000000..f247fa4 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/cloud.imageset/cloud.png differ diff --git a/Meditation/Assets.xcassets/Icons/code.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/code.imageset/Contents.json new file mode 100644 index 0000000..c749ad0 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/code.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "code.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/code.imageset/code.png b/Meditation/Assets.xcassets/Icons/code.imageset/code.png new file mode 100644 index 0000000..6e122b3 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/code.imageset/code.png differ diff --git a/Meditation/Assets.xcassets/Icons/coffee.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/coffee.imageset/Contents.json new file mode 100644 index 0000000..f292d11 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/coffee.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "coffee.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/coffee.imageset/coffee.png b/Meditation/Assets.xcassets/Icons/coffee.imageset/coffee.png new file mode 100644 index 0000000..36aff68 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/coffee.imageset/coffee.png differ diff --git a/Meditation/Assets.xcassets/Icons/compass.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/compass.imageset/Contents.json new file mode 100644 index 0000000..6427f2b --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/compass.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "compass.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/compass.imageset/compass.png b/Meditation/Assets.xcassets/Icons/compass.imageset/compass.png new file mode 100644 index 0000000..bd6246f Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/compass.imageset/compass.png differ diff --git a/Meditation/Assets.xcassets/Icons/computer.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/computer.imageset/Contents.json new file mode 100644 index 0000000..64943b3 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/computer.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "computer.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/computer.imageset/computer.png b/Meditation/Assets.xcassets/Icons/computer.imageset/computer.png new file mode 100644 index 0000000..70468ef Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/computer.imageset/computer.png differ diff --git a/Meditation/Assets.xcassets/Icons/controller.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/controller.imageset/Contents.json new file mode 100644 index 0000000..36b72f2 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/controller.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "controller.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/controller.imageset/controller.png b/Meditation/Assets.xcassets/Icons/controller.imageset/controller.png new file mode 100644 index 0000000..9355f6c Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/controller.imageset/controller.png differ diff --git a/Meditation/Assets.xcassets/Icons/creative.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/creative.imageset/Contents.json new file mode 100644 index 0000000..4724ac6 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/creative.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "creative.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/creative.imageset/creative.png b/Meditation/Assets.xcassets/Icons/creative.imageset/creative.png new file mode 100644 index 0000000..d30bc85 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/creative.imageset/creative.png differ diff --git a/Meditation/Assets.xcassets/Icons/diamond-1.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/diamond-1.imageset/Contents.json new file mode 100644 index 0000000..cfe5afe --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/diamond-1.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "diamond.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/diamond-1.imageset/diamond.png b/Meditation/Assets.xcassets/Icons/diamond-1.imageset/diamond.png new file mode 100644 index 0000000..6813067 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/diamond-1.imageset/diamond.png differ diff --git a/Meditation/Assets.xcassets/Icons/diamond.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/diamond.imageset/Contents.json new file mode 100644 index 0000000..cfe5afe --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/diamond.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "diamond.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/diamond.imageset/diamond.png b/Meditation/Assets.xcassets/Icons/diamond.imageset/diamond.png new file mode 100644 index 0000000..46eb05c Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/diamond.imageset/diamond.png differ diff --git a/Meditation/Assets.xcassets/Icons/experiment.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/experiment.imageset/Contents.json new file mode 100644 index 0000000..90e510d --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/experiment.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "experiment.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/experiment.imageset/experiment.png b/Meditation/Assets.xcassets/Icons/experiment.imageset/experiment.png new file mode 100644 index 0000000..9027467 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/experiment.imageset/experiment.png differ diff --git a/Meditation/Assets.xcassets/Icons/fountain-pen-tip.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/fountain-pen-tip.imageset/Contents.json new file mode 100644 index 0000000..7d23c3f --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/fountain-pen-tip.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "fountain-pen-tip.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/fountain-pen-tip.imageset/fountain-pen-tip.png b/Meditation/Assets.xcassets/Icons/fountain-pen-tip.imageset/fountain-pen-tip.png new file mode 100644 index 0000000..b8f83f1 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/fountain-pen-tip.imageset/fountain-pen-tip.png differ diff --git a/Meditation/Assets.xcassets/Icons/fountain-pen.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/fountain-pen.imageset/Contents.json new file mode 100644 index 0000000..b140f9d --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/fountain-pen.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "fountain-pen.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/fountain-pen.imageset/fountain-pen.png b/Meditation/Assets.xcassets/Icons/fountain-pen.imageset/fountain-pen.png new file mode 100644 index 0000000..8d341bb Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/fountain-pen.imageset/fountain-pen.png differ diff --git a/Meditation/Assets.xcassets/Icons/games_control.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/games_control.imageset/Contents.json new file mode 100644 index 0000000..dd4b98b --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/games_control.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "games_control.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/games_control.imageset/games_control.png b/Meditation/Assets.xcassets/Icons/games_control.imageset/games_control.png new file mode 100644 index 0000000..e0eb304 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/games_control.imageset/games_control.png differ diff --git a/Meditation/Assets.xcassets/Icons/gear.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/gear.imageset/Contents.json new file mode 100644 index 0000000..5c76825 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/gear.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "gear.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/gear.imageset/gear.png b/Meditation/Assets.xcassets/Icons/gear.imageset/gear.png new file mode 100644 index 0000000..be2babd Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/gear.imageset/gear.png differ diff --git a/Meditation/Assets.xcassets/Icons/glasses.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/glasses.imageset/Contents.json new file mode 100644 index 0000000..16616fe --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/glasses.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "glasses.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/glasses.imageset/glasses.png b/Meditation/Assets.xcassets/Icons/glasses.imageset/glasses.png new file mode 100644 index 0000000..f981162 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/glasses.imageset/glasses.png differ diff --git a/Meditation/Assets.xcassets/Icons/growth.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/growth.imageset/Contents.json new file mode 100644 index 0000000..8ee7dee --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/growth.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "growth.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/growth.imageset/growth.png b/Meditation/Assets.xcassets/Icons/growth.imageset/growth.png new file mode 100644 index 0000000..7290cb9 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/growth.imageset/growth.png differ diff --git a/Meditation/Assets.xcassets/Icons/head.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/head.imageset/Contents.json new file mode 100644 index 0000000..557c228 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/head.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "head.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/head.imageset/head.png b/Meditation/Assets.xcassets/Icons/head.imageset/head.png new file mode 100644 index 0000000..e690181 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/head.imageset/head.png differ diff --git a/Meditation/Assets.xcassets/Icons/hourglass.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/hourglass.imageset/Contents.json new file mode 100644 index 0000000..a3c2635 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/hourglass.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "hourglass.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/hourglass.imageset/hourglass.png b/Meditation/Assets.xcassets/Icons/hourglass.imageset/hourglass.png new file mode 100644 index 0000000..72cec25 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/hourglass.imageset/hourglass.png differ diff --git a/Meditation/Assets.xcassets/Icons/key.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/key.imageset/Contents.json new file mode 100644 index 0000000..7dea08f --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/key.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "key.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/key.imageset/key.png b/Meditation/Assets.xcassets/Icons/key.imageset/key.png new file mode 100644 index 0000000..6924713 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/key.imageset/key.png differ diff --git a/Meditation/Assets.xcassets/Icons/leaf.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/leaf.imageset/Contents.json new file mode 100644 index 0000000..ed080d7 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/leaf.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "leaf.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/leaf.imageset/leaf.png b/Meditation/Assets.xcassets/Icons/leaf.imageset/leaf.png new file mode 100644 index 0000000..8b1218e Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/leaf.imageset/leaf.png differ diff --git a/Meditation/Assets.xcassets/Icons/letter.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/letter.imageset/Contents.json new file mode 100644 index 0000000..1d33f08 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/letter.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "letter.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/letter.imageset/letter.png b/Meditation/Assets.xcassets/Icons/letter.imageset/letter.png new file mode 100644 index 0000000..9e292d2 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/letter.imageset/letter.png differ diff --git a/Meditation/Assets.xcassets/Icons/monitor.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/monitor.imageset/Contents.json new file mode 100644 index 0000000..3159c74 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/monitor.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "monitor.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/monitor.imageset/monitor.png b/Meditation/Assets.xcassets/Icons/monitor.imageset/monitor.png new file mode 100644 index 0000000..3b9e576 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/monitor.imageset/monitor.png differ diff --git a/Meditation/Assets.xcassets/Icons/prototype.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/prototype.imageset/Contents.json new file mode 100644 index 0000000..5eda8bd --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/prototype.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "prototype.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/prototype.imageset/prototype.png b/Meditation/Assets.xcassets/Icons/prototype.imageset/prototype.png new file mode 100644 index 0000000..485e0fc Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/prototype.imageset/prototype.png differ diff --git a/Meditation/Assets.xcassets/Icons/rocket.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/rocket.imageset/Contents.json new file mode 100644 index 0000000..da97f31 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/rocket.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "rocket.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/rocket.imageset/rocket.png b/Meditation/Assets.xcassets/Icons/rocket.imageset/rocket.png new file mode 100644 index 0000000..c0e97cb Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/rocket.imageset/rocket.png differ diff --git a/Meditation/Assets.xcassets/Icons/satellite-dish.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/satellite-dish.imageset/Contents.json new file mode 100644 index 0000000..0ab22e5 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/satellite-dish.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "satellite-dish.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/satellite-dish.imageset/satellite-dish.png b/Meditation/Assets.xcassets/Icons/satellite-dish.imageset/satellite-dish.png new file mode 100644 index 0000000..350a6bf Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/satellite-dish.imageset/satellite-dish.png differ diff --git a/Meditation/Assets.xcassets/Icons/satellite.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/satellite.imageset/Contents.json new file mode 100644 index 0000000..fe562ef --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/satellite.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "satellite.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/satellite.imageset/satellite.png b/Meditation/Assets.xcassets/Icons/satellite.imageset/satellite.png new file mode 100644 index 0000000..e745a01 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/satellite.imageset/satellite.png differ diff --git a/Meditation/Assets.xcassets/Icons/search.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/search.imageset/Contents.json new file mode 100644 index 0000000..5c7a9de --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/search.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "search.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/search.imageset/search.png b/Meditation/Assets.xcassets/Icons/search.imageset/search.png new file mode 100644 index 0000000..83515a4 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/search.imageset/search.png differ diff --git a/Meditation/Assets.xcassets/Icons/speaker.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/speaker.imageset/Contents.json new file mode 100644 index 0000000..54230ac --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/speaker.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "speaker.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/speaker.imageset/speaker.png b/Meditation/Assets.xcassets/Icons/speaker.imageset/speaker.png new file mode 100644 index 0000000..1b7298e Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/speaker.imageset/speaker.png differ diff --git a/Meditation/Assets.xcassets/Icons/store.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/store.imageset/Contents.json new file mode 100644 index 0000000..3428985 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/store.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "store.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/store.imageset/store.png b/Meditation/Assets.xcassets/Icons/store.imageset/store.png new file mode 100644 index 0000000..69851a2 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/store.imageset/store.png differ diff --git a/Meditation/Assets.xcassets/Icons/telescope.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/telescope.imageset/Contents.json new file mode 100644 index 0000000..7441f74 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/telescope.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "telescope.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/telescope.imageset/telescope.png b/Meditation/Assets.xcassets/Icons/telescope.imageset/telescope.png new file mode 100644 index 0000000..6840fd7 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/telescope.imageset/telescope.png differ diff --git a/Meditation/Assets.xcassets/Icons/trophy.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/trophy.imageset/Contents.json new file mode 100644 index 0000000..f22e83f --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/trophy.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "trophy.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/trophy.imageset/trophy.png b/Meditation/Assets.xcassets/Icons/trophy.imageset/trophy.png new file mode 100644 index 0000000..a5f5ee5 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/trophy.imageset/trophy.png differ diff --git a/Meditation/Assets.xcassets/Icons/virus-autoupdate.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/virus-autoupdate.imageset/Contents.json new file mode 100644 index 0000000..8667f86 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/virus-autoupdate.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "virus-autoupdate.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/virus-autoupdate.imageset/virus-autoupdate.png b/Meditation/Assets.xcassets/Icons/virus-autoupdate.imageset/virus-autoupdate.png new file mode 100644 index 0000000..61af3c7 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/virus-autoupdate.imageset/virus-autoupdate.png differ diff --git a/Meditation/Assets.xcassets/Icons/virus.imageset/Contents.json b/Meditation/Assets.xcassets/Icons/virus.imageset/Contents.json new file mode 100644 index 0000000..1707a17 --- /dev/null +++ b/Meditation/Assets.xcassets/Icons/virus.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "virus.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Icons/virus.imageset/virus.png b/Meditation/Assets.xcassets/Icons/virus.imageset/virus.png new file mode 100644 index 0000000..64766b0 Binary files /dev/null and b/Meditation/Assets.xcassets/Icons/virus.imageset/virus.png differ diff --git a/Meditation/Assets.xcassets/Logo.imageset/Contents.json b/Meditation/Assets.xcassets/Logo.imageset/Contents.json new file mode 100644 index 0000000..0723449 --- /dev/null +++ b/Meditation/Assets.xcassets/Logo.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "iTunesArtwork@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/Logo.imageset/iTunesArtwork@2x.png b/Meditation/Assets.xcassets/Logo.imageset/iTunesArtwork@2x.png new file mode 100644 index 0000000..2d800bc Binary files /dev/null and b/Meditation/Assets.xcassets/Logo.imageset/iTunesArtwork@2x.png differ diff --git a/Meditation/Assets.xcassets/transparent.imageset/Contents.json b/Meditation/Assets.xcassets/transparent.imageset/Contents.json new file mode 100644 index 0000000..9bfe33f --- /dev/null +++ b/Meditation/Assets.xcassets/transparent.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "transparent.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Meditation/Assets.xcassets/transparent.imageset/transparent.png b/Meditation/Assets.xcassets/transparent.imageset/transparent.png new file mode 100644 index 0000000..8171698 Binary files /dev/null and b/Meditation/Assets.xcassets/transparent.imageset/transparent.png differ diff --git a/Meditation/Base.lproj/Main.storyboard b/Meditation/Base.lproj/Main.storyboard new file mode 100644 index 0000000..4482bb9 --- /dev/null +++ b/Meditation/Base.lproj/Main.storyboard @@ -0,0 +1,991 @@ + + + + + + + + + + + + + + GothamPro-Bold + + + GothamPro-Medium + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Meditation/Meditation.entitlements b/Meditation/Meditation.entitlements new file mode 100644 index 0000000..2eb7e33 --- /dev/null +++ b/Meditation/Meditation.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.application-groups + + + diff --git a/Meditation/Meditation/ChooseSound.swift b/Meditation/Meditation/ChooseSound.swift new file mode 100644 index 0000000..cc941f0 --- /dev/null +++ b/Meditation/Meditation/ChooseSound.swift @@ -0,0 +1,101 @@ +import UIKit +import SwiftySound + + +class ChooseSound: UITableViewController { + + @IBOutlet var backButton: UIBarButtonItem! + private var selectedSound: String! + + + + /* MARK: Initialising + /////////////////////////////////////////// */ + override func viewDidLoad() { + // Styling + Utils.createFontAwesomeBarButton(button: backButton, icon: .arrowLeft, style: .solid) + } + + override func viewWillAppear(_ animated: Bool) { + + } + + override var prefersStatusBarHidden: Bool { + return true + } + + + + /* MARK: Actions + /////////////////////////////////////////// */ + @IBAction func backButtonPressed() { + Utils.presentView(self, viewName: Constants.Views.RUN_MEDITATION) + } + + + + /* MARK: Table + /////////////////////////////////////////// */ + override func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return Constants.Meditations.MEDITATION_SOUNDS.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + var cell = tableView.dequeueReusableCell(withIdentifier: "Cell") + if(cell == nil) { cell = UITableViewCell(style: UITableViewCellStyle.default, reuseIdentifier: "Cell") } + + let sound = Constants.Meditations.MEDITATION_SOUNDS[indexPath.row][0] as? String + cell!.textLabel!.text = sound?.replacingOccurrences(of: "_", with: " ") + + // Style + cell!.backgroundColor = UIColor.clear + cell!.textLabel?.font = UIFont.GothamProMedium(size: 15.0) + cell!.textLabel?.textColor = UIColor(hex: Constants.Colors.PRIMARY_TEXT_GRAY) + cell!.tintColor = UIColor(hex: Constants.Colors.PRIMARY_TEXT_GRAY) + cell!.selectionStyle = .none + + // Add tick + cell!.accessoryType = UITableViewCellAccessoryType.none + if Utils.contains(key: Constants.Defaults.SELECTED_MEDITATION_SOUND) { + selectedSound = Utils.string(key: Constants.Defaults.SELECTED_MEDITATION_SOUND) + } + if cell!.textLabel!.text == selectedSound { + cell!.accessoryType = UITableViewCellAccessoryType.checkmark + } + + return cell! + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let cell = tableView.cellForRow(at: indexPath) + + if (Constants.Meditations.MEDITATION_SOUNDS[indexPath.row][1] as? Bool)! && !Purchase.hasUpgraded() { // Premium sound + Utils.set(key: Constants.Defaults.PREVIOUS_VIEW, value: Constants.Views.CHOOSE_SOUND_NAV_CONTROLLER) + Utils.presentView(self, viewName: Constants.Views.UPGRADE) + } + else { + selectedSound = cell?.textLabel?.text!.replacingOccurrences(of: " ", with: "_") + Utils.set(key: Constants.Defaults.SELECTED_MEDITATION_SOUND, value: selectedSound) + + // Music + Sound.stopAll() + Sound.play(file: (Constants.Meditations.MEDITATION_SOUNDS[indexPath.row][0] as? String)!, fileExtension: "mp3", numberOfLoops: -1) + + // Add tick + cell?.accessoryType = UITableViewCellAccessoryType.checkmark + + // Remove tick + let cells = tableView.visibleCells + for c in cells { + let section = tableView.indexPath(for: c)?.section + if (section == indexPath.section && c != cell) { + c.accessoryType = UITableViewCellAccessoryType.none + } + } + } + } +} diff --git a/Meditation/Meditation/Meditation.swift b/Meditation/Meditation/Meditation.swift new file mode 100644 index 0000000..316183d --- /dev/null +++ b/Meditation/Meditation/Meditation.swift @@ -0,0 +1,144 @@ +import UIKit + + +class Meditation: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout { + + @IBOutlet var collectionView: UICollectionView! + @IBOutlet var exerciseLabel: UILabel! + @IBOutlet var goalLabel: UILabel! + @IBOutlet var titleLabel: UILabel! + @IBOutlet var textLabel: UILabel! + @IBOutlet var goButton: UIButton! + @IBOutlet var achievementsButton: UIButton! + @IBOutlet var menuButton: UIBarButtonItem! + + + + /* MARK: Initialising + /////////////////////////////////////////// */ + override func viewDidLoad() { + // Styling + goButton.layer.borderColor = UIColor.white.cgColor + goButton.layer.borderWidth = 6.0 + achievementsButton.layer.borderColor = UIColor.white.cgColor + achievementsButton.layer.borderWidth = 6.0 + + Utils.createFontAwesomeBarButton(button: menuButton, icon: .bars, style: .solid) + Utils.createFontAwesomeButton(button: achievementsButton, size: 15.0, icon: .gem, style: .regular) + } + + + override func viewWillAppear(_ animated: Bool) { + // Reset + Utils.remove(key: Constants.Defaults.SELECTED_MEDITATION_SOUND) + Utils.remove(key: Constants.Defaults.PREVIOUS_VIEW) + + if Utils.contains(key: Constants.Defaults.SELECTED_MEDITATION) { + setMeditation(index: Utils.int(key: Constants.Defaults.SELECTED_MEDITATION)) + } + else { + setMeditation(index: 0) + } + + // Has the user reached an achievement? + ProgressManager.checkAndSetAchievementReached(view: self, type: Constants.Achievements.POINTS_TYPE) + ProgressManager.checkAndSetAchievementReached(view: self, type: Constants.Achievements.SESSIONS_TYPE) + ProgressManager.checkAndSetAchievementReached(view: self, type: Constants.Achievements.TIME_MEDITATING_TYPE) + + // TODO: JOEY: show level up dialog +// let points = Utils.int(key: Constants.Defaults.APP_DATA_TOTAL_POINTS) +// if(ProgressManager.shouldLevelUp(points: (points - 3))) { +// Dialogs.showLevelUpDialog(view: self, level: String(ProgressManager.getLevel(points: (points - 3)))) +// } + } + + + override var prefersStatusBarHidden: Bool { + return true + } + + + + /* MARK: Button Actions + /////////////////////////////////////////// */ + @IBAction func achievementsButtonPressed() { + Utils.presentView(self, viewName: Constants.Views.ACHIEVEMENTS) + } + + @IBAction func goButtonPressed() { + Utils.presentView(self, viewName: Constants.Views.RUN_MEDITATION) + } + + @IBAction func menuButtonPressed(_ sender: AnyObject) { + Utils.presentView(self, viewName: Constants.Views.SETTINGS_NAV_CONTROLLER) + } + + + + /* MARK: Core + /////////////////////////////////////////// */ + func setMeditation(index: Int) { + let meditation = Constants.Meditations.MEDITATIONS_ALL[index] as [Any] + exerciseLabel.text = meditation[1] as? String + goalLabel.text = "• " + (meditation[2] as? String)! + " •" + titleLabel.text = meditation[3] as? String + + let attributedString = NSMutableAttributedString(string: meditation[4] as! String) + let paragraphStyle = NSMutableParagraphStyle() + paragraphStyle.lineSpacing = 8 + attributedString.addAttribute(kCTParagraphStyleAttributeName as NSAttributedStringKey, value:paragraphStyle, range:NSMakeRange(0, attributedString.length)) + textLabel.attributedText = attributedString + + Utils.set(key: Constants.Defaults.SELECTED_MEDITATION, value: index) + } + + + /* MARK: Collection View + /////////////////////////////////////////// */ + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return Constants.Meditations.MEDITATIONS_ALL.count + } + + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "meditationCollectionViewCell", for: indexPath) as! MeditationCollectionViewCell + + let meditation = Constants.Meditations.MEDITATIONS_ALL[indexPath.row] as [Any] + + cell.containerImageView.image = UIImage(named: meditation[0] as! String) + + return cell + } + + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + setMeditation(index: indexPath.row) + } + + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + let width = collectionView.bounds.width / 5 + return CGSize(width: width, height: width) + } + + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { + return UIEdgeInsets.zero + } + + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return 0 + } + + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return 0 + } +} + + + +class MeditationCollectionViewCell: UICollectionViewCell { + @IBOutlet var containerImageView: UIImageView! +} diff --git a/Meditation/Meditation/RunMeditation.swift b/Meditation/Meditation/RunMeditation.swift new file mode 100644 index 0000000..02cec3a --- /dev/null +++ b/Meditation/Meditation/RunMeditation.swift @@ -0,0 +1,218 @@ +import UIKit +import FontAwesome_swift +import KYCircularProgress +import SwiftySound + + +class RunMeditation: UIViewController { + + @IBOutlet var bgImageView: UIImageView! + @IBOutlet var titleLabel: UILabel! + @IBOutlet var soundButton: UIButton! + @IBOutlet var chooseSoundButton: UIButton! + + // Timer + private var circularProgressView: KYCircularProgress! + private var circularProgressValue: Double = 0.0 + private var timer: Timer! + + // To measure length of meditation + var startTime: CFAbsoluteTime = 0.0 + var endTime: CFAbsoluteTime? + + private var meditation: [Any] = [] + private var sound: String! + + + + /* MARK: Initialising + /////////////////////////////////////////// */ + override func viewWillAppear(_ animated: Bool) { + meditation = Constants.Meditations.MEDITATIONS_ALL[Utils.int(key: Constants.Defaults.SELECTED_MEDITATION)] + + // Background + bgImageView.image = UIImage(named: meditation[0] as! String) + let overlay: UIView = UIView(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: view.frame.size.height)) + overlay.backgroundColor = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 0.1) + bgImageView.addSubview(overlay) + + // Styling + soundButton.layer.borderWidth = 2 + soundButton.layer.borderColor = UIColor.white.cgColor + chooseSoundButton.layer.borderWidth = 2 + chooseSoundButton.layer.borderColor = UIColor.white.cgColor + Utils.createFontAwesomeButton(button: soundButton, size: 30, icon: .volumeUp, style: .solid) + Utils.createFontAwesomeButton(button: chooseSoundButton, size: 21, icon: .music, style: .solid) + + // Music + playSound() + + initProgressView() + initPhaseOne() + + startTime = CFAbsoluteTimeGetCurrent() + } + + override var prefersStatusBarHidden: Bool { + return true + } + + + + /* MARK: Core + /////////////////////////////////////////// */ + func playSound() { + sound = meditation[5] as! String + if Utils.contains(key: Constants.Defaults.SELECTED_MEDITATION_SOUND) { // Avoid sound break + sound = Utils.string(key: Constants.Defaults.SELECTED_MEDITATION_SOUND) + } + else { + if !Utils.contains(key: Constants.Defaults.PREVIOUS_VIEW) { // If ChooseSound is opened & closed without choosing + Sound.enabled = true + Sound.play(file: sound, fileExtension: "mp3", numberOfLoops: -1) + } + } + } + + func stop() -> CFAbsoluteTime { + endTime = CFAbsoluteTimeGetCurrent() + return duration! + } + + var duration: CFAbsoluteTime? { + if let endTime = endTime { + return endTime - startTime + } else { + return nil + } + } + + + + /* MARK: Button Actions + /////////////////////////////////////////// */ + @IBAction func backButtonPressed() { + Sound.stopAll() + + let timeElapsed = stop() + + // sessions + var sessions = Utils.int(key: Constants.Defaults.APP_DATA_TOTAL_SESSIONS) + var totalPoints = Utils.int(key: Constants.Defaults.APP_DATA_TOTAL_POINTS) + if timeElapsed >= 180.0 { // default of 3 minutes to earn reward + sessions += 1 + totalPoints += 3 + } + Utils.set(key: Constants.Defaults.APP_DATA_TOTAL_SESSIONS, value: sessions) + Utils.set(key: Constants.Defaults.APP_DATA_TOTAL_POINTS, value: totalPoints) + + + // total time meditating + var totalTimeMeditating = Utils.double(key: Constants.Defaults.APP_DATA_TOTAL_TIME_MEDITATING) + totalTimeMeditating += (timeElapsed / 60) + Utils.set(key: Constants.Defaults.APP_DATA_TOTAL_TIME_MEDITATING, value: totalTimeMeditating) + + + Utils.presentView(self, viewName: Constants.Views.MEDITATION) + } + + @IBAction func soundButtonPressed() { + Sound.enabled = !Sound.enabled + Sound.play(file: sound, fileExtension: "mp3", numberOfLoops: -1) + + if Sound.enabled { + soundButton.setTitle(String.fontAwesomeIcon(name: .volumeUp), for: .normal) + } + else { + soundButton.setTitle(String.fontAwesomeIcon(name: .volumeOff), for: .normal) + } + } + + @IBAction func chooseSoundButtonPressed() { + Utils.presentView(self, viewName: Constants.Views.CHOOSE_SOUND_NAV_CONTROLLER) + } + + + + /* MARK: Progress Functionality + /////////////////////////////////////////// */ + func initProgressView() { + let progressView = KYCircularProgress(frame: CGRect(x: (UIScreen.main.bounds.width / 2) - 75, y: (UIScreen.main.bounds.height / 3) - 50, width: 150, height: 150), showGuide: true) + circularProgressView = Utils.createProgressView(progressView: progressView, color: "fff", guideColor: Constants.Colors.CIRCULAR_MEDITATION_GRAY) + self.view.addSubview(circularProgressView) + } + + + func initPhaseOne() { + let phaseLength = meditation[7] as! Double + Utils.set(key: Constants.Defaults.MEDITATION_PHASE_LENGTH, value: phaseLength) + + titleLabel.text = meditation[6] as? String + + circularProgressValue = 0 + + if timer != nil { + timer.invalidate() + timer = nil + } + timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(self.forwardProgress), userInfo: nil, repeats: true) + + DispatchQueue.main.asyncAfter(deadline: .now() + 5) { + self.initPhaseTwo() + } + } + + + func initPhaseTwo() { + let phaseLength = meditation[9] as! Double + Utils.set(key: Constants.Defaults.MEDITATION_PHASE_LENGTH, value: phaseLength) + + titleLabel.text = meditation[8] as? String + + DispatchQueue.main.asyncAfter(deadline: .now() + phaseLength) { + self.initPhaseThree() + } + } + + + func initPhaseThree() { + let phaseLength = meditation[11] as! Double + Utils.set(key: Constants.Defaults.MEDITATION_PHASE_LENGTH, value: phaseLength) + + titleLabel.text = meditation[10] as? String + + circularProgressValue = 1 + + timer.invalidate() + timer = nil + timer = Timer.scheduledTimer(timeInterval: 0.01, target: self, selector: #selector(self.reverseProgress), userInfo: nil, repeats: true) + + DispatchQueue.main.asyncAfter(deadline: .now() + phaseLength) { + self.initPhaseFour() + } + } + + + func initPhaseFour() { + let phaseLength = meditation[9] as! Double + Utils.set(key: Constants.Defaults.MEDITATION_PHASE_LENGTH, value: phaseLength) + + titleLabel.text = meditation[8] as? String + + DispatchQueue.main.asyncAfter(deadline: .now() + phaseLength) { + self.initPhaseOne() + } + } + + + @objc private func forwardProgress() { + circularProgressValue += 0.01 / Utils.double(key: Constants.Defaults.MEDITATION_PHASE_LENGTH) + circularProgressView.progress = circularProgressValue + } + + + @objc private func reverseProgress() { + circularProgressValue -= 0.01 / Utils.double(key: Constants.Defaults.MEDITATION_PHASE_LENGTH) + circularProgressView.progress = circularProgressValue + } +} diff --git a/Meditation/Purchase/Purchase.swift b/Meditation/Purchase/Purchase.swift new file mode 100644 index 0000000..78e31f5 --- /dev/null +++ b/Meditation/Purchase/Purchase.swift @@ -0,0 +1,172 @@ +import SwiftyStoreKit +import UIKit + + +class Purchase { + + class func handlePurchaseResult(_ result: PurchaseResult, view: UIViewController, purchaseItem: String) { + switch result { + case .success(_): + + if purchaseItem == Constants.Purchases.SUBSCRIPTION_MONTHLY_KEY { + Utils.set(key: Constants.Defaults.USER_HAS_MONTHLY_SUBSCRIPTION, value: true) + } + else if purchaseItem == Constants.Purchases.SUBSCRIPTION_YEARLY_KEY { + Utils.set(key: Constants.Defaults.USER_HAS_YEARLY_SUBSCRIPTION, value: true) + } + else if purchaseItem == Constants.Purchases.UNLOCK_KEY { + Utils.set(key: Constants.Defaults.USER_HAS_UNLOCKED_APP, value: true) + } + + + case .error(let error): + switch error.code { + case .storeProductNotAvailable: + Dialogs.showOkButtonDialog(view: view, message: Constants.Strings.PURCHASE_ERROR_NOT_AVAILABLE) + + case .paymentInvalid: + Dialogs.showOkButtonDialog(view: view, message: Constants.Strings.PURCHASE_ERROR_IDENTIFIER_INVALID) + + case .paymentCancelled: + Dialogs.showOkButtonDialog(view: view, message: Constants.Strings.PURCHASE_ERROR_CANCELLED) + + case .paymentNotAllowed: + Dialogs.showOkButtonDialog(view: view, message: Constants.Strings.PURCHASE_ERROR_NOT_ALLOWED) + + case .clientInvalid: + Dialogs.showOkButtonDialog(view: view, message: Constants.Strings.PURCHASE_ERROR_NOT_ALLOWED) + + default: + Dialogs.showOkButtonDialog(view: view, message: Constants.Strings.PURCHASE_ERROR_LOGIN) + } + } + } + + + class func completeTransactions() { + SwiftyStoreKit.completeTransactions(atomically: true) { purchases in + for purchase in purchases { + switch purchase.transaction.transactionState { + case .purchased, .restored: + if purchase.needsFinishTransaction { + SwiftyStoreKit.finishTransaction(purchase.transaction) + } + case .failed, .purchasing, .deferred: + break + } + } + } + } + + + class func hasUpgraded() -> Bool { + return Utils.bool(key: Constants.Defaults.USER_HAS_MONTHLY_SUBSCRIPTION) || Utils.bool(key: Constants.Defaults.USER_HAS_YEARLY_SUBSCRIPTION) || Utils.bool(key: Constants.Defaults.USER_HAS_UNLOCKED_APP) + } + + + class func restorePurchases(view: UIViewController, showDialog: Bool) { + SwiftyStoreKit.restorePurchases(atomically: true) { results in + if results.restoreFailedPurchases.count > 0 { + if showDialog { Dialogs.showOkButtonDialog(view: view, message: Constants.Strings.PURCHASE_RESTORE_ERROR) } + } + else if results.restoredPurchases.count > 0 { + + for purchase in results.restoredPurchases { + let downloads = purchase.transaction.downloads + + if !downloads.isEmpty { + SwiftyStoreKit.start(downloads) + } + else if purchase.needsFinishTransaction { + + for purchasedItem in purchase.transaction.downloads { + + if purchasedItem.contentIdentifier == Constants.Purchases.SUBSCRIPTION_MONTHLY_KEY { + Utils.set(key: Constants.Defaults.USER_HAS_MONTHLY_SUBSCRIPTION, value: true) + } + else if purchasedItem.contentIdentifier == Constants.Purchases.SUBSCRIPTION_YEARLY_KEY { + Utils.set(key: Constants.Defaults.USER_HAS_YEARLY_SUBSCRIPTION, value: true) + } + else if purchasedItem.contentIdentifier == Constants.Purchases.UNLOCK_KEY { + Utils.set(key: Constants.Defaults.USER_HAS_UNLOCKED_APP, value: true) + } + } + + SwiftyStoreKit.finishTransaction(purchase.transaction) + } + } + + if showDialog { Dialogs.showOkButtonDialog(view: view, message: Constants.Strings.PURCHASE_RESTORE_SUCCESS) } + } + else { + if showDialog { Dialogs.showOkButtonDialog(view: view, message: Constants.Strings.PURCHASE_RESTORE_NOTHING) } + } + } + } + + + class func supportStorePurchase() { + SwiftyStoreKit.shouldAddStorePaymentHandler = { payment, product in + return true + } + } + + + class func verifyReceipt() { + let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: Constants.Purchases.SHARED_SECRET) + SwiftyStoreKit.verifyReceipt(using: appleValidator, forceRefresh: false) { result in + switch result { + case .success(let receipt): + + if Utils.bool(key: Constants.Defaults.USER_HAS_MONTHLY_SUBSCRIPTION) { + let monthlySubscriptionResult = SwiftyStoreKit.verifySubscription ( + ofType: .autoRenewable, + productId: Constants.Purchases.SUBSCRIPTION_MONTHLY_KEY, + inReceipt: receipt + ) + switch monthlySubscriptionResult { + case .purchased(_): + Utils.set(key: Constants.Defaults.USER_HAS_MONTHLY_SUBSCRIPTION, value: true) + + case .expired(_): + Utils.set(key: Constants.Defaults.USER_HAS_MONTHLY_SUBSCRIPTION, value: false) + + case .notPurchased: + break + } + } + + + if Utils.bool(key: Constants.Defaults.USER_HAS_YEARLY_SUBSCRIPTION) { + let yearlySubscriptionResult = SwiftyStoreKit.verifySubscription ( + ofType: .autoRenewable, + productId: Constants.Purchases.SUBSCRIPTION_MONTHLY_KEY, + inReceipt: receipt + ) + switch yearlySubscriptionResult { + case .purchased(_): + Utils.set(key: Constants.Defaults.USER_HAS_YEARLY_SUBSCRIPTION, value: true) + + case .expired(_): + Utils.set(key: Constants.Defaults.USER_HAS_YEARLY_SUBSCRIPTION, value: false) + + case .notPurchased: + break + } + } + + case .error(_): + Utils.set(key: Constants.Defaults.USER_HAS_MONTHLY_SUBSCRIPTION, value: false) + Utils.set(key: Constants.Defaults.USER_HAS_YEARLY_SUBSCRIPTION, value: false) + } + } + } + + + class func verifyReceiptCheck() { + // Only (possibly) show sign into itunes dialog when user has already purchased + if Utils.bool(key: Constants.Defaults.USER_HAS_MONTHLY_SUBSCRIPTION) || Utils.bool(key: Constants.Defaults.USER_HAS_YEARLY_SUBSCRIPTION) { + Purchase.verifyReceipt() + } + } +} diff --git a/Meditation/Purchase/Upgrade.swift b/Meditation/Purchase/Upgrade.swift new file mode 100644 index 0000000..5e116b9 --- /dev/null +++ b/Meditation/Purchase/Upgrade.swift @@ -0,0 +1,196 @@ +import paper_onboarding +import SwiftyStoreKit + + +class Upgrade: UIViewController { + + @IBOutlet var onboarding: PaperOnboarding! + let BUTTON_HEIGHT: CGFloat = 55 + let BUTTON_WIDTH: CGFloat = 90 + let BUTTON_POSITION_FROM_TOP: CGFloat = 140 + + + + /* MARK: Initialising + /////////////////////////////////////////// */ + override func viewDidLoad() { + createCloseButton() + createTitleLabel() + createMonthlySubcriptionButton() + createYearlySubcriptionButton() + createUnlockButton() + createTermsTextView() + } + + override var prefersStatusBarHidden: Bool { + return true + } + + + + /* MARK: Interface Elements: Buttons + /////////////////////////////////////////// */ + func createMonthlySubcriptionButton() { + let button = createUpgradeButton(title: Constants.Strings.UPGRADE_SCREENS_MONTHLY_SUBSCRIBE_BUTTON_TITLE, x: (view.frame.size.width - BUTTON_WIDTH)/2 - 100, y: view.center.y + BUTTON_POSITION_FROM_TOP) + button.addTarget(self, action: #selector(subscribeMonthlyButtonPressed), for: .touchUpInside) + self.view.addSubview(button) + } + + func createYearlySubcriptionButton() { + let button = createUpgradeButton(title: Constants.Strings.UPGRADE_SCREENS_YEARLY_SUBSCRIBE_BUTTON_TITLE, x: (view.frame.size.width - BUTTON_WIDTH)/2, y: view.center.y + BUTTON_POSITION_FROM_TOP) + button.addTarget(self, action: #selector(subscribeYearlyButtonPressed), for: .touchUpInside) + self.view.addSubview(button) + } + + func createUnlockButton() { + let button = createUpgradeButton(title: Constants.Strings.UPGRADE_SCREENS_UNLOCK_BUTTON_TITLE, x: (view.frame.size.width - BUTTON_WIDTH)/2 + 100, y: view.center.y + BUTTON_POSITION_FROM_TOP) + button.addTarget(self, action: #selector(unlockButtonPressed), for: .touchUpInside) + self.view.addSubview(button) + } + + func createUpgradeButton(title: String, x: CGFloat, y: CGFloat) -> UIButton { + let button: UIButton = UIButton(frame: CGRect(x: x, y: y, width: BUTTON_WIDTH, height: BUTTON_HEIGHT)) + button.backgroundColor = UIColor.white + button.setTitle(title, for: .normal) + button.titleLabel?.font = UIFont.GothamProMedium(size: 16) + button.titleLabel?.lineBreakMode = .byWordWrapping + button.titleLabel?.textAlignment = .center + button.setTitleColor(UIColor(hex: Constants.Colors.PRIMARY_TEXT_GRAY), for: .normal) + button.layer.cornerRadius = 4 + return button + } + + + + /* MARK: Interface Elements: Labels + /////////////////////////////////////////// */ + func createCloseButton() { + let button: UIButton = UIButton(frame: CGRect(x: view.frame.size.width - 60, y: 15, width: 40, height: 40)) + button.titleLabel?.font = UIFont.fontAwesome(ofSize: 27, style: .solid) + button.setTitle(String.fontAwesomeIcon(name: .times), for: .normal) + button.addTarget(self, action: #selector(closeButtonPressed), for: .touchUpInside) + self.view.addSubview(button) + } + + func createTitleLabel() { + let label: UILabel = UILabel(frame: CGRect(x: (self.view.frame.size.width - 250)/2, y: 40, width: 250, height: 30)) + label.text = Constants.Strings.UPGRADE_SCREEN_TITLE + label.font = UIFont.GothamProMedium(size: 20) + label.textColor = UIColor.white + label.textAlignment = .center + self.view.addSubview(label) + } + + func createTermsTextView() { + var textView = UITextView(frame: CGRect(x: (self.view.frame.size.width - 350)/2, y: view.center.y + CGFloat(200), width: 350.0, height: 60)) + + let modelName = UIDevice.modelName + if modelName.range(of: "iPad") != nil { + textView = UITextView(frame: CGRect(x: (self.view.frame.size.width - 350)/2, y: view.center.y + CGFloat(200), width: 350.0, height: 300)) + let button = UIButton(frame: CGRect(x: (self.view.frame.size.width - 350)/2, y: view.center.y + CGFloat(200), width: 350.0, height: 300)) + button.addTarget(self, action: #selector(termsTextViewPressed), for: .touchUpInside) + self.view.addSubview(button) + } + + let attributedStringColor: NSDictionary = [NSAttributedStringKey.foregroundColor: UIColor.white] + let attributedString = NSMutableAttributedString(string: Constants.Strings.UPGRADE_SCREENS_INFO, attributes: (attributedStringColor as! [NSAttributedStringKey : Any])) + attributedString.setAsLink(textToFind: Constants.Strings.LINK_PRIVACY_AND_TERMS, linkURL: Constants.Strings.LINK_PRIVACY_AND_TERMS) + + textView.textAlignment = NSTextAlignment.center + textView.isEditable = false + textView.attributedText = attributedString + textView.textAlignment = .center + textView.backgroundColor = nil + textView.font = UIFont.systemFont(ofSize: 14.0) + self.view.addSubview(textView) + } + + + + /* MARK: Button Actions + /////////////////////////////////////////// */ + @objc func closeButtonPressed() { + Utils.presentView(self, viewName: Utils.string(key: Constants.Defaults.PREVIOUS_VIEW)) + } + + @objc func termsTextViewPressed() { + Utils.openURL(url: Constants.Strings.LINK_PRIVACY_AND_TERMS) + } + + @objc func subscribeMonthlyButtonPressed() { + SwiftyStoreKit.purchaseProduct(Constants.Purchases.SUBSCRIPTION_MONTHLY_KEY, atomically: true) { result in + Purchase.handlePurchaseResult(result, view: self, purchaseItem: Constants.Purchases.SUBSCRIPTION_MONTHLY_KEY) + } + } + + @objc func subscribeYearlyButtonPressed() { + SwiftyStoreKit.purchaseProduct(Constants.Purchases.SUBSCRIPTION_YEARLY_KEY, atomically: true) { result in + Purchase.handlePurchaseResult(result, view: self, purchaseItem: Constants.Purchases.SUBSCRIPTION_YEARLY_KEY) + } + } + + @objc func unlockButtonPressed() { + SwiftyStoreKit.purchaseProduct(Constants.Purchases.UNLOCK_KEY, atomically: true) { result in + Purchase.handlePurchaseResult(result, view: self, purchaseItem: Constants.Purchases.UNLOCK_KEY) + } + } +} + +extension Upgrade: PaperOnboardingDelegate { + func onboardingWillTransitonToIndex(_ index: Int) { } + func onboardingDidTransitonToIndex(_ index: Int) { } + func onboardingConfigurationItem(_ item: OnboardingContentViewItem, index: Int) { } +} + +extension Upgrade: PaperOnboardingDataSource { + func onboardingItem(at index: Int) -> OnboardingItemInfo { + let titleFont = UIFont.GothamProBold(size: 30.0) ?? UIFont.boldSystemFont(ofSize: 36.0) + let descriptionFont = UIFont.GothamProRegular(size: CGFloat(15)) ?? UIFont.systemFont(ofSize: CGFloat(15)) + + return [ + OnboardingItemInfo(informationImage: UIImage(named: "rocket")!, + title: Constants.Strings.UPGRADE_SCREEN_ONE_TITLE, + description: Constants.Strings.UPGRADE_SCREEN_ONE_TEXT, + pageIcon: UIImage(named: "transparent")!, + color: UIColor(hex: Constants.Colors.BLUE), + titleColor: UIColor.white, + descriptionColor: UIColor.white, + titleFont: titleFont, + descriptionFont: descriptionFont), + + OnboardingItemInfo(informationImage: UIImage(named: "trophy")!, + title: Constants.Strings.UPGRADE_SCREEN_TWO_TITLE, + description: Constants.Strings.UPGRADE_SCREEN_TWO_TEXT, + pageIcon: UIImage(named: "transparent")!, + color: UIColor(hex: Constants.Colors.PURPLE), + titleColor: UIColor.white, + descriptionColor: UIColor.white, + titleFont: titleFont, + descriptionFont: descriptionFont), + + OnboardingItemInfo(informationImage: UIImage(named: "diamond")!, + title: Constants.Strings.UPGRADE_SCREEN_THREE_TITLE, + description: Constants.Strings.UPGRADE_SCREEN_THREE_TEXT, + pageIcon: UIImage(named: "transparent")!, + color: UIColor(hex: Constants.Colors.GREEN), + titleColor: UIColor.white, + descriptionColor: UIColor.white, + titleFont: titleFont, + descriptionFont: descriptionFont), + + OnboardingItemInfo(informationImage: UIImage(named: "growth")!, + title: Constants.Strings.UPGRADE_SCREEN_FOUR_TITLE, + description: Constants.Strings.UPGRADE_SCREEN_FOUR_TEXT, + pageIcon: UIImage(named: "transparent")!, + color: UIColor(hex: Constants.Colors.ORANGE), + titleColor: UIColor.white, + descriptionColor: UIColor.white, + titleFont: titleFont, + descriptionFont: descriptionFont) + ][index] + } + + func onboardingItemsCount() -> Int { + return 4 + } +} diff --git a/Meditation/Resources/Base.lproj/LaunchScreen.storyboard b/Meditation/Resources/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..fa7337c --- /dev/null +++ b/Meditation/Resources/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + GothamPro-Bold + + + GothamPro-Medium + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Meditation/Resources/Fonts/GothamProBlack.ttf b/Meditation/Resources/Fonts/GothamProBlack.ttf new file mode 100755 index 0000000..aae076a Binary files /dev/null and b/Meditation/Resources/Fonts/GothamProBlack.ttf differ diff --git a/Meditation/Resources/Fonts/GothamProBold.ttf b/Meditation/Resources/Fonts/GothamProBold.ttf new file mode 100755 index 0000000..5ea74f2 Binary files /dev/null and b/Meditation/Resources/Fonts/GothamProBold.ttf differ diff --git a/Meditation/Resources/Fonts/GothamProMedium.ttf b/Meditation/Resources/Fonts/GothamProMedium.ttf new file mode 100755 index 0000000..a24d08b Binary files /dev/null and b/Meditation/Resources/Fonts/GothamProMedium.ttf differ diff --git a/Meditation/Resources/Fonts/GothamProRegular.ttf b/Meditation/Resources/Fonts/GothamProRegular.ttf new file mode 100755 index 0000000..6dc1fea Binary files /dev/null and b/Meditation/Resources/Fonts/GothamProRegular.ttf differ diff --git a/Meditation/Resources/Info.plist b/Meditation/Resources/Info.plist new file mode 100644 index 0000000..1003a85 --- /dev/null +++ b/Meditation/Resources/Info.plist @@ -0,0 +1,107 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Calm Focus + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.1.0 + CFBundleURLTypes + + + CFBundleTypeRole + Editor + CFBundleURLName + Meditation0 + CFBundleURLSchemes + + Meditation0 + + + + CFBundleTypeRole + Editor + CFBundleURLName + Meditation1 + CFBundleURLSchemes + + Meditation1 + + + + CFBundleTypeRole + Editor + CFBundleURLName + Meditation2 + CFBundleURLSchemes + + Meditation2 + + + + CFBundleTypeRole + Editor + CFBundleURLName + Meditation3 + CFBundleURLSchemes + + Meditation3 + + + + CFBundleTypeRole + Editor + CFBundleURLName + Meditation4 + CFBundleURLSchemes + + Meditation4 + + + + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIAppFonts + + GothamProBlack.ttf + GothamProBold.ttf + GothamProMedium.ttf + GothamProRegular.ttf + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Meditation/Resources/Sounds/Cave_Water_Dripping.mp3 b/Meditation/Resources/Sounds/Cave_Water_Dripping.mp3 new file mode 100644 index 0000000..acd9fc9 Binary files /dev/null and b/Meditation/Resources/Sounds/Cave_Water_Dripping.mp3 differ diff --git a/Meditation/Resources/Sounds/Crackling_Fire.mp3 b/Meditation/Resources/Sounds/Crackling_Fire.mp3 new file mode 100644 index 0000000..f4069be Binary files /dev/null and b/Meditation/Resources/Sounds/Crackling_Fire.mp3 differ diff --git a/Meditation/Resources/Sounds/Hailstones.mp3 b/Meditation/Resources/Sounds/Hailstones.mp3 new file mode 100644 index 0000000..f50eae4 Binary files /dev/null and b/Meditation/Resources/Sounds/Hailstones.mp3 differ diff --git a/Meditation/Resources/Sounds/Jungle_Birds_And_Insects.mp3 b/Meditation/Resources/Sounds/Jungle_Birds_And_Insects.mp3 new file mode 100644 index 0000000..3c56ab4 Binary files /dev/null and b/Meditation/Resources/Sounds/Jungle_Birds_And_Insects.mp3 differ diff --git a/Meditation/Resources/Sounds/Jungle_River.mp3 b/Meditation/Resources/Sounds/Jungle_River.mp3 new file mode 100644 index 0000000..6070fe0 Binary files /dev/null and b/Meditation/Resources/Sounds/Jungle_River.mp3 differ diff --git a/Meditation/Resources/Sounds/Majestic_Rain.mp3 b/Meditation/Resources/Sounds/Majestic_Rain.mp3 new file mode 100644 index 0000000..6da9b9f Binary files /dev/null and b/Meditation/Resources/Sounds/Majestic_Rain.mp3 differ diff --git a/Meditation/Resources/Sounds/Nightime_Jungle.mp3 b/Meditation/Resources/Sounds/Nightime_Jungle.mp3 new file mode 100644 index 0000000..7b3cfbe Binary files /dev/null and b/Meditation/Resources/Sounds/Nightime_Jungle.mp3 differ diff --git a/Meditation/Resources/Sounds/Ocean_Waves.mp3 b/Meditation/Resources/Sounds/Ocean_Waves.mp3 new file mode 100644 index 0000000..ac06033 Binary files /dev/null and b/Meditation/Resources/Sounds/Ocean_Waves.mp3 differ diff --git a/Meditation/Resources/Sounds/Rainforest_Ambience.mp3 b/Meditation/Resources/Sounds/Rainforest_Ambience.mp3 new file mode 100644 index 0000000..7f522f2 Binary files /dev/null and b/Meditation/Resources/Sounds/Rainforest_Ambience.mp3 differ diff --git a/Meditation/Resources/Sounds/Rocky Shore Waves.mp3 b/Meditation/Resources/Sounds/Rocky Shore Waves.mp3 new file mode 100644 index 0000000..35fa0c0 Binary files /dev/null and b/Meditation/Resources/Sounds/Rocky Shore Waves.mp3 differ diff --git a/Meditation/Resources/Sounds/Running_Cave_Water.mp3 b/Meditation/Resources/Sounds/Running_Cave_Water.mp3 new file mode 100644 index 0000000..b0eb79e Binary files /dev/null and b/Meditation/Resources/Sounds/Running_Cave_Water.mp3 differ diff --git a/Meditation/Resources/Sounds/Summer_Birds_Singing.mp3 b/Meditation/Resources/Sounds/Summer_Birds_Singing.mp3 new file mode 100644 index 0000000..9a17cbd Binary files /dev/null and b/Meditation/Resources/Sounds/Summer_Birds_Singing.mp3 differ diff --git a/Meditation/Resources/Sounds/Thunderstorm.mp3 b/Meditation/Resources/Sounds/Thunderstorm.mp3 new file mode 100644 index 0000000..751632f Binary files /dev/null and b/Meditation/Resources/Sounds/Thunderstorm.mp3 differ diff --git a/Meditation/Resources/Sounds/Waves_Breaking_Onto_Shore.mp3 b/Meditation/Resources/Sounds/Waves_Breaking_Onto_Shore.mp3 new file mode 100644 index 0000000..8395fa4 Binary files /dev/null and b/Meditation/Resources/Sounds/Waves_Breaking_Onto_Shore.mp3 differ diff --git a/Meditation/Settings.swift b/Meditation/Settings.swift new file mode 100644 index 0000000..8776b18 --- /dev/null +++ b/Meditation/Settings.swift @@ -0,0 +1,156 @@ +import UIKit +import Social +import MessageUI +import FontAwesome_swift + + +class Settings: UITableViewController, UITextFieldDelegate, MFMailComposeViewControllerDelegate { + + @IBOutlet var aboutButton: UIButton! + @IBOutlet var upgradeButtonIcon: UIButton! + @IBOutlet var learnableiOSAppButtonIcon: UIButton! + @IBOutlet var restorePurchasesButtonIcon: UIButton! + @IBOutlet var reviewButtonIcon: UIButton! + @IBOutlet var sendFeedbackButtonIcon: UIButton! + @IBOutlet var followDeveloperButtonIcon: UIButton! + @IBOutlet var shareButtonIcon: UIButton! + @IBOutlet var twitterButtonIcon: UIButton! + @IBOutlet var instagramButtonIcon: UIButton! + @IBOutlet var backButton: UIBarButtonItem! + + + + /* MARK: Initialising + /////////////////////////////////////////// */ + override func viewWillAppear(_ animated: Bool) { + setButtonIcon(button: upgradeButtonIcon, icon: String.fontAwesomeIcon(name: .trophy), type: .solid) + setButtonIcon(button: learnableiOSAppButtonIcon, icon: String.fontAwesomeIcon(name: .heart), type: .solid) + setButtonIcon(button: restorePurchasesButtonIcon, icon: String.fontAwesomeIcon(name: .flask), type: .solid) + setButtonIcon(button: reviewButtonIcon, icon: String.fontAwesomeIcon(name: .gem), type: .regular) + setButtonIcon(button: sendFeedbackButtonIcon, icon: String.fontAwesomeIcon(name: .pen), type: .solid) + setButtonIcon(button: followDeveloperButtonIcon, icon: String.fontAwesomeIcon(name: .userAstronaut), type: .solid) + setButtonIcon(button: shareButtonIcon, icon: String.fontAwesomeIcon(name: .rocket), type: .solid) + setButtonIcon(button: twitterButtonIcon, icon: String.fontAwesomeIcon(name: .twitter), type: .brands) + setButtonIcon(button: instagramButtonIcon, icon: String.fontAwesomeIcon(name: .instagram), type: .brands) + + // Styling + Utils.createFontAwesomeBarButton(button: backButton, icon: .arrowLeft, style: .solid) + tableView.separatorColor = UIColor.clear + aboutButton?.titleLabel?.numberOfLines = 15 + } + + func setButtonIcon(button: UIButton, icon: String, type: FontAwesomeStyle) { + button.titleLabel?.font = UIFont.fontAwesome(ofSize: 21, style: type) + button.setTitle(icon, for: .normal) + } + + override var prefersStatusBarHidden: Bool { + return true + } + + + + /* MARK: Table + /////////////////////////////////////////// */ + override func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) { + (view as? UITableViewHeaderFooterView)?.textLabel?.text = (view as? UITableViewHeaderFooterView)?.textLabel?.text?.capitalized + (view as? UITableViewHeaderFooterView)?.textLabel?.font = UIFont.GothamProMedium(size: 14.5) + (view as? UITableViewHeaderFooterView)?.textLabel?.textColor = UIColor.white + } + + override func tableView(_ tableView: UITableView, willDisplayFooterView view: UIView, forSection section: Int) { + if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String { + (view as? UITableViewHeaderFooterView)?.textLabel?.text = "Version " + version + "\n© " + Constants.Core.APP_NAME + " \n" + } + + (view as? UITableViewHeaderFooterView)?.textLabel?.font = UIFont.GothamProMedium(size: 12.5) + (view as? UITableViewHeaderFooterView)?.textLabel?.textColor = UIColor.white + (view as? UITableViewHeaderFooterView)?.textLabel?.textAlignment = .center + } + + public override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + if indexPath.row == 0 && indexPath.section == 0 { + return 160.0 + } + return 44.0 + } + + public override func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { + if section == 3 { + return 70.0 + } + return 0 + } + + + + /* MARK: Button Action + /////////////////////////////////////////// */ + @IBAction func backButtonPressed() { + Utils.presentView(self, viewName: Constants.Views.MEDITATION) + } + + @IBAction func aboutButtonPressed() { + Utils.openURL(url: Constants.Strings.LINK_TWITTER_JOEY) + } + + + // Premium + @IBAction func upgradeButtonPressed() { + Utils.set(key: Constants.Defaults.PREVIOUS_VIEW, value: Constants.Views.SETTINGS_NAV_CONTROLLER) + Utils.presentView(self, viewName: Constants.Views.UPGRADE) + } + + @IBAction func restorePurchasesButtonPressed() { + Purchase.restorePurchases(view: self, showDialog: true) + } + + + // App + @IBAction func learnableiOSAppButtonPressed() { + Utils.openURL(url: Constants.Strings.LINK_LEARNABLE_IOS_STORE) + } + + @IBAction func reviewButtonPressed() { + Utils.openURL(url: Constants.Strings.LINK_APP_REVIEW) + } + + @IBAction func sendFeedbackButtonPressed() { + var possibleVersion = "Version " + if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String { + possibleVersion = possibleVersion + version + } + let systemVersion = UIDevice.current.systemName + " " + UIDevice.current.systemVersion + let modelName = UIDevice.modelName + + let emailBody = Constants.Strings.SEND_FEEDBACK_BODY + possibleVersion + " \n " + systemVersion + " \n " + modelName + + let mailComposer = MFMailComposeViewController() + mailComposer.mailComposeDelegate = self + mailComposer.setToRecipients([Constants.Strings.EMAIL]) + mailComposer.setSubject(Constants.Strings.SEND_FEEDBACK_SUBJECT) + mailComposer.setMessageBody(emailBody, isHTML: false) + + if MFMailComposeViewController.canSendMail() { + present(mailComposer, animated: true) + } + } + + public func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) { + controller.dismiss(animated: true, completion: nil) + } + + + // Share + @IBAction func shareButtonPressed() { + Utils.openShareView(viewController: self) + } + + @IBAction func twitterButtonPressed() { + Utils.openURL(url: Constants.Strings.LINK_TWITTER) + } + + @IBAction func instagramButtonPressed() { + Utils.openURL(url: Constants.Strings.LINK_INSTAGRAM) + } +} diff --git a/Meditation/Utils/Constants.swift b/Meditation/Utils/Constants.swift new file mode 100644 index 0000000..b9ca647 --- /dev/null +++ b/Meditation/Utils/Constants.swift @@ -0,0 +1,262 @@ +import UIKit + + +class Constants { + + struct Achievements { + static let COLORS = ["1abc9c", "2ecc71", "3498db", "9b59b6", "f1c40f", "e67e22", "e74c3c", "34495e"] + + // Points + static let POINTS_TYPE = "pointsType" + static let ACHIEVEMENT1 = ["10p", COLORS[0], "virus", "Blast-off", "You've earned 10 points! Congrats!"] + static let ACHIEVEMENT2 = ["25p", COLORS[1], "virus-autoupdate", "Quarter", "You've earned 25 points! Congrats!"] + static let ACHIEVEMENT3 = ["50p", COLORS[2], "satellite", "Halves", "You've earned 50 points! Congrats!"] + static let ACHIEVEMENT4 = ["100p", COLORS[3], "satellite-dish", "Century", "You've earned 100 points! Congrats!"] + static let ACHIEVEMENT5 = ["250p", COLORS[4], "atom", "Two-fifty", "You've earned 250 points! Congrats!"] + + // Sessions Completed + static let SESSIONS_TYPE = "sessionsType" + static let ACHIEVEMENT6 = ["3s", COLORS[5], "cloud", "Playtime", "You've completed 3 sessions! Congrats!"] + static let ACHIEVEMENT7 = ["7s", COLORS[6], "calculator", "In Tune", "You've completed 7 sessions! Congrats!"] + static let ACHIEVEMENT8 = ["15s", COLORS[7], "certificate", "Practitioner", "You've completed 15 sessions! Congrats!"] + static let ACHIEVEMENT9 = ["30s", COLORS[0], "best_product", "Monk", "You've completed 30 sessions! Congrats!"] + static let ACHIEVEMENT10 = ["50s", COLORS[1], "leaf", "Buddhist", "You've completed 50 sessions! Congrats!"] + static let ACHIEVEMENT11 = ["100s", COLORS[2], "diamond-1", "Zen Master", "You've completed 100 sessions! Congrats!"] + + // Total Time Meditating + static let TIME_MEDITATING_TYPE = "timeMeditatingType" + static let ACHIEVEMENT12 = ["30t", COLORS[3], "basketball", "Student", "You've spent 30 minutes meditating! Congrats!"] + static let ACHIEVEMENT13 = ["60t", COLORS[4], "apps", "Focused", "You've spent 60 minutes meditating! Congrats!"] + static let ACHIEVEMENT14 = ["90t", COLORS[5], "fountain-pen-tip", "Sublime", "You've spent 90 minutes meditating! Congrats!"] + static let ACHIEVEMENT15 = ["150t", COLORS[6], "fountain-pen", "Resolute", "You've spent 150 minutes meditating! Congrats!"] + static let ACHIEVEMENT16 = ["300t", COLORS[7], "brightness", "Zen", "You've spent 300 minutes meditating! Congrats!"] + static let ACHIEVEMENT17 = ["500t", COLORS[0], "balance", "At Peace", "You've spent 500 minutes meditating! Congrats!"] + + static let ACHIEVEMENTS_POINTS = [ACHIEVEMENT1, ACHIEVEMENT2, ACHIEVEMENT3, ACHIEVEMENT4, ACHIEVEMENT5] + static let ACHIEVEMENTS_SESSIONS = [ACHIEVEMENT6, ACHIEVEMENT7, ACHIEVEMENT8, ACHIEVEMENT9, ACHIEVEMENT10, ACHIEVEMENT11] + static let ACHIEVEMENTS_TIME_MEDITATING = [ACHIEVEMENT12, ACHIEVEMENT13, ACHIEVEMENT14, ACHIEVEMENT15, ACHIEVEMENT16, ACHIEVEMENT17] + static let ACHIEVEMENTS_ALL = [ACHIEVEMENT1, ACHIEVEMENT2, ACHIEVEMENT3, ACHIEVEMENT4, ACHIEVEMENT5, ACHIEVEMENT6, ACHIEVEMENT7, ACHIEVEMENT8, ACHIEVEMENT9, ACHIEVEMENT10, ACHIEVEMENT11, ACHIEVEMENT12, ACHIEVEMENT13, ACHIEVEMENT14, ACHIEVEMENT15, ACHIEVEMENT16, ACHIEVEMENT17] + } + + + struct Meditations { + static let MEDITATION_SOUNDS = [["Crackling_Fire", false], ["Jungle_Birds_And_Insects", false], ["Jungle_River", false], ["Summer_Birds_Singing", false], ["Waves_Breaking_Onto_Shore", false], ["Cave_Water_Dripping", true], ["Hailstones", true], ["Majestic_Rain", true], ["Nightime_Jungle", true], ["Ocean_Waves", true], ["Rainforest_Ambience", true], ["Rocky_Shore_Waves", true], ["Running_Cave_Water", true], ["Thunderstorm", true]] + + static let MEDITATION_ONE_ICON = "1" + static let MEDITATION_ONE_EXERCISE = "EXERCISE ONE" + static let MEDITATION_ONE_GOAL = "Reduce stress" + static let MEDITATION_ONE_TITLE = "Equal Breathing" + static let MEDITATION_ONE_TEXT = "1. Breathe in through the nose for a count of 5. \n\n2. Pause for a count of 5. \n\n3. Breathe out through the nose for a count of 5." + static let MEDITATION_ONE_SOUND = "Jungle_Birds_And_Insects" + static let MEDITATION_ONE_PHASE_ONE = "BREATHE IN \n\nfor 5 seconds" + static let MEDITATION_ONE_PHASE_ONE_LENGTH = 5.0 + static let MEDITATION_ONE_PHASE_TWO = "PAUSE \n\nfor 5 seconds" + static let MEDITATION_ONE_PHASE_TWO_LENGTH = 5.0 + static let MEDITATION_ONE_PHASE_THREE = "BREATHE OUT \n\nfor 5 seconds" + static let MEDITATION_ONE_PHASE_THREE_LENGTH = 5.0 + + static let MEDITATION_TWO_ICON = "2" + static let MEDITATION_TWO_EXERCISE = "EXERCISE TWO" + static let MEDITATION_TWO_GOAL = "Strenghten the diaphragm" + static let MEDITATION_TWO_TITLE = "5 - 4 - 8" + static let MEDITATION_TWO_TEXT = "1. Sitting upright or lying down, place your hands on your belly. \n\n2. Slowly breathe in, expanding your belly, for a count of 5. \n\n3. Pause for a count of 4. \n\n4. Slowly breathe out for a count of 8." + static let MEDITATION_TWO_SOUND = "Summer_Birds_Singing" + static let MEDITATION_TWO_PHASE_ONE = "BREATHE IN \n\nfor 5 seconds" + static let MEDITATION_TWO_PHASE_ONE_LENGTH = 5.0 + static let MEDITATION_TWO_PHASE_TWO = "PAUSE \n\nfor 4 seconds" + static let MEDITATION_TWO_PHASE_TWO_LENGTH = 4.0 + static let MEDITATION_TWO_PHASE_THREE = "BREATHE OUT \n\nfor 8 seconds" + static let MEDITATION_TWO_PHASE_THREE_LENGTH = 8.0 + + static let MEDITATION_THREE_ICON = "3" + static let MEDITATION_THREE_EXERCISE = "EXERCISE THREE" + static let MEDITATION_THREE_GOAL = "Sleep tight" + static let MEDITATION_THREE_TITLE = "Lucid Breathing" + static let MEDITATION_THREE_TEXT = "1. Lay down on your bed in a comfortable position. \n\n2. Breathe in through the nose for a count of 6. \n\n3. Pause for a count of 4 & smile. \n\n4. Breathe out for a count of 6." + static let MEDITATION_THREE_SOUND = "Jungle_River" + static let MEDITATION_THREE_PHASE_ONE = "BREATHE IN \n\nfor 6 seconds" + static let MEDITATION_THREE_PHASE_ONE_LENGTH = 6.0 + static let MEDITATION_THREE_PHASE_TWO = "PAUSE \n\nfor 4 seconds" + static let MEDITATION_THREE_PHASE_TWO_LENGTH = 4.0 + static let MEDITATION_THREE_PHASE_THREE = "BREATHE OUT \n\nfor 6 seconds" + static let MEDITATION_THREE_PHASE_THREE_LENGTH = 6.0 + + static let MEDITATION_FOUR_ICON = "4" + static let MEDITATION_FOUR_EXERCISE = "EXERCISE FOUR" + static let MEDITATION_FOUR_GOAL = "Focus at will" + static let MEDITATION_FOUR_TITLE = "Calming Breath" + static let MEDITATION_FOUR_TEXT = "1. Sit up straight & place your hands on your belly. \n\n2. As you inhale, lean forward & expand your belly. \n\n3. As you exhale, curl forward while leaning backward until you’re out of breath." + static let MEDITATION_FOUR_SOUND = "Crackling_Fire" + static let MEDITATION_FOUR_PHASE_ONE = "BREATHE IN \n\nfor 8 seconds" + static let MEDITATION_FOUR_PHASE_ONE_LENGTH = 8.0 + static let MEDITATION_FOUR_PHASE_TWO = "PAUSE \n\nfor 4 seconds" + static let MEDITATION_FOUR_PHASE_TWO_LENGTH = 4.0 + static let MEDITATION_FOUR_PHASE_THREE = "BREATHE OUT \n\nfor 8 seconds" + static let MEDITATION_FOUR_PHASE_THREE_LENGTH = 8.0 + + static let MEDITATION_FIVE_ICON = "5" + static let MEDITATION_FIVE_EXERCISE = "EXERCISE FIVE" + static let MEDITATION_FIVE_GOAL = "Increase happiness" + static let MEDITATION_FIVE_TITLE = "Aha Breathing" + static let MEDITATION_FIVE_TEXT = "1. Stand up, arms straight out & palms facing up. \n\n2. As you inhale, gently allow you arms to fall by your side. \n\n3. Exhale quickly, thrusting your arms back up to position 1 while saying “Ha” out loud." + static let MEDITATION_FIVE_SOUND = "Waves_Breaking_Onto_Shore" + static let MEDITATION_FIVE_PHASE_ONE = "BREATHE IN \n\nfor 6 seconds" + static let MEDITATION_FIVE_PHASE_ONE_LENGTH = 6.0 + static let MEDITATION_FIVE_PHASE_TWO = "PAUSE \n\nfor 2 seconds" + static let MEDITATION_FIVE_PHASE_TWO_LENGTH = 2.0 + static let MEDITATION_FIVE_PHASE_THREE = "BREATHE OUT \n\nfor 5 seconds" + static let MEDITATION_FIVE_PHASE_THREE_LENGTH = 5.0 + + static let MEDITATION_ONE = [MEDITATION_ONE_ICON, MEDITATION_ONE_EXERCISE, MEDITATION_ONE_GOAL, MEDITATION_ONE_TITLE, MEDITATION_ONE_TEXT, MEDITATION_ONE_SOUND, MEDITATION_ONE_PHASE_ONE, MEDITATION_ONE_PHASE_ONE_LENGTH, MEDITATION_ONE_PHASE_TWO, MEDITATION_ONE_PHASE_TWO_LENGTH, MEDITATION_ONE_PHASE_THREE, MEDITATION_ONE_PHASE_THREE_LENGTH] as [Any] + static let MEDITATION_TWO = [MEDITATION_TWO_ICON, MEDITATION_TWO_EXERCISE, MEDITATION_TWO_GOAL, MEDITATION_TWO_TITLE, MEDITATION_TWO_TEXT, MEDITATION_TWO_SOUND, MEDITATION_TWO_PHASE_ONE, MEDITATION_TWO_PHASE_ONE_LENGTH, MEDITATION_TWO_PHASE_TWO, MEDITATION_TWO_PHASE_TWO_LENGTH, MEDITATION_TWO_PHASE_THREE, MEDITATION_TWO_PHASE_THREE_LENGTH] as [Any] + static let MEDITATION_THREE = [MEDITATION_THREE_ICON, MEDITATION_THREE_EXERCISE, MEDITATION_THREE_GOAL, MEDITATION_THREE_TITLE, MEDITATION_THREE_TEXT, MEDITATION_THREE_SOUND, MEDITATION_THREE_PHASE_ONE, MEDITATION_THREE_PHASE_ONE_LENGTH, MEDITATION_THREE_PHASE_TWO, MEDITATION_THREE_PHASE_TWO_LENGTH, MEDITATION_THREE_PHASE_THREE, MEDITATION_THREE_PHASE_THREE_LENGTH] as [Any] + static let MEDITATION_FOUR = [MEDITATION_FOUR_ICON, MEDITATION_FOUR_EXERCISE, MEDITATION_FOUR_GOAL, MEDITATION_FOUR_TITLE, MEDITATION_FOUR_TEXT, MEDITATION_FOUR_SOUND, MEDITATION_FOUR_PHASE_ONE, MEDITATION_FOUR_PHASE_ONE_LENGTH, MEDITATION_FOUR_PHASE_TWO, MEDITATION_FOUR_PHASE_TWO_LENGTH, MEDITATION_FOUR_PHASE_THREE, MEDITATION_FOUR_PHASE_THREE_LENGTH] as [Any] + static let MEDITATION_FIVE = [MEDITATION_FIVE_ICON, MEDITATION_FIVE_EXERCISE, MEDITATION_FIVE_GOAL, MEDITATION_FIVE_TITLE, MEDITATION_FIVE_TEXT, MEDITATION_FIVE_SOUND, MEDITATION_FIVE_PHASE_ONE, MEDITATION_FIVE_PHASE_ONE_LENGTH, MEDITATION_FIVE_PHASE_TWO, MEDITATION_FIVE_PHASE_TWO_LENGTH, MEDITATION_FIVE_PHASE_THREE, MEDITATION_FIVE_PHASE_THREE_LENGTH] as [Any] + + static let MEDITATIONS_ALL = [MEDITATION_ONE, MEDITATION_TWO, MEDITATION_THREE, MEDITATION_FOUR, MEDITATION_FIVE] + } + + + struct Colors { + static let PRIMARY_PURPLE = "A09FED" + + static let BLUE = "69CDFC" + static let GREEN = "2ecc71" + static let ORANGE = "f39c12" + static let PURPLE = "B0B1F1" + static let PRIMARY_TEXT_GRAY = "5D5D5C" + + // Widgets, titles, texts + static let ACHIEVEMENT_GRAY = "C0C0C0" + static let CIRCULAR_MEDITATION_GRAY = "E5E5E5" + } + + + struct Core { + static let APP_ID = "1431521616" + static let APP_NAME = "Calm Meditation" + } + + + struct Defaults { + static let PREVIOUS_VIEW = "previousView" + + // Achievements + static let APP_DATA = "appData" + static let APP_DATA_ACHIEVEMENTS = "achievements" + static let APP_DATA_TOTAL_POINTS = "totalPoints" + static let APP_DATA_TOTAL_SESSIONS = "totalSessions" // + 3p if over 3min + static let APP_DATA_TOTAL_TIME_MEDITATING = "totalTimeMeditating" + + // Meditation + static let MEDITATION_PHASE_LENGTH = "meditationPhaseLength" + static let SELECTED_MEDITATION = "selectedMeditation" + static let SELECTED_MEDITATION_SOUND = "selectedMeditationSound" + + // Upgrade + static let USER_HAS_MONTHLY_SUBSCRIPTION = "userHasMonthlySubscription" + static let USER_HAS_YEARLY_SUBSCRIPTION = "userHasYearlySubscription" + static let USER_HAS_UNLOCKED_APP = "userHasUnlockedApp" + } + + + struct Design { + static let LOGO = "Logo" + } + + + struct Purchases { + static let SHARED_SECRET = "3573e017835041aa8fd77cb8b1842af4" + static let SUBSCRIPTION_MONTHLY_KEY = "com.joeyt.meditation.subscription.monthly" + static let SUBSCRIPTION_YEARLY_KEY = "com.joeyt.meditation.subscription.yearly" + static let UNLOCK_KEY = "com.joeyt.meditation.unlock" + } + + + struct Strings { + // Dialog: Alert + static let ALERT_DIALOG_INFO = "Info" + + + // Dialog: Achievements + static let ACHIEVEMENTS_INFO_DIALOG_TITLE = "How to Earn Points" + static let ACHIEVEMENTS_INFO_DIALOG_SUBTITLE = "\nEvery meditation session longer than 3 minutes will earn you three points. \n\nEvery session/point brings you closer to earning achievement bagdes and levelling up :) \n" + + + // TODO: JOEY: rename below constants to meditation like constants + // Dialog: Lesson Complete + static let LESSON_COMPLETE_DIALOG_CLOSE_BUTTON = "Let's continue :)" + static let LESSON_COMPLETE_DIALOG_SHARE_BUTTON = "Share my achievement!" + static let LESSON_COMPLETE_DIALOG_TITLE = "Congratulations!" + static let LESSON_COMPLETE_DIALOG_SUBTITLES = ["Congratulations! You have completed the lesson! :)", "Another one bites the dust! :)", "Well done to you, you have completed another chapter!", "Fantastic work. Onwards & upwards!", "Hard work beats talent where talent does not work hard.", "Slow and steady progress will overcome all obstacles."] + + + // Levels + static let LEVEL = "Level " + static let LEVEL_THRESHOLDS = [5, 15, 30, 50, 75, 125, 200, 300, 450, 600, 800] + static let LEVEL_UP = "\n Level Up! You're amazing ;) \n Welcome to level " + static let POINTS_CIRCULAR_VIEW_DESCRIPTION_LABEL = "Total Points: " + + + // Links + static let LINK_APP_REVIEW = "itms-apps://itunes.apple.com/app/apple-store/id" + Core.APP_ID + "?action=write-review" + static let LINK_INSTAGRAM = "https://www.instagram.com/joeytawadrous" + static let LINK_IOS_STORE = "https://itunes.apple.com/us/app/calm-meditation-relaxation/id1431521616?ls=1&mt=8" + static let LINK_LEARNABLE_IOS_STORE = "https://itunes.apple.com/gb/app/learnable-learn-to-code-from-scratch-level-up/id1254862243?mt=8" + static let LINK_PRIVACY_AND_TERMS = "https://www.getLearnable.com/privacy.php" + static let LINK_TWITTER = "https://twitter.com/getlearnable" + static let LINK_TWITTER_JOEY = "https://twitter.com/joeytawadrous" + static let LINK_WEB = "http://www.getlearnable.com" + + + // Purchases: Strings + static let PURCHASE_ERROR_CONTACT_US = " Please contact us." + static let PURCHASE_ERROR_NOT_AVAILABLE = "The product is not available in the current storefront." + PURCHASE_ERROR_CONTACT_US + static let PURCHASE_ERROR_IDENTIFIER_INVALID = "The purchase identifier was invalid." + PURCHASE_ERROR_CONTACT_US + static let PURCHASE_ERROR_CANCELLED = "Your payment was cancelled." + PURCHASE_ERROR_CONTACT_US + static let PURCHASE_ERROR_NOT_ALLOWED = "You are not allowed to make payments." + PURCHASE_ERROR_CONTACT_US + static let PURCHASE_ERROR_LOGIN = "You must login to your App Store account before your payment can be completed." + static let PURCHASE_RESTORE_ERROR = "Restore error." + PURCHASE_ERROR_CONTACT_US + static let PURCHASE_RESTORE_NOTHING = "You have no purchases to restore!" + static let PURCHASE_RESTORE_SUCCESS = "You have successfully restored your previous purchases." + static let PURCHASE_SUCCESS = "Your purchase was successful. Enjoy :)" + + + // Purchases: Upgrade Strings + static let UPGRADE_SCREEN_TITLE = "Calm Meditation Premium" + static let UPGRADE_SCREEN_ONE_TITLE = "Unlock Everything" + static let UPGRADE_SCREEN_ONE_TEXT = "Gain access to all features, achievements, sounds & unlockable content." + static let UPGRADE_SCREEN_TWO_TITLE = "Earn Achievements" + static let UPGRADE_SCREEN_TWO_TEXT = "Unlock badges and level up as your enjoy you relaxing meditation sessions." + static let UPGRADE_SCREEN_THREE_TITLE = "Premium Sounds" + static let UPGRADE_SCREEN_THREE_TEXT = "Gain access to all premium sounds to customize your meditations." + static let UPGRADE_SCREEN_FOUR_TITLE = "Unlimited Meditations" + static let UPGRADE_SCREEN_FOUR_TEXT = "Listen to unlimited meditations & ensure you're completely relaxed." + static let UPGRADE_SCREENS_MONTHLY_SUBSCRIBE_BUTTON_TITLE = "$1.99 \nmonth" + static let UPGRADE_SCREENS_YEARLY_SUBSCRIBE_BUTTON_TITLE = "$3.99 \nyear" + static let UPGRADE_SCREENS_UNLOCK_BUTTON_TITLE = "$6.99 \nonce" + static let UPGRADE_SCREENS_INFO = "You'll be charged $1.99 a month or $3.99 a year at confirmation of purchase. Your subscription will renew after 1 month unless turned off 24-hours before the end of the subscription period. You can manage this in your App Store settings. Any unused portion of a free trial period, will be forfeited when the user purchases a subscription. For details, see " + Constants.Strings.LINK_PRIVACY_AND_TERMS + + + // Send Feedback + static let EMAIL = "joeytawadrous@gmail.com" + static let SEND_FEEDBACK_SUBJECT = "Calm Meditation Feedback!" + static let SEND_FEEDBACK_BODY = "I want to make Calm Meditation better. Here are my ideas... \n\n What I like about Calm Meditation: \n 1. \n 2. \n 3. \n\n What I don't like about Calm Meditation: \n 1. \n 2. \n 3. \n\n" + + + // Share + static let SHARE = "Check out " + Constants.Core.APP_NAME + " on the App Store, where you can easily meditate to chilling nature sounds. #CalmMeditation #iOS \n\nDownload for free now: " + Constants.Strings.LINK_IOS_STORE + } + + + struct Views { + static let ACHIEVEMENTS = "Achievements" + static let CHOOSE_SOUND = "ChooseSound" + static let CHOOSE_SOUND_NAV_CONTROLLER = "ChooseSoundNavController" + static let MEDITATION = "Meditation" + static let RUN_MEDITATION = "RunMeditation" + static let SETTINGS = "Settings" + static let SETTINGS_NAV_CONTROLLER = "SettingsNavController" + static let UPGRADE = "Upgrade" + } +} diff --git a/Meditation/Utils/CustomClasses.swift b/Meditation/Utils/CustomClasses.swift new file mode 100644 index 0000000..60fb4ed --- /dev/null +++ b/Meditation/Utils/CustomClasses.swift @@ -0,0 +1,21 @@ +import UIKit + + +class LCircleButton: UIButton { + override open func layoutSubviews() { + super.layoutSubviews() + layer.cornerRadius = self.frame.width / 2 + clipsToBounds = true + layer.masksToBounds = true + } +} + + +class LCircleImageView: UIImageView { + override open func layoutSubviews() { + super.layoutSubviews() + layer.cornerRadius = self.frame.width / 2 + clipsToBounds = true + layer.masksToBounds = true + } +} diff --git a/Meditation/Utils/Dialogs.swift b/Meditation/Utils/Dialogs.swift new file mode 100644 index 0000000..8c30506 --- /dev/null +++ b/Meditation/Utils/Dialogs.swift @@ -0,0 +1,75 @@ +import UIKit +import SCLAlertView + + +class Dialogs { + + class func showAchievementDialog(view: UIViewController, reached: Bool, achievement: [String]) { + let appearance = SCLAlertView.SCLAppearance( + kCircleHeight: 120.0, + kCircleIconHeight: 80.0, + kTitleTop: 72.0, + showCloseButton: true + ) + + let alertView = SCLAlertView(appearance: appearance) + let alertViewIcon = UIImage(named: achievement[2]) + + if reached { + alertView.addButton(Constants.Strings.LESSON_COMPLETE_DIALOG_SHARE_BUTTON) { + Utils.openShareView(viewController: view) + } + } + + alertView.showCustom(achievement[3], subTitle: achievement[4], color: UIColor(hex: achievement[1]), icon: alertViewIcon!, animationStyle: .leftToRight) + } + + + class func showInfoDialog(title: String, subTitle: String) { + let appearance = SCLAlertView.SCLAppearance( + kCircleHeight: 100.0, + kCircleIconHeight: 60.0, + kTitleTop: 62.0, + showCloseButton: true + ) + + let alertView = SCLAlertView(appearance: appearance) + let alertViewIcon = UIImage(named: "diamond-1") + + alertView.showCustom(title, subTitle: subTitle, color: UIColor(hex: Constants.Colors.PRIMARY_PURPLE), icon: alertViewIcon!, animationStyle: .rightToLeft) + } + + + class func showLevelUpDialog(view: UIViewController, level: String) { + let appearance = SCLAlertView.SCLAppearance( + kCircleHeight: 100.0, + kCircleIconHeight: 60.0, + kTitleTop: 62.0, + showCloseButton: false + ) + + let alertView = SCLAlertView(appearance: appearance) + let alertViewIcon = UIImage(named: "diamond-1") + + alertView.addButton(Constants.Strings.LESSON_COMPLETE_DIALOG_SHARE_BUTTON) { + Utils.openShareView(viewController: view) + } + alertView.addButton(Constants.Strings.LESSON_COMPLETE_DIALOG_CLOSE_BUTTON) {} + + alertView.showInfo(Constants.Strings.LESSON_COMPLETE_DIALOG_TITLE, subTitle: Constants.Strings.LEVEL_UP + level + ". \n", circleIconImage: alertViewIcon) + } + + + class func showOkButtonDialog(view: UIViewController, message: String) { + let appearance = SCLAlertView.SCLAppearance( + kCircleHeight: 100.0, + kCircleIconHeight: 60.0, + kTitleTop: 62.0 + ) + + let alertView = SCLAlertView(appearance: appearance) + let alertViewIcon = UIImage(named: "diamond-1") + + alertView.showCustom(Constants.Strings.ALERT_DIALOG_INFO, subTitle: message, color: UIColor(hex: Constants.Colors.GREEN), icon: alertViewIcon!, animationStyle: .leftToRight) + } +} diff --git a/Meditation/Utils/Extensions.swift b/Meditation/Utils/Extensions.swift new file mode 100644 index 0000000..346a165 --- /dev/null +++ b/Meditation/Utils/Extensions.swift @@ -0,0 +1,151 @@ +import UIKit + + +extension NSMutableAttributedString { + public func setAsLink(textToFind:String, linkURL:String) { + let foundRange = self.mutableString.range(of: textToFind) + if foundRange.location != NSNotFound { + self.addAttribute(.link, value: linkURL, range: foundRange) + } + } +} + + +extension UIColor { + convenience init(hex: String) { + let hexString = hex.trimmingCharacters(in: CharacterSet.alphanumerics.inverted) + var int = UInt32() + Scanner(string: hexString).scanHexInt32(&int) + let a, r, g, b: UInt32 + switch hex.characters.count { + case 3: // RGB (12-bit) + (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17) + case 6: // RGB (24-bit) + (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF) + case 8: // ARGB (32-bit) + (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF) + default: + (a, r, g, b) = (1, 1, 1, 0) + } + self.init(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255) + } +} + + +extension UIDevice { + static let modelName: String = { + var systemInfo = utsname() + uname(&systemInfo) + let machineMirror = Mirror(reflecting: systemInfo.machine) + let identifier = machineMirror.children.reduce("") { identifier, element in + guard let value = element.value as? Int8, value != 0 else { return identifier } + return identifier + String(UnicodeScalar(UInt8(value))) + } + + func mapToDevice(identifier: String) -> String { // swiftlint:disable:this cyclomatic_complexity + #if os(iOS) + switch identifier { + case "iPod5,1": return "iPod Touch 5" + case "iPod7,1": return "iPod Touch 6" + case "iPhone3,1", "iPhone3,2", "iPhone3,3": return "iPhone 4" + case "iPhone4,1": return "iPhone 4s" + case "iPhone5,1", "iPhone5,2": return "iPhone 5" + case "iPhone5,3", "iPhone5,4": return "iPhone 5c" + case "iPhone6,1", "iPhone6,2": return "iPhone 5s" + case "iPhone7,2": return "iPhone 6" + case "iPhone7,1": return "iPhone 6 Plus" + case "iPhone8,1": return "iPhone 6s" + case "iPhone8,2": return "iPhone 6s Plus" + case "iPhone9,1", "iPhone9,3": return "iPhone 7" + case "iPhone9,2", "iPhone9,4": return "iPhone 7 Plus" + case "iPhone8,4": return "iPhone SE" + case "iPhone10,1", "iPhone10,4": return "iPhone 8" + case "iPhone10,2", "iPhone10,5": return "iPhone 8 Plus" + case "iPhone10,3", "iPhone10,6": return "iPhone X" + case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4":return "iPad 2" + case "iPad3,1", "iPad3,2", "iPad3,3": return "iPad 3" + case "iPad3,4", "iPad3,5", "iPad3,6": return "iPad 4" + case "iPad4,1", "iPad4,2", "iPad4,3": return "iPad Air" + case "iPad5,3", "iPad5,4": return "iPad Air 2" + case "iPad6,11", "iPad6,12": return "iPad 5" + case "iPad7,5", "iPad7,6": return "iPad 6" + case "iPad2,5", "iPad2,6", "iPad2,7": return "iPad Mini" + case "iPad4,4", "iPad4,5", "iPad4,6": return "iPad Mini 2" + case "iPad4,7", "iPad4,8", "iPad4,9": return "iPad Mini 3" + case "iPad5,1", "iPad5,2": return "iPad Mini 4" + case "iPad6,3", "iPad6,4": return "iPad Pro 9.7 Inch" + case "iPad6,7", "iPad6,8": return "iPad Pro 12.9 Inch" + case "iPad7,1", "iPad7,2": return "iPad Pro 12.9 Inch 2. Generation" + case "iPad7,3", "iPad7,4": return "iPad Pro 10.5 Inch" + case "AppleTV5,3": return "Apple TV" + case "AppleTV6,2": return "Apple TV 4K" + case "AudioAccessory1,1": return "HomePod" + case "i386", "x86_64": return "Simulator \(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "iOS"))" + default: return identifier + } + #elseif os(tvOS) + switch identifier { + case "AppleTV5,3": return "Apple TV 4" + case "AppleTV6,2": return "Apple TV 4K" + case "i386", "x86_64": return "Simulator \(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "tvOS"))" + default: return identifier + } + #endif + } + + return mapToDevice(identifier: identifier) + }() +} + + +extension UIFont { + class func GothamProBlack(size: CGFloat) -> UIFont? { + return UIFont(name: "GothamPro-Black", size: size) + } + + class func GothamProBold(size: CGFloat) -> UIFont? { + return UIFont(name: "GothamPro-Bold", size: size) + } + + class func GothamProMedium(size: CGFloat) -> UIFont? { + return UIFont(name: "GothamPro-Medium", size: size) + } + + class func GothamProRegular(size: CGFloat) -> UIFont? { + return UIFont(name: "GothamPro-Regular", size: size) + } +} + + +extension UIImage { + func maskWithColor(color: UIColor) -> UIImage? { + let maskImage = cgImage! + + let width = size.width + let height = size.height + let bounds = CGRect(x: 0, y: 0, width: width, height: height) + + let colorSpace = CGColorSpaceCreateDeviceRGB() + let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue) + let context = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: 8, bytesPerRow: 0, space: colorSpace, bitmapInfo: bitmapInfo.rawValue)! + + context.clip(to: bounds, mask: maskImage) + context.setFillColor(color.cgColor) + context.fill(bounds) + + if let cgImage = context.makeImage() { + let coloredImage = UIImage(cgImage: cgImage) + return coloredImage + } else { + return nil + } + } +} + + +extension String { + func capitalizeFirst() -> String { + let firstIndex = self.index(startIndex, offsetBy: 1) + return self.substring(to: firstIndex).capitalized + self.substring(from: firstIndex).lowercased() + } +} diff --git a/Meditation/Utils/Utils.swift b/Meditation/Utils/Utils.swift new file mode 100644 index 0000000..99edbee --- /dev/null +++ b/Meditation/Utils/Utils.swift @@ -0,0 +1,235 @@ +import UIKit +import MessageUI +import KYCircularProgress +import FontAwesome_swift +import SwiftyJSON + + +class Utils { + + // MARK: Data + /////////////////////////////////////////// */ + class func getCurrentDateString() -> String { + let date = Date() + let formatter = DateFormatter() + formatter.dateFormat = "dd.MM.yyyy" + return formatter.string(from: date) + } + + class func bool(key: String) -> Bool { + return UserDefaults.standard.bool(forKey: key) + } + + class func double(key: String) -> Double { + if Utils.contains(key: key) { + return UserDefaults.standard.double(forKey:key) + } + else { + return 0.0 + } + } + + class func int(key: String) -> Int { + if Utils.contains(key: key) { + return UserDefaults.standard.integer(forKey:key) + } + else { + return 0 + } + } + + class func object(key: String) -> Any { + return UserDefaults.standard.object(forKey: key)! + } + + class func string(key: String) -> String { + return UserDefaults.standard.string(forKey: key)! + } + + class func contains(key: String) -> Bool { + return UserDefaults.standard.object(forKey: key) != nil + } + + class func set(key: String, value: Any) { + UserDefaults.standard.set(value, forKey: key) + } + + class func remove(key: String) { + UserDefaults.standard.removeObject(forKey: key) + } + + class func appData() -> JSON { + var appData = JSON() + + if Utils.contains(key: Constants.Defaults.APP_DATA) { + if let dataFromString = Utils.string(key: Constants.Defaults.APP_DATA).data(using: .utf8, allowLossyConversion: false) { + do { + appData = try JSON(data: dataFromString) + } catch let error { + print(error.localizedDescription) + } + } + } + + return appData + } + + class func setAppData(key: String, json: JSON) { + var appData = Utils.appData() + appData[key] = json + Utils.set(key: Constants.Defaults.APP_DATA, value: appData.rawString()!) + } + + + + // MARK: Social + /////////////////////////////////////////// */ + class func openURL(url: String) { + let url = URL(string: url)! + if #available(iOS 10.0, *) { + UIApplication.shared.open(url, options: [:], completionHandler: nil) + } else { + UIApplication.shared.openURL(url) + } + } + + + class func openShareView(viewController: UIViewController) { + let share = Constants.Strings.SHARE + let link : NSURL = NSURL(string: Constants.Strings.LINK_IOS_STORE)! + let logo: UIImage = UIImage(named: Constants.Design.LOGO)! + + let activityViewController : UIActivityViewController = UIActivityViewController(activityItems: [share, link, logo], applicationActivities: nil) + + // This lines is for the popover you need to show in iPad + activityViewController.popoverPresentationController?.sourceView = viewController.view + + // This line remove the arrow of the popover to show in iPad + activityViewController.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection() + activityViewController.popoverPresentationController?.sourceRect = CGRect(x: 150, y: 150, width: 0, height: 0) + + // Anything you want to exclude + activityViewController.excludedActivityTypes = [ + UIActivityType.postToWeibo, + UIActivityType.print, + UIActivityType.assignToContact, + UIActivityType.saveToCameraRoll, + UIActivityType.addToReadingList, + UIActivityType.postToFlickr, + UIActivityType.postToVimeo, + UIActivityType.postToTencentWeibo + ] + + viewController.present(activityViewController, animated: true, completion: nil) + } + + + + // MARK: Visual: Images + /////////////////////////////////////////// */ + class func createHexagonImageView(imageView: UIImageView, lineWidth: CGFloat, lineColor: UIColor) { + let path = Utils.roundedPolygonPath(rect: imageView.bounds, lineWidth: lineWidth, sides: 6, cornerRadius: 10, rotationOffset: CGFloat(M_PI / 2.0)) + + let mask = CAShapeLayer() + mask.path = path.cgPath + mask.lineWidth = lineWidth + mask.strokeColor = UIColor.clear.cgColor + mask.fillColor = UIColor.white.cgColor + imageView.layer.mask = mask + + let border = CAShapeLayer() + border.path = path.cgPath + border.lineWidth = lineWidth + border.strokeColor = lineColor.cgColor + border.fillColor = UIColor.clear.cgColor + imageView.layer.addSublayer(border) + } + + class func roundedPolygonPath(rect: CGRect, lineWidth: CGFloat, sides: NSInteger, cornerRadius: CGFloat, rotationOffset: CGFloat = 0) + -> UIBezierPath { + let path = UIBezierPath() + let theta: CGFloat = CGFloat(2.0 * M_PI) / CGFloat(sides) // How much to turn at every corner + let offset: CGFloat = cornerRadius * tan(theta / 2.0) // Offset from which to start rounding corners + let width = min(rect.size.width, rect.size.height) // Width of the square + + let center = CGPoint(x: rect.origin.x + width / 2.0, y: rect.origin.y + width / 2.0) + + // Radius of the circle that encircles the polygon + // Notice that the radius is adjusted for the corners, that way the largest outer + // dimension of the resulting shape is always exactly the width - linewidth + let radius = (width - lineWidth + cornerRadius - (cos(theta) * cornerRadius)) / 2.0 + + // Start drawing at a point, which by default is at the right hand edge + // but can be offset + var angle = CGFloat(rotationOffset) + + let corner = CGPoint(x: center.x + (radius - cornerRadius) * cos(angle), y: center.y + (radius - cornerRadius) * sin(angle)) + path.move(to: CGPoint(x: corner.x + cornerRadius * cos(angle + theta), y: corner.y + cornerRadius * sin(angle + theta))) + + for _ in 0 ..< sides { + angle += theta + + let corner = CGPoint(x: center.x + (radius - cornerRadius) * cos(angle), y: center.y + (radius - cornerRadius) * sin(angle)) + let tip = CGPoint(x: center.x + radius * cos(angle), y: center.y + radius * sin(angle)) + let start = CGPoint(x: corner.x + cornerRadius * cos(angle - theta), y: corner.y + cornerRadius * sin(angle - theta)) + let end = CGPoint(x: corner.x + cornerRadius * cos(angle + theta), y: corner.y + cornerRadius * sin(angle + theta)) + + path.addLine(to: start) + path.addQuadCurve(to: end, controlPoint: tip) + } + + path.close() + + // Move the path to the correct origins + let bounds = path.bounds + let transform = CGAffineTransform(translationX: -bounds.origin.x + rect.origin.x + lineWidth / 2.0, + y: -bounds.origin.y + rect.origin.y + lineWidth / 2.0) + path.apply(transform) + + return path + } + + + + // MARK: Visual: UI Elements + /////////////////////////////////////////// */ + class func createProgressView(progressView: KYCircularProgress, color: String, guideColor: String) -> KYCircularProgress { + progressView.colors = [UIColor(hex: color)] + progressView.guideColor = UIColor(hex: guideColor) + progressView.lineCap = kCALineCapRound + progressView.guideLineWidth = 6.0 + return progressView + } + + + + // MARK: Visual + /////////////////////////////////////////// */ + class func getViewController(_ viewName: String) -> UIViewController { + let storyboard = UIStoryboard(name: "Main", bundle: nil) + let vc = storyboard.instantiateViewController(withIdentifier: viewName) as UIViewController + return vc + } + + class func presentView(_ view: UIViewController, viewName: String) { + view.present(getViewController(viewName), animated: true, completion: nil) + } + + class func popView(view: UIViewController) { + view.navigationController?.popViewController(animated: true) + view.dismiss(animated: true, completion: nil) + } + + class func createFontAwesomeBarButton(button: UIBarButtonItem, icon: FontAwesome, style: FontAwesomeStyle) { + var attributes = [NSAttributedStringKey : Any]() + attributes = [.font: UIFont.fontAwesome(ofSize: 21, style: style)] + button.setTitleTextAttributes(attributes, for: .normal) + button.setTitleTextAttributes(attributes, for: .selected) + button.title = String.fontAwesomeIcon(name: icon) + } + + class func createFontAwesomeButton(button: UIButton, size: CGFloat, icon: FontAwesome, style: FontAwesomeStyle) { + button.titleLabel?.font = UIFont.fontAwesome(ofSize: size, style: style) + button.setTitle(String.fontAwesomeIcon(name: icon), for: .normal) + } +} diff --git a/MeditationUITests/Info.plist b/MeditationUITests/Info.plist new file mode 100644 index 0000000..6c40a6c --- /dev/null +++ b/MeditationUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/MeditationUITests/MeditationUITests.swift b/MeditationUITests/MeditationUITests.swift new file mode 100644 index 0000000..007cab8 --- /dev/null +++ b/MeditationUITests/MeditationUITests.swift @@ -0,0 +1,56 @@ +// +// MeditationUITests.swift +// MeditationUITests +// +// Created by Joey Tawadrous on 19/08/2018. +// Copyright © 2018 Joey Tawadrous. All rights reserved. +// + +import XCTest + +class MeditationUITests: XCTestCase { + + override func setUp() { + super.setUp() + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. + XCUIApplication().launch() + + let app = XCUIApplication() + setupSnapshot(app) + app.launch() + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testExample() { + let app = XCUIApplication() + + snapshot("Meditation") + + + + app.buttons["GO"].tap() + snapshot("RunMeditation") + +// XCUIDevice.shared.press(XCUIDevice.Button.home) + +// let scrollViewsQuery = app/*@START_MENU_TOKEN@*/.scrollViews/*[[".otherElements[\"Home screen icons\"]",".otherElements[\"SBFolderScalingView\"].scrollViews",".scrollViews"],[[[-1,2],[-1,1],[-1,0,1]],[[-1,2],[-1,1]]],[0]]@END_MENU_TOKEN@*/ +// scrollViewsQuery.otherElements.containing(.icon, identifier:"Watch").element.swipeRight() +// +// let element = app.children(matching: .other).element.children(matching: .other).element(boundBy: 1) +// element/*@START_MENU_TOKEN@*/.press(forDuration: 2.3);/*[[".tap()",".press(forDuration: 2.3);"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/ +// element/*@START_MENU_TOKEN@*/.swipeLeft()/*[[".swipeUp()",".swipeLeft()"],[[[-1,1],[-1,0]]],[0]]@END_MENU_TOKEN@*/ +// app/*@START_MENU_TOKEN@*/.scrollViews.otherElements.tables.buttons["Insert Meditation Fox"]/*[[".otherElements[\"Home screen icons\"]",".otherElements[\"SBFolderScalingView\"].scrollViews.otherElements.tables",".cells[\"Meditation Fox\"].buttons[\"Insert Meditation Fox\"]",".buttons[\"Insert Meditation Fox\"]",".scrollViews.otherElements.tables"],[[[-1,4,2],[-1,1,2],[-1,0,1]],[[-1,4,2],[-1,1,2]],[[-1,3],[-1,2]]],[0,0]]@END_MENU_TOKEN@*/.tap() +// scrollViewsQuery.otherElements.navigationBars["UITableView"].buttons["Done"].tap() +// app/*@START_MENU_TOKEN@*/.scrollViews.otherElements.scrollViews["WGMajorListViewControllerView"].otherElements.buttons["Show More"]/*[[".otherElements[\"Home screen icons\"]",".otherElements[\"SBFolderScalingView\"].scrollViews.otherElements.scrollViews[\"WGMajorListViewControllerView\"].otherElements",".otherElements[\"WGWidgetPlatterView\"].buttons[\"Show More\"]",".buttons[\"Show More\"]",".scrollViews.otherElements.scrollViews[\"WGMajorListViewControllerView\"].otherElements"],[[[-1,4,2],[-1,1,2],[-1,0,1]],[[-1,4,2],[-1,1,2]],[[-1,3],[-1,2]]],[0,0]]@END_MENU_TOKEN@*/.tap() +// +// snapshot("TodayWidget") + } + +} diff --git a/MeditationUITests/SnapshotHelper.swift b/MeditationUITests/SnapshotHelper.swift new file mode 100644 index 0000000..ad57513 --- /dev/null +++ b/MeditationUITests/SnapshotHelper.swift @@ -0,0 +1,276 @@ +// +// SnapshotHelper.swift +// Example +// +// Created by Felix Krause on 10/8/15. +// + +// ----------------------------------------------------- +// IMPORTANT: When modifying this file, make sure to +// increment the version number at the very +// bottom of the file to notify users about +// the new SnapshotHelper.swift +// ----------------------------------------------------- + +import Foundation +import XCTest + +var deviceLanguage = "" +var locale = "" + +func setupSnapshot(_ app: XCUIApplication) { + Snapshot.setupSnapshot(app) +} + +func snapshot(_ name: String, waitForLoadingIndicator: Bool) { + if waitForLoadingIndicator { + Snapshot.snapshot(name) + } else { + Snapshot.snapshot(name, timeWaitingForIdle: 0) + } +} + +/// - Parameters: +/// - name: The name of the snapshot +/// - timeout: Amount of seconds to wait until the network loading indicator disappears. Pass `0` if you don't want to wait. +func snapshot(_ name: String, timeWaitingForIdle timeout: TimeInterval = 20) { + Snapshot.snapshot(name, timeWaitingForIdle: timeout) +} + +enum SnapshotError: Error, CustomDebugStringConvertible { + case cannotDetectUser + case cannotFindHomeDirectory + case cannotFindSimulatorHomeDirectory + case cannotAccessSimulatorHomeDirectory(String) + case cannotRunOnPhysicalDevice + + var debugDescription: String { + switch self { + case .cannotDetectUser: + return "Couldn't find Snapshot configuration files - can't detect current user " + case .cannotFindHomeDirectory: + return "Couldn't find Snapshot configuration files - can't detect `Users` dir" + case .cannotFindSimulatorHomeDirectory: + return "Couldn't find simulator home location. Please, check SIMULATOR_HOST_HOME env variable." + case .cannotAccessSimulatorHomeDirectory(let simulatorHostHome): + return "Can't prepare environment. Simulator home location is inaccessible. Does \(simulatorHostHome) exist?" + case .cannotRunOnPhysicalDevice: + return "Can't use Snapshot on a physical device." + } + } +} + +@objcMembers +open class Snapshot: NSObject { + static var app: XCUIApplication? + static var cacheDirectory: URL? + static var screenshotsDirectory: URL? { + return cacheDirectory?.appendingPathComponent("screenshots", isDirectory: true) + } + + open class func setupSnapshot(_ app: XCUIApplication) { + + Snapshot.app = app + + do { + let cacheDir = try pathPrefix() + Snapshot.cacheDirectory = cacheDir + setLanguage(app) + setLocale(app) + setLaunchArguments(app) + } catch let error { + print(error) + } + } + + class func setLanguage(_ app: XCUIApplication) { + guard let cacheDirectory = self.cacheDirectory else { + print("CacheDirectory is not set - probably running on a physical device?") + return + } + + let path = cacheDirectory.appendingPathComponent("language.txt") + + do { + let trimCharacterSet = CharacterSet.whitespacesAndNewlines + deviceLanguage = try String(contentsOf: path, encoding: .utf8).trimmingCharacters(in: trimCharacterSet) + app.launchArguments += ["-AppleLanguages", "(\(deviceLanguage))"] + } catch { + print("Couldn't detect/set language...") + } + } + + class func setLocale(_ app: XCUIApplication) { + guard let cacheDirectory = self.cacheDirectory else { + print("CacheDirectory is not set - probably running on a physical device?") + return + } + + let path = cacheDirectory.appendingPathComponent("locale.txt") + + do { + let trimCharacterSet = CharacterSet.whitespacesAndNewlines + locale = try String(contentsOf: path, encoding: .utf8).trimmingCharacters(in: trimCharacterSet) + } catch { + print("Couldn't detect/set locale...") + } + if locale.isEmpty { + locale = Locale(identifier: deviceLanguage).identifier + } + app.launchArguments += ["-AppleLocale", "\"\(locale)\""] + } + + class func setLaunchArguments(_ app: XCUIApplication) { + guard let cacheDirectory = self.cacheDirectory else { + print("CacheDirectory is not set - probably running on a physical device?") + return + } + + let path = cacheDirectory.appendingPathComponent("snapshot-launch_arguments.txt") + app.launchArguments += ["-FASTLANE_SNAPSHOT", "YES", "-ui_testing"] + + do { + let launchArguments = try String(contentsOf: path, encoding: String.Encoding.utf8) + let regex = try NSRegularExpression(pattern: "(\\\".+?\\\"|\\S+)", options: []) + let matches = regex.matches(in: launchArguments, options: [], range: NSRange(location: 0, length: launchArguments.count)) + let results = matches.map { result -> String in + (launchArguments as NSString).substring(with: result.range) + } + app.launchArguments += results + } catch { + print("Couldn't detect/set launch_arguments...") + } + } + + open class func snapshot(_ name: String, timeWaitingForIdle timeout: TimeInterval = 20) { + if timeout > 0 { + waitForLoadingIndicatorToDisappear(within: timeout) + } + + print("snapshot: \(name)") // more information about this, check out https://docs.fastlane.tools/actions/snapshot/#how-does-it-work + + sleep(1) // Waiting for the animation to be finished (kind of) + + #if os(OSX) + XCUIApplication().typeKey(XCUIKeyboardKeySecondaryFn, modifierFlags: []) + #else + + guard let app = self.app else { + print("XCUIApplication is not set. Please call setupSnapshot(app) before snapshot().") + return + } + + let window = app.windows.firstMatch + let screenshot = window.screenshot() + guard let simulator = ProcessInfo().environment["SIMULATOR_DEVICE_NAME"], let screenshotsDir = screenshotsDirectory else { return } + let path = screenshotsDir.appendingPathComponent("\(simulator)-\(name).png") + do { + try screenshot.pngRepresentation.write(to: path) + } catch let error { + print("Problem writing screenshot: \(name) to \(path)") + print(error) + } + #endif + } + + class func waitForLoadingIndicatorToDisappear(within timeout: TimeInterval) { + #if os(tvOS) + return + #endif + + let networkLoadingIndicator = XCUIApplication().otherElements.deviceStatusBars.networkLoadingIndicators.element + let networkLoadingIndicatorDisappeared = XCTNSPredicateExpectation(predicate: NSPredicate(format: "exists == false"), object: networkLoadingIndicator) + _ = XCTWaiter.wait(for: [networkLoadingIndicatorDisappeared], timeout: timeout) + } + + class func pathPrefix() throws -> URL? { + let homeDir: URL + // on OSX config is stored in /Users//Library + // and on iOS/tvOS/WatchOS it's in simulator's home dir + #if os(OSX) + guard let user = ProcessInfo().environment["USER"] else { + throw SnapshotError.cannotDetectUser + } + + guard let usersDir = FileManager.default.urls(for: .userDirectory, in: .localDomainMask).first else { + throw SnapshotError.cannotFindHomeDirectory + } + + homeDir = usersDir.appendingPathComponent(user) + #else + #if arch(i386) || arch(x86_64) + guard let simulatorHostHome = ProcessInfo().environment["SIMULATOR_HOST_HOME"] else { + throw SnapshotError.cannotFindSimulatorHomeDirectory + } + guard let homeDirUrl = URL(string: simulatorHostHome) else { + throw SnapshotError.cannotAccessSimulatorHomeDirectory(simulatorHostHome) + } + homeDir = URL(fileURLWithPath: homeDirUrl.path) + #else + throw SnapshotError.cannotRunOnPhysicalDevice + #endif + #endif + return homeDir.appendingPathComponent("Library/Caches/tools.fastlane") + } +} + +private extension XCUIElementAttributes { + var isNetworkLoadingIndicator: Bool { + if hasWhiteListedIdentifier { return false } + + let hasOldLoadingIndicatorSize = frame.size == CGSize(width: 10, height: 20) + let hasNewLoadingIndicatorSize = frame.size.width.isBetween(46, and: 47) && frame.size.height.isBetween(2, and: 3) + + return hasOldLoadingIndicatorSize || hasNewLoadingIndicatorSize + } + + var hasWhiteListedIdentifier: Bool { + let whiteListedIdentifiers = ["GeofenceLocationTrackingOn", "StandardLocationTrackingOn"] + + return whiteListedIdentifiers.contains(identifier) + } + + func isStatusBar(_ deviceWidth: CGFloat) -> Bool { + if elementType == .statusBar { return true } + guard frame.origin == .zero else { return false } + + let oldStatusBarSize = CGSize(width: deviceWidth, height: 20) + let newStatusBarSize = CGSize(width: deviceWidth, height: 44) + + return [oldStatusBarSize, newStatusBarSize].contains(frame.size) + } +} + +private extension XCUIElementQuery { + var networkLoadingIndicators: XCUIElementQuery { + let isNetworkLoadingIndicator = NSPredicate { (evaluatedObject, _) in + guard let element = evaluatedObject as? XCUIElementAttributes else { return false } + + return element.isNetworkLoadingIndicator + } + + return self.containing(isNetworkLoadingIndicator) + } + + var deviceStatusBars: XCUIElementQuery { + let deviceWidth = XCUIApplication().windows.firstMatch.frame.width + + let isStatusBar = NSPredicate { (evaluatedObject, _) in + guard let element = evaluatedObject as? XCUIElementAttributes else { return false } + + return element.isStatusBar(deviceWidth) + } + + return self.containing(isStatusBar) + } +} + +private extension CGFloat { + func isBetween(_ numberA: CGFloat, and numberB: CGFloat) -> Bool { + return numberA...numberB ~= self + } +} + +// Please don't remove the lines below +// They are used to detect outdated configuration files +// SnapshotHelperVersion [1.12] diff --git a/Podfile b/Podfile new file mode 100644 index 0000000..7143af6 --- /dev/null +++ b/Podfile @@ -0,0 +1,17 @@ +# Uncomment the next line to define a global platform for your project +# platform :ios, '9.0' + +target 'Meditation' do + # Comment the next line if you're not using Swift and don't want to use dynamic frameworks + use_frameworks! + + # Pods for Meditation + pod 'KYCircularProgress' + pod 'FontAwesome.swift' + pod 'SwiftySound' + pod 'SCLAlertView' + pod 'SwiftyStoreKit' + pod 'paper-onboarding' + pod 'SwiftyJSON' + +end diff --git a/Podfile.lock b/Podfile.lock new file mode 100644 index 0000000..af0cbef --- /dev/null +++ b/Podfile.lock @@ -0,0 +1,40 @@ +PODS: + - FontAwesome.swift (1.4.4) + - KYCircularProgress (1.2.0) + - paper-onboarding (4.1.0) + - SCLAlertView (0.8) + - SwiftyJSON (4.1.0) + - SwiftySound (1.0.0) + - SwiftyStoreKit (0.13.3) + +DEPENDENCIES: + - FontAwesome.swift + - KYCircularProgress + - paper-onboarding + - SCLAlertView + - SwiftyJSON + - SwiftySound + - SwiftyStoreKit + +SPEC REPOS: + https://github.com/CocoaPods/Specs.git: + - FontAwesome.swift + - KYCircularProgress + - paper-onboarding + - SCLAlertView + - SwiftyJSON + - SwiftySound + - SwiftyStoreKit + +SPEC CHECKSUMS: + FontAwesome.swift: 17b93524b9f61136a8566344517b8556ed584e87 + KYCircularProgress: b289be602d7c7cde5b4c88f84738231776f3557d + paper-onboarding: 344780cbb0bfc229cd1862f5b9bba446b23c5185 + SCLAlertView: 6a77bb2edfc65e04dbe57725546cb4107a506b85 + SwiftyJSON: c29297daf073d2aa016295d5809cdd68045c39b3 + SwiftySound: b9c3370956fd4ed65ed79d45563070077a50a2cd + SwiftyStoreKit: 9ebd15971e28aa2989825fe8bc092eb5f75dd0b2 + +PODFILE CHECKSUM: d4d372e61d40adccb8596f5a21862d95e0442449 + +COCOAPODS: 1.5.0 diff --git a/Pods/FontAwesome.swift/FontAwesome/Enum.swift b/Pods/FontAwesome.swift/FontAwesome/Enum.swift new file mode 100644 index 0000000..f440ae2 --- /dev/null +++ b/Pods/FontAwesome.swift/FontAwesome/Enum.swift @@ -0,0 +1,2416 @@ +// Enum.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// DO NOT EDIT! This file is auto-generated. To regenerate it, update +// Font-Awesome submodule and run `./codegen.swift`. + +/// An enumaration of FontAwesome icon names. +// swiftlint:disable file_length type_body_length +public enum FontAwesome: String { + case fiveHundredPixels = "\u{f26e}" + case accessibleIcon = "\u{f368}" + case accusoft = "\u{f369}" + case ad = "\u{f641}" + case addressBook = "\u{f2b9}" + case addressCard = "\u{f2bb}" + case adjust = "\u{f042}" + case adn = "\u{f170}" + case adversal = "\u{f36a}" + case affiliatetheme = "\u{f36b}" + case airFreshener = "\u{f5d0}" + case algolia = "\u{f36c}" + case alignCenter = "\u{f037}" + case alignJustify = "\u{f039}" + case alignLeft = "\u{f036}" + case alignRight = "\u{f038}" + case alipay = "\u{f642}" + case allergies = "\u{f461}" + case amazon = "\u{f270}" + case amazonPay = "\u{f42c}" + case ambulance = "\u{f0f9}" + case americanSignLanguageInterpreting = "\u{f2a3}" + case amilia = "\u{f36d}" + case anchor = "\u{f13d}" + case android = "\u{f17b}" + case angellist = "\u{f209}" + case angleDoubleDown = "\u{f103}" + case angleDoubleLeft = "\u{f100}" + case angleDoubleRight = "\u{f101}" + case angleDoubleUp = "\u{f102}" + case angleDown = "\u{f107}" + case angleLeft = "\u{f104}" + case angleRight = "\u{f105}" + case angleUp = "\u{f106}" + case angry = "\u{f556}" + case angrycreative = "\u{f36e}" + case angular = "\u{f420}" + case ankh = "\u{f644}" + case appStore = "\u{f36f}" + case appStoreIos = "\u{f370}" + case apper = "\u{f371}" + case apple = "\u{f179}" + case appleAlt = "\u{f5d1}" + case applePay = "\u{f415}" + case archive = "\u{f187}" + case archway = "\u{f557}" + case arrowAltCircleDown = "\u{f358}" + case arrowAltCircleLeft = "\u{f359}" + case arrowAltCircleRight = "\u{f35a}" + case arrowAltCircleUp = "\u{f35b}" + case arrowCircleDown = "\u{f0ab}" + case arrowCircleLeft = "\u{f0a8}" + case arrowCircleRight = "\u{f0a9}" + case arrowCircleUp = "\u{f0aa}" + case arrowDown = "\u{f063}" + case arrowLeft = "\u{f060}" + case arrowRight = "\u{f061}" + case arrowUp = "\u{f062}" + case arrowsAlt = "\u{f0b2}" + case arrowsAltH = "\u{f337}" + case arrowsAltV = "\u{f338}" + case assistiveListeningSystems = "\u{f2a2}" + case asterisk = "\u{f069}" + case asymmetrik = "\u{f372}" + case at = "\u{f1fa}" + case atlas = "\u{f558}" + case atom = "\u{f5d2}" + case audible = "\u{f373}" + case audioDescription = "\u{f29e}" + case autoprefixer = "\u{f41c}" + case avianex = "\u{f374}" + case aviato = "\u{f421}" + case award = "\u{f559}" + case aws = "\u{f375}" + case backspace = "\u{f55a}" + case backward = "\u{f04a}" + case balanceScale = "\u{f24e}" + case ban = "\u{f05e}" + case bandAid = "\u{f462}" + case bandcamp = "\u{f2d5}" + case barcode = "\u{f02a}" + case bars = "\u{f0c9}" + case baseballBall = "\u{f433}" + case basketballBall = "\u{f434}" + case bath = "\u{f2cd}" + case batteryEmpty = "\u{f244}" + case batteryFull = "\u{f240}" + case batteryHalf = "\u{f242}" + case batteryQuarter = "\u{f243}" + case batteryThreeQuarters = "\u{f241}" + case bed = "\u{f236}" + case beer = "\u{f0fc}" + case behance = "\u{f1b4}" + case behanceSquare = "\u{f1b5}" + case bell = "\u{f0f3}" + case bellSlash = "\u{f1f6}" + case bezierCurve = "\u{f55b}" + case bible = "\u{f647}" + case bicycle = "\u{f206}" + case bimobject = "\u{f378}" + case binoculars = "\u{f1e5}" + case birthdayCake = "\u{f1fd}" + case bitbucket = "\u{f171}" + case bitcoin = "\u{f379}" + case bity = "\u{f37a}" + case blackTie = "\u{f27e}" + case blackberry = "\u{f37b}" + case blender = "\u{f517}" + case blind = "\u{f29d}" + case blogger = "\u{f37c}" + case bloggerB = "\u{f37d}" + case bluetooth = "\u{f293}" + case bluetoothB = "\u{f294}" + case bold = "\u{f032}" + case bolt = "\u{f0e7}" + case bomb = "\u{f1e2}" + case bone = "\u{f5d7}" + case bong = "\u{f55c}" + case book = "\u{f02d}" + case bookOpen = "\u{f518}" + case bookReader = "\u{f5da}" + case bookmark = "\u{f02e}" + case bowlingBall = "\u{f436}" + case box = "\u{f466}" + case boxOpen = "\u{f49e}" + case boxes = "\u{f468}" + case braille = "\u{f2a1}" + case brain = "\u{f5dc}" + case briefcase = "\u{f0b1}" + case briefcaseMedical = "\u{f469}" + case broadcastTower = "\u{f519}" + case broom = "\u{f51a}" + case brush = "\u{f55d}" + case btc = "\u{f15a}" + case bug = "\u{f188}" + case building = "\u{f1ad}" + case bullhorn = "\u{f0a1}" + case bullseye = "\u{f140}" + case burn = "\u{f46a}" + case buromobelexperte = "\u{f37f}" + case bus = "\u{f207}" + case busAlt = "\u{f55e}" + case businessTime = "\u{f64a}" + case buysellads = "\u{f20d}" + case calculator = "\u{f1ec}" + case calendar = "\u{f133}" + case calendarAlt = "\u{f073}" + case calendarCheck = "\u{f274}" + case calendarMinus = "\u{f272}" + case calendarPlus = "\u{f271}" + case calendarTimes = "\u{f273}" + case camera = "\u{f030}" + case cameraRetro = "\u{f083}" + case cannabis = "\u{f55f}" + case capsules = "\u{f46b}" + case car = "\u{f1b9}" + case carAlt = "\u{f5de}" + case carBattery = "\u{f5df}" + case carCrash = "\u{f5e1}" + case carSide = "\u{f5e4}" + case caretDown = "\u{f0d7}" + case caretLeft = "\u{f0d9}" + case caretRight = "\u{f0da}" + case caretSquareDown = "\u{f150}" + case caretSquareLeft = "\u{f191}" + case caretSquareRight = "\u{f152}" + case caretSquareUp = "\u{f151}" + case caretUp = "\u{f0d8}" + case cartArrowDown = "\u{f218}" + case cartPlus = "\u{f217}" + case ccAmazonPay = "\u{f42d}" + case ccAmex = "\u{f1f3}" + case ccApplePay = "\u{f416}" + case ccDinersClub = "\u{f24c}" + case ccDiscover = "\u{f1f2}" + case ccJcb = "\u{f24b}" + case ccMastercard = "\u{f1f1}" + case ccPaypal = "\u{f1f4}" + case ccStripe = "\u{f1f5}" + case ccVisa = "\u{f1f0}" + case centercode = "\u{f380}" + case certificate = "\u{f0a3}" + case chalkboard = "\u{f51b}" + case chalkboardTeacher = "\u{f51c}" + case chargingStation = "\u{f5e7}" + case chartArea = "\u{f1fe}" + case chartBar = "\u{f080}" + case chartLine = "\u{f201}" + case chartPie = "\u{f200}" + case check = "\u{f00c}" + case checkCircle = "\u{f058}" + case checkDouble = "\u{f560}" + case checkSquare = "\u{f14a}" + case chess = "\u{f439}" + case chessBishop = "\u{f43a}" + case chessBoard = "\u{f43c}" + case chessKing = "\u{f43f}" + case chessKnight = "\u{f441}" + case chessPawn = "\u{f443}" + case chessQueen = "\u{f445}" + case chessRook = "\u{f447}" + case chevronCircleDown = "\u{f13a}" + case chevronCircleLeft = "\u{f137}" + case chevronCircleRight = "\u{f138}" + case chevronCircleUp = "\u{f139}" + case chevronDown = "\u{f078}" + case chevronLeft = "\u{f053}" + case chevronRight = "\u{f054}" + case chevronUp = "\u{f077}" + case child = "\u{f1ae}" + case chrome = "\u{f268}" + case church = "\u{f51d}" + case circle = "\u{f111}" + case circleNotch = "\u{f1ce}" + case city = "\u{f64f}" + case clipboard = "\u{f328}" + case clipboardCheck = "\u{f46c}" + case clipboardList = "\u{f46d}" + case clock = "\u{f017}" + case clone = "\u{f24d}" + case closedCaptioning = "\u{f20a}" + case cloud = "\u{f0c2}" + case cloudDownloadAlt = "\u{f381}" + case cloudUploadAlt = "\u{f382}" + case cloudscale = "\u{f383}" + case cloudsmith = "\u{f384}" + case cloudversify = "\u{f385}" + case cocktail = "\u{f561}" + case code = "\u{f121}" + case codeBranch = "\u{f126}" + case codepen = "\u{f1cb}" + case codiepie = "\u{f284}" + case coffee = "\u{f0f4}" + case cog = "\u{f013}" + case cogs = "\u{f085}" + case coins = "\u{f51e}" + case columns = "\u{f0db}" + case comment = "\u{f075}" + case commentAlt = "\u{f27a}" + case commentDollar = "\u{f651}" + case commentDots = "\u{f4ad}" + case commentSlash = "\u{f4b3}" + case comments = "\u{f086}" + case commentsDollar = "\u{f653}" + case compactDisc = "\u{f51f}" + case compass = "\u{f14e}" + case compress = "\u{f066}" + case conciergeBell = "\u{f562}" + case connectdevelop = "\u{f20e}" + case contao = "\u{f26d}" + case cookie = "\u{f563}" + case cookieBite = "\u{f564}" + case copy = "\u{f0c5}" + case copyright = "\u{f1f9}" + case couch = "\u{f4b8}" + case cpanel = "\u{f388}" + case creativeCommons = "\u{f25e}" + case creativeCommonsBy = "\u{f4e7}" + case creativeCommonsNc = "\u{f4e8}" + case creativeCommonsNcEu = "\u{f4e9}" + case creativeCommonsNcJp = "\u{f4ea}" + case creativeCommonsNd = "\u{f4eb}" + case creativeCommonsPd = "\u{f4ec}" + case creativeCommonsPdAlt = "\u{f4ed}" + case creativeCommonsRemix = "\u{f4ee}" + case creativeCommonsSa = "\u{f4ef}" + case creativeCommonsSampling = "\u{f4f0}" + case creativeCommonsSamplingPlus = "\u{f4f1}" + case creativeCommonsShare = "\u{f4f2}" + case creditCard = "\u{f09d}" + case crop = "\u{f125}" + case cropAlt = "\u{f565}" + case cross = "\u{f654}" + case crosshairs = "\u{f05b}" + case crow = "\u{f520}" + case crown = "\u{f521}" + case css3 = "\u{f13c}" + case css3Alt = "\u{f38b}" + case cube = "\u{f1b2}" + case cubes = "\u{f1b3}" + case cut = "\u{f0c4}" + case cuttlefish = "\u{f38c}" + case dAndD = "\u{f38d}" + case dashcube = "\u{f210}" + case database = "\u{f1c0}" + case deaf = "\u{f2a4}" + case delicious = "\u{f1a5}" + case deploydog = "\u{f38e}" + case deskpro = "\u{f38f}" + case desktop = "\u{f108}" + case deviantart = "\u{f1bd}" + case dharmachakra = "\u{f655}" + case diagnoses = "\u{f470}" + case dice = "\u{f522}" + case diceFive = "\u{f523}" + case diceFour = "\u{f524}" + case diceOne = "\u{f525}" + case diceSix = "\u{f526}" + case diceThree = "\u{f527}" + case diceTwo = "\u{f528}" + case digg = "\u{f1a6}" + case digitalOcean = "\u{f391}" + case digitalTachograph = "\u{f566}" + case directions = "\u{f5eb}" + case discord = "\u{f392}" + case discourse = "\u{f393}" + case divide = "\u{f529}" + case dizzy = "\u{f567}" + case dna = "\u{f471}" + case dochub = "\u{f394}" + case docker = "\u{f395}" + case dollarSign = "\u{f155}" + case dolly = "\u{f472}" + case dollyFlatbed = "\u{f474}" + case donate = "\u{f4b9}" + case doorClosed = "\u{f52a}" + case doorOpen = "\u{f52b}" + case dotCircle = "\u{f192}" + case dove = "\u{f4ba}" + case download = "\u{f019}" + case draft2digital = "\u{f396}" + case draftingCompass = "\u{f568}" + case drawPolygon = "\u{f5ee}" + case dribbble = "\u{f17d}" + case dribbbleSquare = "\u{f397}" + case dropbox = "\u{f16b}" + case drum = "\u{f569}" + case drumSteelpan = "\u{f56a}" + case drupal = "\u{f1a9}" + case dumbbell = "\u{f44b}" + case dyalog = "\u{f399}" + case earlybirds = "\u{f39a}" + case ebay = "\u{f4f4}" + case edge = "\u{f282}" + case edit = "\u{f044}" + case eject = "\u{f052}" + case elementor = "\u{f430}" + case ellipsisH = "\u{f141}" + case ellipsisV = "\u{f142}" + case ello = "\u{f5f1}" + case ember = "\u{f423}" + case empire = "\u{f1d1}" + case envelope = "\u{f0e0}" + case envelopeOpen = "\u{f2b6}" + case envelopeOpenText = "\u{f658}" + case envelopeSquare = "\u{f199}" + case envira = "\u{f299}" + case equals = "\u{f52c}" + case eraser = "\u{f12d}" + case erlang = "\u{f39d}" + case ethereum = "\u{f42e}" + case etsy = "\u{f2d7}" + case euroSign = "\u{f153}" + case exchangeAlt = "\u{f362}" + case exclamation = "\u{f12a}" + case exclamationCircle = "\u{f06a}" + case exclamationTriangle = "\u{f071}" + case expand = "\u{f065}" + case expandArrowsAlt = "\u{f31e}" + case expeditedssl = "\u{f23e}" + case externalLinkAlt = "\u{f35d}" + case externalLinkSquareAlt = "\u{f360}" + case eye = "\u{f06e}" + case eyeDropper = "\u{f1fb}" + case eyeSlash = "\u{f070}" + case facebook = "\u{f09a}" + case facebookF = "\u{f39e}" + case facebookMessenger = "\u{f39f}" + case facebookSquare = "\u{f082}" + case fastBackward = "\u{f049}" + case fastForward = "\u{f050}" + case fax = "\u{f1ac}" + case feather = "\u{f52d}" + case featherAlt = "\u{f56b}" + case female = "\u{f182}" + case fighterJet = "\u{f0fb}" + case file = "\u{f15b}" + case fileAlt = "\u{f15c}" + case fileArchive = "\u{f1c6}" + case fileAudio = "\u{f1c7}" + case fileCode = "\u{f1c9}" + case fileContract = "\u{f56c}" + case fileDownload = "\u{f56d}" + case fileExcel = "\u{f1c3}" + case fileExport = "\u{f56e}" + case fileImage = "\u{f1c5}" + case fileImport = "\u{f56f}" + case fileInvoice = "\u{f570}" + case fileInvoiceDollar = "\u{f571}" + case fileMedical = "\u{f477}" + case fileMedicalAlt = "\u{f478}" + case filePdf = "\u{f1c1}" + case filePowerpoint = "\u{f1c4}" + case filePrescription = "\u{f572}" + case fileSignature = "\u{f573}" + case fileUpload = "\u{f574}" + case fileVideo = "\u{f1c8}" + case fileWord = "\u{f1c2}" + case fill = "\u{f575}" + case fillDrip = "\u{f576}" + case film = "\u{f008}" + case filter = "\u{f0b0}" + case fingerprint = "\u{f577}" + case fire = "\u{f06d}" + case fireExtinguisher = "\u{f134}" + case firefox = "\u{f269}" + case firstAid = "\u{f479}" + case firstOrder = "\u{f2b0}" + case firstOrderAlt = "\u{f50a}" + case firstdraft = "\u{f3a1}" + case fish = "\u{f578}" + case flag = "\u{f024}" + case flagCheckered = "\u{f11e}" + case flask = "\u{f0c3}" + case flickr = "\u{f16e}" + case flipboard = "\u{f44d}" + case flushed = "\u{f579}" + case fly = "\u{f417}" + case folder = "\u{f07b}" + case folderMinus = "\u{f65d}" + case folderOpen = "\u{f07c}" + case folderPlus = "\u{f65e}" + case font = "\u{f031}" + case fontAwesome = "\u{f2b4}" + case fontAwesomeAlt = "\u{f35c}" + case fontAwesomeFlag = "\u{f425}" + case fontAwesomeLogoFull = "\u{f4e6}" + case fonticons = "\u{f280}" + case fonticonsFi = "\u{f3a2}" + case footballBall = "\u{f44e}" + case fortAwesome = "\u{f286}" + case fortAwesomeAlt = "\u{f3a3}" + case forumbee = "\u{f211}" + case forward = "\u{f04e}" + case foursquare = "\u{f180}" + case freeCodeCamp = "\u{f2c5}" + case freebsd = "\u{f3a4}" + case frog = "\u{f52e}" + case frown = "\u{f119}" + case frownOpen = "\u{f57a}" + case fulcrum = "\u{f50b}" + case funnelDollar = "\u{f662}" + case futbol = "\u{f1e3}" + case galacticRepublic = "\u{f50c}" + case galacticSenate = "\u{f50d}" + case gamepad = "\u{f11b}" + case gasPump = "\u{f52f}" + case gavel = "\u{f0e3}" + case gem = "\u{f3a5}" + case genderless = "\u{f22d}" + case getPocket = "\u{f265}" + case gg = "\u{f260}" + case ggCircle = "\u{f261}" + case gift = "\u{f06b}" + case git = "\u{f1d3}" + case gitSquare = "\u{f1d2}" + case github = "\u{f09b}" + case githubAlt = "\u{f113}" + case githubSquare = "\u{f092}" + case gitkraken = "\u{f3a6}" + case gitlab = "\u{f296}" + case gitter = "\u{f426}" + case glassMartini = "\u{f000}" + case glassMartiniAlt = "\u{f57b}" + case glasses = "\u{f530}" + case glide = "\u{f2a5}" + case glideG = "\u{f2a6}" + case globe = "\u{f0ac}" + case globeAfrica = "\u{f57c}" + case globeAmericas = "\u{f57d}" + case globeAsia = "\u{f57e}" + case gofore = "\u{f3a7}" + case golfBall = "\u{f450}" + case goodreads = "\u{f3a8}" + case goodreadsG = "\u{f3a9}" + case google = "\u{f1a0}" + case googleDrive = "\u{f3aa}" + case googlePlay = "\u{f3ab}" + case googlePlus = "\u{f2b3}" + case googlePlusG = "\u{f0d5}" + case googlePlusSquare = "\u{f0d4}" + case googleWallet = "\u{f1ee}" + case gopuram = "\u{f664}" + case graduationCap = "\u{f19d}" + case gratipay = "\u{f184}" + case grav = "\u{f2d6}" + case greaterThan = "\u{f531}" + case greaterThanEqual = "\u{f532}" + case grimace = "\u{f57f}" + case grin = "\u{f580}" + case grinAlt = "\u{f581}" + case grinBeam = "\u{f582}" + case grinBeamSweat = "\u{f583}" + case grinHearts = "\u{f584}" + case grinSquint = "\u{f585}" + case grinSquintTears = "\u{f586}" + case grinStars = "\u{f587}" + case grinTears = "\u{f588}" + case grinTongue = "\u{f589}" + case grinTongueSquint = "\u{f58a}" + case grinTongueWink = "\u{f58b}" + case grinWink = "\u{f58c}" + case gripHorizontal = "\u{f58d}" + case gripVertical = "\u{f58e}" + case gripfire = "\u{f3ac}" + case grunt = "\u{f3ad}" + case gulp = "\u{f3ae}" + case hSquare = "\u{f0fd}" + case hackerNews = "\u{f1d4}" + case hackerNewsSquare = "\u{f3af}" + case hackerrank = "\u{f5f7}" + case hamsa = "\u{f665}" + case handHolding = "\u{f4bd}" + case handHoldingHeart = "\u{f4be}" + case handHoldingUsd = "\u{f4c0}" + case handLizard = "\u{f258}" + case handPaper = "\u{f256}" + case handPeace = "\u{f25b}" + case handPointDown = "\u{f0a7}" + case handPointLeft = "\u{f0a5}" + case handPointRight = "\u{f0a4}" + case handPointUp = "\u{f0a6}" + case handPointer = "\u{f25a}" + case handRock = "\u{f255}" + case handScissors = "\u{f257}" + case handSpock = "\u{f259}" + case hands = "\u{f4c2}" + case handsHelping = "\u{f4c4}" + case handshake = "\u{f2b5}" + case hashtag = "\u{f292}" + case haykal = "\u{f666}" + case hdd = "\u{f0a0}" + case heading = "\u{f1dc}" + case headphones = "\u{f025}" + case headphonesAlt = "\u{f58f}" + case headset = "\u{f590}" + case heart = "\u{f004}" + case heartbeat = "\u{f21e}" + case helicopter = "\u{f533}" + case highlighter = "\u{f591}" + case hips = "\u{f452}" + case hireAHelper = "\u{f3b0}" + case history = "\u{f1da}" + case hockeyPuck = "\u{f453}" + case home = "\u{f015}" + case hooli = "\u{f427}" + case hornbill = "\u{f592}" + case hospital = "\u{f0f8}" + case hospitalAlt = "\u{f47d}" + case hospitalSymbol = "\u{f47e}" + case hotTub = "\u{f593}" + case hotel = "\u{f594}" + case hotjar = "\u{f3b1}" + case hourglass = "\u{f254}" + case hourglassEnd = "\u{f253}" + case hourglassHalf = "\u{f252}" + case hourglassStart = "\u{f251}" + case houzz = "\u{f27c}" + case html5 = "\u{f13b}" + case hubspot = "\u{f3b2}" + case iCursor = "\u{f246}" + case idBadge = "\u{f2c1}" + case idCard = "\u{f2c2}" + case idCardAlt = "\u{f47f}" + case image = "\u{f03e}" + case images = "\u{f302}" + case imdb = "\u{f2d8}" + case inbox = "\u{f01c}" + case indent = "\u{f03c}" + case industry = "\u{f275}" + case infinity = "\u{f534}" + case info = "\u{f129}" + case infoCircle = "\u{f05a}" + case instagram = "\u{f16d}" + case internetExplorer = "\u{f26b}" + case ioxhost = "\u{f208}" + case italic = "\u{f033}" + case itunes = "\u{f3b4}" + case itunesNote = "\u{f3b5}" + case java = "\u{f4e4}" + case jedi = "\u{f669}" + case jediOrder = "\u{f50e}" + case jenkins = "\u{f3b6}" + case joget = "\u{f3b7}" + case joint = "\u{f595}" + case joomla = "\u{f1aa}" + case journalWhills = "\u{f66a}" + case js = "\u{f3b8}" + case jsSquare = "\u{f3b9}" + case jsfiddle = "\u{f1cc}" + case kaaba = "\u{f66b}" + case kaggle = "\u{f5fa}" + case key = "\u{f084}" + case keybase = "\u{f4f5}" + case keyboard = "\u{f11c}" + case keycdn = "\u{f3ba}" + case khanda = "\u{f66d}" + case kickstarter = "\u{f3bb}" + case kickstarterK = "\u{f3bc}" + case kiss = "\u{f596}" + case kissBeam = "\u{f597}" + case kissWinkHeart = "\u{f598}" + case kiwiBird = "\u{f535}" + case korvue = "\u{f42f}" + case landmark = "\u{f66f}" + case language = "\u{f1ab}" + case laptop = "\u{f109}" + case laptopCode = "\u{f5fc}" + case laravel = "\u{f3bd}" + case lastfm = "\u{f202}" + case lastfmSquare = "\u{f203}" + case laugh = "\u{f599}" + case laughBeam = "\u{f59a}" + case laughSquint = "\u{f59b}" + case laughWink = "\u{f59c}" + case layerGroup = "\u{f5fd}" + case leaf = "\u{f06c}" + case leanpub = "\u{f212}" + case lemon = "\u{f094}" + case less = "\u{f41d}" + case lessThan = "\u{f536}" + case lessThanEqual = "\u{f537}" + case levelDownAlt = "\u{f3be}" + case levelUpAlt = "\u{f3bf}" + case lifeRing = "\u{f1cd}" + case lightbulb = "\u{f0eb}" + case line = "\u{f3c0}" + case link = "\u{f0c1}" + case linkedin = "\u{f08c}" + case linkedinIn = "\u{f0e1}" + case linode = "\u{f2b8}" + case linux = "\u{f17c}" + case liraSign = "\u{f195}" + case list = "\u{f03a}" + case listAlt = "\u{f022}" + case listOl = "\u{f0cb}" + case listUl = "\u{f0ca}" + case locationArrow = "\u{f124}" + case lock = "\u{f023}" + case lockOpen = "\u{f3c1}" + case longArrowAltDown = "\u{f309}" + case longArrowAltLeft = "\u{f30a}" + case longArrowAltRight = "\u{f30b}" + case longArrowAltUp = "\u{f30c}" + case lowVision = "\u{f2a8}" + case luggageCart = "\u{f59d}" + case lyft = "\u{f3c3}" + case magento = "\u{f3c4}" + case magic = "\u{f0d0}" + case magnet = "\u{f076}" + case mailBulk = "\u{f674}" + case mailchimp = "\u{f59e}" + case male = "\u{f183}" + case mandalorian = "\u{f50f}" + case map = "\u{f279}" + case mapMarked = "\u{f59f}" + case mapMarkedAlt = "\u{f5a0}" + case mapMarker = "\u{f041}" + case mapMarkerAlt = "\u{f3c5}" + case mapPin = "\u{f276}" + case mapSigns = "\u{f277}" + case markdown = "\u{f60f}" + case marker = "\u{f5a1}" + case mars = "\u{f222}" + case marsDouble = "\u{f227}" + case marsStroke = "\u{f229}" + case marsStrokeH = "\u{f22b}" + case marsStrokeV = "\u{f22a}" + case mastodon = "\u{f4f6}" + case maxcdn = "\u{f136}" + case medal = "\u{f5a2}" + case medapps = "\u{f3c6}" + case medium = "\u{f23a}" + case mediumM = "\u{f3c7}" + case medkit = "\u{f0fa}" + case medrt = "\u{f3c8}" + case meetup = "\u{f2e0}" + case megaport = "\u{f5a3}" + case meh = "\u{f11a}" + case mehBlank = "\u{f5a4}" + case mehRollingEyes = "\u{f5a5}" + case memory = "\u{f538}" + case menorah = "\u{f676}" + case mercury = "\u{f223}" + case microchip = "\u{f2db}" + case microphone = "\u{f130}" + case microphoneAlt = "\u{f3c9}" + case microphoneAltSlash = "\u{f539}" + case microphoneSlash = "\u{f131}" + case microscope = "\u{f610}" + case microsoft = "\u{f3ca}" + case minus = "\u{f068}" + case minusCircle = "\u{f056}" + case minusSquare = "\u{f146}" + case mix = "\u{f3cb}" + case mixcloud = "\u{f289}" + case mizuni = "\u{f3cc}" + case mobile = "\u{f10b}" + case mobileAlt = "\u{f3cd}" + case modx = "\u{f285}" + case monero = "\u{f3d0}" + case moneyBill = "\u{f0d6}" + case moneyBillAlt = "\u{f3d1}" + case moneyBillWave = "\u{f53a}" + case moneyBillWaveAlt = "\u{f53b}" + case moneyCheck = "\u{f53c}" + case moneyCheckAlt = "\u{f53d}" + case monument = "\u{f5a6}" + case moon = "\u{f186}" + case mortarPestle = "\u{f5a7}" + case mosque = "\u{f678}" + case motorcycle = "\u{f21c}" + case mousePointer = "\u{f245}" + case music = "\u{f001}" + case napster = "\u{f3d2}" + case neos = "\u{f612}" + case neuter = "\u{f22c}" + case newspaper = "\u{f1ea}" + case nimblr = "\u{f5a8}" + case nintendoSwitch = "\u{f418}" + case node = "\u{f419}" + case nodeJs = "\u{f3d3}" + case notEqual = "\u{f53e}" + case notesMedical = "\u{f481}" + case npm = "\u{f3d4}" + case ns8 = "\u{f3d5}" + case nutritionix = "\u{f3d6}" + case objectGroup = "\u{f247}" + case objectUngroup = "\u{f248}" + case odnoklassniki = "\u{f263}" + case odnoklassnikiSquare = "\u{f264}" + case oilCan = "\u{f613}" + case oldRepublic = "\u{f510}" + case om = "\u{f679}" + case opencart = "\u{f23d}" + case openid = "\u{f19b}" + case opera = "\u{f26a}" + case optinMonster = "\u{f23c}" + case osi = "\u{f41a}" + case outdent = "\u{f03b}" + case page4 = "\u{f3d7}" + case pagelines = "\u{f18c}" + case paintBrush = "\u{f1fc}" + case paintRoller = "\u{f5aa}" + case palette = "\u{f53f}" + case palfed = "\u{f3d8}" + case pallet = "\u{f482}" + case paperPlane = "\u{f1d8}" + case paperclip = "\u{f0c6}" + case parachuteBox = "\u{f4cd}" + case paragraph = "\u{f1dd}" + case parking = "\u{f540}" + case passport = "\u{f5ab}" + case pastafarianism = "\u{f67b}" + case paste = "\u{f0ea}" + case patreon = "\u{f3d9}" + case pause = "\u{f04c}" + case pauseCircle = "\u{f28b}" + case paw = "\u{f1b0}" + case paypal = "\u{f1ed}" + case peace = "\u{f67c}" + case pen = "\u{f304}" + case penAlt = "\u{f305}" + case penFancy = "\u{f5ac}" + case penNib = "\u{f5ad}" + case penSquare = "\u{f14b}" + case pencilAlt = "\u{f303}" + case pencilRuler = "\u{f5ae}" + case peopleCarry = "\u{f4ce}" + case percent = "\u{f295}" + case percentage = "\u{f541}" + case periscope = "\u{f3da}" + case phabricator = "\u{f3db}" + case phoenixFramework = "\u{f3dc}" + case phoenixSquadron = "\u{f511}" + case phone = "\u{f095}" + case phoneSlash = "\u{f3dd}" + case phoneSquare = "\u{f098}" + case phoneVolume = "\u{f2a0}" + case php = "\u{f457}" + case piedPiper = "\u{f2ae}" + case piedPiperAlt = "\u{f1a8}" + case piedPiperHat = "\u{f4e5}" + case piedPiperPp = "\u{f1a7}" + case piggyBank = "\u{f4d3}" + case pills = "\u{f484}" + case pinterest = "\u{f0d2}" + case pinterestP = "\u{f231}" + case pinterestSquare = "\u{f0d3}" + case placeOfWorship = "\u{f67f}" + case plane = "\u{f072}" + case planeArrival = "\u{f5af}" + case planeDeparture = "\u{f5b0}" + case play = "\u{f04b}" + case playCircle = "\u{f144}" + case playstation = "\u{f3df}" + case plug = "\u{f1e6}" + case plus = "\u{f067}" + case plusCircle = "\u{f055}" + case plusSquare = "\u{f0fe}" + case podcast = "\u{f2ce}" + case poll = "\u{f681}" + case pollH = "\u{f682}" + case poo = "\u{f2fe}" + case poop = "\u{f619}" + case portrait = "\u{f3e0}" + case poundSign = "\u{f154}" + case powerOff = "\u{f011}" + case pray = "\u{f683}" + case prayingHands = "\u{f684}" + case prescription = "\u{f5b1}" + case prescriptionBottle = "\u{f485}" + case prescriptionBottleAlt = "\u{f486}" + case print = "\u{f02f}" + case procedures = "\u{f487}" + case productHunt = "\u{f288}" + case projectDiagram = "\u{f542}" + case pushed = "\u{f3e1}" + case puzzlePiece = "\u{f12e}" + case python = "\u{f3e2}" + case qq = "\u{f1d6}" + case qrcode = "\u{f029}" + case question = "\u{f128}" + case questionCircle = "\u{f059}" + case quidditch = "\u{f458}" + case quinscape = "\u{f459}" + case quora = "\u{f2c4}" + case quoteLeft = "\u{f10d}" + case quoteRight = "\u{f10e}" + case quran = "\u{f687}" + case rProject = "\u{f4f7}" + case random = "\u{f074}" + case ravelry = "\u{f2d9}" + case react = "\u{f41b}" + case readme = "\u{f4d5}" + case rebel = "\u{f1d0}" + case receipt = "\u{f543}" + case recycle = "\u{f1b8}" + case redRiver = "\u{f3e3}" + case reddit = "\u{f1a1}" + case redditAlien = "\u{f281}" + case redditSquare = "\u{f1a2}" + case redo = "\u{f01e}" + case redoAlt = "\u{f2f9}" + case registered = "\u{f25d}" + case rendact = "\u{f3e4}" + case renren = "\u{f18b}" + case reply = "\u{f3e5}" + case replyAll = "\u{f122}" + case replyd = "\u{f3e6}" + case researchgate = "\u{f4f8}" + case resolving = "\u{f3e7}" + case retweet = "\u{f079}" + case rev = "\u{f5b2}" + case ribbon = "\u{f4d6}" + case road = "\u{f018}" + case robot = "\u{f544}" + case rocket = "\u{f135}" + case rocketchat = "\u{f3e8}" + case rockrms = "\u{f3e9}" + case route = "\u{f4d7}" + case rss = "\u{f09e}" + case rssSquare = "\u{f143}" + case rubleSign = "\u{f158}" + case ruler = "\u{f545}" + case rulerCombined = "\u{f546}" + case rulerHorizontal = "\u{f547}" + case rulerVertical = "\u{f548}" + case rupeeSign = "\u{f156}" + case sadCry = "\u{f5b3}" + case sadTear = "\u{f5b4}" + case safari = "\u{f267}" + case sass = "\u{f41e}" + case save = "\u{f0c7}" + case schlix = "\u{f3ea}" + case school = "\u{f549}" + case screwdriver = "\u{f54a}" + case scribd = "\u{f28a}" + case search = "\u{f002}" + case searchDollar = "\u{f688}" + case searchLocation = "\u{f689}" + case searchMinus = "\u{f010}" + case searchPlus = "\u{f00e}" + case searchengin = "\u{f3eb}" + case seedling = "\u{f4d8}" + case sellcast = "\u{f2da}" + case sellsy = "\u{f213}" + case server = "\u{f233}" + case servicestack = "\u{f3ec}" + case shapes = "\u{f61f}" + case share = "\u{f064}" + case shareAlt = "\u{f1e0}" + case shareAltSquare = "\u{f1e1}" + case shareSquare = "\u{f14d}" + case shekelSign = "\u{f20b}" + case shieldAlt = "\u{f3ed}" + case ship = "\u{f21a}" + case shippingFast = "\u{f48b}" + case shirtsinbulk = "\u{f214}" + case shoePrints = "\u{f54b}" + case shoppingBag = "\u{f290}" + case shoppingBasket = "\u{f291}" + case shoppingCart = "\u{f07a}" + case shopware = "\u{f5b5}" + case shower = "\u{f2cc}" + case shuttleVan = "\u{f5b6}" + case sign = "\u{f4d9}" + case signInAlt = "\u{f2f6}" + case signLanguage = "\u{f2a7}" + case signOutAlt = "\u{f2f5}" + case signal = "\u{f012}" + case signature = "\u{f5b7}" + case simplybuilt = "\u{f215}" + case sistrix = "\u{f3ee}" + case sitemap = "\u{f0e8}" + case sith = "\u{f512}" + case skull = "\u{f54c}" + case skyatlas = "\u{f216}" + case skype = "\u{f17e}" + case slack = "\u{f198}" + case slackHash = "\u{f3ef}" + case slidersH = "\u{f1de}" + case slideshare = "\u{f1e7}" + case smile = "\u{f118}" + case smileBeam = "\u{f5b8}" + case smileWink = "\u{f4da}" + case smoking = "\u{f48d}" + case smokingBan = "\u{f54d}" + case snapchat = "\u{f2ab}" + case snapchatGhost = "\u{f2ac}" + case snapchatSquare = "\u{f2ad}" + case snowflake = "\u{f2dc}" + case socks = "\u{f696}" + case solarPanel = "\u{f5ba}" + case sort = "\u{f0dc}" + case sortAlphaDown = "\u{f15d}" + case sortAlphaUp = "\u{f15e}" + case sortAmountDown = "\u{f160}" + case sortAmountUp = "\u{f161}" + case sortDown = "\u{f0dd}" + case sortNumericDown = "\u{f162}" + case sortNumericUp = "\u{f163}" + case sortUp = "\u{f0de}" + case soundcloud = "\u{f1be}" + case spa = "\u{f5bb}" + case spaceShuttle = "\u{f197}" + case speakap = "\u{f3f3}" + case spinner = "\u{f110}" + case splotch = "\u{f5bc}" + case spotify = "\u{f1bc}" + case sprayCan = "\u{f5bd}" + case square = "\u{f0c8}" + case squareFull = "\u{f45c}" + case squareRootAlt = "\u{f698}" + case squarespace = "\u{f5be}" + case stackExchange = "\u{f18d}" + case stackOverflow = "\u{f16c}" + case stamp = "\u{f5bf}" + case star = "\u{f005}" + case starAndCrescent = "\u{f699}" + case starHalf = "\u{f089}" + case starHalfAlt = "\u{f5c0}" + case starOfDavid = "\u{f69a}" + case starOfLife = "\u{f621}" + case staylinked = "\u{f3f5}" + case steam = "\u{f1b6}" + case steamSquare = "\u{f1b7}" + case steamSymbol = "\u{f3f6}" + case stepBackward = "\u{f048}" + case stepForward = "\u{f051}" + case stethoscope = "\u{f0f1}" + case stickerMule = "\u{f3f7}" + case stickyNote = "\u{f249}" + case stop = "\u{f04d}" + case stopCircle = "\u{f28d}" + case stopwatch = "\u{f2f2}" + case store = "\u{f54e}" + case storeAlt = "\u{f54f}" + case strava = "\u{f428}" + case stream = "\u{f550}" + case streetView = "\u{f21d}" + case strikethrough = "\u{f0cc}" + case stripe = "\u{f429}" + case stripeS = "\u{f42a}" + case stroopwafel = "\u{f551}" + case studiovinari = "\u{f3f8}" + case stumbleupon = "\u{f1a4}" + case stumbleuponCircle = "\u{f1a3}" + case `subscript` = "\u{f12c}" + case subway = "\u{f239}" + case suitcase = "\u{f0f2}" + case suitcaseRolling = "\u{f5c1}" + case sun = "\u{f185}" + case superpowers = "\u{f2dd}" + case superscript = "\u{f12b}" + case supple = "\u{f3f9}" + case surprise = "\u{f5c2}" + case swatchbook = "\u{f5c3}" + case swimmer = "\u{f5c4}" + case swimmingPool = "\u{f5c5}" + case synagogue = "\u{f69b}" + case sync = "\u{f021}" + case syncAlt = "\u{f2f1}" + case syringe = "\u{f48e}" + case table = "\u{f0ce}" + case tableTennis = "\u{f45d}" + case tablet = "\u{f10a}" + case tabletAlt = "\u{f3fa}" + case tablets = "\u{f490}" + case tachometerAlt = "\u{f3fd}" + case tag = "\u{f02b}" + case tags = "\u{f02c}" + case tape = "\u{f4db}" + case tasks = "\u{f0ae}" + case taxi = "\u{f1ba}" + case teamspeak = "\u{f4f9}" + case teeth = "\u{f62e}" + case teethOpen = "\u{f62f}" + case telegram = "\u{f2c6}" + case telegramPlane = "\u{f3fe}" + case tencentWeibo = "\u{f1d5}" + case terminal = "\u{f120}" + case textHeight = "\u{f034}" + case textWidth = "\u{f035}" + case th = "\u{f00a}" + case thLarge = "\u{f009}" + case thList = "\u{f00b}" + case theRedYeti = "\u{f69d}" + case theaterMasks = "\u{f630}" + case themeco = "\u{f5c6}" + case themeisle = "\u{f2b2}" + case thermometer = "\u{f491}" + case thermometerEmpty = "\u{f2cb}" + case thermometerFull = "\u{f2c7}" + case thermometerHalf = "\u{f2c9}" + case thermometerQuarter = "\u{f2ca}" + case thermometerThreeQuarters = "\u{f2c8}" + case thumbsDown = "\u{f165}" + case thumbsUp = "\u{f164}" + case thumbtack = "\u{f08d}" + case ticketAlt = "\u{f3ff}" + case times = "\u{f00d}" + case timesCircle = "\u{f057}" + case tint = "\u{f043}" + case tintSlash = "\u{f5c7}" + case tired = "\u{f5c8}" + case toggleOff = "\u{f204}" + case toggleOn = "\u{f205}" + case toolbox = "\u{f552}" + case tooth = "\u{f5c9}" + case torah = "\u{f6a0}" + case toriiGate = "\u{f6a1}" + case tradeFederation = "\u{f513}" + case trademark = "\u{f25c}" + case trafficLight = "\u{f637}" + case train = "\u{f238}" + case transgender = "\u{f224}" + case transgenderAlt = "\u{f225}" + case trash = "\u{f1f8}" + case trashAlt = "\u{f2ed}" + case tree = "\u{f1bb}" + case trello = "\u{f181}" + case tripadvisor = "\u{f262}" + case trophy = "\u{f091}" + case truck = "\u{f0d1}" + case truckLoading = "\u{f4de}" + case truckMonster = "\u{f63b}" + case truckMoving = "\u{f4df}" + case truckPickup = "\u{f63c}" + case tshirt = "\u{f553}" + case tty = "\u{f1e4}" + case tumblr = "\u{f173}" + case tumblrSquare = "\u{f174}" + case tv = "\u{f26c}" + case twitch = "\u{f1e8}" + case twitter = "\u{f099}" + case twitterSquare = "\u{f081}" + case typo3 = "\u{f42b}" + case uber = "\u{f402}" + case uikit = "\u{f403}" + case umbrella = "\u{f0e9}" + case umbrellaBeach = "\u{f5ca}" + case underline = "\u{f0cd}" + case undo = "\u{f0e2}" + case undoAlt = "\u{f2ea}" + case uniregistry = "\u{f404}" + case universalAccess = "\u{f29a}" + case university = "\u{f19c}" + case unlink = "\u{f127}" + case unlock = "\u{f09c}" + case unlockAlt = "\u{f13e}" + case untappd = "\u{f405}" + case upload = "\u{f093}" + case usb = "\u{f287}" + case user = "\u{f007}" + case userAlt = "\u{f406}" + case userAltSlash = "\u{f4fa}" + case userAstronaut = "\u{f4fb}" + case userCheck = "\u{f4fc}" + case userCircle = "\u{f2bd}" + case userClock = "\u{f4fd}" + case userCog = "\u{f4fe}" + case userEdit = "\u{f4ff}" + case userFriends = "\u{f500}" + case userGraduate = "\u{f501}" + case userLock = "\u{f502}" + case userMd = "\u{f0f0}" + case userMinus = "\u{f503}" + case userNinja = "\u{f504}" + case userPlus = "\u{f234}" + case userSecret = "\u{f21b}" + case userShield = "\u{f505}" + case userSlash = "\u{f506}" + case userTag = "\u{f507}" + case userTie = "\u{f508}" + case userTimes = "\u{f235}" + case users = "\u{f0c0}" + case usersCog = "\u{f509}" + case ussunnah = "\u{f407}" + case utensilSpoon = "\u{f2e5}" + case utensils = "\u{f2e7}" + case vaadin = "\u{f408}" + case vectorSquare = "\u{f5cb}" + case venus = "\u{f221}" + case venusDouble = "\u{f226}" + case venusMars = "\u{f228}" + case viacoin = "\u{f237}" + case viadeo = "\u{f2a9}" + case viadeoSquare = "\u{f2aa}" + case vial = "\u{f492}" + case vials = "\u{f493}" + case viber = "\u{f409}" + case video = "\u{f03d}" + case videoSlash = "\u{f4e2}" + case vihara = "\u{f6a7}" + case vimeo = "\u{f40a}" + case vimeoSquare = "\u{f194}" + case vimeoV = "\u{f27d}" + case vine = "\u{f1ca}" + case vk = "\u{f189}" + case vnv = "\u{f40b}" + case volleyballBall = "\u{f45f}" + case volumeDown = "\u{f027}" + case volumeOff = "\u{f026}" + case volumeUp = "\u{f028}" + case vuejs = "\u{f41f}" + case walking = "\u{f554}" + case wallet = "\u{f555}" + case warehouse = "\u{f494}" + case weebly = "\u{f5cc}" + case weibo = "\u{f18a}" + case weight = "\u{f496}" + case weightHanging = "\u{f5cd}" + case weixin = "\u{f1d7}" + case whatsapp = "\u{f232}" + case whatsappSquare = "\u{f40c}" + case wheelchair = "\u{f193}" + case whmcs = "\u{f40d}" + case wifi = "\u{f1eb}" + case wikipediaW = "\u{f266}" + case windowClose = "\u{f410}" + case windowMaximize = "\u{f2d0}" + case windowMinimize = "\u{f2d1}" + case windowRestore = "\u{f2d2}" + case windows = "\u{f17a}" + case wineGlass = "\u{f4e3}" + case wineGlassAlt = "\u{f5ce}" + case wix = "\u{f5cf}" + case wolfPackBattalion = "\u{f514}" + case wonSign = "\u{f159}" + case wordpress = "\u{f19a}" + case wordpressSimple = "\u{f411}" + case wpbeginner = "\u{f297}" + case wpexplorer = "\u{f2de}" + case wpforms = "\u{f298}" + case wrench = "\u{f0ad}" + case xRay = "\u{f497}" + case xbox = "\u{f412}" + case xing = "\u{f168}" + case xingSquare = "\u{f169}" + case yCombinator = "\u{f23b}" + case yahoo = "\u{f19e}" + case yandex = "\u{f413}" + case yandexInternational = "\u{f414}" + case yelp = "\u{f1e9}" + case yenSign = "\u{f157}" + case yinYang = "\u{f6ad}" + case yoast = "\u{f2b1}" + case youtube = "\u{f167}" + case youtubeSquare = "\u{f431}" + case zhihu = "\u{f63f}" +} + +/// An array of FontAwesome icon codes. +// swiftlint:disable identifier_name +public let FontAwesomeIcons: [String: String] = [ + "fa-500px": "\u{f26e}", + "fa-accessible-icon": "\u{f368}", + "fa-accusoft": "\u{f369}", + "fa-ad": "\u{f641}", + "fa-address-book": "\u{f2b9}", + "fa-address-card": "\u{f2bb}", + "fa-adjust": "\u{f042}", + "fa-adn": "\u{f170}", + "fa-adversal": "\u{f36a}", + "fa-affiliatetheme": "\u{f36b}", + "fa-air-freshener": "\u{f5d0}", + "fa-algolia": "\u{f36c}", + "fa-align-center": "\u{f037}", + "fa-align-justify": "\u{f039}", + "fa-align-left": "\u{f036}", + "fa-align-right": "\u{f038}", + "fa-alipay": "\u{f642}", + "fa-allergies": "\u{f461}", + "fa-amazon": "\u{f270}", + "fa-amazon-pay": "\u{f42c}", + "fa-ambulance": "\u{f0f9}", + "fa-american-sign-language-interpreting": "\u{f2a3}", + "fa-amilia": "\u{f36d}", + "fa-anchor": "\u{f13d}", + "fa-android": "\u{f17b}", + "fa-angellist": "\u{f209}", + "fa-angle-double-down": "\u{f103}", + "fa-angle-double-left": "\u{f100}", + "fa-angle-double-right": "\u{f101}", + "fa-angle-double-up": "\u{f102}", + "fa-angle-down": "\u{f107}", + "fa-angle-left": "\u{f104}", + "fa-angle-right": "\u{f105}", + "fa-angle-up": "\u{f106}", + "fa-angry": "\u{f556}", + "fa-angrycreative": "\u{f36e}", + "fa-angular": "\u{f420}", + "fa-ankh": "\u{f644}", + "fa-app-store": "\u{f36f}", + "fa-app-store-ios": "\u{f370}", + "fa-apper": "\u{f371}", + "fa-apple": "\u{f179}", + "fa-apple-alt": "\u{f5d1}", + "fa-apple-pay": "\u{f415}", + "fa-archive": "\u{f187}", + "fa-archway": "\u{f557}", + "fa-arrow-alt-circle-down": "\u{f358}", + "fa-arrow-alt-circle-left": "\u{f359}", + "fa-arrow-alt-circle-right": "\u{f35a}", + "fa-arrow-alt-circle-up": "\u{f35b}", + "fa-arrow-circle-down": "\u{f0ab}", + "fa-arrow-circle-left": "\u{f0a8}", + "fa-arrow-circle-right": "\u{f0a9}", + "fa-arrow-circle-up": "\u{f0aa}", + "fa-arrow-down": "\u{f063}", + "fa-arrow-left": "\u{f060}", + "fa-arrow-right": "\u{f061}", + "fa-arrow-up": "\u{f062}", + "fa-arrows-alt": "\u{f0b2}", + "fa-arrows-alt-h": "\u{f337}", + "fa-arrows-alt-v": "\u{f338}", + "fa-assistive-listening-systems": "\u{f2a2}", + "fa-asterisk": "\u{f069}", + "fa-asymmetrik": "\u{f372}", + "fa-at": "\u{f1fa}", + "fa-atlas": "\u{f558}", + "fa-atom": "\u{f5d2}", + "fa-audible": "\u{f373}", + "fa-audio-description": "\u{f29e}", + "fa-autoprefixer": "\u{f41c}", + "fa-avianex": "\u{f374}", + "fa-aviato": "\u{f421}", + "fa-award": "\u{f559}", + "fa-aws": "\u{f375}", + "fa-backspace": "\u{f55a}", + "fa-backward": "\u{f04a}", + "fa-balance-scale": "\u{f24e}", + "fa-ban": "\u{f05e}", + "fa-band-aid": "\u{f462}", + "fa-bandcamp": "\u{f2d5}", + "fa-barcode": "\u{f02a}", + "fa-bars": "\u{f0c9}", + "fa-baseball-ball": "\u{f433}", + "fa-basketball-ball": "\u{f434}", + "fa-bath": "\u{f2cd}", + "fa-battery-empty": "\u{f244}", + "fa-battery-full": "\u{f240}", + "fa-battery-half": "\u{f242}", + "fa-battery-quarter": "\u{f243}", + "fa-battery-three-quarters": "\u{f241}", + "fa-bed": "\u{f236}", + "fa-beer": "\u{f0fc}", + "fa-behance": "\u{f1b4}", + "fa-behance-square": "\u{f1b5}", + "fa-bell": "\u{f0f3}", + "fa-bell-slash": "\u{f1f6}", + "fa-bezier-curve": "\u{f55b}", + "fa-bible": "\u{f647}", + "fa-bicycle": "\u{f206}", + "fa-bimobject": "\u{f378}", + "fa-binoculars": "\u{f1e5}", + "fa-birthday-cake": "\u{f1fd}", + "fa-bitbucket": "\u{f171}", + "fa-bitcoin": "\u{f379}", + "fa-bity": "\u{f37a}", + "fa-black-tie": "\u{f27e}", + "fa-blackberry": "\u{f37b}", + "fa-blender": "\u{f517}", + "fa-blind": "\u{f29d}", + "fa-blogger": "\u{f37c}", + "fa-blogger-b": "\u{f37d}", + "fa-bluetooth": "\u{f293}", + "fa-bluetooth-b": "\u{f294}", + "fa-bold": "\u{f032}", + "fa-bolt": "\u{f0e7}", + "fa-bomb": "\u{f1e2}", + "fa-bone": "\u{f5d7}", + "fa-bong": "\u{f55c}", + "fa-book": "\u{f02d}", + "fa-book-open": "\u{f518}", + "fa-book-reader": "\u{f5da}", + "fa-bookmark": "\u{f02e}", + "fa-bowling-ball": "\u{f436}", + "fa-box": "\u{f466}", + "fa-box-open": "\u{f49e}", + "fa-boxes": "\u{f468}", + "fa-braille": "\u{f2a1}", + "fa-brain": "\u{f5dc}", + "fa-briefcase": "\u{f0b1}", + "fa-briefcase-medical": "\u{f469}", + "fa-broadcast-tower": "\u{f519}", + "fa-broom": "\u{f51a}", + "fa-brush": "\u{f55d}", + "fa-btc": "\u{f15a}", + "fa-bug": "\u{f188}", + "fa-building": "\u{f1ad}", + "fa-bullhorn": "\u{f0a1}", + "fa-bullseye": "\u{f140}", + "fa-burn": "\u{f46a}", + "fa-buromobelexperte": "\u{f37f}", + "fa-bus": "\u{f207}", + "fa-bus-alt": "\u{f55e}", + "fa-business-time": "\u{f64a}", + "fa-buysellads": "\u{f20d}", + "fa-calculator": "\u{f1ec}", + "fa-calendar": "\u{f133}", + "fa-calendar-alt": "\u{f073}", + "fa-calendar-check": "\u{f274}", + "fa-calendar-minus": "\u{f272}", + "fa-calendar-plus": "\u{f271}", + "fa-calendar-times": "\u{f273}", + "fa-camera": "\u{f030}", + "fa-camera-retro": "\u{f083}", + "fa-cannabis": "\u{f55f}", + "fa-capsules": "\u{f46b}", + "fa-car": "\u{f1b9}", + "fa-car-alt": "\u{f5de}", + "fa-car-battery": "\u{f5df}", + "fa-car-crash": "\u{f5e1}", + "fa-car-side": "\u{f5e4}", + "fa-caret-down": "\u{f0d7}", + "fa-caret-left": "\u{f0d9}", + "fa-caret-right": "\u{f0da}", + "fa-caret-square-down": "\u{f150}", + "fa-caret-square-left": "\u{f191}", + "fa-caret-square-right": "\u{f152}", + "fa-caret-square-up": "\u{f151}", + "fa-caret-up": "\u{f0d8}", + "fa-cart-arrow-down": "\u{f218}", + "fa-cart-plus": "\u{f217}", + "fa-cc-amazon-pay": "\u{f42d}", + "fa-cc-amex": "\u{f1f3}", + "fa-cc-apple-pay": "\u{f416}", + "fa-cc-diners-club": "\u{f24c}", + "fa-cc-discover": "\u{f1f2}", + "fa-cc-jcb": "\u{f24b}", + "fa-cc-mastercard": "\u{f1f1}", + "fa-cc-paypal": "\u{f1f4}", + "fa-cc-stripe": "\u{f1f5}", + "fa-cc-visa": "\u{f1f0}", + "fa-centercode": "\u{f380}", + "fa-certificate": "\u{f0a3}", + "fa-chalkboard": "\u{f51b}", + "fa-chalkboard-teacher": "\u{f51c}", + "fa-charging-station": "\u{f5e7}", + "fa-chart-area": "\u{f1fe}", + "fa-chart-bar": "\u{f080}", + "fa-chart-line": "\u{f201}", + "fa-chart-pie": "\u{f200}", + "fa-check": "\u{f00c}", + "fa-check-circle": "\u{f058}", + "fa-check-double": "\u{f560}", + "fa-check-square": "\u{f14a}", + "fa-chess": "\u{f439}", + "fa-chess-bishop": "\u{f43a}", + "fa-chess-board": "\u{f43c}", + "fa-chess-king": "\u{f43f}", + "fa-chess-knight": "\u{f441}", + "fa-chess-pawn": "\u{f443}", + "fa-chess-queen": "\u{f445}", + "fa-chess-rook": "\u{f447}", + "fa-chevron-circle-down": "\u{f13a}", + "fa-chevron-circle-left": "\u{f137}", + "fa-chevron-circle-right": "\u{f138}", + "fa-chevron-circle-up": "\u{f139}", + "fa-chevron-down": "\u{f078}", + "fa-chevron-left": "\u{f053}", + "fa-chevron-right": "\u{f054}", + "fa-chevron-up": "\u{f077}", + "fa-child": "\u{f1ae}", + "fa-chrome": "\u{f268}", + "fa-church": "\u{f51d}", + "fa-circle": "\u{f111}", + "fa-circle-notch": "\u{f1ce}", + "fa-city": "\u{f64f}", + "fa-clipboard": "\u{f328}", + "fa-clipboard-check": "\u{f46c}", + "fa-clipboard-list": "\u{f46d}", + "fa-clock": "\u{f017}", + "fa-clone": "\u{f24d}", + "fa-closed-captioning": "\u{f20a}", + "fa-cloud": "\u{f0c2}", + "fa-cloud-download-alt": "\u{f381}", + "fa-cloud-upload-alt": "\u{f382}", + "fa-cloudscale": "\u{f383}", + "fa-cloudsmith": "\u{f384}", + "fa-cloudversify": "\u{f385}", + "fa-cocktail": "\u{f561}", + "fa-code": "\u{f121}", + "fa-code-branch": "\u{f126}", + "fa-codepen": "\u{f1cb}", + "fa-codiepie": "\u{f284}", + "fa-coffee": "\u{f0f4}", + "fa-cog": "\u{f013}", + "fa-cogs": "\u{f085}", + "fa-coins": "\u{f51e}", + "fa-columns": "\u{f0db}", + "fa-comment": "\u{f075}", + "fa-comment-alt": "\u{f27a}", + "fa-comment-dollar": "\u{f651}", + "fa-comment-dots": "\u{f4ad}", + "fa-comment-slash": "\u{f4b3}", + "fa-comments": "\u{f086}", + "fa-comments-dollar": "\u{f653}", + "fa-compact-disc": "\u{f51f}", + "fa-compass": "\u{f14e}", + "fa-compress": "\u{f066}", + "fa-concierge-bell": "\u{f562}", + "fa-connectdevelop": "\u{f20e}", + "fa-contao": "\u{f26d}", + "fa-cookie": "\u{f563}", + "fa-cookie-bite": "\u{f564}", + "fa-copy": "\u{f0c5}", + "fa-copyright": "\u{f1f9}", + "fa-couch": "\u{f4b8}", + "fa-cpanel": "\u{f388}", + "fa-creative-commons": "\u{f25e}", + "fa-creative-commons-by": "\u{f4e7}", + "fa-creative-commons-nc": "\u{f4e8}", + "fa-creative-commons-nc-eu": "\u{f4e9}", + "fa-creative-commons-nc-jp": "\u{f4ea}", + "fa-creative-commons-nd": "\u{f4eb}", + "fa-creative-commons-pd": "\u{f4ec}", + "fa-creative-commons-pd-alt": "\u{f4ed}", + "fa-creative-commons-remix": "\u{f4ee}", + "fa-creative-commons-sa": "\u{f4ef}", + "fa-creative-commons-sampling": "\u{f4f0}", + "fa-creative-commons-sampling-plus": "\u{f4f1}", + "fa-creative-commons-share": "\u{f4f2}", + "fa-credit-card": "\u{f09d}", + "fa-crop": "\u{f125}", + "fa-crop-alt": "\u{f565}", + "fa-cross": "\u{f654}", + "fa-crosshairs": "\u{f05b}", + "fa-crow": "\u{f520}", + "fa-crown": "\u{f521}", + "fa-css3": "\u{f13c}", + "fa-css3-alt": "\u{f38b}", + "fa-cube": "\u{f1b2}", + "fa-cubes": "\u{f1b3}", + "fa-cut": "\u{f0c4}", + "fa-cuttlefish": "\u{f38c}", + "fa-d-and-d": "\u{f38d}", + "fa-dashcube": "\u{f210}", + "fa-database": "\u{f1c0}", + "fa-deaf": "\u{f2a4}", + "fa-delicious": "\u{f1a5}", + "fa-deploydog": "\u{f38e}", + "fa-deskpro": "\u{f38f}", + "fa-desktop": "\u{f108}", + "fa-deviantart": "\u{f1bd}", + "fa-dharmachakra": "\u{f655}", + "fa-diagnoses": "\u{f470}", + "fa-dice": "\u{f522}", + "fa-dice-five": "\u{f523}", + "fa-dice-four": "\u{f524}", + "fa-dice-one": "\u{f525}", + "fa-dice-six": "\u{f526}", + "fa-dice-three": "\u{f527}", + "fa-dice-two": "\u{f528}", + "fa-digg": "\u{f1a6}", + "fa-digital-ocean": "\u{f391}", + "fa-digital-tachograph": "\u{f566}", + "fa-directions": "\u{f5eb}", + "fa-discord": "\u{f392}", + "fa-discourse": "\u{f393}", + "fa-divide": "\u{f529}", + "fa-dizzy": "\u{f567}", + "fa-dna": "\u{f471}", + "fa-dochub": "\u{f394}", + "fa-docker": "\u{f395}", + "fa-dollar-sign": "\u{f155}", + "fa-dolly": "\u{f472}", + "fa-dolly-flatbed": "\u{f474}", + "fa-donate": "\u{f4b9}", + "fa-door-closed": "\u{f52a}", + "fa-door-open": "\u{f52b}", + "fa-dot-circle": "\u{f192}", + "fa-dove": "\u{f4ba}", + "fa-download": "\u{f019}", + "fa-draft2digital": "\u{f396}", + "fa-drafting-compass": "\u{f568}", + "fa-draw-polygon": "\u{f5ee}", + "fa-dribbble": "\u{f17d}", + "fa-dribbble-square": "\u{f397}", + "fa-dropbox": "\u{f16b}", + "fa-drum": "\u{f569}", + "fa-drum-steelpan": "\u{f56a}", + "fa-drupal": "\u{f1a9}", + "fa-dumbbell": "\u{f44b}", + "fa-dyalog": "\u{f399}", + "fa-earlybirds": "\u{f39a}", + "fa-ebay": "\u{f4f4}", + "fa-edge": "\u{f282}", + "fa-edit": "\u{f044}", + "fa-eject": "\u{f052}", + "fa-elementor": "\u{f430}", + "fa-ellipsis-h": "\u{f141}", + "fa-ellipsis-v": "\u{f142}", + "fa-ello": "\u{f5f1}", + "fa-ember": "\u{f423}", + "fa-empire": "\u{f1d1}", + "fa-envelope": "\u{f0e0}", + "fa-envelope-open": "\u{f2b6}", + "fa-envelope-open-text": "\u{f658}", + "fa-envelope-square": "\u{f199}", + "fa-envira": "\u{f299}", + "fa-equals": "\u{f52c}", + "fa-eraser": "\u{f12d}", + "fa-erlang": "\u{f39d}", + "fa-ethereum": "\u{f42e}", + "fa-etsy": "\u{f2d7}", + "fa-euro-sign": "\u{f153}", + "fa-exchange-alt": "\u{f362}", + "fa-exclamation": "\u{f12a}", + "fa-exclamation-circle": "\u{f06a}", + "fa-exclamation-triangle": "\u{f071}", + "fa-expand": "\u{f065}", + "fa-expand-arrows-alt": "\u{f31e}", + "fa-expeditedssl": "\u{f23e}", + "fa-external-link-alt": "\u{f35d}", + "fa-external-link-square-alt": "\u{f360}", + "fa-eye": "\u{f06e}", + "fa-eye-dropper": "\u{f1fb}", + "fa-eye-slash": "\u{f070}", + "fa-facebook": "\u{f09a}", + "fa-facebook-f": "\u{f39e}", + "fa-facebook-messenger": "\u{f39f}", + "fa-facebook-square": "\u{f082}", + "fa-fast-backward": "\u{f049}", + "fa-fast-forward": "\u{f050}", + "fa-fax": "\u{f1ac}", + "fa-feather": "\u{f52d}", + "fa-feather-alt": "\u{f56b}", + "fa-female": "\u{f182}", + "fa-fighter-jet": "\u{f0fb}", + "fa-file": "\u{f15b}", + "fa-file-alt": "\u{f15c}", + "fa-file-archive": "\u{f1c6}", + "fa-file-audio": "\u{f1c7}", + "fa-file-code": "\u{f1c9}", + "fa-file-contract": "\u{f56c}", + "fa-file-download": "\u{f56d}", + "fa-file-excel": "\u{f1c3}", + "fa-file-export": "\u{f56e}", + "fa-file-image": "\u{f1c5}", + "fa-file-import": "\u{f56f}", + "fa-file-invoice": "\u{f570}", + "fa-file-invoice-dollar": "\u{f571}", + "fa-file-medical": "\u{f477}", + "fa-file-medical-alt": "\u{f478}", + "fa-file-pdf": "\u{f1c1}", + "fa-file-powerpoint": "\u{f1c4}", + "fa-file-prescription": "\u{f572}", + "fa-file-signature": "\u{f573}", + "fa-file-upload": "\u{f574}", + "fa-file-video": "\u{f1c8}", + "fa-file-word": "\u{f1c2}", + "fa-fill": "\u{f575}", + "fa-fill-drip": "\u{f576}", + "fa-film": "\u{f008}", + "fa-filter": "\u{f0b0}", + "fa-fingerprint": "\u{f577}", + "fa-fire": "\u{f06d}", + "fa-fire-extinguisher": "\u{f134}", + "fa-firefox": "\u{f269}", + "fa-first-aid": "\u{f479}", + "fa-first-order": "\u{f2b0}", + "fa-first-order-alt": "\u{f50a}", + "fa-firstdraft": "\u{f3a1}", + "fa-fish": "\u{f578}", + "fa-flag": "\u{f024}", + "fa-flag-checkered": "\u{f11e}", + "fa-flask": "\u{f0c3}", + "fa-flickr": "\u{f16e}", + "fa-flipboard": "\u{f44d}", + "fa-flushed": "\u{f579}", + "fa-fly": "\u{f417}", + "fa-folder": "\u{f07b}", + "fa-folder-minus": "\u{f65d}", + "fa-folder-open": "\u{f07c}", + "fa-folder-plus": "\u{f65e}", + "fa-font": "\u{f031}", + "fa-font-awesome": "\u{f2b4}", + "fa-font-awesome-alt": "\u{f35c}", + "fa-font-awesome-flag": "\u{f425}", + "fa-font-awesome-logo-full": "\u{f4e6}", + "fa-fonticons": "\u{f280}", + "fa-fonticons-fi": "\u{f3a2}", + "fa-football-ball": "\u{f44e}", + "fa-fort-awesome": "\u{f286}", + "fa-fort-awesome-alt": "\u{f3a3}", + "fa-forumbee": "\u{f211}", + "fa-forward": "\u{f04e}", + "fa-foursquare": "\u{f180}", + "fa-free-code-camp": "\u{f2c5}", + "fa-freebsd": "\u{f3a4}", + "fa-frog": "\u{f52e}", + "fa-frown": "\u{f119}", + "fa-frown-open": "\u{f57a}", + "fa-fulcrum": "\u{f50b}", + "fa-funnel-dollar": "\u{f662}", + "fa-futbol": "\u{f1e3}", + "fa-galactic-republic": "\u{f50c}", + "fa-galactic-senate": "\u{f50d}", + "fa-gamepad": "\u{f11b}", + "fa-gas-pump": "\u{f52f}", + "fa-gavel": "\u{f0e3}", + "fa-gem": "\u{f3a5}", + "fa-genderless": "\u{f22d}", + "fa-get-pocket": "\u{f265}", + "fa-gg": "\u{f260}", + "fa-gg-circle": "\u{f261}", + "fa-gift": "\u{f06b}", + "fa-git": "\u{f1d3}", + "fa-git-square": "\u{f1d2}", + "fa-github": "\u{f09b}", + "fa-github-alt": "\u{f113}", + "fa-github-square": "\u{f092}", + "fa-gitkraken": "\u{f3a6}", + "fa-gitlab": "\u{f296}", + "fa-gitter": "\u{f426}", + "fa-glass-martini": "\u{f000}", + "fa-glass-martini-alt": "\u{f57b}", + "fa-glasses": "\u{f530}", + "fa-glide": "\u{f2a5}", + "fa-glide-g": "\u{f2a6}", + "fa-globe": "\u{f0ac}", + "fa-globe-africa": "\u{f57c}", + "fa-globe-americas": "\u{f57d}", + "fa-globe-asia": "\u{f57e}", + "fa-gofore": "\u{f3a7}", + "fa-golf-ball": "\u{f450}", + "fa-goodreads": "\u{f3a8}", + "fa-goodreads-g": "\u{f3a9}", + "fa-google": "\u{f1a0}", + "fa-google-drive": "\u{f3aa}", + "fa-google-play": "\u{f3ab}", + "fa-google-plus": "\u{f2b3}", + "fa-google-plus-g": "\u{f0d5}", + "fa-google-plus-square": "\u{f0d4}", + "fa-google-wallet": "\u{f1ee}", + "fa-gopuram": "\u{f664}", + "fa-graduation-cap": "\u{f19d}", + "fa-gratipay": "\u{f184}", + "fa-grav": "\u{f2d6}", + "fa-greater-than": "\u{f531}", + "fa-greater-than-equal": "\u{f532}", + "fa-grimace": "\u{f57f}", + "fa-grin": "\u{f580}", + "fa-grin-alt": "\u{f581}", + "fa-grin-beam": "\u{f582}", + "fa-grin-beam-sweat": "\u{f583}", + "fa-grin-hearts": "\u{f584}", + "fa-grin-squint": "\u{f585}", + "fa-grin-squint-tears": "\u{f586}", + "fa-grin-stars": "\u{f587}", + "fa-grin-tears": "\u{f588}", + "fa-grin-tongue": "\u{f589}", + "fa-grin-tongue-squint": "\u{f58a}", + "fa-grin-tongue-wink": "\u{f58b}", + "fa-grin-wink": "\u{f58c}", + "fa-grip-horizontal": "\u{f58d}", + "fa-grip-vertical": "\u{f58e}", + "fa-gripfire": "\u{f3ac}", + "fa-grunt": "\u{f3ad}", + "fa-gulp": "\u{f3ae}", + "fa-h-square": "\u{f0fd}", + "fa-hacker-news": "\u{f1d4}", + "fa-hacker-news-square": "\u{f3af}", + "fa-hackerrank": "\u{f5f7}", + "fa-hamsa": "\u{f665}", + "fa-hand-holding": "\u{f4bd}", + "fa-hand-holding-heart": "\u{f4be}", + "fa-hand-holding-usd": "\u{f4c0}", + "fa-hand-lizard": "\u{f258}", + "fa-hand-paper": "\u{f256}", + "fa-hand-peace": "\u{f25b}", + "fa-hand-point-down": "\u{f0a7}", + "fa-hand-point-left": "\u{f0a5}", + "fa-hand-point-right": "\u{f0a4}", + "fa-hand-point-up": "\u{f0a6}", + "fa-hand-pointer": "\u{f25a}", + "fa-hand-rock": "\u{f255}", + "fa-hand-scissors": "\u{f257}", + "fa-hand-spock": "\u{f259}", + "fa-hands": "\u{f4c2}", + "fa-hands-helping": "\u{f4c4}", + "fa-handshake": "\u{f2b5}", + "fa-hashtag": "\u{f292}", + "fa-haykal": "\u{f666}", + "fa-hdd": "\u{f0a0}", + "fa-heading": "\u{f1dc}", + "fa-headphones": "\u{f025}", + "fa-headphones-alt": "\u{f58f}", + "fa-headset": "\u{f590}", + "fa-heart": "\u{f004}", + "fa-heartbeat": "\u{f21e}", + "fa-helicopter": "\u{f533}", + "fa-highlighter": "\u{f591}", + "fa-hips": "\u{f452}", + "fa-hire-a-helper": "\u{f3b0}", + "fa-history": "\u{f1da}", + "fa-hockey-puck": "\u{f453}", + "fa-home": "\u{f015}", + "fa-hooli": "\u{f427}", + "fa-hornbill": "\u{f592}", + "fa-hospital": "\u{f0f8}", + "fa-hospital-alt": "\u{f47d}", + "fa-hospital-symbol": "\u{f47e}", + "fa-hot-tub": "\u{f593}", + "fa-hotel": "\u{f594}", + "fa-hotjar": "\u{f3b1}", + "fa-hourglass": "\u{f254}", + "fa-hourglass-end": "\u{f253}", + "fa-hourglass-half": "\u{f252}", + "fa-hourglass-start": "\u{f251}", + "fa-houzz": "\u{f27c}", + "fa-html5": "\u{f13b}", + "fa-hubspot": "\u{f3b2}", + "fa-i-cursor": "\u{f246}", + "fa-id-badge": "\u{f2c1}", + "fa-id-card": "\u{f2c2}", + "fa-id-card-alt": "\u{f47f}", + "fa-image": "\u{f03e}", + "fa-images": "\u{f302}", + "fa-imdb": "\u{f2d8}", + "fa-inbox": "\u{f01c}", + "fa-indent": "\u{f03c}", + "fa-industry": "\u{f275}", + "fa-infinity": "\u{f534}", + "fa-info": "\u{f129}", + "fa-info-circle": "\u{f05a}", + "fa-instagram": "\u{f16d}", + "fa-internet-explorer": "\u{f26b}", + "fa-ioxhost": "\u{f208}", + "fa-italic": "\u{f033}", + "fa-itunes": "\u{f3b4}", + "fa-itunes-note": "\u{f3b5}", + "fa-java": "\u{f4e4}", + "fa-jedi": "\u{f669}", + "fa-jedi-order": "\u{f50e}", + "fa-jenkins": "\u{f3b6}", + "fa-joget": "\u{f3b7}", + "fa-joint": "\u{f595}", + "fa-joomla": "\u{f1aa}", + "fa-journal-whills": "\u{f66a}", + "fa-js": "\u{f3b8}", + "fa-js-square": "\u{f3b9}", + "fa-jsfiddle": "\u{f1cc}", + "fa-kaaba": "\u{f66b}", + "fa-kaggle": "\u{f5fa}", + "fa-key": "\u{f084}", + "fa-keybase": "\u{f4f5}", + "fa-keyboard": "\u{f11c}", + "fa-keycdn": "\u{f3ba}", + "fa-khanda": "\u{f66d}", + "fa-kickstarter": "\u{f3bb}", + "fa-kickstarter-k": "\u{f3bc}", + "fa-kiss": "\u{f596}", + "fa-kiss-beam": "\u{f597}", + "fa-kiss-wink-heart": "\u{f598}", + "fa-kiwi-bird": "\u{f535}", + "fa-korvue": "\u{f42f}", + "fa-landmark": "\u{f66f}", + "fa-language": "\u{f1ab}", + "fa-laptop": "\u{f109}", + "fa-laptop-code": "\u{f5fc}", + "fa-laravel": "\u{f3bd}", + "fa-lastfm": "\u{f202}", + "fa-lastfm-square": "\u{f203}", + "fa-laugh": "\u{f599}", + "fa-laugh-beam": "\u{f59a}", + "fa-laugh-squint": "\u{f59b}", + "fa-laugh-wink": "\u{f59c}", + "fa-layer-group": "\u{f5fd}", + "fa-leaf": "\u{f06c}", + "fa-leanpub": "\u{f212}", + "fa-lemon": "\u{f094}", + "fa-less": "\u{f41d}", + "fa-less-than": "\u{f536}", + "fa-less-than-equal": "\u{f537}", + "fa-level-down-alt": "\u{f3be}", + "fa-level-up-alt": "\u{f3bf}", + "fa-life-ring": "\u{f1cd}", + "fa-lightbulb": "\u{f0eb}", + "fa-line": "\u{f3c0}", + "fa-link": "\u{f0c1}", + "fa-linkedin": "\u{f08c}", + "fa-linkedin-in": "\u{f0e1}", + "fa-linode": "\u{f2b8}", + "fa-linux": "\u{f17c}", + "fa-lira-sign": "\u{f195}", + "fa-list": "\u{f03a}", + "fa-list-alt": "\u{f022}", + "fa-list-ol": "\u{f0cb}", + "fa-list-ul": "\u{f0ca}", + "fa-location-arrow": "\u{f124}", + "fa-lock": "\u{f023}", + "fa-lock-open": "\u{f3c1}", + "fa-long-arrow-alt-down": "\u{f309}", + "fa-long-arrow-alt-left": "\u{f30a}", + "fa-long-arrow-alt-right": "\u{f30b}", + "fa-long-arrow-alt-up": "\u{f30c}", + "fa-low-vision": "\u{f2a8}", + "fa-luggage-cart": "\u{f59d}", + "fa-lyft": "\u{f3c3}", + "fa-magento": "\u{f3c4}", + "fa-magic": "\u{f0d0}", + "fa-magnet": "\u{f076}", + "fa-mail-bulk": "\u{f674}", + "fa-mailchimp": "\u{f59e}", + "fa-male": "\u{f183}", + "fa-mandalorian": "\u{f50f}", + "fa-map": "\u{f279}", + "fa-map-marked": "\u{f59f}", + "fa-map-marked-alt": "\u{f5a0}", + "fa-map-marker": "\u{f041}", + "fa-map-marker-alt": "\u{f3c5}", + "fa-map-pin": "\u{f276}", + "fa-map-signs": "\u{f277}", + "fa-markdown": "\u{f60f}", + "fa-marker": "\u{f5a1}", + "fa-mars": "\u{f222}", + "fa-mars-double": "\u{f227}", + "fa-mars-stroke": "\u{f229}", + "fa-mars-stroke-h": "\u{f22b}", + "fa-mars-stroke-v": "\u{f22a}", + "fa-mastodon": "\u{f4f6}", + "fa-maxcdn": "\u{f136}", + "fa-medal": "\u{f5a2}", + "fa-medapps": "\u{f3c6}", + "fa-medium": "\u{f23a}", + "fa-medium-m": "\u{f3c7}", + "fa-medkit": "\u{f0fa}", + "fa-medrt": "\u{f3c8}", + "fa-meetup": "\u{f2e0}", + "fa-megaport": "\u{f5a3}", + "fa-meh": "\u{f11a}", + "fa-meh-blank": "\u{f5a4}", + "fa-meh-rolling-eyes": "\u{f5a5}", + "fa-memory": "\u{f538}", + "fa-menorah": "\u{f676}", + "fa-mercury": "\u{f223}", + "fa-microchip": "\u{f2db}", + "fa-microphone": "\u{f130}", + "fa-microphone-alt": "\u{f3c9}", + "fa-microphone-alt-slash": "\u{f539}", + "fa-microphone-slash": "\u{f131}", + "fa-microscope": "\u{f610}", + "fa-microsoft": "\u{f3ca}", + "fa-minus": "\u{f068}", + "fa-minus-circle": "\u{f056}", + "fa-minus-square": "\u{f146}", + "fa-mix": "\u{f3cb}", + "fa-mixcloud": "\u{f289}", + "fa-mizuni": "\u{f3cc}", + "fa-mobile": "\u{f10b}", + "fa-mobile-alt": "\u{f3cd}", + "fa-modx": "\u{f285}", + "fa-monero": "\u{f3d0}", + "fa-money-bill": "\u{f0d6}", + "fa-money-bill-alt": "\u{f3d1}", + "fa-money-bill-wave": "\u{f53a}", + "fa-money-bill-wave-alt": "\u{f53b}", + "fa-money-check": "\u{f53c}", + "fa-money-check-alt": "\u{f53d}", + "fa-monument": "\u{f5a6}", + "fa-moon": "\u{f186}", + "fa-mortar-pestle": "\u{f5a7}", + "fa-mosque": "\u{f678}", + "fa-motorcycle": "\u{f21c}", + "fa-mouse-pointer": "\u{f245}", + "fa-music": "\u{f001}", + "fa-napster": "\u{f3d2}", + "fa-neos": "\u{f612}", + "fa-neuter": "\u{f22c}", + "fa-newspaper": "\u{f1ea}", + "fa-nimblr": "\u{f5a8}", + "fa-nintendo-switch": "\u{f418}", + "fa-node": "\u{f419}", + "fa-node-js": "\u{f3d3}", + "fa-not-equal": "\u{f53e}", + "fa-notes-medical": "\u{f481}", + "fa-npm": "\u{f3d4}", + "fa-ns8": "\u{f3d5}", + "fa-nutritionix": "\u{f3d6}", + "fa-object-group": "\u{f247}", + "fa-object-ungroup": "\u{f248}", + "fa-odnoklassniki": "\u{f263}", + "fa-odnoklassniki-square": "\u{f264}", + "fa-oil-can": "\u{f613}", + "fa-old-republic": "\u{f510}", + "fa-om": "\u{f679}", + "fa-opencart": "\u{f23d}", + "fa-openid": "\u{f19b}", + "fa-opera": "\u{f26a}", + "fa-optin-monster": "\u{f23c}", + "fa-osi": "\u{f41a}", + "fa-outdent": "\u{f03b}", + "fa-page4": "\u{f3d7}", + "fa-pagelines": "\u{f18c}", + "fa-paint-brush": "\u{f1fc}", + "fa-paint-roller": "\u{f5aa}", + "fa-palette": "\u{f53f}", + "fa-palfed": "\u{f3d8}", + "fa-pallet": "\u{f482}", + "fa-paper-plane": "\u{f1d8}", + "fa-paperclip": "\u{f0c6}", + "fa-parachute-box": "\u{f4cd}", + "fa-paragraph": "\u{f1dd}", + "fa-parking": "\u{f540}", + "fa-passport": "\u{f5ab}", + "fa-pastafarianism": "\u{f67b}", + "fa-paste": "\u{f0ea}", + "fa-patreon": "\u{f3d9}", + "fa-pause": "\u{f04c}", + "fa-pause-circle": "\u{f28b}", + "fa-paw": "\u{f1b0}", + "fa-paypal": "\u{f1ed}", + "fa-peace": "\u{f67c}", + "fa-pen": "\u{f304}", + "fa-pen-alt": "\u{f305}", + "fa-pen-fancy": "\u{f5ac}", + "fa-pen-nib": "\u{f5ad}", + "fa-pen-square": "\u{f14b}", + "fa-pencil-alt": "\u{f303}", + "fa-pencil-ruler": "\u{f5ae}", + "fa-people-carry": "\u{f4ce}", + "fa-percent": "\u{f295}", + "fa-percentage": "\u{f541}", + "fa-periscope": "\u{f3da}", + "fa-phabricator": "\u{f3db}", + "fa-phoenix-framework": "\u{f3dc}", + "fa-phoenix-squadron": "\u{f511}", + "fa-phone": "\u{f095}", + "fa-phone-slash": "\u{f3dd}", + "fa-phone-square": "\u{f098}", + "fa-phone-volume": "\u{f2a0}", + "fa-php": "\u{f457}", + "fa-pied-piper": "\u{f2ae}", + "fa-pied-piper-alt": "\u{f1a8}", + "fa-pied-piper-hat": "\u{f4e5}", + "fa-pied-piper-pp": "\u{f1a7}", + "fa-piggy-bank": "\u{f4d3}", + "fa-pills": "\u{f484}", + "fa-pinterest": "\u{f0d2}", + "fa-pinterest-p": "\u{f231}", + "fa-pinterest-square": "\u{f0d3}", + "fa-place-of-worship": "\u{f67f}", + "fa-plane": "\u{f072}", + "fa-plane-arrival": "\u{f5af}", + "fa-plane-departure": "\u{f5b0}", + "fa-play": "\u{f04b}", + "fa-play-circle": "\u{f144}", + "fa-playstation": "\u{f3df}", + "fa-plug": "\u{f1e6}", + "fa-plus": "\u{f067}", + "fa-plus-circle": "\u{f055}", + "fa-plus-square": "\u{f0fe}", + "fa-podcast": "\u{f2ce}", + "fa-poll": "\u{f681}", + "fa-poll-h": "\u{f682}", + "fa-poo": "\u{f2fe}", + "fa-poop": "\u{f619}", + "fa-portrait": "\u{f3e0}", + "fa-pound-sign": "\u{f154}", + "fa-power-off": "\u{f011}", + "fa-pray": "\u{f683}", + "fa-praying-hands": "\u{f684}", + "fa-prescription": "\u{f5b1}", + "fa-prescription-bottle": "\u{f485}", + "fa-prescription-bottle-alt": "\u{f486}", + "fa-print": "\u{f02f}", + "fa-procedures": "\u{f487}", + "fa-product-hunt": "\u{f288}", + "fa-project-diagram": "\u{f542}", + "fa-pushed": "\u{f3e1}", + "fa-puzzle-piece": "\u{f12e}", + "fa-python": "\u{f3e2}", + "fa-qq": "\u{f1d6}", + "fa-qrcode": "\u{f029}", + "fa-question": "\u{f128}", + "fa-question-circle": "\u{f059}", + "fa-quidditch": "\u{f458}", + "fa-quinscape": "\u{f459}", + "fa-quora": "\u{f2c4}", + "fa-quote-left": "\u{f10d}", + "fa-quote-right": "\u{f10e}", + "fa-quran": "\u{f687}", + "fa-r-project": "\u{f4f7}", + "fa-random": "\u{f074}", + "fa-ravelry": "\u{f2d9}", + "fa-react": "\u{f41b}", + "fa-readme": "\u{f4d5}", + "fa-rebel": "\u{f1d0}", + "fa-receipt": "\u{f543}", + "fa-recycle": "\u{f1b8}", + "fa-red-river": "\u{f3e3}", + "fa-reddit": "\u{f1a1}", + "fa-reddit-alien": "\u{f281}", + "fa-reddit-square": "\u{f1a2}", + "fa-redo": "\u{f01e}", + "fa-redo-alt": "\u{f2f9}", + "fa-registered": "\u{f25d}", + "fa-rendact": "\u{f3e4}", + "fa-renren": "\u{f18b}", + "fa-reply": "\u{f3e5}", + "fa-reply-all": "\u{f122}", + "fa-replyd": "\u{f3e6}", + "fa-researchgate": "\u{f4f8}", + "fa-resolving": "\u{f3e7}", + "fa-retweet": "\u{f079}", + "fa-rev": "\u{f5b2}", + "fa-ribbon": "\u{f4d6}", + "fa-road": "\u{f018}", + "fa-robot": "\u{f544}", + "fa-rocket": "\u{f135}", + "fa-rocketchat": "\u{f3e8}", + "fa-rockrms": "\u{f3e9}", + "fa-route": "\u{f4d7}", + "fa-rss": "\u{f09e}", + "fa-rss-square": "\u{f143}", + "fa-ruble-sign": "\u{f158}", + "fa-ruler": "\u{f545}", + "fa-ruler-combined": "\u{f546}", + "fa-ruler-horizontal": "\u{f547}", + "fa-ruler-vertical": "\u{f548}", + "fa-rupee-sign": "\u{f156}", + "fa-sad-cry": "\u{f5b3}", + "fa-sad-tear": "\u{f5b4}", + "fa-safari": "\u{f267}", + "fa-sass": "\u{f41e}", + "fa-save": "\u{f0c7}", + "fa-schlix": "\u{f3ea}", + "fa-school": "\u{f549}", + "fa-screwdriver": "\u{f54a}", + "fa-scribd": "\u{f28a}", + "fa-search": "\u{f002}", + "fa-search-dollar": "\u{f688}", + "fa-search-location": "\u{f689}", + "fa-search-minus": "\u{f010}", + "fa-search-plus": "\u{f00e}", + "fa-searchengin": "\u{f3eb}", + "fa-seedling": "\u{f4d8}", + "fa-sellcast": "\u{f2da}", + "fa-sellsy": "\u{f213}", + "fa-server": "\u{f233}", + "fa-servicestack": "\u{f3ec}", + "fa-shapes": "\u{f61f}", + "fa-share": "\u{f064}", + "fa-share-alt": "\u{f1e0}", + "fa-share-alt-square": "\u{f1e1}", + "fa-share-square": "\u{f14d}", + "fa-shekel-sign": "\u{f20b}", + "fa-shield-alt": "\u{f3ed}", + "fa-ship": "\u{f21a}", + "fa-shipping-fast": "\u{f48b}", + "fa-shirtsinbulk": "\u{f214}", + "fa-shoe-prints": "\u{f54b}", + "fa-shopping-bag": "\u{f290}", + "fa-shopping-basket": "\u{f291}", + "fa-shopping-cart": "\u{f07a}", + "fa-shopware": "\u{f5b5}", + "fa-shower": "\u{f2cc}", + "fa-shuttle-van": "\u{f5b6}", + "fa-sign": "\u{f4d9}", + "fa-sign-in-alt": "\u{f2f6}", + "fa-sign-language": "\u{f2a7}", + "fa-sign-out-alt": "\u{f2f5}", + "fa-signal": "\u{f012}", + "fa-signature": "\u{f5b7}", + "fa-simplybuilt": "\u{f215}", + "fa-sistrix": "\u{f3ee}", + "fa-sitemap": "\u{f0e8}", + "fa-sith": "\u{f512}", + "fa-skull": "\u{f54c}", + "fa-skyatlas": "\u{f216}", + "fa-skype": "\u{f17e}", + "fa-slack": "\u{f198}", + "fa-slack-hash": "\u{f3ef}", + "fa-sliders-h": "\u{f1de}", + "fa-slideshare": "\u{f1e7}", + "fa-smile": "\u{f118}", + "fa-smile-beam": "\u{f5b8}", + "fa-smile-wink": "\u{f4da}", + "fa-smoking": "\u{f48d}", + "fa-smoking-ban": "\u{f54d}", + "fa-snapchat": "\u{f2ab}", + "fa-snapchat-ghost": "\u{f2ac}", + "fa-snapchat-square": "\u{f2ad}", + "fa-snowflake": "\u{f2dc}", + "fa-socks": "\u{f696}", + "fa-solar-panel": "\u{f5ba}", + "fa-sort": "\u{f0dc}", + "fa-sort-alpha-down": "\u{f15d}", + "fa-sort-alpha-up": "\u{f15e}", + "fa-sort-amount-down": "\u{f160}", + "fa-sort-amount-up": "\u{f161}", + "fa-sort-down": "\u{f0dd}", + "fa-sort-numeric-down": "\u{f162}", + "fa-sort-numeric-up": "\u{f163}", + "fa-sort-up": "\u{f0de}", + "fa-soundcloud": "\u{f1be}", + "fa-spa": "\u{f5bb}", + "fa-space-shuttle": "\u{f197}", + "fa-speakap": "\u{f3f3}", + "fa-spinner": "\u{f110}", + "fa-splotch": "\u{f5bc}", + "fa-spotify": "\u{f1bc}", + "fa-spray-can": "\u{f5bd}", + "fa-square": "\u{f0c8}", + "fa-square-full": "\u{f45c}", + "fa-square-root-alt": "\u{f698}", + "fa-squarespace": "\u{f5be}", + "fa-stack-exchange": "\u{f18d}", + "fa-stack-overflow": "\u{f16c}", + "fa-stamp": "\u{f5bf}", + "fa-star": "\u{f005}", + "fa-star-and-crescent": "\u{f699}", + "fa-star-half": "\u{f089}", + "fa-star-half-alt": "\u{f5c0}", + "fa-star-of-david": "\u{f69a}", + "fa-star-of-life": "\u{f621}", + "fa-staylinked": "\u{f3f5}", + "fa-steam": "\u{f1b6}", + "fa-steam-square": "\u{f1b7}", + "fa-steam-symbol": "\u{f3f6}", + "fa-step-backward": "\u{f048}", + "fa-step-forward": "\u{f051}", + "fa-stethoscope": "\u{f0f1}", + "fa-sticker-mule": "\u{f3f7}", + "fa-sticky-note": "\u{f249}", + "fa-stop": "\u{f04d}", + "fa-stop-circle": "\u{f28d}", + "fa-stopwatch": "\u{f2f2}", + "fa-store": "\u{f54e}", + "fa-store-alt": "\u{f54f}", + "fa-strava": "\u{f428}", + "fa-stream": "\u{f550}", + "fa-street-view": "\u{f21d}", + "fa-strikethrough": "\u{f0cc}", + "fa-stripe": "\u{f429}", + "fa-stripe-s": "\u{f42a}", + "fa-stroopwafel": "\u{f551}", + "fa-studiovinari": "\u{f3f8}", + "fa-stumbleupon": "\u{f1a4}", + "fa-stumbleupon-circle": "\u{f1a3}", + "fa-subscript": "\u{f12c}", + "fa-subway": "\u{f239}", + "fa-suitcase": "\u{f0f2}", + "fa-suitcase-rolling": "\u{f5c1}", + "fa-sun": "\u{f185}", + "fa-superpowers": "\u{f2dd}", + "fa-superscript": "\u{f12b}", + "fa-supple": "\u{f3f9}", + "fa-surprise": "\u{f5c2}", + "fa-swatchbook": "\u{f5c3}", + "fa-swimmer": "\u{f5c4}", + "fa-swimming-pool": "\u{f5c5}", + "fa-synagogue": "\u{f69b}", + "fa-sync": "\u{f021}", + "fa-sync-alt": "\u{f2f1}", + "fa-syringe": "\u{f48e}", + "fa-table": "\u{f0ce}", + "fa-table-tennis": "\u{f45d}", + "fa-tablet": "\u{f10a}", + "fa-tablet-alt": "\u{f3fa}", + "fa-tablets": "\u{f490}", + "fa-tachometer-alt": "\u{f3fd}", + "fa-tag": "\u{f02b}", + "fa-tags": "\u{f02c}", + "fa-tape": "\u{f4db}", + "fa-tasks": "\u{f0ae}", + "fa-taxi": "\u{f1ba}", + "fa-teamspeak": "\u{f4f9}", + "fa-teeth": "\u{f62e}", + "fa-teeth-open": "\u{f62f}", + "fa-telegram": "\u{f2c6}", + "fa-telegram-plane": "\u{f3fe}", + "fa-tencent-weibo": "\u{f1d5}", + "fa-terminal": "\u{f120}", + "fa-text-height": "\u{f034}", + "fa-text-width": "\u{f035}", + "fa-th": "\u{f00a}", + "fa-th-large": "\u{f009}", + "fa-th-list": "\u{f00b}", + "fa-the-red-yeti": "\u{f69d}", + "fa-theater-masks": "\u{f630}", + "fa-themeco": "\u{f5c6}", + "fa-themeisle": "\u{f2b2}", + "fa-thermometer": "\u{f491}", + "fa-thermometer-empty": "\u{f2cb}", + "fa-thermometer-full": "\u{f2c7}", + "fa-thermometer-half": "\u{f2c9}", + "fa-thermometer-quarter": "\u{f2ca}", + "fa-thermometer-three-quarters": "\u{f2c8}", + "fa-thumbs-down": "\u{f165}", + "fa-thumbs-up": "\u{f164}", + "fa-thumbtack": "\u{f08d}", + "fa-ticket-alt": "\u{f3ff}", + "fa-times": "\u{f00d}", + "fa-times-circle": "\u{f057}", + "fa-tint": "\u{f043}", + "fa-tint-slash": "\u{f5c7}", + "fa-tired": "\u{f5c8}", + "fa-toggle-off": "\u{f204}", + "fa-toggle-on": "\u{f205}", + "fa-toolbox": "\u{f552}", + "fa-tooth": "\u{f5c9}", + "fa-torah": "\u{f6a0}", + "fa-torii-gate": "\u{f6a1}", + "fa-trade-federation": "\u{f513}", + "fa-trademark": "\u{f25c}", + "fa-traffic-light": "\u{f637}", + "fa-train": "\u{f238}", + "fa-transgender": "\u{f224}", + "fa-transgender-alt": "\u{f225}", + "fa-trash": "\u{f1f8}", + "fa-trash-alt": "\u{f2ed}", + "fa-tree": "\u{f1bb}", + "fa-trello": "\u{f181}", + "fa-tripadvisor": "\u{f262}", + "fa-trophy": "\u{f091}", + "fa-truck": "\u{f0d1}", + "fa-truck-loading": "\u{f4de}", + "fa-truck-monster": "\u{f63b}", + "fa-truck-moving": "\u{f4df}", + "fa-truck-pickup": "\u{f63c}", + "fa-tshirt": "\u{f553}", + "fa-tty": "\u{f1e4}", + "fa-tumblr": "\u{f173}", + "fa-tumblr-square": "\u{f174}", + "fa-tv": "\u{f26c}", + "fa-twitch": "\u{f1e8}", + "fa-twitter": "\u{f099}", + "fa-twitter-square": "\u{f081}", + "fa-typo3": "\u{f42b}", + "fa-uber": "\u{f402}", + "fa-uikit": "\u{f403}", + "fa-umbrella": "\u{f0e9}", + "fa-umbrella-beach": "\u{f5ca}", + "fa-underline": "\u{f0cd}", + "fa-undo": "\u{f0e2}", + "fa-undo-alt": "\u{f2ea}", + "fa-uniregistry": "\u{f404}", + "fa-universal-access": "\u{f29a}", + "fa-university": "\u{f19c}", + "fa-unlink": "\u{f127}", + "fa-unlock": "\u{f09c}", + "fa-unlock-alt": "\u{f13e}", + "fa-untappd": "\u{f405}", + "fa-upload": "\u{f093}", + "fa-usb": "\u{f287}", + "fa-user": "\u{f007}", + "fa-user-alt": "\u{f406}", + "fa-user-alt-slash": "\u{f4fa}", + "fa-user-astronaut": "\u{f4fb}", + "fa-user-check": "\u{f4fc}", + "fa-user-circle": "\u{f2bd}", + "fa-user-clock": "\u{f4fd}", + "fa-user-cog": "\u{f4fe}", + "fa-user-edit": "\u{f4ff}", + "fa-user-friends": "\u{f500}", + "fa-user-graduate": "\u{f501}", + "fa-user-lock": "\u{f502}", + "fa-user-md": "\u{f0f0}", + "fa-user-minus": "\u{f503}", + "fa-user-ninja": "\u{f504}", + "fa-user-plus": "\u{f234}", + "fa-user-secret": "\u{f21b}", + "fa-user-shield": "\u{f505}", + "fa-user-slash": "\u{f506}", + "fa-user-tag": "\u{f507}", + "fa-user-tie": "\u{f508}", + "fa-user-times": "\u{f235}", + "fa-users": "\u{f0c0}", + "fa-users-cog": "\u{f509}", + "fa-ussunnah": "\u{f407}", + "fa-utensil-spoon": "\u{f2e5}", + "fa-utensils": "\u{f2e7}", + "fa-vaadin": "\u{f408}", + "fa-vector-square": "\u{f5cb}", + "fa-venus": "\u{f221}", + "fa-venus-double": "\u{f226}", + "fa-venus-mars": "\u{f228}", + "fa-viacoin": "\u{f237}", + "fa-viadeo": "\u{f2a9}", + "fa-viadeo-square": "\u{f2aa}", + "fa-vial": "\u{f492}", + "fa-vials": "\u{f493}", + "fa-viber": "\u{f409}", + "fa-video": "\u{f03d}", + "fa-video-slash": "\u{f4e2}", + "fa-vihara": "\u{f6a7}", + "fa-vimeo": "\u{f40a}", + "fa-vimeo-square": "\u{f194}", + "fa-vimeo-v": "\u{f27d}", + "fa-vine": "\u{f1ca}", + "fa-vk": "\u{f189}", + "fa-vnv": "\u{f40b}", + "fa-volleyball-ball": "\u{f45f}", + "fa-volume-down": "\u{f027}", + "fa-volume-off": "\u{f026}", + "fa-volume-up": "\u{f028}", + "fa-vuejs": "\u{f41f}", + "fa-walking": "\u{f554}", + "fa-wallet": "\u{f555}", + "fa-warehouse": "\u{f494}", + "fa-weebly": "\u{f5cc}", + "fa-weibo": "\u{f18a}", + "fa-weight": "\u{f496}", + "fa-weight-hanging": "\u{f5cd}", + "fa-weixin": "\u{f1d7}", + "fa-whatsapp": "\u{f232}", + "fa-whatsapp-square": "\u{f40c}", + "fa-wheelchair": "\u{f193}", + "fa-whmcs": "\u{f40d}", + "fa-wifi": "\u{f1eb}", + "fa-wikipedia-w": "\u{f266}", + "fa-window-close": "\u{f410}", + "fa-window-maximize": "\u{f2d0}", + "fa-window-minimize": "\u{f2d1}", + "fa-window-restore": "\u{f2d2}", + "fa-windows": "\u{f17a}", + "fa-wine-glass": "\u{f4e3}", + "fa-wine-glass-alt": "\u{f5ce}", + "fa-wix": "\u{f5cf}", + "fa-wolf-pack-battalion": "\u{f514}", + "fa-won-sign": "\u{f159}", + "fa-wordpress": "\u{f19a}", + "fa-wordpress-simple": "\u{f411}", + "fa-wpbeginner": "\u{f297}", + "fa-wpexplorer": "\u{f2de}", + "fa-wpforms": "\u{f298}", + "fa-wrench": "\u{f0ad}", + "fa-x-ray": "\u{f497}", + "fa-xbox": "\u{f412}", + "fa-xing": "\u{f168}", + "fa-xing-square": "\u{f169}", + "fa-y-combinator": "\u{f23b}", + "fa-yahoo": "\u{f19e}", + "fa-yandex": "\u{f413}", + "fa-yandex-international": "\u{f414}", + "fa-yelp": "\u{f1e9}", + "fa-yen-sign": "\u{f157}", + "fa-yin-yang": "\u{f6ad}", + "fa-yoast": "\u{f2b1}", + "fa-youtube": "\u{f167}", + "fa-youtube-square": "\u{f431}", + "fa-zhihu": "\u{f63f}" +] diff --git a/Pods/FontAwesome.swift/FontAwesome/Font Awesome 5 Brands-Regular-400.otf b/Pods/FontAwesome.swift/FontAwesome/Font Awesome 5 Brands-Regular-400.otf new file mode 100644 index 0000000..a327deb Binary files /dev/null and b/Pods/FontAwesome.swift/FontAwesome/Font Awesome 5 Brands-Regular-400.otf differ diff --git a/Pods/FontAwesome.swift/FontAwesome/Font Awesome 5 Free-Regular-400.otf b/Pods/FontAwesome.swift/FontAwesome/Font Awesome 5 Free-Regular-400.otf new file mode 100644 index 0000000..a99730a Binary files /dev/null and b/Pods/FontAwesome.swift/FontAwesome/Font Awesome 5 Free-Regular-400.otf differ diff --git a/Pods/FontAwesome.swift/FontAwesome/Font Awesome 5 Free-Solid-900.otf b/Pods/FontAwesome.swift/FontAwesome/Font Awesome 5 Free-Solid-900.otf new file mode 100644 index 0000000..8a68da2 Binary files /dev/null and b/Pods/FontAwesome.swift/FontAwesome/Font Awesome 5 Free-Solid-900.otf differ diff --git a/Pods/FontAwesome.swift/FontAwesome/FontAwesome.swift b/Pods/FontAwesome.swift/FontAwesome/FontAwesome.swift new file mode 100644 index 0000000..1357e28 --- /dev/null +++ b/Pods/FontAwesome.swift/FontAwesome/FontAwesome.swift @@ -0,0 +1,225 @@ +// FontAwesome.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit +import CoreText + +// MARK: - Public + +/// A configuration namespace for FontAwesome. +public struct FontAwesomeConfig { + + // Marked private to prevent initialization of this struct. + private init() { } + + /// Taken from FontAwesome.io's Fixed Width Icon CSS. + public static let fontAspectRatio: CGFloat = 1.28571429 +} + +public enum FontAwesomeStyle: String { + case solid + case regular + case brands + + func fontName() -> String { + switch self { + case .solid: + return "FontAwesome5FreeSolid" + case .regular: + return "FontAwesome5FreeRegular" + case .brands: + return "FontAwesome5BrandsRegular" + } + } + + func fontFilename() -> String { + switch self { + case .solid: + return "Font Awesome 5 Free-Solid-900" + case .regular: + return "Font Awesome 5 Free-Regular-400" + case .brands: + return "Font Awesome 5 Brands-Regular-400" + } + } + + func fontFamilyName() -> String { + switch self { + case .brands: + return "Font Awesome 5 Brands" + case .regular, + .solid: + return "Font Awesome 5 Free" + } + } +} + +/// A FontAwesome extension to UIFont. +public extension UIFont { + + /// Get a UIFont object of FontAwesome. + /// + /// - parameter ofSize: The preferred font size. + /// - returns: A UIFont object of FontAwesome. + public class func fontAwesome(ofSize fontSize: CGFloat, style: FontAwesomeStyle) -> UIFont { + loadFontAwesome(ofStyle: style) + return UIFont(name: style.fontName(), size: fontSize)! + } + + /// Loads the FontAwesome font in to memory. + /// This method should be called when setting icons without using code. + public class func loadFontAwesome(ofStyle style: FontAwesomeStyle) { + if UIFont.fontNames(forFamilyName: style.fontFamilyName()).contains(style.fontName()) { + return + } + + FontLoader.loadFont(style.fontFilename()) + } +} + +/// A FontAwesome extension to String. +public extension String { + + /// Get a FontAwesome icon string with the given icon name. + /// + /// - parameter name: The preferred icon name. + /// - returns: A string that will appear as icon with FontAwesome. + public static func fontAwesomeIcon(name: FontAwesome) -> String { + let toIndex = name.rawValue.index(name.rawValue.startIndex, offsetBy: 1) + return String(name.rawValue[name.rawValue.startIndex.. String? { + + guard let name = self.fontAwesome(code: code) else { + return nil + } + + return self.fontAwesomeIcon(name: name) + } + + /// Get a FontAwesome icon with the given CSS icon code. Icon code can be found here: http://fontawesome.io/icons/ + /// + /// - parameter code: The preferred icon name. + /// - returns: An internal corresponding FontAwesome code. + public static func fontAwesome(code: String) -> FontAwesome? { + guard let raw = FontAwesomeIcons[code] else { return nil } + return FontAwesome(rawValue: raw) + } +} + +/// A FontAwesome extension to UIImage. +public extension UIImage { + + /// Get a FontAwesome image with the given icon name, text color, size and an optional background color. + /// + /// - parameter name: The preferred icon name. + /// - parameter style: The font style. Either .solid, .regular or .brands. + /// - parameter textColor: The text color. + /// - parameter size: The image size. + /// - parameter backgroundColor: The background color (optional). + /// - returns: A string that will appear as icon with FontAwesome + public static func fontAwesomeIcon(name: FontAwesome, style: FontAwesomeStyle, textColor: UIColor, size: CGSize, backgroundColor: UIColor = UIColor.clear, borderWidth: CGFloat = 0, borderColor: UIColor = UIColor.clear) -> UIImage { + + // Prevent application crash when passing size where width or height is set equal to or less than zero, by clipping width and height to a minimum of 1 pixel. + var size = size + if size.width <= 0 { size.width = 1 } + if size.height <= 0 { size.height = 1 } + + let paragraph = NSMutableParagraphStyle() + paragraph.alignment = NSTextAlignment.center + + let fontSize = min(size.width / FontAwesomeConfig.fontAspectRatio, size.height) + + // stroke width expects a whole number percentage of the font size + let strokeWidth: CGFloat = fontSize == 0 ? 0 : (-100 * borderWidth / fontSize) + + let attributedString = NSAttributedString(string: String.fontAwesomeIcon(name: name), attributes: [ + NSAttributedStringKey.font: UIFont.fontAwesome(ofSize: fontSize, style: style), + NSAttributedStringKey.foregroundColor: textColor, + NSAttributedStringKey.backgroundColor: backgroundColor, + NSAttributedStringKey.paragraphStyle: paragraph, + NSAttributedStringKey.strokeWidth: strokeWidth, + NSAttributedStringKey.strokeColor: borderColor + ]) + + UIGraphicsBeginImageContextWithOptions(size, false, 0.0) + attributedString.draw(in: CGRect(x: 0, y: (size.height - fontSize) / 2, width: size.width, height: fontSize)) + let image = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return image! + } + + /// Get a FontAwesome image with the given icon css code, text color, size and an optional background color. + /// + /// - parameter code: The preferred icon css code. + /// - parameter style: The font style. Either .solid, .regular or .brands. + /// - parameter textColor: The text color. + /// - parameter size: The image size. + /// - parameter backgroundColor: The background color (optional). + /// - returns: A string that will appear as icon with FontAwesome + public static func fontAwesomeIcon(code: String, style: FontAwesomeStyle, textColor: UIColor, size: CGSize, backgroundColor: UIColor = UIColor.clear, borderWidth: CGFloat = 0, borderColor: UIColor = UIColor.clear) -> UIImage? { + guard let name = String.fontAwesome(code: code) else { return nil } + return fontAwesomeIcon(name: name, style: style, textColor: textColor, size: size, backgroundColor: backgroundColor, borderWidth: borderWidth, borderColor: borderColor) + } +} + +// MARK: - Private + +private class FontLoader { + class func loadFont(_ name: String) { + guard + let fontURL = URL.fontURL(for: name), + let data = try? Data(contentsOf: fontURL), + let provider = CGDataProvider(data: data as CFData), + let font = CGFont(provider) + else { return } + + var error: Unmanaged? + if !CTFontManagerRegisterGraphicsFont(font, &error) { + let errorDescription: CFString = CFErrorCopyDescription(error!.takeUnretainedValue()) + guard let nsError = error?.takeUnretainedValue() as AnyObject as? NSError else { return } + NSException(name: NSExceptionName.internalInconsistencyException, reason: errorDescription as String, userInfo: [NSUnderlyingErrorKey: nsError]).raise() + } + } +} + +extension URL { + static func fontURL(for fontName: String) -> URL? { + let bundle = Bundle(for: FontLoader.self) + + if let fontURL = bundle.url(forResource: fontName, withExtension: "otf") { + return fontURL + } + + // If this framework is added using CocoaPods, resources is placed under a subdirectory + if let fontURL = bundle.url(forResource: fontName, withExtension: "otf", subdirectory: "FontAwesome.swift.bundle") { + return fontURL + } + + return nil + } +} diff --git a/Pods/FontAwesome.swift/FontAwesome/FontAwesomeBarButtonItem.swift b/Pods/FontAwesome.swift/FontAwesome/FontAwesomeBarButtonItem.swift new file mode 100644 index 0000000..a6fb117 --- /dev/null +++ b/Pods/FontAwesome.swift/FontAwesome/FontAwesomeBarButtonItem.swift @@ -0,0 +1,77 @@ +// FontAwesomeBarButtonItem.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit + +@IBDesignable public class FontAwesomeBarButtonItem: UIBarButtonItem { + + @IBInspectable public var isFontAwesomeCSSCode: Bool = true + @IBInspectable public var styleName: String = "Brands" + @IBInspectable public var size: CGFloat = 25.0 + + public override func awakeFromNib() { + super.awakeFromNib() + useFontAwesome() + } + + public override func prepareForInterfaceBuilder() { + useFontAwesome() + } + + private func useFontAwesome() { + updateText { + if let cssCode = title { + title = String.fontAwesomeIcon(code: cssCode) + } + } + updateFontAttributes { (state, font) in + let currentAttributes = titleTextAttributes(for: state) ?? [:] + var attributes = [NSAttributedStringKey: Any]() + currentAttributes.enumerated().forEach { + let currentAttribute = NSAttributedStringKey(rawValue: $0.element.key) + attributes[currentAttribute] = $0.element.value + } + attributes[NSAttributedStringKey.font] = font + setTitleTextAttributes(attributes, for: state) + } + } + +} + +extension FontAwesomeBarButtonItem: FontAwesomeTextRepresentable { + var isTextCSSCode: Bool { + return isFontAwesomeCSSCode + } + + var textSize: CGFloat { + return size + } + + var fontStyle: FontAwesomeStyle { + return FontAwesomeStyle(rawValue: styleName) ?? .solid + } + + static func supportedStates() -> [UIControlState] { + return [.normal, .highlighted, .disabled] + } + +} diff --git a/Pods/FontAwesome.swift/FontAwesome/FontAwesomeExtension.swift b/Pods/FontAwesome.swift/FontAwesome/FontAwesomeExtension.swift new file mode 100644 index 0000000..ce17a1a --- /dev/null +++ b/Pods/FontAwesome.swift/FontAwesome/FontAwesomeExtension.swift @@ -0,0 +1,36 @@ +// FontAwesomeExtension.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +public extension FontAwesome { + /// Get a FontAwesome string from the given CSS icon code. Icon code can be found here: http://fontawesome.io/icons/ + /// + /// - parameter code: The preferred icon name. + /// - returns: FontAwesome icon. + public static func fromCode(_ code: String) -> FontAwesome? { + guard let raw = FontAwesomeIcons[code], let icon = FontAwesome(rawValue: raw) else { + return nil + } + return icon + } +} diff --git a/Pods/FontAwesome.swift/FontAwesome/FontAwesomeImageRepresentable.swift b/Pods/FontAwesome.swift/FontAwesome/FontAwesomeImageRepresentable.swift new file mode 100644 index 0000000..0a814aa --- /dev/null +++ b/Pods/FontAwesome.swift/FontAwesome/FontAwesomeImageRepresentable.swift @@ -0,0 +1,56 @@ +// FontAwesomeImageRepresentable.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit + +protocol FontAwesomeImageRepresentable: class { + + typealias ImageConfig = (cssIconName: String, style: FontAwesomeStyle, color: UIColor?, backgroundColor: UIColor?) + + var imageWidth: CGFloat { get } + var imageConfigs: [ImageConfig] { get } + + func createImages(configurationHandler: (_ image: UIImage?, _ index: Int) -> Void) +} + +extension FontAwesomeImageRepresentable { + + func createImages(configurationHandler: (_ image: UIImage?, _ index: Int) -> Void) { + let imgSize = imageSizeForAspectRatio() + for (index, config) in imageConfigs.enumerated() { + let img = createImage(config: config, size: imgSize) + configurationHandler(img, index) + } + } + + private func createImage(config: ImageConfig, size: CGSize) -> UIImage? { + return UIImage.fontAwesomeIcon(code: config.cssIconName, + style: config.style, + textColor: config.color ?? .black, + size: size, + backgroundColor: config.backgroundColor ?? .clear) + } + + private func imageSizeForAspectRatio() -> CGSize { + return CGSize(width: imageWidth, height: imageWidth / FontAwesomeConfig.fontAspectRatio) + } +} diff --git a/Pods/FontAwesome.swift/FontAwesome/FontAwesomeImageView.swift b/Pods/FontAwesome.swift/FontAwesome/FontAwesomeImageView.swift new file mode 100644 index 0000000..2b7ece9 --- /dev/null +++ b/Pods/FontAwesome.swift/FontAwesome/FontAwesomeImageView.swift @@ -0,0 +1,60 @@ +// FontAwesomeImageView.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit + +@IBDesignable public class FontAwesomeImageView: UIImageView { + + @IBInspectable public var cssCode: String = "fa-font-awesome-flag" + @IBInspectable public var imageColor: UIColor = .black + @IBInspectable public var imageBackgroundColor: UIColor = .clear + @IBInspectable public var styleName: String = "Brands" + + public override func awakeFromNib() { + super.awakeFromNib() + useFontAwesomeImage() + } + + public override func prepareForInterfaceBuilder() { + useFontAwesomeImage() + } + + private func useFontAwesomeImage() { + createImages { (img, _) in + image = img + } + } + +} + +extension FontAwesomeImageView: FontAwesomeImageRepresentable { + + var imageWidth: CGFloat { + return frame.width + } + + var imageConfigs: [ImageConfig] { + guard let style = FontAwesomeStyle(rawValue: styleName.lowercased()) else { return [] } + return [(cssCode, style, imageColor, imageBackgroundColor)] + } + +} diff --git a/Pods/FontAwesome.swift/FontAwesome/FontAwesomeSegmentedControl.swift b/Pods/FontAwesome.swift/FontAwesome/FontAwesomeSegmentedControl.swift new file mode 100644 index 0000000..b34e6ea --- /dev/null +++ b/Pods/FontAwesome.swift/FontAwesome/FontAwesomeSegmentedControl.swift @@ -0,0 +1,78 @@ +// FontAwesomeSegmentedControl.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit + +@IBDesignable public class FontAwesomeSegmentedControl: UISegmentedControl { + + @IBInspectable public var isFontAwesomeCSSCode: Bool = true + @IBInspectable public var styleName: String = "Brands" + @IBInspectable public var size: CGFloat = 22.0 + + public override func awakeFromNib() { + super.awakeFromNib() + useFontAwesome() + } + + public override func prepareForInterfaceBuilder() { + useFontAwesome() + } + + private func useFontAwesome() { + updateText { + for index in 0 ..< numberOfSegments { + if let cssCode = titleForSegment(at: index) { + setTitle(String.fontAwesomeIcon(code: cssCode), forSegmentAt: index) + } + } + } + updateFontAttributes { (state, font) in + var attributes = titleTextAttributes(for: state) ?? [:] + attributes[NSAttributedStringKey.font] = font + setTitleTextAttributes(attributes, for: state) + } + } + +} + +extension FontAwesomeSegmentedControl: FontAwesomeTextRepresentable { + var isTextCSSCode: Bool { + return isFontAwesomeCSSCode + } + + var fontStyle: FontAwesomeStyle { + return FontAwesomeStyle(rawValue: styleName) ?? .solid + } + + var textSize: CGFloat { + return size + } + + static func supportedStates() -> [UIControlState] { + if #available(iOS 9.0, *) { + return [.normal, .highlighted, .disabled, .focused, .selected, .application, .reserved] + } else { + return [.normal, .highlighted, .disabled, .selected, .application, .reserved] + } + } + +} diff --git a/Pods/FontAwesome.swift/FontAwesome/FontAwesomeStateRequirement.swift b/Pods/FontAwesome.swift/FontAwesome/FontAwesomeStateRequirement.swift new file mode 100644 index 0000000..4f9de74 --- /dev/null +++ b/Pods/FontAwesome.swift/FontAwesome/FontAwesomeStateRequirement.swift @@ -0,0 +1,29 @@ +// FontAwesomeStateRequirement.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit + +protocol FontAwesomeStateRequirement: class { + + static func supportedStates() -> [UIControlState] + +} diff --git a/Pods/FontAwesome.swift/FontAwesome/FontAwesomeTabBarItem.swift b/Pods/FontAwesome.swift/FontAwesome/FontAwesomeTabBarItem.swift new file mode 100644 index 0000000..73c0175 --- /dev/null +++ b/Pods/FontAwesome.swift/FontAwesome/FontAwesomeTabBarItem.swift @@ -0,0 +1,64 @@ +// FontAwesomeTabBarItem.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit + +@IBDesignable public class FontAwesomeTabBarItem: UITabBarItem { + + @IBInspectable public var iconName: String = "fa-font-awesome-flag" + @IBInspectable public var selectedIconName: String = "fa-font-awesome-flag" + @IBInspectable public var size: CGFloat = 38.0 + @IBInspectable public var styleName: String = "Brands" + + public override func awakeFromNib() { + super.awakeFromNib() + useFontAwesomeImage() + } + + public override func prepareForInterfaceBuilder() { + useFontAwesomeImage() + } + + private func useFontAwesomeImage() { + createImages { (img, index) in + if index == 0 { + image = img + } else { + selectedImage = img + } + } + } + +} + +extension FontAwesomeTabBarItem: FontAwesomeImageRepresentable { + + var imageWidth: CGFloat { + return size + } + + var imageConfigs: [ImageConfig] { + guard let style = FontAwesomeStyle(rawValue: styleName.lowercased()) else { return [] } + return [(iconName, style, nil, nil), (selectedIconName, style, nil, nil)] + } + +} diff --git a/Pods/FontAwesome.swift/FontAwesome/FontAwesomeTextRepresentable.swift b/Pods/FontAwesome.swift/FontAwesome/FontAwesomeTextRepresentable.swift new file mode 100644 index 0000000..677a8b4 --- /dev/null +++ b/Pods/FontAwesome.swift/FontAwesome/FontAwesomeTextRepresentable.swift @@ -0,0 +1,54 @@ +// FontAwesomeTextRepresentable.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit + +protocol FontAwesomeTextRepresentable: FontAwesomeStateRequirement { + + var textSize: CGFloat { get } + var isTextCSSCode: Bool { get } + var fontStyle: FontAwesomeStyle { get } + + func updateText(_ updateTextBlock: () -> Void) + func updateFontAttributes(forStates stateBlock: (UIControlState, UIFont) -> Void) + +} + +extension FontAwesomeTextRepresentable { + + public func updateText(_ updateTextBlock: () -> Void) { + guard isTextCSSCode else { + return + } + + updateTextBlock() + } + + public func updateFontAttributes(forStates stateBlock: (UIControlState, UIFont) -> Void) { + let states = type(of: self).supportedStates() + let font = UIFont.fontAwesome(ofSize: textSize, style: fontStyle) + + for state in states { + stateBlock(state, font) + } + } +} diff --git a/Pods/FontAwesome.swift/FontAwesome/FontAwesomeView.swift b/Pods/FontAwesome.swift/FontAwesome/FontAwesomeView.swift new file mode 100644 index 0000000..6f53f6c --- /dev/null +++ b/Pods/FontAwesome.swift/FontAwesome/FontAwesomeView.swift @@ -0,0 +1,75 @@ +// FontAwesomeView.swift +// +// Copyright (c) 2014-present FontAwesome.swift contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit + +/// A view for FontAwesome icons. +@IBDesignable public class FontAwesomeView: UIView { + + @IBInspectable + public var iconCode: String = "" { + didSet { + self.iconView.text = String.fontAwesomeIcon(code: iconCode) + } + } + + @IBInspectable + public var styleName: String = "Brands" + + private var iconView = UILabel() + + override init(frame: CGRect) { + super.init(frame: frame) + setupViews() + } + + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + setupViews() + } + + override public func prepareForInterfaceBuilder() { + setupViews() + } + + /// Add a UILabel subview containing FontAwesome icon + func setupViews() { + // Fits icon in the view + self.iconView.textAlignment = NSTextAlignment.center + self.iconView.text = String.fontAwesomeIcon(code: self.iconCode) + self.iconView.textColor = self.tintColor + self.addSubview(iconView) + } + + override public func tintColorDidChange() { + self.iconView.textColor = self.tintColor + } + + override public func layoutSubviews() { + super.layoutSubviews() + self.clipsToBounds = true + let size = bounds.size.width < bounds.size.height ? bounds.size.width : bounds.size.height + let style = FontAwesomeStyle(rawValue: styleName) ?? .solid + self.iconView.font = UIFont.fontAwesome(ofSize: size, style: style) + self.iconView.frame = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: bounds.size.width, height: bounds.size.height)) + } +} diff --git a/Pods/FontAwesome.swift/LICENSE b/Pods/FontAwesome.swift/LICENSE new file mode 100644 index 0000000..8dde6c9 --- /dev/null +++ b/Pods/FontAwesome.swift/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014-present FontAwesome.swift contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Pods/FontAwesome.swift/README.md b/Pods/FontAwesome.swift/README.md new file mode 100644 index 0000000..ded3a22 --- /dev/null +++ b/Pods/FontAwesome.swift/README.md @@ -0,0 +1,98 @@ +# FontAwesome.swift + +[![Build Status](http://img.shields.io/travis/thii/FontAwesome.swift.svg?style=flat)](https://travis-ci.org/thii/FontAwesome.swift) +[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/FontAwesome.swift.svg)](https://img.shields.io/cocoapods/v/FontAwesome.swift.svg) +[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![Platform](https://img.shields.io/cocoapods/p/FontAwesome.swift.svg?style=flat)](http://cocoadocs.org/docsets/FontAwesome.swift) +[![License](https://img.shields.io/cocoapods/l/FontAwesome.swift.svg)](https://raw.githubusercontent.com/thii/FontAwesome.swift/master/LICENSE) + +Use Font Awesome in your Swift projects + +To see the complete set of 3,652 icons in Font Awesome 5, please check the [FontAwesome.com](http://fontawesome.com/icons/) site. + +## Installation + +Since this is a Swift project, integrating using Carthage is the recommended way. Releases which support CocoaPods might be delayed sometimes. + +### Carthage + +To integrate FontAwesome into your Xcode project using Carthage, specify it in your `Cartfile`: + +```ogdl +github "thii/FontAwesome.swift" +``` + +Then add `import FontAwesome` to the top of the files using FontAwesome. + +### CocoaPods + +To integrate FontAwesome into your Xcode project using CocoaPods, specify it in your `Podfile`: + +```ruby +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +use_frameworks! + +pod 'FontAwesome.swift' +``` + +Then, run the following command: + +```bash +$ pod install +``` + +And add `import FontAwesome_swift` to the top of the files using FontAwesome. + +### Manually +- Drag and drop all `.otf` and `.swift` files into your project + +## Examples + +```swift +// FontAwesome icon in label +label.font = UIFont.fontAwesome(ofSize: 100, style: .brands) +label.text = String.fontAwesomeIcon(name: .github) + +let attributes = [NSAttributedStringKey.font: UIFont.fontAwesome(ofSize: 20, style: .brands)] + +// FontAwesome icon in button +button.titleLabel?.font = UIFont.fontAwesome(ofSize: 30, style: .brands) +button.setTitle(String.fontAwesomeIcon(name: .github), for: .normal) + +// FontAwesome icon as navigation bar item +barButton.setTitleTextAttributes(attributes, for: .normal) +barButton.title = String.fontAwesomeIcon(name: .github) + +// FontAwesome icon as toolbar item +toolbarItem.setTitleTextAttributes(attributes, for: .normal) +toolbarItem.title = String.fontAwesomeIcon(name: .github) + +// FontAwesome icon as image +imageView.image = UIImage.fontAwesomeIcon(name: .github, style: .brands, textColor: .black, size: CGSize(width: 4000, height: 4000)) + +// FontAwesome icon as image with background color +imageViewColored.image = UIImage.fontAwesomeIcon(name: .github, style: .brands, textColor: .white, size: CGSize(width: 4000, height: 4000), backgroundColor: .black) +``` + +## Requirements + +iOS 8 or later. + +## Development +To update this project to include all the latest icons from the new verison of +Font Awesome (replace `x.y.z` with the new font version): + + bundle exec fastlane update_font version:x.y.z + +To release a new version `x.y.z` (replace `x.y.z` with a real version number): + + bundle exec fastlane release version:x.y.z + +Since it will automatically make a new commit to bump version and push to CocoaPods +trunk, make sure you have write access to this repo and be one of the podspec owners. +If you are a maintainer but don't have these privilege yet, please let me know. + +## License +- All font files licensed under [SIL OFL 1.1](http://scripts.sil.org/OFL) +- FontAwesome.swift licensed under [MIT](http://thi.mit-license.org/) diff --git a/Pods/KYCircularProgress/LICENSE b/Pods/KYCircularProgress/LICENSE new file mode 100644 index 0000000..a3ceee5 --- /dev/null +++ b/Pods/KYCircularProgress/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014 Kengo YOKOYAMA + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/Pods/KYCircularProgress/README.md b/Pods/KYCircularProgress/README.md new file mode 100644 index 0000000..699514d --- /dev/null +++ b/Pods/KYCircularProgress/README.md @@ -0,0 +1,120 @@ +KYCircularProgress +================== + +[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/KYCircularProgress.svg)](https://img.shields.io/cocoapods/v/KYCircularProgress.svg) +[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![License](http://img.shields.io/badge/license-MIT-lightgrey.svg?style=flat +)](http://mit-license.org) +[![Platform](http://img.shields.io/badge/platform-ios-blue.svg?style=flat +)](https://developer.apple.com/iphone/index.action) +[![Language](http://img.shields.io/badge/language-swift-brightgreen.svg?style=flat +)](https://developer.apple.com/swift) +[![Percentage of issues still open](http://isitmaintained.com/badge/open/kentya6/KYCircularProgress.svg)](http://isitmaintained.com/project/kentya6/KYCircularProgress "Percentage of issues still open") +[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/kentya6/KYCircularProgress.svg)](http://isitmaintained.com/project/kentya6/KYCircularProgress "Average time to resolve an issue") + +Flexible progress bar written in Swift. + +## Features +- [x] Gradation Color +- [x] Progress Closure +- [x] UIBezierPath Progress Bar +- [x] Progress Gauge Guide +- [x] Customizable on Storyboard +- [x] Progress Change Animation + +## Demo +

+ +

+ +## Requirement +- Swift4 + +## Usage +#### Create KYCircularProgress +```swift +// create KYCircularProgress +let circularProgress = KYCircularProgress(frame: view.bounds) + +// create KYCircularProgress with gauge guide +let circularProgress = KYCircularProgress(frame: view.bounds, showGuide: true) +``` + +#### Gradation Color +```swift +// support Hex color to RGBA color +circularProgress.colors = [UIColor(rgba: 0xA6E39D11), UIColor(rgba: 0xAEC1E355), UIColor(rgba: 0xAEC1E3AA), UIColor(rgba: 0xF3C0ABFF)] + +// combine Hex color and UIColor +circularProgress.colors = [.purple, UIColor(rgba: 0xFFF77A55), .orange] +``` + +#### Progress Closure +```swift +circularProgress.progressChanged { + (progress: Double, circularProgress: KYCircularProgress) in + print("progress: \(progress)") +} +``` + +#### UIBezierPath Progress Bar +```swift +// create "Star progress bar" +let path = UIBezierPath() +path.move(to: CGPoint(x: 50.0, y: 2.0)) +path.addLine(to: CGPoint(x: 84.0, y: 86.0)) +path.addLine(to: CGPoint(x: 6.0, y: 33.0)) +path.addLine(to: CGPoint(x: 96.0, y: 33.0)) +path.addLine(to: CGPoint(x: 17.0, y: 86.0)) +path.close() +starProgress.path = path +``` + +## Installation +#### CocoaPods +[CocoaPods](https://cocoapods.org) is a dependency manager for Cocoa projects. + +To integrate KYCircularProgress into your Xcode project using CocoaPods, specify it in your `podfile`: + +``` +source 'https://github.com/CocoaPods/Specs.git' +platform :ios, '8.0' +use_frameworks! + +pod 'KYCircularProgress' +``` + +Then, run the following command: + +``` +$ pod install +``` + +#### Carthage (iOS 8+) +[Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that automates the process of adding frameworks to your Cocoa application. + +You can install Carthage with [Homebrew](http://brew.sh/) using the following command: + +```bash +$ brew update +$ brew install carthage +``` + +To integrate KYCircularProgress into your Xcode project using Carthage, specify it in your `Cartfile`: + +```ogdl +github "kentya6/KYCircularProgress" >= 1.2.0 +``` + +#### Manually +Add `KYCircularProgress.swift` into your Xcode project. + +## Licence + +The MIT License (MIT) + +Copyright (c) 2014-2018 Kengo YOKOYAMA + +## Author + +[kentya6](https://github.com/kentya6) diff --git a/Pods/KYCircularProgress/Source/KYCircularProgress.swift b/Pods/KYCircularProgress/Source/KYCircularProgress.swift new file mode 100644 index 0000000..373acc3 --- /dev/null +++ b/Pods/KYCircularProgress/Source/KYCircularProgress.swift @@ -0,0 +1,337 @@ +// KYCircularProgress.swift +// +// Copyright (c) 2014-2018 Kengo Yokoyama. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import UIKit + +// MARK: - KYCircularProgress +@IBDesignable +open class KYCircularProgress: UIView { + + /** + Current progress value. (0.0 - 1.0) + */ + @IBInspectable open var progress: Double = 0.0 { + didSet { + let clipProgress = max( min( progress, 1.0), 0.0 ) + progressView.update(progress: normalize(progress: clipProgress)) + + progressChanged?(clipProgress, self) + delegate?.progressChanged(progress: clipProgress, circularProgress: self) + } + } + + /** + Main progress line width. + */ + @IBInspectable open var lineWidth: Double = 8.0 { + didSet { + progressView.shapeLayer.lineWidth = CGFloat(lineWidth) + } + } + + /** + Progress bar line cap. The cap style used when stroking the path. + */ + @IBInspectable open var lineCap: String = kCALineCapButt { + didSet { + progressView.shapeLayer.lineCap = lineCap + } + } + + /** + Guide progress line width. + */ + @IBInspectable open var guideLineWidth: Double = 8.0 { + didSet { + guideView.shapeLayer.lineWidth = CGFloat(guideLineWidth) + } + } + + /** + Progress guide bar color. + */ + @IBInspectable open var guideColor: UIColor = UIColor(red: 0.1, green: 0.1, blue: 0.1, alpha: 0.2) { + didSet { + guideLayer.backgroundColor = guideColor.cgColor + } + } + + /** + Switch of progress guide view. If you set to `true`, progress guide view is enabled. + */ + @IBInspectable open var showGuide: Bool = false { + didSet { + guideView.isHidden = !showGuide + guideLayer.backgroundColor = showGuide ? guideColor.cgColor : UIColor.clear.cgColor + } + } + + /** + Progress bar path. You can create various type of progress bar. + */ + open var path: UIBezierPath? { + didSet { + progressView.shapeLayer.path = path?.cgPath + guideView.shapeLayer.path = path?.cgPath + } + } + + /** + Progress bar colors. You can set many colors in `colors` property, and it makes gradation color in `colors`. + */ + open var colors: [UIColor] = [UIColor(rgba: 0x9ACDE7FF), UIColor(rgba: 0xE7A5C9FF)] { + didSet { + update(colors: colors) + } + } + + /** + Progress start offset. (0.0 - 1.0) + */ + @IBInspectable open var strokeStart: Double = 0.0 { + didSet { + progressView.shapeLayer.strokeStart = CGFloat(max( min(strokeStart, 1.0), 0.0 )) + guideView.shapeLayer.strokeStart = CGFloat(max( min(strokeStart, 1.0), 0.0 )) + } + } + + /** + Progress end offset. (0.0 - 1.0) + */ + @IBInspectable open var strokeEnd: Double = 1.0 { + didSet { + progressView.shapeLayer.strokeEnd = CGFloat(max( min(strokeEnd, 1.0), 0.0 )) + guideView.shapeLayer.strokeEnd = CGFloat(max( min(strokeEnd, 1.0), 0.0 )) + } + } + + open var delegate: KYCircularProgressDelegate? + + /** + Typealias of progressChangedClosure. + */ + public typealias progressChangedHandler = (_ progress: Double, _ circularProgress: KYCircularProgress) -> Void + + /** + This closure is called when set value to `progress` property. + */ + private var progressChanged: progressChangedHandler? + + /** + Main progress view. + */ + private lazy var progressView: KYCircularShapeView = { + let progressView = KYCircularShapeView(frame: self.bounds) + progressView.shapeLayer.fillColor = UIColor.clear.cgColor + progressView.shapeLayer.lineWidth = CGFloat(self.lineWidth) + progressView.shapeLayer.lineCap = self.lineCap + progressView.radius = self.radius + progressView.shapeLayer.path = self.path?.cgPath + progressView.shapeLayer.strokeColor = self.tintColor.cgColor + return progressView + }() + + /** + Gradient mask layer of `progressView`. + */ + private lazy var progressLayer: CAGradientLayer = { + let progressLayer = CAGradientLayer(layer: self.layer) + progressLayer.frame = self.progressView.frame + progressLayer.startPoint = CGPoint(x: 0, y: 0.5) + progressLayer.endPoint = CGPoint(x: 1, y: 0.5) + progressLayer.mask = self.progressView.shapeLayer + progressLayer.colors = self.colors + self.layer.addSublayer(progressLayer) + return progressLayer + }() + + /** + Guide view of `progressView`. + */ + private lazy var guideView: KYCircularShapeView = { + let guideView = KYCircularShapeView(frame: self.bounds) + guideView.shapeLayer.fillColor = UIColor.clear.cgColor + guideView.shapeLayer.lineWidth = CGFloat(self.guideLineWidth) + guideView.radius = self.radius + self.progressView.radius = self.radius + guideView.shapeLayer.path = self.progressView.shapeLayer.path + guideView.shapeLayer.strokeColor = self.tintColor.cgColor + guideView.update(progress: normalize(progress: 1.0)) + return guideView + }() + + /** + Mask layer of `progressGuideView`. + */ + private lazy var guideLayer: CALayer = { + let guideLayer = CAGradientLayer(layer: self.layer) + guideLayer.frame = self.guideView.frame + guideLayer.mask = self.guideView.shapeLayer + guideLayer.backgroundColor = self.guideColor.cgColor + guideLayer.zPosition = -1 + self.layer.addSublayer(guideLayer) + return guideLayer + }() + + private var radius: Double { + return lineWidth >= guideLineWidth ? lineWidth : guideLineWidth + } + + required public init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + + setNeedsLayout() + layoutIfNeeded() + + update(colors: colors) + } + + public override init(frame: CGRect) { + super.init(frame: frame) + + setNeedsLayout() + layoutIfNeeded() + } + + /** + Create `KYCircularProgress` with progress guide. + + - parameter frame: `KYCircularProgress` frame. + - parameter showProgressGuide: If you set to `true`, progress guide view is enabled. + */ + public init(frame: CGRect, showGuide: Bool) { + super.init(frame: frame) + self.showGuide = showGuide + guideLayer.backgroundColor = showGuide ? guideColor.cgColor : UIColor.clear.cgColor + } + + /** + This closure is called when set value to `progress` property. + + - parameter completion: progress changed closure. + */ + open func progressChanged(completion: @escaping progressChangedHandler) { + progressChanged = completion + } + + public func set(progress: Double, duration: Double) { + let clipProgress = max( min(progress, 1.0), 0.0 ) + progressView.update(progress: normalize(progress: clipProgress), duration: duration) + + progressChanged?(clipProgress, self) + delegate?.progressChanged(progress: clipProgress, circularProgress: self) + } + + private func update(colors: [UIColor]) { + progressLayer.colors = colors.map {$0.cgColor} + if colors.count == 1 { + progressLayer.colors?.append(colors.first!.cgColor) + } + } + + private func normalize(progress: Double) -> CGFloat { + return CGFloat(strokeStart + progress * (strokeEnd - strokeStart)) + } + + override open func layoutSubviews() { + super.layoutSubviews() + + let lineHalf = CGFloat(lineWidth / 2) + progressView.scale = (x: (bounds.width - lineHalf) / progressView.frame.width, y: (bounds.height - lineHalf) / progressView.frame.height) + progressView.frame = CGRect(x: bounds.origin.x + lineHalf, y: bounds.origin.y + lineHalf, width: bounds.width - lineHalf, height: bounds.height - lineHalf) + progressLayer.frame = bounds + guideView.scale = progressView.scale + guideView.frame = progressView.frame + guideLayer.frame = bounds + } +} + +public protocol KYCircularProgressDelegate { + func progressChanged(progress: Double, circularProgress: KYCircularProgress) +} + +// MARK: - KYCircularShapeView +class KYCircularShapeView: UIView { + var radius = 0.0 + var scale: (x: CGFloat, y: CGFloat) = (1.0, 1.0) + + override class var layerClass : AnyClass { + return CAShapeLayer.self + } + + var shapeLayer: CAShapeLayer { + return layer as! CAShapeLayer + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + } + + override init(frame: CGRect) { + super.init(frame: frame) + update(progress: 0) + } + + override func layoutSubviews() { + super.layoutSubviews() + + shapeLayer.path = shapeLayer.path ?? layoutPath().cgPath + var affineScale = CGAffineTransform(scaleX: scale.x, y: scale.y) + shapeLayer.path = shapeLayer.path?.copy(using: &affineScale) + } + + private func layoutPath() -> UIBezierPath { + let halfWidth = CGFloat(frame.width / 2.0) + return UIBezierPath(arcCenter: CGPoint(x: halfWidth, y: halfWidth), radius: (frame.width - CGFloat(radius)) / 2, startAngle: 0, endAngle: CGFloat(Double.pi * 2), clockwise: true) + } + + fileprivate func update(progress: CGFloat) { + CATransaction.begin() + CATransaction.setValue(kCFBooleanTrue, forKey: kCATransactionDisableActions) + shapeLayer.strokeEnd = progress + CATransaction.commit() + } + + fileprivate func update(progress: CGFloat, duration: Double) { + CATransaction.begin() + let animation = CABasicAnimation(keyPath: "strokeEnd") + animation.duration = duration + animation.isRemovedOnCompletion = false + animation.fromValue = shapeLayer.presentation()?.value(forKeyPath: "strokeEnd") as? CGFloat + animation.toValue = progress + shapeLayer.add(animation, forKey: "animateStrokeEnd") + CATransaction.commit() + shapeLayer.strokeEnd = progress + } +} + +// MARK: - UIColor Extension +extension UIColor { + convenience public init(rgba: Int64) { + let red = CGFloat((rgba & 0xFF000000) >> 24) / 255.0 + let green = CGFloat((rgba & 0x00FF0000) >> 16) / 255.0 + let blue = CGFloat((rgba & 0x0000FF00) >> 8) / 255.0 + let alpha = CGFloat( rgba & 0x000000FF) / 255.0 + + self.init(red: red, green: green, blue: blue, alpha: alpha) + } +} diff --git a/Pods/Manifest.lock b/Pods/Manifest.lock new file mode 100644 index 0000000..af0cbef --- /dev/null +++ b/Pods/Manifest.lock @@ -0,0 +1,40 @@ +PODS: + - FontAwesome.swift (1.4.4) + - KYCircularProgress (1.2.0) + - paper-onboarding (4.1.0) + - SCLAlertView (0.8) + - SwiftyJSON (4.1.0) + - SwiftySound (1.0.0) + - SwiftyStoreKit (0.13.3) + +DEPENDENCIES: + - FontAwesome.swift + - KYCircularProgress + - paper-onboarding + - SCLAlertView + - SwiftyJSON + - SwiftySound + - SwiftyStoreKit + +SPEC REPOS: + https://github.com/CocoaPods/Specs.git: + - FontAwesome.swift + - KYCircularProgress + - paper-onboarding + - SCLAlertView + - SwiftyJSON + - SwiftySound + - SwiftyStoreKit + +SPEC CHECKSUMS: + FontAwesome.swift: 17b93524b9f61136a8566344517b8556ed584e87 + KYCircularProgress: b289be602d7c7cde5b4c88f84738231776f3557d + paper-onboarding: 344780cbb0bfc229cd1862f5b9bba446b23c5185 + SCLAlertView: 6a77bb2edfc65e04dbe57725546cb4107a506b85 + SwiftyJSON: c29297daf073d2aa016295d5809cdd68045c39b3 + SwiftySound: b9c3370956fd4ed65ed79d45563070077a50a2cd + SwiftyStoreKit: 9ebd15971e28aa2989825fe8bc092eb5f75dd0b2 + +PODFILE CHECKSUM: d4d372e61d40adccb8596f5a21862d95e0442449 + +COCOAPODS: 1.5.0 diff --git a/Pods/Pods.xcodeproj/project.pbxproj b/Pods/Pods.xcodeproj/project.pbxproj new file mode 100644 index 0000000..9de4843 --- /dev/null +++ b/Pods/Pods.xcodeproj/project.pbxproj @@ -0,0 +1,1869 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 011993838523DB2203CEE840275B8F0E /* SwiftySound-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = FC1A213285E82ABB96628D6093E3A9CB /* SwiftySound-dummy.m */; }; + 01DBE6E7AAFBF3366F330B105CCB8287 /* KYCircularProgress-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 73FD990BD005F2D8C14A4D987C54E658 /* KYCircularProgress-dummy.m */; }; + 08DFB59557320FA598DF54FAC82F25FB /* SKProduct+LocalizedPrice.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABEA88B78122F134BDAB967120FDEEF8 /* SKProduct+LocalizedPrice.swift */; }; + 0B2116700FA0CFFB7FB4903C53230FED /* Font Awesome 5 Brands-Regular-400.otf in Resources */ = {isa = PBXBuildFile; fileRef = 6F61E34A94080D1B9FB9941CA76CD86E /* Font Awesome 5 Brands-Regular-400.otf */; }; + 0FE9C1DB0A04041F2D6D572B7B519367 /* FontAwesomeTabBarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 11B1E90A4E038798FE83CB79B7FBEFCA /* FontAwesomeTabBarItem.swift */; }; + 0FF663F919A014E6DE08F3726ED3D26F /* FontAwesomeStateRequirement.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F38ECCF95EC9559CE7FF6510865D017 /* FontAwesomeStateRequirement.swift */; }; + 10BD6D329B02C3275995F12648C56B90 /* PageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86BFDADD4611FD0FAFA4B92B83F371D9 /* PageView.swift */; }; + 1759D0E7AD6E8B6AA04353E8E4E758EC /* ProductsInfoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1074D6D0D5ACA8F07F24F80F162A97FA /* ProductsInfoController.swift */; }; + 2B476160A1663094CDFBDC35DC9EFD32 /* FontAwesome.swift.bundle in Resources */ = {isa = PBXBuildFile; fileRef = B13926D511F3248452E4EADB70FEE0AD /* FontAwesome.swift.bundle */; }; + 2D2DD9882ED57FA4A43A5912F7325970 /* FontAwesomeImageRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1566F0B04D5DF9BFC5FF8FB0C4E61195 /* FontAwesomeImageRepresentable.swift */; }; + 2D30889B4E1FC09CB6FDB15B96AD30E9 /* GestureControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 380273F3390BA067FCD5F916AD1A0F64 /* GestureControl.swift */; }; + 2F69F75A30AFC330E110ACE9D3CAB4F8 /* SCLAlertView-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E95FAECD64766FECC3E575D28BBB558 /* SCLAlertView-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 304B20D30A477EC0DA0D7E988BEBE699 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88DB1BB41A8FDB246D9D6EFEDEB35D6B /* Foundation.framework */; }; + 31514774FEEC7F179F887E6F1CC84857 /* Font Awesome 5 Free-Solid-900.otf in Resources */ = {isa = PBXBuildFile; fileRef = 3074FCD5A2C4A59DCCDC962475065793 /* Font Awesome 5 Free-Solid-900.otf */; }; + 3346ABD8DBFC61551BA6DD0AEC5CB9DE /* PageContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2287D76C382E623A11AC0D753EC6C546 /* PageContainer.swift */; }; + 409BCBFBF2161753E6B85649EF6633C5 /* OnboardingContantViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 51E33BFD19F5B262846B8CC70998DE24 /* OnboardingContantViewItem.swift */; }; + 4242F5D61F5CE9E8FE109FF1E6CD0215 /* OS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 99B4E0D33DAA6D7B3F2F72F151235F36 /* OS.swift */; }; + 424F3651FB734F6AE3AE1CF84980D23B /* RestorePurchasesController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F0B593B75EB155F6B87194D5A34A7D9 /* RestorePurchasesController.swift */; }; + 454FEFC1DAC2B37EB656BE133CD84CD4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88DB1BB41A8FDB246D9D6EFEDEB35D6B /* Foundation.framework */; }; + 46D033163F2C11E62AE79ACA3AFB0394 /* SwiftyStoreKit-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 0505CDAEE4D660728AE00619FF8713C0 /* SwiftyStoreKit-dummy.m */; }; + 47610629C7A7408DFCBE1EEA5DB0A7E3 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BDF5959A3B474869CA9A59BA22D9B1EA /* UIKit.framework */; }; + 48696FE82A4D8086055541E81DC94107 /* PageViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8CD5A35ABAA43271D29F14B8FA32F1E /* PageViewItem.swift */; }; + 4B7A91F57EE818D4A289E665CA99DD42 /* Pods-Meditation-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = C8E0ADDC21A0C51DD833D49074902B5A /* Pods-Meditation-dummy.m */; }; + 4F5FAA1BFB6846C4C80D881F3B9355A7 /* FillAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = ABE2B63EA097D934B83415BB91D2ADFF /* FillAnimationView.swift */; }; + 532B94DAC89623F895B0D5C1F04DB994 /* FontAwesomeSegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F7E814EC1BD3FE2CC6E421C0FC2F81B /* FontAwesomeSegmentedControl.swift */; }; + 545AE3C5E061CC97FB166DAF1C5143E8 /* KYCircularProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6212DD845AE5E286E958AE561AF8085D /* KYCircularProgress.swift */; }; + 59EF401D011A3D635E87E3E3F1D1523B /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88DB1BB41A8FDB246D9D6EFEDEB35D6B /* Foundation.framework */; }; + 5A1AE014B6FF9AC20A3402A7B05BA7E5 /* OnboardingContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE98872E716424595BD3CF1FA1122F09 /* OnboardingContentView.swift */; }; + 5CF29744A4F25FABBF17B60C983F2A12 /* KYCircularProgress-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 70D737D74E126F7D3995477EF0C95082 /* KYCircularProgress-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 5F277F3427F528CCF898259A48934211 /* SwiftyJSON-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 8DAF7FE629C474507D924430E46E508D /* SwiftyJSON-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 608E5DEF6E46FDD42D1AAD8A979ECA2C /* AppleReceiptValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF2A125CF3AE1560DA12F7C5EFA282B /* AppleReceiptValidator.swift */; }; + 60BD3C93AC4A20EB0F10272EF740A7B1 /* InAppProductQueryRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EC01ABC7DE24A048434D8FE49C99024 /* InAppProductQueryRequest.swift */; }; + 61430A34EB37246ABC3F8E8E29806F38 /* Configuring.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1552D81E377BC74C3F717366FB8ADF71 /* Configuring.swift */; }; + 63443D77C6E8695AB0E1324CAF15EEDA /* Sound.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46702EF5E19E75295B39C98306EDC38E /* Sound.swift */; }; + 64C8A1224EF3FD9FA3E7B014D3A6C68C /* InAppReceipt.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4FE23D7AC76377481342D0ECDDE9E2E /* InAppReceipt.swift */; }; + 66D2E16A248430B979133176DCC3B109 /* PaperOnboardingDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6188F7B3385AF43E2D11CFCD4172663E /* PaperOnboardingDelegate.swift */; }; + 6B5413138C15627AB31211A88A063709 /* FontAwesomeTextRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9DFAF573EF0882F6952891C3B7555D1 /* FontAwesomeTextRepresentable.swift */; }; + 6EC40D7BADA054B926209056DFBFE316 /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7D34E9BE62EC54EFD569B0F613DD5CDB /* CoreText.framework */; }; + 6F9C78CE9BA836E9272D3D08F2EBD5AE /* FontAwesomeExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB42F083F242EA7414479200D160683F /* FontAwesomeExtension.swift */; }; + 711AFA1876EDB79EB2B1C97B1135D5F5 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88DB1BB41A8FDB246D9D6EFEDEB35D6B /* Foundation.framework */; }; + 72B67D26F3E1A6160E56B43945C301DB /* SwiftyJSON-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 934FF2D307444AB387E9B60E447F224B /* SwiftyJSON-dummy.m */; }; + 7455C21A8CDE7750973B1EA649305312 /* paper-onboarding-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C47D3C2A396B7D53EA56B97907E416F /* paper-onboarding-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 7C214903E97437C9DE70A5F4C0B56041 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88DB1BB41A8FDB246D9D6EFEDEB35D6B /* Foundation.framework */; }; + 828E84A639F7F74F9C871F82A4E252B9 /* Enum.swift in Sources */ = {isa = PBXBuildFile; fileRef = E79D3EFBBDB3CEA80F40A805053859D0 /* Enum.swift */; }; + 8482EEDD4DE819F389D4628C63899E5E /* SwiftyStoreKit-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = C1E327891637CA046A60DFA49A7B0571 /* SwiftyStoreKit-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 85EE7A0CB68505A4ABBC14C8B528EABB /* ConstraintsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DA3D80CAE3E9FA539202B7C596317F2 /* ConstraintsHelper.swift */; }; + 89F604C5071756146A5C682E03897724 /* FontAwesomeBarButtonItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48C4081AC199AFAD5B060440DB7973F8 /* FontAwesomeBarButtonItem.swift */; }; + 8A1EE22D27DC4FEA41A11AC1F20B3478 /* PaymentQueueController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32CC4AB09CE92B8F8D971175B8174700 /* PaymentQueueController.swift */; }; + 91F13A71F646FE645B075554061D2A5E /* SCLAlertView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0711951D5871D0C685A7DB958EB33F0 /* SCLAlertView.swift */; }; + 93929E495F12F2702B2522EE518586C5 /* FontAwesomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6943603CA944789F663A70DB87BB2FB7 /* FontAwesomeView.swift */; }; + 9419ACA668E00FFC1F70AA23359DF3E5 /* FontAwesome.swift in Sources */ = {isa = PBXBuildFile; fileRef = E68C8303228EFF646C999DFAD59444ED /* FontAwesome.swift */; }; + A3088DBACC68068F17A9B20A8F442ACA /* Font Awesome 5 Free-Regular-400.otf in Resources */ = {isa = PBXBuildFile; fileRef = 7A891C3C4705897389A4816FBB4F7EC5 /* Font Awesome 5 Free-Regular-400.otf */; }; + A6995F65FD29E381359809E39D7D8649 /* Pods-Meditation-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = F0E48D27BC892B55D06F4376805E2F1E /* Pods-Meditation-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + B035285D1DE27A46C68D12F26226F68E /* InAppReceiptRefreshRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BDA1E0D099BAA1C62F2B69DB6A4F1E0 /* InAppReceiptRefreshRequest.swift */; }; + B22CD5BAA233DD92D22269BAF31049B9 /* FontAwesomeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 98C5C3B5D7080A323CAB0616B82E50AE /* FontAwesomeImageView.swift */; }; + B57125DCD2436C8C82CF55D663EB46FC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88DB1BB41A8FDB246D9D6EFEDEB35D6B /* Foundation.framework */; }; + B633D850FB892345DDA1F0DB05213C58 /* FontAwesome.swift-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = FA028D5954DB3D648E530FCA7758398E /* FontAwesome.swift-dummy.m */; }; + BD1195A277722CA59284A9E84AF5F90D /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B0BA97DB46D5B969F46166BD5EDA801 /* AVFoundation.framework */; }; + C2B42FF9CBAB7677D9F6BF03E6C44F7E /* InAppReceiptVerificator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4ED34731AEE0CBDD1213E7A5C124D4A5 /* InAppReceiptVerificator.swift */; }; + C2D6F2BBEE478C78BA9C80354D38945C /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88DB1BB41A8FDB246D9D6EFEDEB35D6B /* Foundation.framework */; }; + CB11F359CD23BE2FFFF2370AC6F0C67A /* SwiftyStoreKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10A7FF702CE17217555AA92F4D15EC38 /* SwiftyStoreKit.swift */; }; + CCB18EFF51F3186398FD939995842880 /* PaymentsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D160819B2711AB32B17A3FD7446CC8DE /* PaymentsController.swift */; }; + CD0A71E316A3C77C88AE6C30C3562CB0 /* PaperOnboarding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EABDCF610BF913C3B37911CAFD951CF /* PaperOnboarding.swift */; }; + CDEAFF50FED59401B66C354798994E30 /* PaperOnboardingDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E7AF0B26E412772E1C4E54910E634CF /* PaperOnboardingDataSource.swift */; }; + D72A933FC1885E404D150260FDDAD3A6 /* SwiftyStoreKit+Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79CDB75218FA0BA5A3AC089366677150 /* SwiftyStoreKit+Types.swift */; }; + DC1474A8A5F9C27D852181B661944619 /* paper-onboarding-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 34F8EF59CC5913D4CF56859FD4545CDF /* paper-onboarding-dummy.m */; }; + DD296D6DF8AC3E1814624874847905C8 /* FontAwesome.swift-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = A03F612DDAC3DB361CC038D8BF86C1B5 /* FontAwesome.swift-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EB2A29005F96454DDE7C02A23ABE48DB /* SwiftySound-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 215B8471CB693EE9D6DE14EF2DB3CC84 /* SwiftySound-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + EB81ABE70D61966AED8A5827D2754019 /* SCLAlertView-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 0C4EEE1B950D8AD93F74381F2DF2E077 /* SCLAlertView-dummy.m */; }; + EC1529EC711090345F55EDA2ADC0CD14 /* SCLExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65B9114F61C5E19FFFD805D6C637845F /* SCLExtensions.swift */; }; + F7B77FAD4019B1F675A054B7643F4AFA /* SwiftyJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 72A67E12692EBB3CA940B613D94A0306 /* SwiftyJSON.swift */; }; + F7EDFF8ADB6D166B2289ABFA8D68F262 /* CompleteTransactionsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1843E7A0E184AC88368B22993D29E6 /* CompleteTransactionsController.swift */; }; + FFF5BA92707F567C4E4E7CB6A8FD4B70 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 88DB1BB41A8FDB246D9D6EFEDEB35D6B /* Foundation.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 113DCF3A426F8076F932F2160AED20F2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5C1F0BC8AD9DB9D282F6612636923786; + remoteInfo = KYCircularProgress; + }; + 11C46040B80D714B8D493D1A3B1820AC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = CB2EAB5C326E4BB9AE6483289BB7C828; + remoteInfo = "FontAwesome.swift-FontAwesome.swift"; + }; + 22E9F06E55C562186E2D693F08E4F232 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = B8B08C578828E3A292E3B948F8124CBA; + remoteInfo = SwiftyStoreKit; + }; + 23C0B2BD1058F519EA5061D43F0BA311 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 6C582F0467383C85FA2748503EBDD043; + remoteInfo = SCLAlertView; + }; + 392A0D952FE65BFA6F1AE65D2205EE42 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 08B04875F4404729D5274077AC117F66; + remoteInfo = SwiftyJSON; + }; + 5EE922725D9F386CD7495D29B6F08120 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 738B2050D3EE6CEF2F8FC5020EAB44A1; + remoteInfo = "paper-onboarding"; + }; + 8EAD1867DC5795F6C72A5BDA158E6BCD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = C53CF332138BDC1006E1342662A1F706; + remoteInfo = SwiftySound; + }; + 9105A9F7762DB59670EA1D2845DE7D6B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = 29124FF4E0F9AB7495AA7588B0A1A107; + remoteInfo = FontAwesome.swift; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 0505CDAEE4D660728AE00619FF8713C0 /* SwiftyStoreKit-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SwiftyStoreKit-dummy.m"; sourceTree = ""; }; + 074DCBFF16AA0E6E82CB2BE592E940FA /* Pods-Meditation.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Meditation.release.xcconfig"; sourceTree = ""; }; + 094DA2BA234D9C6AED781D71A5CA75D0 /* SwiftySound-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftySound-prefix.pch"; sourceTree = ""; }; + 0AAD23A6E6A9A4B374E69535DC560E15 /* SwiftyJSON.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = SwiftyJSON.framework; path = SwiftyJSON.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 0C4EEE1B950D8AD93F74381F2DF2E077 /* SCLAlertView-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SCLAlertView-dummy.m"; sourceTree = ""; }; + 1074D6D0D5ACA8F07F24F80F162A97FA /* ProductsInfoController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ProductsInfoController.swift; path = SwiftyStoreKit/ProductsInfoController.swift; sourceTree = ""; }; + 10A7FF702CE17217555AA92F4D15EC38 /* SwiftyStoreKit.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwiftyStoreKit.swift; path = SwiftyStoreKit/SwiftyStoreKit.swift; sourceTree = ""; }; + 11B1E90A4E038798FE83CB79B7FBEFCA /* FontAwesomeTabBarItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FontAwesomeTabBarItem.swift; path = FontAwesome/FontAwesomeTabBarItem.swift; sourceTree = ""; }; + 1552D81E377BC74C3F717366FB8ADF71 /* Configuring.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Configuring.swift; path = Source/Helpers/Configuring.swift; sourceTree = ""; }; + 1566F0B04D5DF9BFC5FF8FB0C4E61195 /* FontAwesomeImageRepresentable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FontAwesomeImageRepresentable.swift; path = FontAwesome/FontAwesomeImageRepresentable.swift; sourceTree = ""; }; + 1E3E5997C5E6F420353511A9D8E53744 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 1EF3E5A3FD9A981EA5FB772BDAF5CC9A /* KYCircularProgress-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "KYCircularProgress-prefix.pch"; sourceTree = ""; }; + 2137245788B5F2143661700B732D51B4 /* FontAwesome.swift.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = FontAwesome.swift.xcconfig; sourceTree = ""; }; + 215B8471CB693EE9D6DE14EF2DB3CC84 /* SwiftySound-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftySound-umbrella.h"; sourceTree = ""; }; + 2287D76C382E623A11AC0D753EC6C546 /* PageContainer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PageContainer.swift; path = Source/PageView/PageContainerView/PageContainer.swift; sourceTree = ""; }; + 27E0527C0B227763266EA736BCE23366 /* paper-onboarding.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "paper-onboarding.modulemap"; sourceTree = ""; }; + 2E7AF0B26E412772E1C4E54910E634CF /* PaperOnboardingDataSource.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PaperOnboardingDataSource.swift; path = Source/PaperOnboardingDataSource.swift; sourceTree = ""; }; + 3074FCD5A2C4A59DCCDC962475065793 /* Font Awesome 5 Free-Solid-900.otf */ = {isa = PBXFileReference; includeInIndex = 1; name = "Font Awesome 5 Free-Solid-900.otf"; path = "FontAwesome/Font Awesome 5 Free-Solid-900.otf"; sourceTree = ""; }; + 32CC4AB09CE92B8F8D971175B8174700 /* PaymentQueueController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PaymentQueueController.swift; path = SwiftyStoreKit/PaymentQueueController.swift; sourceTree = ""; }; + 34F8EF59CC5913D4CF56859FD4545CDF /* paper-onboarding-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "paper-onboarding-dummy.m"; sourceTree = ""; }; + 380273F3390BA067FCD5F916AD1A0F64 /* GestureControl.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GestureControl.swift; path = Source/GestureControl/GestureControl.swift; sourceTree = ""; }; + 3C47D3C2A396B7D53EA56B97907E416F /* paper-onboarding-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "paper-onboarding-umbrella.h"; sourceTree = ""; }; + 3E3C5A5633558E34FB16ACD91B84058F /* SCLAlertView.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SCLAlertView.xcconfig; sourceTree = ""; }; + 45113075C07B3B3CCBA21D2BD676651D /* KYCircularProgress.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = KYCircularProgress.xcconfig; sourceTree = ""; }; + 46702EF5E19E75295B39C98306EDC38E /* Sound.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Sound.swift; path = Sources/Sound.swift; sourceTree = ""; }; + 47D07AE48ABDE4AA65609E9154037202 /* Pods-Meditation-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Meditation-frameworks.sh"; sourceTree = ""; }; + 48C4081AC199AFAD5B060440DB7973F8 /* FontAwesomeBarButtonItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FontAwesomeBarButtonItem.swift; path = FontAwesome/FontAwesomeBarButtonItem.swift; sourceTree = ""; }; + 4AC9E90081FE71E63EBDFE4C00C4A41B /* FontAwesome.swift-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "FontAwesome.swift-prefix.pch"; sourceTree = ""; }; + 4BDC34BFC35F326C807B33DC113C8A38 /* SCLAlertView.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = SCLAlertView.framework; path = SCLAlertView.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4DA3D80CAE3E9FA539202B7C596317F2 /* ConstraintsHelper.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ConstraintsHelper.swift; path = Source/Helpers/ConstraintsHelper.swift; sourceTree = ""; }; + 4E8A935F4E73C5D023E63F2AC854FFA7 /* SwiftyStoreKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = SwiftyStoreKit.framework; path = SwiftyStoreKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4ED34731AEE0CBDD1213E7A5C124D4A5 /* InAppReceiptVerificator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = InAppReceiptVerificator.swift; path = SwiftyStoreKit/InAppReceiptVerificator.swift; sourceTree = ""; }; + 51E33BFD19F5B262846B8CC70998DE24 /* OnboardingContantViewItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OnboardingContantViewItem.swift; path = Source/OnboardingContentView/Item/OnboardingContantViewItem.swift; sourceTree = ""; }; + 5303D316EA88E89FD5E7C01663744AD6 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 534F10FCD1A82884CCE9C2CDD2A9749C /* SwiftyJSON.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = SwiftyJSON.modulemap; sourceTree = ""; }; + 54BB495848699394FDC237D05100785C /* Pods_Meditation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_Meditation.framework; path = "Pods-Meditation.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + 5721C93FB5FDBD06B2AAD2E1330DFDD8 /* SwiftySound.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftySound.xcconfig; sourceTree = ""; }; + 5982A12A45C5063F93AFED3C4B7E9C28 /* ResourceBundle-FontAwesome.swift-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "ResourceBundle-FontAwesome.swift-Info.plist"; sourceTree = ""; }; + 5ABB8BF6F0BA842FDDF3174DFEC7A715 /* FontAwesome.swift.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = FontAwesome.swift.modulemap; sourceTree = ""; }; + 5F38ECCF95EC9559CE7FF6510865D017 /* FontAwesomeStateRequirement.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FontAwesomeStateRequirement.swift; path = FontAwesome/FontAwesomeStateRequirement.swift; sourceTree = ""; }; + 5F7E814EC1BD3FE2CC6E421C0FC2F81B /* FontAwesomeSegmentedControl.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FontAwesomeSegmentedControl.swift; path = FontAwesome/FontAwesomeSegmentedControl.swift; sourceTree = ""; }; + 6188F7B3385AF43E2D11CFCD4172663E /* PaperOnboardingDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PaperOnboardingDelegate.swift; path = Source/PaperOnboardingDelegate.swift; sourceTree = ""; }; + 6212DD845AE5E286E958AE561AF8085D /* KYCircularProgress.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KYCircularProgress.swift; path = Source/KYCircularProgress.swift; sourceTree = ""; }; + 65B9114F61C5E19FFFD805D6C637845F /* SCLExtensions.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SCLExtensions.swift; path = SCLAlertView/SCLExtensions.swift; sourceTree = ""; }; + 6943603CA944789F663A70DB87BB2FB7 /* FontAwesomeView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FontAwesomeView.swift; path = FontAwesome/FontAwesomeView.swift; sourceTree = ""; }; + 6CF2A125CF3AE1560DA12F7C5EFA282B /* AppleReceiptValidator.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = AppleReceiptValidator.swift; path = SwiftyStoreKit/AppleReceiptValidator.swift; sourceTree = ""; }; + 6D67B98A9A1FDE891A24ACBBB6DB7C0B /* SwiftyJSON-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyJSON-prefix.pch"; sourceTree = ""; }; + 6EC01ABC7DE24A048434D8FE49C99024 /* InAppProductQueryRequest.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = InAppProductQueryRequest.swift; path = SwiftyStoreKit/InAppProductQueryRequest.swift; sourceTree = ""; }; + 6F0B593B75EB155F6B87194D5A34A7D9 /* RestorePurchasesController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RestorePurchasesController.swift; path = SwiftyStoreKit/RestorePurchasesController.swift; sourceTree = ""; }; + 6F61E34A94080D1B9FB9941CA76CD86E /* Font Awesome 5 Brands-Regular-400.otf */ = {isa = PBXFileReference; includeInIndex = 1; name = "Font Awesome 5 Brands-Regular-400.otf"; path = "FontAwesome/Font Awesome 5 Brands-Regular-400.otf"; sourceTree = ""; }; + 70D737D74E126F7D3995477EF0C95082 /* KYCircularProgress-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "KYCircularProgress-umbrella.h"; sourceTree = ""; }; + 729BBB5672097165784C9A456B255191 /* Pods-Meditation.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-Meditation.modulemap"; sourceTree = ""; }; + 72A67E12692EBB3CA940B613D94A0306 /* SwiftyJSON.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwiftyJSON.swift; path = Source/SwiftyJSON.swift; sourceTree = ""; }; + 73FD990BD005F2D8C14A4D987C54E658 /* KYCircularProgress-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "KYCircularProgress-dummy.m"; sourceTree = ""; }; + 79CDB75218FA0BA5A3AC089366677150 /* SwiftyStoreKit+Types.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "SwiftyStoreKit+Types.swift"; path = "SwiftyStoreKit/SwiftyStoreKit+Types.swift"; sourceTree = ""; }; + 7A891C3C4705897389A4816FBB4F7EC5 /* Font Awesome 5 Free-Regular-400.otf */ = {isa = PBXFileReference; includeInIndex = 1; name = "Font Awesome 5 Free-Regular-400.otf"; path = "FontAwesome/Font Awesome 5 Free-Regular-400.otf"; sourceTree = ""; }; + 7AED5452724E79D3534F1E4DD674BEFC /* Pods-Meditation-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-Meditation-acknowledgements.markdown"; sourceTree = ""; }; + 7B0BA97DB46D5B969F46166BD5EDA801 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/AVFoundation.framework; sourceTree = DEVELOPER_DIR; }; + 7D34E9BE62EC54EFD569B0F613DD5CDB /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/CoreText.framework; sourceTree = DEVELOPER_DIR; }; + 84E615D9EDF47C12EF271062ABACED76 /* SwiftyJSON.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftyJSON.xcconfig; sourceTree = ""; }; + 86BFDADD4611FD0FAFA4B92B83F371D9 /* PageView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PageView.swift; path = Source/PageView/PageView.swift; sourceTree = ""; }; + 88DB1BB41A8FDB246D9D6EFEDEB35D6B /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + 8BDA1E0D099BAA1C62F2B69DB6A4F1E0 /* InAppReceiptRefreshRequest.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = InAppReceiptRefreshRequest.swift; path = SwiftyStoreKit/InAppReceiptRefreshRequest.swift; sourceTree = ""; }; + 8DAF7FE629C474507D924430E46E508D /* SwiftyJSON-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyJSON-umbrella.h"; sourceTree = ""; }; + 8E95FAECD64766FECC3E575D28BBB558 /* SCLAlertView-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SCLAlertView-umbrella.h"; sourceTree = ""; }; + 934FF2D307444AB387E9B60E447F224B /* SwiftyJSON-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SwiftyJSON-dummy.m"; sourceTree = ""; }; + 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 93C7C6B66482C1871F8D0A5289CA80F5 /* SCLAlertView-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SCLAlertView-prefix.pch"; sourceTree = ""; }; + 9492DF1BC5814CE0D3A484387EBD986E /* paper_onboarding.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = paper_onboarding.framework; path = "paper-onboarding.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + 94C15723EF3F61C5564AADC1F3930E74 /* SwiftyStoreKit.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = SwiftyStoreKit.modulemap; sourceTree = ""; }; + 98C5C3B5D7080A323CAB0616B82E50AE /* FontAwesomeImageView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FontAwesomeImageView.swift; path = FontAwesome/FontAwesomeImageView.swift; sourceTree = ""; }; + 99B4E0D33DAA6D7B3F2F72F151235F36 /* OS.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OS.swift; path = SwiftyStoreKit/OS.swift; sourceTree = ""; }; + 9EABDCF610BF913C3B37911CAFD951CF /* PaperOnboarding.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PaperOnboarding.swift; path = Source/PaperOnboarding.swift; sourceTree = ""; }; + A03F612DDAC3DB361CC038D8BF86C1B5 /* FontAwesome.swift-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "FontAwesome.swift-umbrella.h"; sourceTree = ""; }; + A587290BB2CDBC93E189CB653042B456 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + A9DFAF573EF0882F6952891C3B7555D1 /* FontAwesomeTextRepresentable.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FontAwesomeTextRepresentable.swift; path = FontAwesome/FontAwesomeTextRepresentable.swift; sourceTree = ""; }; + AA6A27E4978A7E4B303E4822753444CC /* SwiftySound.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = SwiftySound.framework; path = SwiftySound.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + ABE2B63EA097D934B83415BB91D2ADFF /* FillAnimationView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FillAnimationView.swift; path = Source/FillAnimationView/FillAnimationView.swift; sourceTree = ""; }; + ABEA88B78122F134BDAB967120FDEEF8 /* SKProduct+LocalizedPrice.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "SKProduct+LocalizedPrice.swift"; path = "SwiftyStoreKit/SKProduct+LocalizedPrice.swift"; sourceTree = ""; }; + B13926D511F3248452E4EADB70FEE0AD /* FontAwesome.swift.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; name = FontAwesome.swift.bundle; path = "FontAwesome.swift-FontAwesome.swift.bundle"; sourceTree = BUILT_PRODUCTS_DIR; }; + B5FD5BF4A9EDDC62E6E27B02D6891984 /* KYCircularProgress.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = KYCircularProgress.framework; path = KYCircularProgress.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B7C51D9A3616CC80799DE3917D1440EF /* SCLAlertView.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = SCLAlertView.modulemap; sourceTree = ""; }; + BB8CC94A9E8BD95522CE4A88DD226EE3 /* SwiftyStoreKit.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftyStoreKit.xcconfig; sourceTree = ""; }; + BD834FC5691865F36A5DBC3A307679D4 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BDF5959A3B474869CA9A59BA22D9B1EA /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.3.sdk/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; + BF1843E7A0E184AC88368B22993D29E6 /* CompleteTransactionsController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = CompleteTransactionsController.swift; path = SwiftyStoreKit/CompleteTransactionsController.swift; sourceTree = ""; }; + C1E327891637CA046A60DFA49A7B0571 /* SwiftyStoreKit-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyStoreKit-umbrella.h"; sourceTree = ""; }; + C8CD5A35ABAA43271D29F14B8FA32F1E /* PageViewItem.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PageViewItem.swift; path = Source/PageView/Item/PageViewItem.swift; sourceTree = ""; }; + C8E0ADDC21A0C51DD833D49074902B5A /* Pods-Meditation-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Meditation-dummy.m"; sourceTree = ""; }; + CE98872E716424595BD3CF1FA1122F09 /* OnboardingContentView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = OnboardingContentView.swift; path = Source/OnboardingContentView/OnboardingContentView.swift; sourceTree = ""; }; + D055FF047BE989A6FB7813872E828CBD /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D0711951D5871D0C685A7DB958EB33F0 /* SCLAlertView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SCLAlertView.swift; path = SCLAlertView/SCLAlertView.swift; sourceTree = ""; }; + D160819B2711AB32B17A3FD7446CC8DE /* PaymentsController.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = PaymentsController.swift; path = SwiftyStoreKit/PaymentsController.swift; sourceTree = ""; }; + D299D856E4DF2E69A4FBB67C1898597D /* paper-onboarding.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "paper-onboarding.xcconfig"; sourceTree = ""; }; + D4FE23D7AC76377481342D0ECDDE9E2E /* InAppReceipt.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = InAppReceipt.swift; path = SwiftyStoreKit/InAppReceipt.swift; sourceTree = ""; }; + D5924C74731B35B14404774A21CAC749 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D601CB1C7AA34B5EFD4C03C3811264FE /* SwiftyStoreKit-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyStoreKit-prefix.pch"; sourceTree = ""; }; + D8E1EE83C915FC56C271260D63D10641 /* Pods-Meditation-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-Meditation-resources.sh"; sourceTree = ""; }; + E1D330B452623CBB8500A608FF7464CF /* KYCircularProgress.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = KYCircularProgress.modulemap; sourceTree = ""; }; + E68C8303228EFF646C999DFAD59444ED /* FontAwesome.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FontAwesome.swift; path = FontAwesome/FontAwesome.swift; sourceTree = ""; }; + E73968610FE92FB14685A5210CC09350 /* SwiftySound.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = SwiftySound.modulemap; sourceTree = ""; }; + E79D3EFBBDB3CEA80F40A805053859D0 /* Enum.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Enum.swift; path = FontAwesome/Enum.swift; sourceTree = ""; }; + E9951D63CC213D13D63F3B1ECA1BE912 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + EB42F083F242EA7414479200D160683F /* FontAwesomeExtension.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = FontAwesomeExtension.swift; path = FontAwesome/FontAwesomeExtension.swift; sourceTree = ""; }; + EEE286D657744E67965CA47C67C3DD2A /* Pods-Meditation-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Meditation-acknowledgements.plist"; sourceTree = ""; }; + F01A0C22FB1D7099EA182DE64E5110CC /* Pods-Meditation.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-Meditation.debug.xcconfig"; sourceTree = ""; }; + F0988FC588586548F2B49B4EAE186A67 /* paper-onboarding-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "paper-onboarding-prefix.pch"; sourceTree = ""; }; + F0E48D27BC892B55D06F4376805E2F1E /* Pods-Meditation-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-Meditation-umbrella.h"; sourceTree = ""; }; + F5C9672475032C280D57392FCC5DD050 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + FA028D5954DB3D648E530FCA7758398E /* FontAwesome.swift-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "FontAwesome.swift-dummy.m"; sourceTree = ""; }; + FC1A213285E82ABB96628D6093E3A9CB /* SwiftySound-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SwiftySound-dummy.m"; sourceTree = ""; }; + FDD7A00DC0325AF5986C44DB3832039E /* FontAwesome_swift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = FontAwesome_swift.framework; path = FontAwesome.swift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 00F28D70748E64998973BCC7B65B0D36 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 59EF401D011A3D635E87E3E3F1D1523B /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1120CBB1146640BF7F1E4CBF7CF55DD8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + C2D6F2BBEE478C78BA9C80354D38945C /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 634CC3A51B4EF82126C1829BA6A74E8B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 454FEFC1DAC2B37EB656BE133CD84CD4 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 81CB756C19F41368409453330FD5D5C7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 6EC40D7BADA054B926209056DFBFE316 /* CoreText.framework in Frameworks */, + FFF5BA92707F567C4E4E7CB6A8FD4B70 /* Foundation.framework in Frameworks */, + 47610629C7A7408DFCBE1EEA5DB0A7E3 /* UIKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 87E07E39981584EEBC3CDFD56AB72FAC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 7C214903E97437C9DE70A5F4C0B56041 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9BB839AAC39F5C5621D2322EF4705FFB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 711AFA1876EDB79EB2B1C97B1135D5F5 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AF37D16DED482F2D7F59DFA4929A4710 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D08D12F47DA659E0B9A1F0F2AF6635A8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BD1195A277722CA59284A9E84AF5F90D /* AVFoundation.framework in Frameworks */, + 304B20D30A477EC0DA0D7E988BEBE699 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F3746C2FA1AD4FF920182E59788C931B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B57125DCD2436C8C82CF55D663EB46FC /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 04F12B532A40CF5A265B7A7830E670A4 /* SwiftyStoreKit */ = { + isa = PBXGroup; + children = ( + 6CF2A125CF3AE1560DA12F7C5EFA282B /* AppleReceiptValidator.swift */, + BF1843E7A0E184AC88368B22993D29E6 /* CompleteTransactionsController.swift */, + 6EC01ABC7DE24A048434D8FE49C99024 /* InAppProductQueryRequest.swift */, + D4FE23D7AC76377481342D0ECDDE9E2E /* InAppReceipt.swift */, + 8BDA1E0D099BAA1C62F2B69DB6A4F1E0 /* InAppReceiptRefreshRequest.swift */, + 4ED34731AEE0CBDD1213E7A5C124D4A5 /* InAppReceiptVerificator.swift */, + 99B4E0D33DAA6D7B3F2F72F151235F36 /* OS.swift */, + 32CC4AB09CE92B8F8D971175B8174700 /* PaymentQueueController.swift */, + D160819B2711AB32B17A3FD7446CC8DE /* PaymentsController.swift */, + 1074D6D0D5ACA8F07F24F80F162A97FA /* ProductsInfoController.swift */, + 6F0B593B75EB155F6B87194D5A34A7D9 /* RestorePurchasesController.swift */, + ABEA88B78122F134BDAB967120FDEEF8 /* SKProduct+LocalizedPrice.swift */, + 10A7FF702CE17217555AA92F4D15EC38 /* SwiftyStoreKit.swift */, + 79CDB75218FA0BA5A3AC089366677150 /* SwiftyStoreKit+Types.swift */, + 3B334200513A65547E1D44E8F425E68D /* Support Files */, + ); + name = SwiftyStoreKit; + path = SwiftyStoreKit; + sourceTree = ""; + }; + 0827AE6DCB64364D4EF094C5228A16AF /* Resources */ = { + isa = PBXGroup; + children = ( + 6F61E34A94080D1B9FB9941CA76CD86E /* Font Awesome 5 Brands-Regular-400.otf */, + 7A891C3C4705897389A4816FBB4F7EC5 /* Font Awesome 5 Free-Regular-400.otf */, + 3074FCD5A2C4A59DCCDC962475065793 /* Font Awesome 5 Free-Solid-900.otf */, + ); + name = Resources; + sourceTree = ""; + }; + 14B8B9B15ECBE87983FF987239AB2D7B /* Frameworks */ = { + isa = PBXGroup; + children = ( + A930CD48B1922EB07AD130618A97C793 /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + 210B8EA6BBA8AAF90B67675E0052D361 /* Support Files */ = { + isa = PBXGroup; + children = ( + D5924C74731B35B14404774A21CAC749 /* Info.plist */, + E1D330B452623CBB8500A608FF7464CF /* KYCircularProgress.modulemap */, + 45113075C07B3B3CCBA21D2BD676651D /* KYCircularProgress.xcconfig */, + 73FD990BD005F2D8C14A4D987C54E658 /* KYCircularProgress-dummy.m */, + 1EF3E5A3FD9A981EA5FB772BDAF5CC9A /* KYCircularProgress-prefix.pch */, + 70D737D74E126F7D3995477EF0C95082 /* KYCircularProgress-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/KYCircularProgress"; + sourceTree = ""; + }; + 32755FF9695A45BACF3965FBB54B2885 /* SwiftySound */ = { + isa = PBXGroup; + children = ( + 46702EF5E19E75295B39C98306EDC38E /* Sound.swift */, + CD8CD6001B69C6DC98E461EC3DAC0194 /* Support Files */, + ); + name = SwiftySound; + path = SwiftySound; + sourceTree = ""; + }; + 3B334200513A65547E1D44E8F425E68D /* Support Files */ = { + isa = PBXGroup; + children = ( + E9951D63CC213D13D63F3B1ECA1BE912 /* Info.plist */, + 94C15723EF3F61C5564AADC1F3930E74 /* SwiftyStoreKit.modulemap */, + BB8CC94A9E8BD95522CE4A88DD226EE3 /* SwiftyStoreKit.xcconfig */, + 0505CDAEE4D660728AE00619FF8713C0 /* SwiftyStoreKit-dummy.m */, + D601CB1C7AA34B5EFD4C03C3811264FE /* SwiftyStoreKit-prefix.pch */, + C1E327891637CA046A60DFA49A7B0571 /* SwiftyStoreKit-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/SwiftyStoreKit"; + sourceTree = ""; + }; + 56184B87108AF83143DFADF2255A12BD /* KYCircularProgress */ = { + isa = PBXGroup; + children = ( + 6212DD845AE5E286E958AE561AF8085D /* KYCircularProgress.swift */, + 210B8EA6BBA8AAF90B67675E0052D361 /* Support Files */, + ); + name = KYCircularProgress; + path = KYCircularProgress; + sourceTree = ""; + }; + 56B87E304508246E84FE83AF36438116 /* paper-onboarding */ = { + isa = PBXGroup; + children = ( + 1552D81E377BC74C3F717366FB8ADF71 /* Configuring.swift */, + 4DA3D80CAE3E9FA539202B7C596317F2 /* ConstraintsHelper.swift */, + ABE2B63EA097D934B83415BB91D2ADFF /* FillAnimationView.swift */, + 380273F3390BA067FCD5F916AD1A0F64 /* GestureControl.swift */, + 51E33BFD19F5B262846B8CC70998DE24 /* OnboardingContantViewItem.swift */, + CE98872E716424595BD3CF1FA1122F09 /* OnboardingContentView.swift */, + 2287D76C382E623A11AC0D753EC6C546 /* PageContainer.swift */, + 86BFDADD4611FD0FAFA4B92B83F371D9 /* PageView.swift */, + C8CD5A35ABAA43271D29F14B8FA32F1E /* PageViewItem.swift */, + 9EABDCF610BF913C3B37911CAFD951CF /* PaperOnboarding.swift */, + 2E7AF0B26E412772E1C4E54910E634CF /* PaperOnboardingDataSource.swift */, + 6188F7B3385AF43E2D11CFCD4172663E /* PaperOnboardingDelegate.swift */, + B6C026700A49651505430A7A063D6CD9 /* Support Files */, + ); + name = "paper-onboarding"; + path = "paper-onboarding"; + sourceTree = ""; + }; + 77B031A32A19D334DD3B0702B119873B /* Pods */ = { + isa = PBXGroup; + children = ( + EE6CEF5A82B4FD037C0755C753F6ACF3 /* FontAwesome.swift */, + 56184B87108AF83143DFADF2255A12BD /* KYCircularProgress */, + 56B87E304508246E84FE83AF36438116 /* paper-onboarding */, + C8AE6D4A8480E70AA1E785FC696DBA91 /* SCLAlertView */, + 8DD1184E3DEDFE88C096B9AD694BD53F /* SwiftyJSON */, + 32755FF9695A45BACF3965FBB54B2885 /* SwiftySound */, + 04F12B532A40CF5A265B7A7830E670A4 /* SwiftyStoreKit */, + ); + name = Pods; + sourceTree = ""; + }; + 7DB346D0F39D3F0E887471402A8071AB = { + isa = PBXGroup; + children = ( + 93A4A3777CF96A4AAC1D13BA6DCCEA73 /* Podfile */, + 14B8B9B15ECBE87983FF987239AB2D7B /* Frameworks */, + 77B031A32A19D334DD3B0702B119873B /* Pods */, + B9718947CFED31746EAE5058FF231357 /* Products */, + C76935568FA448F6FA59F06DBD98CCA4 /* Targets Support Files */, + ); + sourceTree = ""; + }; + 8DD1184E3DEDFE88C096B9AD694BD53F /* SwiftyJSON */ = { + isa = PBXGroup; + children = ( + 72A67E12692EBB3CA940B613D94A0306 /* SwiftyJSON.swift */, + A6F5514FBE476B7A6F2B0ED5DB3E4D06 /* Support Files */, + ); + name = SwiftyJSON; + path = SwiftyJSON; + sourceTree = ""; + }; + A6F5514FBE476B7A6F2B0ED5DB3E4D06 /* Support Files */ = { + isa = PBXGroup; + children = ( + BD834FC5691865F36A5DBC3A307679D4 /* Info.plist */, + 534F10FCD1A82884CCE9C2CDD2A9749C /* SwiftyJSON.modulemap */, + 84E615D9EDF47C12EF271062ABACED76 /* SwiftyJSON.xcconfig */, + 934FF2D307444AB387E9B60E447F224B /* SwiftyJSON-dummy.m */, + 6D67B98A9A1FDE891A24ACBBB6DB7C0B /* SwiftyJSON-prefix.pch */, + 8DAF7FE629C474507D924430E46E508D /* SwiftyJSON-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/SwiftyJSON"; + sourceTree = ""; + }; + A930CD48B1922EB07AD130618A97C793 /* iOS */ = { + isa = PBXGroup; + children = ( + 7B0BA97DB46D5B969F46166BD5EDA801 /* AVFoundation.framework */, + 7D34E9BE62EC54EFD569B0F613DD5CDB /* CoreText.framework */, + 88DB1BB41A8FDB246D9D6EFEDEB35D6B /* Foundation.framework */, + BDF5959A3B474869CA9A59BA22D9B1EA /* UIKit.framework */, + ); + name = iOS; + sourceTree = ""; + }; + B6C026700A49651505430A7A063D6CD9 /* Support Files */ = { + isa = PBXGroup; + children = ( + D055FF047BE989A6FB7813872E828CBD /* Info.plist */, + 27E0527C0B227763266EA736BCE23366 /* paper-onboarding.modulemap */, + D299D856E4DF2E69A4FBB67C1898597D /* paper-onboarding.xcconfig */, + 34F8EF59CC5913D4CF56859FD4545CDF /* paper-onboarding-dummy.m */, + F0988FC588586548F2B49B4EAE186A67 /* paper-onboarding-prefix.pch */, + 3C47D3C2A396B7D53EA56B97907E416F /* paper-onboarding-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/paper-onboarding"; + sourceTree = ""; + }; + B9718947CFED31746EAE5058FF231357 /* Products */ = { + isa = PBXGroup; + children = ( + B13926D511F3248452E4EADB70FEE0AD /* FontAwesome.swift.bundle */, + FDD7A00DC0325AF5986C44DB3832039E /* FontAwesome_swift.framework */, + B5FD5BF4A9EDDC62E6E27B02D6891984 /* KYCircularProgress.framework */, + 9492DF1BC5814CE0D3A484387EBD986E /* paper_onboarding.framework */, + 54BB495848699394FDC237D05100785C /* Pods_Meditation.framework */, + 4BDC34BFC35F326C807B33DC113C8A38 /* SCLAlertView.framework */, + 0AAD23A6E6A9A4B374E69535DC560E15 /* SwiftyJSON.framework */, + AA6A27E4978A7E4B303E4822753444CC /* SwiftySound.framework */, + 4E8A935F4E73C5D023E63F2AC854FFA7 /* SwiftyStoreKit.framework */, + ); + name = Products; + sourceTree = ""; + }; + C76935568FA448F6FA59F06DBD98CCA4 /* Targets Support Files */ = { + isa = PBXGroup; + children = ( + D8424D2C5FAB347E939490FD67993181 /* Pods-Meditation */, + ); + name = "Targets Support Files"; + sourceTree = ""; + }; + C8AE6D4A8480E70AA1E785FC696DBA91 /* SCLAlertView */ = { + isa = PBXGroup; + children = ( + D0711951D5871D0C685A7DB958EB33F0 /* SCLAlertView.swift */, + 65B9114F61C5E19FFFD805D6C637845F /* SCLExtensions.swift */, + CCE366C4FD3B45B94C1D76D6A0223FFC /* Support Files */, + ); + name = SCLAlertView; + path = SCLAlertView; + sourceTree = ""; + }; + CCE366C4FD3B45B94C1D76D6A0223FFC /* Support Files */ = { + isa = PBXGroup; + children = ( + A587290BB2CDBC93E189CB653042B456 /* Info.plist */, + B7C51D9A3616CC80799DE3917D1440EF /* SCLAlertView.modulemap */, + 3E3C5A5633558E34FB16ACD91B84058F /* SCLAlertView.xcconfig */, + 0C4EEE1B950D8AD93F74381F2DF2E077 /* SCLAlertView-dummy.m */, + 93C7C6B66482C1871F8D0A5289CA80F5 /* SCLAlertView-prefix.pch */, + 8E95FAECD64766FECC3E575D28BBB558 /* SCLAlertView-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/SCLAlertView"; + sourceTree = ""; + }; + CD8CD6001B69C6DC98E461EC3DAC0194 /* Support Files */ = { + isa = PBXGroup; + children = ( + F5C9672475032C280D57392FCC5DD050 /* Info.plist */, + E73968610FE92FB14685A5210CC09350 /* SwiftySound.modulemap */, + 5721C93FB5FDBD06B2AAD2E1330DFDD8 /* SwiftySound.xcconfig */, + FC1A213285E82ABB96628D6093E3A9CB /* SwiftySound-dummy.m */, + 094DA2BA234D9C6AED781D71A5CA75D0 /* SwiftySound-prefix.pch */, + 215B8471CB693EE9D6DE14EF2DB3CC84 /* SwiftySound-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/SwiftySound"; + sourceTree = ""; + }; + D8424D2C5FAB347E939490FD67993181 /* Pods-Meditation */ = { + isa = PBXGroup; + children = ( + 5303D316EA88E89FD5E7C01663744AD6 /* Info.plist */, + 729BBB5672097165784C9A456B255191 /* Pods-Meditation.modulemap */, + 7AED5452724E79D3534F1E4DD674BEFC /* Pods-Meditation-acknowledgements.markdown */, + EEE286D657744E67965CA47C67C3DD2A /* Pods-Meditation-acknowledgements.plist */, + C8E0ADDC21A0C51DD833D49074902B5A /* Pods-Meditation-dummy.m */, + 47D07AE48ABDE4AA65609E9154037202 /* Pods-Meditation-frameworks.sh */, + D8E1EE83C915FC56C271260D63D10641 /* Pods-Meditation-resources.sh */, + F0E48D27BC892B55D06F4376805E2F1E /* Pods-Meditation-umbrella.h */, + F01A0C22FB1D7099EA182DE64E5110CC /* Pods-Meditation.debug.xcconfig */, + 074DCBFF16AA0E6E82CB2BE592E940FA /* Pods-Meditation.release.xcconfig */, + ); + name = "Pods-Meditation"; + path = "Target Support Files/Pods-Meditation"; + sourceTree = ""; + }; + D8D32BE101FBED45892ED08886F4B1F2 /* Support Files */ = { + isa = PBXGroup; + children = ( + 5ABB8BF6F0BA842FDDF3174DFEC7A715 /* FontAwesome.swift.modulemap */, + 2137245788B5F2143661700B732D51B4 /* FontAwesome.swift.xcconfig */, + FA028D5954DB3D648E530FCA7758398E /* FontAwesome.swift-dummy.m */, + 4AC9E90081FE71E63EBDFE4C00C4A41B /* FontAwesome.swift-prefix.pch */, + A03F612DDAC3DB361CC038D8BF86C1B5 /* FontAwesome.swift-umbrella.h */, + 1E3E5997C5E6F420353511A9D8E53744 /* Info.plist */, + 5982A12A45C5063F93AFED3C4B7E9C28 /* ResourceBundle-FontAwesome.swift-Info.plist */, + ); + name = "Support Files"; + path = "../Target Support Files/FontAwesome.swift"; + sourceTree = ""; + }; + EE6CEF5A82B4FD037C0755C753F6ACF3 /* FontAwesome.swift */ = { + isa = PBXGroup; + children = ( + E79D3EFBBDB3CEA80F40A805053859D0 /* Enum.swift */, + E68C8303228EFF646C999DFAD59444ED /* FontAwesome.swift */, + 48C4081AC199AFAD5B060440DB7973F8 /* FontAwesomeBarButtonItem.swift */, + EB42F083F242EA7414479200D160683F /* FontAwesomeExtension.swift */, + 1566F0B04D5DF9BFC5FF8FB0C4E61195 /* FontAwesomeImageRepresentable.swift */, + 98C5C3B5D7080A323CAB0616B82E50AE /* FontAwesomeImageView.swift */, + 5F7E814EC1BD3FE2CC6E421C0FC2F81B /* FontAwesomeSegmentedControl.swift */, + 5F38ECCF95EC9559CE7FF6510865D017 /* FontAwesomeStateRequirement.swift */, + 11B1E90A4E038798FE83CB79B7FBEFCA /* FontAwesomeTabBarItem.swift */, + A9DFAF573EF0882F6952891C3B7555D1 /* FontAwesomeTextRepresentable.swift */, + 6943603CA944789F663A70DB87BB2FB7 /* FontAwesomeView.swift */, + 0827AE6DCB64364D4EF094C5228A16AF /* Resources */, + D8D32BE101FBED45892ED08886F4B1F2 /* Support Files */, + ); + name = FontAwesome.swift; + path = FontAwesome.swift; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 2417DCDC1616BAD7D203C158FE5B0F35 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + DD296D6DF8AC3E1814624874847905C8 /* FontAwesome.swift-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 3A8D13B8BAE5FDFD45EF8407A63FCDBC /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 8482EEDD4DE819F389D4628C63899E5E /* SwiftyStoreKit-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4322BC9F667778FC04285736D5A871E8 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + A6995F65FD29E381359809E39D7D8649 /* Pods-Meditation-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 53B10C6C4AC37F1592A049A2771A5191 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 5F277F3427F528CCF898259A48934211 /* SwiftyJSON-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 722E0A468137E46B7DD5B3B5971E1C00 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 5CF29744A4F25FABBF17B60C983F2A12 /* KYCircularProgress-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + AB61D096B6F9534BEC05C7690ACC7F61 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + EB2A29005F96454DDE7C02A23ABE48DB /* SwiftySound-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D025A52D0EFD2820155A870EEA13A4C6 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 7455C21A8CDE7750973B1EA649305312 /* paper-onboarding-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DFD916F47D792507B4F455191F5042E8 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 2F69F75A30AFC330E110ACE9D3CAB4F8 /* SCLAlertView-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 08B04875F4404729D5274077AC117F66 /* SwiftyJSON */ = { + isa = PBXNativeTarget; + buildConfigurationList = A1F951077FC6BCF93A6B875D8445623C /* Build configuration list for PBXNativeTarget "SwiftyJSON" */; + buildPhases = ( + 250D941C7A51B29661A15702C1FB3DCA /* Sources */, + 634CC3A51B4EF82126C1829BA6A74E8B /* Frameworks */, + 53B10C6C4AC37F1592A049A2771A5191 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SwiftyJSON; + productName = SwiftyJSON; + productReference = 0AAD23A6E6A9A4B374E69535DC560E15 /* SwiftyJSON.framework */; + productType = "com.apple.product-type.framework"; + }; + 29124FF4E0F9AB7495AA7588B0A1A107 /* FontAwesome.swift */ = { + isa = PBXNativeTarget; + buildConfigurationList = A80A527DA4E27E5F6BB54D77CF803FB4 /* Build configuration list for PBXNativeTarget "FontAwesome.swift" */; + buildPhases = ( + 700B061BB8418D063C16F57AA01105C4 /* Sources */, + 81CB756C19F41368409453330FD5D5C7 /* Frameworks */, + 7AD3B2D76D4860EC61264A9E91F7B979 /* Resources */, + 2417DCDC1616BAD7D203C158FE5B0F35 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + 168EED59381E6A33050008C1EBD91077 /* PBXTargetDependency */, + ); + name = FontAwesome.swift; + productName = FontAwesome.swift; + productReference = FDD7A00DC0325AF5986C44DB3832039E /* FontAwesome_swift.framework */; + productType = "com.apple.product-type.framework"; + }; + 5C1F0BC8AD9DB9D282F6612636923786 /* KYCircularProgress */ = { + isa = PBXNativeTarget; + buildConfigurationList = EAC3A72F55A00F6D6FE5E6F51DC1CC60 /* Build configuration list for PBXNativeTarget "KYCircularProgress" */; + buildPhases = ( + 076B314F7564797CE9EEF725033C13C1 /* Sources */, + 00F28D70748E64998973BCC7B65B0D36 /* Frameworks */, + 722E0A468137E46B7DD5B3B5971E1C00 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = KYCircularProgress; + productName = KYCircularProgress; + productReference = B5FD5BF4A9EDDC62E6E27B02D6891984 /* KYCircularProgress.framework */; + productType = "com.apple.product-type.framework"; + }; + 655504A1CEF49BBA51092D7CE0A6BC42 /* Pods-Meditation */ = { + isa = PBXNativeTarget; + buildConfigurationList = 130AEC88E8508C370403BB10AD7DC9E2 /* Build configuration list for PBXNativeTarget "Pods-Meditation" */; + buildPhases = ( + 82F52BF5CE18FD7BAC6F1CFD782FBFB5 /* Sources */, + 9BB839AAC39F5C5621D2322EF4705FFB /* Frameworks */, + 4322BC9F667778FC04285736D5A871E8 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + 0885E051A542231EF8E07F9C2B70316F /* PBXTargetDependency */, + EFE762D435F9A904FF45246A67F1981B /* PBXTargetDependency */, + A85B8FD40234A7549AC4DAD1BDADB8C7 /* PBXTargetDependency */, + 4B0532ED08285984936AEC09A46E662E /* PBXTargetDependency */, + BD34A28E3C90C3930B403B5794DBDBC0 /* PBXTargetDependency */, + D68192D672B92E3F4357C1620CBE7258 /* PBXTargetDependency */, + 645ADE5EEA52659087C1B41AE3C7E42B /* PBXTargetDependency */, + ); + name = "Pods-Meditation"; + productName = "Pods-Meditation"; + productReference = 54BB495848699394FDC237D05100785C /* Pods_Meditation.framework */; + productType = "com.apple.product-type.framework"; + }; + 6C582F0467383C85FA2748503EBDD043 /* SCLAlertView */ = { + isa = PBXNativeTarget; + buildConfigurationList = D7413AD3952D39B8F4B3358D8E72915C /* Build configuration list for PBXNativeTarget "SCLAlertView" */; + buildPhases = ( + B86F027DB269977092AF4AF9B3D690AE /* Sources */, + F3746C2FA1AD4FF920182E59788C931B /* Frameworks */, + DFD916F47D792507B4F455191F5042E8 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SCLAlertView; + productName = SCLAlertView; + productReference = 4BDC34BFC35F326C807B33DC113C8A38 /* SCLAlertView.framework */; + productType = "com.apple.product-type.framework"; + }; + 738B2050D3EE6CEF2F8FC5020EAB44A1 /* paper-onboarding */ = { + isa = PBXNativeTarget; + buildConfigurationList = 0C0F1C8062762AC00D8F9FF77BD2BCD9 /* Build configuration list for PBXNativeTarget "paper-onboarding" */; + buildPhases = ( + 51939CF808A8AD03A5E37CB8C1090A43 /* Sources */, + 87E07E39981584EEBC3CDFD56AB72FAC /* Frameworks */, + D025A52D0EFD2820155A870EEA13A4C6 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "paper-onboarding"; + productName = "paper-onboarding"; + productReference = 9492DF1BC5814CE0D3A484387EBD986E /* paper_onboarding.framework */; + productType = "com.apple.product-type.framework"; + }; + B8B08C578828E3A292E3B948F8124CBA /* SwiftyStoreKit */ = { + isa = PBXNativeTarget; + buildConfigurationList = 94213C8DBEBFB4847AA2522F62B21F21 /* Build configuration list for PBXNativeTarget "SwiftyStoreKit" */; + buildPhases = ( + DA3BCD1589646BD7F10AEC04B52C5CFF /* Sources */, + 1120CBB1146640BF7F1E4CBF7CF55DD8 /* Frameworks */, + 3A8D13B8BAE5FDFD45EF8407A63FCDBC /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SwiftyStoreKit; + productName = SwiftyStoreKit; + productReference = 4E8A935F4E73C5D023E63F2AC854FFA7 /* SwiftyStoreKit.framework */; + productType = "com.apple.product-type.framework"; + }; + C53CF332138BDC1006E1342662A1F706 /* SwiftySound */ = { + isa = PBXNativeTarget; + buildConfigurationList = F2A250C96B33500F2C782B8A893D1DF9 /* Build configuration list for PBXNativeTarget "SwiftySound" */; + buildPhases = ( + C1BF601753FF452106C63EFE915CE6F1 /* Sources */, + D08D12F47DA659E0B9A1F0F2AF6635A8 /* Frameworks */, + AB61D096B6F9534BEC05C7690ACC7F61 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SwiftySound; + productName = SwiftySound; + productReference = AA6A27E4978A7E4B303E4822753444CC /* SwiftySound.framework */; + productType = "com.apple.product-type.framework"; + }; + CB2EAB5C326E4BB9AE6483289BB7C828 /* FontAwesome.swift-FontAwesome.swift */ = { + isa = PBXNativeTarget; + buildConfigurationList = EBA26AD231745C1751BE4055B27A6510 /* Build configuration list for PBXNativeTarget "FontAwesome.swift-FontAwesome.swift" */; + buildPhases = ( + DB2699BD7760421C55713B9415FF0274 /* Sources */, + AF37D16DED482F2D7F59DFA4929A4710 /* Frameworks */, + 0284765E3DCE50F1BA8BC52089D9EF11 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "FontAwesome.swift-FontAwesome.swift"; + productName = "FontAwesome.swift-FontAwesome.swift"; + productReference = B13926D511F3248452E4EADB70FEE0AD /* FontAwesome.swift.bundle */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D41D8CD98F00B204E9800998ECF8427E /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0930; + LastUpgradeCheck = 0930; + }; + buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 7DB346D0F39D3F0E887471402A8071AB; + productRefGroup = B9718947CFED31746EAE5058FF231357 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 29124FF4E0F9AB7495AA7588B0A1A107 /* FontAwesome.swift */, + CB2EAB5C326E4BB9AE6483289BB7C828 /* FontAwesome.swift-FontAwesome.swift */, + 5C1F0BC8AD9DB9D282F6612636923786 /* KYCircularProgress */, + 738B2050D3EE6CEF2F8FC5020EAB44A1 /* paper-onboarding */, + 655504A1CEF49BBA51092D7CE0A6BC42 /* Pods-Meditation */, + 6C582F0467383C85FA2748503EBDD043 /* SCLAlertView */, + 08B04875F4404729D5274077AC117F66 /* SwiftyJSON */, + C53CF332138BDC1006E1342662A1F706 /* SwiftySound */, + B8B08C578828E3A292E3B948F8124CBA /* SwiftyStoreKit */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 0284765E3DCE50F1BA8BC52089D9EF11 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 0B2116700FA0CFFB7FB4903C53230FED /* Font Awesome 5 Brands-Regular-400.otf in Resources */, + A3088DBACC68068F17A9B20A8F442ACA /* Font Awesome 5 Free-Regular-400.otf in Resources */, + 31514774FEEC7F179F887E6F1CC84857 /* Font Awesome 5 Free-Solid-900.otf in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 7AD3B2D76D4860EC61264A9E91F7B979 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2B476160A1663094CDFBDC35DC9EFD32 /* FontAwesome.swift.bundle in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 076B314F7564797CE9EEF725033C13C1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 01DBE6E7AAFBF3366F330B105CCB8287 /* KYCircularProgress-dummy.m in Sources */, + 545AE3C5E061CC97FB166DAF1C5143E8 /* KYCircularProgress.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 250D941C7A51B29661A15702C1FB3DCA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 72B67D26F3E1A6160E56B43945C301DB /* SwiftyJSON-dummy.m in Sources */, + F7B77FAD4019B1F675A054B7643F4AFA /* SwiftyJSON.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 51939CF808A8AD03A5E37CB8C1090A43 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 61430A34EB37246ABC3F8E8E29806F38 /* Configuring.swift in Sources */, + 85EE7A0CB68505A4ABBC14C8B528EABB /* ConstraintsHelper.swift in Sources */, + 4F5FAA1BFB6846C4C80D881F3B9355A7 /* FillAnimationView.swift in Sources */, + 2D30889B4E1FC09CB6FDB15B96AD30E9 /* GestureControl.swift in Sources */, + 409BCBFBF2161753E6B85649EF6633C5 /* OnboardingContantViewItem.swift in Sources */, + 5A1AE014B6FF9AC20A3402A7B05BA7E5 /* OnboardingContentView.swift in Sources */, + 3346ABD8DBFC61551BA6DD0AEC5CB9DE /* PageContainer.swift in Sources */, + 10BD6D329B02C3275995F12648C56B90 /* PageView.swift in Sources */, + 48696FE82A4D8086055541E81DC94107 /* PageViewItem.swift in Sources */, + DC1474A8A5F9C27D852181B661944619 /* paper-onboarding-dummy.m in Sources */, + CD0A71E316A3C77C88AE6C30C3562CB0 /* PaperOnboarding.swift in Sources */, + CDEAFF50FED59401B66C354798994E30 /* PaperOnboardingDataSource.swift in Sources */, + 66D2E16A248430B979133176DCC3B109 /* PaperOnboardingDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 700B061BB8418D063C16F57AA01105C4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 828E84A639F7F74F9C871F82A4E252B9 /* Enum.swift in Sources */, + 9419ACA668E00FFC1F70AA23359DF3E5 /* FontAwesome.swift in Sources */, + B633D850FB892345DDA1F0DB05213C58 /* FontAwesome.swift-dummy.m in Sources */, + 89F604C5071756146A5C682E03897724 /* FontAwesomeBarButtonItem.swift in Sources */, + 6F9C78CE9BA836E9272D3D08F2EBD5AE /* FontAwesomeExtension.swift in Sources */, + 2D2DD9882ED57FA4A43A5912F7325970 /* FontAwesomeImageRepresentable.swift in Sources */, + B22CD5BAA233DD92D22269BAF31049B9 /* FontAwesomeImageView.swift in Sources */, + 532B94DAC89623F895B0D5C1F04DB994 /* FontAwesomeSegmentedControl.swift in Sources */, + 0FF663F919A014E6DE08F3726ED3D26F /* FontAwesomeStateRequirement.swift in Sources */, + 0FE9C1DB0A04041F2D6D572B7B519367 /* FontAwesomeTabBarItem.swift in Sources */, + 6B5413138C15627AB31211A88A063709 /* FontAwesomeTextRepresentable.swift in Sources */, + 93929E495F12F2702B2522EE518586C5 /* FontAwesomeView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 82F52BF5CE18FD7BAC6F1CFD782FBFB5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4B7A91F57EE818D4A289E665CA99DD42 /* Pods-Meditation-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B86F027DB269977092AF4AF9B3D690AE /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EB81ABE70D61966AED8A5827D2754019 /* SCLAlertView-dummy.m in Sources */, + 91F13A71F646FE645B075554061D2A5E /* SCLAlertView.swift in Sources */, + EC1529EC711090345F55EDA2ADC0CD14 /* SCLExtensions.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C1BF601753FF452106C63EFE915CE6F1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 63443D77C6E8695AB0E1324CAF15EEDA /* Sound.swift in Sources */, + 011993838523DB2203CEE840275B8F0E /* SwiftySound-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DA3BCD1589646BD7F10AEC04B52C5CFF /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 608E5DEF6E46FDD42D1AAD8A979ECA2C /* AppleReceiptValidator.swift in Sources */, + F7EDFF8ADB6D166B2289ABFA8D68F262 /* CompleteTransactionsController.swift in Sources */, + 60BD3C93AC4A20EB0F10272EF740A7B1 /* InAppProductQueryRequest.swift in Sources */, + 64C8A1224EF3FD9FA3E7B014D3A6C68C /* InAppReceipt.swift in Sources */, + B035285D1DE27A46C68D12F26226F68E /* InAppReceiptRefreshRequest.swift in Sources */, + C2B42FF9CBAB7677D9F6BF03E6C44F7E /* InAppReceiptVerificator.swift in Sources */, + 4242F5D61F5CE9E8FE109FF1E6CD0215 /* OS.swift in Sources */, + 8A1EE22D27DC4FEA41A11AC1F20B3478 /* PaymentQueueController.swift in Sources */, + CCB18EFF51F3186398FD939995842880 /* PaymentsController.swift in Sources */, + 1759D0E7AD6E8B6AA04353E8E4E758EC /* ProductsInfoController.swift in Sources */, + 424F3651FB734F6AE3AE1CF84980D23B /* RestorePurchasesController.swift in Sources */, + 08DFB59557320FA598DF54FAC82F25FB /* SKProduct+LocalizedPrice.swift in Sources */, + D72A933FC1885E404D150260FDDAD3A6 /* SwiftyStoreKit+Types.swift in Sources */, + 46D033163F2C11E62AE79ACA3AFB0394 /* SwiftyStoreKit-dummy.m in Sources */, + CB11F359CD23BE2FFFF2370AC6F0C67A /* SwiftyStoreKit.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DB2699BD7760421C55713B9415FF0274 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 0885E051A542231EF8E07F9C2B70316F /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = FontAwesome.swift; + target = 29124FF4E0F9AB7495AA7588B0A1A107 /* FontAwesome.swift */; + targetProxy = 9105A9F7762DB59670EA1D2845DE7D6B /* PBXContainerItemProxy */; + }; + 168EED59381E6A33050008C1EBD91077 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "FontAwesome.swift-FontAwesome.swift"; + target = CB2EAB5C326E4BB9AE6483289BB7C828 /* FontAwesome.swift-FontAwesome.swift */; + targetProxy = 11C46040B80D714B8D493D1A3B1820AC /* PBXContainerItemProxy */; + }; + 4B0532ED08285984936AEC09A46E662E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = SwiftyJSON; + target = 08B04875F4404729D5274077AC117F66 /* SwiftyJSON */; + targetProxy = 392A0D952FE65BFA6F1AE65D2205EE42 /* PBXContainerItemProxy */; + }; + 645ADE5EEA52659087C1B41AE3C7E42B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "paper-onboarding"; + target = 738B2050D3EE6CEF2F8FC5020EAB44A1 /* paper-onboarding */; + targetProxy = 5EE922725D9F386CD7495D29B6F08120 /* PBXContainerItemProxy */; + }; + A85B8FD40234A7549AC4DAD1BDADB8C7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = SCLAlertView; + target = 6C582F0467383C85FA2748503EBDD043 /* SCLAlertView */; + targetProxy = 23C0B2BD1058F519EA5061D43F0BA311 /* PBXContainerItemProxy */; + }; + BD34A28E3C90C3930B403B5794DBDBC0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = SwiftySound; + target = C53CF332138BDC1006E1342662A1F706 /* SwiftySound */; + targetProxy = 8EAD1867DC5795F6C72A5BDA158E6BCD /* PBXContainerItemProxy */; + }; + D68192D672B92E3F4357C1620CBE7258 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = SwiftyStoreKit; + target = B8B08C578828E3A292E3B948F8124CBA /* SwiftyStoreKit */; + targetProxy = 22E9F06E55C562186E2D693F08E4F232 /* PBXContainerItemProxy */; + }; + EFE762D435F9A904FF45246A67F1981B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = KYCircularProgress; + target = 5C1F0BC8AD9DB9D282F6612636923786 /* KYCircularProgress */; + targetProxy = 113DCF3A426F8076F932F2160AED20F2 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 21B519DB3A7D9E3BF95432CAB50F2332 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2137245788B5F2143661700B732D51B4 /* FontAwesome.swift.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/FontAwesome.swift"; + INFOPLIST_FILE = "Target Support Files/FontAwesome.swift/ResourceBundle-FontAwesome.swift-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + PRODUCT_NAME = FontAwesome.swift; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = bundle; + }; + name = Release; + }; + 24F8FA94C7E0C929F6833B010DF1899A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BB8CC94A9E8BD95522CE4A88DD226EE3 /* SwiftyStoreKit.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/SwiftyStoreKit/SwiftyStoreKit-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SwiftyStoreKit/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/SwiftyStoreKit/SwiftyStoreKit.modulemap"; + PRODUCT_MODULE_NAME = SwiftyStoreKit; + PRODUCT_NAME = SwiftyStoreKit; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 2598FB4387339EE3CFD47B52774555F2 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = BB8CC94A9E8BD95522CE4A88DD226EE3 /* SwiftyStoreKit.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/SwiftyStoreKit/SwiftyStoreKit-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SwiftyStoreKit/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/SwiftyStoreKit/SwiftyStoreKit.modulemap"; + PRODUCT_MODULE_NAME = SwiftyStoreKit; + PRODUCT_NAME = SwiftyStoreKit; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 38587CCC4FF29AA902577CF6671BD54E /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5721C93FB5FDBD06B2AAD2E1330DFDD8 /* SwiftySound.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/SwiftySound/SwiftySound-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SwiftySound/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/SwiftySound/SwiftySound.modulemap"; + PRODUCT_MODULE_NAME = SwiftySound; + PRODUCT_NAME = SwiftySound; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 49E382917D90F9CC8053C3EB9D169DAF /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2137245788B5F2143661700B732D51B4 /* FontAwesome.swift.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/FontAwesome.swift/FontAwesome.swift-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/FontAwesome.swift/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/FontAwesome.swift/FontAwesome.swift.modulemap"; + PRODUCT_MODULE_NAME = FontAwesome_swift; + PRODUCT_NAME = FontAwesome_swift; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 5DC0083F57E9AB706B46C5B5D73B38D8 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGNING_ALLOWED = NO; + CODE_SIGNING_REQUIRED = NO; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_DEBUG=1", + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Debug; + }; + 819B2F5087C8630DC6A642BBABEDCAE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2137245788B5F2143661700B732D51B4 /* FontAwesome.swift.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/FontAwesome.swift/FontAwesome.swift-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/FontAwesome.swift/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/FontAwesome.swift/FontAwesome.swift.modulemap"; + PRODUCT_MODULE_NAME = FontAwesome_swift; + PRODUCT_NAME = FontAwesome_swift; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 94A5B5CE04EBC0CEA6ACB768C04085E0 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3E3C5A5633558E34FB16ACD91B84058F /* SCLAlertView.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/SCLAlertView/SCLAlertView-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SCLAlertView/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/SCLAlertView/SCLAlertView.modulemap"; + PRODUCT_MODULE_NAME = SCLAlertView; + PRODUCT_NAME = SCLAlertView; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 99D7B30E41BB57B22D4358777DEC50A1 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5721C93FB5FDBD06B2AAD2E1330DFDD8 /* SwiftySound.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/SwiftySound/SwiftySound-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SwiftySound/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/SwiftySound/SwiftySound.modulemap"; + PRODUCT_MODULE_NAME = SwiftySound; + PRODUCT_NAME = SwiftySound; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 9E94B853A7284D8B491B0751772C5170 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2137245788B5F2143661700B732D51B4 /* FontAwesome.swift.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = "iPhone Developer"; + CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/FontAwesome.swift"; + INFOPLIST_FILE = "Target Support Files/FontAwesome.swift/ResourceBundle-FontAwesome.swift-Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + PRODUCT_NAME = FontAwesome.swift; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + WRAPPER_EXTENSION = bundle; + }; + name = Debug; + }; + A872CE62CEB1F2D3AE986148C350652F /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 84E615D9EDF47C12EF271062ABACED76 /* SwiftyJSON.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SwiftyJSON/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/SwiftyJSON/SwiftyJSON.modulemap"; + PRODUCT_MODULE_NAME = SwiftyJSON; + PRODUCT_NAME = SwiftyJSON; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + AD07F3A56AE0D31BF194CF55437C4EBA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3E3C5A5633558E34FB16ACD91B84058F /* SCLAlertView.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/SCLAlertView/SCLAlertView-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SCLAlertView/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/SCLAlertView/SCLAlertView.modulemap"; + PRODUCT_MODULE_NAME = SCLAlertView; + PRODUCT_NAME = SCLAlertView; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + AEEA80476F98EB645C4DD53EB2BB6D88 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGNING_ALLOWED = NO; + CODE_SIGNING_REQUIRED = NO; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_RELEASE=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Release; + }; + B7E0A791680F404C23952BCADA2566D5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 84E615D9EDF47C12EF271062ABACED76 /* SwiftyJSON.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SwiftyJSON/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/SwiftyJSON/SwiftyJSON.modulemap"; + PRODUCT_MODULE_NAME = SwiftyJSON; + PRODUCT_NAME = SwiftyJSON; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + CEBFBB8CD6FCCAC178CF6DBDBB6D07E6 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 45113075C07B3B3CCBA21D2BD676651D /* KYCircularProgress.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/KYCircularProgress/KYCircularProgress-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/KYCircularProgress/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/KYCircularProgress/KYCircularProgress.modulemap"; + PRODUCT_MODULE_NAME = KYCircularProgress; + PRODUCT_NAME = KYCircularProgress; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + CF3D8714A0AA06B4B3517888FEB1E919 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D299D856E4DF2E69A4FBB67C1898597D /* paper-onboarding.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/paper-onboarding/paper-onboarding-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/paper-onboarding/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/paper-onboarding/paper-onboarding.modulemap"; + PRODUCT_MODULE_NAME = paper_onboarding; + PRODUCT_NAME = paper_onboarding; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + DA1C5D110AB0E86320E5D1977E9AAFDB /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = D299D856E4DF2E69A4FBB67C1898597D /* paper-onboarding.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/paper-onboarding/paper-onboarding-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/paper-onboarding/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/paper-onboarding/paper-onboarding.modulemap"; + PRODUCT_MODULE_NAME = paper_onboarding; + PRODUCT_NAME = paper_onboarding; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + ECAF10D269B8FC8F9CA6C997ACF29FEA /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 074DCBFF16AA0E6E82CB2BE592E940FA /* Pods-Meditation.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-Meditation/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-Meditation/Pods-Meditation.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + F369FB95993195596003804E81CD26CD /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F01A0C22FB1D7099EA182DE64E5110CC /* Pods-Meditation.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-Meditation/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.4; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-Meditation/Pods-Meditation.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + FC82286DA45AB0CC4C19700A6FC8D5D9 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 45113075C07B3B3CCBA21D2BD676651D /* KYCircularProgress.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/KYCircularProgress/KYCircularProgress-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/KYCircularProgress/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/KYCircularProgress/KYCircularProgress.modulemap"; + PRODUCT_MODULE_NAME = KYCircularProgress; + PRODUCT_NAME = KYCircularProgress; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 0C0F1C8062762AC00D8F9FF77BD2BCD9 /* Build configuration list for PBXNativeTarget "paper-onboarding" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DA1C5D110AB0E86320E5D1977E9AAFDB /* Debug */, + CF3D8714A0AA06B4B3517888FEB1E919 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 130AEC88E8508C370403BB10AD7DC9E2 /* Build configuration list for PBXNativeTarget "Pods-Meditation" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F369FB95993195596003804E81CD26CD /* Debug */, + ECAF10D269B8FC8F9CA6C997ACF29FEA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5DC0083F57E9AB706B46C5B5D73B38D8 /* Debug */, + AEEA80476F98EB645C4DD53EB2BB6D88 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 94213C8DBEBFB4847AA2522F62B21F21 /* Build configuration list for PBXNativeTarget "SwiftyStoreKit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 24F8FA94C7E0C929F6833B010DF1899A /* Debug */, + 2598FB4387339EE3CFD47B52774555F2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A1F951077FC6BCF93A6B875D8445623C /* Build configuration list for PBXNativeTarget "SwiftyJSON" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A872CE62CEB1F2D3AE986148C350652F /* Debug */, + B7E0A791680F404C23952BCADA2566D5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A80A527DA4E27E5F6BB54D77CF803FB4 /* Build configuration list for PBXNativeTarget "FontAwesome.swift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 49E382917D90F9CC8053C3EB9D169DAF /* Debug */, + 819B2F5087C8630DC6A642BBABEDCAE5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D7413AD3952D39B8F4B3358D8E72915C /* Build configuration list for PBXNativeTarget "SCLAlertView" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 94A5B5CE04EBC0CEA6ACB768C04085E0 /* Debug */, + AD07F3A56AE0D31BF194CF55437C4EBA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + EAC3A72F55A00F6D6FE5E6F51DC1CC60 /* Build configuration list for PBXNativeTarget "KYCircularProgress" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC82286DA45AB0CC4C19700A6FC8D5D9 /* Debug */, + CEBFBB8CD6FCCAC178CF6DBDBB6D07E6 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + EBA26AD231745C1751BE4055B27A6510 /* Build configuration list for PBXNativeTarget "FontAwesome.swift-FontAwesome.swift" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9E94B853A7284D8B491B0751772C5170 /* Debug */, + 21B519DB3A7D9E3BF95432CAB50F2332 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F2A250C96B33500F2C782B8A893D1DF9 /* Build configuration list for PBXNativeTarget "SwiftySound" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 99D7B30E41BB57B22D4358777DEC50A1 /* Debug */, + 38587CCC4FF29AA902577CF6671BD54E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = D41D8CD98F00B204E9800998ECF8427E /* Project object */; +} diff --git a/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/FontAwesome.swift-FontAwesome.swift.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/FontAwesome.swift-FontAwesome.swift.xcscheme new file mode 100644 index 0000000..f3f0857 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/FontAwesome.swift-FontAwesome.swift.xcscheme @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/FontAwesome.swift.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/FontAwesome.swift.xcscheme new file mode 100644 index 0000000..20164b4 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/FontAwesome.swift.xcscheme @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/KYCircularProgress.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/KYCircularProgress.xcscheme new file mode 100644 index 0000000..2e43246 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/KYCircularProgress.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/Pods-Meditation.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/Pods-Meditation.xcscheme new file mode 100644 index 0000000..4528d21 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/Pods-Meditation.xcscheme @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/SCLAlertView.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/SCLAlertView.xcscheme new file mode 100644 index 0000000..f12d330 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/SCLAlertView.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/SwiftyJSON.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/SwiftyJSON.xcscheme new file mode 100644 index 0000000..8dfe5df --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/SwiftyJSON.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/SwiftySound.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/SwiftySound.xcscheme new file mode 100644 index 0000000..3140495 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/SwiftySound.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/SwiftyStoreKit.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/SwiftyStoreKit.xcscheme new file mode 100644 index 0000000..63365b3 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/SwiftyStoreKit.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/paper-onboarding.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/paper-onboarding.xcscheme new file mode 100644 index 0000000..8145e2a --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/paper-onboarding.xcscheme @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/xcschememanagement.plist b/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..9d1c22a --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/joeytawadrous.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,74 @@ + + + + + SchemeUserState + + FontAwesome.swift-FontAwesome.swift.xcscheme + + isShown + + orderHint + 6 + + FontAwesome.swift.xcscheme + + isShown + + orderHint + 5 + + KYCircularProgress.xcscheme + + isShown + + orderHint + 7 + + Pods-Meditation.xcscheme + + isShown + + orderHint + 9 + + SCLAlertView.xcscheme + + isShown + + orderHint + 10 + + SwiftyJSON.xcscheme + + isShown + + orderHint + 11 + + SwiftySound.xcscheme + + isShown + + orderHint + 12 + + SwiftyStoreKit.xcscheme + + isShown + + orderHint + 13 + + paper-onboarding.xcscheme + + isShown + + orderHint + 8 + + + SuppressBuildableAutocreation + + + diff --git a/Pods/SCLAlertView/LICENCE b/Pods/SCLAlertView/LICENCE new file mode 100644 index 0000000..46b7dbd --- /dev/null +++ b/Pods/SCLAlertView/LICENCE @@ -0,0 +1,19 @@ +Copyright (c) 2013-2014 SCPopUpView by Viktor Radchenko + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Pods/SCLAlertView/README.md b/Pods/SCLAlertView/README.md new file mode 100644 index 0000000..9ee9d0f --- /dev/null +++ b/Pods/SCLAlertView/README.md @@ -0,0 +1,280 @@ +SCLAlertView +=========== + +[![Version](https://img.shields.io/cocoapods/v/SCLAlertView.svg?style=flat)](http://cocoadocs.org/docsets/SCLAlertView/) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) + +Animated Alert View written in Swift, which can be used as a `UIAlertView` or `UIAlertController` replacement. Since `UIAlertView` is deprecated and `UIAlertController` only works on iOS 8.x or above, if you have a Swift project where you want to support iOS 7.x too, SCLAlertView is an ideal substitution. + +![BackgroundImage](https://raw.githubusercontent.com/vikmeup/SCPopUpView/master/successScreenshot.png)_ +![BackgroundImage](https://raw.githubusercontent.com/vikmeup/SCPopUpView/master/editScreenshot.png) + +Easy to use +---- + +### Get Started + +```swift +// Get started +SCLAlertView().showInfo("Important info", subTitle: "You are great") +``` + +### Updating the alert view + +```swift +let alertViewResponder: SCLAlertViewResponder = SCLAlertView().showSuccess("Hello World", subTitle: "This is a more descriptive text.") + +// Upon displaying, change/close view +alertViewResponder.setTitle("New Title") // Rename title +alertViewResponder.setSubTitle("New description") // Rename subtitle +alertViewResponder.close() // Close view +``` + +### Alternative alert types + +``` +SCLAlertView().showError("Hello Error", subTitle: "This is a more descriptive error text.") // Error +SCLAlertView().showNotice("Hello Notice", subTitle: "This is a more descriptive notice text.") // Notice +SCLAlertView().showWarning("Hello Warning", subTitle: "This is a more descriptive warning text.") // Warning +SCLAlertView().showInfo("Hello Info", subTitle: "This is a more descriptive info text.") // Info +SCLAlertView().showEdit("Hello Edit", subTitle: "This is a more descriptive info text.") // Edit +``` + +### Raw call to showTitle() + +```swift +SCLAlertView().showTitle( + "Congratulations", // Title of view + subTitle: "Operation successfully completed.", // String of view + duration: 2.0, // Duration to show before closing automatically, default: 0.0 + completeText: "Done", // Optional button value, default: "" + style: .success, // Styles - see below. + colorStyle: 0xA429FF, + colorTextButton: 0xFFFFFF +) +``` + +### Controls + +#### Custom Appearance + +```swift +// SCLAlertView.SCLAppearanc has more than 15 different properties to customize. See below. + +let appearance = SCLAlertView.SCLAppearance( + kTitleFont: UIFont(name: "HelveticaNeue", size: 20)!, + kTextFont: UIFont(name: "HelveticaNeue", size: 14)!, + kButtonFont: UIFont(name: "HelveticaNeue-Bold", size: 14)!, + showCloseButton: false +) + +let alert = SCLAlertView(appearance: appearance) +``` + +#### Add buttons + +```swift +let alertView = SCLAlertView() +alertView.addButton("First Button", target:self, selector:Selector("firstButton")) +alertView.addButton("Second Button") { + print("Second button tapped") +} +alertView.showSuccess("Button View", subTitle: "This alert view has buttons") +``` + +#### Hide default close button + +```swift +let appearance = SCLAlertView.SCLAppearance( + showCloseButton: false +) +let alertView = SCLAlertView(appearance: appearance) +alertView.showSuccess("No button", subTitle: "You will have hard times trying to close me") +``` + +#### Hide default close button & a duration to close the alert + +```swift +let appearance = SCLAlertView.SCLAppearance( + showCloseButton: false +) +let alertView = SCLAlertView(appearance: appearance) +alertView.showWarning("No button", subTitle: "Just wait for 3 seconds and I will disappear", duration: 3) +``` + + +#### Hide alert icon + +```swift +let appearance = SCLAlertView.SCLAppearance( + showCircularIcon: false +) +let alertView = SCLAlertView(appearance: appearance) +alertView.showSuccess("No icon", subTitle: "This is a clean alert without Icon!") +``` + +#### Use a custom icon + +```swift +let appearance = SCLAlertView.SCLAppearance( + showCircularIcon: true +) +let alertView = SCLAlertView(appearance: appearance) +let alertViewIcon = UIImage(named: "IconImage") //Replace the IconImage text with the image name +alertView.showInfo("Custom icon", subTitle: "This is a nice alert with a custom icon you choose", circleIconImage: alertViewIcon) +``` + + +#### Add Text fields + +```swift +// Add a text field +let alert = SCLAlertView() +let txt = alert.addTextField(title:"Enter your name") +alert.addButton("Show Name") { + println("Text value: \(txt.text)") +} +alert.showEdit("Edit View", subTitle: "This alert view shows a text box") +``` + +#### Use a custom subview instead of a subtitle +```swift +// Example of using the view to add two text fields to the alert +// Create the subview +let appearance = SCLAlertView.SCLAppearance( + kTitleFont: UIFont(name: "HelveticaNeue", size: 20)!, + kTextFont: UIFont(name: "HelveticaNeue", size: 14)!, + kButtonFont: UIFont(name: "HelveticaNeue-Bold", size: 14)!, + showCloseButton: false +) + +// Initialize SCLAlertView using custom Appearance +let alert = SCLAlertView(appearance: appearance) + +// Creat the subview +let subview = UIView(frame: CGRectMake(0,0,216,70)) +let x = (subview.frame.width - 180) / 2 + +// Add textfield 1 +let textfield1 = UITextField(frame: CGRectMake(x,10,180,25)) +textfield1.layer.borderColor = UIColor.greenColor().CGColor +textfield1.layer.borderWidth = 1.5 +textfield1.layer.cornerRadius = 5 +textfield1.placeholder = "Username" +textfield1.textAlignment = NSTextAlignment.Center +subview.addSubview(textfield1) + +// Add textfield 2 +let textfield2 = UITextField(frame: CGRectMake(x,textfield1.frame.maxY + 10,180,25)) +textfield2.secureTextEntry = true +textfield2.layer.borderColor = UIColor.blueColor().CGColor +textfield2.layer.borderWidth = 1.5 +textfield2.layer.cornerRadius = 5 +textfield1.layer.borderColor = UIColor.blueColor().CGColor +textfield2.placeholder = "Password" +textfield2.textAlignment = NSTextAlignment.Center +subview.addSubview(textfield2) + +// Add the subview to the alert's UI property +alert.customSubview = subview +alert.addButton("Login") { + print("Logged in") +} + +// Add Button with Duration Status and custom Colors +alert.addButton("Duration Button", backgroundColor: UIColor.brownColor(), textColor: UIColor.yellowColor(), showDurationStatus: true) { + print("Duration Button tapped") +} + +alert.showInfo("Login", subTitle: "", duration: 10) +``` + + +#### List of properties to customize + +```swift +// Button +kButtonFont: UIFont +buttonCornerRadius : CGFloat +showCloseButton: Bool +kButtonHeight: CGFloat + +// Circle Image +showCircularIcon: Bool +kCircleTopPosition: CGFloat +kCircleBackgroundTopPosition: CGFloat +kCircleHeight: CGFloat +kCircleIconHeight: CGFloat + +// Text +kTitleFont: UIFont +kTitleTop:CGFloat +kTitleHeight:CGFloat +kTextFont: UIFont +kTextHeight: CGFloat +kTextFieldHeight: CGFloat +kTextViewdHeight: CGFloat + +// View +kDefaultShadowOpacity: CGFloat +kWindowWidth: CGFloat +kWindowHeight: CGFloat +shouldAutoDismiss: Bool // Set this false to 'Disable' Auto hideView when SCLButton is tapped +fieldCornerRadius : CGFloat +contentViewCornerRadius : CGFloat +disableTapGesture: Bool // set this to true if adding tableview to subView +``` + + +### Alert View Styles + +```swift +enum SCLAlertViewStyle: Int { + case success, error, notice, warning, info, edit, wait, question +} +``` + + +### Alert show animation Styles + +```swift +// Animation Styles +public enum SCLAnimationStyle { + case noAnimation, topToBottom, bottomToTop, leftToRight, rightToLeft +} +``` + + +Installation +--- + +SCLAlertView is available through + +### [CocoaPods](http://cocoapods.org) + +To install add the following line to your Podfile: + + pod 'SCLAlertView' + +### [Carthage](https://github.com/Carthage/Carthage) + +To install add the following line to your Cartfile: + +`github "vikmeup/SCLAlertView-Swift" "master"` + +Collaboration +--- + +I tried to build an easy to use API, while beeing flexible enough for multiple variations, but I'm sure there are ways of improving and adding more features, so feel free to collaborate with ideas, issues and/or pull requests. + +Incoming improvements +--- + +- More animations +- Performance tests + +Has been developed initially for the [Scroll Feed](https://itunes.apple.com/us/app/scroll-feed/id842422195?ls=1&mt=8) app + +- Design [@SherzodMx](https://twitter.com/SherzodMx) Sherzod Max +- Development [@vikmeup](https://twitter.com/vikmeup) Viktor Radchenko +- Improvements by [@bih](http://github.com/bih) Bilawal Hameed, [@rizjoj](http://github.com/rizjoj) Riz Joj diff --git a/Pods/SCLAlertView/SCLAlertView/SCLAlertView.swift b/Pods/SCLAlertView/SCLAlertView/SCLAlertView.swift new file mode 100644 index 0000000..24ca4fa --- /dev/null +++ b/Pods/SCLAlertView/SCLAlertView/SCLAlertView.swift @@ -0,0 +1,1330 @@ +// +// SCLAlertView.swift +// SCLAlertView Example +// +// Created by Viktor Radchenko on 6/5/14. +// Copyright (c) 2014 Viktor Radchenko. All rights reserved. +// + +import Foundation +import UIKit +fileprivate func < (lhs: T?, rhs: T?) -> Bool { + switch (lhs, rhs) { + case let (l?, r?): + return l < r + case (nil, _?): + return true + default: + return false + } +} + +fileprivate func > (lhs: T?, rhs: T?) -> Bool { + switch (lhs, rhs) { + case let (l?, r?): + return l > r + default: + return rhs < lhs + } +} + + +// Pop Up Styles +public enum SCLAlertViewStyle { + case success, error, notice, warning, info, edit, wait, question + + public var defaultColorInt: UInt { + switch self { + case .success: + return 0x22B573 + case .error: + return 0xC1272D + case .notice: + return 0x727375 + case .warning: + return 0xFFD110 + case .info: + return 0x2866BF + case .edit: + return 0xA429FF + case .wait: + return 0xD62DA5 + case .question: + return 0x727375 + } + + } + +} + +// Animation Styles +public enum SCLAnimationStyle { + case noAnimation, topToBottom, bottomToTop, leftToRight, rightToLeft +} + +// Action Types +public enum SCLActionType { + case none, selector, closure +} + +public enum SCLAlertButtonLayout { + case horizontal, vertical +} +// Button sub-class +open class SCLButton: UIButton { + var actionType = SCLActionType.none + var target:AnyObject! + var selector:Selector! + var action:(()->Void)! + var customBackgroundColor:UIColor? + var customTextColor:UIColor? + var initialTitle:String! + var showTimeout:ShowTimeoutConfiguration? + + public struct ShowTimeoutConfiguration { + let prefix: String + let suffix: String + + public init(prefix: String = "", suffix: String = "") { + self.prefix = prefix + self.suffix = suffix + } + } + + public init() { + super.init(frame: CGRect.zero) + } + + required public init?(coder aDecoder: NSCoder) { + super.init(coder:aDecoder) + } + + override public init(frame:CGRect) { + super.init(frame:frame) + } +} + +// Allow alerts to be closed/renamed in a chainable manner +// Example: SCLAlertView().showSuccess(self, title: "Test", subTitle: "Value").close() +open class SCLAlertViewResponder { + let alertview: SCLAlertView + + // Initialisation and Title/Subtitle/Close functions + public init(alertview: SCLAlertView) { + self.alertview = alertview + } + + open func setTitle(_ title: String) { + self.alertview.labelTitle.text = title + } + + open func setSubTitle(_ subTitle: String) { + self.alertview.viewText.text = subTitle + } + + open func close() { + self.alertview.hideView() + } + + open func setDismissBlock(_ dismissBlock: @escaping DismissBlock) { + self.alertview.dismissBlock = dismissBlock + } +} + +let kCircleHeightBackground: CGFloat = 62.0 +let uniqueTag: Int = Int(arc4random() % UInt32(Int32.max)) +let uniqueAccessibilityIdentifier: String = "SCLAlertView" + +public typealias DismissBlock = () -> Void + +// The Main Class +open class SCLAlertView: UIViewController { + + public struct SCLAppearance { + let kDefaultShadowOpacity: CGFloat + let kCircleTopPosition: CGFloat + let kCircleBackgroundTopPosition: CGFloat + let kCircleHeight: CGFloat + let kCircleIconHeight: CGFloat + let kTitleTop:CGFloat + let kTitleHeight:CGFloat + let kTitleMinimumScaleFactor: CGFloat + let kWindowWidth: CGFloat + var kWindowHeight: CGFloat + var kTextHeight: CGFloat + let kTextFieldHeight: CGFloat + let kTextViewdHeight: CGFloat + let kButtonHeight: CGFloat + let circleBackgroundColor: UIColor + let contentViewColor: UIColor + let contentViewBorderColor: UIColor + let titleColor: UIColor + + // Fonts + let kTitleFont: UIFont + let kTextFont: UIFont + let kButtonFont: UIFont + + // UI Options + var disableTapGesture: Bool + var showCloseButton: Bool + var showCircularIcon: Bool + var shouldAutoDismiss: Bool // Set this false to 'Disable' Auto hideView when SCLButton is tapped + var contentViewCornerRadius : CGFloat + var fieldCornerRadius : CGFloat + var buttonCornerRadius : CGFloat + var dynamicAnimatorActive : Bool + var buttonsLayout: SCLAlertButtonLayout + + // Actions + var hideWhenBackgroundViewIsTapped: Bool + + // Activity indicator + var activityIndicatorStyle: UIActivityIndicatorViewStyle + + public init(kDefaultShadowOpacity: CGFloat = 0.7, kCircleTopPosition: CGFloat = 0.0, kCircleBackgroundTopPosition: CGFloat = 6.0, kCircleHeight: CGFloat = 56.0, kCircleIconHeight: CGFloat = 20.0, kTitleTop:CGFloat = 30.0, kTitleHeight:CGFloat = 25.0, kWindowWidth: CGFloat = 240.0, kWindowHeight: CGFloat = 178.0, kTextHeight: CGFloat = 90.0, kTextFieldHeight: CGFloat = 45.0, kTextViewdHeight: CGFloat = 80.0, kButtonHeight: CGFloat = 45.0, kTitleFont: UIFont = UIFont.systemFont(ofSize: 20), kTitleMinimumScaleFactor: CGFloat = 1.0, kTextFont: UIFont = UIFont.systemFont(ofSize: 14), kButtonFont: UIFont = UIFont.boldSystemFont(ofSize: 14), showCloseButton: Bool = true, showCircularIcon: Bool = true, shouldAutoDismiss: Bool = true, contentViewCornerRadius: CGFloat = 5.0, fieldCornerRadius: CGFloat = 3.0, buttonCornerRadius: CGFloat = 3.0, hideWhenBackgroundViewIsTapped: Bool = false, circleBackgroundColor: UIColor = UIColor.white, contentViewColor: UIColor = UIColorFromRGB(0xFFFFFF), contentViewBorderColor: UIColor = UIColorFromRGB(0xCCCCCC), titleColor: UIColor = UIColorFromRGB(0x4D4D4D), dynamicAnimatorActive: Bool = false, disableTapGesture: Bool = false, buttonsLayout: SCLAlertButtonLayout = .vertical, activityIndicatorStyle: UIActivityIndicatorViewStyle = .white) { + + self.kDefaultShadowOpacity = kDefaultShadowOpacity + self.kCircleTopPosition = kCircleTopPosition + self.kCircleBackgroundTopPosition = kCircleBackgroundTopPosition + self.kCircleHeight = kCircleHeight + self.kCircleIconHeight = kCircleIconHeight + self.kTitleTop = kTitleTop + self.kTitleHeight = kTitleHeight + self.kWindowWidth = kWindowWidth + self.kWindowHeight = kWindowHeight + self.kTextHeight = kTextHeight + self.kTextFieldHeight = kTextFieldHeight + self.kTextViewdHeight = kTextViewdHeight + self.kButtonHeight = kButtonHeight + self.circleBackgroundColor = circleBackgroundColor + self.contentViewColor = contentViewColor + self.contentViewBorderColor = contentViewBorderColor + self.titleColor = titleColor + + self.kTitleFont = kTitleFont + self.kTitleMinimumScaleFactor = kTitleMinimumScaleFactor + self.kTextFont = kTextFont + self.kButtonFont = kButtonFont + + self.disableTapGesture = disableTapGesture + self.showCloseButton = showCloseButton + self.showCircularIcon = showCircularIcon + self.shouldAutoDismiss = shouldAutoDismiss + self.contentViewCornerRadius = contentViewCornerRadius + self.fieldCornerRadius = fieldCornerRadius + self.buttonCornerRadius = buttonCornerRadius + + self.hideWhenBackgroundViewIsTapped = hideWhenBackgroundViewIsTapped + self.dynamicAnimatorActive = dynamicAnimatorActive + self.buttonsLayout = buttonsLayout + + self.activityIndicatorStyle = activityIndicatorStyle + } + + mutating func setkWindowHeight(_ kWindowHeight:CGFloat) { + self.kWindowHeight = kWindowHeight + } + + mutating func setkTextHeight(_ kTextHeight:CGFloat) { + self.kTextHeight = kTextHeight + } + } + + public struct SCLTimeoutConfiguration { + + public typealias ActionType = () -> Void + + var value: TimeInterval + let action: ActionType + + mutating func increaseValue(by: Double) { + self.value = value + by + } + + public init(timeoutValue: TimeInterval, timeoutAction: @escaping ActionType) { + self.value = timeoutValue + self.action = timeoutAction + } + + } + + var appearance: SCLAppearance! + + // UI Colour + var viewColor = UIColor() + + // UI Options + open var iconTintColor: UIColor? + open var customSubview : UIView? + + // Members declaration + var baseView = UIView() + var labelTitle = UILabel() + var viewText = UITextView() + var contentView = UIView() + var circleBG = UIView(frame:CGRect(x:0, y:0, width:kCircleHeightBackground, height:kCircleHeightBackground)) + var circleView = UIView() + var circleIconView : UIView? + var timeout: SCLTimeoutConfiguration? + var showTimeoutTimer: Timer? + var timeoutTimer: Timer? + var dismissBlock : DismissBlock? + fileprivate var inputs = [UITextField]() + fileprivate var input = [UITextView]() + internal var buttons = [SCLButton]() + fileprivate var selfReference: SCLAlertView? + + public init(appearance: SCLAppearance) { + self.appearance = appearance + super.init(nibName:nil, bundle:nil) + setup() + } + + required public init?(coder aDecoder: NSCoder) { + fatalError("NSCoding not supported") + } + + required public init() { + appearance = SCLAppearance() + super.init(nibName:nil, bundle:nil) + setup() + } + + override public init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { + appearance = SCLAppearance() + super.init(nibName:nibNameOrNil, bundle:nibBundleOrNil) + } + + fileprivate func setup() { + // Set up main view + view.frame = UIScreen.main.bounds + view.autoresizingMask = [UIViewAutoresizing.flexibleHeight, UIViewAutoresizing.flexibleWidth] + view.backgroundColor = UIColor(red:0, green:0, blue:0, alpha:appearance.kDefaultShadowOpacity) + view.addSubview(baseView) + // Base View + baseView.frame = view.frame + baseView.addSubview(contentView) + // Content View + contentView.layer.cornerRadius = appearance.contentViewCornerRadius + contentView.layer.masksToBounds = true + contentView.layer.borderWidth = 0.5 + contentView.addSubview(labelTitle) + contentView.addSubview(viewText) + // Circle View + circleBG.backgroundColor = appearance.circleBackgroundColor + circleBG.layer.cornerRadius = circleBG.frame.size.height / 2 + baseView.addSubview(circleBG) + circleBG.addSubview(circleView) + let x = (kCircleHeightBackground - appearance.kCircleHeight) / 2 + circleView.frame = CGRect(x:x, y:x+appearance.kCircleTopPosition, width:appearance.kCircleHeight, height:appearance.kCircleHeight) + circleView.layer.cornerRadius = circleView.frame.size.height / 2 + // Title + labelTitle.numberOfLines = 0 + labelTitle.textAlignment = .center + labelTitle.font = appearance.kTitleFont + if(appearance.kTitleMinimumScaleFactor < 1){ + labelTitle.minimumScaleFactor = appearance.kTitleMinimumScaleFactor + labelTitle.adjustsFontSizeToFitWidth = true + } + labelTitle.frame = CGRect(x:12, y:appearance.kTitleTop, width: appearance.kWindowWidth - 24, height:appearance.kTitleHeight) + // View text + viewText.isEditable = false + viewText.isSelectable = false + viewText.textAlignment = .center + viewText.textContainerInset = UIEdgeInsets.zero + viewText.textContainer.lineFragmentPadding = 0; + viewText.font = appearance.kTextFont + // Colours + contentView.backgroundColor = appearance.contentViewColor + viewText.backgroundColor = appearance.contentViewColor + labelTitle.textColor = appearance.titleColor + viewText.textColor = appearance.titleColor + contentView.layer.borderColor = appearance.contentViewBorderColor.cgColor + //Gesture Recognizer for tapping outside the textinput + if appearance.disableTapGesture == false { + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(SCLAlertView.tapped(_:))) + tapGesture.numberOfTapsRequired = 1 + self.view.addGestureRecognizer(tapGesture) + } + } + + override open func viewWillLayoutSubviews() { + super.viewWillLayoutSubviews() + let rv = UIApplication.shared.keyWindow! as UIWindow + let sz = rv.frame.size + + // Set background frame + view.frame.size = sz + + let hMargin: CGFloat = 12 + let defaultTopOffset: CGFloat = 32 + + // get actual height of title text + var titleActualHeight: CGFloat = 0 + if let title = labelTitle.text { + titleActualHeight = title.heightWithConstrainedWidth(width: appearance.kWindowWidth - hMargin * 2, font: labelTitle.font) + 10 + // get the larger height for the title text + titleActualHeight = (titleActualHeight > appearance.kTitleHeight ? titleActualHeight : appearance.kTitleHeight) + } + + // computing the right size to use for the textView + let maxHeight = sz.height - 100 // max overall height + var consumedHeight = CGFloat(0) + consumedHeight += (titleActualHeight > 0 ? appearance.kTitleTop + titleActualHeight : defaultTopOffset) + consumedHeight += 14 + + if appearance.buttonsLayout == .vertical { + consumedHeight += appearance.kButtonHeight * CGFloat(buttons.count) + } else { + consumedHeight += appearance.kButtonHeight + } + consumedHeight += appearance.kTextFieldHeight * CGFloat(inputs.count) + consumedHeight += appearance.kTextViewdHeight * CGFloat(input.count) + let maxViewTextHeight = maxHeight - consumedHeight + let viewTextWidth = appearance.kWindowWidth - hMargin * 2 + var viewTextHeight = appearance.kTextHeight + + // Check if there is a custom subview and add it over the textview + if let customSubview = customSubview { + viewTextHeight = min(customSubview.frame.height, maxViewTextHeight) + viewText.text = "" + viewText.addSubview(customSubview) + } else { + // computing the right size to use for the textView + let suggestedViewTextSize = viewText.sizeThatFits(CGSize(width: viewTextWidth, height: CGFloat.greatestFiniteMagnitude)) + viewTextHeight = min(suggestedViewTextSize.height, maxViewTextHeight) + + // scroll management + if (suggestedViewTextSize.height > maxViewTextHeight) { + viewText.isScrollEnabled = true + } else { + viewText.isScrollEnabled = false + } + } + + let windowHeight = consumedHeight + viewTextHeight + // Set frames + var x = (sz.width - appearance.kWindowWidth) / 2 + var y = (sz.height - windowHeight - (appearance.kCircleHeight / 8)) / 2 + contentView.frame = CGRect(x:x, y:y, width:appearance.kWindowWidth, height:windowHeight) + contentView.layer.cornerRadius = appearance.contentViewCornerRadius + y -= kCircleHeightBackground * 0.6 + x = (sz.width - kCircleHeightBackground) / 2 + circleBG.frame = CGRect(x:x, y:y+appearance.kCircleBackgroundTopPosition, width:kCircleHeightBackground, height:kCircleHeightBackground) + + //adjust Title frame based on circularIcon show/hide flag +// let titleOffset : CGFloat = appearance.showCircularIcon ? 0.0 : -12.0 + let titleOffset: CGFloat = 0 + labelTitle.frame = labelTitle.frame.offsetBy(dx: 0, dy: titleOffset) + + // Subtitle + y = titleActualHeight > 0 ? appearance.kTitleTop + titleActualHeight + titleOffset : defaultTopOffset + viewText.frame = CGRect(x:hMargin, y:y, width: appearance.kWindowWidth - hMargin * 2, height:appearance.kTextHeight) + viewText.frame = CGRect(x:hMargin, y:y, width: viewTextWidth, height:viewTextHeight) + // Text fields + y += viewTextHeight + 14.0 + for txt in inputs { + txt.frame = CGRect(x:hMargin, y:y, width:appearance.kWindowWidth - hMargin * 2, height:30) + txt.layer.cornerRadius = appearance.fieldCornerRadius + y += appearance.kTextFieldHeight + } + for txt in input { + txt.frame = CGRect(x:hMargin, y:y, width:appearance.kWindowWidth - hMargin * 2, height:appearance.kTextViewdHeight - hMargin) + //txt.layer.cornerRadius = fieldCornerRadius + y += appearance.kTextViewdHeight + } + // Buttons + let numberOfButton = CGFloat(buttons.count) + let buttonsSpace = numberOfButton >= 1 ? CGFloat(10) * (numberOfButton - 1) : 0 + let widthEachButton = (appearance.kWindowWidth - 24 - buttonsSpace) / numberOfButton + var buttonX = CGFloat(12) + + switch appearance.buttonsLayout { + case .vertical: + for btn in buttons { + btn.frame = CGRect(x:12, y:y, width:appearance.kWindowWidth - 24, height:35) + btn.layer.cornerRadius = appearance.buttonCornerRadius + y += appearance.kButtonHeight + } + case .horizontal: + for btn in buttons { + btn.frame = CGRect(x:buttonX, y:y, width: widthEachButton, height:35) + btn.layer.cornerRadius = appearance.buttonCornerRadius + buttonX += widthEachButton + buttonX += buttonsSpace + } + } + } + + override open func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + NotificationCenter.default.addObserver(self, selector: #selector(SCLAlertView.keyboardWillShow(_:)), name:NSNotification.Name.UIKeyboardWillShow, object: nil); + NotificationCenter.default.addObserver(self, selector: #selector(SCLAlertView.keyboardWillHide(_:)), name:NSNotification.Name.UIKeyboardWillHide, object: nil); + } + + open override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil) + NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil) + } + + override open func touchesEnded(_ touches:Set, with event:UIEvent?) { + if event?.touches(for: view)?.count > 0 { + view.endEditing(true) + } + } + + open func addTextField(_ title:String?=nil)->UITextField { + // Update view height + appearance.setkWindowHeight(appearance.kWindowHeight + appearance.kTextFieldHeight) + // Add text field + let txt = UITextField() + txt.borderStyle = UITextBorderStyle.roundedRect + txt.font = appearance.kTextFont + txt.autocapitalizationType = UITextAutocapitalizationType.words + txt.clearButtonMode = UITextFieldViewMode.whileEditing + + txt.layer.masksToBounds = true + txt.layer.borderWidth = 1.0 + + if title != nil { + txt.placeholder = title! + } + + contentView.addSubview(txt) + inputs.append(txt) + return txt + } + + open func addTextView()->UITextView { + // Update view height + appearance.setkWindowHeight(appearance.kWindowHeight + appearance.kTextViewdHeight) + // Add text view + let txt = UITextView() + // No placeholder with UITextView but you can use KMPlaceholderTextView library + txt.font = appearance.kTextFont + //txt.autocapitalizationType = UITextAutocapitalizationType.Words + //txt.clearButtonMode = UITextFieldViewMode.WhileEditing + txt.layer.masksToBounds = true + txt.layer.borderWidth = 1.0 + contentView.addSubview(txt) + input.append(txt) + return txt + } + + @discardableResult + open func addButton(_ title:String, backgroundColor:UIColor? = nil, textColor:UIColor? = nil, showTimeout:SCLButton.ShowTimeoutConfiguration? = nil, action:@escaping ()->Void)->SCLButton { + let btn = addButton(title, backgroundColor: backgroundColor, textColor: textColor, showTimeout: showTimeout) + btn.actionType = SCLActionType.closure + btn.action = action + btn.addTarget(self, action:#selector(SCLAlertView.buttonTapped(_:)), for:.touchUpInside) + btn.addTarget(self, action:#selector(SCLAlertView.buttonTapDown(_:)), for:[.touchDown, .touchDragEnter]) + btn.addTarget(self, action:#selector(SCLAlertView.buttonRelease(_:)), for:[.touchUpInside, .touchUpOutside, .touchCancel, .touchDragOutside] ) + return btn + } + + @discardableResult + open func addButton(_ title:String, backgroundColor:UIColor? = nil, textColor:UIColor? = nil, showTimeout:SCLButton.ShowTimeoutConfiguration? = nil, target:AnyObject, selector:Selector)->SCLButton { + let btn = addButton(title, backgroundColor: backgroundColor, textColor: textColor, showTimeout: showTimeout) + btn.actionType = SCLActionType.selector + btn.target = target + btn.selector = selector + btn.addTarget(self, action:#selector(SCLAlertView.buttonTapped(_:)), for:.touchUpInside) + btn.addTarget(self, action:#selector(SCLAlertView.buttonTapDown(_:)), for:[.touchDown, .touchDragEnter]) + btn.addTarget(self, action:#selector(SCLAlertView.buttonRelease(_:)), for:[.touchUpInside, .touchUpOutside, .touchCancel, .touchDragOutside] ) + return btn + } + + @discardableResult + fileprivate func addButton(_ title:String, backgroundColor:UIColor? = nil, textColor:UIColor? = nil, showTimeout:SCLButton.ShowTimeoutConfiguration? = nil)->SCLButton { + // Update view height + appearance.setkWindowHeight(appearance.kWindowHeight + appearance.kButtonHeight) + + // Add button + let btn = SCLButton() + btn.layer.masksToBounds = true + btn.setTitle(title, for: UIControlState()) + btn.titleLabel?.font = appearance.kButtonFont + btn.customBackgroundColor = backgroundColor + btn.customTextColor = textColor + btn.initialTitle = title + btn.showTimeout = showTimeout + contentView.addSubview(btn) + buttons.append(btn) + return btn + } + + @objc func buttonTapped(_ btn:SCLButton) { + if btn.actionType == SCLActionType.closure { + btn.action() + } else if btn.actionType == SCLActionType.selector { + let ctrl = UIControl() + ctrl.sendAction(btn.selector, to:btn.target, for:nil) + } else { + print("Unknow action type for button") + } + + if(self.view.alpha != 0.0 && appearance.shouldAutoDismiss){ hideView() } + } + + + @objc func buttonTapDown(_ btn:SCLButton) { + var hue : CGFloat = 0 + var saturation : CGFloat = 0 + var brightness : CGFloat = 0 + var alpha : CGFloat = 0 + let pressBrightnessFactor = 0.85 + btn.backgroundColor?.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) + brightness = brightness * CGFloat(pressBrightnessFactor) + btn.backgroundColor = UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: alpha) + } + + @objc func buttonRelease(_ btn:SCLButton) { + btn.backgroundColor = btn.customBackgroundColor ?? viewColor + } + + var tmpContentViewFrameOrigin: CGPoint? + var tmpCircleViewFrameOrigin: CGPoint? + var keyboardHasBeenShown:Bool = false + + @objc func keyboardWillShow(_ notification: Notification) { + keyboardHasBeenShown = true + + guard let userInfo = (notification as NSNotification).userInfo else {return} + guard let endKeyBoardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.minY else {return} + + if tmpContentViewFrameOrigin == nil { + tmpContentViewFrameOrigin = self.contentView.frame.origin + } + + if tmpCircleViewFrameOrigin == nil { + tmpCircleViewFrameOrigin = self.circleBG.frame.origin + } + + var newContentViewFrameY = self.contentView.frame.maxY - endKeyBoardFrame + if newContentViewFrameY < 0 { + newContentViewFrameY = 0 + } + + let newBallViewFrameY = self.circleBG.frame.origin.y - newContentViewFrameY + self.contentView.frame.origin.y -= newContentViewFrameY + self.circleBG.frame.origin.y = newBallViewFrameY + } + + @objc func keyboardWillHide(_ notification: Notification) { + if(keyboardHasBeenShown){//This could happen on the simulator (keyboard will be hidden) + if(self.tmpContentViewFrameOrigin != nil){ + self.contentView.frame.origin.y = self.tmpContentViewFrameOrigin!.y + self.tmpContentViewFrameOrigin = nil + } + if(self.tmpCircleViewFrameOrigin != nil){ + self.circleBG.frame.origin.y = self.tmpCircleViewFrameOrigin!.y + self.tmpCircleViewFrameOrigin = nil + } + + keyboardHasBeenShown = false + } + } + + //Dismiss keyboard when tapped outside textfield & close SCLAlertView when hideWhenBackgroundViewIsTapped + @objc func tapped(_ gestureRecognizer: UITapGestureRecognizer) { + self.view.endEditing(true) + + if let tappedView = gestureRecognizer.view , tappedView.hitTest(gestureRecognizer.location(in: tappedView), with: nil) == baseView && appearance.hideWhenBackgroundViewIsTapped { + + hideView() + } + } + + // showCustom(view, title, subTitle, UIColor, UIImage) + @discardableResult + open func showCustom(_ title: String, subTitle: String, color: UIColor, icon: UIImage, closeButtonTitle:String?=nil, timeout:SCLTimeoutConfiguration?=nil, colorStyle: UInt=SCLAlertViewStyle.success.defaultColorInt, colorTextButton: UInt=0xFFFFFF, circleIconImage: UIImage? = nil, animationStyle: SCLAnimationStyle = .topToBottom) -> SCLAlertViewResponder { + var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0 + + color.getRed(&red, green: &green, blue: &blue, alpha: &alpha) + + var colorAsUInt32 : UInt32 = 0 + colorAsUInt32 += UInt32(red * 255.0) << 16 + colorAsUInt32 += UInt32(green * 255.0) << 8 + colorAsUInt32 += UInt32(blue * 255.0) + + let colorAsUInt = UInt(colorAsUInt32) + + return showTitle(title, subTitle: subTitle, timeout: timeout, completeText:closeButtonTitle, style: .success, colorStyle: colorAsUInt, colorTextButton: colorTextButton, circleIconImage: icon, animationStyle: animationStyle) + } + + // showSuccess(view, title, subTitle) + @discardableResult + open func showSuccess(_ title: String, subTitle: String, closeButtonTitle:String?=nil, timeout:SCLTimeoutConfiguration?=nil, colorStyle: UInt=SCLAlertViewStyle.success.defaultColorInt, colorTextButton: UInt=0xFFFFFF, circleIconImage: UIImage? = nil, animationStyle: SCLAnimationStyle = .topToBottom) -> SCLAlertViewResponder { + return showTitle(title, subTitle: subTitle, timeout: timeout, completeText:closeButtonTitle, style: .success, colorStyle: colorStyle, colorTextButton: colorTextButton, circleIconImage: circleIconImage, animationStyle: animationStyle) + } + + // showError(view, title, subTitle) + @discardableResult + open func showError(_ title: String, subTitle: String, closeButtonTitle:String?=nil, timeout:SCLTimeoutConfiguration?=nil, colorStyle: UInt=SCLAlertViewStyle.error.defaultColorInt, colorTextButton: UInt=0xFFFFFF, circleIconImage: UIImage? = nil, animationStyle: SCLAnimationStyle = .topToBottom) -> SCLAlertViewResponder { + return showTitle(title, subTitle: subTitle, timeout: timeout, completeText:closeButtonTitle, style: .error, colorStyle: colorStyle, colorTextButton: colorTextButton, circleIconImage: circleIconImage, animationStyle: animationStyle) + } + + // showNotice(view, title, subTitle) + @discardableResult + open func showNotice(_ title: String, subTitle: String, closeButtonTitle:String?=nil, timeout:SCLTimeoutConfiguration?=nil, colorStyle: UInt=SCLAlertViewStyle.notice.defaultColorInt, colorTextButton: UInt=0xFFFFFF, circleIconImage: UIImage? = nil, animationStyle: SCLAnimationStyle = .topToBottom) -> SCLAlertViewResponder { + return showTitle(title, subTitle: subTitle, timeout: timeout, completeText:closeButtonTitle, style: .notice, colorStyle: colorStyle, colorTextButton: colorTextButton, circleIconImage: circleIconImage, animationStyle: animationStyle) + } + + // showWarning(view, title, subTitle) + @discardableResult + open func showWarning(_ title: String, subTitle: String, closeButtonTitle:String?=nil, timeout:SCLTimeoutConfiguration?=nil, colorStyle: UInt=SCLAlertViewStyle.warning.defaultColorInt, colorTextButton: UInt=0x000000, circleIconImage: UIImage? = nil, animationStyle: SCLAnimationStyle = .topToBottom) -> SCLAlertViewResponder { + return showTitle(title, subTitle: subTitle, timeout: timeout, completeText:closeButtonTitle, style: .warning, colorStyle: colorStyle, colorTextButton: colorTextButton, circleIconImage: circleIconImage, animationStyle: animationStyle) + } + + // showInfo(view, title, subTitle) + @discardableResult + open func showInfo(_ title: String, subTitle: String, closeButtonTitle:String?=nil, timeout:SCLTimeoutConfiguration?=nil, colorStyle: UInt=SCLAlertViewStyle.info.defaultColorInt, colorTextButton: UInt=0xFFFFFF, circleIconImage: UIImage? = nil, animationStyle: SCLAnimationStyle = .topToBottom) -> SCLAlertViewResponder { + return showTitle(title, subTitle: subTitle, timeout: timeout, completeText:closeButtonTitle, style: .info, colorStyle: colorStyle, colorTextButton: colorTextButton, circleIconImage: circleIconImage, animationStyle: animationStyle) + } + + // showWait(view, title, subTitle) + @discardableResult + open func showWait(_ title: String, subTitle: String, closeButtonTitle:String?=nil, timeout:SCLTimeoutConfiguration?=nil, colorStyle: UInt?=SCLAlertViewStyle.wait.defaultColorInt, colorTextButton: UInt=0xFFFFFF, circleIconImage: UIImage? = nil, animationStyle: SCLAnimationStyle = .topToBottom) -> SCLAlertViewResponder { + return showTitle(title, subTitle: subTitle, timeout: timeout, completeText:closeButtonTitle, style: .wait, colorStyle: colorStyle, colorTextButton: colorTextButton, circleIconImage: circleIconImage, animationStyle: animationStyle) + } + + @discardableResult + open func showEdit(_ title: String, subTitle: String, closeButtonTitle:String?=nil, timeout:SCLTimeoutConfiguration?=nil, colorStyle: UInt=SCLAlertViewStyle.edit.defaultColorInt, colorTextButton: UInt=0xFFFFFF, circleIconImage: UIImage? = nil, animationStyle: SCLAnimationStyle = .topToBottom) -> SCLAlertViewResponder { + return showTitle(title, subTitle: subTitle, timeout: timeout, completeText:closeButtonTitle, style: .edit, colorStyle: colorStyle, colorTextButton: colorTextButton, circleIconImage: circleIconImage, animationStyle: animationStyle) + } + + // showTitle(view, title, subTitle, style) + @discardableResult + open func showTitle(_ title: String, subTitle: String, style: SCLAlertViewStyle, closeButtonTitle:String?=nil, timeout:SCLTimeoutConfiguration?=nil, colorStyle: UInt?=0x000000, colorTextButton: UInt=0xFFFFFF, circleIconImage: UIImage? = nil, animationStyle: SCLAnimationStyle = .topToBottom) -> SCLAlertViewResponder { + + return showTitle(title, subTitle: subTitle, timeout:timeout, completeText:closeButtonTitle, style: style, colorStyle: colorStyle, colorTextButton: colorTextButton, circleIconImage: circleIconImage, animationStyle: animationStyle) + } + + // showTitle(view, title, subTitle, timeout, style) + @discardableResult + open func showTitle(_ title: String, subTitle: String, timeout: SCLTimeoutConfiguration?, completeText: String?, style: SCLAlertViewStyle, colorStyle: UInt?=0x000000, colorTextButton: UInt?=0xFFFFFF, circleIconImage: UIImage? = nil, animationStyle: SCLAnimationStyle = .topToBottom) -> SCLAlertViewResponder { + selfReference = self + view.alpha = 0 + view.tag = uniqueTag + view.accessibilityIdentifier = uniqueAccessibilityIdentifier + let rv = UIApplication.shared.keyWindow! as UIWindow + rv.addSubview(view) + view.frame = rv.bounds + baseView.frame = rv.bounds + + // Alert colour/icon + viewColor = UIColor() + var iconImage: UIImage? + let colorInt = colorStyle ?? style.defaultColorInt + viewColor = UIColorFromRGB(colorInt) + + // Icon style + switch style { + case .success: + + iconImage = checkCircleIconImage(circleIconImage, defaultImage: SCLAlertViewStyleKit.imageOfCheckmark) + + case .error: + + iconImage = checkCircleIconImage(circleIconImage, defaultImage: SCLAlertViewStyleKit.imageOfCross) + + case .notice: + + iconImage = checkCircleIconImage(circleIconImage, defaultImage:SCLAlertViewStyleKit.imageOfNotice) + + case .warning: + + iconImage = checkCircleIconImage(circleIconImage, defaultImage:SCLAlertViewStyleKit.imageOfWarning) + + case .info: + + iconImage = checkCircleIconImage(circleIconImage, defaultImage:SCLAlertViewStyleKit.imageOfInfo) + + case .edit: + + iconImage = checkCircleIconImage(circleIconImage, defaultImage:SCLAlertViewStyleKit.imageOfEdit) + + case .wait: + iconImage = nil + + case .question: + iconImage = checkCircleIconImage(circleIconImage, defaultImage:SCLAlertViewStyleKit.imageOfQuestion) + } + + // Title + if !title.isEmpty { + self.labelTitle.text = title + let actualHeight = title.heightWithConstrainedWidth(width: appearance.kWindowWidth - 24, font: self.labelTitle.font) + self.labelTitle.frame = CGRect(x:12, y:appearance.kTitleTop, width: appearance.kWindowWidth - 24, height:actualHeight) + } + + // Subtitle + if !subTitle.isEmpty { + viewText.text = subTitle + // Adjust text view size, if necessary + let str = subTitle as NSString + let attr = [NSAttributedStringKey.font:viewText.font ?? UIFont()] + let sz = CGSize(width: appearance.kWindowWidth - 24, height:90) + let r = str.boundingRect(with: sz, options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes:attr, context:nil) + let ht = ceil(r.size.height) + if ht < appearance.kTextHeight { + appearance.kWindowHeight -= (appearance.kTextHeight - ht) + appearance.setkTextHeight(ht) + } + } + + // Done button + if appearance.showCloseButton { + _ = addButton(completeText ?? "Done", target:self, selector:#selector(SCLAlertView.hideView)) + } + + //hidden/show circular view based on the ui option + circleView.isHidden = !appearance.showCircularIcon + circleBG.isHidden = !appearance.showCircularIcon + + // Alert view colour and images + circleView.backgroundColor = viewColor + + // Spinner / icon + if style == .wait { + let indicator = UIActivityIndicatorView(activityIndicatorStyle: appearance.activityIndicatorStyle) + indicator.startAnimating() + circleIconView = indicator + } + else { + if let iconTintColor = iconTintColor { + circleIconView = UIImageView(image: iconImage!.withRenderingMode(.alwaysTemplate)) + circleIconView?.tintColor = iconTintColor + } + else { + circleIconView = UIImageView(image: iconImage!) + } + } + circleView.addSubview(circleIconView!) + let x = (appearance.kCircleHeight - appearance.kCircleIconHeight) / 2 + circleIconView!.frame = CGRect( x: x, y: x, width: appearance.kCircleIconHeight, height: appearance.kCircleIconHeight) + circleIconView?.layer.masksToBounds = true + + for txt in inputs { + txt.layer.borderColor = viewColor.cgColor + } + + for txt in input { + txt.layer.borderColor = viewColor.cgColor + } + + for btn in buttons { + if let customBackgroundColor = btn.customBackgroundColor { + // Custom BackgroundColor set + btn.backgroundColor = customBackgroundColor + } else { + // Use default BackgroundColor derived from AlertStyle + btn.backgroundColor = viewColor + } + + if let customTextColor = btn.customTextColor { + // Custom TextColor set + btn.setTitleColor(customTextColor, for:UIControlState()) + } else { + // Use default BackgroundColor derived from AlertStyle + btn.setTitleColor(UIColorFromRGB(colorTextButton ?? 0xFFFFFF), for:UIControlState()) + } + } + + // Adding timeout + if let timeout = timeout { + self.timeout = timeout + timeoutTimer?.invalidate() + timeoutTimer = Timer.scheduledTimer(timeInterval: timeout.value, target: self, selector: #selector(SCLAlertView.hideViewTimeout), userInfo: nil, repeats: false) + showTimeoutTimer?.invalidate() + showTimeoutTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(SCLAlertView.updateShowTimeout), userInfo: nil, repeats: true) + } + + // Animate in the alert view + self.showAnimation(animationStyle) + + // Chainable objects + return SCLAlertViewResponder(alertview: self) + } + + // Show animation in the alert view + fileprivate func showAnimation(_ animationStyle: SCLAnimationStyle = .topToBottom, animationStartOffset: CGFloat = -400.0, boundingAnimationOffset: CGFloat = 15.0, animationDuration: TimeInterval = 0.2) { + + let rv = UIApplication.shared.keyWindow! as UIWindow + var animationStartOrigin = self.baseView.frame.origin + var animationCenter : CGPoint = rv.center + + switch animationStyle { + + case .noAnimation: + self.view.alpha = 1.0 + return; + + case .topToBottom: + animationStartOrigin = CGPoint(x: animationStartOrigin.x, y: self.baseView.frame.origin.y + animationStartOffset) + animationCenter = CGPoint(x: animationCenter.x, y: animationCenter.y + boundingAnimationOffset) + + case .bottomToTop: + animationStartOrigin = CGPoint(x: animationStartOrigin.x, y: self.baseView.frame.origin.y - animationStartOffset) + animationCenter = CGPoint(x: animationCenter.x, y: animationCenter.y - boundingAnimationOffset) + + case .leftToRight: + animationStartOrigin = CGPoint(x: self.baseView.frame.origin.x + animationStartOffset, y: animationStartOrigin.y) + animationCenter = CGPoint(x: animationCenter.x + boundingAnimationOffset, y: animationCenter.y) + + case .rightToLeft: + animationStartOrigin = CGPoint(x: self.baseView.frame.origin.x - animationStartOffset, y: animationStartOrigin.y) + animationCenter = CGPoint(x: animationCenter.x - boundingAnimationOffset, y: animationCenter.y) + } + + self.baseView.frame.origin = animationStartOrigin + + if self.appearance.dynamicAnimatorActive { + UIView.animate(withDuration: animationDuration, animations: { + self.view.alpha = 1.0 + }) + self.animate(item: self.baseView, center: rv.center) + } else { + UIView.animate(withDuration: animationDuration, animations: { + self.view.alpha = 1.0 + self.baseView.center = animationCenter + }, completion: { finished in + UIView.animate(withDuration: animationDuration, animations: { + self.view.alpha = 1.0 + self.baseView.center = rv.center + }) + }) + } + } + + // DynamicAnimator function + var animator : UIDynamicAnimator? + var snapBehavior : UISnapBehavior? + + fileprivate func animate(item : UIView , center: CGPoint) { + + if let snapBehavior = self.snapBehavior { + self.animator?.removeBehavior(snapBehavior) + } + + self.animator = UIDynamicAnimator.init(referenceView: self.view) + let tempSnapBehavior = UISnapBehavior.init(item: item, snapTo: center) + self.animator?.addBehavior(tempSnapBehavior) + self.snapBehavior? = tempSnapBehavior + } + + // + @objc open func updateShowTimeout() { + + guard let timeout = self.timeout else { + return + } + + self.timeout?.value = timeout.value.advanced(by: -1) + + for btn in buttons { + guard let showTimeout = btn.showTimeout else { + continue + } + + let timeoutStr: String = showTimeout.prefix + String(Int(timeout.value)) + showTimeout.suffix + let txt = String(btn.initialTitle) + " " + timeoutStr + btn.setTitle(txt, for: UIControlState()) + + } + + } + + // Close SCLAlertView + @objc open func hideView() { + UIView.animate(withDuration: 0.2, animations: { + self.view.alpha = 0 + }, completion: { finished in + + // Stop timeoutTimer so alertView does not attempt to hide itself and fire it's dimiss block a second time when close button is tapped + self.timeoutTimer?.invalidate() + + // Stop showTimeoutTimer + self.showTimeoutTimer?.invalidate() + + if let dismissBlock = self.dismissBlock { + // Call completion handler when the alert is dismissed + dismissBlock() + } + + // This is necessary for SCLAlertView to be de-initialized, preventing a strong reference cycle with the viewcontroller calling SCLAlertView. + for button in self.buttons { + button.action = nil + button.target = nil + button.selector = nil + } + + self.view.removeFromSuperview() + self.selfReference = nil + }) + } + + @objc open func hideViewTimeout() { + self.timeout?.action() + self.hideView() + } + + func checkCircleIconImage(_ circleIconImage: UIImage?, defaultImage: UIImage) -> UIImage { + if let image = circleIconImage { + return image + } else { + return defaultImage + } + } + + //Return true if a SCLAlertView is already being shown, false otherwise + open func isShowing() -> Bool { + if let subviews = UIApplication.shared.keyWindow?.subviews { + for view in subviews { + if view.tag == uniqueTag && view.accessibilityIdentifier == uniqueAccessibilityIdentifier { + return true + } + } + } + return false + } +} + +// Helper function to convert from RGB to UIColor +public func UIColorFromRGB(_ rgbValue: UInt) -> UIColor { + return UIColor( + red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, + green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, + blue: CGFloat(rgbValue & 0x0000FF) / 255.0, + alpha: CGFloat(1.0) + ) +} + +// ------------------------------------ +// Icon drawing +// Code generated by PaintCode +// ------------------------------------ + +class SCLAlertViewStyleKit : NSObject { + + // Cache + struct Cache { + static var imageOfCheckmark: UIImage? + static var checkmarkTargets: [AnyObject]? + static var imageOfCross: UIImage? + static var crossTargets: [AnyObject]? + static var imageOfNotice: UIImage? + static var noticeTargets: [AnyObject]? + static var imageOfWarning: UIImage? + static var warningTargets: [AnyObject]? + static var imageOfInfo: UIImage? + static var infoTargets: [AnyObject]? + static var imageOfEdit: UIImage? + static var editTargets: [AnyObject]? + static var imageOfQuestion: UIImage? + static var questionTargets: [AnyObject]? + } + + // Initialization + /// swift 1.2 abolish func load + // override class func load() { + // } + + // Drawing Methods + class func drawCheckmark() { + // Checkmark Shape Drawing + let checkmarkShapePath = UIBezierPath() + checkmarkShapePath.move(to: CGPoint(x: 73.25, y: 14.05)) + checkmarkShapePath.addCurve(to: CGPoint(x: 64.51, y: 13.86), controlPoint1: CGPoint(x: 70.98, y: 11.44), controlPoint2: CGPoint(x: 66.78, y: 11.26)) + checkmarkShapePath.addLine(to: CGPoint(x: 27.46, y: 52)) + checkmarkShapePath.addLine(to: CGPoint(x: 15.75, y: 39.54)) + checkmarkShapePath.addCurve(to: CGPoint(x: 6.84, y: 39.54), controlPoint1: CGPoint(x: 13.48, y: 36.93), controlPoint2: CGPoint(x: 9.28, y: 36.93)) + checkmarkShapePath.addCurve(to: CGPoint(x: 6.84, y: 49.02), controlPoint1: CGPoint(x: 4.39, y: 42.14), controlPoint2: CGPoint(x: 4.39, y: 46.42)) + checkmarkShapePath.addLine(to: CGPoint(x: 22.91, y: 66.14)) + checkmarkShapePath.addCurve(to: CGPoint(x: 27.28, y: 68), controlPoint1: CGPoint(x: 24.14, y: 67.44), controlPoint2: CGPoint(x: 25.71, y: 68)) + checkmarkShapePath.addCurve(to: CGPoint(x: 31.65, y: 66.14), controlPoint1: CGPoint(x: 28.86, y: 68), controlPoint2: CGPoint(x: 30.43, y: 67.26)) + checkmarkShapePath.addLine(to: CGPoint(x: 73.08, y: 23.35)) + checkmarkShapePath.addCurve(to: CGPoint(x: 73.25, y: 14.05), controlPoint1: CGPoint(x: 75.52, y: 20.75), controlPoint2: CGPoint(x: 75.7, y: 16.65)) + checkmarkShapePath.close() + checkmarkShapePath.miterLimit = 4; + + UIColor.white.setFill() + checkmarkShapePath.fill() + } + + class func drawCross() { + // Cross Shape Drawing + let crossShapePath = UIBezierPath() + crossShapePath.move(to: CGPoint(x: 10, y: 70)) + crossShapePath.addLine(to: CGPoint(x: 70, y: 10)) + crossShapePath.move(to: CGPoint(x: 10, y: 10)) + crossShapePath.addLine(to: CGPoint(x: 70, y: 70)) + crossShapePath.lineCapStyle = CGLineCap.round; + crossShapePath.lineJoinStyle = CGLineJoin.round; + UIColor.white.setStroke() + crossShapePath.lineWidth = 14 + crossShapePath.stroke() + } + + class func drawNotice() { + // Notice Shape Drawing + let noticeShapePath = UIBezierPath() + noticeShapePath.move(to: CGPoint(x: 72, y: 48.54)) + noticeShapePath.addLine(to: CGPoint(x: 72, y: 39.9)) + noticeShapePath.addCurve(to: CGPoint(x: 66.38, y: 34.01), controlPoint1: CGPoint(x: 72, y: 36.76), controlPoint2: CGPoint(x: 69.48, y: 34.01)) + noticeShapePath.addCurve(to: CGPoint(x: 61.53, y: 35.97), controlPoint1: CGPoint(x: 64.82, y: 34.01), controlPoint2: CGPoint(x: 62.69, y: 34.8)) + noticeShapePath.addCurve(to: CGPoint(x: 60.36, y: 35.78), controlPoint1: CGPoint(x: 61.33, y: 35.97), controlPoint2: CGPoint(x: 62.3, y: 35.78)) + noticeShapePath.addLine(to: CGPoint(x: 60.36, y: 33.22)) + noticeShapePath.addCurve(to: CGPoint(x: 54.16, y: 26.16), controlPoint1: CGPoint(x: 60.36, y: 29.3), controlPoint2: CGPoint(x: 57.65, y: 26.16)) + noticeShapePath.addCurve(to: CGPoint(x: 48.73, y: 29.89), controlPoint1: CGPoint(x: 51.64, y: 26.16), controlPoint2: CGPoint(x: 50.67, y: 27.73)) + noticeShapePath.addLine(to: CGPoint(x: 48.73, y: 28.71)) + noticeShapePath.addCurve(to: CGPoint(x: 43.49, y: 21.64), controlPoint1: CGPoint(x: 48.73, y: 24.78), controlPoint2: CGPoint(x: 46.98, y: 21.64)) + noticeShapePath.addCurve(to: CGPoint(x: 39.03, y: 25.37), controlPoint1: CGPoint(x: 40.97, y: 21.64), controlPoint2: CGPoint(x: 39.03, y: 23.01)) + noticeShapePath.addLine(to: CGPoint(x: 39.03, y: 9.07)) + noticeShapePath.addCurve(to: CGPoint(x: 32.24, y: 2), controlPoint1: CGPoint(x: 39.03, y: 5.14), controlPoint2: CGPoint(x: 35.73, y: 2)) + noticeShapePath.addCurve(to: CGPoint(x: 25.45, y: 9.07), controlPoint1: CGPoint(x: 28.56, y: 2), controlPoint2: CGPoint(x: 25.45, y: 5.14)) + noticeShapePath.addLine(to: CGPoint(x: 25.45, y: 41.47)) + noticeShapePath.addCurve(to: CGPoint(x: 24.29, y: 43.44), controlPoint1: CGPoint(x: 25.45, y: 42.45), controlPoint2: CGPoint(x: 24.68, y: 43.04)) + noticeShapePath.addCurve(to: CGPoint(x: 9.55, y: 43.04), controlPoint1: CGPoint(x: 16.73, y: 40.88), controlPoint2: CGPoint(x: 11.88, y: 40.69)) + noticeShapePath.addCurve(to: CGPoint(x: 8, y: 46.58), controlPoint1: CGPoint(x: 8.58, y: 43.83), controlPoint2: CGPoint(x: 8, y: 45.2)) + noticeShapePath.addCurve(to: CGPoint(x: 14.4, y: 55.81), controlPoint1: CGPoint(x: 8.19, y: 50.31), controlPoint2: CGPoint(x: 12.07, y: 53.84)) + noticeShapePath.addLine(to: CGPoint(x: 27.2, y: 69.56)) + noticeShapePath.addCurve(to: CGPoint(x: 42.91, y: 77.8), controlPoint1: CGPoint(x: 30.5, y: 74.47), controlPoint2: CGPoint(x: 35.73, y: 77.21)) + noticeShapePath.addCurve(to: CGPoint(x: 43.88, y: 77.8), controlPoint1: CGPoint(x: 43.3, y: 77.8), controlPoint2: CGPoint(x: 43.68, y: 77.8)) + noticeShapePath.addCurve(to: CGPoint(x: 47.18, y: 78), controlPoint1: CGPoint(x: 45.04, y: 77.8), controlPoint2: CGPoint(x: 46.01, y: 78)) + noticeShapePath.addLine(to: CGPoint(x: 48.34, y: 78)) + noticeShapePath.addLine(to: CGPoint(x: 48.34, y: 78)) + noticeShapePath.addCurve(to: CGPoint(x: 71.61, y: 52.08), controlPoint1: CGPoint(x: 56.48, y: 78), controlPoint2: CGPoint(x: 69.87, y: 75.05)) + noticeShapePath.addCurve(to: CGPoint(x: 72, y: 48.54), controlPoint1: CGPoint(x: 71.81, y: 51.29), controlPoint2: CGPoint(x: 72, y: 49.72)) + noticeShapePath.close() + noticeShapePath.miterLimit = 4; + + UIColor.white.setFill() + noticeShapePath.fill() + } + + class func drawWarning() { + // Color Declarations + let greyColor = UIColor(red: 0.236, green: 0.236, blue: 0.236, alpha: 1.000) + + // Warning Group + // Warning Circle Drawing + let warningCirclePath = UIBezierPath() + warningCirclePath.move(to: CGPoint(x: 40.94, y: 63.39)) + warningCirclePath.addCurve(to: CGPoint(x: 36.03, y: 65.55), controlPoint1: CGPoint(x: 39.06, y: 63.39), controlPoint2: CGPoint(x: 37.36, y: 64.18)) + warningCirclePath.addCurve(to: CGPoint(x: 34.14, y: 70.45), controlPoint1: CGPoint(x: 34.9, y: 66.92), controlPoint2: CGPoint(x: 34.14, y: 68.49)) + warningCirclePath.addCurve(to: CGPoint(x: 36.22, y: 75.54), controlPoint1: CGPoint(x: 34.14, y: 72.41), controlPoint2: CGPoint(x: 34.9, y: 74.17)) + warningCirclePath.addCurve(to: CGPoint(x: 40.94, y: 77.5), controlPoint1: CGPoint(x: 37.54, y: 76.91), controlPoint2: CGPoint(x: 39.06, y: 77.5)) + warningCirclePath.addCurve(to: CGPoint(x: 45.86, y: 75.35), controlPoint1: CGPoint(x: 42.83, y: 77.5), controlPoint2: CGPoint(x: 44.53, y: 76.72)) + warningCirclePath.addCurve(to: CGPoint(x: 47.93, y: 70.45), controlPoint1: CGPoint(x: 47.18, y: 74.17), controlPoint2: CGPoint(x: 47.93, y: 72.41)) + warningCirclePath.addCurve(to: CGPoint(x: 45.86, y: 65.35), controlPoint1: CGPoint(x: 47.93, y: 68.49), controlPoint2: CGPoint(x: 47.18, y: 66.72)) + warningCirclePath.addCurve(to: CGPoint(x: 40.94, y: 63.39), controlPoint1: CGPoint(x: 44.53, y: 64.18), controlPoint2: CGPoint(x: 42.83, y: 63.39)) + warningCirclePath.close() + warningCirclePath.miterLimit = 4; + + greyColor.setFill() + warningCirclePath.fill() + + + // Warning Shape Drawing + let warningShapePath = UIBezierPath() + warningShapePath.move(to: CGPoint(x: 46.23, y: 4.26)) + warningShapePath.addCurve(to: CGPoint(x: 40.94, y: 2.5), controlPoint1: CGPoint(x: 44.91, y: 3.09), controlPoint2: CGPoint(x: 43.02, y: 2.5)) + warningShapePath.addCurve(to: CGPoint(x: 34.71, y: 4.26), controlPoint1: CGPoint(x: 38.68, y: 2.5), controlPoint2: CGPoint(x: 36.03, y: 3.09)) + warningShapePath.addCurve(to: CGPoint(x: 31.5, y: 8.77), controlPoint1: CGPoint(x: 33.01, y: 5.44), controlPoint2: CGPoint(x: 31.5, y: 7.01)) + warningShapePath.addLine(to: CGPoint(x: 31.5, y: 19.36)) + warningShapePath.addLine(to: CGPoint(x: 34.71, y: 54.44)) + warningShapePath.addCurve(to: CGPoint(x: 40.38, y: 58.16), controlPoint1: CGPoint(x: 34.9, y: 56.2), controlPoint2: CGPoint(x: 36.41, y: 58.16)) + warningShapePath.addCurve(to: CGPoint(x: 45.67, y: 54.44), controlPoint1: CGPoint(x: 44.34, y: 58.16), controlPoint2: CGPoint(x: 45.67, y: 56.01)) + warningShapePath.addLine(to: CGPoint(x: 48.5, y: 19.36)) + warningShapePath.addLine(to: CGPoint(x: 48.5, y: 8.77)) + warningShapePath.addCurve(to: CGPoint(x: 46.23, y: 4.26), controlPoint1: CGPoint(x: 48.5, y: 7.01), controlPoint2: CGPoint(x: 47.74, y: 5.44)) + warningShapePath.close() + warningShapePath.miterLimit = 4; + + greyColor.setFill() + warningShapePath.fill() + } + + class func drawInfo() { + // Color Declarations + let color0 = UIColor(red: 1.000, green: 1.000, blue: 1.000, alpha: 1.000) + + // Info Shape Drawing + let infoShapePath = UIBezierPath() + infoShapePath.move(to: CGPoint(x: 45.66, y: 15.96)) + infoShapePath.addCurve(to: CGPoint(x: 45.66, y: 5.22), controlPoint1: CGPoint(x: 48.78, y: 12.99), controlPoint2: CGPoint(x: 48.78, y: 8.19)) + infoShapePath.addCurve(to: CGPoint(x: 34.34, y: 5.22), controlPoint1: CGPoint(x: 42.53, y: 2.26), controlPoint2: CGPoint(x: 37.47, y: 2.26)) + infoShapePath.addCurve(to: CGPoint(x: 34.34, y: 15.96), controlPoint1: CGPoint(x: 31.22, y: 8.19), controlPoint2: CGPoint(x: 31.22, y: 12.99)) + infoShapePath.addCurve(to: CGPoint(x: 45.66, y: 15.96), controlPoint1: CGPoint(x: 37.47, y: 18.92), controlPoint2: CGPoint(x: 42.53, y: 18.92)) + infoShapePath.close() + infoShapePath.move(to: CGPoint(x: 48, y: 69.41)) + infoShapePath.addCurve(to: CGPoint(x: 40, y: 77), controlPoint1: CGPoint(x: 48, y: 73.58), controlPoint2: CGPoint(x: 44.4, y: 77)) + infoShapePath.addLine(to: CGPoint(x: 40, y: 77)) + infoShapePath.addCurve(to: CGPoint(x: 32, y: 69.41), controlPoint1: CGPoint(x: 35.6, y: 77), controlPoint2: CGPoint(x: 32, y: 73.58)) + infoShapePath.addLine(to: CGPoint(x: 32, y: 35.26)) + infoShapePath.addCurve(to: CGPoint(x: 40, y: 27.67), controlPoint1: CGPoint(x: 32, y: 31.08), controlPoint2: CGPoint(x: 35.6, y: 27.67)) + infoShapePath.addLine(to: CGPoint(x: 40, y: 27.67)) + infoShapePath.addCurve(to: CGPoint(x: 48, y: 35.26), controlPoint1: CGPoint(x: 44.4, y: 27.67), controlPoint2: CGPoint(x: 48, y: 31.08)) + infoShapePath.addLine(to: CGPoint(x: 48, y: 69.41)) + infoShapePath.close() + color0.setFill() + infoShapePath.fill() + } + + class func drawEdit() { + // Color Declarations + let color = UIColor(red:1.0, green:1.0, blue:1.0, alpha:1.0) + + // Edit shape Drawing + let editPathPath = UIBezierPath() + editPathPath.move(to: CGPoint(x: 71, y: 2.7)) + editPathPath.addCurve(to: CGPoint(x: 71.9, y: 15.2), controlPoint1: CGPoint(x: 74.7, y: 5.9), controlPoint2: CGPoint(x: 75.1, y: 11.6)) + editPathPath.addLine(to: CGPoint(x: 64.5, y: 23.7)) + editPathPath.addLine(to: CGPoint(x: 49.9, y: 11.1)) + editPathPath.addLine(to: CGPoint(x: 57.3, y: 2.6)) + editPathPath.addCurve(to: CGPoint(x: 69.7, y: 1.7), controlPoint1: CGPoint(x: 60.4, y: -1.1), controlPoint2: CGPoint(x: 66.1, y: -1.5)) + editPathPath.addLine(to: CGPoint(x: 71, y: 2.7)) + editPathPath.addLine(to: CGPoint(x: 71, y: 2.7)) + editPathPath.close() + editPathPath.move(to: CGPoint(x: 47.8, y: 13.5)) + editPathPath.addLine(to: CGPoint(x: 13.4, y: 53.1)) + editPathPath.addLine(to: CGPoint(x: 15.7, y: 55.1)) + editPathPath.addLine(to: CGPoint(x: 50.1, y: 15.5)) + editPathPath.addLine(to: CGPoint(x: 47.8, y: 13.5)) + editPathPath.addLine(to: CGPoint(x: 47.8, y: 13.5)) + editPathPath.close() + editPathPath.move(to: CGPoint(x: 17.7, y: 56.7)) + editPathPath.addLine(to: CGPoint(x: 23.8, y: 62.2)) + editPathPath.addLine(to: CGPoint(x: 58.2, y: 22.6)) + editPathPath.addLine(to: CGPoint(x: 52, y: 17.1)) + editPathPath.addLine(to: CGPoint(x: 17.7, y: 56.7)) + editPathPath.addLine(to: CGPoint(x: 17.7, y: 56.7)) + editPathPath.close() + editPathPath.move(to: CGPoint(x: 25.8, y: 63.8)) + editPathPath.addLine(to: CGPoint(x: 60.1, y: 24.2)) + editPathPath.addLine(to: CGPoint(x: 62.3, y: 26.1)) + editPathPath.addLine(to: CGPoint(x: 28.1, y: 65.7)) + editPathPath.addLine(to: CGPoint(x: 25.8, y: 63.8)) + editPathPath.addLine(to: CGPoint(x: 25.8, y: 63.8)) + editPathPath.close() + editPathPath.move(to: CGPoint(x: 25.9, y: 68.1)) + editPathPath.addLine(to: CGPoint(x: 4.2, y: 79.5)) + editPathPath.addLine(to: CGPoint(x: 11.3, y: 55.5)) + editPathPath.addLine(to: CGPoint(x: 25.9, y: 68.1)) + editPathPath.close() + editPathPath.miterLimit = 4; + editPathPath.usesEvenOddFillRule = true; + color.setFill() + editPathPath.fill() + } + + class func drawQuestion() { + // Color Declarations + let color = UIColor(red: CGFloat(1.0), green: CGFloat(1.0), blue: CGFloat(1.0), alpha: CGFloat(1.0)) + // Questionmark Shape Drawing + let questionShapePath = UIBezierPath() + questionShapePath.move(to: CGPoint(x: CGFloat(33.75), y: CGFloat(54.1))) + questionShapePath.addLine(to: CGPoint(x: CGFloat(44.15), y: CGFloat(54.1))) + questionShapePath.addLine(to: CGPoint(x: CGFloat(44.15), y: CGFloat(47.5))) + questionShapePath.addCurve(to: CGPoint(x: CGFloat(51.85), y: CGFloat(37.2)), controlPoint1: CGPoint(x: CGFloat(44.15), y: CGFloat(42.9)), controlPoint2: CGPoint(x: CGFloat(46.75), y: CGFloat(41.2))) + questionShapePath.addCurve(to: CGPoint(x: CGFloat(61.95), y: CGFloat(19.9)), controlPoint1: CGPoint(x: CGFloat(59.05), y: CGFloat(31.6)), controlPoint2: CGPoint(x: CGFloat(61.95), y: CGFloat(28.5))) + questionShapePath.addCurve(to: CGPoint(x: CGFloat(41.45), y: CGFloat(2.8)), controlPoint1: CGPoint(x: CGFloat(61.95), y: CGFloat(7.6)), controlPoint2: CGPoint(x: CGFloat(52.85), y: CGFloat(2.8))) + questionShapePath.addCurve(to: CGPoint(x: CGFloat(25.05), y: CGFloat(5.8)), controlPoint1: CGPoint(x: CGFloat(34.75), y: CGFloat(2.8)), controlPoint2: CGPoint(x: CGFloat(29.65), y: CGFloat(3.8))) + questionShapePath.addLine(to: CGPoint(x: CGFloat(25.05), y: CGFloat(14.4))) + questionShapePath.addCurve(to: CGPoint(x: CGFloat(38.15), y: CGFloat(12.3)), controlPoint1: CGPoint(x: CGFloat(29.15), y: CGFloat(13.2)), controlPoint2: CGPoint(x: CGFloat(32.35), y: CGFloat(12.3))) + questionShapePath.addCurve(to: CGPoint(x: CGFloat(49.65), y: CGFloat(20.8)), controlPoint1: CGPoint(x: CGFloat(45.65), y: CGFloat(12.3)), controlPoint2: CGPoint(x: CGFloat(49.65), y: CGFloat(14.4))) + questionShapePath.addCurve(to: CGPoint(x: CGFloat(43.65), y: CGFloat(31.7)), controlPoint1: CGPoint(x: CGFloat(49.65), y: CGFloat(26)), controlPoint2: CGPoint(x: CGFloat(47.95), y: CGFloat(28.4))) + questionShapePath.addCurve(to: CGPoint(x: CGFloat(33.75), y: CGFloat(46.6)), controlPoint1: CGPoint(x: CGFloat(37.15), y: CGFloat(36.9)), controlPoint2: CGPoint(x: CGFloat(33.75), y: CGFloat(39.7))) + questionShapePath.addLine(to: CGPoint(x: CGFloat(33.75), y: CGFloat(54.1))) + questionShapePath.close() + questionShapePath.move(to: CGPoint(x: CGFloat(33.15), y: CGFloat(75.4))) + questionShapePath.addLine(to: CGPoint(x: CGFloat(45.35), y: CGFloat(75.4))) + questionShapePath.addLine(to: CGPoint(x: CGFloat(45.35), y: CGFloat(63.7))) + questionShapePath.addLine(to: CGPoint(x: CGFloat(33.15), y: CGFloat(63.7))) + questionShapePath.addLine(to: CGPoint(x: CGFloat(33.15), y: CGFloat(75.4))) + questionShapePath.close() + color.setFill() + questionShapePath.fill() + } + + // Generated Images + class var imageOfCheckmark: UIImage { + if (Cache.imageOfCheckmark != nil) { + return Cache.imageOfCheckmark! + } + UIGraphicsBeginImageContextWithOptions(CGSize(width: 80, height: 80), false, 0) + SCLAlertViewStyleKit.drawCheckmark() + Cache.imageOfCheckmark = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return Cache.imageOfCheckmark! + } + + class var imageOfCross: UIImage { + if (Cache.imageOfCross != nil) { + return Cache.imageOfCross! + } + UIGraphicsBeginImageContextWithOptions(CGSize(width: 80, height: 80), false, 0) + SCLAlertViewStyleKit.drawCross() + Cache.imageOfCross = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return Cache.imageOfCross! + } + + class var imageOfNotice: UIImage { + if (Cache.imageOfNotice != nil) { + return Cache.imageOfNotice! + } + UIGraphicsBeginImageContextWithOptions(CGSize(width: 80, height: 80), false, 0) + SCLAlertViewStyleKit.drawNotice() + Cache.imageOfNotice = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return Cache.imageOfNotice! + } + + class var imageOfWarning: UIImage { + if (Cache.imageOfWarning != nil) { + return Cache.imageOfWarning! + } + UIGraphicsBeginImageContextWithOptions(CGSize(width: 80, height: 80), false, 0) + SCLAlertViewStyleKit.drawWarning() + Cache.imageOfWarning = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return Cache.imageOfWarning! + } + + class var imageOfInfo: UIImage { + if (Cache.imageOfInfo != nil) { + return Cache.imageOfInfo! + } + UIGraphicsBeginImageContextWithOptions(CGSize(width: 80, height: 80), false, 0) + SCLAlertViewStyleKit.drawInfo() + Cache.imageOfInfo = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return Cache.imageOfInfo! + } + + class var imageOfEdit: UIImage { + if (Cache.imageOfEdit != nil) { + return Cache.imageOfEdit! + } + UIGraphicsBeginImageContextWithOptions(CGSize(width: 80, height: 80), false, 0) + SCLAlertViewStyleKit.drawEdit() + Cache.imageOfEdit = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return Cache.imageOfEdit! + } + + class var imageOfQuestion: UIImage { + if (Cache.imageOfQuestion != nil) { + return Cache.imageOfQuestion! + } + UIGraphicsBeginImageContextWithOptions(CGSize(width: 80, height: 80), false, 0) + SCLAlertViewStyleKit.drawQuestion() + Cache.imageOfQuestion = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return Cache.imageOfQuestion! + } +} diff --git a/Pods/SCLAlertView/SCLAlertView/SCLExtensions.swift b/Pods/SCLAlertView/SCLAlertView/SCLExtensions.swift new file mode 100644 index 0000000..1399fa4 --- /dev/null +++ b/Pods/SCLAlertView/SCLAlertView/SCLExtensions.swift @@ -0,0 +1,53 @@ +// +// SCLExtensions.swift +// SCLAlertView +// +// Created by Christian Cabarrocas on 16/04/16. +// Copyright © 2016 Alexey Poimtsev. All rights reserved. +// + +import UIKit + +extension Int { + + func toUIColor() -> UIColor { + return UIColor( + red: CGFloat((self & 0xFF0000) >> 16) / 255.0, + green: CGFloat((self & 0x00FF00) >> 8) / 255.0, + blue: CGFloat(self & 0x0000FF) / 255.0, + alpha: CGFloat(1.0) + ) + } + + func toCGColor() -> CGColor { + return self.toUIColor().cgColor + } +} + +extension UInt { + + func toUIColor() -> UIColor { + return UIColor( + red: CGFloat((self & 0xFF0000) >> 16) / 255.0, + green: CGFloat((self & 0x00FF00) >> 8) / 255.0, + blue: CGFloat(self & 0x0000FF) / 255.0, + alpha: CGFloat(1.0) + ) + } + + func toCGColor() -> CGColor { + return self.toUIColor().cgColor + } +} + +extension String { + + func heightWithConstrainedWidth(width: CGFloat, font: UIFont) -> CGFloat { + let constraintRect = CGSize(width: width, height: CGFloat.greatestFiniteMagnitude) + + let boundingBox = self.boundingRect(with: constraintRect, options: NSStringDrawingOptions.usesLineFragmentOrigin, attributes: [NSAttributedStringKey.font: font], context: nil) + + return boundingBox.height + } + +} diff --git a/Pods/SwiftyJSON/LICENSE b/Pods/SwiftyJSON/LICENSE new file mode 100644 index 0000000..68e3fd7 --- /dev/null +++ b/Pods/SwiftyJSON/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Ruoyu Fu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Pods/SwiftyJSON/README.md b/Pods/SwiftyJSON/README.md new file mode 100644 index 0000000..3274979 --- /dev/null +++ b/Pods/SwiftyJSON/README.md @@ -0,0 +1,549 @@ +# SwiftyJSON + +[![Travis CI](https://travis-ci.org/SwiftyJSON/SwiftyJSON.svg?branch=master)](https://travis-ci.org/SwiftyJSON/SwiftyJSON) [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) ![CocoaPods](https://img.shields.io/cocoapods/v/SwiftyJSON.svg) ![Platform](https://img.shields.io/badge/platforms-iOS%208.0+%20%7C%20macOS%2010.10+%20%7C%20tvOS%209.0+%20%7C%20watchOS%202.0+-333333.svg) + +SwiftyJSON makes it easy to deal with JSON data in Swift. + +1. [Why is the typical JSON handling in Swift NOT good](#why-is-the-typical-json-handling-in-swift-not-good) +2. [Requirements](#requirements) +3. [Integration](#integration) +4. [Usage](#usage) + - [Initialization](#initialization) + - [Subscript](#subscript) + - [Loop](#loop) + - [Error](#error) + - [Optional getter](#optional-getter) + - [Non-optional getter](#non-optional-getter) + - [Setter](#setter) + - [Raw object](#raw-object) + - [Literal convertibles](#literal-convertibles) + - [Merging](#merging) +5. [Work with Alamofire](#work-with-alamofire) +6. [Work with Moya](#work-with-moya) + +> [中文介绍](http://tangplin.github.io/swiftyjson/) + + +## Why is the typical JSON handling in Swift NOT good? + +Swift is very strict about types. But although explicit typing is good for saving us from mistakes, it becomes painful when dealing with JSON and other areas that are, by nature, implicit about types. + +Take the Twitter API for example. Say we want to retrieve a user's "name" value of some tweet in Swift (according to [Twitter's API](https://developer.twitter.com/en/docs/tweets/timelines/api-reference/get-statuses-home_timeline)). + +The code would look like this: + +```swift +if let statusesArray = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [[String: Any]], + let user = statusesArray[0]["user"] as? [String: Any], + let username = user["name"] as? String { + // Finally we got the username +} +``` + +It's not good. + +Even if we use optional chaining, it would be messy: + +```swift +if let JSONObject = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [[String: Any]], + let username = (JSONObject[0]["user"] as? [String: Any])?["name"] as? String { + // There's our username +} +``` + +An unreadable mess--for something that should really be simple! + +With SwiftyJSON all you have to do is: + +```swift +let json = JSON(data: dataFromNetworking) +if let userName = json[0]["user"]["name"].string { + //Now you got your value +} +``` + +And don't worry about the Optional Wrapping thing. It's done for you automatically. + +```swift +let json = JSON(data: dataFromNetworking) +if let userName = json[999999]["wrong_key"]["wrong_name"].string { + //Calm down, take it easy, the ".string" property still produces the correct Optional String type with safety +} else { + //Print the error + print(json[999999]["wrong_key"]["wrong_name"]) +} +``` + +## Requirements + +- iOS 8.0+ | macOS 10.10+ | tvOS 9.0+ | watchOS 2.0+ +- Xcode 8 + +## Integration + +#### CocoaPods (iOS 8+, OS X 10.9+) + +You can use [CocoaPods](http://cocoapods.org/) to install `SwiftyJSON` by adding it to your `Podfile`: + +```ruby +platform :ios, '8.0' +use_frameworks! + +target 'MyApp' do + pod 'SwiftyJSON', '~> 4.0' +end +``` + +#### Carthage (iOS 8+, OS X 10.9+) + +You can use [Carthage](https://github.com/Carthage/Carthage) to install `SwiftyJSON` by adding it to your `Cartfile`: + +``` +github "SwiftyJSON/SwiftyJSON" ~> 4.0 +``` + +If you use Carthage to build your dependencies, make sure you have added `SwiftyJSON.framework` to the "Linked Frameworks and Libraries" section of your target, and have included them in your Carthage framework copying build phase. + +#### Swift Package Manager + +You can use [The Swift Package Manager](https://swift.org/package-manager) to install `SwiftyJSON` by adding the proper description to your `Package.swift` file: + +```swift +// swift-tools-version:4.0 +import PackageDescription + +let package = Package( + name: "YOUR_PROJECT_NAME", + dependencies: [ + .package(url: "https://github.com/SwiftyJSON/SwiftyJSON.git", from: "4.0.0"), + ] +) +``` +Then run `swift build` whenever you get prepared. + +#### Manually (iOS 7+, OS X 10.9+) + +To use this library in your project manually you may: + +1. for Projects, just drag SwiftyJSON.swift to the project tree +2. for Workspaces, include the whole SwiftyJSON.xcodeproj + +## Usage + +#### Initialization + +```swift +import SwiftyJSON +``` + +```swift +let json = JSON(data: dataFromNetworking) +``` +Or + +```swift +let json = JSON(jsonObject) +``` +Or + +```swift +if let dataFromString = jsonString.data(using: .utf8, allowLossyConversion: false) { + let json = JSON(data: dataFromString) +} +``` + +#### Subscript + +```swift +// Getting a double from a JSON Array +let name = json[0].double +``` + +```swift +// Getting an array of string from a JSON Array +let arrayNames = json["users"].arrayValue.map({$0["name"].stringValue}) +``` + +```swift +// Getting a string from a JSON Dictionary +let name = json["name"].stringValue +``` + +```swift +// Getting a string using a path to the element +let path: [JSONSubscriptType] = [1,"list",2,"name"] +let name = json[path].string +// Just the same +let name = json[1]["list"][2]["name"].string +// Alternatively +let name = json[1,"list",2,"name"].string +``` + +```swift +// With a hard way +let name = json[].string +``` + +```swift +// With a custom way +let keys:[JSONSubscriptType] = [1,"list",2,"name"] +let name = json[keys].string +``` + +#### Loop + +```swift +// If json is .Dictionary +for (key,subJson):(String, JSON) in json { + // Do something you want +} +``` + +*The first element is always a String, even if the JSON is an Array* + +```swift +// If json is .Array +// The `index` is 0.. = json["list"].arrayValue +``` + +```swift +// If not a Dictionary or nil, return [:] +let user: Dictionary = json["user"].dictionaryValue +``` + +#### Setter + +```swift +json["name"] = JSON("new-name") +json[0] = JSON(1) +``` + +```swift +json["id"].int = 1234567890 +json["coordinate"].double = 8766.766 +json["name"].string = "Jack" +json.arrayObject = [1,2,3,4] +json.dictionaryObject = ["name":"Jack", "age":25] +``` + +#### Raw object + +```swift +let rawObject: Any = json.object +``` + +```swift +let rawValue: Any = json.rawValue +``` + +```swift +//convert the JSON to raw NSData +do { + let rawData = try json.rawData() + //Do something you want +} catch { + print("Error \(error)") +} +``` + +```swift +//convert the JSON to a raw String +if let rawString = json.rawString() { + //Do something you want +} else { + print("json.rawString is nil") +} +``` + +#### Existence + +```swift +// shows you whether value specified in JSON or not +if json["name"].exists() +``` + +#### Literal convertibles + +For more info about literal convertibles: [Swift Literal Convertibles](http://nshipster.com/swift-literal-convertible/) + +```swift +// StringLiteralConvertible +let json: JSON = "I'm a json" +``` + +```swift +/ /IntegerLiteralConvertible +let json: JSON = 12345 +``` + +```swift +// BooleanLiteralConvertible +let json: JSON = true +``` + +```swift +// FloatLiteralConvertible +let json: JSON = 2.8765 +``` + +```swift +// DictionaryLiteralConvertible +let json: JSON = ["I":"am", "a":"json"] +``` + +```swift +// ArrayLiteralConvertible +let json: JSON = ["I", "am", "a", "json"] +``` + +```swift +// With subscript in array +var json: JSON = [1,2,3] +json[0] = 100 +json[1] = 200 +json[2] = 300 +json[999] = 300 // Don't worry, nothing will happen +``` + +```swift +// With subscript in dictionary +var json: JSON = ["name": "Jack", "age": 25] +json["name"] = "Mike" +json["age"] = "25" // It's OK to set String +json["address"] = "L.A." // Add the "address": "L.A." in json +``` + +```swift +// Array & Dictionary +var json: JSON = ["name": "Jack", "age": 25, "list": ["a", "b", "c", ["what": "this"]]] +json["list"][3]["what"] = "that" +json["list",3,"what"] = "that" +let path: [JSONSubscriptType] = ["list",3,"what"] +json[path] = "that" +``` + +```swift +// With other JSON objects +let user: JSON = ["username" : "Steve", "password": "supersecurepassword"] +let auth: JSON = [ + "user": user.object, // use user.object instead of just user + "apikey": "supersecretapitoken" +] +``` + +#### Merging + +It is possible to merge one JSON into another JSON. Merging a JSON into another JSON adds all non existing values to the original JSON which are only present in the `other` JSON. + +If both JSONs contain a value for the same key, _mostly_ this value gets overwritten in the original JSON, but there are two cases where it provides some special treatment: + +- In case of both values being a `JSON.Type.array` the values form the array found in the `other` JSON getting appended to the original JSON's array value. +- In case of both values being a `JSON.Type.dictionary` both JSON-values are getting merged the same way the encapsulating JSON is merged. + +In case, where two fields in a JSON have a different types, the value will get always overwritten. + +There are two different fashions for merging: `merge` modifies the original JSON, whereas `merged` works non-destructively on a copy. + +```swift +let original: JSON = [ + "first_name": "John", + "age": 20, + "skills": ["Coding", "Reading"], + "address": [ + "street": "Front St", + "zip": "12345", + ] +] + +let update: JSON = [ + "last_name": "Doe", + "age": 21, + "skills": ["Writing"], + "address": [ + "zip": "12342", + "city": "New York City" + ] +] + +let updated = original.merge(with: update) +// [ +// "first_name": "John", +// "last_name": "Doe", +// "age": 21, +// "skills": ["Coding", "Reading", "Writing"], +// "address": [ +// "street": "Front St", +// "zip": "12342", +// "city": "New York City" +// ] +// ] +``` + +## String representation +There are two options available: +- use the default Swift one +- use a custom one that will handle optionals well and represent `nil` as `"null"`: +```swift +let dict = ["1":2, "2":"two", "3": nil] as [String: Any?] +let json = JSON(dict) +let representation = json.rawString(options: [.castNilToNSNull: true]) +// representation is "{\"1\":2,\"2\":\"two\",\"3\":null}", which represents {"1":2,"2":"two","3":null} +``` + +## Work with [Alamofire](https://github.com/Alamofire/Alamofire) + +SwiftyJSON nicely wraps the result of the Alamofire JSON response handler: + +```swift +Alamofire.request(url, method: .get).validate().responseJSON { response in + switch response.result { + case .success(let value): + let json = JSON(value) + print("JSON: \(json)") + case .failure(let error): + print(error) + } +} +``` + +We also provide an extension of Alamofire for serializing NSData to SwiftyJSON's JSON. + +See: [Alamofire-SwiftyJSON](https://github.com/SwiftyJSON/Alamofire-SwiftyJSON) + + +## Work with [Moya](https://github.com/Moya/Moya) + +SwiftyJSON parse data to JSON: + +```swift +let provider = MoyaProvider() +provider.request(.showProducts) { result in + switch result { + case let .success(moyaResponse): + let data = moyaResponse.data + let json = JSON(data: data) // convert network data to json + print(json) + case let .failure(error): + print("error: \(error)") + } +} + +``` diff --git a/Pods/SwiftyJSON/Source/SwiftyJSON.swift b/Pods/SwiftyJSON/Source/SwiftyJSON.swift new file mode 100644 index 0000000..f3553fe --- /dev/null +++ b/Pods/SwiftyJSON/Source/SwiftyJSON.swift @@ -0,0 +1,1563 @@ +// SwiftyJSON.swift +// +// Copyright (c) 2014 - 2017 Ruoyu Fu, Pinglin Tang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +// MARK: - Error +// swiftlint:disable line_length +/// Error domain +@available(*, deprecated, message: "ErrorDomain is deprecated. Use `SwiftyJSONError.errorDomain` instead.", renamed: "SwiftyJSONError.errorDomain") +public let ErrorDomain: String = "SwiftyJSONErrorDomain" + +/// Error code +@available(*, deprecated, message: "ErrorUnsupportedType is deprecated. Use `SwiftyJSONError.unsupportedType` instead.", renamed: "SwiftyJSONError.unsupportedType") +public let ErrorUnsupportedType: Int = 999 +@available(*, deprecated, message: "ErrorIndexOutOfBounds is deprecated. Use `SwiftyJSONError.indexOutOfBounds` instead.", renamed: "SwiftyJSONError.indexOutOfBounds") +public let ErrorIndexOutOfBounds: Int = 900 +@available(*, deprecated, message: "ErrorWrongType is deprecated. Use `SwiftyJSONError.wrongType` instead.", renamed: "SwiftyJSONError.wrongType") +public let ErrorWrongType: Int = 901 +@available(*, deprecated, message: "ErrorNotExist is deprecated. Use `SwiftyJSONError.notExist` instead.", renamed: "SwiftyJSONError.notExist") +public let ErrorNotExist: Int = 500 +@available(*, deprecated, message: "ErrorInvalidJSON is deprecated. Use `SwiftyJSONError.invalidJSON` instead.", renamed: "SwiftyJSONError.invalidJSON") +public let ErrorInvalidJSON: Int = 490 + +public enum SwiftyJSONError: Int, Swift.Error { + case unsupportedType = 999 + case indexOutOfBounds = 900 + case elementTooDeep = 902 + case wrongType = 901 + case notExist = 500 + case invalidJSON = 490 +} + +extension SwiftyJSONError: CustomNSError { + + /// return the error domain of SwiftyJSONError + public static var errorDomain: String { return "com.swiftyjson.SwiftyJSON" } + + /// return the error code of SwiftyJSONError + public var errorCode: Int { return self.rawValue } + + /// return the userInfo of SwiftyJSONError + public var errorUserInfo: [String: Any] { + switch self { + case .unsupportedType: + return [NSLocalizedDescriptionKey: "It is an unsupported type."] + case .indexOutOfBounds: + return [NSLocalizedDescriptionKey: "Array Index is out of bounds."] + case .wrongType: + return [NSLocalizedDescriptionKey: "Couldn't merge, because the JSONs differ in type on top level."] + case .notExist: + return [NSLocalizedDescriptionKey: "Dictionary key does not exist."] + case .invalidJSON: + return [NSLocalizedDescriptionKey: "JSON is invalid."] + case .elementTooDeep: + return [NSLocalizedDescriptionKey: "Element too deep. Increase maxObjectDepth and make sure there is no reference loop."] + } + } +} + +// MARK: - JSON Type + +/** +JSON's type definitions. + +See http://www.json.org +*/ +public enum Type: Int { + case number + case string + case bool + case array + case dictionary + case null + case unknown +} + +// MARK: - JSON Base + +public struct JSON { + + /** + Creates a JSON using the data. + + - parameter data: The NSData used to convert to json.Top level object in data is an NSArray or NSDictionary + - parameter opt: The JSON serialization reading options. `[]` by default. + + - returns: The created JSON + */ + public init(data: Data, options opt: JSONSerialization.ReadingOptions = []) throws { + let object: Any = try JSONSerialization.jsonObject(with: data, options: opt) + self.init(jsonObject: object) + } + + /** + Creates a JSON object + - note: this does not parse a `String` into JSON, instead use `init(parseJSON: String)` + + - parameter object: the object + + - returns: the created JSON object + */ + public init(_ object: Any) { + switch object { + case let object as Data: + do { + try self.init(data: object) + } catch { + self.init(jsonObject: NSNull()) + } + default: + self.init(jsonObject: object) + } + } + + /** + Parses the JSON string into a JSON object + + - parameter json: the JSON string + + - returns: the created JSON object + */ + public init(parseJSON jsonString: String) { + if let data = jsonString.data(using: .utf8) { + self.init(data) + } else { + self.init(NSNull()) + } + } + + /** + Creates a JSON from JSON string + + - parameter json: Normal json string like '{"a":"b"}' + + - returns: The created JSON + */ + @available(*, deprecated, message: "Use instead `init(parseJSON: )`") + public static func parse(_ json: String) -> JSON { + return json.data(using: String.Encoding.utf8) + .flatMap { try? JSON(data: $0) } ?? JSON(NSNull()) + } + + /** + Creates a JSON using the object. + + - parameter jsonObject: The object must have the following properties: All objects are NSString/String, NSNumber/Int/Float/Double/Bool, NSArray/Array, NSDictionary/Dictionary, or NSNull; All dictionary keys are NSStrings/String; NSNumbers are not NaN or infinity. + + - returns: The created JSON + */ + fileprivate init(jsonObject: Any) { + self.object = jsonObject + } + + /** + Merges another JSON into this JSON, whereas primitive values which are not present in this JSON are getting added, + present values getting overwritten, array values getting appended and nested JSONs getting merged the same way. + + - parameter other: The JSON which gets merged into this JSON + + - throws `ErrorWrongType` if the other JSONs differs in type on the top level. + */ + public mutating func merge(with other: JSON) throws { + try self.merge(with: other, typecheck: true) + } + + /** + Merges another JSON into this JSON and returns a new JSON, whereas primitive values which are not present in this JSON are getting added, + present values getting overwritten, array values getting appended and nested JSONS getting merged the same way. + + - parameter other: The JSON which gets merged into this JSON + + - throws `ErrorWrongType` if the other JSONs differs in type on the top level. + + - returns: New merged JSON + */ + public func merged(with other: JSON) throws -> JSON { + var merged = self + try merged.merge(with: other, typecheck: true) + return merged + } + + /** + Private woker function which does the actual merging + Typecheck is set to true for the first recursion level to prevent total override of the source JSON + */ + fileprivate mutating func merge(with other: JSON, typecheck: Bool) throws { + if self.type == other.type { + switch self.type { + case .dictionary: + for (key, _) in other { + try self[key].merge(with: other[key], typecheck: false) + } + case .array: + self = JSON(self.arrayValue + other.arrayValue) + default: + self = other + } + } else { + if typecheck { + throw SwiftyJSONError.wrongType + } else { + self = other + } + } + } + + /// Private object + fileprivate var rawArray: [Any] = [] + fileprivate var rawDictionary: [String: Any] = [:] + fileprivate var rawString: String = "" + fileprivate var rawNumber: NSNumber = 0 + fileprivate var rawNull: NSNull = NSNull() + fileprivate var rawBool: Bool = false + + /// JSON type, fileprivate setter + public fileprivate(set) var type: Type = .null + + /// Error in JSON, fileprivate setter + public fileprivate(set) var error: SwiftyJSONError? + + /// Object in JSON + public var object: Any { + get { + switch self.type { + case .array: + return self.rawArray + case .dictionary: + return self.rawDictionary + case .string: + return self.rawString + case .number: + return self.rawNumber + case .bool: + return self.rawBool + default: + return self.rawNull + } + } + set { + error = nil + switch unwrap(newValue) { + case let number as NSNumber: + if number.isBool { + type = .bool + self.rawBool = number.boolValue + } else { + type = .number + self.rawNumber = number + } + case let string as String: + type = .string + self.rawString = string + case _ as NSNull: + type = .null + case nil: + type = .null + case let array as [Any]: + type = .array + self.rawArray = array + case let dictionary as [String: Any]: + type = .dictionary + self.rawDictionary = dictionary + default: + type = .unknown + error = SwiftyJSONError.unsupportedType + } + } + } + + /// The static null JSON + @available(*, unavailable, renamed:"null") + public static var nullJSON: JSON { return null } + public static var null: JSON { return JSON(NSNull()) } +} + +/// Private method to unwarp an object recursively +private func unwrap(_ object: Any) -> Any { + switch object { + case let json as JSON: + return unwrap(json.object) + case let array as [Any]: + return array.map(unwrap) + case let dictionary as [String: Any]: + var unwrappedDic = dictionary + for (k, v) in dictionary { + unwrappedDic[k] = unwrap(v) + } + return unwrappedDic + default: + return object + } +} + +public enum Index: Comparable { + case array(Int) + case dictionary(DictionaryIndex) + case null + + static public func == (lhs: Index, rhs: Index) -> Bool { + switch (lhs, rhs) { + case (.array(let left), .array(let right)): + return left == right + case (.dictionary(let left), .dictionary(let right)): + return left == right + case (.null, .null): return true + default: + return false + } + } + + static public func < (lhs: Index, rhs: Index) -> Bool { + switch (lhs, rhs) { + case (.array(let left), .array(let right)): + return left < right + case (.dictionary(let left), .dictionary(let right)): + return left < right + default: + return false + } + } +} + +public typealias JSONIndex = Index +public typealias JSONRawIndex = Index + +extension JSON: Swift.Collection { + + public typealias Index = JSONRawIndex + + public var startIndex: Index { + switch type { + case .array: + return .array(rawArray.startIndex) + case .dictionary: + return .dictionary(rawDictionary.startIndex) + default: + return .null + } + } + + public var endIndex: Index { + switch type { + case .array: + return .array(rawArray.endIndex) + case .dictionary: + return .dictionary(rawDictionary.endIndex) + default: + return .null + } + } + + public func index(after i: Index) -> Index { + switch i { + case .array(let idx): + return .array(rawArray.index(after: idx)) + case .dictionary(let idx): + return .dictionary(rawDictionary.index(after: idx)) + default: + return .null + } + } + + public subscript (position: Index) -> (String, JSON) { + switch position { + case .array(let idx): + return (String(idx), JSON(self.rawArray[idx])) + case .dictionary(let idx): + let (key, value) = self.rawDictionary[idx] + return (key, JSON(value)) + default: + return ("", JSON.null) + } + } +} + +// MARK: - Subscript + +/** + * To mark both String and Int can be used in subscript. + */ +public enum JSONKey { + case index(Int) + case key(String) +} + +public protocol JSONSubscriptType { + var jsonKey: JSONKey { get } +} + +extension Int: JSONSubscriptType { + public var jsonKey: JSONKey { + return JSONKey.index(self) + } +} + +extension String: JSONSubscriptType { + public var jsonKey: JSONKey { + return JSONKey.key(self) + } +} + +extension JSON { + + /// If `type` is `.array`, return json whose object is `array[index]`, otherwise return null json with error. + fileprivate subscript(index index: Int) -> JSON { + get { + if self.type != .array { + var r = JSON.null + r.error = self.error ?? SwiftyJSONError.wrongType + return r + } else if self.rawArray.indices.contains(index) { + return JSON(self.rawArray[index]) + } else { + var r = JSON.null + r.error = SwiftyJSONError.indexOutOfBounds + return r + } + } + set { + if self.type == .array && + self.rawArray.indices.contains(index) && + newValue.error == nil { + self.rawArray[index] = newValue.object + } + } + } + + /// If `type` is `.dictionary`, return json whose object is `dictionary[key]` , otherwise return null json with error. + fileprivate subscript(key key: String) -> JSON { + get { + var r = JSON.null + if self.type == .dictionary { + if let o = self.rawDictionary[key] { + r = JSON(o) + } else { + r.error = SwiftyJSONError.notExist + } + } else { + r.error = self.error ?? SwiftyJSONError.wrongType + } + return r + } + set { + if self.type == .dictionary && newValue.error == nil { + self.rawDictionary[key] = newValue.object + } + } + } + + /// If `sub` is `Int`, return `subscript(index:)`; If `sub` is `String`, return `subscript(key:)`. + fileprivate subscript(sub sub: JSONSubscriptType) -> JSON { + get { + switch sub.jsonKey { + case .index(let index): return self[index: index] + case .key(let key): return self[key: key] + } + } + set { + switch sub.jsonKey { + case .index(let index): self[index: index] = newValue + case .key(let key): self[key: key] = newValue + } + } + } + + /** + Find a json in the complex data structures by using array of Int and/or String as path. + + Example: + + ``` + let json = JSON[data] + let path = [9,"list","person","name"] + let name = json[path] + ``` + + The same as: let name = json[9]["list"]["person"]["name"] + + - parameter path: The target json's path. + + - returns: Return a json found by the path or a null json with error + */ + public subscript(path: [JSONSubscriptType]) -> JSON { + get { + return path.reduce(self) { $0[sub: $1] } + } + set { + switch path.count { + case 0: + return + case 1: + self[sub:path[0]].object = newValue.object + default: + var aPath = path + aPath.remove(at: 0) + var nextJSON = self[sub: path[0]] + nextJSON[aPath] = newValue + self[sub: path[0]] = nextJSON + } + } + } + + /** + Find a json in the complex data structures by using array of Int and/or String as path. + + - parameter path: The target json's path. Example: + + let name = json[9,"list","person","name"] + + The same as: let name = json[9]["list"]["person"]["name"] + + - returns: Return a json found by the path or a null json with error + */ + public subscript(path: JSONSubscriptType...) -> JSON { + get { + return self[path] + } + set { + self[path] = newValue + } + } +} + +// MARK: - LiteralConvertible + +extension JSON: Swift.ExpressibleByStringLiteral { + + public init(stringLiteral value: StringLiteralType) { + self.init(value) + } + + public init(extendedGraphemeClusterLiteral value: StringLiteralType) { + self.init(value) + } + + public init(unicodeScalarLiteral value: StringLiteralType) { + self.init(value) + } +} + +extension JSON: Swift.ExpressibleByIntegerLiteral { + + public init(integerLiteral value: IntegerLiteralType) { + self.init(value) + } +} + +extension JSON: Swift.ExpressibleByBooleanLiteral { + + public init(booleanLiteral value: BooleanLiteralType) { + self.init(value) + } +} + +extension JSON: Swift.ExpressibleByFloatLiteral { + + public init(floatLiteral value: FloatLiteralType) { + self.init(value) + } +} + +extension JSON: Swift.ExpressibleByDictionaryLiteral { + public init(dictionaryLiteral elements: (String, Any)...) { + let dictionary = elements.reduce(into: [String: Any](), { $0[$1.0] = $1.1}) + self.init(dictionary) + } +} + +extension JSON: Swift.ExpressibleByArrayLiteral { + + public init(arrayLiteral elements: Any...) { + self.init(elements) + } +} + +extension JSON: Swift.ExpressibleByNilLiteral { + + @available(*, deprecated, message: "use JSON.null instead. Will be removed in future versions") + public init(nilLiteral: ()) { + self.init(NSNull() as Any) + } +} + +// MARK: - Raw + +extension JSON: Swift.RawRepresentable { + + public init?(rawValue: Any) { + if JSON(rawValue).type == .unknown { + return nil + } else { + self.init(rawValue) + } + } + + public var rawValue: Any { + return self.object + } + + public func rawData(options opt: JSONSerialization.WritingOptions = JSONSerialization.WritingOptions(rawValue: 0)) throws -> Data { + guard JSONSerialization.isValidJSONObject(self.object) else { + throw SwiftyJSONError.invalidJSON + } + + return try JSONSerialization.data(withJSONObject: self.object, options: opt) + } + + public func rawString(_ encoding: String.Encoding = .utf8, options opt: JSONSerialization.WritingOptions = .prettyPrinted) -> String? { + do { + return try _rawString(encoding, options: [.jsonSerialization: opt]) + } catch { + print("Could not serialize object to JSON because:", error.localizedDescription) + return nil + } + } + + public func rawString(_ options: [writingOptionsKeys: Any]) -> String? { + let encoding = options[.encoding] as? String.Encoding ?? String.Encoding.utf8 + let maxObjectDepth = options[.maxObjextDepth] as? Int ?? 10 + do { + return try _rawString(encoding, options: options, maxObjectDepth: maxObjectDepth) + } catch { + print("Could not serialize object to JSON because:", error.localizedDescription) + return nil + } + } + + fileprivate func _rawString(_ encoding: String.Encoding = .utf8, options: [writingOptionsKeys: Any], maxObjectDepth: Int = 10) throws -> String? { + guard maxObjectDepth > 0 else { throw SwiftyJSONError.invalidJSON } + switch self.type { + case .dictionary: + do { + if !(options[.castNilToNSNull] as? Bool ?? false) { + let jsonOption = options[.jsonSerialization] as? JSONSerialization.WritingOptions ?? JSONSerialization.WritingOptions.prettyPrinted + let data = try self.rawData(options: jsonOption) + return String(data: data, encoding: encoding) + } + + guard let dict = self.object as? [String: Any?] else { + return nil + } + let body = try dict.keys.map { key throws -> String in + guard let value = dict[key] else { + return "\"\(key)\": null" + } + guard let unwrappedValue = value else { + return "\"\(key)\": null" + } + + let nestedValue = JSON(unwrappedValue) + guard let nestedString = try nestedValue._rawString(encoding, options: options, maxObjectDepth: maxObjectDepth - 1) else { + throw SwiftyJSONError.elementTooDeep + } + if nestedValue.type == .string { + return "\"\(key)\": \"\(nestedString.replacingOccurrences(of: "\\", with: "\\\\").replacingOccurrences(of: "\"", with: "\\\""))\"" + } else { + return "\"\(key)\": \(nestedString)" + } + } + + return "{\(body.joined(separator: ","))}" + } catch _ { + return nil + } + case .array: + do { + if !(options[.castNilToNSNull] as? Bool ?? false) { + let jsonOption = options[.jsonSerialization] as? JSONSerialization.WritingOptions ?? JSONSerialization.WritingOptions.prettyPrinted + let data = try self.rawData(options: jsonOption) + return String(data: data, encoding: encoding) + } + + guard let array = self.object as? [Any?] else { + return nil + } + let body = try array.map { value throws -> String in + guard let unwrappedValue = value else { + return "null" + } + + let nestedValue = JSON(unwrappedValue) + guard let nestedString = try nestedValue._rawString(encoding, options: options, maxObjectDepth: maxObjectDepth - 1) else { + throw SwiftyJSONError.invalidJSON + } + if nestedValue.type == .string { + return "\"\(nestedString.replacingOccurrences(of: "\\", with: "\\\\").replacingOccurrences(of: "\"", with: "\\\""))\"" + } else { + return nestedString + } + } + + return "[\(body.joined(separator: ","))]" + } catch _ { + return nil + } + case .string: + return self.rawString + case .number: + return self.rawNumber.stringValue + case .bool: + return self.rawBool.description + case .null: + return "null" + default: + return nil + } + } +} + +// MARK: - Printable, DebugPrintable + +extension JSON: Swift.CustomStringConvertible, Swift.CustomDebugStringConvertible { + + public var description: String { + if let string = self.rawString(options: .prettyPrinted) { + return string + } else { + return "unknown" + } + } + + public var debugDescription: String { + return description + } +} + +// MARK: - Array + +extension JSON { + + //Optional [JSON] + public var array: [JSON]? { + if self.type == .array { + return self.rawArray.map { JSON($0) } + } else { + return nil + } + } + + //Non-optional [JSON] + public var arrayValue: [JSON] { + return self.array ?? [] + } + + //Optional [Any] + public var arrayObject: [Any]? { + get { + switch self.type { + case .array: + return self.rawArray + default: + return nil + } + } + set { + if let array = newValue { + self.object = array + } else { + self.object = NSNull() + } + } + } +} + +// MARK: - Dictionary + +extension JSON { + + //Optional [String : JSON] + public var dictionary: [String: JSON]? { + if self.type == .dictionary { + var d = [String: JSON](minimumCapacity: rawDictionary.count) + for (key, value) in rawDictionary { + d[key] = JSON(value) + } + return d + } else { + return nil + } + } + + //Non-optional [String : JSON] + public var dictionaryValue: [String: JSON] { + return self.dictionary ?? [:] + } + + //Optional [String : Any] + + public var dictionaryObject: [String: Any]? { + get { + switch self.type { + case .dictionary: + return self.rawDictionary + default: + return nil + } + } + set { + if let v = newValue { + self.object = v + } else { + self.object = NSNull() + } + } + } +} + +// MARK: - Bool + +extension JSON { // : Swift.Bool + + //Optional bool + public var bool: Bool? { + get { + switch self.type { + case .bool: + return self.rawBool + default: + return nil + } + } + set { + if let newValue = newValue { + self.object = newValue as Bool + } else { + self.object = NSNull() + } + } + } + + //Non-optional bool + public var boolValue: Bool { + get { + switch self.type { + case .bool: + return self.rawBool + case .number: + return self.rawNumber.boolValue + case .string: + return ["true", "y", "t", "yes", "1"].contains { self.rawString.caseInsensitiveCompare($0) == .orderedSame } + default: + return false + } + } + set { + self.object = newValue + } + } +} + +// MARK: - String + +extension JSON { + + //Optional string + public var string: String? { + get { + switch self.type { + case .string: + return self.object as? String + default: + return nil + } + } + set { + if let newValue = newValue { + self.object = NSString(string: newValue) + } else { + self.object = NSNull() + } + } + } + + //Non-optional string + public var stringValue: String { + get { + switch self.type { + case .string: + return self.object as? String ?? "" + case .number: + return self.rawNumber.stringValue + case .bool: + return (self.object as? Bool).map { String($0) } ?? "" + default: + return "" + } + } + set { + self.object = NSString(string: newValue) + } + } +} + +// MARK: - Number + +extension JSON { + + //Optional number + public var number: NSNumber? { + get { + switch self.type { + case .number: + return self.rawNumber + case .bool: + return NSNumber(value: self.rawBool ? 1 : 0) + default: + return nil + } + } + set { + self.object = newValue ?? NSNull() + } + } + + //Non-optional number + public var numberValue: NSNumber { + get { + switch self.type { + case .string: + let decimal = NSDecimalNumber(string: self.object as? String) + if decimal == NSDecimalNumber.notANumber { // indicates parse error + return NSDecimalNumber.zero + } + return decimal + case .number: + return self.object as? NSNumber ?? NSNumber(value: 0) + case .bool: + return NSNumber(value: self.rawBool ? 1 : 0) + default: + return NSNumber(value: 0.0) + } + } + set { + self.object = newValue + } + } +} + +// MARK: - Null + +extension JSON { + + public var null: NSNull? { + get { + switch self.type { + case .null: + return self.rawNull + default: + return nil + } + } + set { + self.object = NSNull() + } + } + public func exists() -> Bool { + if let errorValue = error, (400...1000).contains(errorValue.errorCode) { + return false + } + return true + } +} + +// MARK: - URL + +extension JSON { + + //Optional URL + public var url: URL? { + get { + switch self.type { + case .string: + // Check for existing percent escapes first to prevent double-escaping of % character + if self.rawString.range(of: "%[0-9A-Fa-f]{2}", options: .regularExpression, range: nil, locale: nil) != nil { + return Foundation.URL(string: self.rawString) + } else if let encodedString_ = self.rawString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) { + // We have to use `Foundation.URL` otherwise it conflicts with the variable name. + return Foundation.URL(string: encodedString_) + } else { + return nil + } + default: + return nil + } + } + set { + self.object = newValue?.absoluteString ?? NSNull() + } + } +} + +// MARK: - Int, Double, Float, Int8, Int16, Int32, Int64 + +extension JSON { + + public var double: Double? { + get { + return self.number?.doubleValue + } + set { + if let newValue = newValue { + self.object = NSNumber(value: newValue) + } else { + self.object = NSNull() + } + } + } + + public var doubleValue: Double { + get { + return self.numberValue.doubleValue + } + set { + self.object = NSNumber(value: newValue) + } + } + + public var float: Float? { + get { + return self.number?.floatValue + } + set { + if let newValue = newValue { + self.object = NSNumber(value: newValue) + } else { + self.object = NSNull() + } + } + } + + public var floatValue: Float { + get { + return self.numberValue.floatValue + } + set { + self.object = NSNumber(value: newValue) + } + } + + public var int: Int? { + get { + return self.number?.intValue + } + set { + if let newValue = newValue { + self.object = NSNumber(value: newValue) + } else { + self.object = NSNull() + } + } + } + + public var intValue: Int { + get { + return self.numberValue.intValue + } + set { + self.object = NSNumber(value: newValue) + } + } + + public var uInt: UInt? { + get { + return self.number?.uintValue + } + set { + if let newValue = newValue { + self.object = NSNumber(value: newValue) + } else { + self.object = NSNull() + } + } + } + + public var uIntValue: UInt { + get { + return self.numberValue.uintValue + } + set { + self.object = NSNumber(value: newValue) + } + } + + public var int8: Int8? { + get { + return self.number?.int8Value + } + set { + if let newValue = newValue { + self.object = NSNumber(value: Int(newValue)) + } else { + self.object = NSNull() + } + } + } + + public var int8Value: Int8 { + get { + return self.numberValue.int8Value + } + set { + self.object = NSNumber(value: Int(newValue)) + } + } + + public var uInt8: UInt8? { + get { + return self.number?.uint8Value + } + set { + if let newValue = newValue { + self.object = NSNumber(value: newValue) + } else { + self.object = NSNull() + } + } + } + + public var uInt8Value: UInt8 { + get { + return self.numberValue.uint8Value + } + set { + self.object = NSNumber(value: newValue) + } + } + + public var int16: Int16? { + get { + return self.number?.int16Value + } + set { + if let newValue = newValue { + self.object = NSNumber(value: newValue) + } else { + self.object = NSNull() + } + } + } + + public var int16Value: Int16 { + get { + return self.numberValue.int16Value + } + set { + self.object = NSNumber(value: newValue) + } + } + + public var uInt16: UInt16? { + get { + return self.number?.uint16Value + } + set { + if let newValue = newValue { + self.object = NSNumber(value: newValue) + } else { + self.object = NSNull() + } + } + } + + public var uInt16Value: UInt16 { + get { + return self.numberValue.uint16Value + } + set { + self.object = NSNumber(value: newValue) + } + } + + public var int32: Int32? { + get { + return self.number?.int32Value + } + set { + if let newValue = newValue { + self.object = NSNumber(value: newValue) + } else { + self.object = NSNull() + } + } + } + + public var int32Value: Int32 { + get { + return self.numberValue.int32Value + } + set { + self.object = NSNumber(value: newValue) + } + } + + public var uInt32: UInt32? { + get { + return self.number?.uint32Value + } + set { + if let newValue = newValue { + self.object = NSNumber(value: newValue) + } else { + self.object = NSNull() + } + } + } + + public var uInt32Value: UInt32 { + get { + return self.numberValue.uint32Value + } + set { + self.object = NSNumber(value: newValue) + } + } + + public var int64: Int64? { + get { + return self.number?.int64Value + } + set { + if let newValue = newValue { + self.object = NSNumber(value: newValue) + } else { + self.object = NSNull() + } + } + } + + public var int64Value: Int64 { + get { + return self.numberValue.int64Value + } + set { + self.object = NSNumber(value: newValue) + } + } + + public var uInt64: UInt64? { + get { + return self.number?.uint64Value + } + set { + if let newValue = newValue { + self.object = NSNumber(value: newValue) + } else { + self.object = NSNull() + } + } + } + + public var uInt64Value: UInt64 { + get { + return self.numberValue.uint64Value + } + set { + self.object = NSNumber(value: newValue) + } + } +} + +// MARK: - Comparable + +extension JSON: Swift.Comparable {} + +public func == (lhs: JSON, rhs: JSON) -> Bool { + + switch (lhs.type, rhs.type) { + case (.number, .number): + return lhs.rawNumber == rhs.rawNumber + case (.string, .string): + return lhs.rawString == rhs.rawString + case (.bool, .bool): + return lhs.rawBool == rhs.rawBool + case (.array, .array): + return lhs.rawArray as NSArray == rhs.rawArray as NSArray + case (.dictionary, .dictionary): + return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary + case (.null, .null): + return true + default: + return false + } +} + +public func <= (lhs: JSON, rhs: JSON) -> Bool { + + switch (lhs.type, rhs.type) { + case (.number, .number): + return lhs.rawNumber <= rhs.rawNumber + case (.string, .string): + return lhs.rawString <= rhs.rawString + case (.bool, .bool): + return lhs.rawBool == rhs.rawBool + case (.array, .array): + return lhs.rawArray as NSArray == rhs.rawArray as NSArray + case (.dictionary, .dictionary): + return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary + case (.null, .null): + return true + default: + return false + } +} + +public func >= (lhs: JSON, rhs: JSON) -> Bool { + + switch (lhs.type, rhs.type) { + case (.number, .number): + return lhs.rawNumber >= rhs.rawNumber + case (.string, .string): + return lhs.rawString >= rhs.rawString + case (.bool, .bool): + return lhs.rawBool == rhs.rawBool + case (.array, .array): + return lhs.rawArray as NSArray == rhs.rawArray as NSArray + case (.dictionary, .dictionary): + return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary + case (.null, .null): + return true + default: + return false + } +} + +public func > (lhs: JSON, rhs: JSON) -> Bool { + + switch (lhs.type, rhs.type) { + case (.number, .number): + return lhs.rawNumber > rhs.rawNumber + case (.string, .string): + return lhs.rawString > rhs.rawString + default: + return false + } +} + +public func < (lhs: JSON, rhs: JSON) -> Bool { + + switch (lhs.type, rhs.type) { + case (.number, .number): + return lhs.rawNumber < rhs.rawNumber + case (.string, .string): + return lhs.rawString < rhs.rawString + default: + return false + } +} + +private let trueNumber = NSNumber(value: true) +private let falseNumber = NSNumber(value: false) +private let trueObjCType = String(cString: trueNumber.objCType) +private let falseObjCType = String(cString: falseNumber.objCType) + +// MARK: - NSNumber: Comparable + +extension NSNumber { + fileprivate var isBool: Bool { + let objCType = String(cString: self.objCType) + if (self.compare(trueNumber) == .orderedSame && objCType == trueObjCType) || (self.compare(falseNumber) == .orderedSame && objCType == falseObjCType) { + return true + } else { + return false + } + } +} + +func == (lhs: NSNumber, rhs: NSNumber) -> Bool { + switch (lhs.isBool, rhs.isBool) { + case (false, true): + return false + case (true, false): + return false + default: + return lhs.compare(rhs) == .orderedSame + } +} + +func != (lhs: NSNumber, rhs: NSNumber) -> Bool { + return !(lhs == rhs) +} + +func < (lhs: NSNumber, rhs: NSNumber) -> Bool { + + switch (lhs.isBool, rhs.isBool) { + case (false, true): + return false + case (true, false): + return false + default: + return lhs.compare(rhs) == .orderedAscending + } +} + +func > (lhs: NSNumber, rhs: NSNumber) -> Bool { + + switch (lhs.isBool, rhs.isBool) { + case (false, true): + return false + case (true, false): + return false + default: + return lhs.compare(rhs) == ComparisonResult.orderedDescending + } +} + +func <= (lhs: NSNumber, rhs: NSNumber) -> Bool { + + switch (lhs.isBool, rhs.isBool) { + case (false, true): + return false + case (true, false): + return false + default: + return lhs.compare(rhs) != .orderedDescending + } +} + +func >= (lhs: NSNumber, rhs: NSNumber) -> Bool { + + switch (lhs.isBool, rhs.isBool) { + case (false, true): + return false + case (true, false): + return false + default: + return lhs.compare(rhs) != .orderedAscending + } +} + +public enum writingOptionsKeys { + case jsonSerialization + case castNilToNSNull + case maxObjextDepth + case encoding +} + +// MARK: - JSON: Codable +extension JSON: Codable { + private static var codableTypes: [Codable.Type] { + return [ + Bool.self, + Int.self, + Int8.self, + Int16.self, + Int32.self, + Int64.self, + UInt.self, + UInt8.self, + UInt16.self, + UInt32.self, + UInt64.self, + Double.self, + String.self, + [JSON].self, + [String: JSON].self + ] + } + public init(from decoder: Decoder) throws { + var object: Any? + + if let container = try? decoder.singleValueContainer(), !container.decodeNil() { + for type in JSON.codableTypes { + if object != nil { + break + } + // try to decode value + switch type { + case let boolType as Bool.Type: + object = try? container.decode(boolType) + case let intType as Int.Type: + object = try? container.decode(intType) + case let int8Type as Int8.Type: + object = try? container.decode(int8Type) + case let int32Type as Int32.Type: + object = try? container.decode(int32Type) + case let int64Type as Int64.Type: + object = try? container.decode(int64Type) + case let uintType as UInt.Type: + object = try? container.decode(uintType) + case let uint8Type as UInt8.Type: + object = try? container.decode(uint8Type) + case let uint16Type as UInt16.Type: + object = try? container.decode(uint16Type) + case let uint32Type as UInt32.Type: + object = try? container.decode(uint32Type) + case let uint64Type as UInt64.Type: + object = try? container.decode(uint64Type) + case let doubleType as Double.Type: + object = try? container.decode(doubleType) + case let stringType as String.Type: + object = try? container.decode(stringType) + case let jsonValueArrayType as [JSON].Type: + object = try? container.decode(jsonValueArrayType) + case let jsonValueDictType as [String: JSON].Type: + object = try? container.decode(jsonValueDictType) + default: + break + } + } + } + self.init(object ?? NSNull()) + } + public func encode(to encoder: Encoder) throws { + var container = encoder.singleValueContainer() + if object is NSNull { + try container.encodeNil() + return + } + switch object { + case let intValue as Int: + try container.encode(intValue) + case let int8Value as Int8: + try container.encode(int8Value) + case let int32Value as Int32: + try container.encode(int32Value) + case let int64Value as Int64: + try container.encode(int64Value) + case let uintValue as UInt: + try container.encode(uintValue) + case let uint8Value as UInt8: + try container.encode(uint8Value) + case let uint16Value as UInt16: + try container.encode(uint16Value) + case let uint32Value as UInt32: + try container.encode(uint32Value) + case let uint64Value as UInt64: + try container.encode(uint64Value) + case let doubleValue as Double: + try container.encode(doubleValue) + case let boolValue as Bool: + try container.encode(boolValue) + case let stringValue as String: + try container.encode(stringValue) + case is [Any]: + let jsonValueArray = array ?? [] + try container.encode(jsonValueArray) + case is [String: Any]: + let jsonValueDictValue = dictionary ?? [:] + try container.encode(jsonValueDictValue) + default: + break + } + } +} diff --git a/Pods/SwiftySound/LICENSE b/Pods/SwiftySound/LICENSE new file mode 100644 index 0000000..0d49a65 --- /dev/null +++ b/Pods/SwiftySound/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Adam Cichy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Pods/SwiftySound/README.md b/Pods/SwiftySound/README.md new file mode 100644 index 0000000..922d13a --- /dev/null +++ b/Pods/SwiftySound/README.md @@ -0,0 +1,159 @@ +## SwiftySound + +[![CocoaPods License](https://img.shields.io/cocoapods/l/SwiftySound.svg)](https://raw.githubusercontent.com/adamcichy/SwiftySound/master/LICENSE) +[![CocoaPods](https://img.shields.io/cocoapods/v/SwiftySound.svg)](https://cocoapods.org/pods/SwiftySound) +[![CocoaPods Platforms](https://img.shields.io/cocoapods/p/SwiftySound.svg)](https://cocoapods.org/pods/SwiftySound) +[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-brightgreen.svg)](https://github.com/Carthage/Carthage) +[![SPM ready](https://img.shields.io/badge/SPM-ready-orange.svg)](https://swift.org/package-manager/) +[![Build status](https://api.travis-ci.org/adamcichy/SwiftySound.svg?branch=master)](https://travis-ci.org/adamcichy/SwiftySound) +[![codecov](https://codecov.io/gh/adamcichy/SwiftySound/branch/master/graph/badge.svg)](https://codecov.io/gh/adamcichy/SwiftySound) +[![codebeat](https://codebeat.co/badges/b51bedad-3c13-4ef2-a632-5c4e3d4fa759)](https://codebeat.co/projects/github-com-adamcichy-swiftysound-master) +[![Codacy](https://api.codacy.com/project/badge/Grade/048aad599e8549fa9f5d433f690dd796)](https://www.codacy.com/app/adamcichy/SwiftySound?utm_source=github.com&utm_medium=referral&utm_content=adamcichy/SwiftySound&utm_campaign=Badge_Grade) + +## Overview +SwiftySound is a simple library that lets you deal with Swift sounds easily. + +##### Static methods + +```swift +Sound.play(file: "dog.wav") +``` + +```swift +Sound.play(url: fileURL) +``` + +More advanced example: + +```swift +Sound.play(file: "dog", fileExtension: "wav", numberOfLoops: 2) +``` +The above will play the sound three times. + +Specify a negative number of loops to play the sound continously in an infinite loop: + +```swift +Sound.play(file: "dog", fileExtension: "wav", numberOfLoops: -1) +``` + +Stop currently playing sounds: + +```swift +Sound.stopAll() +``` + +Enable/disable all sounds: + +```swift +Sound.enabled = true +Sound.enabled = false +``` + +The value of `Sound.enabled` property will be automatically persisted in `UserDefaults` and restored on the next launch of your app. + +Change sound categories. SwiftySound provides a simple way of changing sound category: + +```swift +Sound.category = .ambient +``` +This changes the category of the underlying shared `AVAudioSession` instance. The default value is `SoundCategory.ambient`. Due to `AVAudioSession` architecture, this property is not available on macOS. + +##### Creating instances of *Sound* class + +You can also create an instance of a Sound class and store it somewhere in your app. + +```swift +let mySound = Sound(url: fileURL) +mySound.play() +``` + +Creating an instance has more benefits like the ability to adjust the volume and playback callbacks. + +##### Change the volume + +You can change the volume of each *Sound* instance. + +```swift +mySound.volume = 0.5 +``` +The value of *volume* property should be between 0.0 and 1.0, where 1.0 is the maximum. + +##### Callbacks + +You can pass a callback to the `play` method. It will be played after the sound finished playing. For looped sounds, the callback will be called once after the last loop has been played. + +```swift +mySound.play { completed in + print("completed: \(completed)") +} +``` + + + +## Features +- [x] Playing single sounds +- [x] Loops +- [x] Infinite loops +- [x] Playing the same sound multiple times simultaneously +- [x] Stopping all sounds with a global static method +- [x] Ability to pause and resume +- [x] Adjusting sound volume +- [x] Callbacks +- [x] Global static variable to enable/disable all sounds + +## Requirements +- Swift 4 +- Xcode 9.0 or later +- iOS 8.0 or later +- tvOS 9.0 or later +- macOS 10.9 or later + +For Xcode 8 and Swift 3 support, please use SwiftySound version `0.7.0`. + +## Installation +### Installation with CocoaPods + +[CocoaPods](http://cocoapods.org/) is a dependency manager which automates and simplifies the process of using third-party libraries in your projects. See the [Get Started](http://cocoapods.org/#get_started) section for more details. + +#### Podfile +```ruby +platform :ios, '8.0' +use_frameworks! +pod 'SwiftySound' +``` +### Installation with Carthage +[Carthage](https://github.com/Carthage/Carthage) is a lightweight dependency manager for Swift and Objective-C. It leverages CocoaTouch modules and is less invasive than CocoaPods. + +To install with carthage, follow the instruction on [Carthage](https://github.com/Carthage/Carthage) + +#### Cartfile +``` +github "adamcichy/SwiftySound" +``` + +### Installation with Swift Package Manager + +The Swift Package Manager is a tool for managing the distribution of Swift code. Just add the url of this repo to your `Package.swift` file as a dependency: + +```swift +import PackageDescription + +let package = Package( + name: "YourPackage", + dependencies: [ + .Package(url: "https://github.com/adamcichy/SwiftySound.git", + majorVersion: 0) + ] +) +``` + +Then run `swift build` and wait for SPM to install SwiftySound. + +### Manual installation +Drop the `Sound.swift` file into your project, link against `AVFoundation.framework` and you are ready to go. + +## Licenses + +SwiftySound is licensed under the [MIT License](https://raw.githubusercontent.com/adamcichy/SwiftySound/master/LICENSE). diff --git a/Pods/SwiftySound/Sources/Sound.swift b/Pods/SwiftySound/Sources/Sound.swift new file mode 100644 index 0000000..77d65cf --- /dev/null +++ b/Pods/SwiftySound/Sources/Sound.swift @@ -0,0 +1,383 @@ +// +// Sound.swift +// SwiftySound +// +// Created by Adam Cichy on 21/02/17. +// +// Copyright (c) 2017 Adam Cichy +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +import Foundation +import AVFoundation + +#if os(iOS) || os(tvOS) +/// SoundCategory is a convenient wrapper for AVAudioSessions category constants. + public enum SoundCategory { + + /// Equivalent of AVAudioSessionCategoryAmbient. + case ambient + /// Equivalent of AVAudioSessionCategorySoloAmbient. + case soloAmbient + /// Equivalent of AVAudioSessionCategoryPlayback. + case playback + /// Equivalent of AVAudioSessionCategoryRecord. + case record + /// Equivalent of AVAudioSessionCategoryPlayAndRecord. + case playAndRecord + + fileprivate var avFoundationCategory: String { + get { + switch self { + case .ambient: + return AVAudioSessionCategoryAmbient + case .soloAmbient: + return AVAudioSessionCategorySoloAmbient + case .playback: + return AVAudioSessionCategoryPlayback + case .record: + return AVAudioSessionCategoryRecord + case .playAndRecord: + return AVAudioSessionCategoryPlayAndRecord + } + } + } + } +#endif + +/// Sound is a class that allows you to easily play sounds in Swift. It uses AVFoundation framework under the hood. +open class Sound { + + // MARK: - Global settings + + /// Number of AVAudioPlayer instances created for every sound. SwiftySound creates 5 players for every sound to make sure that it will be able to play the same sound more than once. If your app doesn't need this functionality, you can reduce the number of players to 1 and reduce memory usage. You can increase the number if your app plays the sound more than 5 times at the same time. + public static var playersPerSound: Int = 5 { + didSet { + stopAll() + sounds.removeAll() + } + } + + #if os(iOS) || os(tvOS) + /// Sound session. The default value is the shared `AVAudioSession` session. + public static var session: Session = AVAudioSession.sharedInstance() + + /// Sound category for current session. Using this variable is a convenient way to set AVAudioSessions category. The default value is .ambient. + public static var category: SoundCategory = { + let defaultCategory = SoundCategory.ambient + try? session.setCategory(defaultCategory.avFoundationCategory) + return defaultCategory + }() { + didSet { + try? session.setCategory(category.avFoundationCategory) + } + } + #endif + + private static var sounds = [URL: Sound]() + + private static let defaultsKey = "com.moonlightapps.SwiftySound.enabled" + + /// Globally enable or disable sound. This setting value is stored in UserDefaults and will be loaded on app launch. + public static var enabled: Bool = { + return !UserDefaults.standard.bool(forKey: defaultsKey) + }() { didSet { + let value = !enabled + UserDefaults.standard.set(value, forKey: defaultsKey) + if value { + stopAll() + } + } + } + + private let players: [Player] + + private var counter = 0 + + /// The class that is used to create `Player` instances. Defaults to `AVAudioPlayer`. + public static var playerClass: Player.Type = AVAudioPlayer.self + + /// The bundle used to load sounds from filenames. The default value of this property is Bunde.main. It can be changed to load sounds from another bundle. + public static var soundsBundle: Bundle = .main + + // MARK: - Initialization + + /// Create a sound object. + /// + /// - Parameter url: Sound file URL. + public init?(url: URL) { + #if os(iOS) || os(tvOS) + _ = Sound.category + #endif + let playersPerSound = max(Sound.playersPerSound, 1) + var myPlayers: [Player] = [] + myPlayers.reserveCapacity(playersPerSound) + for _ in 0.. Bool { + if !Sound.enabled { + return false + } + paused = false + counter = (counter + 1) % players.count + let player = players[counter] + return player.play(numberOfLoops: numberOfLoops, completion: completion) + } + + // MARK: - Stop playing + + /// Stop playing the sound. + public func stop() { + for player in players { + player.stop() + } + paused = false + } + + /// Pause current playback. + public func pause() { + players[counter].pause() + paused = true + } + + + /// Resume playing. + @discardableResult public func resume() -> Bool { + if paused { + players[counter].resume() + paused = false + return true + } + return false + } + + /// Indicates if the sound is currently playing. + public var playing: Bool { + return players[counter].isPlaying + } + + /// Indicates if the sound is paused. + public private(set) var paused: Bool = false + + // MARK: - Prepare sound + + /// Prepare the sound for playback + /// + /// - Returns: True if the sound has been prepared, false in case of error + @discardableResult public func prepare() -> Bool { + let nextIndex = (counter + 1) % players.count + return players[nextIndex].prepareToPlay() + } + + // MARK: - Convenience static methods + + /// Play sound from a sound file. + /// + /// - Parameters: + /// - file: Sound file name. + /// - fileExtension: Sound file extension. + /// - numberOfLoops: Number of loops. Specify a negative number for an infinite loop. Default value of 0 means that the sound will be played once. + /// - Returns: If the sound was played successfully the return value will be true. It will be false if sounds are disabled or if system could not play the sound. + @discardableResult public static func play(file: String, fileExtension: String? = nil, numberOfLoops: Int = 0) -> Bool { + if let url = url(for: file, fileExtension: fileExtension) { + return play(url: url, numberOfLoops: numberOfLoops) + } + return false + } + + /// Play a sound from URL. + /// + /// - Parameters: + /// - url: Sound file URL. + /// - numberOfLoops: Number of loops. Specify a negative number for an infinite loop. Default value of 0 means that the sound will be played once. + /// - Returns: If the sound was played successfully the return value will be true. It will be false if sounds are disabled or if system could not play the sound. + @discardableResult public static func play(url: URL, numberOfLoops: Int = 0) -> Bool { + if !Sound.enabled { + return false + } + var sound = sounds[url] + if sound == nil { + sound = Sound(url: url) + sounds[url] = sound + } + return sound?.play(numberOfLoops: numberOfLoops) ?? false + } + + /// Stop playing sound for given URL. + /// + /// - Parameter url: Sound file URL. + public static func stop(for url: URL) { + let sound = sounds[url] + sound?.stop() + } + + /// Duration of the sound. + public var duration: TimeInterval { + get { + return players[counter].duration + } + } + + /// Sound volume. + /// A value in the range 0.0 to 1.0, with 0.0 representing the minimum volume and 1.0 representing the maximum volume. + public var volume: Float { + get { + return players[counter].volume + } + set { + for player in players { + player.volume = newValue + } + } + } + + /// Stop playing sound for given sound file. + /// + /// - Parameters: + /// - file: Sound file name. + /// - fileExtension: Sound file extension. + public static func stop(file: String, fileExtension: String? = nil) { + if let url = url(for: file, fileExtension: fileExtension) { + let sound = sounds[url] + sound?.stop() + } + } + + /// Stop playing all sounds. + public static func stopAll() { + NotificationCenter.default.post(name: stopNotificationName, object: nil) + } + + // MARK: - Private helper method + private static func url(for file: String, fileExtension: String? = nil) -> URL? { + return soundsBundle.url(forResource: file, withExtension: fileExtension) + } + +} + +/// Player protocol. It duplicates `AVAudioPlayer` methods. +public protocol Player: class { + + /// Play the sound. + /// + /// - Parameters: + /// - numberOfLoops: Number of loops. + /// - completion: Complation handler. + /// - Returns: true if the sound was played successfully. False otherwise. + func play(numberOfLoops: Int, completion: PlayerCompletion?) -> Bool + + /// Stop playing the sound. + func stop() + + /// Pause current playback. + func pause() + + /// Resume playing. + func resume() + + /// Prepare the sound. + func prepareToPlay() -> Bool + + /// Create a Player for sound url. + /// + /// - Parameter url: sound url. + init(contentsOf url: URL) throws + + /// Duration of the sound. + var duration: TimeInterval { get } + + /// Sound volume. + var volume: Float { get set } + + /// Indicates if the player is currently playing. + var isPlaying: Bool { get } +} + +fileprivate var associatedCallbackKey = "com.moonlightapps.SwiftySound.associatedCallbackKey" + +public typealias PlayerCompletion = ((Bool) -> ()) + +extension AVAudioPlayer: Player, AVAudioPlayerDelegate { + + public func play(numberOfLoops: Int, completion: PlayerCompletion?) -> Bool { + if let cmpl = completion { + objc_setAssociatedObject(self, &associatedCallbackKey, cmpl, .OBJC_ASSOCIATION_COPY_NONATOMIC) + self.delegate = self + } + self.numberOfLoops = numberOfLoops + return play() + } + + public func resume() { + play() + } + + public func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) { + let cmpl = objc_getAssociatedObject(self, &associatedCallbackKey) as? PlayerCompletion + cmpl?(flag) + objc_removeAssociatedObjects(self) + self.delegate = nil + } + + public func audioPlayerDecodeErrorDidOccur(_ player: AVAudioPlayer, error: Error?) { + print("SwiftySound playback error: \(String(describing: error))") + } + +} + +#if os(iOS) || os(tvOS) +/// Session protocol. It duplicates `setCategory` method of `AVAudioSession` class. +public protocol Session: class { + /// Set category for session. + /// + /// - Parameter category: category. + func setCategory(_ category: String) throws +} + +extension AVAudioSession: Session {} +#endif diff --git a/Pods/SwiftyStoreKit/LICENSE.md b/Pods/SwiftyStoreKit/LICENSE.md new file mode 100644 index 0000000..5750a7f --- /dev/null +++ b/Pods/SwiftyStoreKit/LICENSE.md @@ -0,0 +1,7 @@ +Copyright (c) 2015-2016 Andrea Bizzotto bizz84@gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Pods/SwiftyStoreKit/README.md b/Pods/SwiftyStoreKit/README.md new file mode 100644 index 0000000..4677d49 --- /dev/null +++ b/Pods/SwiftyStoreKit/README.md @@ -0,0 +1,747 @@ +![](https://github.com/bizz84/SwiftyStoreKit/raw/master/SwiftyStoreKit-logo.png) + +[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](http://mit-license.org) +[![Platform](http://img.shields.io/badge/platform-ios%20%7C%20macos%20%7C%20tvos-lightgrey.svg?style=flat)](https://developer.apple.com/resources/) +[![Language](https://img.shields.io/badge/swift-3.0-orange.svg)](https://developer.apple.com/swift) +[![Build](https://img.shields.io/travis/bizz84/SwiftyStoreKit.svg?style=flat)](https://travis-ci.org/bizz84/SwiftyStoreKit) +[![Issues](https://img.shields.io/github/issues/bizz84/SwiftyStoreKit.svg?style=flat)](https://github.com/bizz84/SwiftyStoreKit/issues) +[![Cocoapod](http://img.shields.io/cocoapods/v/SwiftyStoreKit.svg?style=flat)](http://cocoadocs.org/docsets/SwiftyStoreKit/) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) +[![Downloads](https://img.shields.io/cocoapods/dm/SwiftyStoreKit.svg)](https://cocoapods.org/pods/SwiftyStoreKit) +[![Twitter](https://img.shields.io/badge/twitter-@biz84-blue.svg?maxAge=2592000)](http://twitter.com/biz84) + +SwiftyStoreKit is a lightweight In App Purchases framework for iOS 8.0+, tvOS 9.0+ and macOS 10.10+. + +### Preview + + + +### Note from the Author + +I started [**Coding with Flutter**](https://www.youtube.com/playlist?list=PLNnAcB93JKV9iZ2cwk9MEx3_JG8BRikMP), a YouTube video series on building apps with Flutter. Interested? [**Subscribe here**](https://mailchi.mp/908b29bd9311/coding-with-flutter). + +### Like SwiftyStoreKit? Please consider [becoming a Patron](https://www.patreon.com/biz84). + +## Content + +- [Installation](#installation) + - [CocoaPods](#cocoapods) + - [Carthage](#carthage) +- [Features](#features) +- [Contributing](#contributing) +- [App startup](#app-startup) + - [Complete Transactions](#complete-transactions) +- [Purchases](#purchases) + - [Retrieve products info](#retrieve-products-info) + - [Purchase a product (given a product id)](#purchase-a-product-given-a-product-id) + - [Purchase a product (given a SKProduct)](#purchase-a-product-given-a-skproduct) + - [Handle purchases started on the App Store (iOS 11)](#handle-purchases-started-on-the-app-store-ios-11) + - [Restore previous purchases](#restore-previous-purchases) + - [Downloading content hosted with Apple](#downloading-content-hosted-with-apple) +- [Receipt verification](#receipt-verification) + - [Retrieve local receipt (encrypted)](#retrieve-local-receipt-encrypted) + - [Fetch receipt (encrypted)](#fetch-receipt-encrypted) + - [Verify Receipt](#verify-receipt) +- [Verifying purchases and subscriptions](#verifying-purchases-and-subscriptions) + - [Verify Purchase](#verify-purchase) + - [Verify Subscription](#verify-subscription) + - [Subscription Groups](#subscription-groups) +- [Notes](#notes) +- [Change Log](#change-log) +- [Sample Code](#sample-code) +- [Essential Reading](#essential-reading) + - [Troubleshooting](#troubleshooting) +- [Video Tutorials](#video-tutorials) +- [Payment flows: implementation details](#payment-flows-implementation-details) +- [Credits](#credits) +- [Apps using SwiftyStoreKit](#apps-using-swiftystorekit) +- [License](#license) + +## Installation + +### CocoaPods + +SwiftyStoreKit can be installed as a [CocoaPod](https://cocoapods.org/) and builds as a Swift framework. To install, include this in your Podfile. + +```ruby +use_frameworks! + +pod 'SwiftyStoreKit' +``` +Once installed, just ```import SwiftyStoreKit``` in your classes and you're good to go. + +### Carthage + +To integrate SwiftyStoreKit into your Xcode project using [Carthage](https://github.com/Carthage/Carthage), specify it in your Cartfile: + +```ogdl +github "bizz84/SwiftyStoreKit" +``` + +**NOTE**: Please ensure that you have the [latest](https://github.com/Carthage/Carthage/releases) Carthage installed. + +## Features + +- Super easy to use block based API +- Support for consumable, non-consumable in-app purchases +- Support for free, auto-renewable and non-renewing subscriptions +- Support for in-app purchases started in the App Store (iOS 11) +- Remote receipt verification +- Verify purchases, subscriptions, subscription groups +- Downloading content hosted with Apple +- iOS, tvOS and macOS compatible + +## Contributing + +#### Got issues / pull requests / want to contribute? [Read here](CONTRIBUTING.md). + + +## App startup + +### Complete Transactions + +Apple recommends to register a transaction observer [as soon as the app starts](https://developer.apple.com/library/ios/technotes/tn2387/_index.html): +> Adding your app's observer at launch ensures that it will persist during all launches of your app, thus allowing your app to receive all the payment queue notifications. + +SwiftyStoreKit supports this by calling `completeTransactions()` when the app starts: + +```swift +func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + // see notes below for the meaning of Atomic / Non-Atomic + SwiftyStoreKit.completeTransactions(atomically: true) { purchases in + for purchase in purchases { + switch purchase.transaction.transactionState { + case .purchased, .restored: + if purchase.needsFinishTransaction { + // Deliver content from server, then: + SwiftyStoreKit.finishTransaction(purchase.transaction) + } + // Unlock content + case .failed, .purchasing, .deferred: + break // do nothing + } + } + } + return true +} +``` + +If there are any pending transactions at this point, these will be reported by the completion block so that the app state and UI can be updated. + +If there are no pending transactions, the completion block will **not** be called. + +Note that `completeTransactions()` **should only be called once** in your code, in `application(:didFinishLaunchingWithOptions:)`. + +## Purchases + +### Retrieve products info +```swift +SwiftyStoreKit.retrieveProductsInfo(["com.musevisions.SwiftyStoreKit.Purchase1"]) { result in + if let product = result.retrievedProducts.first { + let priceString = product.localizedPrice! + print("Product: \(product.localizedDescription), price: \(priceString)") + } + else if let invalidProductId = result.invalidProductIDs.first { + print("Invalid product identifier: \(invalidProductId)") + } + else { + print("Error: \(result.error)") + } +} +``` + +### Purchase a product (given a product id) + +* **Atomic**: to be used when the content is delivered immediately. + +```swift +SwiftyStoreKit.purchaseProduct("com.musevisions.SwiftyStoreKit.Purchase1", quantity: 1, atomically: true) { result in + switch result { + case .success(let purchase): + print("Purchase Success: \(purchase.productId)") + case .error(let error): + switch error.code { + case .unknown: print("Unknown error. Please contact support") + case .clientInvalid: print("Not allowed to make the payment") + case .paymentCancelled: break + case .paymentInvalid: print("The purchase identifier was invalid") + case .paymentNotAllowed: print("The device is not allowed to make the payment") + case .storeProductNotAvailable: print("The product is not available in the current storefront") + case .cloudServicePermissionDenied: print("Access to cloud service information is not allowed") + case .cloudServiceNetworkConnectionFailed: print("Could not connect to the network") + case .cloudServiceRevoked: print("User has revoked permission to use this cloud service") + } + } +} +``` + +* **Non-Atomic**: to be used when the content is delivered by the server. + +```swift +SwiftyStoreKit.purchaseProduct("com.musevisions.SwiftyStoreKit.Purchase1", quantity: 1, atomically: false) { result in + switch result { + case .success(let product): + // fetch content from your server, then: + if product.needsFinishTransaction { + SwiftyStoreKit.finishTransaction(product.transaction) + } + print("Purchase Success: \(product.productId)") + case .error(let error): + switch error.code { + case .unknown: print("Unknown error. Please contact support") + case .clientInvalid: print("Not allowed to make the payment") + case .paymentCancelled: break + case .paymentInvalid: print("The purchase identifier was invalid") + case .paymentNotAllowed: print("The device is not allowed to make the payment") + case .storeProductNotAvailable: print("The product is not available in the current storefront") + case .cloudServicePermissionDenied: print("Access to cloud service information is not allowed") + case .cloudServiceNetworkConnectionFailed: print("Could not connect to the network") + case .cloudServiceRevoked: print("User has revoked permission to use this cloud service") + } + } +} +``` + +### Purchase a product (given a SKProduct) + +This is a variant of the method above that can be used to purchase a product when the corresponding `SKProduct` has already been retrieved with `retrieveProductsInfo`: + +```swift +SwiftyStoreKit.retrieveProductsInfo(["com.musevisions.SwiftyStoreKit.Purchase1"]) { result in + if let product = result.retrievedProducts.first { + SwiftyStoreKit.purchaseProduct(product, quantity: 1, atomically: true) { result in + // handle result (same as above) + } + } +} +``` + +Using this `purchaseProduct` method guarantees that only one network call is made to StoreKit to perform the purchase, as opposed to one call to get the product and another to perform the purchase. + +### Handle purchases started on the App Store (iOS 11) + +iOS 11 adds a new delegate method on `SKPaymentTransactionObserver`: + +```swift +@available(iOS 11.0, *) +optional public func paymentQueue(_ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment, for product: SKProduct) -> Bool +``` + +From [Apple Docs](https://developer.apple.com/documentation/storekit/skpaymenttransactionobserver/2877502-paymentqueue): + +> This delegate method is called when the user has started an in-app purchase in the App Store, and is continuing the transaction in your app. Specifically, if your app is already installed, the method is called automatically. +If your app is not yet installed when the user starts the in-app purchase in the App Store, the user gets a notification when the app installation is complete. This method is called when the user taps the notification. Otherwise, if the user opens the app manually, this method is called only if the app is opened soon after the purchase was started. + +SwiftyStoreKit supports this with a new handler, called like this: + +```swift +SwiftyStoreKit.shouldAddStorePaymentHandler = { payment, product in + // return true if the content can be delivered by your app + // return false otherwise +} +``` + +To test this in sandbox mode, open this URL in Safari: + +``` +itms-services://?action=purchaseIntent&bundleId=com.example.app&productIdentifier=product_name +``` + +More information on the [WWDC17 session What's New in StoreKit](https://developer.apple.com/videos/play/wwdc2017/303) +([slide number 165](https://devstreaming-cdn.apple.com/videos/wwdc/2017/303f0u5froddl13/303/303_whats_new_in_storekit.pdf) shows the link above). + +### Restore previous purchases + +According to [Apple - Restoring Purchased Products](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Restoring.html#//apple_ref/doc/uid/TP40008267-CH8-SW9): + +> In most cases, all your app needs to do is refresh its receipt and deliver the products in its receipt. The refreshed receipt contains a record of the user’s purchases in this app, on this device or any other device. + +> Restoring completed transactions creates a new transaction for every completed transaction the user made, essentially replaying history for your transaction queue observer. + +See the **Receipt Verification** section below for how to restore previous purchases using the receipt. + +This section shows how to restore completed transactions with the `restorePurchases` method instead. When successful, the method returns all non-consumable purchases, as well as all auto-renewable subscription purchases, **regardless of whether they are expired or not**. + +* **Atomic**: to be used when the content is delivered immediately. + +```swift +SwiftyStoreKit.restorePurchases(atomically: true) { results in + if results.restoreFailedPurchases.count > 0 { + print("Restore Failed: \(results.restoreFailedPurchases)") + } + else if results.restoredPurchases.count > 0 { + print("Restore Success: \(results.restoredPurchases)") + } + else { + print("Nothing to Restore") + } +} +``` + +* **Non-Atomic**: to be used when the content is delivered by the server. + +```swift +SwiftyStoreKit.restorePurchases(atomically: false) { results in + if results.restoreFailedPurchases.count > 0 { + print("Restore Failed: \(results.restoreFailedPurchases)") + } + else if results.restoredPurchases.count > 0 { + for purchase in results.restoredPurchases { + // fetch content from your server, then: + if purchase.needsFinishTransaction { + SwiftyStoreKit.finishTransaction(purchase.transaction) + } + } + print("Restore Success: \(results.restoredPurchases)") + } + else { + print("Nothing to Restore") + } +} +``` + +#### What does atomic / non-atomic mean? + +When you purchase a product the following things happen: + +* A payment is added to the payment queue for your IAP. +* When the payment has been processed with Apple, the payment queue is updated so that the appropriate transaction can be handled. +* If the transaction state is **purchased** or **restored**, the app can unlock the functionality purchased by the user. +* The app should call `finishTransaction(_:)` to complete the purchase. + +This is what is [recommended by Apple](https://developer.apple.com/reference/storekit/skpaymentqueue/1506003-finishtransaction): + +> Your application should call `finishTransaction(_:)` only after it has successfully processed the transaction and unlocked the functionality purchased by the user. + +* A purchase is **atomic** when the app unlocks the functionality purchased by the user immediately and call `finishTransaction(_:)` at the same time. This is desirable if you're unlocking functionality that is already inside the app. + +* In cases when you need to make a request to your own server in order to unlock the functionality, you can use a **non-atomic** purchase instead. + +* **Note**: SwiftyStoreKit doesn't yet support downloading content hosted by Apple for non-consumable products. See [this feature request](https://github.com/bizz84/SwiftyStoreKit/issues/128). + +SwiftyStoreKit provides three operations that can be performed **atomically** or **non-atomically**: + +* Making a purchase +* Restoring purchases +* Completing transactions on app launch + +### Downloading content hosted with Apple + +Quoting Apple Docs: + +> When you create a product in iTunes Connect, you can associate one or more pieces of downloadable content with it. At runtime, when a product is purchased by a user, your app uses SKDownload objects to download the content from the App Store. + +> Your app never directly creates a SKDownload object. Instead, after a payment is processed, your app reads the transaction object’s downloads property to retrieve an array of SKDownload objects associated with the transaction. + +> To download the content, you queue a download object on the payment queue and wait for the content to be downloaded. After a download completes, read the download object’s contentURL property to get a URL to the downloaded content. Your app must process the downloaded file before completing the transaction. For example, it might copy the file into a directory whose contents are persistent. When all downloads are complete, you finish the transaction. After the transaction is finished, the download objects cannot be queued to the payment queue and any URLs to the downloaded content are invalid. + +To start the downloads (this can be done in `purchaseProduct()`, `completeTransactions()` or `restorePurchases()`): + +```swift +SwiftyStoreKit.purchaseProduct("com.musevisions.SwiftyStoreKit.Purchase1", quantity: 1, atomically: false) { result in + switch result { + case .success(let product): + let downloads = purchase.transaction.downloads + if !downloads.isEmpty { + SwiftyStoreKit.start(downloads) + } + case .error(let error): + print("\(error)") + } +} +``` + +To check the updated downloads, setup a `updatedDownloadsHandler` block in your AppDelegate: + +```swift +SwiftyStoreKit.updatedDownloadsHandler = { downloads in + + // contentURL is not nil if downloadState == .finished + let contentURLs = downloads.flatMap { $0.contentURL } + if contentURLs.count == downloads.count { + // process all downloaded files, then finish the transaction + SwiftyStoreKit.finishTransaction(downloads[0].transaction) + } +} +``` + +To control the state of the downloads, SwiftyStoreKit offers `start()`, `pause()`, `resume()`, `cancel()` methods. + +## Receipt verification + +According to [Apple - Delivering Products](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/DeliverProduct.html#//apple_ref/doc/uid/TP40008267-CH5-SW4): + +> The app receipt contains a record of the user’s purchases, cryptographically signed by Apple. For more information, see [Receipt Validation Programming Guide](https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Introduction.html#//apple_ref/doc/uid/TP40010573). + +> Information about consumable products is added to the receipt when they’re paid for and remains in the receipt until you finish the transaction. After you finish the transaction, this information is removed the next time the receipt is updated—for example, the next time the user makes a purchase. + +> Information about all other kinds of purchases is added to the receipt when they’re paid for and remains in the receipt indefinitely. + +When an app is first installed, the app receipt is missing. + +As soon as a user completes a purchase or restores purchases, StoreKit creates and stores the receipt locally as a file, located by `Bundle.main.appStoreReceiptURL`. + +### Retrieve local receipt (encrypted) + +This helper can be used to retrieve the (encrypted) local receipt data: + +```swift +let receiptData = SwiftyStoreKit.localReceiptData +let receiptString = receiptData.base64EncodedString(options: []) +// do your receipt validation here +``` + +However, the receipt file may be missing or outdated. + +### Fetch receipt (encrypted) + +Use this method to get the updated receipt: + +```swift +SwiftyStoreKit.fetchReceipt(forceRefresh: true) { result in + switch result { + case .success(let receiptData): + let encryptedReceipt = receiptData.base64EncodedString(options: []) + print("Fetch receipt success:\n\(encryptedReceipt)") + case .error(let error): + print("Fetch receipt failed: \(error)") + } +} +``` + +This method works as follows: + +* If `forceRefresh = false`, it returns the local receipt from file, or refreshes it if missing. +* If `forceRefresh = true`, it always refreshes the receipt regardless. + +**Notes** + +* If the local receipt is missing or `forceRefresh = true` when calling `fetchReceipt`, a network call is made to refresh it. +* If the user is not logged to the App Store, StoreKit will present a popup asking to **Sign In to the iTunes Store**. +* If the user enters valid credentials, the receipt will be refreshed. +* If the user cancels, receipt refresh will fail with a **Cannot connect to iTunes Store** error. + +If `fetchReceipt` is successful, it will return the **encrypted** receipt as a string. For this reason, a **validation** step is needed to get all the receipt fields in readable form. This can be done in various ways: + +1. Validate with Apple via the `AppleReceiptValidator` (see [`verifyReceipt`](#verify-receipt) below). +2. Perform local receipt validation (see [#101](https://github.com/bizz84/SwiftyStoreKit/issues/101)). +3. Post the receipt data and validate on server. + +### Verify Receipt + +Use this method to (optionally) refresh the receipt and perform validation in one step. + +```swift +let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: "your-shared-secret") +SwiftyStoreKit.verifyReceipt(using: appleValidator, forceRefresh: false) { result in + switch result { + case .success(let receipt): + print("Verify receipt success: \(receipt)") + case .error(let error): + print("Verify receipt failed: \(error)") + } +} +``` + +**Notes** + +* This method is based on `fetchReceipt`, and the same refresh logic discussed above applies. +* `AppleReceiptValidator` is a **reference implementation** that validates the receipt with Apple and results in a network call. _This is prone to man-in-the-middle attacks._ +* You should implement your secure logic by validating your receipt locally, or sending the encrypted receipt data and validating it in your server. +* Local receipt validation is not implemented (see [issue #101](https://github.com/bizz84/SwiftyStoreKit/issues/101) for details). +* You can implement your own receipt validator by conforming to the `ReceiptValidator` protocol and passing it to `verifyReceipt`. + +## Verifying purchases and subscriptions + +Once you have retrieved the receipt using the `verifyReceipt` method, you can verify your purchases and subscriptions by product identifier. + +Verifying multiple purchases and subscriptions in one call is not yet supported (see [issue #194](https://github.com/bizz84/SwiftyStoreKit/issues/194) for more details). + +If you need to verify multiple purchases / subscriptions, you can either: + +* manually parse the receipt dictionary returned by `verifyReceipt` +* call `verifyPurchase` or `verifySubscription` multiple times with different product identifiers + +### Verify Purchase + +```swift +let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: "your-shared-secret") +SwiftyStoreKit.verifyReceipt(using: appleValidator) { result in + switch result { + case .success(let receipt): + let productId = "com.musevisions.SwiftyStoreKit.Purchase1" + // Verify the purchase of Consumable or NonConsumable + let purchaseResult = SwiftyStoreKit.verifyPurchase( + productId: productId, + inReceipt: receipt) + + switch purchaseResult { + case .purchased(let receiptItem): + print("\(productId) is purchased: \(receiptItem)") + case .notPurchased: + print("The user has never purchased \(productId)") + } + case .error(let error): + print("Receipt verification failed: \(error)") + } +} +``` + +Note that for consumable products, the receipt will only include the information for a couple of minutes after the purchase. + +### Verify Subscription + +This can be used to check if a subscription was previously purchased, and whether it is still active or if it's expired. + +From [Apple - Working with Subscriptions](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Subscriptions.html#//apple_ref/doc/uid/TP40008267-CH7-SW6): + +> keep a record of the date that each piece of content is published. Read the Original Purchase Date and Subscription Expiration Date field from each receipt entry to determine the start and end dates of the subscription. + +When one or more subscriptions are found for a given product id, they are returned as a `ReceiptItem` array ordered by `expiryDate`, with the first one being the newest. + +```swift +let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: "your-shared-secret") +SwiftyStoreKit.verifyReceipt(using: appleValidator) { result in + switch result { + case .success(let receipt): + let productId = "com.musevisions.SwiftyStoreKit.Subscription" + // Verify the purchase of a Subscription + let purchaseResult = SwiftyStoreKit.verifySubscription( + ofType: .autoRenewable, // or .nonRenewing (see below) + productId: productId, + inReceipt: receipt) + + switch purchaseResult { + case .purchased(let expiryDate, let items): + print("\(productId) is valid until \(expiryDate)\n\(items)\n") + case .expired(let expiryDate, let items): + print("\(productId) is expired since \(expiryDate)\n\(items)\n") + case .notPurchased: + print("The user has never purchased \(productId)") + } + + case .error(let error): + print("Receipt verification failed: \(error)") + } +} +``` + +#### Auto-Renewable +```swift +let purchaseResult = SwiftyStoreKit.verifySubscription( + ofType: .autoRenewable, + productId: "com.musevisions.SwiftyStoreKit.Subscription", + inReceipt: receipt) +``` + +#### Non-Renewing +```swift +// validDuration: time interval in seconds +let purchaseResult = SwiftyStoreKit.verifySubscription( + ofType: .nonRenewing(validDuration: 3600 * 24 * 30), + productId: "com.musevisions.SwiftyStoreKit.Subscription", + inReceipt: receipt) +``` + +**Notes** + +* The expiration dates are calculated against the receipt date. This is the date of the last successful call to `verifyReceipt`. +* When purchasing subscriptions in sandbox mode, the expiry dates are set just minutes after the purchase date for testing purposes. + +#### Purchasing and verifying a subscription + +The `verifySubscription` method can be used together with the `purchaseProduct` method to purchase a subscription and check its expiration date, like so: + +```swift +let productId = "your-product-id" +SwiftyStoreKit.purchaseProduct(productId, atomically: true) { result in + + if case .success(let purchase) = result { + // Deliver content from server, then: + if purchase.needsFinishTransaction { + SwiftyStoreKit.finishTransaction(purchase.transaction) + } + + let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: "your-shared-secret") + SwiftyStoreKit.verifyReceipt(using: appleValidator) { result in + + if case .success(let receipt) = result { + let purchaseResult = SwiftyStoreKit.verifySubscription( + ofType: .autoRenewable, + productId: productId, + inReceipt: receipt) + + switch purchaseResult { + case .purchased(let expiryDate, let receiptItems): + print("Product is valid until \(expiryDate)") + case .expired(let expiryDate, let receiptItems): + print("Product is expired since \(expiryDate)") + case .notPurchased: + print("This product has never been purchased") + } + + } else { + // receipt verification error + } + } + } else { + // purchase error + } +} +``` + +### Subscription Groups + +From [Apple Docs - Offering Subscriptions](https://developer.apple.com/app-store/subscriptions/): + +> A subscription group is a set of in-app purchases that you can create to provide users with a range of content offerings, service levels, or durations to best meet their needs. Users can only buy one subscription within a subscription group at a time. If users would want to buy more that one type of subscription — for example, to subscribe to more than one channel in a streaming app — you can put these in-app purchases in different subscription groups. + +You can verify all subscriptions within the same group with the `verifySubscriptions` method: + +```swift +let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: "your-shared-secret") +SwiftyStoreKit.verifyReceipt(using: appleValidator) { result in + switch result { + case .success(let receipt): + let productIds = Set([ "com.musevisions.SwiftyStoreKit.Weekly", + "com.musevisions.SwiftyStoreKit.Monthly", + "com.musevisions.SwiftyStoreKit.Yearly" ]) + let purchaseResult = SwiftyStoreKit.verifySubscriptions(productIds: productIds, inReceipt: receipt) + switch purchaseResult { + case .purchased(let expiryDate, let items): + print("\(productIds) are valid until \(expiryDate)\n\(items)\n") + case .expired(let expiryDate, let items): + print("\(productIds) are expired since \(expiryDate)\n\(items)\n") + case .notPurchased: + print("The user has never purchased \(productIds)") + } + case .error(let error): + print("Receipt verification failed: \(error)") + } +} +``` + +## Notes +The framework provides a simple block based API with robust error handling on top of the existing StoreKit framework. It does **NOT** persist in app purchases data locally. It is up to clients to do this with a storage solution of choice (i.e. NSUserDefaults, CoreData, Keychain). + +#### Swift 2.x / 3.x / 4.x + +| Language | Branch | Pod version | Xcode version | +| --------- | ------ | ----------- | ------------- | +| Swift 4.x | [master](https://github.com/bizz84/SwiftyStoreKit/tree/master) | >= 0.10.4 | Xcode 9 or greater| +| Swift 3.x | [master](https://github.com/bizz84/SwiftyStoreKit/tree/master) | >= 0.5.x | Xcode 8.x | +| Swift 2.3 | [swift-2.3](https://github.com/bizz84/SwiftyStoreKit/tree/swift-2.3) | 0.4.x | Xcode 8, Xcode 7.3.x | +| Swift 2.2 | [swift-2.2](https://github.com/bizz84/SwiftyStoreKit/tree/swift-2.2) | 0.3.x | Xcode 7.3.x | + + +## Change Log + +See the [Releases Page](https://github.com/bizz84/SwiftyStoreKit/releases). + +## Sample Code +The project includes demo apps [for iOS](https://github.com/bizz84/SwiftyStoreKit/blob/master/SwiftyStoreKit-iOS-Demo/ViewController.swift) [and macOS](https://github.com/bizz84/SwiftyStoreKit/blob/master/SwiftyStoreKit-macOS-Demo/ViewController.swift) showing how to use SwiftyStoreKit. +Note that the pre-registered in app purchases in the demo apps are for illustration purposes only and may not work as iTunes Connect may invalidate them. + +## Essential Reading +* [Apple - WWDC16, Session 702: Using Store Kit for In-app Purchases with Swift 3](https://developer.apple.com/videos/play/wwdc2016/702/) +* [Apple - TN2387: In-App Purchase Best Practices](https://developer.apple.com/library/content/technotes/tn2387/_index.html) +* [Apple - TN2413: In-App Purchase FAQ](https://developer.apple.com/library/content/technotes/tn2413/_index.html) (also see [Cannot connect to iTunes Store](https://developer.apple.com/library/content/technotes/tn2413/_index.html#//apple_ref/doc/uid/DTS40016228-CH1-ERROR_MESSAGES-CANNOT_CONNECT_TO_ITUNES_STORE)) +* [Apple - TN2259: Adding In-App Purchase to Your Applications](https://developer.apple.com/library/content/technotes/tn2259/_index.html) +* [iTunes Connect Developer Help - Workflow for configuring in-app purchases](https://help.apple.com/itunes-connect/developer/#/devb57be10e7) +* [Apple - About Receipt Validation](https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Introduction.html) +* [Apple - Receipt Validation Programming Guide](https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html#//apple_ref/doc/uid/TP40010573-CH106-SW1) +* [Apple - Validating Receipts Locally](https://developer.apple.com/library/content/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateLocally.html) +* [Apple - Working with Subscriptions](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Subscriptions.html#//apple_ref/doc/uid/TP40008267-CH7-SW6) +* [Apple - Offering Subscriptions](https://developer.apple.com/app-store/subscriptions/) +* [Apple - Restoring Purchased Products](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Restoring.html#//apple_ref/doc/uid/TP40008267-CH8-SW9) +* [Apple - Testing In-App Purchase Products](https://developer.apple.com/library/content/documentation/LanguagesUtilities/Conceptual/iTunesConnectInAppPurchase_Guide/Chapters/TestingInAppPurchases.html): includes info on duration of subscriptions in sandbox mode +* [objc.io - Receipt Validation](https://www.objc.io/issues/17-security/receipt-validation/) + +I have also written about building SwiftyStoreKit on Medium: + +* [How I got 1000 ⭐️ on my GitHub Project](https://medium.com/ios-os-x-development/how-i-got-1000-%EF%B8%8F-on-my-github-project-654d3d394ca6#.1idp27olf) +* [Maintaining a Growing Open Source Project](https://medium.com/@biz84/maintaining-a-growing-open-source-project-1d385ca84c5#.4cv2g7tdc) + +### Troubleshooting + +* [Apple TN 2413 - Why are my product identifiers being returned in the invalidProductIdentifiers array?](https://developer.apple.com/library/content/technotes/tn2413/_index.html#//apple_ref/doc/uid/DTS40016228-CH1-TROUBLESHOOTING-WHY_ARE_MY_PRODUCT_IDENTIFIERS_BEING_RETURNED_IN_THE_INVALIDPRODUCTIDENTIFIERS_ARRAY_) +* [Invalid Product IDs](http://troybrant.net/blog/2010/01/invalid-product-ids/): Checklist of common mistakes +* [Testing Auto-Renewable Subscriptions on iOS](http://davidbarnard.com/post/164337147440/testing-auto-renewable-subscriptions-on-ios) +* [Apple forums - iOS 11 beta sandbox - cannot connect to App Store](https://forums.developer.apple.com/message/261428#261428) + +## Video Tutorials + +#### Jared Davidson: In App Purchases! (Swift 3 in Xcode : Swifty Store Kit) + + + +#### [@rebeloper](https://github.com/rebeloper): Ultimate In-app Purchases Guide + + + +## Payment flows: implementation details +In order to make a purchase, two operations are needed: + +- Perform a `SKProductRequest` to obtain the `SKProduct` corresponding to the product identifier. + +- Submit the payment and listen for updated transactions on the `SKPaymentQueue`. + +The framework takes care of caching SKProducts so that future requests for the same `SKProduct` don't need to perform a new `SKProductRequest`. + +#### Payment queue + +The following list outlines how requests are processed by SwiftyStoreKit. + +* `SKPaymentQueue` is used to queue payments or restore purchases requests. +* Payments are processed serially and in-order and require user interaction. +* Restore purchases requests don't require user interaction and can jump ahead of the queue. +* `SKPaymentQueue` rejects multiple restore purchases calls. +* Failed transactions only ever belong to queued payment requests. +* `restoreCompletedTransactionsFailedWithError` is always called when a restore purchases request fails. +* `paymentQueueRestoreCompletedTransactionsFinished` is always called following 0 or more update transactions when a restore purchases request succeeds. +* A complete transactions handler is require to catch any transactions that are updated when the app is not running. +* Registering a complete transactions handler when the app launches ensures that any pending transactions can be cleared. +* If a complete transactions handler is missing, pending transactions can be mis-attributed to any new incoming payments or restore purchases. + +The order in which transaction updates are processed is: + +1. payments (transactionState: `.purchased` and `.failed` for matching product identifiers) +2. restore purchases (transactionState: `.restored`, or `restoreCompletedTransactionsFailedWithError`, or `paymentQueueRestoreCompletedTransactionsFinished`) +3. complete transactions (transactionState: `.purchased`, `.failed`, `.restored`, `.deferred`) + +Any transactions where state is `.purchasing` are ignored. + +See [this pull request](https://github.com/bizz84/SwiftyStoreKit/pull/131) for full details about how the payment flows have been implemented. + +## Credits +Many thanks to [phimage](https://github.com/phimage) for adding macOS support and receipt verification. + +## Apps using SwiftyStoreKit + +It would be great to showcase apps using SwiftyStoreKit here. Pull requests welcome :) + +* [MDacne](https://itunes.apple.com/app/id1044050208) - Acne analysis and treatment +* [Pixel Picker](https://itunes.apple.com/app/id930804327) - Image Color Picker +* [KType](https://itunes.apple.com/app/id1037000234) - Space shooter game +* [iPic](https://itunes.apple.com/app/id1101244278) - Automatically upload images and save Markdown links +* [iHosts](https://itunes.apple.com/app/id1102004240) - Perfect for editing /etc/hosts +* [Arise](http://www.abnehm-app.de/) - Calorie counter +* [Truth Truth Lie](https://itunes.apple.com/app/id1130832864) - iMessage game, featured by Apple +* [Tactus Music Player](https://itunes.apple.com/app/id557446352) - Alternative music player app +* [Drops](https://itunes.apple.com/app/id939540371) - Language learning app +* [Fresh Snow](https://itunes.apple.com/app/id1063000470) - Colorado Ski Report +* [Zmeu Grand Canyon](http://grandcanyon.zmeu.guide/) - Interactive hiking map & planner +* [OB Monitor](https://itunes.apple.com/app/id1073398446) - The app for Texas Longhorns athletics fans +* [Talk Dim Sum](https://itunes.apple.com/us/app/talk-dim-sum/id953929066) - Your dim sum companion + +A full list of apps is published [on AppSight](https://www.appsight.io/sdk/574154). + +## License + +Copyright (c) 2015-2018 Andrea Bizzotto bizz84@gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Pods/SwiftyStoreKit/SwiftyStoreKit/AppleReceiptValidator.swift b/Pods/SwiftyStoreKit/SwiftyStoreKit/AppleReceiptValidator.swift new file mode 100644 index 0000000..d01df88 --- /dev/null +++ b/Pods/SwiftyStoreKit/SwiftyStoreKit/AppleReceiptValidator.swift @@ -0,0 +1,124 @@ +// +// InAppReceipt.swift +// SwiftyStoreKit +// +// Created by phimage on 22/12/15. +// Copyright (c) 2015 Andrea Bizzotto (bizz84@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +// https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ValidateRemotely.html + +public struct AppleReceiptValidator: ReceiptValidator { + + public enum VerifyReceiptURLType: String { + case production = "https://buy.itunes.apple.com/verifyReceipt" + case sandbox = "https://sandbox.itunes.apple.com/verifyReceipt" + } + + private let service: VerifyReceiptURLType + private let sharedSecret: String? + + /** + * Reference Apple Receipt Validator + * - Parameter service: Either .production or .sandbox + * - Parameter sharedSecret: Only used for receipts that contain auto-renewable subscriptions. Your app’s shared secret (a hexadecimal string). + */ + public init(service: VerifyReceiptURLType = .production, sharedSecret: String? = nil) { + self.service = service + self.sharedSecret = sharedSecret + } + + public func validate(receiptData: Data, completion: @escaping (VerifyReceiptResult) -> Void) { + + let storeURL = URL(string: service.rawValue)! // safe (until no more) + let storeRequest = NSMutableURLRequest(url: storeURL) + storeRequest.httpMethod = "POST" + + let receipt = receiptData.base64EncodedString(options: []) + let requestContents: NSMutableDictionary = [ "receipt-data": receipt ] + // password if defined + if let password = sharedSecret { + requestContents.setValue(password, forKey: "password") + } + + // Encore request body + do { + storeRequest.httpBody = try JSONSerialization.data(withJSONObject: requestContents, options: []) + } catch let e { + completion(.error(error: .requestBodyEncodeError(error: e))) + return + } + + // Remote task + let task = URLSession.shared.dataTask(with: storeRequest as URLRequest) { data, _, error -> Void in + + // there is an error + if let networkError = error { + completion(.error(error: .networkError(error: networkError))) + return + } + + // there is no data + guard let safeData = data else { + completion(.error(error: .noRemoteData)) + return + } + + // cannot decode data + guard let receiptInfo = try? JSONSerialization.jsonObject(with: safeData, options: .mutableLeaves) as? ReceiptInfo ?? [:] else { + let jsonStr = String(data: safeData, encoding: String.Encoding.utf8) + completion(.error(error: .jsonDecodeError(string: jsonStr))) + return + } + + // get status from info + if let status = receiptInfo["status"] as? Int { + /* + * http://stackoverflow.com/questions/16187231/how-do-i-know-if-an-in-app-purchase-receipt-comes-from-the-sandbox + * How do I verify my receipt (iOS)? + * Always verify your receipt first with the production URL; proceed to verify + * with the sandbox URL if you receive a 21007 status code. Following this + * approach ensures that you do not have to switch between URLs while your + * application is being tested or reviewed in the sandbox or is live in the + * App Store. + + * Note: The 21007 status code indicates that this receipt is a sandbox receipt, + * but it was sent to the production service for verification. + */ + let receiptStatus = ReceiptStatus(rawValue: status) ?? ReceiptStatus.unknown + if case .testReceipt = receiptStatus { + let sandboxValidator = AppleReceiptValidator(service: .sandbox, sharedSecret: self.sharedSecret) + sandboxValidator.validate(receiptData: receiptData, completion: completion) + } else { + if receiptStatus.isValid { + completion(.success(receipt: receiptInfo)) + } else { + completion(.error(error: .receiptInvalid(receipt: receiptInfo, status: receiptStatus))) + } + } + } else { + completion(.error(error: .receiptInvalid(receipt: receiptInfo, status: ReceiptStatus.none))) + } + } + task.resume() + } +} diff --git a/Pods/SwiftyStoreKit/SwiftyStoreKit/CompleteTransactionsController.swift b/Pods/SwiftyStoreKit/SwiftyStoreKit/CompleteTransactionsController.swift new file mode 100644 index 0000000..0ee898a --- /dev/null +++ b/Pods/SwiftyStoreKit/SwiftyStoreKit/CompleteTransactionsController.swift @@ -0,0 +1,77 @@ +// +// CompleteTransactionsController.swift +// SwiftyStoreKit +// +// Copyright (c) 2017 Andrea Bizzotto (bizz84@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation +import StoreKit + +struct CompleteTransactions { + let atomically: Bool + let callback: ([Purchase]) -> Void + + init(atomically: Bool, callback: @escaping ([Purchase]) -> Void) { + self.atomically = atomically + self.callback = callback + } +} + +class CompleteTransactionsController: TransactionController { + + var completeTransactions: CompleteTransactions? + + func processTransactions(_ transactions: [SKPaymentTransaction], on paymentQueue: PaymentQueue) -> [SKPaymentTransaction] { + + guard let completeTransactions = completeTransactions else { + print("SwiftyStoreKit.completeTransactions() should be called once when the app launches.") + return transactions + } + + var unhandledTransactions: [SKPaymentTransaction] = [] + var purchases: [Purchase] = [] + + for transaction in transactions { + + let transactionState = transaction.transactionState + + if transactionState != .purchasing { + + let willFinishTransaction = completeTransactions.atomically || transactionState == .failed + let purchase = Purchase(productId: transaction.payment.productIdentifier, quantity: transaction.payment.quantity, transaction: transaction, originalTransaction: transaction.original, needsFinishTransaction: !willFinishTransaction) + + purchases.append(purchase) + + if willFinishTransaction { + print("Finishing transaction for payment \"\(transaction.payment.productIdentifier)\" with state: \(transactionState.debugDescription)") + paymentQueue.finishTransaction(transaction) + } + } else { + unhandledTransactions.append(transaction) + } + } + if purchases.count > 0 { + completeTransactions.callback(purchases) + } + + return unhandledTransactions + } +} diff --git a/Pods/SwiftyStoreKit/SwiftyStoreKit/InAppProductQueryRequest.swift b/Pods/SwiftyStoreKit/SwiftyStoreKit/InAppProductQueryRequest.swift new file mode 100644 index 0000000..880a226 --- /dev/null +++ b/Pods/SwiftyStoreKit/SwiftyStoreKit/InAppProductQueryRequest.swift @@ -0,0 +1,79 @@ +// +// InAppPurchaseProductRequest.swift +// SwiftyStoreKit +// +// Copyright (c) 2015 Andrea Bizzotto (bizz84@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import StoreKit + +typealias InAppProductRequestCallback = (RetrieveResults) -> Void + +protocol InAppProductRequest: class { + func start() + func cancel() +} + +class InAppProductQueryRequest: NSObject, InAppProductRequest, SKProductsRequestDelegate { + + private let callback: InAppProductRequestCallback + private let request: SKProductsRequest + + deinit { + request.delegate = nil + } + init(productIds: Set, callback: @escaping InAppProductRequestCallback) { + + self.callback = callback + request = SKProductsRequest(productIdentifiers: productIds) + super.init() + request.delegate = self + } + + func start() { + request.start() + } + func cancel() { + request.cancel() + } + + // MARK: SKProductsRequestDelegate + func productsRequest(_ request: SKProductsRequest, didReceive response: SKProductsResponse) { + + let retrievedProducts = Set(response.products) + let invalidProductIDs = Set(response.invalidProductIdentifiers) + performCallback(RetrieveResults(retrievedProducts: retrievedProducts, + invalidProductIDs: invalidProductIDs, error: nil)) + } + + func requestDidFinish(_ request: SKRequest) { + + } + + func request(_ request: SKRequest, didFailWithError error: Error) { + performCallback(RetrieveResults(retrievedProducts: Set(), invalidProductIDs: Set(), error: error)) + } + + private func performCallback(_ results: RetrieveResults) { + DispatchQueue.main.async { + self.callback(results) + } + } +} diff --git a/Pods/SwiftyStoreKit/SwiftyStoreKit/InAppReceipt.swift b/Pods/SwiftyStoreKit/SwiftyStoreKit/InAppReceipt.swift new file mode 100644 index 0000000..e86abbf --- /dev/null +++ b/Pods/SwiftyStoreKit/SwiftyStoreKit/InAppReceipt.swift @@ -0,0 +1,239 @@ +// +// InAppReceipt.swift +// SwiftyStoreKit +// +// Created by phimage on 22/12/15. +// Copyright (c) 2015 Andrea Bizzotto (bizz84@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +extension Date { + + init?(millisecondsSince1970: String) { + guard let millisecondsNumber = Double(millisecondsSince1970) else { + return nil + } + self = Date(timeIntervalSince1970: millisecondsNumber / 1000) + } +} + +extension ReceiptItem { + + public init?(receiptInfo: ReceiptInfo) { + guard + let productId = receiptInfo["product_id"] as? String, + let quantityString = receiptInfo["quantity"] as? String, + let quantity = Int(quantityString), + let transactionId = receiptInfo["transaction_id"] as? String, + let originalTransactionId = receiptInfo["original_transaction_id"] as? String, + let purchaseDate = ReceiptItem.parseDate(from: receiptInfo, key: "purchase_date_ms"), + let originalPurchaseDate = ReceiptItem.parseDate(from: receiptInfo, key: "original_purchase_date_ms") + else { + print("could not parse receipt item: \(receiptInfo). Skipping...") + return nil + } + self.productId = productId + self.quantity = quantity + self.transactionId = transactionId + self.originalTransactionId = originalTransactionId + self.purchaseDate = purchaseDate + self.originalPurchaseDate = originalPurchaseDate + self.webOrderLineItemId = receiptInfo["web_order_line_item_id"] as? String + self.subscriptionExpirationDate = ReceiptItem.parseDate(from: receiptInfo, key: "expires_date_ms") + self.cancellationDate = ReceiptItem.parseDate(from: receiptInfo, key: "cancellation_date_ms") + if let isTrialPeriod = receiptInfo["is_trial_period"] as? String { + self.isTrialPeriod = Bool(isTrialPeriod) ?? false + } else { + self.isTrialPeriod = false + } + } + + private static func parseDate(from receiptInfo: ReceiptInfo, key: String) -> Date? { + + guard + let requestDateString = receiptInfo[key] as? String, + let requestDateMs = Double(requestDateString) else { + return nil + } + return Date(timeIntervalSince1970: requestDateMs / 1000) + } +} + +// MARK: - receipt mangement +internal class InAppReceipt { + + /** + * Verify the purchase of a Consumable or NonConsumable product in a receipt + * - Parameter productId: the product id of the purchase to verify + * - Parameter inReceipt: the receipt to use for looking up the purchase + * - return: either notPurchased or purchased + */ + class func verifyPurchase( + productId: String, + inReceipt receipt: ReceiptInfo + ) -> VerifyPurchaseResult { + + // Get receipts info for the product + let receipts = getInAppReceipts(receipt: receipt) + let filteredReceiptsInfo = filterReceiptsInfo(receipts: receipts, withProductIds: [productId]) + let nonCancelledReceiptsInfo = filteredReceiptsInfo.filter { receipt in receipt["cancellation_date"] == nil } + + #if swift(>=4.1) + let receiptItems = nonCancelledReceiptsInfo.compactMap { ReceiptItem(receiptInfo: $0) } + #else + let receiptItems = nonCancelledReceiptsInfo.flatMap { ReceiptItem(receiptInfo: $0) } + #endif + + // Verify that at least one receipt has the right product id + if let firstItem = receiptItems.first { + return .purchased(item: firstItem) + } + return .notPurchased + } + + /** + * Verify the validity of a set of subscriptions in a receipt. + * + * This method extracts all transactions matching the given productIds and sorts them by date in descending order. It then compares the first transaction expiry date against the receipt date, to determine its validity. + * - Note: You can use this method to check the validity of (mutually exclusive) subscriptions in a subscription group. + * - Remark: The type parameter determines how the expiration dates are calculated for all subscriptions. Make sure all productIds match the specified subscription type to avoid incorrect results. + * - Parameter type: .autoRenewable or .nonRenewing. + * - Parameter productIds: The product ids of the subscriptions to verify. + * - Parameter receipt: The receipt to use for looking up the subscriptions + * - Parameter validUntil: Date to check against the expiry date of the subscriptions. This is only used if a date is not found in the receipt. + * - return: Either .notPurchased or .purchased / .expired with the expiry date found in the receipt. + */ + class func verifySubscriptions( + ofType type: SubscriptionType, + productIds: Set, + inReceipt receipt: ReceiptInfo, + validUntil date: Date = Date() + ) -> VerifySubscriptionResult { + + // The values of the latest_receipt and latest_receipt_info keys are useful when checking whether an auto-renewable subscription is currently active. By providing any transaction receipt for the subscription and checking these values, you can get information about the currently-active subscription period. If the receipt being validated is for the latest renewal, the value for latest_receipt is the same as receipt-data (in the request) and the value for latest_receipt_info is the same as receipt. + let (receipts, duration) = getReceiptsAndDuration(for: type, inReceipt: receipt) + let receiptsInfo = filterReceiptsInfo(receipts: receipts, withProductIds: productIds) + let nonCancelledReceiptsInfo = receiptsInfo.filter { receipt in receipt["cancellation_date"] == nil } + if nonCancelledReceiptsInfo.count == 0 { + return .notPurchased + } + + let receiptDate = getReceiptRequestDate(inReceipt: receipt) ?? date + + #if swift(>=4.1) + let receiptItems = nonCancelledReceiptsInfo.compactMap { ReceiptItem(receiptInfo: $0) } + #else + let receiptItems = nonCancelledReceiptsInfo.flatMap { ReceiptItem(receiptInfo: $0) } + #endif + + if nonCancelledReceiptsInfo.count > receiptItems.count { + print("receipt has \(nonCancelledReceiptsInfo.count) items, but only \(receiptItems.count) were parsed") + } + + let sortedExpiryDatesAndItems = expiryDatesAndItems(receiptItems: receiptItems, duration: duration).sorted { a, b in + return a.0 > b.0 + } + + guard let firstExpiryDateItemPair = sortedExpiryDatesAndItems.first else { + return .notPurchased + } + + let sortedReceiptItems = sortedExpiryDatesAndItems.map { $0.1 } + if firstExpiryDateItemPair.0 > receiptDate { + return .purchased(expiryDate: firstExpiryDateItemPair.0, items: sortedReceiptItems) + } else { + return .expired(expiryDate: firstExpiryDateItemPair.0, items: sortedReceiptItems) + } + } + + private class func expiryDatesAndItems(receiptItems: [ReceiptItem], duration: TimeInterval?) -> [(Date, ReceiptItem)] { + + if let duration = duration { + return receiptItems.map { + let expirationDate = Date(timeIntervalSince1970: $0.originalPurchaseDate.timeIntervalSince1970 + duration) + return (expirationDate, $0) + } + } else { + #if swift(>=4.1) + return receiptItems.compactMap { + if let expirationDate = $0.subscriptionExpirationDate { + return (expirationDate, $0) + } + return nil + } + #else + return receiptItems.flatMap { + if let expirationDate = $0.subscriptionExpirationDate { + return (expirationDate, $0) + } + return nil + } + #endif + } + } + + private class func getReceiptsAndDuration(for subscriptionType: SubscriptionType, inReceipt receipt: ReceiptInfo) -> ([ReceiptInfo]?, TimeInterval?) { + switch subscriptionType { + case .autoRenewable: + return (receipt["latest_receipt_info"] as? [ReceiptInfo], nil) + case .nonRenewing(let duration): + return (getInAppReceipts(receipt: receipt), duration) + } + } + + private class func getReceiptRequestDate(inReceipt receipt: ReceiptInfo) -> Date? { + + guard let receiptInfo = receipt["receipt"] as? ReceiptInfo, + let requestDateString = receiptInfo["request_date_ms"] as? String else { + return nil + } + return Date(millisecondsSince1970: requestDateString) + } + + private class func getInAppReceipts(receipt: ReceiptInfo) -> [ReceiptInfo]? { + + let appReceipt = receipt["receipt"] as? ReceiptInfo + return appReceipt?["in_app"] as? [ReceiptInfo] + } + + /** + * Get all the receipts info for a specific product + * - Parameter receipts: the receipts array to grab info from + * - Parameter productId: the product id + */ + private class func filterReceiptsInfo(receipts: [ReceiptInfo]?, withProductIds productIds: Set) -> [ReceiptInfo] { + + guard let receipts = receipts else { + return [] + } + + // Filter receipts with matching product ids + let receiptsMatchingProductIds = receipts + .filter { (receipt) -> Bool in + if let productId = receipt["product_id"] as? String { + return productIds.contains(productId) + } + return false + } + + return receiptsMatchingProductIds + } +} diff --git a/Pods/SwiftyStoreKit/SwiftyStoreKit/InAppReceiptRefreshRequest.swift b/Pods/SwiftyStoreKit/SwiftyStoreKit/InAppReceiptRefreshRequest.swift new file mode 100644 index 0000000..eea370b --- /dev/null +++ b/Pods/SwiftyStoreKit/SwiftyStoreKit/InAppReceiptRefreshRequest.swift @@ -0,0 +1,81 @@ +// +// InAppReceiptRefreshRequest.swift +// SwiftyStoreKit +// +// Created by phimage on 23/12/15. +// Copyright (c) 2015 Andrea Bizzotto (bizz84@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import StoreKit +import Foundation + +class InAppReceiptRefreshRequest: NSObject, SKRequestDelegate { + + enum ResultType { + case success + case error(e: Error) + } + + typealias RequestCallback = (ResultType) -> Void + typealias ReceiptRefresh = (_ receiptProperties: [String : Any]?, _ callback: @escaping RequestCallback) -> InAppReceiptRefreshRequest + + class func refresh(_ receiptProperties: [String : Any]? = nil, callback: @escaping RequestCallback) -> InAppReceiptRefreshRequest { + let request = InAppReceiptRefreshRequest(receiptProperties: receiptProperties, callback: callback) + request.start() + return request + } + + let refreshReceiptRequest: SKReceiptRefreshRequest + let callback: RequestCallback + + deinit { + refreshReceiptRequest.delegate = nil + } + + init(receiptProperties: [String : Any]? = nil, callback: @escaping RequestCallback) { + self.callback = callback + self.refreshReceiptRequest = SKReceiptRefreshRequest(receiptProperties: receiptProperties) + super.init() + self.refreshReceiptRequest.delegate = self + } + + func start() { + self.refreshReceiptRequest.start() + } + + func requestDidFinish(_ request: SKRequest) { + /*if let resoreRequest = request as? SKReceiptRefreshRequest { + let receiptProperties = resoreRequest.receiptProperties ?? [:] + for (k, v) in receiptProperties { + print("\(k): \(v)") + } + }*/ + performCallback(.success) + } + func request(_ request: SKRequest, didFailWithError error: Error) { + // XXX could here check domain and error code to return typed exception + performCallback(.error(e: error)) + } + private func performCallback(_ result: ResultType) { + DispatchQueue.main.async { + self.callback(result) + } + } +} diff --git a/Pods/SwiftyStoreKit/SwiftyStoreKit/InAppReceiptVerificator.swift b/Pods/SwiftyStoreKit/SwiftyStoreKit/InAppReceiptVerificator.swift new file mode 100644 index 0000000..d49ba5d --- /dev/null +++ b/Pods/SwiftyStoreKit/SwiftyStoreKit/InAppReceiptVerificator.swift @@ -0,0 +1,115 @@ +// +// InAppReceiptVerificator.swift +// SwiftyStoreKit +// +// Created by Andrea Bizzotto on 16/05/2017. +// Copyright (c) 2017 Andrea Bizzotto (bizz84@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +class InAppReceiptVerificator: NSObject { + + let appStoreReceiptURL: URL? + init(appStoreReceiptURL: URL? = Bundle.main.appStoreReceiptURL) { + self.appStoreReceiptURL = appStoreReceiptURL + } + + var appStoreReceiptData: Data? { + guard let receiptDataURL = appStoreReceiptURL, + let data = try? Data(contentsOf: receiptDataURL) else { + return nil + } + return data + } + + private var receiptRefreshRequest: InAppReceiptRefreshRequest? + + /** + * Verify application receipt. + * - Parameter validator: Validator to check the encrypted receipt and return the receipt in readable format + * - Parameter forceRefresh: If true, refreshes the receipt even if one already exists. + * - Parameter refresh: closure to perform receipt refresh (this is made explicit for testability) + * - Parameter completion: handler for result + */ + public func verifyReceipt(using validator: ReceiptValidator, + forceRefresh: Bool, + refresh: InAppReceiptRefreshRequest.ReceiptRefresh = InAppReceiptRefreshRequest.refresh, + completion: @escaping (VerifyReceiptResult) -> Void) { + + fetchReceipt(forceRefresh: forceRefresh, refresh: refresh) { result in + switch result { + case .success(let receiptData): + self.verify(receiptData: receiptData, using: validator, completion: completion) + case .error(let error): + completion(.error(error: error)) + } + } + } + + /** + * Fetch application receipt. This method does two things: + * * If the receipt is missing, refresh it + * * If the receipt is available or is refreshed, validate it + * - Parameter forceRefresh: If true, refreshes the receipt even if one already exists. + * - Parameter refresh: closure to perform receipt refresh (this is made explicit for testability) + * - Parameter completion: handler for result + */ + public func fetchReceipt(forceRefresh: Bool, + refresh: InAppReceiptRefreshRequest.ReceiptRefresh = InAppReceiptRefreshRequest.refresh, + completion: @escaping (FetchReceiptResult) -> Void) { + + if let receiptData = appStoreReceiptData, forceRefresh == false { + completion(.success(receiptData: receiptData)) + } else { + + receiptRefreshRequest = refresh(nil) { result in + + self.receiptRefreshRequest = nil + + switch result { + case .success: + if let receiptData = self.appStoreReceiptData { + completion(.success(receiptData: receiptData)) + } else { + completion(.error(error: .noReceiptData)) + } + case .error(let e): + completion(.error(error: .networkError(error: e))) + } + } + } + } + + /** + * - Parameter receiptData: encrypted receipt data + * - Parameter validator: Validator to check the encrypted receipt and return the receipt in readable format + * - Parameter completion: handler for result + */ + private func verify(receiptData: Data, using validator: ReceiptValidator, completion: @escaping (VerifyReceiptResult) -> Void) { + + validator.validate(receiptData: receiptData) { result in + + DispatchQueue.main.async { + completion(result) + } + } + } +} diff --git a/Pods/SwiftyStoreKit/SwiftyStoreKit/OS.swift b/Pods/SwiftyStoreKit/SwiftyStoreKit/OS.swift new file mode 100644 index 0000000..49c103c --- /dev/null +++ b/Pods/SwiftyStoreKit/SwiftyStoreKit/OS.swift @@ -0,0 +1,35 @@ +// +// OS.swift +// SwiftyStoreKit +// +// Copyright (c) 2015 Andrea Bizzotto (bizz84@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import StoreKit + +// MARK: - missing SKMutablePayment init with product on OSX +#if os(OSX) + extension SKMutablePayment { + convenience init(product: SKProduct) { + self.init() + self.productIdentifier = product.productIdentifier + } + } +#endif diff --git a/Pods/SwiftyStoreKit/SwiftyStoreKit/PaymentQueueController.swift b/Pods/SwiftyStoreKit/SwiftyStoreKit/PaymentQueueController.swift new file mode 100644 index 0000000..834e18e --- /dev/null +++ b/Pods/SwiftyStoreKit/SwiftyStoreKit/PaymentQueueController.swift @@ -0,0 +1,248 @@ +// +// PaymentQueueController.swift +// SwiftyStoreKit +// +// Copyright (c) 2017 Andrea Bizzotto (bizz84@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation +import StoreKit + +protocol TransactionController { + + /** + * - param transactions: transactions to process + * - param paymentQueue: payment queue for finishing transactions + * - return: array of unhandled transactions + */ + func processTransactions(_ transactions: [SKPaymentTransaction], on paymentQueue: PaymentQueue) -> [SKPaymentTransaction] +} + +public enum TransactionResult { + case purchased(purchase: PurchaseDetails) + case restored(purchase: Purchase) + case failed(error: SKError) +} + +public protocol PaymentQueue: class { + + func add(_ observer: SKPaymentTransactionObserver) + func remove(_ observer: SKPaymentTransactionObserver) + + func add(_ payment: SKPayment) + + func start(_ downloads: [SKDownload]) + func pause(_ downloads: [SKDownload]) + func resume(_ downloads: [SKDownload]) + func cancel(_ downloads: [SKDownload]) + + func restoreCompletedTransactions(withApplicationUsername username: String?) + + func finishTransaction(_ transaction: SKPaymentTransaction) +} + +extension SKPaymentQueue: PaymentQueue { } + +extension SKPaymentTransaction { + + open override var debugDescription: String { + let transactionId = transactionIdentifier ?? "null" + return "productId: \(payment.productIdentifier), transactionId: \(transactionId), state: \(transactionState), date: \(String(describing: transactionDate))" + } +} + +extension SKPaymentTransactionState: CustomDebugStringConvertible { + + public var debugDescription: String { + + switch self { + case .purchasing: return "purchasing" + case .purchased: return "purchased" + case .failed: return "failed" + case .restored: return "restored" + case .deferred: return "deferred" + } + } +} + +class PaymentQueueController: NSObject, SKPaymentTransactionObserver { + + private let paymentsController: PaymentsController + + private let restorePurchasesController: RestorePurchasesController + + private let completeTransactionsController: CompleteTransactionsController + + unowned let paymentQueue: PaymentQueue + + deinit { + paymentQueue.remove(self) + } + + init(paymentQueue: PaymentQueue = SKPaymentQueue.default(), + paymentsController: PaymentsController = PaymentsController(), + restorePurchasesController: RestorePurchasesController = RestorePurchasesController(), + completeTransactionsController: CompleteTransactionsController = CompleteTransactionsController()) { + + self.paymentQueue = paymentQueue + self.paymentsController = paymentsController + self.restorePurchasesController = restorePurchasesController + self.completeTransactionsController = completeTransactionsController + super.init() + paymentQueue.add(self) + } + + private func assertCompleteTransactionsWasCalled() { + + let message = "SwiftyStoreKit.completeTransactions() must be called when the app launches." + assert(completeTransactionsController.completeTransactions != nil, message) + } + + func startPayment(_ payment: Payment) { + assertCompleteTransactionsWasCalled() + + let skPayment = SKMutablePayment(product: payment.product) + skPayment.applicationUsername = payment.applicationUsername + skPayment.quantity = payment.quantity + +#if os(iOS) || os(tvOS) + if #available(iOS 8.3, tvOS 9.0, *) { + skPayment.simulatesAskToBuyInSandbox = payment.simulatesAskToBuyInSandbox + } +#endif + + paymentQueue.add(skPayment) + + paymentsController.append(payment) + } + + func restorePurchases(_ restorePurchases: RestorePurchases) { + assertCompleteTransactionsWasCalled() + + if restorePurchasesController.restorePurchases != nil { + return + } + + paymentQueue.restoreCompletedTransactions(withApplicationUsername: restorePurchases.applicationUsername) + + restorePurchasesController.restorePurchases = restorePurchases + } + + func completeTransactions(_ completeTransactions: CompleteTransactions) { + + guard completeTransactionsController.completeTransactions == nil else { + print("SwiftyStoreKit.completeTransactions() should only be called once when the app launches. Ignoring this call") + return + } + + completeTransactionsController.completeTransactions = completeTransactions + } + + func finishTransaction(_ transaction: PaymentTransaction) { + guard let skTransaction = transaction as? SKPaymentTransaction else { + print("Object is not a SKPaymentTransaction: \(transaction)") + return + } + paymentQueue.finishTransaction(skTransaction) + } + + func start(_ downloads: [SKDownload]) { + paymentQueue.start(downloads) + } + func pause(_ downloads: [SKDownload]) { + paymentQueue.pause(downloads) + } + func resume(_ downloads: [SKDownload]) { + paymentQueue.resume(downloads) + } + func cancel(_ downloads: [SKDownload]) { + paymentQueue.cancel(downloads) + } + + var shouldAddStorePaymentHandler: ShouldAddStorePaymentHandler? + var updatedDownloadsHandler: UpdatedDownloadsHandler? + + // MARK: SKPaymentTransactionObserver + func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { + + /* + * Some notes about how requests are processed by SKPaymentQueue: + * + * SKPaymentQueue is used to queue payments or restore purchases requests. + * Payments are processed serially and in-order and require user interaction. + * Restore purchases requests don't require user interaction and can jump ahead of the queue. + * SKPaymentQueue rejects multiple restore purchases calls. + * Having one payment queue observer for each request causes extra processing + * Failed transactions only ever belong to queued payment requests. + * restoreCompletedTransactionsFailedWithError is always called when a restore purchases request fails. + * paymentQueueRestoreCompletedTransactionsFinished is always called following 0 or more update transactions when a restore purchases request succeeds. + * A complete transactions handler is require to catch any transactions that are updated when the app is not running. + * Registering a complete transactions handler when the app launches ensures that any pending transactions can be cleared. + * If a complete transactions handler is missing, pending transactions can be mis-attributed to any new incoming payments or restore purchases. + * + * The order in which transaction updates are processed is: + * 1. payments (transactionState: .purchased and .failed for matching product identifiers) + * 2. restore purchases (transactionState: .restored, or restoreCompletedTransactionsFailedWithError, or paymentQueueRestoreCompletedTransactionsFinished) + * 3. complete transactions (transactionState: .purchased, .failed, .restored, .deferred) + * Any transactions where state == .purchasing are ignored. + */ + var unhandledTransactions = transactions.filter { $0.transactionState != .purchasing } + + if unhandledTransactions.count > 0 { + + unhandledTransactions = paymentsController.processTransactions(transactions, on: paymentQueue) + + unhandledTransactions = restorePurchasesController.processTransactions(unhandledTransactions, on: paymentQueue) + + unhandledTransactions = completeTransactionsController.processTransactions(unhandledTransactions, on: paymentQueue) + + if unhandledTransactions.count > 0 { + let strings = unhandledTransactions.map { $0.debugDescription }.joined(separator: "\n") + print("unhandledTransactions:\n\(strings)") + } + } + } + + func paymentQueue(_ queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction]) { + + } + + func paymentQueue(_ queue: SKPaymentQueue, restoreCompletedTransactionsFailedWithError error: Error) { + + restorePurchasesController.restoreCompletedTransactionsFailed(withError: error) + } + + func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) { + + restorePurchasesController.restoreCompletedTransactionsFinished() + } + + func paymentQueue(_ queue: SKPaymentQueue, updatedDownloads downloads: [SKDownload]) { + + updatedDownloadsHandler?(downloads) + } + + #if os(iOS) + func paymentQueue(_ queue: SKPaymentQueue, shouldAddStorePayment payment: SKPayment, for product: SKProduct) -> Bool { + + return shouldAddStorePaymentHandler?(payment, product) ?? false + } + #endif +} diff --git a/Pods/SwiftyStoreKit/SwiftyStoreKit/PaymentsController.swift b/Pods/SwiftyStoreKit/SwiftyStoreKit/PaymentsController.swift new file mode 100644 index 0000000..1fad4ee --- /dev/null +++ b/Pods/SwiftyStoreKit/SwiftyStoreKit/PaymentsController.swift @@ -0,0 +1,112 @@ +// +// PaymentsController.swift +// SwiftyStoreKit +// +// Copyright (c) 2017 Andrea Bizzotto (bizz84@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation +import StoreKit + +struct Payment: Hashable { + let product: SKProduct + let quantity: Int + let atomically: Bool + let applicationUsername: String + let simulatesAskToBuyInSandbox: Bool + let callback: (TransactionResult) -> Void + + var hashValue: Int { + return product.productIdentifier.hashValue + } + static func == (lhs: Payment, rhs: Payment) -> Bool { + return lhs.product.productIdentifier == rhs.product.productIdentifier + } +} + +class PaymentsController: TransactionController { + + private var payments: [Payment] = [] + + private func findPaymentIndex(withProductIdentifier identifier: String) -> Int? { + for payment in payments where payment.product.productIdentifier == identifier { + return payments.index(of: payment) + } + return nil + } + + func hasPayment(_ payment: Payment) -> Bool { + return findPaymentIndex(withProductIdentifier: payment.product.productIdentifier) != nil + } + + func append(_ payment: Payment) { + payments.append(payment) + } + + func processTransaction(_ transaction: SKPaymentTransaction, on paymentQueue: PaymentQueue) -> Bool { + + let transactionProductIdentifier = transaction.payment.productIdentifier + + guard let paymentIndex = findPaymentIndex(withProductIdentifier: transactionProductIdentifier) else { + + return false + } + let payment = payments[paymentIndex] + + let transactionState = transaction.transactionState + + if transactionState == .purchased { + let purchase = PurchaseDetails(productId: transactionProductIdentifier, quantity: transaction.payment.quantity, product: payment.product, transaction: transaction, originalTransaction: transaction.original, needsFinishTransaction: !payment.atomically) + + payment.callback(.purchased(purchase: purchase)) + + if payment.atomically { + paymentQueue.finishTransaction(transaction) + } + payments.remove(at: paymentIndex) + return true + } + if transactionState == .failed { + + payment.callback(.failed(error: transactionError(for: transaction.error as NSError?))) + + paymentQueue.finishTransaction(transaction) + payments.remove(at: paymentIndex) + return true + } + + if transactionState == .restored { + print("Unexpected restored transaction for payment \(transactionProductIdentifier)") + } + return false + } + + func transactionError(for error: NSError?) -> SKError { + let message = "Unknown error" + let altError = NSError(domain: SKErrorDomain, code: SKError.unknown.rawValue, userInfo: [ NSLocalizedDescriptionKey: message ]) + let nsError = error ?? altError + return SKError(_nsError: nsError) + } + + func processTransactions(_ transactions: [SKPaymentTransaction], on paymentQueue: PaymentQueue) -> [SKPaymentTransaction] { + + return transactions.filter { !processTransaction($0, on: paymentQueue) } + } +} diff --git a/Pods/SwiftyStoreKit/SwiftyStoreKit/ProductsInfoController.swift b/Pods/SwiftyStoreKit/SwiftyStoreKit/ProductsInfoController.swift new file mode 100644 index 0000000..cb5bff4 --- /dev/null +++ b/Pods/SwiftyStoreKit/SwiftyStoreKit/ProductsInfoController.swift @@ -0,0 +1,75 @@ +// +// ProductsInfoController.swift +// SwiftyStoreKit +// +// Copyright (c) 2015 Andrea Bizzotto (bizz84@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation +import StoreKit + +protocol InAppProductRequestBuilder: class { + func request(productIds: Set, callback: @escaping InAppProductRequestCallback) -> InAppProductRequest +} + +class InAppProductQueryRequestBuilder: InAppProductRequestBuilder { + + func request(productIds: Set, callback: @escaping InAppProductRequestCallback) -> InAppProductRequest { + return InAppProductQueryRequest(productIds: productIds, callback: callback) + } +} + +class ProductsInfoController: NSObject { + + struct InAppProductQuery { + let request: InAppProductRequest + var completionHandlers: [InAppProductRequestCallback] + } + + let inAppProductRequestBuilder: InAppProductRequestBuilder + init(inAppProductRequestBuilder: InAppProductRequestBuilder = InAppProductQueryRequestBuilder()) { + self.inAppProductRequestBuilder = inAppProductRequestBuilder + } + + // As we can have multiple inflight requests, we store them in a dictionary by product ids + private var inflightRequests: [Set: InAppProductQuery] = [:] + + func retrieveProductsInfo(_ productIds: Set, completion: @escaping (RetrieveResults) -> Void) { + + if inflightRequests[productIds] == nil { + let request = inAppProductRequestBuilder.request(productIds: productIds) { results in + + if let query = self.inflightRequests[productIds] { + for completion in query.completionHandlers { + completion(results) + } + self.inflightRequests[productIds] = nil + } else { + // should not get here, but if it does it seems reasonable to call the outer completion block + completion(results) + } + } + inflightRequests[productIds] = InAppProductQuery(request: request, completionHandlers: [completion]) + request.start() + } else { + inflightRequests[productIds]!.completionHandlers.append(completion) + } + } +} diff --git a/Pods/SwiftyStoreKit/SwiftyStoreKit/RestorePurchasesController.swift b/Pods/SwiftyStoreKit/SwiftyStoreKit/RestorePurchasesController.swift new file mode 100644 index 0000000..4e37414 --- /dev/null +++ b/Pods/SwiftyStoreKit/SwiftyStoreKit/RestorePurchasesController.swift @@ -0,0 +1,108 @@ +// +// RestorePurchasesController.swift +// SwiftyStoreKit +// +// Copyright (c) 2017 Andrea Bizzotto (bizz84@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation +import StoreKit + +struct RestorePurchases { + let atomically: Bool + let applicationUsername: String? + let callback: ([TransactionResult]) -> Void + + init(atomically: Bool, applicationUsername: String? = nil, callback: @escaping ([TransactionResult]) -> Void) { + self.atomically = atomically + self.applicationUsername = applicationUsername + self.callback = callback + } +} + +class RestorePurchasesController: TransactionController { + + public var restorePurchases: RestorePurchases? + + private var restoredPurchases: [TransactionResult] = [] + + func processTransaction(_ transaction: SKPaymentTransaction, atomically: Bool, on paymentQueue: PaymentQueue) -> Purchase? { + + let transactionState = transaction.transactionState + + if transactionState == .restored { + + let transactionProductIdentifier = transaction.payment.productIdentifier + + let purchase = Purchase(productId: transactionProductIdentifier, quantity: transaction.payment.quantity, transaction: transaction, originalTransaction: transaction.original, needsFinishTransaction: !atomically) + if atomically { + paymentQueue.finishTransaction(transaction) + } + return purchase + } + return nil + } + + func processTransactions(_ transactions: [SKPaymentTransaction], on paymentQueue: PaymentQueue) -> [SKPaymentTransaction] { + + guard let restorePurchases = restorePurchases else { + return transactions + } + + var unhandledTransactions: [SKPaymentTransaction] = [] + for transaction in transactions { + if let restoredPurchase = processTransaction(transaction, atomically: restorePurchases.atomically, on: paymentQueue) { + restoredPurchases.append(.restored(purchase: restoredPurchase)) + } else { + unhandledTransactions.append(transaction) + } + } + + return unhandledTransactions + } + + func restoreCompletedTransactionsFailed(withError error: Error) { + + guard let restorePurchases = restorePurchases else { + print("Callback already called. Returning") + return + } + restoredPurchases.append(.failed(error: SKError(_nsError: error as NSError))) + restorePurchases.callback(restoredPurchases) + + // Reset state after error received + restoredPurchases = [] + self.restorePurchases = nil + + } + + func restoreCompletedTransactionsFinished() { + + guard let restorePurchases = restorePurchases else { + print("Callback already called. Returning") + return + } + restorePurchases.callback(restoredPurchases) + + // Reset state after error transactions finished + restoredPurchases = [] + self.restorePurchases = nil + } +} diff --git a/Pods/SwiftyStoreKit/SwiftyStoreKit/SKProduct+LocalizedPrice.swift b/Pods/SwiftyStoreKit/SwiftyStoreKit/SKProduct+LocalizedPrice.swift new file mode 100644 index 0000000..f6ebb49 --- /dev/null +++ b/Pods/SwiftyStoreKit/SwiftyStoreKit/SKProduct+LocalizedPrice.swift @@ -0,0 +1,40 @@ +// +// SKProduct+LocalizedPrice.swift +// SwiftyStoreKit +// +// Created by Andrea Bizzotto on 19/10/2016. +// Copyright (c) 2015 Andrea Bizzotto (bizz84@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import StoreKit + +public extension SKProduct { + + public var localizedPrice: String? { + return priceFormatter(locale: priceLocale).string(from: price) + } + + private func priceFormatter(locale: Locale) -> NumberFormatter { + let formatter = NumberFormatter() + formatter.locale = locale + formatter.numberStyle = .currency + return formatter + } +} diff --git a/Pods/SwiftyStoreKit/SwiftyStoreKit/SwiftyStoreKit+Types.swift b/Pods/SwiftyStoreKit/SwiftyStoreKit/SwiftyStoreKit+Types.swift new file mode 100644 index 0000000..df34915 --- /dev/null +++ b/Pods/SwiftyStoreKit/SwiftyStoreKit/SwiftyStoreKit+Types.swift @@ -0,0 +1,238 @@ +// +// SwiftyStoreKit+Types.swift +// SwiftyStoreKit +// +// Copyright (c) 2015 Andrea Bizzotto (bizz84@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import StoreKit + +// MARK: Purchases + +// Restored product +public struct Purchase { + public let productId: String + public let quantity: Int + public let transaction: PaymentTransaction + public let originalTransaction: PaymentTransaction? + public let needsFinishTransaction: Bool +} + +// Purchased product +public struct PurchaseDetails { + public let productId: String + public let quantity: Int + public let product: SKProduct + public let transaction: PaymentTransaction + public let originalTransaction: PaymentTransaction? + public let needsFinishTransaction: Bool +} + +//Conform to this protocol to provide custom receipt validator +public protocol ReceiptValidator { + func validate(receiptData: Data, completion: @escaping (VerifyReceiptResult) -> Void) +} + +// Payment transaction +public protocol PaymentTransaction { + var transactionDate: Date? { get } + var transactionState: SKPaymentTransactionState { get } + var transactionIdentifier: String? { get } + var downloads: [SKDownload] { get } +} + +// Add PaymentTransaction conformance to SKPaymentTransaction +extension SKPaymentTransaction : PaymentTransaction { } + +// Products information +public struct RetrieveResults { + public let retrievedProducts: Set + public let invalidProductIDs: Set + public let error: Error? +} + +// Purchase result +public enum PurchaseResult { + case success(purchase: PurchaseDetails) + case error(error: SKError) +} + +// Restore purchase results +public struct RestoreResults { + public let restoredPurchases: [Purchase] + public let restoreFailedPurchases: [(SKError, String?)] +} + +public typealias ShouldAddStorePaymentHandler = (_ payment: SKPayment, _ product: SKProduct) -> Bool +public typealias UpdatedDownloadsHandler = (_ downloads: [SKDownload]) -> Void + +// MARK: Receipt verification + +// Info for receipt returned by server +public typealias ReceiptInfo = [String: AnyObject] + +// Fetch receipt result +public enum FetchReceiptResult { + case success(receiptData: Data) + case error(error: ReceiptError) +} + +// Verify receipt result +public enum VerifyReceiptResult { + case success(receipt: ReceiptInfo) + case error(error: ReceiptError) +} + +// Result for Consumable and NonConsumable +public enum VerifyPurchaseResult { + case purchased(item: ReceiptItem) + case notPurchased +} + +// Verify subscription result +public enum VerifySubscriptionResult { + case purchased(expiryDate: Date, items: [ReceiptItem]) + case expired(expiryDate: Date, items: [ReceiptItem]) + case notPurchased +} + +public enum SubscriptionType { + case autoRenewable + case nonRenewing(validDuration: TimeInterval) +} + +public struct ReceiptItem { + // The product identifier of the item that was purchased. This value corresponds to the productIdentifier property of the SKPayment object stored in the transaction’s payment property. + public let productId: String + // The number of items purchased. This value corresponds to the quantity property of the SKPayment object stored in the transaction’s payment property. + public let quantity: Int + // The transaction identifier of the item that was purchased. This value corresponds to the transaction’s transactionIdentifier property. + public let transactionId: String + // For a transaction that restores a previous transaction, the transaction identifier of the original transaction. Otherwise, identical to the transaction identifier. This value corresponds to the original transaction’s transactionIdentifier property. All receipts in a chain of renewals for an auto-renewable subscription have the same value for this field. + public let originalTransactionId: String + // The date and time that the item was purchased. This value corresponds to the transaction’s transactionDate property. + public let purchaseDate: Date + // For a transaction that restores a previous transaction, the date of the original transaction. This value corresponds to the original transaction’s transactionDate property. In an auto-renewable subscription receipt, this indicates the beginning of the subscription period, even if the subscription has been renewed. + public let originalPurchaseDate: Date + // The primary key for identifying subscription purchases. + public let webOrderLineItemId: String? + // The expiration date for the subscription, expressed as the number of milliseconds since January 1, 1970, 00:00:00 GMT. This key is only present for auto-renewable subscription receipts. + public let subscriptionExpirationDate: Date? + // For a transaction that was canceled by Apple customer support, the time and date of the cancellation. Treat a canceled receipt the same as if no purchase had ever been made. + public let cancellationDate: Date? + + public let isTrialPeriod: Bool +} + +// Error when managing receipt +public enum ReceiptError: Swift.Error { + // No receipt data + case noReceiptData + // No data received + case noRemoteData + // Error when encoding HTTP body into JSON + case requestBodyEncodeError(error: Swift.Error) + // Error when proceeding request + case networkError(error: Swift.Error) + // Error when decoding response + case jsonDecodeError(string: String?) + // Receive invalid - bad status returned + case receiptInvalid(receipt: ReceiptInfo, status: ReceiptStatus) +} + +// Status code returned by remote server +// see Table 2-1 Status codes +public enum ReceiptStatus: Int { + // Not decodable status + case unknown = -2 + // No status returned + case none = -1 + // valid statu + case valid = 0 + // The App Store could not read the JSON object you provided. + case jsonNotReadable = 21000 + // The data in the receipt-data property was malformed or missing. + case malformedOrMissingData = 21002 + // The receipt could not be authenticated. + case receiptCouldNotBeAuthenticated = 21003 + // The shared secret you provided does not match the shared secret on file for your account. + case secretNotMatching = 21004 + // The receipt server is not currently available. + case receiptServerUnavailable = 21005 + // This receipt is valid but the subscription has expired. When this status code is returned to your server, the receipt data is also decoded and returned as part of the response. + case subscriptionExpired = 21006 + // This receipt is from the test environment, but it was sent to the production environment for verification. Send it to the test environment instead. + case testReceipt = 21007 + // This receipt is from the production environment, but it was sent to the test environment for verification. Send it to the production environment instead. + case productionEnvironment = 21008 + + var isValid: Bool { return self == .valid} +} + +// Receipt field as defined in : https://developer.apple.com/library/ios/releasenotes/General/ValidateAppStoreReceipt/Chapters/ReceiptFields.html#//apple_ref/doc/uid/TP40010573-CH106-SW1 +public enum ReceiptInfoField: String { + // Bundle Identifier. This corresponds to the value of CFBundleIdentifier in the Info.plist file. + case bundle_id + // The app’s version number.This corresponds to the value of CFBundleVersion (in iOS) or CFBundleShortVersionString (in OS X) in the Info.plist. + case application_version + // The version of the app that was originally purchased. This corresponds to the value of CFBundleVersion (in iOS) or CFBundleShortVersionString (in OS X) in the Info.plist file when the purchase was originally made. + case original_application_version + // The date when the app receipt was created. + case creation_date + // The date that the app receipt expires. This key is present only for apps purchased through the Volume Purchase Program. + case expiration_date + + // The receipt for an in-app purchase. + case in_app + + public enum InApp: String { + // The number of items purchased. This value corresponds to the quantity property of the SKPayment object stored in the transaction’s payment property. + case quantity + // The product identifier of the item that was purchased. This value corresponds to the productIdentifier property of the SKPayment object stored in the transaction’s payment property. + case product_id + // The transaction identifier of the item that was purchased. This value corresponds to the transaction’s transactionIdentifier property. + case transaction_id + // For a transaction that restores a previous transaction, the transaction identifier of the original transaction. Otherwise, identical to the transaction identifier. This value corresponds to the original transaction’s transactionIdentifier property. All receipts in a chain of renewals for an auto-renewable subscription have the same value for this field. + case original_transaction_id + // The date and time that the item was purchased. This value corresponds to the transaction’s transactionDate property. + case purchase_date + // For a transaction that restores a previous transaction, the date of the original transaction. This value corresponds to the original transaction’s transactionDate property. In an auto-renewable subscription receipt, this indicates the beginning of the subscription period, even if the subscription has been renewed. + case original_purchase_date + // The expiration date for the subscription, expressed as the number of milliseconds since January 1, 1970, 00:00:00 GMT. This key is only present for auto-renewable subscription receipts. + case expires_date + // For a transaction that was canceled by Apple customer support, the time and date of the cancellation. Treat a canceled receipt the same as if no purchase had ever been made. + case cancellation_date + #if os(iOS) || os(tvOS) + // A string that the App Store uses to uniquely identify the application that created the transaction. If your server supports multiple applications, you can use this value to differentiate between them. Apps are assigned an identifier only in the production environment, so this key is not present for receipts created in the test environment. This field is not present for Mac apps. See also Bundle Identifier. + case app_item_id + #endif + // An arbitrary number that uniquely identifies a revision of your application. This key is not present for receipts created in the test environment. + case version_external_identifier + // The primary key for identifying subscription purchases. + case web_order_line_item_id + } +} + +#if os(OSX) + public enum ReceiptExitCode: Int32 { + // If validation fails in OS X, call exit with a status of 173. This exit status notifies the system that your application has determined that its receipt is invalid. At this point, the system attempts to obtain a valid receipt and may prompt for the user’s iTunes credentials + case notValid = 173 + } +#endif diff --git a/Pods/SwiftyStoreKit/SwiftyStoreKit/SwiftyStoreKit.swift b/Pods/SwiftyStoreKit/SwiftyStoreKit/SwiftyStoreKit.swift new file mode 100644 index 0000000..481fe06 --- /dev/null +++ b/Pods/SwiftyStoreKit/SwiftyStoreKit/SwiftyStoreKit.swift @@ -0,0 +1,317 @@ +// +// SwiftyStoreKit.swift +// SwiftyStoreKit +// +// Copyright (c) 2015 Andrea Bizzotto (bizz84@gmail.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import StoreKit + +public class SwiftyStoreKit { + + private let productsInfoController: ProductsInfoController + + fileprivate let paymentQueueController: PaymentQueueController + + fileprivate let receiptVerificator: InAppReceiptVerificator + + init(productsInfoController: ProductsInfoController = ProductsInfoController(), + paymentQueueController: PaymentQueueController = PaymentQueueController(paymentQueue: SKPaymentQueue.default()), + receiptVerificator: InAppReceiptVerificator = InAppReceiptVerificator()) { + + self.productsInfoController = productsInfoController + self.paymentQueueController = paymentQueueController + self.receiptVerificator = receiptVerificator + } + + // MARK: private methods + fileprivate func retrieveProductsInfo(_ productIds: Set, completion: @escaping (RetrieveResults) -> Void) { + return productsInfoController.retrieveProductsInfo(productIds, completion: completion) + } + + fileprivate func purchaseProduct(_ productId: String, quantity: Int = 1, atomically: Bool = true, applicationUsername: String = "", simulatesAskToBuyInSandbox: Bool = false, completion: @escaping ( PurchaseResult) -> Void) { + + retrieveProductsInfo(Set([productId])) { result -> Void in + if let product = result.retrievedProducts.first { + self.purchase(product: product, quantity: quantity, atomically: atomically, applicationUsername: applicationUsername, simulatesAskToBuyInSandbox: simulatesAskToBuyInSandbox, completion: completion) + } else if let error = result.error { + completion(.error(error: SKError(_nsError: error as NSError))) + } else if let invalidProductId = result.invalidProductIDs.first { + let userInfo = [ NSLocalizedDescriptionKey: "Invalid product id: \(invalidProductId)" ] + let error = NSError(domain: SKErrorDomain, code: SKError.paymentInvalid.rawValue, userInfo: userInfo) + completion(.error(error: SKError(_nsError: error))) + } + } + } + + fileprivate func purchase(product: SKProduct, quantity: Int, atomically: Bool, applicationUsername: String = "", simulatesAskToBuyInSandbox: Bool = false, completion: @escaping (PurchaseResult) -> Void) { + guard SwiftyStoreKit.canMakePayments else { + let error = NSError(domain: SKErrorDomain, code: SKError.paymentNotAllowed.rawValue, userInfo: nil) + completion(.error(error: SKError(_nsError: error))) + return + } + + paymentQueueController.startPayment(Payment(product: product, quantity: quantity, atomically: atomically, applicationUsername: applicationUsername, simulatesAskToBuyInSandbox: simulatesAskToBuyInSandbox) { result in + + completion(self.processPurchaseResult(result)) + }) + } + + fileprivate func restorePurchases(atomically: Bool = true, applicationUsername: String = "", completion: @escaping (RestoreResults) -> Void) { + + paymentQueueController.restorePurchases(RestorePurchases(atomically: atomically, applicationUsername: applicationUsername) { results in + + let results = self.processRestoreResults(results) + completion(results) + }) + } + + fileprivate func completeTransactions(atomically: Bool = true, completion: @escaping ([Purchase]) -> Void) { + + paymentQueueController.completeTransactions(CompleteTransactions(atomically: atomically, callback: completion)) + } + + fileprivate func finishTransaction(_ transaction: PaymentTransaction) { + + paymentQueueController.finishTransaction(transaction) + } + + private func processPurchaseResult(_ result: TransactionResult) -> PurchaseResult { + switch result { + case .purchased(let purchase): + return .success(purchase: purchase) + case .failed(let error): + return .error(error: error) + case .restored(let purchase): + return .error(error: storeInternalError(description: "Cannot restore product \(purchase.productId) from purchase path")) + } + } + + private func processRestoreResults(_ results: [TransactionResult]) -> RestoreResults { + var restoredPurchases: [Purchase] = [] + var restoreFailedPurchases: [(SKError, String?)] = [] + for result in results { + switch result { + case .purchased(let purchase): + let error = storeInternalError(description: "Cannot purchase product \(purchase.productId) from restore purchases path") + restoreFailedPurchases.append((error, purchase.productId)) + case .failed(let error): + restoreFailedPurchases.append((error, nil)) + case .restored(let purchase): + restoredPurchases.append(purchase) + } + } + return RestoreResults(restoredPurchases: restoredPurchases, restoreFailedPurchases: restoreFailedPurchases) + } + + private func storeInternalError(code: SKError.Code = SKError.unknown, description: String = "") -> SKError { + let error = NSError(domain: SKErrorDomain, code: code.rawValue, userInfo: [ NSLocalizedDescriptionKey: description ]) + return SKError(_nsError: error) + } +} + +extension SwiftyStoreKit { + + // MARK: Singleton + fileprivate static let sharedInstance = SwiftyStoreKit() + + // MARK: Public methods - Purchases + + /** + * Return NO if this device is not able or allowed to make payments + */ + public class var canMakePayments: Bool { + return SKPaymentQueue.canMakePayments() + } + + /** + * Retrieve products information + * - Parameter productIds: The set of product identifiers to retrieve corresponding products for + * - Parameter completion: handler for result + */ + public class func retrieveProductsInfo(_ productIds: Set, completion: @escaping (RetrieveResults) -> Void) { + + return sharedInstance.retrieveProductsInfo(productIds, completion: completion) + } + + /** + * Purchase a product + * - Parameter productId: productId as specified in iTunes Connect + * - Parameter quantity: quantity of the product to be purchased + * - Parameter atomically: whether the product is purchased atomically (e.g. finishTransaction is called immediately) + * - Parameter applicationUsername: an opaque identifier for the user’s account on your system + * - Parameter completion: handler for result + */ + public class func purchaseProduct(_ productId: String, quantity: Int = 1, atomically: Bool = true, applicationUsername: String = "", simulatesAskToBuyInSandbox: Bool = false, completion: @escaping (PurchaseResult) -> Void) { + + sharedInstance.purchaseProduct(productId, quantity: quantity, atomically: atomically, applicationUsername: applicationUsername, simulatesAskToBuyInSandbox: simulatesAskToBuyInSandbox, completion: completion) + } + + /** + * Purchase a product + * - Parameter product: product to be purchased + * - Parameter quantity: quantity of the product to be purchased + * - Parameter atomically: whether the product is purchased atomically (e.g. finishTransaction is called immediately) + * - Parameter applicationUsername: an opaque identifier for the user’s account on your system + * - Parameter completion: handler for result + */ + public class func purchaseProduct(_ product: SKProduct, quantity: Int = 1, atomically: Bool = true, applicationUsername: String = "", simulatesAskToBuyInSandbox: Bool = false, completion: @escaping ( PurchaseResult) -> Void) { + + sharedInstance.purchase(product: product, quantity: quantity, atomically: atomically, applicationUsername: applicationUsername, simulatesAskToBuyInSandbox: simulatesAskToBuyInSandbox, completion: completion) + } + + /** + * Restore purchases + * - Parameter atomically: whether the product is purchased atomically (e.g. finishTransaction is called immediately) + * - Parameter applicationUsername: an opaque identifier for the user’s account on your system + * - Parameter completion: handler for result + */ + public class func restorePurchases(atomically: Bool = true, applicationUsername: String = "", completion: @escaping (RestoreResults) -> Void) { + + sharedInstance.restorePurchases(atomically: atomically, applicationUsername: applicationUsername, completion: completion) + } + + /** + * Complete transactions + * - Parameter atomically: whether the product is purchased atomically (e.g. finishTransaction is called immediately) + * - Parameter completion: handler for result + */ + public class func completeTransactions(atomically: Bool = true, completion: @escaping ([Purchase]) -> Void) { + + sharedInstance.completeTransactions(atomically: atomically, completion: completion) + } + + /** + * Finish a transaction + * Once the content has been delivered, call this method to finish a transaction that was performed non-atomically + * - Parameter transaction: transaction to finish + */ + public class func finishTransaction(_ transaction: PaymentTransaction) { + + sharedInstance.finishTransaction(transaction) + } + + /** + * Register a handler for SKPaymentQueue.shouldAddStorePayment delegate method in iOS 11 + */ + public static var shouldAddStorePaymentHandler: ShouldAddStorePaymentHandler? { + didSet { + sharedInstance.paymentQueueController.shouldAddStorePaymentHandler = shouldAddStorePaymentHandler + } + } + + /** + * Register a handler for paymentQueue(_:updatedDownloads:) + */ + public static var updatedDownloadsHandler: UpdatedDownloadsHandler? { + didSet { + sharedInstance.paymentQueueController.updatedDownloadsHandler = updatedDownloadsHandler + } + } + + public class func start(_ downloads: [SKDownload]) { + sharedInstance.paymentQueueController.start(downloads) + } + public class func pause(_ downloads: [SKDownload]) { + sharedInstance.paymentQueueController.pause(downloads) + } + public class func resume(_ downloads: [SKDownload]) { + sharedInstance.paymentQueueController.resume(downloads) + } + public class func cancel(_ downloads: [SKDownload]) { + sharedInstance.paymentQueueController.cancel(downloads) + } +} + +extension SwiftyStoreKit { + + // MARK: Public methods - Receipt verification + + /** + * Return receipt data from the application bundle. This is read from Bundle.main.appStoreReceiptURL + */ + public static var localReceiptData: Data? { + return sharedInstance.receiptVerificator.appStoreReceiptData + } + + /** + * Verify application receipt + * - Parameter validator: receipt validator to use + * - Parameter forceRefresh: If true, refreshes the receipt even if one already exists. + * - Parameter completion: handler for result + */ + public class func verifyReceipt(using validator: ReceiptValidator, forceRefresh: Bool = false, completion: @escaping (VerifyReceiptResult) -> Void) { + + sharedInstance.receiptVerificator.verifyReceipt(using: validator, forceRefresh: forceRefresh, completion: completion) + } + + /** + * Fetch application receipt + * - Parameter forceRefresh: If true, refreshes the receipt even if one already exists. + * - Parameter completion: handler for result + */ + public class func fetchReceipt(forceRefresh: Bool, completion: @escaping (FetchReceiptResult) -> Void) { + + sharedInstance.receiptVerificator.fetchReceipt(forceRefresh: forceRefresh, completion: completion) + } + + /** + * Verify the purchase of a Consumable or NonConsumable product in a receipt + * - Parameter productId: the product id of the purchase to verify + * - Parameter inReceipt: the receipt to use for looking up the purchase + * - return: either notPurchased or purchased + */ + public class func verifyPurchase(productId: String, inReceipt receipt: ReceiptInfo) -> VerifyPurchaseResult { + + return InAppReceipt.verifyPurchase(productId: productId, inReceipt: receipt) + } + + /** + * Verify the validity of a subscription (auto-renewable, free or non-renewing) in a receipt. + * + * This method extracts all transactions matching the given productId and sorts them by date in descending order. It then compares the first transaction expiry date against the receipt date to determine its validity. + * - Parameter type: .autoRenewable or .nonRenewing. + * - Parameter productId: The product id of the subscription to verify. + * - Parameter receipt: The receipt to use for looking up the subscription. + * - Parameter validUntil: Date to check against the expiry date of the subscription. This is only used if a date is not found in the receipt. + * - return: Either .notPurchased or .purchased / .expired with the expiry date found in the receipt. + */ + public class func verifySubscription(ofType type: SubscriptionType, productId: String, inReceipt receipt: ReceiptInfo, validUntil date: Date = Date()) -> VerifySubscriptionResult { + + return InAppReceipt.verifySubscriptions(ofType: type, productIds: [productId], inReceipt: receipt, validUntil: date) + } + + /** + * Verify the validity of a set of subscriptions in a receipt. + * + * This method extracts all transactions matching the given productIds and sorts them by date in descending order. It then compares the first transaction expiry date against the receipt date, to determine its validity. + * - Note: You can use this method to check the validity of (mutually exclusive) subscriptions in a subscription group. + * - Remark: The type parameter determines how the expiration dates are calculated for all subscriptions. Make sure all productIds match the specified subscription type to avoid incorrect results. + * - Parameter type: .autoRenewable or .nonRenewing. + * - Parameter productIds: The product ids of the subscriptions to verify. + * - Parameter receipt: The receipt to use for looking up the subscriptions + * - Parameter validUntil: Date to check against the expiry date of the subscriptions. This is only used if a date is not found in the receipt. + * - return: Either .notPurchased or .purchased / .expired with the expiry date found in the receipt. + */ + public class func verifySubscriptions(ofType type: SubscriptionType = .autoRenewable, productIds: Set, inReceipt receipt: ReceiptInfo, validUntil date: Date = Date()) -> VerifySubscriptionResult { + + return InAppReceipt.verifySubscriptions(ofType: type, productIds: productIds, inReceipt: receipt, validUntil: date) + } +} diff --git a/Pods/Target Support Files/FontAwesome.swift/FontAwesome.swift-dummy.m b/Pods/Target Support Files/FontAwesome.swift/FontAwesome.swift-dummy.m new file mode 100644 index 0000000..1868e9d --- /dev/null +++ b/Pods/Target Support Files/FontAwesome.swift/FontAwesome.swift-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_FontAwesome_swift : NSObject +@end +@implementation PodsDummy_FontAwesome_swift +@end diff --git a/Pods/Target Support Files/FontAwesome.swift/FontAwesome.swift-prefix.pch b/Pods/Target Support Files/FontAwesome.swift/FontAwesome.swift-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/FontAwesome.swift/FontAwesome.swift-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/FontAwesome.swift/FontAwesome.swift-umbrella.h b/Pods/Target Support Files/FontAwesome.swift/FontAwesome.swift-umbrella.h new file mode 100644 index 0000000..602d1bf --- /dev/null +++ b/Pods/Target Support Files/FontAwesome.swift/FontAwesome.swift-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double FontAwesome_swiftVersionNumber; +FOUNDATION_EXPORT const unsigned char FontAwesome_swiftVersionString[]; + diff --git a/Pods/Target Support Files/FontAwesome.swift/FontAwesome.swift.modulemap b/Pods/Target Support Files/FontAwesome.swift/FontAwesome.swift.modulemap new file mode 100644 index 0000000..9f021dd --- /dev/null +++ b/Pods/Target Support Files/FontAwesome.swift/FontAwesome.swift.modulemap @@ -0,0 +1,6 @@ +framework module FontAwesome_swift { + umbrella header "FontAwesome.swift-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/FontAwesome.swift/FontAwesome.swift.xcconfig b/Pods/Target Support Files/FontAwesome.swift/FontAwesome.swift.xcconfig new file mode 100644 index 0000000..0cadbb8 --- /dev/null +++ b/Pods/Target Support Files/FontAwesome.swift/FontAwesome.swift.xcconfig @@ -0,0 +1,10 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/FontAwesome.swift +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_LDFLAGS = -framework "CoreText" -framework "UIKit" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/FontAwesome.swift +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Pods/Target Support Files/FontAwesome.swift/Info.plist b/Pods/Target Support Files/FontAwesome.swift/Info.plist new file mode 100644 index 0000000..b152246 --- /dev/null +++ b/Pods/Target Support Files/FontAwesome.swift/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.4.4 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/FontAwesome.swift/ResourceBundle-FontAwesome.swift-Info.plist b/Pods/Target Support Files/FontAwesome.swift/ResourceBundle-FontAwesome.swift-Info.plist new file mode 100644 index 0000000..ffe7a8e --- /dev/null +++ b/Pods/Target Support Files/FontAwesome.swift/ResourceBundle-FontAwesome.swift-Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.4.4 + CFBundleSignature + ???? + CFBundleVersion + 1 + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/KYCircularProgress/Info.plist b/Pods/Target Support Files/KYCircularProgress/Info.plist new file mode 100644 index 0000000..2a9158a --- /dev/null +++ b/Pods/Target Support Files/KYCircularProgress/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.2.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/KYCircularProgress/KYCircularProgress-dummy.m b/Pods/Target Support Files/KYCircularProgress/KYCircularProgress-dummy.m new file mode 100644 index 0000000..24bd731 --- /dev/null +++ b/Pods/Target Support Files/KYCircularProgress/KYCircularProgress-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_KYCircularProgress : NSObject +@end +@implementation PodsDummy_KYCircularProgress +@end diff --git a/Pods/Target Support Files/KYCircularProgress/KYCircularProgress-prefix.pch b/Pods/Target Support Files/KYCircularProgress/KYCircularProgress-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/KYCircularProgress/KYCircularProgress-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/KYCircularProgress/KYCircularProgress-umbrella.h b/Pods/Target Support Files/KYCircularProgress/KYCircularProgress-umbrella.h new file mode 100644 index 0000000..e56005a --- /dev/null +++ b/Pods/Target Support Files/KYCircularProgress/KYCircularProgress-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double KYCircularProgressVersionNumber; +FOUNDATION_EXPORT const unsigned char KYCircularProgressVersionString[]; + diff --git a/Pods/Target Support Files/KYCircularProgress/KYCircularProgress.modulemap b/Pods/Target Support Files/KYCircularProgress/KYCircularProgress.modulemap new file mode 100644 index 0000000..e7a8101 --- /dev/null +++ b/Pods/Target Support Files/KYCircularProgress/KYCircularProgress.modulemap @@ -0,0 +1,6 @@ +framework module KYCircularProgress { + umbrella header "KYCircularProgress-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/KYCircularProgress/KYCircularProgress.xcconfig b/Pods/Target Support Files/KYCircularProgress/KYCircularProgress.xcconfig new file mode 100644 index 0000000..daada2d --- /dev/null +++ b/Pods/Target Support Files/KYCircularProgress/KYCircularProgress.xcconfig @@ -0,0 +1,9 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/KYCircularProgress +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/KYCircularProgress +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Pods/Target Support Files/Pods-Meditation/Info.plist b/Pods/Target Support Files/Pods-Meditation/Info.plist new file mode 100644 index 0000000..2243fe6 --- /dev/null +++ b/Pods/Target Support Files/Pods-Meditation/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/Pods-Meditation/Pods-Meditation-acknowledgements.markdown b/Pods/Target Support Files/Pods-Meditation/Pods-Meditation-acknowledgements.markdown new file mode 100644 index 0000000..3c80e7f --- /dev/null +++ b/Pods/Target Support Files/Pods-Meditation/Pods-Meditation-acknowledgements.markdown @@ -0,0 +1,161 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## FontAwesome.swift + +Copyright (c) 2014-present FontAwesome.swift contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +## KYCircularProgress + +The MIT License (MIT) + +Copyright (c) 2014 Kengo YOKOYAMA + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +## SCLAlertView + +Copyright (c) 2013-2014 SCPopUpView by Viktor Radchenko + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +## SwiftyJSON + +The MIT License (MIT) + +Copyright (c) 2017 Ruoyu Fu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + +## SwiftySound + +MIT License + +Copyright (c) 2017 Adam Cichy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +## SwiftyStoreKit + +Copyright (c) 2015-2016 Andrea Bizzotto bizz84@gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +## paper-onboarding + +The MIT License (MIT) + +Copyright (c) 2016 Ramotion + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +Generated by CocoaPods - https://cocoapods.org diff --git a/Pods/Target Support Files/Pods-Meditation/Pods-Meditation-acknowledgements.plist b/Pods/Target Support Files/Pods-Meditation/Pods-Meditation-acknowledgements.plist new file mode 100644 index 0000000..1e5d970 --- /dev/null +++ b/Pods/Target Support Files/Pods-Meditation/Pods-Meditation-acknowledgements.plist @@ -0,0 +1,229 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + Copyright (c) 2014-present FontAwesome.swift contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + License + MIT + Title + FontAwesome.swift + Type + PSGroupSpecifier + + + FooterText + The MIT License (MIT) + +Copyright (c) 2014 Kengo YOKOYAMA + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + License + MIT + Title + KYCircularProgress + Type + PSGroupSpecifier + + + FooterText + Copyright (c) 2013-2014 SCPopUpView by Viktor Radchenko + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + License + MIT + Title + SCLAlertView + Type + PSGroupSpecifier + + + FooterText + The MIT License (MIT) + +Copyright (c) 2017 Ruoyu Fu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + License + MIT + Title + SwiftyJSON + Type + PSGroupSpecifier + + + FooterText + MIT License + +Copyright (c) 2017 Adam Cichy + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + License + MIT + Title + SwiftySound + Type + PSGroupSpecifier + + + FooterText + Copyright (c) 2015-2016 Andrea Bizzotto bizz84@gmail.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + License + MIT + Title + SwiftyStoreKit + Type + PSGroupSpecifier + + + FooterText + The MIT License (MIT) + +Copyright (c) 2016 Ramotion + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + License + MIT + Title + paper-onboarding + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/Pods/Target Support Files/Pods-Meditation/Pods-Meditation-dummy.m b/Pods/Target Support Files/Pods-Meditation/Pods-Meditation-dummy.m new file mode 100644 index 0000000..0277058 --- /dev/null +++ b/Pods/Target Support Files/Pods-Meditation/Pods-Meditation-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_Meditation : NSObject +@end +@implementation PodsDummy_Pods_Meditation +@end diff --git a/Pods/Target Support Files/Pods-Meditation/Pods-Meditation-frameworks.sh b/Pods/Target Support Files/Pods-Meditation/Pods-Meditation-frameworks.sh new file mode 100755 index 0000000..ef856ac --- /dev/null +++ b/Pods/Target Support Files/Pods-Meditation/Pods-Meditation-frameworks.sh @@ -0,0 +1,165 @@ +#!/bin/sh +set -e +set -u +set -o pipefail + +if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then + # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy + # frameworks to, so exit 0 (signalling the script phase was successful). + exit 0 +fi + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" +SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" + +# Used as a return value for each invocation of `strip_invalid_archs` function. +STRIP_BINARY_RETVAL=0 + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +# Copies and strips a vendored framework +install_framework() +{ + if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then + local source="${BUILT_PRODUCTS_DIR}/$1" + elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then + local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" + elif [ -r "$1" ]; then + local source="$1" + fi + + local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + + if [ -L "${source}" ]; then + echo "Symlinked..." + source="$(readlink "${source}")" + fi + + # Use filter instead of exclude so missing patterns don't throw errors. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + + local basename + basename="$(basename -s .framework "$1")" + binary="${destination}/${basename}.framework/${basename}" + if ! [ -r "$binary" ]; then + binary="${destination}/${basename}" + fi + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then + strip_invalid_archs "$binary" + fi + + # Resign the code if required by the build settings to avoid unstable apps + code_sign_if_enabled "${destination}/$(basename "$1")" + + # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. + if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then + local swift_runtime_libs + swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) + for lib in $swift_runtime_libs; do + echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" + rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" + code_sign_if_enabled "${destination}/${lib}" + done + fi +} + +# Copies and strips a vendored dSYM +install_dsym() { + local source="$1" + if [ -r "$source" ]; then + # Copy the dSYM into a the targets temp dir. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" + + local basename + basename="$(basename -s .framework.dSYM "$source")" + binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then + strip_invalid_archs "$binary" + fi + + if [[ $STRIP_BINARY_RETVAL == 1 ]]; then + # Move the stripped file into its final destination. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" + else + # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. + touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" + fi + fi +} + +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identitiy + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" + + if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + code_sign_cmd="$code_sign_cmd &" + fi + echo "$code_sign_cmd" + eval "$code_sign_cmd" + fi +} + +# Strip invalid architectures +strip_invalid_archs() { + binary="$1" + # Get architectures for current target binary + binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" + # Intersect them with the architectures we are building for + intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" + # If there are no archs supported by this binary then warn the user + if [[ -z "$intersected_archs" ]]; then + echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." + STRIP_BINARY_RETVAL=0 + return + fi + stripped="" + for arch in $binary_archs; do + if ! [[ "${ARCHS}" == *"$arch"* ]]; then + # Strip non-valid architectures in-place + lipo -remove "$arch" -output "$binary" "$binary" || exit 1 + stripped="$stripped $arch" + fi + done + if [[ "$stripped" ]]; then + echo "Stripped $binary of architectures:$stripped" + fi + STRIP_BINARY_RETVAL=1 +} + + +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/FontAwesome.swift/FontAwesome_swift.framework" + install_framework "${BUILT_PRODUCTS_DIR}/KYCircularProgress/KYCircularProgress.framework" + install_framework "${BUILT_PRODUCTS_DIR}/SCLAlertView/SCLAlertView.framework" + install_framework "${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework" + install_framework "${BUILT_PRODUCTS_DIR}/SwiftySound/SwiftySound.framework" + install_framework "${BUILT_PRODUCTS_DIR}/SwiftyStoreKit/SwiftyStoreKit.framework" + install_framework "${BUILT_PRODUCTS_DIR}/paper-onboarding/paper_onboarding.framework" +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/FontAwesome.swift/FontAwesome_swift.framework" + install_framework "${BUILT_PRODUCTS_DIR}/KYCircularProgress/KYCircularProgress.framework" + install_framework "${BUILT_PRODUCTS_DIR}/SCLAlertView/SCLAlertView.framework" + install_framework "${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework" + install_framework "${BUILT_PRODUCTS_DIR}/SwiftySound/SwiftySound.framework" + install_framework "${BUILT_PRODUCTS_DIR}/SwiftyStoreKit/SwiftyStoreKit.framework" + install_framework "${BUILT_PRODUCTS_DIR}/paper-onboarding/paper_onboarding.framework" +fi +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/Pods/Target Support Files/Pods-Meditation/Pods-Meditation-resources.sh b/Pods/Target Support Files/Pods-Meditation/Pods-Meditation-resources.sh new file mode 100755 index 0000000..fe3f9c7 --- /dev/null +++ b/Pods/Target Support Files/Pods-Meditation/Pods-Meditation-resources.sh @@ -0,0 +1,118 @@ +#!/bin/sh +set -e +set -u +set -o pipefail + +if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then + # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy + # resources to, so exit 0 (signalling the script phase was successful). + exit 0 +fi + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + +RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt +> "$RESOURCES_TO_COPY" + +XCASSET_FILES=() + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +case "${TARGETED_DEVICE_FAMILY:-}" in + 1,2) + TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" + ;; + 1) + TARGET_DEVICE_ARGS="--target-device iphone" + ;; + 2) + TARGET_DEVICE_ARGS="--target-device ipad" + ;; + 3) + TARGET_DEVICE_ARGS="--target-device tv" + ;; + 4) + TARGET_DEVICE_ARGS="--target-device watch" + ;; + *) + TARGET_DEVICE_ARGS="--target-device mac" + ;; +esac + +install_resource() +{ + if [[ "$1" = /* ]] ; then + RESOURCE_PATH="$1" + else + RESOURCE_PATH="${PODS_ROOT}/$1" + fi + if [[ ! -e "$RESOURCE_PATH" ]] ; then + cat << EOM +error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. +EOM + exit 1 + fi + case $RESOURCE_PATH in + *.storyboard) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true + ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} + ;; + *.xib) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true + ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} + ;; + *.framework) + echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true + mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + ;; + *.xcdatamodel) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" + ;; + *.xcdatamodeld) + echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true + xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" + ;; + *.xcmappingmodel) + echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true + xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" + ;; + *.xcassets) + ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" + XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") + ;; + *) + echo "$RESOURCE_PATH" || true + echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" + ;; + esac +} + +mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then + mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +fi +rm -f "$RESOURCES_TO_COPY" + +if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] +then + # Find all other xcassets (this unfortunately includes those of path pods and other targets). + OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) + while read line; do + if [[ $line != "${PODS_ROOT}*" ]]; then + XCASSET_FILES+=("$line") + fi + done <<<"$OTHER_XCASSETS" + + if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then + printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + else + printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_BUILD_DIR}/assetcatalog_generated_info.plist" + fi +fi diff --git a/Pods/Target Support Files/Pods-Meditation/Pods-Meditation-umbrella.h b/Pods/Target Support Files/Pods-Meditation/Pods-Meditation-umbrella.h new file mode 100644 index 0000000..2aedbf7 --- /dev/null +++ b/Pods/Target Support Files/Pods-Meditation/Pods-Meditation-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double Pods_MeditationVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_MeditationVersionString[]; + diff --git a/Pods/Target Support Files/Pods-Meditation/Pods-Meditation.debug.xcconfig b/Pods/Target Support Files/Pods-Meditation/Pods-Meditation.debug.xcconfig new file mode 100644 index 0000000..0821250 --- /dev/null +++ b/Pods/Target Support Files/Pods-Meditation/Pods-Meditation.debug.xcconfig @@ -0,0 +1,11 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FontAwesome.swift" "${PODS_CONFIGURATION_BUILD_DIR}/KYCircularProgress" "${PODS_CONFIGURATION_BUILD_DIR}/SCLAlertView" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftySound" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyStoreKit" "${PODS_CONFIGURATION_BUILD_DIR}/paper-onboarding" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FontAwesome.swift/FontAwesome_swift.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/KYCircularProgress/KYCircularProgress.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SCLAlertView/SCLAlertView.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON/SwiftyJSON.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SwiftySound/SwiftySound.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyStoreKit/SwiftyStoreKit.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/paper-onboarding/paper_onboarding.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "FontAwesome_swift" -framework "KYCircularProgress" -framework "SCLAlertView" -framework "SwiftyJSON" -framework "SwiftySound" -framework "SwiftyStoreKit" -framework "paper_onboarding" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/Pods/Target Support Files/Pods-Meditation/Pods-Meditation.modulemap b/Pods/Target Support Files/Pods-Meditation/Pods-Meditation.modulemap new file mode 100644 index 0000000..94f4c0f --- /dev/null +++ b/Pods/Target Support Files/Pods-Meditation/Pods-Meditation.modulemap @@ -0,0 +1,6 @@ +framework module Pods_Meditation { + umbrella header "Pods-Meditation-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/Pods-Meditation/Pods-Meditation.release.xcconfig b/Pods/Target Support Files/Pods-Meditation/Pods-Meditation.release.xcconfig new file mode 100644 index 0000000..0821250 --- /dev/null +++ b/Pods/Target Support Files/Pods-Meditation/Pods-Meditation.release.xcconfig @@ -0,0 +1,11 @@ +ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/FontAwesome.swift" "${PODS_CONFIGURATION_BUILD_DIR}/KYCircularProgress" "${PODS_CONFIGURATION_BUILD_DIR}/SCLAlertView" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftySound" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyStoreKit" "${PODS_CONFIGURATION_BUILD_DIR}/paper-onboarding" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/FontAwesome.swift/FontAwesome_swift.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/KYCircularProgress/KYCircularProgress.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SCLAlertView/SCLAlertView.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON/SwiftyJSON.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SwiftySound/SwiftySound.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyStoreKit/SwiftyStoreKit.framework/Headers" -iquote "${PODS_CONFIGURATION_BUILD_DIR}/paper-onboarding/paper_onboarding.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "FontAwesome_swift" -framework "KYCircularProgress" -framework "SCLAlertView" -framework "SwiftyJSON" -framework "SwiftySound" -framework "SwiftyStoreKit" -framework "paper_onboarding" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/Pods/Target Support Files/SCLAlertView/Info.plist b/Pods/Target Support Files/SCLAlertView/Info.plist new file mode 100644 index 0000000..2cf03a7 --- /dev/null +++ b/Pods/Target Support Files/SCLAlertView/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 0.8.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/SCLAlertView/SCLAlertView-dummy.m b/Pods/Target Support Files/SCLAlertView/SCLAlertView-dummy.m new file mode 100644 index 0000000..27c49dd --- /dev/null +++ b/Pods/Target Support Files/SCLAlertView/SCLAlertView-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_SCLAlertView : NSObject +@end +@implementation PodsDummy_SCLAlertView +@end diff --git a/Pods/Target Support Files/SCLAlertView/SCLAlertView-prefix.pch b/Pods/Target Support Files/SCLAlertView/SCLAlertView-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/SCLAlertView/SCLAlertView-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/SCLAlertView/SCLAlertView-umbrella.h b/Pods/Target Support Files/SCLAlertView/SCLAlertView-umbrella.h new file mode 100644 index 0000000..95b14c3 --- /dev/null +++ b/Pods/Target Support Files/SCLAlertView/SCLAlertView-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double SCLAlertViewVersionNumber; +FOUNDATION_EXPORT const unsigned char SCLAlertViewVersionString[]; + diff --git a/Pods/Target Support Files/SCLAlertView/SCLAlertView.modulemap b/Pods/Target Support Files/SCLAlertView/SCLAlertView.modulemap new file mode 100644 index 0000000..0f04947 --- /dev/null +++ b/Pods/Target Support Files/SCLAlertView/SCLAlertView.modulemap @@ -0,0 +1,6 @@ +framework module SCLAlertView { + umbrella header "SCLAlertView-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/SCLAlertView/SCLAlertView.xcconfig b/Pods/Target Support Files/SCLAlertView/SCLAlertView.xcconfig new file mode 100644 index 0000000..2557426 --- /dev/null +++ b/Pods/Target Support Files/SCLAlertView/SCLAlertView.xcconfig @@ -0,0 +1,9 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SCLAlertView +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/SCLAlertView +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Pods/Target Support Files/SwiftyJSON/Info.plist b/Pods/Target Support Files/SwiftyJSON/Info.plist new file mode 100644 index 0000000..c26f36f --- /dev/null +++ b/Pods/Target Support Files/SwiftyJSON/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 4.1.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-dummy.m b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-dummy.m new file mode 100644 index 0000000..3159bec --- /dev/null +++ b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_SwiftyJSON : NSObject +@end +@implementation PodsDummy_SwiftyJSON +@end diff --git a/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-umbrella.h b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-umbrella.h new file mode 100644 index 0000000..b627dec --- /dev/null +++ b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double SwiftyJSONVersionNumber; +FOUNDATION_EXPORT const unsigned char SwiftyJSONVersionString[]; + diff --git a/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.modulemap b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.modulemap new file mode 100644 index 0000000..6f41751 --- /dev/null +++ b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.modulemap @@ -0,0 +1,6 @@ +framework module SwiftyJSON { + umbrella header "SwiftyJSON-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.xcconfig b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.xcconfig new file mode 100644 index 0000000..749cb73 --- /dev/null +++ b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.xcconfig @@ -0,0 +1,9 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftyJSON +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Pods/Target Support Files/SwiftySound/Info.plist b/Pods/Target Support Files/SwiftySound/Info.plist new file mode 100644 index 0000000..2243fe6 --- /dev/null +++ b/Pods/Target Support Files/SwiftySound/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/SwiftySound/SwiftySound-dummy.m b/Pods/Target Support Files/SwiftySound/SwiftySound-dummy.m new file mode 100644 index 0000000..40b94c6 --- /dev/null +++ b/Pods/Target Support Files/SwiftySound/SwiftySound-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_SwiftySound : NSObject +@end +@implementation PodsDummy_SwiftySound +@end diff --git a/Pods/Target Support Files/SwiftySound/SwiftySound-prefix.pch b/Pods/Target Support Files/SwiftySound/SwiftySound-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/SwiftySound/SwiftySound-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/SwiftySound/SwiftySound-umbrella.h b/Pods/Target Support Files/SwiftySound/SwiftySound-umbrella.h new file mode 100644 index 0000000..e02f63d --- /dev/null +++ b/Pods/Target Support Files/SwiftySound/SwiftySound-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double SwiftySoundVersionNumber; +FOUNDATION_EXPORT const unsigned char SwiftySoundVersionString[]; + diff --git a/Pods/Target Support Files/SwiftySound/SwiftySound.modulemap b/Pods/Target Support Files/SwiftySound/SwiftySound.modulemap new file mode 100644 index 0000000..720b4d8 --- /dev/null +++ b/Pods/Target Support Files/SwiftySound/SwiftySound.modulemap @@ -0,0 +1,6 @@ +framework module SwiftySound { + umbrella header "SwiftySound-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/SwiftySound/SwiftySound.xcconfig b/Pods/Target Support Files/SwiftySound/SwiftySound.xcconfig new file mode 100644 index 0000000..90c32af --- /dev/null +++ b/Pods/Target Support Files/SwiftySound/SwiftySound.xcconfig @@ -0,0 +1,10 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftySound +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_LDFLAGS = -framework "AVFoundation" -framework "Foundation" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftySound +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Pods/Target Support Files/SwiftyStoreKit/Info.plist b/Pods/Target Support Files/SwiftyStoreKit/Info.plist new file mode 100644 index 0000000..ca979b2 --- /dev/null +++ b/Pods/Target Support Files/SwiftyStoreKit/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 0.13.3 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit-dummy.m b/Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit-dummy.m new file mode 100644 index 0000000..a36a2e9 --- /dev/null +++ b/Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_SwiftyStoreKit : NSObject +@end +@implementation PodsDummy_SwiftyStoreKit +@end diff --git a/Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit-prefix.pch b/Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit-umbrella.h b/Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit-umbrella.h new file mode 100644 index 0000000..9d0a2d2 --- /dev/null +++ b/Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double SwiftyStoreKitVersionNumber; +FOUNDATION_EXPORT const unsigned char SwiftyStoreKitVersionString[]; + diff --git a/Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit.modulemap b/Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit.modulemap new file mode 100644 index 0000000..0c130ff --- /dev/null +++ b/Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit.modulemap @@ -0,0 +1,6 @@ +framework module SwiftyStoreKit { + umbrella header "SwiftyStoreKit-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit.xcconfig b/Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit.xcconfig new file mode 100644 index 0000000..75446ad --- /dev/null +++ b/Pods/Target Support Files/SwiftyStoreKit/SwiftyStoreKit.xcconfig @@ -0,0 +1,9 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/SwiftyStoreKit +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftyStoreKit +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Pods/Target Support Files/paper-onboarding/Info.plist b/Pods/Target Support Files/paper-onboarding/Info.plist new file mode 100644 index 0000000..c26f36f --- /dev/null +++ b/Pods/Target Support Files/paper-onboarding/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 4.1.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/paper-onboarding/paper-onboarding-dummy.m b/Pods/Target Support Files/paper-onboarding/paper-onboarding-dummy.m new file mode 100644 index 0000000..023adb0 --- /dev/null +++ b/Pods/Target Support Files/paper-onboarding/paper-onboarding-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_paper_onboarding : NSObject +@end +@implementation PodsDummy_paper_onboarding +@end diff --git a/Pods/Target Support Files/paper-onboarding/paper-onboarding-prefix.pch b/Pods/Target Support Files/paper-onboarding/paper-onboarding-prefix.pch new file mode 100644 index 0000000..beb2a24 --- /dev/null +++ b/Pods/Target Support Files/paper-onboarding/paper-onboarding-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Pods/Target Support Files/paper-onboarding/paper-onboarding-umbrella.h b/Pods/Target Support Files/paper-onboarding/paper-onboarding-umbrella.h new file mode 100644 index 0000000..ab4c571 --- /dev/null +++ b/Pods/Target Support Files/paper-onboarding/paper-onboarding-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double paper_onboardingVersionNumber; +FOUNDATION_EXPORT const unsigned char paper_onboardingVersionString[]; + diff --git a/Pods/Target Support Files/paper-onboarding/paper-onboarding.modulemap b/Pods/Target Support Files/paper-onboarding/paper-onboarding.modulemap new file mode 100644 index 0000000..daa94cb --- /dev/null +++ b/Pods/Target Support Files/paper-onboarding/paper-onboarding.modulemap @@ -0,0 +1,6 @@ +framework module paper_onboarding { + umbrella header "paper-onboarding-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/paper-onboarding/paper-onboarding.xcconfig b/Pods/Target Support Files/paper-onboarding/paper-onboarding.xcconfig new file mode 100644 index 0000000..3de9d6c --- /dev/null +++ b/Pods/Target Support Files/paper-onboarding/paper-onboarding.xcconfig @@ -0,0 +1,9 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/paper-onboarding +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/paper-onboarding +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Pods/paper-onboarding/LICENSE b/Pods/paper-onboarding/LICENSE new file mode 100644 index 0000000..bb45230 --- /dev/null +++ b/Pods/paper-onboarding/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Ramotion + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Pods/paper-onboarding/README.md b/Pods/paper-onboarding/README.md new file mode 100644 index 0000000..8f43589 --- /dev/null +++ b/Pods/paper-onboarding/README.md @@ -0,0 +1,153 @@ +![header](./header.png) + +

+ +# paper-onboarding +[![Twitter](https://img.shields.io/badge/Twitter-@Ramotion-blue.svg?style=flat)](http://twitter.com/Ramotion) +[![CocoaPods](https://img.shields.io/cocoapods/p/paper-onboarding.svg)](https://cocoapods.org/pods/paper-onboarding) +[![CocoaPods](https://img.shields.io/cocoapods/v/paper-onboarding.svg)](http://cocoapods.org/pods/paper-onboarding) +[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Ramotion/paper-onboarding) +[![Travis](https://img.shields.io/travis/Ramotion/paper-onboarding.svg)](https://travis-ci.org/Ramotion/paper-onboarding) +[![codebeat badge](https://codebeat.co/badges/d06237c6-6ff7-4560-9602-b6cc65063383)](https://codebeat.co/projects/github-com-ramotion-paper-onboarding) +[![Donate](https://img.shields.io/badge/Donate-PayPal-blue.svg)](https://paypal.me/Ramotion) + +# Check this library on other platforms: + + + +**Looking for developers for your project?**
+This project is maintained by Ramotion, Inc. We specialize in the designing and coding of custom UI for Mobile Apps and Websites. + + +
+ +The [iPhone mockup](https://store.ramotion.com?utm_source=gthb&utm_medium=special&utm_campaign=paper-onboarding) available [here](https://store.ramotion.com?utm_source=gthb&utm_medium=special&utm_campaign=paper-onboarding). + +## Requirements + +- iOS 10.0+ +- Xcode 9 + +## Installation + +Just add the Source folder to your project. + +or use [CocoaPods](https://cocoapods.org) with Podfile: + +``` ruby +pod 'paper-onboarding' +``` + +or [Carthage](https://github.com/Carthage/Carthage) users can simply add to their `Cartfile`: +``` +github "Ramotion/paper-onboarding" +``` + +## Usage + +#### Storyboard + +1) Create a new UIView inheriting from ```PaperOnboarding``` + +2) Set dataSource in attribute inspector + +#### or Code + +``` swift +override func viewDidLoad() { + super.viewDidLoad() + + let onboarding = PaperOnboarding(itemsCount: 3) + onboarding.dataSource = self + onboarding.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(onboarding) + + // add constraints + for attribute: NSLayoutAttribute in [.Left, .Right, .Top, .Bottom] { + let constraint = NSLayoutConstraint(item: onboarding, + attribute: attribute, + relatedBy: .Equal, + toItem: view, + attribute: attribute, + multiplier: 1, + constant: 0) + view.addConstraint(constraint) + } +} +``` + +#### For adding content use dataSource methods: + +``` swift + func onboardingItem(at index: Int) -> OnboardingItemInfo { + + return [ + OnboardingItemInfo(informationImage: IMAGE, + title: "title", + description: "description", + pageIcon: IMAGE, + color: UIColor.RANDOM, + titleColor: UIColor.RANDOM, + descriptionColor: UIColor.RANDOM, + titleFont: UIFont.FONT, + descriptionFont: UIFont.FONT), + + OnboardingItemInfo(informationImage: IMAGE, + title: "title", + description: "description", + pageIcon: IMAGE, + color: UIColor.RANDOM, + titleColor: UIColor.RANDOM, + descriptionColor: UIColor.RANDOM, + titleFont: UIFont.FONT, + descriptionFont: UIFont.FONT), + + OnboardingItemInfo(informationImage: IMAGE, + title: "title", + description: "description", + pageIcon: IMAGE, + color: UIColor.RANDOM, + titleColor: UIColor.RANDOM, + descriptionColor: UIColor.RANDOM, + titleFont: UIFont.FONT, + descriptionFont: UIFont.FONT) + ][index] + } + + func onboardingItemsCount() -> Int { + return 3 + } + +``` + +#### configuring content item: + +``` swift +func onboardingConfigurationItem(item: OnboardingContentViewItem, index: Int) { + +// item.titleLabel?.backgroundColor = .redColor() +// item.descriptionLabel?.backgroundColor = .redColor() +// item.imageView = ... + } +``` +## License + +paper-onboarding is released under the MIT license. +See [LICENSE](./LICENSE) for details. + +
+ +# Get the Showroom App for iOS to give it a try +Try this UI component and more like this in our iOS app. Contact us if interested. + + + + + + +
+
+ +Follow us for the latest updates
+ + diff --git a/Pods/paper-onboarding/Source/FillAnimationView/FillAnimationView.swift b/Pods/paper-onboarding/Source/FillAnimationView/FillAnimationView.swift new file mode 100644 index 0000000..4d018ad --- /dev/null +++ b/Pods/paper-onboarding/Source/FillAnimationView/FillAnimationView.swift @@ -0,0 +1,94 @@ +// +// FillAnimationView.swift +// FillanimationTest +// +// Created by Alex K. on 20/04/16. +// Copyright © 2016 Alex K. All rights reserved. +// + +import UIKit + +class FillAnimationView: UIView { + + fileprivate struct Constant { + static let path = "path" + static let circle = "circle" + } +} + +// MARK: public + +extension FillAnimationView { + + class func animationViewOnView(_ view: UIView, color: UIColor) -> FillAnimationView { + let animationView = Init(FillAnimationView(frame: CGRect.zero)) { + $0.backgroundColor = color + $0.translatesAutoresizingMaskIntoConstraints = false + } + + view.addSubview(animationView) + + // add constraints + for attribute in [NSLayoutAttribute.left, NSLayoutAttribute.right, NSLayoutAttribute.top, NSLayoutAttribute.bottom] { + (view, animationView) >>>- { $0.attribute = attribute; return } + } + + return animationView + } + + func fillAnimation(_ color: UIColor, centerPosition: CGPoint, duration: Double) { + + let radius = max(bounds.size.width, bounds.size.height) * 1.5 + let circle = createCircleLayer(centerPosition, color: color) + + let animation = animationToRadius(radius, center: centerPosition, duration: duration) + animation.setValue(circle, forKey: Constant.circle) + circle.add(animation, forKey: nil) + } +} + +// MARK: create + +extension FillAnimationView { + + fileprivate func createCircleLayer(_ position: CGPoint, color: UIColor) -> CAShapeLayer { + let path = UIBezierPath(arcCenter: position, radius: 1, startAngle: 0, endAngle: CGFloat(2.0 * Double.pi), clockwise: true) + let layer = Init(CAShapeLayer()) { + $0.path = path.cgPath + $0.fillColor = color.cgColor + $0.shouldRasterize = true + } + + self.layer.addSublayer(layer) + return layer + } +} + +// MARK: animation + +extension FillAnimationView: CAAnimationDelegate { + + fileprivate func animationToRadius(_ radius: CGFloat, center: CGPoint, duration: Double) -> CABasicAnimation { + let path = UIBezierPath(arcCenter: center, radius: radius, startAngle: 0, endAngle: CGFloat(2.0 * Double.pi), clockwise: true) + let animation = Init(CABasicAnimation(keyPath: Constant.path)) { + $0.duration = duration + $0.toValue = path.cgPath + $0.isRemovedOnCompletion = false + $0.fillMode = kCAFillModeForwards + $0.delegate = self + $0.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseIn) + } + return animation + } + + // animation delegate + + func animationDidStop(_ anim: CAAnimation, finished _: Bool) { + + guard let circleLayer = anim.value(forKey: Constant.circle) as? CAShapeLayer else { + return + } + layer.backgroundColor = circleLayer.fillColor + circleLayer.removeFromSuperlayer() + } +} diff --git a/Pods/paper-onboarding/Source/GestureControl/GestureControl.swift b/Pods/paper-onboarding/Source/GestureControl/GestureControl.swift new file mode 100644 index 0000000..4da6adc --- /dev/null +++ b/Pods/paper-onboarding/Source/GestureControl/GestureControl.swift @@ -0,0 +1,57 @@ +// +// GestureControl.swift +// AnimatedPageView +// +// Created by Alex K. on 21/04/16. +// Copyright © 2016 Alex K. All rights reserved. +// + +import UIKit + +protocol GestureControlDelegate: class { + func gestureControlDidSwipe(_ direction: UISwipeGestureRecognizerDirection) +} + +class GestureControl: UIView { + + weak var delegate: GestureControlDelegate! + + init(view: UIView, delegate: GestureControlDelegate) { + self.delegate = delegate + + super.init(frame: CGRect.zero) + + let swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(GestureControl.swipeHandler(_:))) + swipeLeft.direction = .left + addGestureRecognizer(swipeLeft) + + let swipeRight = UISwipeGestureRecognizer(target: self, action: #selector(GestureControl.swipeHandler(_:))) + swipeRight.direction = .right + addGestureRecognizer(swipeRight) + + translatesAutoresizingMaskIntoConstraints = false + backgroundColor = .clear + + view.addSubview(self) + // add constraints + for attribute: NSLayoutAttribute in [.left, .right, .top, .bottom] { + (view, self) >>>- { + $0.attribute = attribute + return + } + } + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +// MARK: actions + +extension GestureControl { + + @objc dynamic func swipeHandler(_ gesture: UISwipeGestureRecognizer) { + delegate.gestureControlDidSwipe(gesture.direction) + } +} diff --git a/Pods/paper-onboarding/Source/Helpers/Configuring.swift b/Pods/paper-onboarding/Source/Helpers/Configuring.swift new file mode 100644 index 0000000..80851cd --- /dev/null +++ b/Pods/paper-onboarding/Source/Helpers/Configuring.swift @@ -0,0 +1,14 @@ +// +// Configuring.swift +// AnimatedPageView +// +// Created by Alex K. on 12/04/16. +// Copyright © 2016 Alex K. All rights reserved. +// + +import Foundation + +internal func Init(_ value: Type, block: (_ object: Type) -> Void) -> Type { + block(value) + return value +} diff --git a/Pods/paper-onboarding/Source/Helpers/ConstraintsHelper.swift b/Pods/paper-onboarding/Source/Helpers/ConstraintsHelper.swift new file mode 100644 index 0000000..87c7968 --- /dev/null +++ b/Pods/paper-onboarding/Source/Helpers/ConstraintsHelper.swift @@ -0,0 +1,77 @@ +// +// ConstraintsHelper.swift +// AnimatedPageView +// +// Created by Alex K. on 13/04/16. +// Copyright © 2016 Alex K. All rights reserved. +// + +import UIKit + +struct ConstraintInfo { + var attribute: NSLayoutAttribute = .left + var secondAttribute: NSLayoutAttribute = .notAnAttribute + var constant: CGFloat = 0 + var identifier: String? + var relation: NSLayoutRelation = .equal +} + +precedencegroup constOp { + associativity: left + higherThan: AssignmentPrecedence +} + +infix operator >>>-: constOp + +@discardableResult +func >>>- (left: (T, T), block: (inout ConstraintInfo) -> Void) -> NSLayoutConstraint { + var info = ConstraintInfo() + block(&info) + info.secondAttribute = info.secondAttribute == .notAnAttribute ? info.attribute : info.secondAttribute + + let constraint = NSLayoutConstraint(item: left.1, + attribute: info.attribute, + relatedBy: info.relation, + toItem: left.0, + attribute: info.secondAttribute, + multiplier: 1, + constant: info.constant) + constraint.identifier = info.identifier + left.0.addConstraint(constraint) + return constraint +} + +@discardableResult +func >>>- (left: T, block: (inout ConstraintInfo) -> Void) -> NSLayoutConstraint { + var info = ConstraintInfo() + block(&info) + + let constraint = NSLayoutConstraint(item: left, + attribute: info.attribute, + relatedBy: info.relation, + toItem: nil, + attribute: info.attribute, + multiplier: 1, + constant: info.constant) + constraint.identifier = info.identifier + left.addConstraint(constraint) + return constraint +} + +@discardableResult +func >>>- (left: (T, T, T), block: (inout ConstraintInfo) -> Void) -> NSLayoutConstraint { + var info = ConstraintInfo() + block(&info) + info.secondAttribute = info.secondAttribute == .notAnAttribute ? info.attribute : info.secondAttribute + + let constraint = NSLayoutConstraint(item: left.1, + attribute: info.attribute, + relatedBy: info.relation, + toItem: left.2, + attribute: info.secondAttribute, + multiplier: 1, + constant: info.constant) + constraint.identifier = info.identifier + left.0.addConstraint(constraint) + return constraint +} diff --git a/Pods/paper-onboarding/Source/OnboardingContentView/Item/OnboardingContantViewItem.swift b/Pods/paper-onboarding/Source/OnboardingContentView/Item/OnboardingContantViewItem.swift new file mode 100644 index 0000000..8562489 --- /dev/null +++ b/Pods/paper-onboarding/Source/OnboardingContentView/Item/OnboardingContantViewItem.swift @@ -0,0 +1,187 @@ +// +// OnboardingContentViewItem.swift +// AnimatedPageView +// +// Created by Alex K. on 21/04/16. +// Copyright © 2016 Alex K. All rights reserved. +// + +import UIKit + +open class OnboardingContentViewItem: UIView { + + public var descriptionBottomConstraint: NSLayoutConstraint? + public var titleCenterConstraint: NSLayoutConstraint? + public var informationImageWidthConstraint: NSLayoutConstraint? + public var informationImageHeightConstraint: NSLayoutConstraint? + + open var imageView: UIImageView? + open var titleLabel: UILabel? + open var descriptionLabel: UILabel? + + override init(frame: CGRect) { + super.init(frame: frame) + commonInit() + } + + public required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +// MARK: public + +extension OnboardingContentViewItem { + + class func itemOnView(_ view: UIView) -> OnboardingContentViewItem { + let item = Init(OnboardingContentViewItem(frame: CGRect.zero)) { + $0.backgroundColor = .clear + $0.translatesAutoresizingMaskIntoConstraints = false + } + + view.addSubview(item) + + // add constraints + item >>>- { + $0.attribute = .height + $0.constant = 10000 + $0.relation = .lessThanOrEqual + return + } + + for attribute in [NSLayoutAttribute.leading, NSLayoutAttribute.trailing] { + (view, item) >>>- { + $0.attribute = attribute + return + } + } + + for attribute in [NSLayoutAttribute.centerX, NSLayoutAttribute.centerY] { + (view, item) >>>- { + $0.attribute = attribute + return + } + } + + return item + } +} + +// MARK: create + +private extension OnboardingContentViewItem { + + func commonInit() { + + let titleLabel = createTitleLabel(self) + let descriptionLabel = createDescriptionLabel(self) + let imageView = createImage(self) + + // added constraints + titleCenterConstraint = (self, titleLabel, imageView) >>>- { + $0.attribute = .top + $0.secondAttribute = .bottom + $0.constant = 50 + return + } + (self, descriptionLabel, titleLabel) >>>- { + $0.attribute = .top + $0.secondAttribute = .bottom + $0.constant = 10 + return + } + + self.titleLabel = titleLabel + self.descriptionLabel = descriptionLabel + self.imageView = imageView + } + + func createTitleLabel(_ onView: UIView) -> UILabel { + let label = Init(createLabel()) { + $0.font = UIFont(name: "Nunito-Bold", size: 36) + } + onView.addSubview(label) + + // add constraints + label >>>- { + $0.attribute = .height + $0.constant = 10000 + $0.relation = .lessThanOrEqual + return + } + + for attribute in [NSLayoutAttribute.centerX, NSLayoutAttribute.leading, NSLayoutAttribute.trailing] { + (onView, label) >>>- { + $0.attribute = attribute + return + } + } + return label + } + + func createDescriptionLabel(_ onView: UIView) -> UILabel { + let label = Init(createLabel()) { + $0.font = UIFont(name: "OpenSans-Regular", size: 14) + $0.numberOfLines = 0 + } + onView.addSubview(label) + + // add constraints + label >>>- { + $0.attribute = .height + $0.constant = 10000 + $0.relation = .lessThanOrEqual + return + } + + for (attribute, constant) in [(NSLayoutAttribute.leading, 30), (NSLayoutAttribute.trailing, -30)] { + (onView, label) >>>- { + $0.attribute = attribute + $0.constant = CGFloat(constant) + return + } + } + + (onView, label) >>>- { $0.attribute = .centerX; return } + descriptionBottomConstraint = (onView, label) >>>- { $0.attribute = .bottom; return } + + return label + } + + func createLabel() -> UILabel { + return Init(UILabel(frame: CGRect.zero)) { + $0.backgroundColor = .clear + $0.translatesAutoresizingMaskIntoConstraints = false + $0.textAlignment = .center + $0.textColor = .white + } + } + + func createImage(_ onView: UIView) -> UIImageView { + let imageView = Init(UIImageView(frame: CGRect.zero)) { + $0.contentMode = .scaleAspectFit + $0.translatesAutoresizingMaskIntoConstraints = false + } + + onView.addSubview(imageView) + + // add constratints + informationImageWidthConstraint = imageView >>>- { + $0.attribute = NSLayoutAttribute.width + $0.constant = 188 + return + } + + informationImageHeightConstraint = imageView >>>- { + $0.attribute = NSLayoutAttribute.height + $0.constant = 188 + return + } + + for attribute in [NSLayoutAttribute.centerX, NSLayoutAttribute.top] { + (onView, imageView) >>>- { $0.attribute = attribute; return } + } + + return imageView + } +} diff --git a/Pods/paper-onboarding/Source/OnboardingContentView/OnboardingContentView.swift b/Pods/paper-onboarding/Source/OnboardingContentView/OnboardingContentView.swift new file mode 100644 index 0000000..3bb6dc9 --- /dev/null +++ b/Pods/paper-onboarding/Source/OnboardingContentView/OnboardingContentView.swift @@ -0,0 +1,149 @@ +// +// OnboardingContentView.swift +// AnimatedPageView +// +// Created by Alex K. on 21/04/16. +// Copyright © 2016 Alex K. All rights reserved. +// + +import UIKit + +protocol OnboardingContentViewDelegate: class { + + func onboardingItemAtIndex(_ index: Int) -> OnboardingItemInfo? + func onboardingConfigurationItem(_ item: OnboardingContentViewItem, index: Int) +} + +class OnboardingContentView: UIView { + + fileprivate struct Constants { + static let dyOffsetAnimation: CGFloat = 110 + static let showDuration: Double = 0.8 + static let hideDuration: Double = 0.2 + } + + fileprivate var currentItem: OnboardingContentViewItem? + weak var delegate: OnboardingContentViewDelegate? + + init(itemsCount _: Int, delegate: OnboardingContentViewDelegate) { + self.delegate = delegate + super.init(frame: CGRect.zero) + + commonInit() + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +// MARK: public + +extension OnboardingContentView { + + func currentItem(_ index: Int, animated _: Bool) { + + let showItem = createItem(index) + showItemView(showItem, duration: Constants.showDuration) + + hideItemView(currentItem, duration: Constants.hideDuration) + + currentItem = showItem + } +} + +// MARK: life cicle + +extension OnboardingContentView { + + class func contentViewOnView(_ view: UIView, delegate: OnboardingContentViewDelegate, itemsCount: Int, bottomConstant: CGFloat) -> OnboardingContentView { + let contentView = Init(OnboardingContentView(itemsCount: itemsCount, delegate: delegate)) { + $0.backgroundColor = .clear + $0.translatesAutoresizingMaskIntoConstraints = false + } + view.addSubview(contentView) + + // add constraints + for attribute in [NSLayoutAttribute.left, NSLayoutAttribute.right, NSLayoutAttribute.top] { + (view, contentView) >>>- { $0.attribute = attribute; return } + } + (view, contentView) >>>- { + $0.attribute = .bottom + $0.constant = bottomConstant + return + } + return contentView + } +} + +// MARK: create + +extension OnboardingContentView { + + fileprivate func commonInit() { + + currentItem = createItem(0) + } + + fileprivate func createItem(_ index: Int) -> OnboardingContentViewItem { + + guard let info = delegate?.onboardingItemAtIndex(index) else { + return OnboardingContentViewItem.itemOnView(self) + } + + let item = Init(OnboardingContentViewItem.itemOnView(self)) { + $0.imageView?.image = info.informationImage + $0.titleLabel?.text = info.title + $0.titleLabel?.font = info.titleFont + $0.titleLabel?.textColor = info.titleColor + $0.descriptionLabel?.text = info.description + $0.descriptionLabel?.font = info.descriptionFont + $0.descriptionLabel?.textColor = info.descriptionColor + } + + delegate?.onboardingConfigurationItem(item, index: index) + return item + } +} + +// MARK: animations + +extension OnboardingContentView { + + fileprivate func hideItemView(_ item: OnboardingContentViewItem?, duration: Double) { + guard let item = item else { + return + } + + item.descriptionBottomConstraint?.constant -= Constants.dyOffsetAnimation + item.titleCenterConstraint?.constant *= 1.3 + + UIView.animate(withDuration: duration, + delay: 0, + options: .curveEaseOut, animations: { + item.alpha = 0 + self.layoutIfNeeded() + }, + completion: { _ in + item.removeFromSuperview() + }) + } + + fileprivate func showItemView(_ item: OnboardingContentViewItem, duration: Double) { + item.descriptionBottomConstraint?.constant = Constants.dyOffsetAnimation + item.titleCenterConstraint?.constant /= 2 + item.alpha = 0 + layoutIfNeeded() + + item.descriptionBottomConstraint?.constant = 0 + item.titleCenterConstraint?.constant *= 2 + + UIView.animate(withDuration: duration, + delay: 0, + options: .curveEaseOut, animations: { + item.alpha = 0 + item.alpha = 1 + self.layoutIfNeeded() + }, completion: nil) + } +} diff --git a/Pods/paper-onboarding/Source/PageView/Item/PageViewItem.swift b/Pods/paper-onboarding/Source/PageView/Item/PageViewItem.swift new file mode 100644 index 0000000..e48b194 --- /dev/null +++ b/Pods/paper-onboarding/Source/PageView/Item/PageViewItem.swift @@ -0,0 +1,155 @@ +// +// PageViewItem.swift +// AnimatedPageView +// +// Created by Alex K. on 12/04/16. +// Copyright © 2016 Alex K. All rights reserved. +// + +import UIKit + +class PageViewItem: UIView { + + let circleRadius: CGFloat + let selectedCircleRadius: CGFloat + let lineWidth: CGFloat + let itemColor: UIColor + + var select: Bool + + var centerView: UIView? + var imageView: UIImageView? + var circleLayer: CAShapeLayer? + var tickIndex: Int = 0 + + init(radius: CGFloat, itemColor: UIColor, selectedRadius: CGFloat, lineWidth: CGFloat = 3, isSelect: Bool = false) { + self.itemColor = itemColor + self.lineWidth = lineWidth + circleRadius = radius + selectedCircleRadius = selectedRadius + select = isSelect + super.init(frame: CGRect.zero) + commonInit() + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +// MARK: public + +extension PageViewItem { + + func animationSelected(_ selected: Bool, duration: Double, fillColor: Bool) { + let toAlpha: CGFloat = selected == true ? 1 : 0 + imageAlphaAnimation(toAlpha, duration: duration) + + let currentRadius = selected == true ? selectedCircleRadius : circleRadius + let scaleAnimation = circleScaleAnimation(currentRadius - lineWidth / 2.0, duration: duration) + let toColor = fillColor == true ? itemColor : UIColor.clear + let colorAnimation = circleBackgroundAnimation(toColor, duration: duration) + + circleLayer?.add(scaleAnimation, forKey: nil) + circleLayer?.add(colorAnimation, forKey: nil) + } +} + +// MARK: configuration + +extension PageViewItem { + + fileprivate func commonInit() { + centerView = createBorderView() + imageView = createImageView() + } + + fileprivate func createBorderView() -> UIView { + let view = Init(UIView(frame: CGRect.zero)) { + $0.backgroundColor = .blue + $0.translatesAutoresizingMaskIntoConstraints = false + } + addSubview(view) + + // create circle layer + let currentRadius = select == true ? selectedCircleRadius : circleRadius + let circleLayer = createCircleLayer(currentRadius, lineWidth: lineWidth) + view.layer.addSublayer(circleLayer) + self.circleLayer = circleLayer + + // add constraints + [NSLayoutAttribute.centerX, NSLayoutAttribute.centerY].forEach { attribute in + (self, view) >>>- { + $0.attribute = attribute + return + } + } + [NSLayoutAttribute.height, NSLayoutAttribute.width].forEach { attribute in + view >>>- { + $0.attribute = attribute + return + } + } + return view + } + + fileprivate func createCircleLayer(_ radius: CGFloat, lineWidth: CGFloat) -> CAShapeLayer { + let path = UIBezierPath(arcCenter: CGPoint.zero, radius: radius - lineWidth / 2.0, startAngle: 0, endAngle: CGFloat(2.0 * Double.pi), clockwise: true) + let layer = Init(CAShapeLayer()) { + $0.path = path.cgPath + $0.lineWidth = lineWidth + $0.strokeColor = itemColor.cgColor + $0.fillColor = UIColor.clear.cgColor + } + return layer + } + + fileprivate func createImageView() -> UIImageView { + let imageView = Init(UIImageView(frame: CGRect.zero)) { + $0.contentMode = .scaleAspectFit + $0.translatesAutoresizingMaskIntoConstraints = false + $0.alpha = select == true ? 1 : 0 + } + addSubview(imageView) + + // add constraints + [NSLayoutAttribute.left, NSLayoutAttribute.right, NSLayoutAttribute.top, NSLayoutAttribute.bottom].forEach { attribute in + (self, imageView) >>>- { $0.attribute = attribute; return } + } + + return imageView + } +} + +// MARK: animations + +extension PageViewItem { + + fileprivate func circleScaleAnimation(_ toRadius: CGFloat, duration: Double) -> CABasicAnimation { + let path = UIBezierPath(arcCenter: CGPoint.zero, radius: toRadius, startAngle: 0, endAngle: CGFloat(2.0 * Double.pi), clockwise: true) + let animation = Init(CABasicAnimation(keyPath: "path")) { + $0.duration = duration + $0.toValue = path.cgPath + $0.isRemovedOnCompletion = false + $0.fillMode = kCAFillModeForwards + } + return animation + } + + fileprivate func circleBackgroundAnimation(_ toColor: UIColor, duration: Double) -> CABasicAnimation { + let animation = Init(CABasicAnimation(keyPath: "fillColor")) { + $0.duration = duration + $0.toValue = toColor.cgColor + $0.isRemovedOnCompletion = false + $0.fillMode = kCAFillModeForwards + $0.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut) + } + return animation + } + + fileprivate func imageAlphaAnimation(_ toValue: CGFloat, duration: Double) { + UIView.animate(withDuration: duration, delay: 0, options: UIViewAnimationOptions(), animations: { + self.imageView?.alpha = toValue + }, completion: nil) + } +} diff --git a/Pods/paper-onboarding/Source/PageView/PageContainerView/PageContainer.swift b/Pods/paper-onboarding/Source/PageView/PageContainerView/PageContainer.swift new file mode 100644 index 0000000..977cfc4 --- /dev/null +++ b/Pods/paper-onboarding/Source/PageView/PageContainerView/PageContainer.swift @@ -0,0 +1,139 @@ +// +// PageContaineView.swift +// AnimatedPageView +// +// Created by Alex K. on 13/04/16. +// Copyright © 2016 Alex K. All rights reserved. +// + +import UIKit + +class PageContrainer: UIView { + + var items: [PageViewItem]? + let space: CGFloat // space between items + var currentIndex = 0 + + fileprivate let itemRadius: CGFloat + fileprivate let selectedItemRadius: CGFloat + fileprivate let itemsCount: Int + fileprivate let animationKey = "animationKey" + + init(radius: CGFloat, selectedRadius: CGFloat, space: CGFloat, itemsCount: Int, itemColor: (Int) -> UIColor) { + self.itemsCount = itemsCount + self.space = space + itemRadius = radius + selectedItemRadius = selectedRadius + super.init(frame: CGRect.zero) + items = createItems(itemsCount, radius: radius, selectedRadius: selectedRadius, itemColor: itemColor) + } + + required init?(coder _: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +// MARK: public + +extension PageContrainer { + + func currenteIndex(_ index: Int, duration: Double, animated _: Bool) { + guard let items = self.items, + index != currentIndex else { return } + + animationItem(items[index], selected: true, duration: duration) + + let fillColor = index > currentIndex ? true : false + animationItem(items[currentIndex], selected: false, duration: duration, fillColor: fillColor) + + currentIndex = index + } +} + +// MARK: animations + +extension PageContrainer { + + fileprivate func animationItem(_ item: PageViewItem, selected: Bool, duration: Double, fillColor: Bool = false) { + let toValue = selected == true ? selectedItemRadius * 2 : itemRadius * 2 + item.constraints + .filter { $0.identifier == "animationKey" } + .forEach { + $0.constant = toValue + } + + UIView.animate(withDuration: duration, delay: 0, options: .curveEaseOut, animations: { + self.layoutIfNeeded() + }, completion: nil) + + item.animationSelected(selected, duration: duration, fillColor: fillColor) + } +} + +// MARK: create + +extension PageContrainer { + + fileprivate func createItems(_ count: Int, radius: CGFloat, selectedRadius: CGFloat, itemColor: (Int) -> UIColor) -> [PageViewItem] { + var items = [PageViewItem]() + // create first item + var tag = 1 + var item = createItem(radius, selectedRadius: selectedRadius, isSelect: true, itemColor: itemColor(tag - 1)) + item.tag = tag + addConstraintsToView(item, radius: selectedRadius) + items.append(item) + + for _ in 1 ..< count { + tag += 1 + let nextItem = createItem(radius, selectedRadius: selectedRadius, itemColor: itemColor(tag - 1)) + addConstraintsToView(nextItem, leftItem: item, radius: radius) + items.append(nextItem) + item = nextItem + item.tag = tag + } + return items + } + + fileprivate func createItem(_ radius: CGFloat, selectedRadius: CGFloat, isSelect: Bool = false, itemColor: UIColor) -> PageViewItem { + let item = Init(PageViewItem(radius: radius, itemColor: itemColor, selectedRadius: selectedRadius, isSelect: isSelect)) { + $0.translatesAutoresizingMaskIntoConstraints = false + $0.backgroundColor = .clear + } + addSubview(item) + + return item + } + + fileprivate func addConstraintsToView(_ item: UIView, radius: CGFloat) { + [NSLayoutAttribute.left, NSLayoutAttribute.centerY].forEach { attribute in + (self, item) >>>- { $0.attribute = attribute; return } + } + + [NSLayoutAttribute.width, NSLayoutAttribute.height].forEach { attribute in + item >>>- { + $0.attribute = attribute + $0.constant = radius * 2.0 + $0.identifier = animationKey + return + } + } + } + + fileprivate func addConstraintsToView(_ item: UIView, leftItem: UIView, radius: CGFloat) { + (self, item) >>>- { $0.attribute = .centerY; return } + (self, item, leftItem) >>>- { + $0.attribute = .leading + $0.secondAttribute = .trailing + $0.constant = space + return + } + [NSLayoutAttribute.width, NSLayoutAttribute.height].forEach { attribute in + item >>>- { + $0.attribute = attribute + $0.constant = radius * 2.0 + $0.identifier = animationKey + return + } + } + } +} diff --git a/Pods/paper-onboarding/Source/PageView/PageView.swift b/Pods/paper-onboarding/Source/PageView/PageView.swift new file mode 100644 index 0000000..f469e76 --- /dev/null +++ b/Pods/paper-onboarding/Source/PageView/PageView.swift @@ -0,0 +1,184 @@ +// +// PageView.swift +// AnimatedPageView +// +// Created by Alex K. on 13/04/16. +// Copyright © 2016 Alex K. All rights reserved. +// + +import UIKit + +class PageView: UIView { + + var itemsCount = 3 + var itemRadius: CGFloat = 8.0 + var selectedItemRadius: CGFloat = 22.0 + var duration: Double = 0.7 + var space: CGFloat = 20 // space between items + let itemColor: (Int) -> UIColor + + // configure items set image or chage color for border view + var configuration: ((_ item: PageViewItem, _ index: Int) -> Void)? { + didSet { + configurePageItems(containerView?.items) + } + } + + fileprivate var containerX: NSLayoutConstraint? + var containerView: PageContrainer? + + init(frame: CGRect, itemsCount: Int, radius: CGFloat, selectedRadius: CGFloat, itemColor: @escaping (Int) -> UIColor) { + self.itemsCount = itemsCount + itemRadius = radius + selectedItemRadius = selectedRadius + self.itemColor = itemColor + super.init(frame: frame) + commonInit() + } + + required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") } + + override func hitTest(_ point: CGPoint, with _: UIEvent?) -> UIView? { + guard + let containerView = self.containerView, + let items = containerView.items + else { return nil } + for item in items { + let frame = item.frame.insetBy(dx: -10, dy: -10) + guard frame.contains(point) else { continue } + return item + } + return nil + } +} + +// MARK: public + +extension PageView { + + class func pageViewOnView(_ view: UIView, itemsCount: Int, bottomConstant: CGFloat, radius: CGFloat, selectedRadius: CGFloat, itemColor: @escaping (Int) -> UIColor) -> PageView { + let pageView = PageView(frame: CGRect.zero, + itemsCount: itemsCount, + radius: radius, + selectedRadius: selectedRadius, + itemColor: itemColor) + pageView.translatesAutoresizingMaskIntoConstraints = false + pageView.alpha = 0.4 + view.addSubview(pageView) + + let layoutAttribs:[(NSLayoutAttribute, Int)] = [(NSLayoutAttribute.left, 0), (NSLayoutAttribute.right, 0), (NSLayoutAttribute.bottom, Int(bottomConstant))] + + // add constraints + for (attribute, const) in layoutAttribs { + (view, pageView) >>>- { + $0.constant = CGFloat(const) + $0.attribute = attribute + return + } + } + pageView >>>- { + $0.attribute = .height + $0.constant = 30 + return + } + + return pageView + } + + func currentIndex(_ index: Int, animated: Bool) { + + if 0 ..< itemsCount ~= index { + containerView?.currenteIndex(index, duration: duration * 0.5, animated: animated) + moveContainerTo(index, animated: animated, duration: duration) + } + } + + func positionItemIndex(_ index: Int, onView: UIView) -> CGPoint? { + if 0 ..< itemsCount ~= index { + if let currentItem = containerView?.items?[index].imageView { + let pos = currentItem.convert(currentItem.center, to: onView) + return pos + } + } + return nil + } +} + +// MARK: life cicle + +extension PageView { + + fileprivate func commonInit() { + containerView = createContainerView() + currentIndex(0, animated: false) + backgroundColor = .clear + } +} + +// MARK: create + +extension PageView { + + fileprivate func createContainerView() -> PageContrainer { + let pageControl = PageContrainer(radius: itemRadius, + selectedRadius: selectedItemRadius, + space: space, + itemsCount: itemsCount, + itemColor: itemColor) + let container = Init(pageControl) { + $0.backgroundColor = .clear + $0.translatesAutoresizingMaskIntoConstraints = false + } + addSubview(container) + + // add constraints + for attribute in [NSLayoutAttribute.top, NSLayoutAttribute.bottom] { + (self, container) >>>- { $0.attribute = attribute; return } + } + + containerX = (self, container) >>>- { $0.attribute = .centerX; return } + + container >>>- { + $0.attribute = .width + $0.constant = selectedItemRadius * 2 + CGFloat(itemsCount - 1) * (itemRadius * 2) + space * CGFloat(itemsCount - 1) + return + } + return container + } + + fileprivate func configurePageItems(_ items: [PageViewItem]?) { + guard let items = items else { + return + } + for index in 0 ..< items.count { + configuration?(items[index], index) + } + } +} + +// MARK: animation + +extension PageView { + + fileprivate func moveContainerTo(_ index: Int, animated: Bool = true, duration: Double = 0) { + guard let containerX = self.containerX else { + return + } + + let containerWidth = CGFloat(itemsCount + 1) * selectedItemRadius + space * CGFloat(itemsCount - 1) + let toValue = containerWidth / 2.0 - selectedItemRadius - (selectedItemRadius + space) * CGFloat(index) + containerX.constant = toValue + + if animated == true { + UIView.animate(withDuration: duration, + delay: 0, + options: UIViewAnimationOptions(), + animations: { + self.layoutIfNeeded() + }, + completion: nil) + } else { + layoutIfNeeded() + } + } +} diff --git a/Pods/paper-onboarding/Source/PaperOnboarding.swift b/Pods/paper-onboarding/Source/PaperOnboarding.swift new file mode 100644 index 0000000..be4c7d6 --- /dev/null +++ b/Pods/paper-onboarding/Source/PaperOnboarding.swift @@ -0,0 +1,227 @@ +// +// PaperOnboarding.swift +// AnimatedPageView +// +// Created by Alex K. on 20/04/16. +// Copyright © 2016 Alex K. All rights reserved. +// + +import UIKit + +public struct OnboardingItemInfo { + public let informationImage: UIImage + public let title: String + public let description: String + public let pageIcon: UIImage + public let color: UIColor + public let titleColor: UIColor + public let descriptionColor: UIColor + public let titleFont: UIFont + public let descriptionFont: UIFont + + public init (informationImage: UIImage, title: String, description: String, pageIcon: UIImage, color: UIColor, titleColor: UIColor, descriptionColor: UIColor, titleFont: UIFont, descriptionFont: UIFont) { + self.informationImage = informationImage + self.title = title + self.description = description + self.pageIcon = pageIcon + self.color = color + self.titleColor = titleColor + self.descriptionColor = descriptionColor + self.titleFont = titleFont + self.descriptionFont = descriptionFont + } +} + +/// An instance of PaperOnboarding which display collection of information. +open class PaperOnboarding: UIView { + + /// The object that acts as the data source of the PaperOnboardingDataSource. + @IBOutlet weak open var dataSource: AnyObject? { + didSet { + commonInit() + } + } + + /// The object that acts as the delegate of the PaperOnboarding. PaperOnboardingDelegate protocol + @IBOutlet weak open var delegate: AnyObject? + + /// current index item + open fileprivate(set) var currentIndex: Int = 0 + fileprivate(set) var itemsCount: Int = 0 + + fileprivate var itemsInfo: [OnboardingItemInfo]? + + fileprivate let pageViewBottomConstant: CGFloat + fileprivate var pageViewSelectedRadius: CGFloat = 22 + fileprivate var pageViewRadius: CGFloat = 8 + + fileprivate var fillAnimationView: FillAnimationView? + fileprivate var pageView: PageView? + fileprivate var gestureControl: GestureControl? + fileprivate var contentView: OnboardingContentView? + + public init(pageViewBottomConstant: CGFloat = 32) { + + self.pageViewBottomConstant = pageViewBottomConstant + + super.init(frame: CGRect.zero) + } + + public required init?(coder aDecoder: NSCoder) { + + self.pageViewBottomConstant = 32 + self.pageViewSelectedRadius = 22 + self.pageViewRadius = 8 + + super.init(coder: aDecoder) + } +} + +// MARK: methods + +public extension PaperOnboarding { + + /** + Scrolls through the PaperOnboarding until a index is at a particular location on the screen. + + - parameter index: Scrolling to a curretn index item. + - parameter animated: True if you want to animate the change in position; false if it should be immediate. + */ + func currentIndex(_ index: Int, animated: Bool) { + if 0 ..< itemsCount ~= index { + (delegate as? PaperOnboardingDelegate)?.onboardingWillTransitonToIndex(index) + currentIndex = index + CATransaction.begin() + + CATransaction.setCompletionBlock({ + (self.delegate as? PaperOnboardingDelegate)?.onboardingDidTransitonToIndex(index) + }) + + if let postion = pageView?.positionItemIndex(index, onView: self) { + fillAnimationView?.fillAnimation(backgroundColor(currentIndex), centerPosition: postion, duration: 0.5) + } + pageView?.currentIndex(index, animated: animated) + contentView?.currentItem(index, animated: animated) + CATransaction.commit() + } else if index >= itemsCount { + (delegate as? PaperOnboardingDelegate)?.onboardingWillTransitonToLeaving() + } + } +} + +// MARK: create + +extension PaperOnboarding { + + fileprivate func commonInit() { + if case let dataSource as PaperOnboardingDataSource = dataSource { + itemsCount = dataSource.onboardingItemsCount() + } + if case let dataSource as PaperOnboardingDataSource = dataSource { + pageViewRadius = dataSource.onboardinPageItemRadius() + } + if case let dataSource as PaperOnboardingDataSource = dataSource { + pageViewSelectedRadius = dataSource.onboardingPageItemSelectedRadius() + } + itemsInfo = createItemsInfo() + translatesAutoresizingMaskIntoConstraints = false + fillAnimationView = FillAnimationView.animationViewOnView(self, color: backgroundColor(currentIndex)) + contentView = OnboardingContentView.contentViewOnView(self, + delegate: self, + itemsCount: itemsCount, + bottomConstant: pageViewBottomConstant * -1 - pageViewSelectedRadius) + pageView = createPageView() + gestureControl = GestureControl(view: self, delegate: self) + + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapAction)) + addGestureRecognizer(tapGesture) + } + + @objc fileprivate func tapAction(_ sender: UITapGestureRecognizer) { + guard + (delegate as? PaperOnboardingDelegate)?.enableTapsOnPageControl == true, + let pageView = self.pageView, + let pageControl = pageView.containerView + else { return } + let touchLocation = sender.location(in: self) + let convertedLocation = pageControl.convert(touchLocation, from: self) + guard let pageItem = pageView.hitTest(convertedLocation, with: nil) else { return } + let index = pageItem.tag - 1 + guard index != currentIndex else { return } + currentIndex(index, animated: true) + (delegate as? PaperOnboardingDelegate)?.onboardingWillTransitonToIndex(index) + } + + fileprivate func createPageView() -> PageView { + let pageView = PageView.pageViewOnView( + self, + itemsCount: itemsCount, + bottomConstant: pageViewBottomConstant * -1, + radius: pageViewRadius, + selectedRadius: pageViewSelectedRadius, + itemColor: { [weak self] in + guard let dataSource = self?.dataSource as? PaperOnboardingDataSource else { return .white } + return dataSource.onboardingPageItemColor(at: $0) + }) + + pageView.configuration = { [weak self] item, index in + item.imageView?.image = self?.itemsInfo?[index].pageIcon + } + + return pageView + } + + fileprivate func createItemsInfo() -> [OnboardingItemInfo] { + guard case let dataSource as PaperOnboardingDataSource = self.dataSource else { + fatalError("set dataSource") + } + + var items = [OnboardingItemInfo]() + for index in 0 ..< itemsCount { + let info = dataSource.onboardingItem(at: index) + items.append(info) + } + return items + } +} + +// MARK: helpers + +extension PaperOnboarding { + + fileprivate func backgroundColor(_ index: Int) -> UIColor { + guard let color = itemsInfo?[index].color else { + return .black + } + return color + } +} + +// MARK: GestureControlDelegate + +extension PaperOnboarding: GestureControlDelegate { + + func gestureControlDidSwipe(_ direction: UISwipeGestureRecognizerDirection) { + switch direction { + case UISwipeGestureRecognizerDirection.right: + currentIndex(currentIndex - 1, animated: true) + case UISwipeGestureRecognizerDirection.left: + currentIndex(currentIndex + 1, animated: true) + default: + fatalError() + } + } +} + +// MARK: OnboardingDelegate + +extension PaperOnboarding: OnboardingContentViewDelegate { + + func onboardingItemAtIndex(_ index: Int) -> OnboardingItemInfo? { + return itemsInfo?[index] + } + + @objc func onboardingConfigurationItem(_ item: OnboardingContentViewItem, index: Int) { + (delegate as? PaperOnboardingDelegate)?.onboardingConfigurationItem(item, index: index) + } +} diff --git a/Pods/paper-onboarding/Source/PaperOnboardingDataSource.swift b/Pods/paper-onboarding/Source/PaperOnboardingDataSource.swift new file mode 100644 index 0000000..f83865e --- /dev/null +++ b/Pods/paper-onboarding/Source/PaperOnboardingDataSource.swift @@ -0,0 +1,65 @@ +// +// PaperOnboardingDataSource.swift +// PaperOnboardingDemo +// +// Created by Abdurahim Jauzee on 05/06/2017. +// Copyright © 2017 Alex K. All rights reserved. +// + +import UIKit + +/** + * The PaperOnboardingDataSource protocol is adopted by an object that mediates the application’™s data model for a PaperOnboarding object. + The data source information it needs to construct and modify a PaperOnboarding. + */ +public protocol PaperOnboardingDataSource { + + /** + Asks the data source to return the number of items. + + - parameter index: An index of item in PaperOnboarding. + - returns: The number of items in PaperOnboarding. + */ + func onboardingItemsCount() -> Int + + /** + Asks the data source for configureation item. + + - parameter index: An index of item in PaperOnboarding. + - returns: configuration info for item + */ + func onboardingItem(at index: Int) -> OnboardingItemInfo + + /** + Asks the color for PageView item + + - parameter index: An index of item in PaperOnboarding. + - returns: color PageView Item + */ + func onboardingPageItemColor(at index: Int) -> UIColor + + /// Asks for the radius of the PageView item + /// + /// - Returns: radius of the PageView Item + func onboardinPageItemRadius() -> CGFloat + + /// Asks for the selected state radius of the PageView item + /// + /// - Returns: selected state radius of the PageView Item + func onboardingPageItemSelectedRadius() -> CGFloat +} + +public extension PaperOnboardingDataSource { + + func onboardingPageItemColor(at index: Int) -> UIColor { + return .white + } + + func onboardinPageItemRadius() -> CGFloat { + return 8 + } + + func onboardingPageItemSelectedRadius() -> CGFloat { + return 22 + } +} diff --git a/Pods/paper-onboarding/Source/PaperOnboardingDelegate.swift b/Pods/paper-onboarding/Source/PaperOnboardingDelegate.swift new file mode 100644 index 0000000..5e3058d --- /dev/null +++ b/Pods/paper-onboarding/Source/PaperOnboardingDelegate.swift @@ -0,0 +1,58 @@ +// +// PaperOnboardingDelegate.swift +// PaperOnboardingDemo +// +// Created by Abdurahim Jauzee on 05/06/2017. +// Copyright © 2017 Alex K. All rights reserved. +// + +import Foundation + +/** + * The delegate of a PaperOnboarding object must adopt the PaperOnboardingDelegate protocol. Optional methods of the + protocol allow the delegate to manage items, configure items, and perform other actions. + */ +public protocol PaperOnboardingDelegate { + + /** + Tells the delegate that the paperOnbording start scrolling. + + - parameter index: An curretn index item + */ + func onboardingWillTransitonToIndex(_ index: Int) + + /** + Tells the delegate that the paperOnbording will try to transition to a screen after the last + */ + func onboardingWillTransitonToLeaving() + + /** + Tells the delegate that the specified item is now selected + + - parameter index: An curretn index item + */ + func onboardingDidTransitonToIndex(_ index: Int) + + /** + Tells the delegate the PaperOnboarding is about to draw a item for a particular row. Use this method for configure items + + - parameter item: A OnboardingContentViewItem object that PaperOnboarding is going to use when drawing the row. + - parameter index: An curretn index item + */ + func onboardingConfigurationItem(_ item: OnboardingContentViewItem, index: Int) + + /** + Should `PaperOnboarding` react to taps on `PageControl` view. + If `true`, will scroll to tapped page. + */ + var enableTapsOnPageControl: Bool { get } +} + +// This extension will make the delegate method optional +public extension PaperOnboardingDelegate { + func onboardingWillTransitonToIndex(_: Int) {} + func onboardingDidTransitonToIndex(_: Int) {} + func onboardingWillTransitonToLeaving() {} + func onboardingConfigurationItem(_: OnboardingContentViewItem, index _: Int) {} + var enableTapsOnPageControl: Bool { return true } +} diff --git a/TodayExtension/Base.lproj/MainInterface.storyboard b/TodayExtension/Base.lproj/MainInterface.storyboard new file mode 100644 index 0000000..f285be7 --- /dev/null +++ b/TodayExtension/Base.lproj/MainInterface.storyboard @@ -0,0 +1,101 @@ + + + + + + + + + + + + + + + GothamPro + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TodayExtension/Info.plist b/TodayExtension/Info.plist new file mode 100644 index 0000000..5bbcc2c --- /dev/null +++ b/TodayExtension/Info.plist @@ -0,0 +1,31 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Calm Focus + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + 1.1.0 + CFBundleVersion + 1 + NSExtension + + NSExtensionMainStoryboard + MainInterface + NSExtensionPointIdentifier + com.apple.widget-extension + + + diff --git a/TodayExtension/TodayMeditations.swift b/TodayExtension/TodayMeditations.swift new file mode 100644 index 0000000..0ea290e --- /dev/null +++ b/TodayExtension/TodayMeditations.swift @@ -0,0 +1,67 @@ +import UIKit +import NotificationCenter + + +class TodayMeditations: UIViewController, UITableViewDataSource, UITableViewDelegate, NCWidgetProviding { + + @IBOutlet var tableView: UITableView! + + + + /* MARK: Initialising + /////////////////////////////////////////// */ + override func viewDidLoad() { + super.viewDidLoad() + extensionContext?.widgetLargestAvailableDisplayMode = .expanded + } + + + func widgetActiveDisplayModeDidChange(_ activeDisplayMode: NCWidgetDisplayMode, withMaximumSize maxSize: CGSize) { + if activeDisplayMode == .expanded { + preferredContentSize = CGSize(width: 0.0, height: 220.0) + } + else { + preferredContentSize = CGSize(width: 0.0, height: 180.0) + } + } + + + func widgetPerformUpdate(completionHandler: (@escaping (NCUpdateResult) -> Void)) { + completionHandler(NCUpdateResult.newData) + } + + + + /* MARK: Table Functionality + /////////////////////////////////////////// */ + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return Constants.Meditations.MEDITATIONS_ALL.count + } + + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TodayMeditationCell + + let meditation = Constants.Meditations.MEDITATIONS_ALL[indexPath.row] as [Any] + + cell.bgImageView?.image = UIImage(named: meditation[0] as! String) + + let goal = meditation[2] as? String + let title = meditation[3] as? String + cell.titleLabel?.text = title! + " (" + goal! + ")" + + return cell + } + + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + self.extensionContext?.open(URL(string: "Meditation" + String(indexPath.row) + "://")!, completionHandler: nil) + } +} + + + +class TodayMeditationCell : UITableViewCell { + @IBOutlet var bgImageView: UIImageView? + @IBOutlet var titleLabel: UILabel? +} diff --git a/WatchKit Extension/Resources/ExtensionDelegate.swift b/WatchKit Extension/Resources/ExtensionDelegate.swift new file mode 100644 index 0000000..57cd4ec --- /dev/null +++ b/WatchKit Extension/Resources/ExtensionDelegate.swift @@ -0,0 +1,43 @@ +import WatchKit + + +class ExtensionDelegate: NSObject, WKExtensionDelegate { + + func applicationDidFinishLaunching() { + // Perform any final initialization of your application. + } + + func applicationDidBecomeActive() { + // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. + } + + func applicationWillResignActive() { + // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. + // Use this method to pause ongoing tasks, disable timers, etc. + } + + func handle(_ backgroundTasks: Set) { + // Sent when the system needs to launch the application in the background to process tasks. Tasks arrive in a set, so loop through and process each one. + for task in backgroundTasks { + // Use a switch statement to check the task type + switch task { + case let backgroundTask as WKApplicationRefreshBackgroundTask: + // Be sure to complete the background task once you’re done. + backgroundTask.setTaskCompletedWithSnapshot(false) + case let snapshotTask as WKSnapshotRefreshBackgroundTask: + // Snapshot tasks have a unique completion call, make sure to set your expiration date + snapshotTask.setTaskCompleted(restoredDefaultState: true, estimatedSnapshotExpiration: Date.distantFuture, userInfo: nil) + case let connectivityTask as WKWatchConnectivityRefreshBackgroundTask: + // Be sure to complete the connectivity task once you’re done. + connectivityTask.setTaskCompletedWithSnapshot(false) + case let urlSessionTask as WKURLSessionRefreshBackgroundTask: + // Be sure to complete the URL session task once you’re done. + urlSessionTask.setTaskCompletedWithSnapshot(false) + default: + // make sure to complete unhandled task types + task.setTaskCompletedWithSnapshot(false) + } + } + } + +} diff --git a/WatchKit Extension/Resources/Info.plist b/WatchKit Extension/Resources/Info.plist new file mode 100644 index 0000000..5d71353 --- /dev/null +++ b/WatchKit Extension/Resources/Info.plist @@ -0,0 +1,36 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Calm Focus + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + 1.1.0 + CFBundleVersion + 1 + NSExtension + + NSExtensionAttributes + + WKAppBundleIdentifier + com.joeyt.meditation.watchkitapp + + NSExtensionPointIdentifier + com.apple.watchkit + + WKExtensionDelegateClassName + $(PRODUCT_MODULE_NAME).ExtensionDelegate + + diff --git a/WatchKit Extension/WatchMeditation.swift b/WatchKit Extension/WatchMeditation.swift new file mode 100644 index 0000000..f47cca5 --- /dev/null +++ b/WatchKit Extension/WatchMeditation.swift @@ -0,0 +1,91 @@ +import WatchKit +import SpriteKit +import AVFoundation + + +class WatchMeditation: WKInterfaceController { + + @IBOutlet var bgImageGroup: WKInterfaceGroup! + @IBOutlet var overlayGroup: WKInterfaceGroup! + @IBOutlet var titleLabel: WKInterfaceLabel! + @IBOutlet var progressGroup: WKInterfaceGroup! + + var player: AVAudioPlayer? + + + + override func awake(withContext context: Any?) { + super.awake(withContext: context) + + let meditation = context as! [Any] + + bgImageGroup.setBackgroundImage(UIImage(named: (meditation[0] as? String)!)) + overlayGroup.setBackgroundColor(UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 0.15)) + progressGroup.setBackgroundImageNamed("progress") + + initPhaseOne(meditation: meditation) + playSound(meditation: meditation) + } + + override func didDeactivate() { + player?.stop() + } + + + func playSound(meditation: [Any]) { + if let path = Bundle.main.path(forResource: (meditation[5] as! String), ofType: "mp3") { + let fileUrl = URL(fileURLWithPath: path) + + do { + try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback) + try AVAudioSession.sharedInstance().setActive(true) + player = try AVAudioPlayer(contentsOf: fileUrl) + player?.numberOfLoops = -1 + player?.play() + } + catch + { + print("Could not play audio file") + } + } + } + + func initPhaseOne(meditation: [Any]) { + let phaseLength = meditation[7] as! Double + titleLabel.setText((meditation[6] as? String)?.replacingOccurrences(of: "\n\n", with: "\n", options: .literal, range: nil)) + progressGroup.startAnimatingWithImages(in: NSRange(location: 0, length: 100), duration: phaseLength, repeatCount: 0) + + DispatchQueue.main.asyncAfter(deadline: .now() + (phaseLength * 0.9)) { // this is not called before images restart showing from 0 + self.progressGroup.stopAnimating() + self.initPhaseTwo(meditation: meditation) + } + } + + func initPhaseTwo(meditation: [Any]) { + let phaseLength = meditation[9] as! Double + titleLabel.setText((meditation[8] as? String)?.replacingOccurrences(of: "\n\n", with: "\n", options: .literal, range: nil)) + + DispatchQueue.main.asyncAfter(deadline: .now() + phaseLength) { + self.initPhaseThree(meditation: meditation) + } + } + + func initPhaseThree(meditation: [Any]) { + let phaseLength = meditation[11] as! Double + titleLabel.setText((meditation[10] as? String)?.replacingOccurrences(of: "\n\n", with: "\n", options: .literal, range: nil)) + progressGroup.startAnimatingWithImages(in: NSRange(location: 0, length: 100), duration: -phaseLength, repeatCount: 1) + + DispatchQueue.main.asyncAfter(deadline: .now() + (phaseLength * 0.9)) { + self.initPhaseFour(meditation: meditation) + } + } + + func initPhaseFour(meditation: [Any]) { + let phaseLength = meditation[9] as! Double + titleLabel.setText((meditation[8] as? String)?.replacingOccurrences(of: "\n\n", with: "\n", options: .literal, range: nil)) + + DispatchQueue.main.asyncAfter(deadline: .now() + phaseLength) { + self.initPhaseOne(meditation: meditation) + } + } +} diff --git a/WatchKit Extension/WatchMeditations.swift b/WatchKit Extension/WatchMeditations.swift new file mode 100644 index 0000000..1adb6ee --- /dev/null +++ b/WatchKit Extension/WatchMeditations.swift @@ -0,0 +1,55 @@ +import WatchKit +import Foundation +import UIKit + + +class WatchMeditations: WKInterfaceController { + + @IBOutlet var table: WKInterfaceTable! + + + + /* MARK: Initialising + /////////////////////////////////////////// */ + override func awake(withContext context: Any?) { + super.awake(withContext: context) + + table.setNumberOfRows(Constants.Meditations.MEDITATIONS_ALL.count, withRowType: "MeditationRow") + + for index in 0.. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+ +
+
+
diff --git a/WatchKit/Info.plist b/WatchKit/Info.plist new file mode 100644 index 0000000..e7db254 --- /dev/null +++ b/WatchKit/Info.plist @@ -0,0 +1,33 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Calm Focus + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.1.0 + CFBundleVersion + 1 + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + + WKCompanionAppBundleIdentifier + com.joeyt.meditation + WKWatchKitApp + + + diff --git a/fastlane/Appfile b/fastlane/Appfile new file mode 100644 index 0000000..ab5c1e5 --- /dev/null +++ b/fastlane/Appfile @@ -0,0 +1,8 @@ +app_identifier("com.joeyt.meditation") # The bundle identifier of your app +apple_id("joeytawadrous@gmail.com") # Your Apple email address + +itc_team_id("643609") # App Store Connect Team ID +team_id("P5Z7RL76WF") # Developer Portal Team ID + +# For more information about the Appfile, see: +# https://docs.fastlane.tools/advanced/#appfile diff --git a/fastlane/Deliverfile b/fastlane/Deliverfile new file mode 100644 index 0000000..74739f7 --- /dev/null +++ b/fastlane/Deliverfile @@ -0,0 +1,3 @@ +# The Deliverfile allows you to store various App Store Connect metadata +# For more information, check out the docs +# https://docs.fastlane.tools/actions/deliver/ diff --git a/fastlane/Fastfile b/fastlane/Fastfile new file mode 100644 index 0000000..6c03944 --- /dev/null +++ b/fastlane/Fastfile @@ -0,0 +1,30 @@ +# This file contains the fastlane.tools configuration +# You can find the documentation at https://docs.fastlane.tools +# +# For a list of all available actions, check out +# +# https://docs.fastlane.tools/actions +# +# For a list of all available plugins, check out +# +# https://docs.fastlane.tools/plugins/available-plugins +# + +# Uncomment the line if you want fastlane to automatically update itself +# update_fastlane + +default_platform(:ios) + +build_app(workspace: "Meditation.xcworkspace", scheme: "Meditation") + +deliver( + submit_for_review: true, + automatic_release: true, + force: true, # no screenshots generated html status page + metadata_path: "./metadata", + skip_screenshots: true, + skip_metadata: false, + app_version: "1.0.7", + run_precheck_before_submit: false, + reject_if_possible: true +) diff --git a/fastlane/Snapfile b/fastlane/Snapfile new file mode 100644 index 0000000..289d892 --- /dev/null +++ b/fastlane/Snapfile @@ -0,0 +1,29 @@ +# Uncomment the lines below you want to change by removing the # in the beginning + +# A list of devices you want to take the screenshots from +devices([ + "iPhone 8 Plus", + "iPad Pro (12.9-inch)" +]) + +# languages([ +# "en-US", +# "de-DE", +# "it-IT", +# ["pt", "pt_BR"] # Portuguese with Brazilian locale +# ]) + +# The name of the scheme which contains the UI Tests +# scheme "SchemeName" + +# Where should the resulting screenshots be stored? +# output_directory "./screenshots" + +# remove the '#' to clear all previously generated screenshots before creating new ones +clear_previous_screenshots false + +# Arguments to pass to the app on launch. See https://docs.fastlane.tools/actions/snapshot/#launch-arguments +# launch_arguments(["-favColor red"]) + +# For more information about all available options run +# fastlane action snapshot diff --git a/fastlane/metadata/app_icon.jpg b/fastlane/metadata/app_icon.jpg new file mode 100644 index 0000000..e1b01a4 Binary files /dev/null and b/fastlane/metadata/app_icon.jpg differ diff --git a/fastlane/metadata/copyright.txt b/fastlane/metadata/copyright.txt new file mode 100644 index 0000000..f910c57 --- /dev/null +++ b/fastlane/metadata/copyright.txt @@ -0,0 +1 @@ +Joey Tawadrous diff --git a/fastlane/metadata/en-GB/description.txt b/fastlane/metadata/en-GB/description.txt new file mode 100644 index 0000000..1a60fb2 --- /dev/null +++ b/fastlane/metadata/en-GB/description.txt @@ -0,0 +1,20 @@ +Take a break from all your hard work. + +Relax and enjoy the sounds of nature while focusing on your breathing. + +Earn Achievements: Unlock badges and level up as you enjoy your relaxing meditation sessions. Gamify the achieving of your balance through meditation. + +As always, we absolutely love your ideas and suggestions. So please, do not hesitate to contact us at any time through our apps or website. + +Features Today Widget & Apple Watch App! + + +More on Meditation Fox Premium: + +Upgrade to Meditation Fox Premium to get access to all content, meditations, themes & app features (+ a continuous stream of new content in the future)! Subscribe for $1.99 / $4.99 month, you will be charged in your local currency at the prevailing exchange rate as defined by iTunes. + +——————————————————————— + +If you choose to purchase Meditation Fox Premium, payment will be charged to your iTunes account at confirmation of purchase. You can manage auto-renew of your subscription in your iTunes account settings. Your account will be charged for renewal within 24-hours prior to the end of the current period at the price of the original subscription. You may turn off the auto-renewal of your subscription via your Account Settings. Full terms of use can be found at https://www.getLearnable.com/privacy&terms.php. No cancellation of the current subscription is allowed during the active subscription period. + +If you have any questions, don't hesitate to get in touch with us via email or Twitter - both links available at https://www.getLearnable.com diff --git a/fastlane/metadata/en-GB/keywords.txt b/fastlane/metadata/en-GB/keywords.txt new file mode 100644 index 0000000..12f6f20 --- /dev/null +++ b/fastlane/metadata/en-GB/keywords.txt @@ -0,0 +1 @@ +app for kids,gamify,calm,coach,focus,help,guided,meditate,meditation,mindfulness,relax,stress,zen diff --git a/fastlane/metadata/en-GB/marketing_url.txt b/fastlane/metadata/en-GB/marketing_url.txt new file mode 100644 index 0000000..2d48d07 --- /dev/null +++ b/fastlane/metadata/en-GB/marketing_url.txt @@ -0,0 +1 @@ +https://www.getlearnable.com diff --git a/fastlane/metadata/en-GB/name.txt b/fastlane/metadata/en-GB/name.txt new file mode 100644 index 0000000..463d29f --- /dev/null +++ b/fastlane/metadata/en-GB/name.txt @@ -0,0 +1 @@ +Meditation Fox: Daily Focus diff --git a/fastlane/metadata/en-GB/privacy_url.txt b/fastlane/metadata/en-GB/privacy_url.txt new file mode 100644 index 0000000..c1ebfa1 --- /dev/null +++ b/fastlane/metadata/en-GB/privacy_url.txt @@ -0,0 +1 @@ +https://www.getlearnable.com/privacy&terms.php diff --git a/fastlane/metadata/en-GB/promotional_text.txt b/fastlane/metadata/en-GB/promotional_text.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/fastlane/metadata/en-GB/promotional_text.txt @@ -0,0 +1 @@ + diff --git a/fastlane/metadata/en-GB/release_notes.txt b/fastlane/metadata/en-GB/release_notes.txt new file mode 100644 index 0000000..a1863e0 --- /dev/null +++ b/fastlane/metadata/en-GB/release_notes.txt @@ -0,0 +1,5 @@ +- Settings menu design. +- Design improvements. +- Data storage improvements. + +Your feedback means the world to me, so please do not hesitate to let me know what you think. I will read and answer every single review and do my best to ensure you have a great experience while using the app! :) diff --git a/fastlane/metadata/en-GB/subtitle.txt b/fastlane/metadata/en-GB/subtitle.txt new file mode 100644 index 0000000..6bad37e --- /dev/null +++ b/fastlane/metadata/en-GB/subtitle.txt @@ -0,0 +1 @@ +Gamify your focused meditation diff --git a/fastlane/metadata/en-GB/support_url.txt b/fastlane/metadata/en-GB/support_url.txt new file mode 100644 index 0000000..2d48d07 --- /dev/null +++ b/fastlane/metadata/en-GB/support_url.txt @@ -0,0 +1 @@ +https://www.getlearnable.com diff --git a/fastlane/metadata/primary_category.txt b/fastlane/metadata/primary_category.txt new file mode 100644 index 0000000..5304273 --- /dev/null +++ b/fastlane/metadata/primary_category.txt @@ -0,0 +1 @@ +MZGenre.Lifestyle diff --git a/fastlane/metadata/primary_first_sub_category.txt b/fastlane/metadata/primary_first_sub_category.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/fastlane/metadata/primary_first_sub_category.txt @@ -0,0 +1 @@ + diff --git a/fastlane/metadata/primary_second_sub_category.txt b/fastlane/metadata/primary_second_sub_category.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/fastlane/metadata/primary_second_sub_category.txt @@ -0,0 +1 @@ + diff --git a/fastlane/metadata/review_information/demo_password.txt b/fastlane/metadata/review_information/demo_password.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/fastlane/metadata/review_information/demo_password.txt @@ -0,0 +1 @@ + diff --git a/fastlane/metadata/review_information/demo_user.txt b/fastlane/metadata/review_information/demo_user.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/fastlane/metadata/review_information/demo_user.txt @@ -0,0 +1 @@ + diff --git a/fastlane/metadata/review_information/email_address.txt b/fastlane/metadata/review_information/email_address.txt new file mode 100644 index 0000000..b98fb2f --- /dev/null +++ b/fastlane/metadata/review_information/email_address.txt @@ -0,0 +1 @@ +joeytawadrous@gmail.com diff --git a/fastlane/metadata/review_information/first_name.txt b/fastlane/metadata/review_information/first_name.txt new file mode 100644 index 0000000..4ad34c7 --- /dev/null +++ b/fastlane/metadata/review_information/first_name.txt @@ -0,0 +1 @@ +Joey diff --git a/fastlane/metadata/review_information/last_name.txt b/fastlane/metadata/review_information/last_name.txt new file mode 100644 index 0000000..d4a6538 --- /dev/null +++ b/fastlane/metadata/review_information/last_name.txt @@ -0,0 +1 @@ +Tawadrous diff --git a/fastlane/metadata/review_information/notes.txt b/fastlane/metadata/review_information/notes.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/fastlane/metadata/review_information/notes.txt @@ -0,0 +1 @@ + diff --git a/fastlane/metadata/review_information/phone_number.txt b/fastlane/metadata/review_information/phone_number.txt new file mode 100644 index 0000000..906508e --- /dev/null +++ b/fastlane/metadata/review_information/phone_number.txt @@ -0,0 +1 @@ ++353863325570 diff --git a/fastlane/metadata/secondary_category.txt b/fastlane/metadata/secondary_category.txt new file mode 100644 index 0000000..10afdc0 --- /dev/null +++ b/fastlane/metadata/secondary_category.txt @@ -0,0 +1 @@ +MZGenre.Healthcare_Fitness diff --git a/fastlane/metadata/secondary_first_sub_category.txt b/fastlane/metadata/secondary_first_sub_category.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/fastlane/metadata/secondary_first_sub_category.txt @@ -0,0 +1 @@ + diff --git a/fastlane/metadata/secondary_second_sub_category.txt b/fastlane/metadata/secondary_second_sub_category.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/fastlane/metadata/secondary_second_sub_category.txt @@ -0,0 +1 @@ + diff --git a/fastlane/metadata/trade_representative_contact_information/address_line1.txt b/fastlane/metadata/trade_representative_contact_information/address_line1.txt new file mode 100644 index 0000000..90d38e7 --- /dev/null +++ b/fastlane/metadata/trade_representative_contact_information/address_line1.txt @@ -0,0 +1 @@ +Apt 74, The Bramley, Orchard Gardens diff --git a/fastlane/metadata/trade_representative_contact_information/address_line2.txt b/fastlane/metadata/trade_representative_contact_information/address_line2.txt new file mode 100644 index 0000000..e2b73ba --- /dev/null +++ b/fastlane/metadata/trade_representative_contact_information/address_line2.txt @@ -0,0 +1 @@ +Dennehys Cross diff --git a/fastlane/metadata/trade_representative_contact_information/address_line3.txt b/fastlane/metadata/trade_representative_contact_information/address_line3.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/fastlane/metadata/trade_representative_contact_information/address_line3.txt @@ -0,0 +1 @@ + diff --git a/fastlane/metadata/trade_representative_contact_information/city_name.txt b/fastlane/metadata/trade_representative_contact_information/city_name.txt new file mode 100644 index 0000000..3cc88cc --- /dev/null +++ b/fastlane/metadata/trade_representative_contact_information/city_name.txt @@ -0,0 +1 @@ +Cork diff --git a/fastlane/metadata/trade_representative_contact_information/country.txt b/fastlane/metadata/trade_representative_contact_information/country.txt new file mode 100644 index 0000000..c8cfae1 --- /dev/null +++ b/fastlane/metadata/trade_representative_contact_information/country.txt @@ -0,0 +1 @@ +Ireland diff --git a/fastlane/metadata/trade_representative_contact_information/email_address.txt b/fastlane/metadata/trade_representative_contact_information/email_address.txt new file mode 100644 index 0000000..b98fb2f --- /dev/null +++ b/fastlane/metadata/trade_representative_contact_information/email_address.txt @@ -0,0 +1 @@ +joeytawadrous@gmail.com diff --git a/fastlane/metadata/trade_representative_contact_information/first_name.txt b/fastlane/metadata/trade_representative_contact_information/first_name.txt new file mode 100644 index 0000000..4ad34c7 --- /dev/null +++ b/fastlane/metadata/trade_representative_contact_information/first_name.txt @@ -0,0 +1 @@ +Joey diff --git a/fastlane/metadata/trade_representative_contact_information/is_displayed_on_app_store.txt b/fastlane/metadata/trade_representative_contact_information/is_displayed_on_app_store.txt new file mode 100644 index 0000000..c508d53 --- /dev/null +++ b/fastlane/metadata/trade_representative_contact_information/is_displayed_on_app_store.txt @@ -0,0 +1 @@ +false diff --git a/fastlane/metadata/trade_representative_contact_information/last_name.txt b/fastlane/metadata/trade_representative_contact_information/last_name.txt new file mode 100644 index 0000000..d4a6538 --- /dev/null +++ b/fastlane/metadata/trade_representative_contact_information/last_name.txt @@ -0,0 +1 @@ +Tawadrous diff --git a/fastlane/metadata/trade_representative_contact_information/phone_number.txt b/fastlane/metadata/trade_representative_contact_information/phone_number.txt new file mode 100644 index 0000000..906508e --- /dev/null +++ b/fastlane/metadata/trade_representative_contact_information/phone_number.txt @@ -0,0 +1 @@ ++353863325570 diff --git a/fastlane/metadata/trade_representative_contact_information/postal_code.txt b/fastlane/metadata/trade_representative_contact_information/postal_code.txt new file mode 100644 index 0000000..8f087a3 --- /dev/null +++ b/fastlane/metadata/trade_representative_contact_information/postal_code.txt @@ -0,0 +1 @@ +000 diff --git a/fastlane/metadata/trade_representative_contact_information/state.txt b/fastlane/metadata/trade_representative_contact_information/state.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/fastlane/metadata/trade_representative_contact_information/state.txt @@ -0,0 +1 @@ + diff --git a/fastlane/metadata/trade_representative_contact_information/trade_name.txt b/fastlane/metadata/trade_representative_contact_information/trade_name.txt new file mode 100644 index 0000000..f910c57 --- /dev/null +++ b/fastlane/metadata/trade_representative_contact_information/trade_name.txt @@ -0,0 +1 @@ +Joey Tawadrous diff --git a/fastlane/metadata/watch_icon.jpg b/fastlane/metadata/watch_icon.jpg new file mode 100644 index 0000000..e1b01a4 Binary files /dev/null and b/fastlane/metadata/watch_icon.jpg differ diff --git a/fastlane/screenshots/README.txt b/fastlane/screenshots/README.txt new file mode 100644 index 0000000..8b015ec --- /dev/null +++ b/fastlane/screenshots/README.txt @@ -0,0 +1,7 @@ +Put all screenshots you want to use inside the folder of its language (e.g. en-US). +The device type will automatically be recognized using the image resolution. Apple TV screenshots +should be stored in a subdirectory named appleTV with language folders inside of it. iMessage +screenshots, like Apple TV screenshots, should also be stored in a subdirectory named iMessage +with language folders inside of it. + +The screenshots can be named whatever you want, but keep in mind they are sorted alphabetically. diff --git a/fastlane/screenshots/en-GB/1_ipadPro_1.iPad Pro (12.9-inch)-Meditation_framed.png b/fastlane/screenshots/en-GB/1_ipadPro_1.iPad Pro (12.9-inch)-Meditation_framed.png new file mode 100644 index 0000000..e478268 Binary files /dev/null and b/fastlane/screenshots/en-GB/1_ipadPro_1.iPad Pro (12.9-inch)-Meditation_framed.png differ diff --git a/fastlane/screenshots/en-GB/1_iphone6Plus_1.iPhone 8 Plus-Meditation_framed.png b/fastlane/screenshots/en-GB/1_iphone6Plus_1.iPhone 8 Plus-Meditation_framed.png new file mode 100644 index 0000000..989eb51 Binary files /dev/null and b/fastlane/screenshots/en-GB/1_iphone6Plus_1.iPhone 8 Plus-Meditation_framed.png differ diff --git a/fastlane/screenshots/en-GB/1_watch_1.WatchMeditations.png b/fastlane/screenshots/en-GB/1_watch_1.WatchMeditations.png new file mode 100644 index 0000000..7501b5c Binary files /dev/null and b/fastlane/screenshots/en-GB/1_watch_1.WatchMeditations.png differ diff --git a/fastlane/screenshots/en-GB/2_ipadPro_2.iPad Pro (12.9-inch)-Achievements_framed.png b/fastlane/screenshots/en-GB/2_ipadPro_2.iPad Pro (12.9-inch)-Achievements_framed.png new file mode 100644 index 0000000..6259029 Binary files /dev/null and b/fastlane/screenshots/en-GB/2_ipadPro_2.iPad Pro (12.9-inch)-Achievements_framed.png differ diff --git a/fastlane/screenshots/en-GB/2_iphone6Plus_2.iPhone 8 Plus-Achievements_framed.png b/fastlane/screenshots/en-GB/2_iphone6Plus_2.iPhone 8 Plus-Achievements_framed.png new file mode 100644 index 0000000..6b2c9dc Binary files /dev/null and b/fastlane/screenshots/en-GB/2_iphone6Plus_2.iPhone 8 Plus-Achievements_framed.png differ diff --git a/fastlane/screenshots/en-GB/2_watch_2.WatchMeditation.png b/fastlane/screenshots/en-GB/2_watch_2.WatchMeditation.png new file mode 100644 index 0000000..1480809 Binary files /dev/null and b/fastlane/screenshots/en-GB/2_watch_2.WatchMeditation.png differ diff --git a/fastlane/screenshots/en-GB/3_ipadPro_3.iPad Pro (12.9-inch)-Run_framed.png b/fastlane/screenshots/en-GB/3_ipadPro_3.iPad Pro (12.9-inch)-Run_framed.png new file mode 100644 index 0000000..ad40149 Binary files /dev/null and b/fastlane/screenshots/en-GB/3_ipadPro_3.iPad Pro (12.9-inch)-Run_framed.png differ diff --git a/fastlane/screenshots/en-GB/3_iphone6Plus_3.iPhone 8 Plus-Run_framed.png b/fastlane/screenshots/en-GB/3_iphone6Plus_3.iPhone 8 Plus-Run_framed.png new file mode 100644 index 0000000..0568c54 Binary files /dev/null and b/fastlane/screenshots/en-GB/3_iphone6Plus_3.iPhone 8 Plus-Run_framed.png differ diff --git a/fastlane/screenshots/en-GB/4_ipadPro_4.iPad Pro (12.9-inch)-TodayView_framed.png b/fastlane/screenshots/en-GB/4_ipadPro_4.iPad Pro (12.9-inch)-TodayView_framed.png new file mode 100644 index 0000000..78ddad6 Binary files /dev/null and b/fastlane/screenshots/en-GB/4_ipadPro_4.iPad Pro (12.9-inch)-TodayView_framed.png differ diff --git a/fastlane/screenshots/en-GB/4_iphone6Plus_4.iPhone 8 Plus-TodayView_framed.png b/fastlane/screenshots/en-GB/4_iphone6Plus_4.iPhone 8 Plus-TodayView_framed.png new file mode 100644 index 0000000..ccae372 Binary files /dev/null and b/fastlane/screenshots/en-GB/4_iphone6Plus_4.iPhone 8 Plus-TodayView_framed.png differ diff --git a/fastlane/screenshots/en-US/Framefile.json b/fastlane/screenshots/en-US/Framefile.json new file mode 100644 index 0000000..da79f0c --- /dev/null +++ b/fastlane/screenshots/en-US/Framefile.json @@ -0,0 +1,41 @@ +{ + "default": { + "title": { + "color": "#fff", + "font": "assets/GothamProMedium.ttf" + }, + "padding": 40, + "font_scale_factor": 0.2, + "show_complete_frame": true + }, + + + "data": [{ + "filter": "Meditation", + "background": "assets/orange.jpeg", + "title": { + "text": "Take a well deserved break \nfrom all your hard work" + } + }, + { + "filter": "Achievements", + "background": "assets/dark_blue.jpeg", + "title": { + "text": "Meditate and unlock badges \nwhile you level up" + } + }, + { + "filter": "Run", + "background": "assets/blue.jpeg", + "title": { + "text": "Enjoy the sounds of nature while \nfocusing on your breathing" + } + }, + { + "filter": "TodayView", + "background": "assets/purple.jpeg", + "title": { + "text": "View all your meditations in a \nhandy today view widget" + } + }] +} \ No newline at end of file diff --git a/fastlane/screenshots/en-US/assets/GothamProMedium.ttf b/fastlane/screenshots/en-US/assets/GothamProMedium.ttf new file mode 100755 index 0000000..a24d08b Binary files /dev/null and b/fastlane/screenshots/en-US/assets/GothamProMedium.ttf differ diff --git a/fastlane/screenshots/en-US/assets/blue.jpeg b/fastlane/screenshots/en-US/assets/blue.jpeg new file mode 100644 index 0000000..555614e Binary files /dev/null and b/fastlane/screenshots/en-US/assets/blue.jpeg differ diff --git a/fastlane/screenshots/en-US/assets/dark_blue.jpeg b/fastlane/screenshots/en-US/assets/dark_blue.jpeg new file mode 100644 index 0000000..081376c Binary files /dev/null and b/fastlane/screenshots/en-US/assets/dark_blue.jpeg differ diff --git a/fastlane/screenshots/en-US/assets/orange.jpeg b/fastlane/screenshots/en-US/assets/orange.jpeg new file mode 100644 index 0000000..6ace062 Binary files /dev/null and b/fastlane/screenshots/en-US/assets/orange.jpeg differ diff --git a/fastlane/screenshots/en-US/assets/purple.jpeg b/fastlane/screenshots/en-US/assets/purple.jpeg new file mode 100644 index 0000000..808f152 Binary files /dev/null and b/fastlane/screenshots/en-US/assets/purple.jpeg differ diff --git a/fastlane/screenshots/en-US/assets/red.jpeg b/fastlane/screenshots/en-US/assets/red.jpeg new file mode 100644 index 0000000..032f6cf Binary files /dev/null and b/fastlane/screenshots/en-US/assets/red.jpeg differ diff --git a/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-Achievements.png b/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-Achievements.png new file mode 100644 index 0000000..5b4e877 Binary files /dev/null and b/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-Achievements.png differ diff --git a/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-Achievements_framed.png b/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-Achievements_framed.png new file mode 100644 index 0000000..c69f03c Binary files /dev/null and b/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-Achievements_framed.png differ diff --git a/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-Meditation.png b/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-Meditation.png new file mode 100644 index 0000000..c70c969 Binary files /dev/null and b/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-Meditation.png differ diff --git a/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-Meditation_framed.png b/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-Meditation_framed.png new file mode 100644 index 0000000..ffec4e5 Binary files /dev/null and b/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-Meditation_framed.png differ diff --git a/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-Run.png b/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-Run.png new file mode 100644 index 0000000..5c0f339 Binary files /dev/null and b/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-Run.png differ diff --git a/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-Run_framed.png b/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-Run_framed.png new file mode 100644 index 0000000..2284d7a Binary files /dev/null and b/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-Run_framed.png differ diff --git a/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-TodayView.png b/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-TodayView.png new file mode 100644 index 0000000..8e15c6a Binary files /dev/null and b/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-TodayView.png differ diff --git a/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-TodayView_framed.png b/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-TodayView_framed.png new file mode 100644 index 0000000..78783df Binary files /dev/null and b/fastlane/screenshots/en-US/iPad Pro (12.9-inch)-TodayView_framed.png differ diff --git a/fastlane/screenshots/en-US/iPhone 8 Plus-Achievements.png b/fastlane/screenshots/en-US/iPhone 8 Plus-Achievements.png new file mode 100644 index 0000000..6be2942 Binary files /dev/null and b/fastlane/screenshots/en-US/iPhone 8 Plus-Achievements.png differ diff --git a/fastlane/screenshots/en-US/iPhone 8 Plus-Achievements_framed.png b/fastlane/screenshots/en-US/iPhone 8 Plus-Achievements_framed.png new file mode 100644 index 0000000..99d88b3 Binary files /dev/null and b/fastlane/screenshots/en-US/iPhone 8 Plus-Achievements_framed.png differ diff --git a/fastlane/screenshots/en-US/iPhone 8 Plus-Meditation.png b/fastlane/screenshots/en-US/iPhone 8 Plus-Meditation.png new file mode 100644 index 0000000..43eea1a Binary files /dev/null and b/fastlane/screenshots/en-US/iPhone 8 Plus-Meditation.png differ diff --git a/fastlane/screenshots/en-US/iPhone 8 Plus-Meditation_framed.png b/fastlane/screenshots/en-US/iPhone 8 Plus-Meditation_framed.png new file mode 100644 index 0000000..8b28586 Binary files /dev/null and b/fastlane/screenshots/en-US/iPhone 8 Plus-Meditation_framed.png differ diff --git a/fastlane/screenshots/en-US/iPhone 8 Plus-Run.png b/fastlane/screenshots/en-US/iPhone 8 Plus-Run.png new file mode 100644 index 0000000..fd1f481 Binary files /dev/null and b/fastlane/screenshots/en-US/iPhone 8 Plus-Run.png differ diff --git a/fastlane/screenshots/en-US/iPhone 8 Plus-Run_framed.png b/fastlane/screenshots/en-US/iPhone 8 Plus-Run_framed.png new file mode 100644 index 0000000..450eb26 Binary files /dev/null and b/fastlane/screenshots/en-US/iPhone 8 Plus-Run_framed.png differ diff --git a/fastlane/screenshots/en-US/iPhone 8 Plus-TodayView.png b/fastlane/screenshots/en-US/iPhone 8 Plus-TodayView.png new file mode 100644 index 0000000..810e050 Binary files /dev/null and b/fastlane/screenshots/en-US/iPhone 8 Plus-TodayView.png differ diff --git a/fastlane/screenshots/en-US/iPhone 8 Plus-TodayView_framed.png b/fastlane/screenshots/en-US/iPhone 8 Plus-TodayView_framed.png new file mode 100644 index 0000000..c2f9c51 Binary files /dev/null and b/fastlane/screenshots/en-US/iPhone 8 Plus-TodayView_framed.png differ diff --git a/screenshots/en-US/Framefile.json b/screenshots/en-US/Framefile.json new file mode 100644 index 0000000..da79f0c --- /dev/null +++ b/screenshots/en-US/Framefile.json @@ -0,0 +1,41 @@ +{ + "default": { + "title": { + "color": "#fff", + "font": "assets/GothamProMedium.ttf" + }, + "padding": 40, + "font_scale_factor": 0.2, + "show_complete_frame": true + }, + + + "data": [{ + "filter": "Meditation", + "background": "assets/orange.jpeg", + "title": { + "text": "Take a well deserved break \nfrom all your hard work" + } + }, + { + "filter": "Achievements", + "background": "assets/dark_blue.jpeg", + "title": { + "text": "Meditate and unlock badges \nwhile you level up" + } + }, + { + "filter": "Run", + "background": "assets/blue.jpeg", + "title": { + "text": "Enjoy the sounds of nature while \nfocusing on your breathing" + } + }, + { + "filter": "TodayView", + "background": "assets/purple.jpeg", + "title": { + "text": "View all your meditations in a \nhandy today view widget" + } + }] +} \ No newline at end of file diff --git a/screenshots/en-US/assets/GothamProMedium.ttf b/screenshots/en-US/assets/GothamProMedium.ttf new file mode 100755 index 0000000..a24d08b Binary files /dev/null and b/screenshots/en-US/assets/GothamProMedium.ttf differ diff --git a/screenshots/en-US/assets/blue.jpeg b/screenshots/en-US/assets/blue.jpeg new file mode 100644 index 0000000..555614e Binary files /dev/null and b/screenshots/en-US/assets/blue.jpeg differ diff --git a/screenshots/en-US/assets/dark_blue.jpeg b/screenshots/en-US/assets/dark_blue.jpeg new file mode 100644 index 0000000..081376c Binary files /dev/null and b/screenshots/en-US/assets/dark_blue.jpeg differ diff --git a/screenshots/en-US/assets/orange.jpeg b/screenshots/en-US/assets/orange.jpeg new file mode 100644 index 0000000..6ace062 Binary files /dev/null and b/screenshots/en-US/assets/orange.jpeg differ diff --git a/screenshots/en-US/assets/purple.jpeg b/screenshots/en-US/assets/purple.jpeg new file mode 100644 index 0000000..808f152 Binary files /dev/null and b/screenshots/en-US/assets/purple.jpeg differ diff --git a/screenshots/en-US/assets/red.jpeg b/screenshots/en-US/assets/red.jpeg new file mode 100644 index 0000000..032f6cf Binary files /dev/null and b/screenshots/en-US/assets/red.jpeg differ diff --git a/screenshots/en-US/iPad Pro (12.9-inch)-Achievements.png b/screenshots/en-US/iPad Pro (12.9-inch)-Achievements.png new file mode 100644 index 0000000..5b4e877 Binary files /dev/null and b/screenshots/en-US/iPad Pro (12.9-inch)-Achievements.png differ diff --git a/screenshots/en-US/iPad Pro (12.9-inch)-Achievements_framed.png b/screenshots/en-US/iPad Pro (12.9-inch)-Achievements_framed.png new file mode 100644 index 0000000..c69f03c Binary files /dev/null and b/screenshots/en-US/iPad Pro (12.9-inch)-Achievements_framed.png differ diff --git a/screenshots/en-US/iPad Pro (12.9-inch)-Meditation.png b/screenshots/en-US/iPad Pro (12.9-inch)-Meditation.png new file mode 100644 index 0000000..c70c969 Binary files /dev/null and b/screenshots/en-US/iPad Pro (12.9-inch)-Meditation.png differ diff --git a/screenshots/en-US/iPad Pro (12.9-inch)-Meditation_framed.png b/screenshots/en-US/iPad Pro (12.9-inch)-Meditation_framed.png new file mode 100644 index 0000000..ffec4e5 Binary files /dev/null and b/screenshots/en-US/iPad Pro (12.9-inch)-Meditation_framed.png differ diff --git a/screenshots/en-US/iPad Pro (12.9-inch)-Run.png b/screenshots/en-US/iPad Pro (12.9-inch)-Run.png new file mode 100644 index 0000000..5c0f339 Binary files /dev/null and b/screenshots/en-US/iPad Pro (12.9-inch)-Run.png differ diff --git a/screenshots/en-US/iPad Pro (12.9-inch)-Run_framed.png b/screenshots/en-US/iPad Pro (12.9-inch)-Run_framed.png new file mode 100644 index 0000000..2284d7a Binary files /dev/null and b/screenshots/en-US/iPad Pro (12.9-inch)-Run_framed.png differ diff --git a/screenshots/en-US/iPad Pro (12.9-inch)-TodayView.png b/screenshots/en-US/iPad Pro (12.9-inch)-TodayView.png new file mode 100644 index 0000000..8e15c6a Binary files /dev/null and b/screenshots/en-US/iPad Pro (12.9-inch)-TodayView.png differ diff --git a/screenshots/en-US/iPad Pro (12.9-inch)-TodayView_framed.png b/screenshots/en-US/iPad Pro (12.9-inch)-TodayView_framed.png new file mode 100644 index 0000000..78783df Binary files /dev/null and b/screenshots/en-US/iPad Pro (12.9-inch)-TodayView_framed.png differ diff --git a/screenshots/en-US/iPhone 8 Plus-Achievements.png b/screenshots/en-US/iPhone 8 Plus-Achievements.png new file mode 100644 index 0000000..6be2942 Binary files /dev/null and b/screenshots/en-US/iPhone 8 Plus-Achievements.png differ diff --git a/screenshots/en-US/iPhone 8 Plus-Achievements_framed.png b/screenshots/en-US/iPhone 8 Plus-Achievements_framed.png new file mode 100644 index 0000000..99d88b3 Binary files /dev/null and b/screenshots/en-US/iPhone 8 Plus-Achievements_framed.png differ diff --git a/screenshots/en-US/iPhone 8 Plus-Meditation.png b/screenshots/en-US/iPhone 8 Plus-Meditation.png new file mode 100644 index 0000000..43eea1a Binary files /dev/null and b/screenshots/en-US/iPhone 8 Plus-Meditation.png differ diff --git a/screenshots/en-US/iPhone 8 Plus-Meditation_framed.png b/screenshots/en-US/iPhone 8 Plus-Meditation_framed.png new file mode 100644 index 0000000..8b28586 Binary files /dev/null and b/screenshots/en-US/iPhone 8 Plus-Meditation_framed.png differ diff --git a/screenshots/en-US/iPhone 8 Plus-Run.png b/screenshots/en-US/iPhone 8 Plus-Run.png new file mode 100644 index 0000000..fd1f481 Binary files /dev/null and b/screenshots/en-US/iPhone 8 Plus-Run.png differ diff --git a/screenshots/en-US/iPhone 8 Plus-Run_framed.png b/screenshots/en-US/iPhone 8 Plus-Run_framed.png new file mode 100644 index 0000000..450eb26 Binary files /dev/null and b/screenshots/en-US/iPhone 8 Plus-Run_framed.png differ diff --git a/screenshots/en-US/iPhone 8 Plus-TodayView.png b/screenshots/en-US/iPhone 8 Plus-TodayView.png new file mode 100644 index 0000000..810e050 Binary files /dev/null and b/screenshots/en-US/iPhone 8 Plus-TodayView.png differ diff --git a/screenshots/en-US/iPhone 8 Plus-TodayView_framed.png b/screenshots/en-US/iPhone 8 Plus-TodayView_framed.png new file mode 100644 index 0000000..c2f9c51 Binary files /dev/null and b/screenshots/en-US/iPhone 8 Plus-TodayView_framed.png differ