├── .DS_Store ├── .gitmodules ├── LICENSE.md ├── Left.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── Left.xcscmblueprint │ └── xcuserdata │ │ └── ale_patron.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── ale_patron.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ ├── Left.xcscheme │ └── xcschememanagement.plist ├── Left.xcworkspace ├── contents.xcworkspacedata └── xcuserdata │ └── ale_patron.xcuserdatad │ ├── UserInterfaceState.xcuserstate │ └── xcdebugger │ └── Breakpoints_v2.xcbkptlist ├── Left ├── .DS_Store ├── Assets.xcassets │ ├── .DS_Store │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-40@2x-1.png │ │ ├── Icon-40@2x.png │ │ ├── Icon-40@3x.png │ │ ├── Icon-60@2x.png │ │ ├── Icon-60@3x.png │ │ ├── Icon-76@1x.png │ │ ├── Icon-76@2x.png │ │ └── Icon-83.5@2x.png │ ├── Contents.json │ ├── LaunchImage.launchimage │ │ └── Contents.json │ ├── back.imageset │ │ ├── Contents.json │ │ ├── back@2x.png │ │ └── back@3x.png │ ├── default.imageset │ │ ├── Contents.json │ │ ├── default-1.png │ │ ├── default-2.png │ │ └── default.png │ ├── delete-ingredient.imageset │ │ ├── Contents.json │ │ ├── delete-ingredient-1.png │ │ ├── delete-ingredient-2.png │ │ └── delete-ingredient.png │ ├── forward.imageset │ │ ├── Contents.json │ │ ├── forward@2x.png │ │ └── forward@3x.png │ ├── placeholder-favorites.imageset │ │ ├── Contents.json │ │ ├── placeholder-favorites.png │ │ └── placeholder-favorites@2x.png │ ├── placeholder-search.imageset │ │ ├── Contents.json │ │ ├── placeholder-search.png │ │ └── placeholder-search@2x.png │ ├── safari-activity.imageset │ │ ├── Contents.json │ │ ├── safari.png │ │ ├── safari@2x.png │ │ └── safari@3x.png │ ├── search-tab-bar.imageset │ │ ├── Contents.json │ │ ├── search@1x.png │ │ ├── search@2x.png │ │ └── search@3x.png │ ├── star-filled.imageset │ │ ├── Contents.json │ │ ├── star-filled-1.png │ │ ├── star-filled-2.png │ │ └── star-filled.png │ ├── star-navbar.imageset │ │ ├── Contents.json │ │ ├── star@1x.png │ │ ├── star@2x.png │ │ └── star@3x.png │ ├── star-tab-bar.imageset │ │ ├── Contents.json │ │ ├── star@1x.png │ │ ├── star@2x.png │ │ └── star@3x.png │ ├── star.imageset │ │ ├── Contents.json │ │ ├── star-1.png │ │ ├── star-2.png │ │ └── star.png │ ├── trash-filled.imageset │ │ ├── Contents.json │ │ ├── trash-1.png │ │ ├── trash-2.png │ │ └── trash.png │ └── trash.imageset │ │ ├── Contents.json │ │ ├── delete-1.png │ │ ├── delete-2.png │ │ └── delete.png ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Extensions │ ├── SearchTableView+Keyboard.swift │ ├── String.swift │ ├── UIColor.swift │ └── UIViewController.swift ├── Info.plist ├── LFTReviewCollectionCell.xib ├── Model │ └── RecipeItem.swift ├── Navigation │ └── NavViewController.swift ├── Other │ ├── AppDelegate.swift │ ├── Left-Bridging-Header.h │ └── SafariActivity.swift ├── Services │ ├── FavoritesManager.swift │ ├── Food2ForkService.swift │ ├── JSONParser.swift │ ├── RecipesLoader.swift │ ├── SKStoreReviewManager.swift │ └── ShortcutsManager.swift ├── View Controller │ ├── FavoritesCollectionView.swift │ ├── RecipeWebView.swift │ ├── ResultsCollectionView.swift │ └── SearchTableView.swift └── View │ └── RecipeCollectionCell.swift ├── LeftTests ├── Info.plist └── LeftTests.swift ├── LeftUITests ├── Info.plist └── LeftUITests.swift ├── Podfile ├── Podfile.lock ├── Pods ├── .DS_Store ├── Alamofire │ ├── LICENSE │ ├── README.md │ └── Source │ │ ├── AFError.swift │ │ ├── Alamofire.swift │ │ ├── DispatchQueue+Alamofire.swift │ │ ├── MultipartFormData.swift │ │ ├── NetworkReachabilityManager.swift │ │ ├── Notifications.swift │ │ ├── ParameterEncoding.swift │ │ ├── Request.swift │ │ ├── Response.swift │ │ ├── ResponseSerialization.swift │ │ ├── Result.swift │ │ ├── ServerTrustPolicy.swift │ │ ├── SessionDelegate.swift │ │ ├── SessionManager.swift │ │ ├── TaskDelegate.swift │ │ ├── Timeline.swift │ │ └── Validation.swift ├── AlamofireNetworkActivityIndicator │ ├── LICENSE │ ├── README.md │ └── Source │ │ └── NetworkActivityIndicatorManager.swift ├── DZNEmptyDataSet │ ├── LICENSE │ ├── README.md │ └── Source │ │ ├── UIScrollView+EmptyDataSet.h │ │ └── UIScrollView+EmptyDataSet.m ├── Local Podspecs │ └── SwiftyJSON.podspec.json ├── Manifest.lock ├── Nuke │ ├── LICENSE │ ├── README.md │ └── Sources │ │ ├── Cache.swift │ │ ├── CancellationToken.swift │ │ ├── DataDecoder.swift │ │ ├── DataLoader.swift │ │ ├── Deduplicator.swift │ │ ├── Loader.swift │ │ ├── Manager.swift │ │ ├── Nuke.swift │ │ ├── Preheater.swift │ │ ├── Processor.swift │ │ ├── Promise.swift │ │ ├── Request.swift │ │ └── Scheduler.swift ├── Pods.xcodeproj │ ├── project.pbxproj │ └── xcuserdata │ │ └── ale_patron.xcuserdatad │ │ └── xcschemes │ │ ├── Alamofire.xcscheme │ │ ├── AlamofireNetworkActivityIndicator.xcscheme │ │ ├── DZNEmptyDataSet.xcscheme │ │ ├── Nuke.xcscheme │ │ ├── Pods-Left.xcscheme │ │ ├── SwiftyDrop.xcscheme │ │ ├── SwiftyJSON.xcscheme │ │ ├── UIScrollView-InfiniteScroll.xcscheme │ │ └── xcschememanagement.plist ├── SwiftyDrop │ ├── LICENSE │ ├── README.md │ └── SwiftyDrop │ │ └── Drop.swift ├── SwiftyJSON │ ├── LICENSE │ ├── README.md │ └── Source │ │ └── SwiftyJSON.swift ├── Target Support Files │ ├── Alamofire │ │ ├── Alamofire-dummy.m │ │ ├── Alamofire-prefix.pch │ │ ├── Alamofire-umbrella.h │ │ ├── Alamofire.modulemap │ │ ├── Alamofire.xcconfig │ │ └── Info.plist │ ├── AlamofireNetworkActivityIndicator │ │ ├── AlamofireNetworkActivityIndicator-dummy.m │ │ ├── AlamofireNetworkActivityIndicator-prefix.pch │ │ ├── AlamofireNetworkActivityIndicator-umbrella.h │ │ ├── AlamofireNetworkActivityIndicator.modulemap │ │ ├── AlamofireNetworkActivityIndicator.xcconfig │ │ └── Info.plist │ ├── DZNEmptyDataSet │ │ ├── DZNEmptyDataSet-dummy.m │ │ ├── DZNEmptyDataSet-prefix.pch │ │ ├── DZNEmptyDataSet-umbrella.h │ │ ├── DZNEmptyDataSet.modulemap │ │ ├── DZNEmptyDataSet.xcconfig │ │ └── Info.plist │ ├── Nuke │ │ ├── Info.plist │ │ ├── Nuke-dummy.m │ │ ├── Nuke-prefix.pch │ │ ├── Nuke-umbrella.h │ │ ├── Nuke.modulemap │ │ └── Nuke.xcconfig │ ├── Pods-Left │ │ ├── Info.plist │ │ ├── Pods-Left-acknowledgements.markdown │ │ ├── Pods-Left-acknowledgements.plist │ │ ├── Pods-Left-dummy.m │ │ ├── Pods-Left-frameworks.sh │ │ ├── Pods-Left-resources.sh │ │ ├── Pods-Left-umbrella.h │ │ ├── Pods-Left.debug.xcconfig │ │ ├── Pods-Left.modulemap │ │ └── Pods-Left.release.xcconfig │ ├── SwiftyDrop │ │ ├── Info.plist │ │ ├── SwiftyDrop-dummy.m │ │ ├── SwiftyDrop-prefix.pch │ │ ├── SwiftyDrop-umbrella.h │ │ ├── SwiftyDrop.modulemap │ │ └── SwiftyDrop.xcconfig │ ├── SwiftyJSON │ │ ├── Info.plist │ │ ├── SwiftyJSON-dummy.m │ │ ├── SwiftyJSON-prefix.pch │ │ ├── SwiftyJSON-umbrella.h │ │ ├── SwiftyJSON.modulemap │ │ └── SwiftyJSON.xcconfig │ └── UIScrollView-InfiniteScroll │ │ ├── Info.plist │ │ ├── UIScrollView-InfiniteScroll-dummy.m │ │ ├── UIScrollView-InfiniteScroll-prefix.pch │ │ ├── UIScrollView-InfiniteScroll-umbrella.h │ │ ├── UIScrollView-InfiniteScroll.modulemap │ │ └── UIScrollView-InfiniteScroll.xcconfig └── UIScrollView-InfiniteScroll │ ├── Classes │ ├── UIScrollView+InfiniteScroll.h │ └── UIScrollView+InfiniteScroll.m │ ├── LICENSE │ └── README.md ├── README.md ├── left1.png └── left2.png /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/.DS_Store -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Alamofire"] 2 | path = Alamofire 3 | url = https://github.com/Alamofire/Alamofire.git 4 | [submodule "Vendor/Freddy"] 5 | path = Vendor/Freddy 6 | url = https://github.com/bignerdranch/Freddy.git 7 | [submodule "SwiftyJSON"] 8 | path = SwiftyJSON 9 | url = https://github.com/SwiftyJSON/SwiftyJSON 10 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Alejandrina Patron 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Left.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Left.xcodeproj/project.xcworkspace/xcshareddata/Left.xcscmblueprint: -------------------------------------------------------------------------------- 1 | { 2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "8117E2D88D6DFC26835CAE7E84D922F008CA7B7E", 3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { 4 | 5 | }, 6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { 7 | "6ADA245F937D8ED6D99F5A7865E19F5267A169E2" : 0, 8 | "8117E2D88D6DFC26835CAE7E84D922F008CA7B7E" : 0, 9 | "C861FC00CEE0F6A6BE81FCFF6785FAA78C58EBB3" : 0, 10 | "67620B5EFA902936DF04070AF595B76AB0333747" : 0 11 | }, 12 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "42669628-17D7-439E-8E6A-5AA87D2652AC", 13 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { 14 | "6ADA245F937D8ED6D99F5A7865E19F5267A169E2" : "Left\/Vendor\/Freddy\/", 15 | "8117E2D88D6DFC26835CAE7E84D922F008CA7B7E" : "Left\/", 16 | "C861FC00CEE0F6A6BE81FCFF6785FAA78C58EBB3" : "Left\/SwiftyJSON\/", 17 | "67620B5EFA902936DF04070AF595B76AB0333747" : "Left\/Alamofire\/" 18 | }, 19 | "DVTSourceControlWorkspaceBlueprintNameKey" : "Left", 20 | "DVTSourceControlWorkspaceBlueprintVersion" : 204, 21 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "Left.xcodeproj", 22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ 23 | { 24 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/Alamofire\/Alamofire.git", 25 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 26 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "67620B5EFA902936DF04070AF595B76AB0333747" 27 | }, 28 | { 29 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/bignerdranch\/Freddy.git", 30 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 31 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "6ADA245F937D8ED6D99F5A7865E19F5267A169E2" 32 | }, 33 | { 34 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.gatech.edu\/apl7\/Left.git", 35 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 36 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "8117E2D88D6DFC26835CAE7E84D922F008CA7B7E" 37 | }, 38 | { 39 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/SwiftyJSON\/SwiftyJSON", 40 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 41 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "C861FC00CEE0F6A6BE81FCFF6785FAA78C58EBB3" 42 | } 43 | ] 44 | } -------------------------------------------------------------------------------- /Left.xcodeproj/project.xcworkspace/xcuserdata/ale_patron.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left.xcodeproj/project.xcworkspace/xcuserdata/ale_patron.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Left.xcodeproj/xcuserdata/ale_patron.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Left.xcodeproj/xcuserdata/ale_patron.xcuserdatad/xcschemes/Left.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /Left.xcodeproj/xcuserdata/ale_patron.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Left.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | ED8039961BAC9D40009487B5 16 | 17 | primary 18 | 19 | 20 | ED8039AC1BAC9D40009487B5 21 | 22 | primary 23 | 24 | 25 | ED8039B71BAC9D40009487B5 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Left.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Left.xcworkspace/xcuserdata/ale_patron.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left.xcworkspace/xcuserdata/ale_patron.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /Left.xcworkspace/xcuserdata/ale_patron.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /Left/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/.DS_Store -------------------------------------------------------------------------------- /Left/Assets.xcassets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/.DS_Store -------------------------------------------------------------------------------- /Left/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "29x29", 26 | "scale" : "3x" 27 | }, 28 | { 29 | "size" : "40x40", 30 | "idiom" : "iphone", 31 | "filename" : "Icon-40@2x.png", 32 | "scale" : "2x" 33 | }, 34 | { 35 | "size" : "40x40", 36 | "idiom" : "iphone", 37 | "filename" : "Icon-40@3x.png", 38 | "scale" : "3x" 39 | }, 40 | { 41 | "idiom" : "iphone", 42 | "size" : "57x57", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "idiom" : "iphone", 47 | "size" : "57x57", 48 | "scale" : "2x" 49 | }, 50 | { 51 | "size" : "60x60", 52 | "idiom" : "iphone", 53 | "filename" : "Icon-60@2x.png", 54 | "scale" : "2x" 55 | }, 56 | { 57 | "size" : "60x60", 58 | "idiom" : "iphone", 59 | "filename" : "Icon-60@3x.png", 60 | "scale" : "3x" 61 | }, 62 | { 63 | "idiom" : "ipad", 64 | "size" : "20x20", 65 | "scale" : "1x" 66 | }, 67 | { 68 | "idiom" : "ipad", 69 | "size" : "20x20", 70 | "scale" : "2x" 71 | }, 72 | { 73 | "idiom" : "ipad", 74 | "size" : "29x29", 75 | "scale" : "1x" 76 | }, 77 | { 78 | "idiom" : "ipad", 79 | "size" : "29x29", 80 | "scale" : "2x" 81 | }, 82 | { 83 | "idiom" : "ipad", 84 | "size" : "40x40", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-40@2x-1.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "idiom" : "ipad", 95 | "size" : "50x50", 96 | "scale" : "1x" 97 | }, 98 | { 99 | "idiom" : "ipad", 100 | "size" : "50x50", 101 | "scale" : "2x" 102 | }, 103 | { 104 | "idiom" : "ipad", 105 | "size" : "72x72", 106 | "scale" : "1x" 107 | }, 108 | { 109 | "idiom" : "ipad", 110 | "size" : "72x72", 111 | "scale" : "2x" 112 | }, 113 | { 114 | "size" : "76x76", 115 | "idiom" : "ipad", 116 | "filename" : "Icon-76@1x.png", 117 | "scale" : "1x" 118 | }, 119 | { 120 | "size" : "76x76", 121 | "idiom" : "ipad", 122 | "filename" : "Icon-76@2x.png", 123 | "scale" : "2x" 124 | }, 125 | { 126 | "size" : "83.5x83.5", 127 | "idiom" : "ipad", 128 | "filename" : "Icon-83.5@2x.png", 129 | "scale" : "2x" 130 | } 131 | ], 132 | "info" : { 133 | "version" : 1, 134 | "author" : "xcode" 135 | } 136 | } -------------------------------------------------------------------------------- /Left/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/AppIcon.appiconset/Icon-76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/AppIcon.appiconset/Icon-76@1x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Left/Assets.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "orientation" : "portrait", 5 | "idiom" : "iphone", 6 | "extent" : "full-screen", 7 | "minimum-system-version" : "8.0", 8 | "subtype" : "736h", 9 | "scale" : "3x" 10 | }, 11 | { 12 | "orientation" : "landscape", 13 | "idiom" : "iphone", 14 | "extent" : "full-screen", 15 | "minimum-system-version" : "8.0", 16 | "subtype" : "736h", 17 | "scale" : "3x" 18 | }, 19 | { 20 | "orientation" : "portrait", 21 | "idiom" : "iphone", 22 | "extent" : "full-screen", 23 | "minimum-system-version" : "8.0", 24 | "subtype" : "667h", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "orientation" : "portrait", 29 | "idiom" : "iphone", 30 | "extent" : "full-screen", 31 | "minimum-system-version" : "7.0", 32 | "scale" : "2x" 33 | }, 34 | { 35 | "orientation" : "portrait", 36 | "idiom" : "iphone", 37 | "extent" : "full-screen", 38 | "minimum-system-version" : "7.0", 39 | "subtype" : "retina4", 40 | "scale" : "2x" 41 | }, 42 | { 43 | "orientation" : "portrait", 44 | "idiom" : "iphone", 45 | "extent" : "full-screen", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "orientation" : "portrait", 50 | "idiom" : "iphone", 51 | "extent" : "full-screen", 52 | "scale" : "2x" 53 | }, 54 | { 55 | "orientation" : "portrait", 56 | "idiom" : "iphone", 57 | "extent" : "full-screen", 58 | "subtype" : "retina4", 59 | "scale" : "2x" 60 | } 61 | ], 62 | "info" : { 63 | "version" : 1, 64 | "author" : "xcode" 65 | } 66 | } -------------------------------------------------------------------------------- /Left/Assets.xcassets/back.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "back@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "back@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Left/Assets.xcassets/back.imageset/back@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/back.imageset/back@2x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/back.imageset/back@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/back.imageset/back@3x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/default.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "default.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "default-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "default-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Left/Assets.xcassets/default.imageset/default-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/default.imageset/default-1.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/default.imageset/default-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/default.imageset/default-2.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/default.imageset/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/default.imageset/default.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/delete-ingredient.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "delete-ingredient.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "delete-ingredient-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "delete-ingredient-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Left/Assets.xcassets/delete-ingredient.imageset/delete-ingredient-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/delete-ingredient.imageset/delete-ingredient-1.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/delete-ingredient.imageset/delete-ingredient-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/delete-ingredient.imageset/delete-ingredient-2.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/delete-ingredient.imageset/delete-ingredient.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/delete-ingredient.imageset/delete-ingredient.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/forward.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "forward@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "forward@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Left/Assets.xcassets/forward.imageset/forward@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/forward.imageset/forward@2x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/forward.imageset/forward@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/forward.imageset/forward@3x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/placeholder-favorites.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "placeholder-favorites.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "placeholder-favorites@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Left/Assets.xcassets/placeholder-favorites.imageset/placeholder-favorites.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/placeholder-favorites.imageset/placeholder-favorites.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/placeholder-favorites.imageset/placeholder-favorites@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/placeholder-favorites.imageset/placeholder-favorites@2x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/placeholder-search.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "placeholder-search.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "placeholder-search@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Left/Assets.xcassets/placeholder-search.imageset/placeholder-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/placeholder-search.imageset/placeholder-search.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/placeholder-search.imageset/placeholder-search@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/placeholder-search.imageset/placeholder-search@2x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/safari-activity.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "safari.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "safari@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "safari@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Left/Assets.xcassets/safari-activity.imageset/safari.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/safari-activity.imageset/safari.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/safari-activity.imageset/safari@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/safari-activity.imageset/safari@2x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/safari-activity.imageset/safari@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/safari-activity.imageset/safari@3x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/search-tab-bar.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "search@1x.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "search@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "search@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Left/Assets.xcassets/search-tab-bar.imageset/search@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/search-tab-bar.imageset/search@1x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/search-tab-bar.imageset/search@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/search-tab-bar.imageset/search@2x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/search-tab-bar.imageset/search@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/search-tab-bar.imageset/search@3x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/star-filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "star-filled.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "star-filled-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "star-filled-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Left/Assets.xcassets/star-filled.imageset/star-filled-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/star-filled.imageset/star-filled-1.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/star-filled.imageset/star-filled-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/star-filled.imageset/star-filled-2.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/star-filled.imageset/star-filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/star-filled.imageset/star-filled.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/star-navbar.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "star@1x.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "star@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "star@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Left/Assets.xcassets/star-navbar.imageset/star@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/star-navbar.imageset/star@1x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/star-navbar.imageset/star@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/star-navbar.imageset/star@2x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/star-navbar.imageset/star@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/star-navbar.imageset/star@3x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/star-tab-bar.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "star@1x.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "star@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "star@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Left/Assets.xcassets/star-tab-bar.imageset/star@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/star-tab-bar.imageset/star@1x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/star-tab-bar.imageset/star@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/star-tab-bar.imageset/star@2x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/star-tab-bar.imageset/star@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/star-tab-bar.imageset/star@3x.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/star.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "star.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "star-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "star-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Left/Assets.xcassets/star.imageset/star-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/star.imageset/star-1.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/star.imageset/star-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/star.imageset/star-2.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/star.imageset/star.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/star.imageset/star.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/trash-filled.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "trash.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "trash-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "trash-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Left/Assets.xcassets/trash-filled.imageset/trash-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/trash-filled.imageset/trash-1.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/trash-filled.imageset/trash-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/trash-filled.imageset/trash-2.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/trash-filled.imageset/trash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/trash-filled.imageset/trash.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/trash.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "delete.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "delete-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "delete-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Left/Assets.xcassets/trash.imageset/delete-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/trash.imageset/delete-1.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/trash.imageset/delete-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/trash.imageset/delete-2.png -------------------------------------------------------------------------------- /Left/Assets.xcassets/trash.imageset/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Left/Assets.xcassets/trash.imageset/delete.png -------------------------------------------------------------------------------- /Left/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /Left/Extensions/SearchTableView+Keyboard.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchTableView+Keyboard.swift 3 | // Left 4 | // 5 | // Created by Alejandrina Patron on 1/25/17. 6 | // Copyright © 2017 Ale Patrón. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension SearchTableView { 12 | func subscribeToKeyboardNotifications() { 13 | NotificationCenter.default.addObserver(self, selector: #selector(SearchTableView.keyboardWillShow), 14 | name: NSNotification.Name.UIKeyboardWillShow, 15 | object: nil) 16 | NotificationCenter.default.addObserver(self, selector: #selector(SearchTableView.keyboardWillHide), 17 | name: NSNotification.Name.UIKeyboardWillHide, 18 | object: nil) 19 | } 20 | 21 | func unsubscribeFromKeyboardNotifications() { 22 | NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillShow, object: nil) 23 | NotificationCenter.default.removeObserver(self, name: NSNotification.Name.UIKeyboardWillHide, object: nil) 24 | } 25 | 26 | func keyboardWillShow(notification: NSNotification) { 27 | if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue { 28 | let keyboardHeight = keyboardSize.height 29 | // if (self.view.frame.origin.y >= 0) { 30 | // view.frame.origin.y -= (keyboardHeight - (self.tabBarController?.tabBar.frame.size.height)!) 31 | // } 32 | self.searchButton.frame.origin.y -= (keyboardHeight - (self.tabBarController?.tabBar.frame.size.height)!) 33 | } 34 | } 35 | 36 | func keyboardWillHide(notification: NSNotification) { 37 | if let keyboardSize = (notification.userInfo?[UIKeyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue { 38 | let keyboardHeight = keyboardSize.height 39 | // if (self.view.frame.origin.y < 0) { 40 | // view.frame.origin.y += (keyboardHeight - (self.tabBarController?.tabBar.frame.size.height)!) 41 | // } 42 | self.searchButton.frame.origin.y += (keyboardHeight - (self.tabBarController?.tabBar.frame.size.height)!) 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /Left/Extensions/String.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String.swift 3 | // Left 4 | // 5 | // Created by Alejandrina Patron on 8/4/16. 6 | // Copyright © 2016 Ale Patrón. All rights reserved. 7 | // 8 | 9 | extension String { 10 | 11 | func verifyRecipeName() -> String { 12 | var verifiedString = self 13 | verifiedString = verifiedString.replacingOccurrences(of: "&", with: "&") 14 | verifiedString = verifiedString.replacingOccurrences(of: "’", with: "'") 15 | verifiedString = verifiedString.replacingOccurrences(of: "™", with: "®") 16 | verifiedString = verifiedString.replacingOccurrences(of: " ", with: " ") 17 | 18 | return verifiedString 19 | } 20 | 21 | func urlToImg(completion: @escaping (UIImage?) -> ()) { 22 | DispatchQueue.global(qos: .background).async { 23 | let url = NSURL(string: self) 24 | if let data = NSData(contentsOf: url! as URL) { 25 | if let image = UIImage(data: data as Data) { 26 | DispatchQueue.main.async { 27 | completion(image) 28 | } 29 | } 30 | } else { 31 | DispatchQueue.main.async { 32 | completion(nil) 33 | } 34 | } 35 | } 36 | } 37 | 38 | func trim() -> String { 39 | return self.trimmingCharacters(in: NSCharacterSet.whitespaces) 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Left/Extensions/UIColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor.swift 3 | // Left 4 | // 5 | // Created by Alejandrina Patron on 8/4/16. 6 | // Copyright © 2016 Ale Patrón. All rights reserved. 7 | // 8 | 9 | extension UIColor { 10 | class func LeftColor() -> UIColor { 11 | return UIColor(red: 153.0/255.0, green: 51.0/255.0, blue:255.0/255.0, alpha: 1.0) 12 | } 13 | } -------------------------------------------------------------------------------- /Left/Extensions/UIViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController.swift 3 | // Left 4 | // 5 | // Created by Alejandrina Patron on 8/4/16. 6 | // Copyright © 2016 Ale Patrón. All rights reserved. 7 | // 8 | 9 | import SwiftyDrop 10 | 11 | extension UIViewController { 12 | 13 | func showAlert(alertType: AlertType) { 14 | switch alertType { 15 | case .MoreThanFiveIngredients: 16 | let alert = UIAlertController(title: "Oops!", message: "You cannot use more than 5 ingredients", preferredStyle: .alert) 17 | let action = UIAlertAction(title: "Got it", style: .default) { _ in } 18 | alert.addAction(action) 19 | self.present(alert, animated: true) {} 20 | 21 | case .NoResults: 22 | let alert = UIAlertController(title: "😢", message: "No results found...", preferredStyle: .alert) 23 | let action = UIAlertAction(title: "Ok", style: .default) { _ in } 24 | alert.addAction(action) 25 | self.present(alert, animated: true) {} 26 | 27 | case .SearchFailure: 28 | let alert = UIAlertController(title: "Something went wrong", message: "Are you sure you are connected to the Internet? 🔌🌎", preferredStyle: .alert) 29 | let action = UIAlertAction(title: "Ok", style: .default) { _ in } 30 | alert.addAction(action) 31 | self.present(alert, animated: true) {} 32 | 33 | case .AtLeastOneIngredient: 34 | let alert = UIAlertController(title: "Hey", message: "Please provide at least one ingredient!", preferredStyle: .alert) 35 | let action = UIAlertAction(title: "Ok", style: .default) { _ in } 36 | alert.addAction(action) 37 | self.present(alert, animated: true) {} 38 | } 39 | } 40 | } 41 | 42 | // MARK: Alert Type 43 | 44 | enum AlertType { 45 | case MoreThanFiveIngredients 46 | case NoResults 47 | case SearchFailure 48 | case AtLeastOneIngredient 49 | } 50 | 51 | // MARK: Custom SwiftyDrop 52 | 53 | enum Custom: DropStatable { 54 | case Left 55 | var backgroundColor: UIColor? { 56 | switch self { 57 | case .Left: return UIColor.LeftColor() 58 | } 59 | } 60 | var font: UIFont? { 61 | switch self { 62 | case .Left: return UIFont(name: "HelveticaNeue", size: 16.0) 63 | } 64 | } 65 | var textColor: UIColor? { 66 | switch self { 67 | case .Left: return .white 68 | } 69 | } 70 | var blurEffect: UIBlurEffect? { 71 | switch self { 72 | case .Left: return nil 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Left/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 2.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSAllowsArbitraryLoads 28 | 29 | 30 | NSHumanReadableCopyright 31 | Copyright © 2016 Ale Patron. All rights reserved. 32 | UILaunchStoryboardName 33 | LaunchScreen 34 | UIMainStoryboardFile 35 | Main 36 | UIRequiredDeviceCapabilities 37 | 38 | armv7 39 | 40 | UIRequiresFullScreen 41 | 42 | UIStatusBarHidden 43 | 44 | UIStatusBarStyle 45 | UIStatusBarStyleLightContent 46 | UIStatusBarTintParameters 47 | 48 | UINavigationBar 49 | 50 | Style 51 | UIBarStyleDefault 52 | Translucent 53 | 54 | 55 | 56 | UISupportedInterfaceOrientations 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /Left/LFTReviewCollectionCell.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /Left/Model/RecipeItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RecipeItem.swift 3 | // Left 4 | // 5 | // Created by Alejandrina Patron on 11/4/15. 6 | // Copyright © 2015 Ale Patrón. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | let RecipeOpenCountKey = "recipeOpenCount" 12 | 13 | class RecipeItem: NSObject { 14 | var name: String 15 | var photo: UIImage? 16 | var url: String 17 | var photoUrl: String? 18 | 19 | let nameKey = "name" 20 | let photoKey = "photo" 21 | let urlKey = "url" 22 | 23 | func encodeWithCoder(_ aCoder: NSCoder) { 24 | aCoder.encode(name, forKey: nameKey) 25 | if let thePhoto = photo { 26 | aCoder.encode(thePhoto, forKey: photoKey) 27 | } 28 | aCoder.encode(url, forKey: urlKey) 29 | } 30 | 31 | required internal init?(coder aDecoder: NSCoder) { 32 | name = aDecoder.decodeObject(forKey: nameKey) as! String 33 | photo = aDecoder.decodeObject(forKey: photoKey) as? UIImage 34 | url = aDecoder.decodeObject(forKey: urlKey) as! String 35 | } 36 | 37 | init(name: String, photo: UIImage?, url: String) { 38 | self.name = name 39 | self.photo = photo 40 | self.url = url 41 | } 42 | 43 | init(name: String, photoUrl: String?, photo: UIImage?, url: String) { 44 | self.name = name 45 | self.photoUrl = photoUrl 46 | self.photo = photo 47 | self.url = url 48 | } 49 | 50 | // MARK: 3D touch quick actions 51 | 52 | func updateOpenCount() { 53 | if #available(iOS 9.0, *) { 54 | let data = UserDefaults.standard 55 | var dict: [String : Int] = data.dictionary(forKey: RecipeOpenCountKey) as? [String : Int] ?? [:] 56 | let key = "\(self.name)~~\(self.url)" 57 | let previousCount = dict[key] ?? 0 58 | dict.updateValue(previousCount + 1, forKey: key) 59 | data.set(dict, forKey: RecipeOpenCountKey) 60 | 61 | RecipeItem.updateShortcutItems() 62 | } 63 | } 64 | 65 | func removeFromDefaults(index: Int) { 66 | // 3D touch available iOS 9.0+ 67 | if #available(iOS 9.0, *) { 68 | let data = UserDefaults.standard 69 | var dict: [String : Int] = data.dictionary(forKey: RecipeOpenCountKey) as? [String : Int] ?? [ : ] 70 | let recipeKey = "\(self.name)~~\(self.url)" 71 | dict.removeValue(forKey: recipeKey) 72 | data.set(dict, forKey: RecipeOpenCountKey) 73 | 74 | RecipeItem.updateShortcutItems() 75 | } 76 | } 77 | 78 | static func updateShortcutItems() { 79 | if #available(iOS 9.0, *) { 80 | let data = UserDefaults.standard 81 | let dict: [String : Int] = data.dictionary(forKey: RecipeOpenCountKey) as? [String : Int] ?? [ : ] 82 | let sortedDict = dict.sorted{ $0.1 > $1.1 } 83 | var shortcuts: [UIApplicationShortcutItem] = [] 84 | 85 | for i in 0.. Bool { 18 | NetworkActivityIndicatorManager.shared.isEnabled = true 19 | return true 20 | } 21 | 22 | @available(iOS 9.0, *) 23 | func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) { 24 | 25 | guard let info = shortcutItem.userInfo else { return } 26 | guard let recipeName = info["NAME"] as? String else { return } 27 | guard let recipeURL = info["URL"] as? String else { return } 28 | 29 | openRecipeFromQuickAction(recipeName: recipeName, recipeURL: recipeURL) 30 | completionHandler(true) 31 | } 32 | 33 | private func openRecipeFromQuickAction(recipeName: String, recipeURL: String) { 34 | let webView = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "RecipeWebView") as! RecipeWebView 35 | webView.recipe = RecipeItem(name: recipeName, photo: nil, url: recipeURL) 36 | let tabsController = self.window?.rootViewController as! UITabBarController 37 | tabsController.selectedIndex = 1 38 | let favsTab = tabsController.viewControllers?[1] as! UINavigationController 39 | favsTab.popToRootViewController(animated: true) 40 | favsTab.pushViewController(webView, animated: true) 41 | } 42 | 43 | func applicationWillResignActive(_ application: UIApplication) { 44 | // 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. 45 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 46 | } 47 | 48 | func applicationDidEnterBackground(_ application: UIApplication) { 49 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 50 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 51 | 52 | // Dismiss keyboard when user presses home button while typing an ingredient on SearchVC 53 | self.window?.endEditing(true) 54 | } 55 | 56 | func applicationWillEnterForeground(_ application: UIApplication) { 57 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 58 | } 59 | 60 | func applicationDidBecomeActive(_ application: UIApplication) { 61 | // 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. 62 | } 63 | 64 | func applicationWillTerminate(_ application: UIApplication) { 65 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 66 | } 67 | 68 | 69 | } 70 | -------------------------------------------------------------------------------- /Left/Other/Left-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Left-Bridging-Header.h 3 | // Left 4 | // 5 | // Created by Alejandrina Patron on 6/3/16. 6 | // Copyright © 2016 Ale Patrón. All rights reserved. 7 | // 8 | 9 | #ifndef BridgingHeader_h 10 | #define BridgingHeader_h 11 | 12 | #import 13 | #import 14 | 15 | 16 | #endif /* BridgingHeader_h */ 17 | -------------------------------------------------------------------------------- /Left/Other/SafariActivity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SafariActivity.swift 3 | // 4 | // 5 | // Created by Alejandrina Patron on 4/14/16. 6 | // 7 | // 8 | 9 | import UIKit 10 | 11 | class SafariActivity : UIActivity { 12 | 13 | var URL: NSURL? 14 | 15 | override var activityType: UIActivityType? { 16 | return UIActivityType(rawValue: NSStringFromClass(SafariActivity.self)) 17 | } 18 | 19 | override var activityTitle: String? { 20 | return "Open in Safari" 21 | } 22 | 23 | override var activityImage: UIImage? { 24 | return UIImage(named: "safari-activity") 25 | } 26 | 27 | override func prepare(withActivityItems activityItems: [Any]) { 28 | for item in activityItems { 29 | if let item = item as? NSURL { 30 | URL = item 31 | } 32 | } 33 | } 34 | 35 | override func canPerform(withActivityItems activityItems: [Any]) -> Bool { 36 | for item in activityItems { 37 | if let _ = item as? NSURL { 38 | return true 39 | } 40 | } 41 | return false 42 | } 43 | 44 | override func perform() { 45 | if let URL = URL { 46 | UIApplication.shared.openURL(URL as URL) 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /Left/Services/FavoritesManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FavoritesManager.swift 3 | // Left 4 | // 5 | // Created by Alejandrina Patron on 3/4/16. 6 | // Copyright © 2016 Ale Patrón. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class FavoritesManager { 12 | 13 | // Singleton 14 | static let shared = FavoritesManager() 15 | 16 | private var favoriteRecipes = [RecipeItem]() 17 | 18 | func addRecipe(recipe: RecipeItem) { 19 | favoriteRecipes.append(recipe) 20 | save() 21 | } 22 | 23 | func deleteRecipeAtIndex(index: Int) { 24 | favoriteRecipes.remove(at: index) 25 | save() 26 | } 27 | 28 | func recipeAtIndex(index: Int) -> RecipeItem? { 29 | return favoriteRecipes[index] 30 | } 31 | 32 | func recipeCount() -> Int { 33 | return favoriteRecipes.count 34 | } 35 | 36 | func archivePath() -> String? { 37 | let directoryList = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) 38 | if let documentsPath = directoryList.first { 39 | return documentsPath + "/Favorites" 40 | } 41 | assertionFailure("Could not determine where to save file.") 42 | return nil 43 | } 44 | 45 | func save() { 46 | if let theArchivePath = archivePath() { 47 | if NSKeyedArchiver.archiveRootObject(favoriteRecipes, toFile: theArchivePath) { 48 | print("We saved!") 49 | } else { 50 | assertionFailure("Could not save to \(theArchivePath)") 51 | } 52 | } 53 | } 54 | 55 | func unarchivedSavedItems() { 56 | if let theArchivePath = archivePath() { 57 | if FileManager.default.fileExists(atPath: theArchivePath) { 58 | favoriteRecipes = NSKeyedUnarchiver.unarchiveObject(withFile: theArchivePath) as! [RecipeItem] 59 | } 60 | } 61 | } 62 | 63 | init() { 64 | unarchivedSavedItems() 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Left/Services/Food2ForkService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Food2ForkService.swift 3 | // Left 4 | // 5 | // Created by Alejandrina Patron on 8/3/16. 6 | // Copyright © 2016 Ale Patrón. All rights reserved. 7 | // 8 | 9 | import Alamofire 10 | import SwiftyJSON 11 | 12 | public struct Food2ForkService { 13 | 14 | private static let API_KEY: String = "570024717057c65d605c4d54f84f2300" 15 | private static let BASE_URL: String = "http://food2fork.com/api/search?key=" + API_KEY + "&q=" 16 | 17 | static func recipesForIngredients(ingredients: String, page: Int, completion: @escaping ([RecipeItem], Error?) -> ()) { 18 | var urlString = BASE_URL + ingredients + "&page=\(page)" 19 | urlString = urlString.addingPercentEncoding(withAllowedCharacters: NSCharacterSet.urlQueryAllowed)! 20 | Alamofire.request(urlString).responseJSON { response in 21 | switch response.result { 22 | case .success: 23 | let result = JSON(response.result.value!) 24 | let recipes = JSONParser.parseRecipes(data: result) 25 | completion(recipes, nil) 26 | case .failure(let error): 27 | completion([], error) 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Left/Services/JSONParser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONParser.swift 3 | // Left 4 | // 5 | // Created by Alejandrina Patron on 8/3/16. 6 | // Copyright © 2016 Ale Patrón. All rights reserved. 7 | // 8 | 9 | import SwiftyJSON 10 | 11 | struct JSONParser { 12 | 13 | static func parseRecipes(data: JSON) -> [RecipeItem] { 14 | return self.JSONObjectsFromData(data: data)?.map(self.parseRecipe) ?? [] 15 | } 16 | 17 | // MARK: Helper 18 | 19 | private static func parseRecipe(recipe: JSON) -> RecipeItem { 20 | let name = (recipe["title"].string ?? "").verifyRecipeName() 21 | let url: String = recipe["source_url"].string ?? "" 22 | let photoURL: String = recipe["image_url"].string! 23 | return RecipeItem(name: name, photoUrl: photoURL, photo: nil, url: url) 24 | } 25 | 26 | private static func JSONObjectsFromData(data: JSON?) -> [JSON]? { 27 | if let data = data { 28 | if let items = data["recipes"].array { 29 | return items 30 | } 31 | } 32 | return nil 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Left/Services/RecipesLoader.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RecipesLoader.swift 3 | // Left 4 | // 5 | // Created by Alejandrina Patron on 8/4/16. 6 | // Copyright © 2016 Ale Patrón. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class RecipesLoader { 12 | private var hasMore: Bool 13 | private var isLoading: Bool = false 14 | private var page: Int = 1 15 | private let ingredients: String 16 | 17 | init(ingredients: String) { 18 | self.ingredients = ingredients 19 | self.hasMore = true 20 | } 21 | 22 | func load(page: Int = 1, completion: @escaping ([RecipeItem], Error?) -> ()) { 23 | if isLoading { 24 | return 25 | } 26 | Food2ForkService.recipesForIngredients(ingredients: self.ingredients, page: page, completion: completion) 27 | 28 | } 29 | func loadMore(completion: @escaping ([RecipeItem], Error?) -> ()) { 30 | page += 1 31 | load(page: page, completion: completion) 32 | } 33 | 34 | func hasMoreRecipes() -> Bool { 35 | return hasMore 36 | } 37 | 38 | func setHasMore(hasMore: Bool) { 39 | self.hasMore = hasMore 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Left/Services/SKStoreReviewManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SKStoreReviewManager.swift 3 | // Left 4 | // 5 | // Created by Alejandrina Patron on 5/14/17. 6 | // Copyright © 2017 Ale Patrón. All rights reserved. 7 | // 8 | 9 | import StoreKit 10 | 11 | struct SKStoreReviewManager { 12 | 13 | private static let APP_RUNS_KEY = "APP_RUNS" 14 | 15 | static func incrementAppRuns() { 16 | let defaults = UserDefaults.standard 17 | var appRuns = defaults.value(forKey: APP_RUNS_KEY) as? Int ?? 0 18 | appRuns += 1 19 | defaults.set(appRuns, forKey: APP_RUNS_KEY) 20 | } 21 | 22 | static func askForReview() { 23 | let defaults = UserDefaults.standard 24 | let appRuns = defaults.value(forKey: APP_RUNS_KEY) as? Int ?? 0 25 | 26 | if #available(iOS 10.3, *) { 27 | if appRuns > 0 && appRuns % 15 == 0 { 28 | SKStoreReviewController.requestReview() 29 | } 30 | } else { 31 | // Fallback on earlier versions 32 | } 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Left/Services/ShortcutsManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ShortcutsManager.swift 3 | // Left 4 | // 5 | // Created by Alejandrina Patron on 6/11/17. 6 | // Copyright © 2017 Ale Patrón. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct ShortcutsManager { 12 | 13 | static func updateCount(for recipe: RecipeItem) { 14 | 15 | } 16 | 17 | static func removeFromDefaults(at index: Int) { 18 | // 3D touch available iOS 9.0+ 19 | // if #available(iOS 9.0, *) { 20 | // let data = UserDefaults.standard 21 | // var dict: [String : Int] = data.dictionary(forKey: RecipeOpenCountKey) as? [String : Int] ?? [ : ] 22 | // let recipeKey = "\(self.name)~~\(self.url)" 23 | // dict.removeValue(forKey: recipeKey) 24 | // data.set(dict, forKey: RecipeOpenCountKey) 25 | // 26 | // RecipeItem.updateShortcutItems() 27 | // } 28 | } 29 | 30 | static func updateShortcutItems() { 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Left/View Controller/RecipeWebView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RecipeWebView.swift 3 | // Left 4 | // 5 | // Created by Alejandrina Patron on 8/4/16. 6 | // Copyright © 2016 Ale Patrón. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SwiftyDrop 11 | 12 | class RecipeWebView: UIViewController, UIWebViewDelegate { 13 | 14 | @IBOutlet weak var webView: UIWebView! 15 | @IBOutlet weak var backButton: UIBarButtonItem! 16 | @IBOutlet weak var forwardButton: UIBarButtonItem! 17 | @IBOutlet weak var activityIndicator: UIActivityIndicatorView! 18 | 19 | let favoritesManager = FavoritesManager.shared 20 | var recipe: RecipeItem! 21 | var saveButton: UIBarButtonItem! 22 | 23 | override func viewDidLoad() { 24 | super.viewDidLoad() 25 | 26 | // NOTE: saveButton visible when coming from Results but not from Favorites 27 | if let saveButton = self.navigationItem.rightBarButtonItem { 28 | saveButton.target = self 29 | saveButton.action = #selector(RecipeWebView.saveButtonPressed(sender:)) 30 | } else { 31 | // Update open count of recipe for 3D touch quick actions 32 | // Only when coming from Favorites 33 | recipe.updateOpenCount() 34 | } 35 | 36 | self.navigationItem.title = recipe.name 37 | activityIndicator.hidesWhenStopped = true 38 | openUrl() 39 | } 40 | 41 | // override func viewWillAppear(_ animated: Bool) { 42 | // super.viewWillAppear(animated) 43 | // 44 | // self.tabBarController?.tabBar.isHidden = true 45 | // } 46 | 47 | @IBAction func refresh(sender: UIBarButtonItem) { 48 | webView.reload() 49 | } 50 | 51 | @IBAction func back(sender: UIBarButtonItem) { 52 | if webView.canGoBack { 53 | webView.goBack() 54 | } 55 | } 56 | 57 | @IBAction func forward(sender: UIBarButtonItem) { 58 | if webView.canGoForward { 59 | webView.goForward() 60 | } 61 | } 62 | 63 | @IBAction func action(sender: UIBarButtonItem) { 64 | showActivityViewController() 65 | } 66 | 67 | func webViewDidStartLoad(_ webView: UIWebView) { 68 | activityIndicator.startAnimating() 69 | } 70 | 71 | func webViewDidFinishLoad(_ webView: UIWebView) { 72 | activityIndicator.stopAnimating() 73 | updateNavButtons() 74 | } 75 | 76 | // MARK: Helper 77 | 78 | private func openUrl() { 79 | let url = NSURL(string: (self.recipe.url)) 80 | let requesObj = NSURLRequest(url: url! as URL) 81 | webView.loadRequest(requesObj as URLRequest) 82 | } 83 | 84 | private func updateNavButtons() { 85 | backButton.isEnabled = webView.canGoBack 86 | forwardButton.isEnabled = webView.canGoForward 87 | } 88 | 89 | @objc private func saveButtonPressed(sender: UIBarButtonItem!) { 90 | favoritesManager.addRecipe(recipe: self.recipe) 91 | Drop.down("Added to your favorites ⭐", state: Custom.Left) 92 | 93 | // Haptic feedback (available iOS 10+) 94 | if #available(iOS 10.0, *) { 95 | let savedRecipeFeedbackGenerator = UIImpactFeedbackGenerator(style: .heavy) 96 | savedRecipeFeedbackGenerator.impactOccurred() 97 | } 98 | } 99 | 100 | // MARK: Safari Activity 101 | 102 | func showActivityViewController() { 103 | let activityItems: [AnyObject] = 104 | ["I found this recipe on Left app " as AnyObject, NSURL(string: self.recipe.url) as AnyObject] 105 | let activities: [UIActivity] = [SafariActivity()] 106 | let activityViewController = UIActivityViewController(activityItems: activityItems, applicationActivities: activities) 107 | activityViewController.view.tintColor = UIColor.LeftColor() 108 | present(activityViewController, animated: true, completion: {}) 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /Left/View Controller/SearchTableView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchTableView.swift 3 | // Left 4 | // 5 | // Created by Alejandrina Patron on 8/6/16. 6 | // Copyright © 2016 Ale Patrón. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import DZNEmptyDataSet 11 | 12 | class SearchTableView: UIViewController, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate, DZNEmptyDataSetSource, DZNEmptyDataSetDelegate { 13 | 14 | @IBOutlet weak var textField: UITextField! 15 | @IBOutlet weak var tableView: UITableView! 16 | @IBOutlet weak var searchButton: UIButton! 17 | 18 | let textFieldPlaceholder = "Type an ingredient and press +" 19 | var ingredients = [String]() 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | 24 | SKStoreReviewManager.incrementAppRuns() 25 | SKStoreReviewManager.askForReview() 26 | 27 | subscribeToKeyboardNotifications() 28 | 29 | self.tableView.emptyDataSetSource = self 30 | self.tableView.emptyDataSetDelegate = self 31 | self.tableView.dataSource = self 32 | 33 | tableView.tableFooterView = UIView(frame: CGRect.zero) 34 | 35 | textField.placeholder = textFieldPlaceholder 36 | textField.tintColor = UIColor.LeftColor() 37 | textField.delegate = self 38 | 39 | searchButtonEnabled() 40 | 41 | let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(SearchTableView.dismissKeyboard)) 42 | self.view.addGestureRecognizer(tap) 43 | } 44 | 45 | // override func viewWillDisappear(_ animated: Bool) { 46 | // super.viewWillDisappear(animated) 47 | // unsubscribeFromKeyboardNotifications() 48 | // } 49 | 50 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 51 | if segue.identifier == "resultsSegue" { 52 | let destinationVC = segue.destination as! ResultsCollectionView 53 | destinationVC.ingredients = ingredients 54 | } 55 | } 56 | 57 | // MARK: IBAction 58 | 59 | @IBAction func addButtonPressed(_ sender: UIBarButtonItem) { 60 | addIngreditent() 61 | } 62 | 63 | @IBAction func searchButtonPressed(_ sender: UIButton) { 64 | dismissKeyboard() 65 | performSegue(withIdentifier: "resultsSegue", sender: sender) 66 | } 67 | 68 | // MARK: Table View Delegate 69 | 70 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 71 | return ingredients.count 72 | } 73 | 74 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 75 | let cell = tableView.dequeueReusableCell(withIdentifier: "ingredientCell") as! IngredientCell 76 | cell.ingredient = ingredients[indexPath.row] 77 | 78 | // Handle delete button action 79 | cell.deleteButton.layer.setValue(indexPath.row, forKey: "index") 80 | cell.deleteButton.addTarget(self, action: #selector(SearchTableView.deleteIngredient(sender:)), for: UIControlEvents.touchUpInside) 81 | 82 | return cell 83 | } 84 | 85 | // MARK: Text Field Delegate 86 | 87 | func textFieldShouldReturn(_ textField: UITextField) -> Bool { 88 | textField.resignFirstResponder() 89 | addIngreditent() 90 | return true 91 | } 92 | 93 | // MARK: DZNEMptyDataSet Delegate 94 | func image(forEmptyDataSet scrollView: UIScrollView!) -> UIImage! { 95 | return UIImage(named: "placeholder-search") 96 | } 97 | 98 | func description(forEmptyDataSet scrollView: UIScrollView!) -> NSAttributedString! { 99 | let message = "What do you have Left today?" 100 | let attribute = [NSForegroundColorAttributeName: UIColor.lightGray, NSFontAttributeName: UIFont(name: "HelveticaNeue", size: 22.0)!] 101 | return NSAttributedString(string: message, attributes: attribute) 102 | } 103 | 104 | 105 | // MARK: Helper 106 | 107 | func dismissKeyboard() { 108 | self.view.endEditing(true) 109 | textField.resignFirstResponder() 110 | } 111 | 112 | func searchButtonEnabled() { 113 | if ingredients.count > 0 { 114 | searchButton.isEnabled = true 115 | searchButton.backgroundColor = UIColor.LeftColor() 116 | } else { 117 | searchButton.isEnabled = false 118 | searchButton.backgroundColor = UIColor.lightGray 119 | } 120 | } 121 | 122 | func addIngreditent() { 123 | if let ingredient = textField.text { 124 | let trimmedIngredient = ingredient.trim() 125 | if !trimmedIngredient.isEmpty { 126 | self.ingredients.append(trimmedIngredient) 127 | self.tableView.reloadData() 128 | self.textField.text = "" 129 | self.textField.placeholder = textFieldPlaceholder 130 | dismissKeyboard() 131 | searchButtonEnabled() 132 | } 133 | } 134 | } 135 | 136 | func deleteIngredient(sender: UIButton) { 137 | let index: Int = (sender.layer.value(forKey: "index")) as! Int 138 | ingredients.remove(at: index) 139 | tableView.reloadData() 140 | dismissKeyboard() 141 | searchButtonEnabled() 142 | } 143 | } 144 | 145 | // MARK: UITableViewCell 146 | 147 | class IngredientCell: UITableViewCell { 148 | 149 | @IBOutlet weak var ingredientLabel: UILabel! 150 | @IBOutlet weak var deleteButton: UIButton! 151 | 152 | var ingredient: String! { 153 | didSet { 154 | self.ingredientLabel.text = ingredient 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /Left/View/RecipeCollectionCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LFTRecipeCollectionCell.swift 3 | // Left 4 | // 5 | // Created by Alejandrina Patron on 6/9/17. 6 | // Copyright © 2017 Ale Patrón. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class RecipeCollectionCell: UICollectionViewCell { 12 | 13 | @IBOutlet weak var actionButton: UIButton! 14 | @IBOutlet weak var recipePhoto: UIImageView! 15 | @IBOutlet weak var recipeName: UILabel! 16 | 17 | var recipe: RecipeItem! { 18 | didSet { 19 | recipeName.text = recipe.name 20 | recipePhoto.contentMode = UIViewContentMode.scaleAspectFill 21 | recipePhoto.clipsToBounds = true 22 | if recipe.photo != nil { 23 | recipePhoto.image = recipe.photo 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LeftTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /LeftTests/LeftTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LeftTests.swift 3 | // LeftTests 4 | // 5 | // Created by Alejandrina Patron on 9/18/15. 6 | // Copyright © 2015 A(pps)PL. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Left 11 | 12 | class LeftTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure // Put the code you want to measure the time of here. 32 | } 33 | } 34 | 35 | 36 | -------------------------------------------------------------------------------- /LeftUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /LeftUITests/LeftUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LeftUITests.swift 3 | // LeftUITests 4 | // 5 | // Created by Alejandrina Patron on 9/18/15. 6 | // Copyright © 2015 A(pps)PL. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | class LeftUITests: XCTestCase { 12 | 13 | override func setUp() { 14 | super.setUp() 15 | 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | 18 | // In UI tests it is usually best to stop immediately when a failure occurs. 19 | continueAfterFailure = false 20 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. 21 | XCUIApplication().launch() 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | override func tearDown() { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | super.tearDown() 29 | } 30 | 31 | func testExample() { 32 | // Use recording to get started writing UI tests. 33 | // Use XCTAssert and related functions to verify your tests produce the correct results. 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | platform :ios, ‘10.0’ 3 | use_frameworks! 4 | target 'Left' do 5 | pod 'Alamofire', '~> 4.0’ 6 | pod 'SwiftyJSON' 7 | pod 'AlamofireNetworkActivityIndicator', '~> 2.0’ 8 | pod 'UIScrollView-InfiniteScroll' 9 | pod 'Nuke', '~> 4.0' 10 | pod 'DZNEmptyDataSet' 11 | pod 'SwiftyDrop', '~>3.0' 12 | end 13 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.2.0) 3 | - AlamofireNetworkActivityIndicator (2.1.0): 4 | - Alamofire (~> 4.1) 5 | - DZNEmptyDataSet (1.8.1) 6 | - Nuke (4.1.2) 7 | - SwiftyDrop (3.0.3) 8 | - SwiftyJSON (3.1.3) 9 | - UIScrollView-InfiniteScroll (0.9.1) 10 | 11 | DEPENDENCIES: 12 | - Alamofire (~> 4.0) 13 | - AlamofireNetworkActivityIndicator (~> 2.0) 14 | - DZNEmptyDataSet 15 | - Nuke (~> 4.0) 16 | - SwiftyDrop (~> 3.0) 17 | - SwiftyJSON 18 | - UIScrollView-InfiniteScroll 19 | 20 | SPEC CHECKSUMS: 21 | Alamofire: aa2e09d871c9160ac53c90e83c68064a94e3dfbe 22 | AlamofireNetworkActivityIndicator: 4716f6554bdfb9c7cf13b1e0e3af21163129690a 23 | DZNEmptyDataSet: 9525833b9e68ac21c30253e1d3d7076cc828eaa7 24 | Nuke: 7cfd2acdf90c36f66e6ea40795bac0d851a9afe0 25 | SwiftyDrop: 785f95aabc26c4d832576bb78dc4ba7d0594a95b 26 | SwiftyJSON: 38a8ea2006779c0fc4c310cb2ee8195327740faf 27 | UIScrollView-InfiniteScroll: 14e204c50ca4040467e1cbfa141dad7a18919b56 28 | 29 | PODFILE CHECKSUM: 62ca1498b01d9b14899d0aff32d9cfc3d0ccce8e 30 | 31 | COCOAPODS: 1.2.0.beta.1 32 | -------------------------------------------------------------------------------- /Pods/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/Pods/.DS_Store -------------------------------------------------------------------------------- /Pods/Alamofire/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/DispatchQueue+Alamofire.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DispatchQueue+Alamofire.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Dispatch 26 | import Foundation 27 | 28 | extension DispatchQueue { 29 | static var userInteractive: DispatchQueue { return DispatchQueue.global(qos: .userInteractive) } 30 | static var userInitiated: DispatchQueue { return DispatchQueue.global(qos: .userInitiated) } 31 | static var utility: DispatchQueue { return DispatchQueue.global(qos: .utility) } 32 | static var background: DispatchQueue { return DispatchQueue.global(qos: .background) } 33 | 34 | func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) { 35 | asyncAfter(deadline: .now() + delay, execute: closure) 36 | } 37 | 38 | func syncResult(_ closure: () -> T) -> T { 39 | var result: T! 40 | sync { result = closure() } 41 | return result 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Notifications.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Notifications.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | extension Notification.Name { 28 | /// Used as a namespace for all `URLSessionTask` related notifications. 29 | public struct Task { 30 | /// Posted when a `URLSessionTask` is resumed. The notification `object` contains the resumed `URLSessionTask`. 31 | public static let DidResume = Notification.Name(rawValue: "org.alamofire.notification.name.task.didResume") 32 | 33 | /// Posted when a `URLSessionTask` is suspended. The notification `object` contains the suspended `URLSessionTask`. 34 | public static let DidSuspend = Notification.Name(rawValue: "org.alamofire.notification.name.task.didSuspend") 35 | 36 | /// Posted when a `URLSessionTask` is cancelled. The notification `object` contains the cancelled `URLSessionTask`. 37 | public static let DidCancel = Notification.Name(rawValue: "org.alamofire.notification.name.task.didCancel") 38 | 39 | /// Posted when a `URLSessionTask` is completed. The notification `object` contains the completed `URLSessionTask`. 40 | public static let DidComplete = Notification.Name(rawValue: "org.alamofire.notification.name.task.didComplete") 41 | } 42 | } 43 | 44 | // MARK: - 45 | 46 | extension Notification { 47 | /// Used as a namespace for all `Notification` user info dictionary keys. 48 | public struct Key { 49 | /// User info dictionary key representing the `URLSessionTask` associated with the notification. 50 | public static let Task = "org.alamofire.notification.key.task" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Pods/Alamofire/Source/Result.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Result.swift 3 | // 4 | // Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/) 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | // THE SOFTWARE. 23 | // 24 | 25 | import Foundation 26 | 27 | /// Used to represent whether a request was successful or encountered an error. 28 | /// 29 | /// - success: The request and all post processing operations were successful resulting in the serialization of the 30 | /// provided associated value. 31 | /// 32 | /// - failure: The request encountered an error resulting in a failure. The associated values are the original data 33 | /// provided by the server as well as the error that caused the failure. 34 | public enum Result { 35 | case success(Value) 36 | case failure(Error) 37 | 38 | /// Returns `true` if the result is a success, `false` otherwise. 39 | public var isSuccess: Bool { 40 | switch self { 41 | case .success: 42 | return true 43 | case .failure: 44 | return false 45 | } 46 | } 47 | 48 | /// Returns `true` if the result is a failure, `false` otherwise. 49 | public var isFailure: Bool { 50 | return !isSuccess 51 | } 52 | 53 | /// Returns the associated value if the result is a success, `nil` otherwise. 54 | public var value: Value? { 55 | switch self { 56 | case .success(let value): 57 | return value 58 | case .failure: 59 | return nil 60 | } 61 | } 62 | 63 | /// Returns the associated error value if the result is a failure, `nil` otherwise. 64 | public var error: Error? { 65 | switch self { 66 | case .success: 67 | return nil 68 | case .failure(let error): 69 | return error 70 | } 71 | } 72 | } 73 | 74 | // MARK: - CustomStringConvertible 75 | 76 | extension Result: CustomStringConvertible { 77 | /// The textual representation used when written to an output stream, which includes whether the result was a 78 | /// success or failure. 79 | public var description: String { 80 | switch self { 81 | case .success: 82 | return "SUCCESS" 83 | case .failure: 84 | return "FAILURE" 85 | } 86 | } 87 | } 88 | 89 | // MARK: - CustomDebugStringConvertible 90 | 91 | extension Result: CustomDebugStringConvertible { 92 | /// The debug textual representation used when written to an output stream, which includes whether the result was a 93 | /// success or failure in addition to the value or error. 94 | public var debugDescription: String { 95 | switch self { 96 | case .success(let value): 97 | return "SUCCESS: \(value)" 98 | case .failure(let error): 99 | return "FAILURE: \(error)" 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /Pods/AlamofireNetworkActivityIndicator/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Alamofire Software Foundation (http://alamofire.org/) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Pods/DZNEmptyDataSet/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Ignacio Romero Zurbuchen iromero@dzen.cl 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | -------------------------------------------------------------------------------- /Pods/Local Podspecs/SwiftyJSON.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SwiftyJSON", 3 | "version": "2.3.2", 4 | "summary": "SwiftyJSON makes it easy to deal with JSON data in Swift", 5 | "homepage": "https://github.com/SwiftyJSON/SwiftyJSON", 6 | "license": { 7 | "type": "MIT" 8 | }, 9 | "authors": { 10 | "lingoer": "lingoerer@gmail.com", 11 | "tangplin": "tangplin@gmail.com" 12 | }, 13 | "requires_arc": true, 14 | "platforms": { 15 | "osx": "10.9", 16 | "ios": "8.0", 17 | "watchos": "2.0", 18 | "tvos": "9.0" 19 | }, 20 | "source": { 21 | "git": "https://github.com/SwiftyJSON/SwiftyJSON.git", 22 | "tag": "2.3.2" 23 | }, 24 | "source_files": "Source/*.swift" 25 | } 26 | -------------------------------------------------------------------------------- /Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.2.0) 3 | - AlamofireNetworkActivityIndicator (2.1.0): 4 | - Alamofire (~> 4.1) 5 | - DZNEmptyDataSet (1.8.1) 6 | - Nuke (4.1.2) 7 | - SwiftyDrop (3.0.3) 8 | - SwiftyJSON (3.1.3) 9 | - UIScrollView-InfiniteScroll (0.9.1) 10 | 11 | DEPENDENCIES: 12 | - Alamofire (~> 4.0) 13 | - AlamofireNetworkActivityIndicator (~> 2.0) 14 | - DZNEmptyDataSet 15 | - Nuke (~> 4.0) 16 | - SwiftyDrop (~> 3.0) 17 | - SwiftyJSON 18 | - UIScrollView-InfiniteScroll 19 | 20 | SPEC CHECKSUMS: 21 | Alamofire: aa2e09d871c9160ac53c90e83c68064a94e3dfbe 22 | AlamofireNetworkActivityIndicator: 4716f6554bdfb9c7cf13b1e0e3af21163129690a 23 | DZNEmptyDataSet: 9525833b9e68ac21c30253e1d3d7076cc828eaa7 24 | Nuke: 7cfd2acdf90c36f66e6ea40795bac0d851a9afe0 25 | SwiftyDrop: 785f95aabc26c4d832576bb78dc4ba7d0594a95b 26 | SwiftyJSON: 38a8ea2006779c0fc4c310cb2ee8195327740faf 27 | UIScrollView-InfiniteScroll: 14e204c50ca4040467e1cbfa141dad7a18919b56 28 | 29 | PODFILE CHECKSUM: 62ca1498b01d9b14899d0aff32d9cfc3d0ccce8e 30 | 31 | COCOAPODS: 1.2.0.beta.1 32 | -------------------------------------------------------------------------------- /Pods/Nuke/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Alexander Grebenyuk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Pods/Nuke/Sources/CancellationToken.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Alexander Grebenyuk (github.com/kean). 4 | 5 | import Foundation 6 | 7 | /// Manages cancellation tokens and signals them when cancellation is requested. 8 | /// 9 | /// All `CancellationTokenSource` methods are thread safe. 10 | public final class CancellationTokenSource { 11 | public private(set) var isCancelling = false 12 | private var observers = [(Void) -> Void]() 13 | private let lock: Lock 14 | 15 | public var token: CancellationToken { 16 | return CancellationToken(source: self) 17 | } 18 | 19 | public init() { 20 | self.lock = Lock() 21 | } 22 | 23 | // Allows to create cts with a shared mutex to avoid excessive allocations. 24 | // This optimization gives you small wins in absolute numbers. It's also 25 | // tricky to get right thus `internal` access modifier. 26 | internal init(lock: Lock) { self.lock = lock } 27 | internal static let lock = Lock() 28 | 29 | fileprivate func register(_ closure: @escaping (Void) -> Void) { 30 | if isCancelling { closure(); return } // fast pre-lock check 31 | lock.sync { 32 | if isCancelling { 33 | closure() 34 | } else { 35 | observers.append(closure) 36 | } 37 | } 38 | } 39 | 40 | /// Communicates a request for cancellation to the managed token. 41 | public func cancel() { 42 | if isCancelling { return } // fast pre-lock check 43 | lock.sync { 44 | if !isCancelling { 45 | isCancelling = true 46 | observers.forEach { $0() } 47 | observers.removeAll() 48 | } 49 | } 50 | } 51 | } 52 | 53 | /// Enables cooperative cancellation of operations. 54 | /// 55 | /// You create a cancellation token by instantiating a `CancellationTokenSource` 56 | /// object and calling its `token` property. You then pass the token to any 57 | /// number of threads, tasks, or operations that should receive notice of 58 | /// cancellation. When the owning object calls `cancel()`, the `isCancelling` 59 | /// property on every copy of the cancellation token is set to `true`. 60 | /// The registered objects can respond in whatever manner is appropriate. 61 | /// 62 | /// All `CancellationToken` methods are thread safe. 63 | public struct CancellationToken { 64 | fileprivate let source: CancellationTokenSource 65 | 66 | /// Returns `true` if cancellation has been requested for this token. 67 | public var isCancelling: Bool { return source.isCancelling } 68 | 69 | /// Registers the closure that will be called when the token is canceled. 70 | /// If this token is already cancelled, the closure will be run immediately 71 | /// and synchronously. 72 | /// - warning: Make sure that you don't capture token inside a closure to 73 | /// avoid retain cycles. 74 | public func register(closure: @escaping (Void) -> Void) { source.register(closure) } 75 | } 76 | -------------------------------------------------------------------------------- /Pods/Nuke/Sources/DataDecoder.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Alexander Grebenyuk (github.com/kean). 4 | 5 | #if os(macOS) 6 | import Cocoa 7 | #else 8 | import UIKit 9 | #endif 10 | 11 | #if os(watchOS) 12 | import WatchKit 13 | #endif 14 | 15 | /// Decodes image data. 16 | public protocol DataDecoding { 17 | /// Decodes image data. 18 | func decode(data: Data, response: URLResponse) -> Image? 19 | } 20 | 21 | private let queue = DispatchQueue(label: "com.github.kean.Nuke.DataDecoder") 22 | 23 | /// Decodes image data. 24 | public struct DataDecoder: DataDecoding { 25 | /// Initializes the receiver. 26 | public init() {} 27 | 28 | /// Creates an `UIImage` (`NSImage` on macOS) with the given data. 29 | /// Image scale is set to the scale of the main screen. 30 | public func decode(data: Data, response: URLResponse) -> Image? { 31 | guard DataDecoder.validate(response: response) else { return nil } 32 | // Image initializers are not thread safe: 33 | // - https://github.com/AFNetworking/AFNetworking/issues/2572 34 | // - https://github.com/Alamofire/AlamofireImage/issues/75 35 | return queue.sync { 36 | #if os(macOS) 37 | return NSImage(data: data) 38 | #else 39 | #if os(iOS) || os(tvOS) 40 | let scale = UIScreen.main.scale 41 | #else 42 | let scale = WKInterfaceDevice.current().screenScale 43 | #endif 44 | return UIImage(data: data, scale: scale) 45 | #endif 46 | } 47 | } 48 | 49 | private static func validate(response: URLResponse) -> Bool { 50 | guard let response = response as? HTTPURLResponse else { return true } 51 | return (200..<300).contains(response.statusCode) 52 | } 53 | } 54 | 55 | /// Composes multiple data decoders. 56 | public final class DataDecoderComposition: DataDecoding { 57 | public let decoders: [DataDecoding] 58 | 59 | /// Composes multiple data decoders. 60 | public init(decoders: [DataDecoding]) { 61 | self.decoders = decoders 62 | } 63 | 64 | /// Decoders are applied in order in which they are present in the decoders 65 | /// array. The decoding stops when one of the decoders produces an image. 66 | public func decode(data: Data, response: URLResponse) -> Image? { 67 | for decoder in decoders { 68 | if let image = decoder.decode(data: data, response: response) { 69 | return image 70 | } 71 | } 72 | return nil 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Pods/Nuke/Sources/DataLoader.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Alexander Grebenyuk (github.com/kean). 4 | 5 | import Foundation 6 | 7 | /// Loads data. 8 | public protocol DataLoading { 9 | /// Loads data with the given request. 10 | func loadData(with request: URLRequest, token: CancellationToken?) -> Promise<(Data, URLResponse)> 11 | } 12 | 13 | /// Provides basic networking using `URLSession`. 14 | public final class DataLoader: DataLoading { 15 | public private(set) var session: URLSession 16 | private let scheduler: AsyncScheduler 17 | 18 | /// Initializes `DataLoader` with the given configuration. 19 | /// - parameter configuration: `URLSessionConfiguration.default` with 20 | /// `URLCache` with 0MB memory capacity and 200MB disk capacity. 21 | /// - parameter scheduler: `OperationQueueScheduler` with `maxConcurrentOperationCount` 8 by default. 22 | /// Scheduler is wrapped in a `RateLimiter` to prevent `URLSession` trashing. 23 | public init(configuration: URLSessionConfiguration = DataLoader.defaultConfiguration(), scheduler: AsyncScheduler = RateLimiter(scheduler: OperationQueueScheduler(maxConcurrentOperationCount: 8))) { 24 | self.session = URLSession(configuration: configuration, delegate: nil, delegateQueue: nil) 25 | self.scheduler = scheduler 26 | } 27 | 28 | private static func defaultConfiguration() -> URLSessionConfiguration { 29 | let conf = URLSessionConfiguration.default 30 | conf.urlCache = URLCache(memoryCapacity: 0, diskCapacity: (200 * 1024 * 1024), diskPath: "com.github.kean.Nuke.Cache") 31 | return conf 32 | } 33 | 34 | /// Loads data with the given request. 35 | public func loadData(with request: URLRequest, token: CancellationToken? = nil) -> Promise<(Data, URLResponse)> { 36 | return Promise() { fulfill, reject in 37 | scheduler.execute(token: token) { finish in 38 | let task = self.session.dataTask(with: request) { data, response, error in 39 | if let data = data, let response = response { 40 | fulfill((data, response)) 41 | } else { 42 | reject(error ?? NSError(domain: NSURLErrorDomain, code: NSURLErrorUnknown, userInfo: nil)) 43 | } 44 | finish() 45 | } 46 | token?.register { 47 | task.cancel() 48 | finish() 49 | } 50 | task.resume() 51 | } 52 | } 53 | } 54 | } 55 | 56 | /// Stores `CachedURLResponse` objects. 57 | public protocol DataCaching { 58 | /// Returns response for the given request. 59 | func response(for request: URLRequest, token: CancellationToken?) -> Promise 60 | 61 | /// Stores response for the given request. 62 | func setResponse(_ response: CachedURLResponse, for request: URLRequest) 63 | } 64 | 65 | public final class CachingDataLoader: DataLoading { 66 | private var loader: DataLoading 67 | private var cache: DataCaching 68 | 69 | public init(loader: DataLoading, cache: DataCaching) { 70 | self.loader = loader 71 | self.cache = cache 72 | } 73 | 74 | public func loadData(with request: URLRequest, token: CancellationToken?) -> Promise<(Data, URLResponse)> { 75 | return cache.response(for: request, token: token) 76 | .then { ($0.data, $0.response) } 77 | .recover { _ in 78 | self.loader.loadData(with: request, token: token).then { 79 | self.cache.setResponse(CachedURLResponse(response: $0.1, data: $0.0), for: request) 80 | } 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Pods/Nuke/Sources/Deduplicator.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Alexander Grebenyuk (github.com/kean). 4 | 5 | import Foundation 6 | 7 | /// Combines requests with the same `loadKey` into a single request. This request 8 | /// is only cancelled when all underlying requests are cancelled. 9 | /// 10 | /// All `Deduplicator` methods are thread-safe. 11 | public final class Deduplicator: Loading { 12 | private let loader: Loading 13 | private var tasks = [AnyHashable: Task]() 14 | private let queue = DispatchQueue(label: "com.github.kean.Nuke.Deduplicator") 15 | 16 | /// Initializes the `Deduplicator` instance with the underlying 17 | /// `loader` used for actual image loading, and the request `equator`. 18 | /// - parameter loader: Underlying loader used for loading images. 19 | public init(loader: Loading) { 20 | self.loader = loader 21 | } 22 | 23 | /// Returns an existing pending promise if there is one. Starts a new request otherwise. 24 | public func loadImage(with request: Request, token: CancellationToken? = nil) -> Promise { 25 | return queue.sync { 26 | let key = Request.loadKey(for: request) 27 | var task: Task! = tasks[key] // Find existing promise 28 | if task == nil { 29 | let cts = CancellationTokenSource() 30 | let promise = loader.loadImage(with: request, token: cts.token) 31 | task = Task(promise: promise, cts: cts) 32 | tasks[key] = task 33 | promise.completion(on: queue) { [weak self, weak task] _ in 34 | if let task = task { self?.remove(task, key: key) } 35 | } 36 | } else { 37 | task.retainCount += 1 38 | } 39 | 40 | token?.register { [weak self, weak task] in 41 | if let task = task { self?.cancel(task, key: key) } 42 | } 43 | 44 | return task.promise 45 | } 46 | } 47 | 48 | private func cancel(_ task: Task, key: AnyHashable) { 49 | queue.async { 50 | task.retainCount -= 1 51 | if task.retainCount == 0 { // No more requests registered 52 | task.cts.cancel() // Cancel underlying request 53 | self.remove(task, key: key) 54 | } 55 | } 56 | } 57 | 58 | private func remove(_ task: Task, key: AnyHashable) { 59 | if tasks[key] === task { // Still managed by Deduplicator 60 | tasks[key] = nil 61 | } 62 | } 63 | 64 | private final class Task { 65 | let promise: Promise 66 | let cts: CancellationTokenSource 67 | var retainCount = 1 68 | init(promise: Promise, cts: CancellationTokenSource) { 69 | self.promise = promise 70 | self.cts = cts 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Pods/Nuke/Sources/Loader.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Alexander Grebenyuk (github.com/kean). 4 | 5 | import Foundation 6 | 7 | /// Loads images. 8 | public protocol Loading { 9 | /// Loads an image with the given request. 10 | func loadImage(with request: Request, token: CancellationToken?) -> Promise 11 | } 12 | 13 | public extension Loading { 14 | /// Loads an image with the given URL. 15 | func loadImage(with url: URL, token: CancellationToken? = nil) -> Promise { 16 | return loadImage(with: Request(url: url), token: token) 17 | } 18 | } 19 | 20 | /// `Loader` implements an image loading pipeline which consists of the 21 | /// several steps: 22 | /// 23 | /// 1. Read an image from the memory cache (if cache isn't `nil`). If the image 24 | /// is found skip remaining steps. 25 | /// 2. Load data using an object conforming to `DataLoading` protocol. 26 | /// 3. Create an image with the data using `DataDecoding` object. 27 | /// 4. Transform the image using processor (`Processing`) provided in the request. 28 | /// 5. Save the image into the memory cache (if cache isn't `nil`). 29 | /// 30 | /// See built-in `CachingDataLoader` class if you need to add custom data cache 31 | /// into the pipeline. 32 | /// 33 | /// `Loader` is thread-safe. 34 | public final class Loader: Loading { 35 | public let loader: DataLoading 36 | public let decoder: DataDecoding 37 | public let cache: Caching? 38 | 39 | private let schedulers: Schedulers 40 | private let queue = DispatchQueue(label: "com.github.kean.Nuke.Loader") 41 | 42 | /// Returns a processor for the given image and request. Default 43 | /// implementation simply returns `request.processor`. 44 | public var makeProcessor: (Image, Request) -> AnyProcessor? = { 45 | return $1.processor 46 | } 47 | 48 | /// Initializes `Loader` instance with the given loader, decoder and cache. 49 | /// - parameter schedulers: `Schedulers()` by default. 50 | public init(loader: DataLoading, decoder: DataDecoding, cache: Caching?, schedulers: Schedulers = Schedulers()) { 51 | self.loader = loader 52 | self.decoder = decoder 53 | self.cache = cache 54 | self.schedulers = schedulers 55 | } 56 | 57 | /// Loads an image for the given request using image loading pipeline. 58 | public func loadImage(with request: Request, token: CancellationToken? = nil) -> Promise { 59 | return queue.sync { promise(with: request, token: token) } 60 | } 61 | 62 | public func promise(with request: Request, token: CancellationToken? = nil) -> Promise { 63 | if request.memoryCacheOptions.readAllowed, let image = cache?[request] { 64 | return Promise(value: image) 65 | } 66 | // It's safe to capture `Loader` in promises 67 | return loader.loadData(with: request.urlRequest, token: token) 68 | .then(on: queue) { self.decode(data: $0, response: $1, token: token) } 69 | .then(on: queue) { self.process(image: $0, request: request, token: token) } 70 | .then(on: queue) { 71 | if request.memoryCacheOptions.writeAllowed { 72 | self.cache?[request] = $0 73 | } 74 | } 75 | } 76 | 77 | private func decode(data: Data, response: URLResponse, token: CancellationToken? = nil) -> Promise { 78 | return Promise() { fulfill, reject in 79 | schedulers.decoding.execute(token: token) { 80 | if let image = self.decoder.decode(data: data, response: response) { 81 | fulfill(image) 82 | } else { 83 | reject(Error.decodingFailed) 84 | } 85 | } 86 | } 87 | } 88 | 89 | private func process(image: Image, request: Request, token: CancellationToken?) -> Promise { 90 | guard let processor = makeProcessor(image, request) else { return Promise(value: image) } 91 | return Promise() { fulfill, reject in 92 | schedulers.processing.execute(token: token) { 93 | if let image = processor.process(image) { 94 | fulfill(image) 95 | } else { 96 | reject(Error.processingFailed) 97 | } 98 | } 99 | } 100 | } 101 | 102 | /// Schedulers used to execute a corresponding steps of the pipeline. 103 | public struct Schedulers { 104 | /// `DispatchQueueScheduler` with a serial queue by default. 105 | public var decoding: Scheduler = DispatchQueueScheduler(queue: DispatchQueue(label: "com.github.kean.Nuke.Decoding")) 106 | // There is no reason to increase `maxConcurrentOperationCount` for 107 | // built-in `DataDecoder` that locks globally while decoding. 108 | 109 | /// `DispatchQueueScheduler` with a serial queue by default. 110 | public var processing: Scheduler = DispatchQueueScheduler(queue: DispatchQueue(label: "com.github.kean.Nuke.Processing")) 111 | } 112 | 113 | /// Error returns by `Loader` class itself. `Loader` might also return 114 | /// errors from underlying `DataLoading` object. 115 | public enum Error: Swift.Error { 116 | case decodingFailed 117 | case processingFailed 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /Pods/Nuke/Sources/Nuke.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Alexander Grebenyuk (github.com/kean). 4 | 5 | import Foundation 6 | 7 | #if os(macOS) 8 | import AppKit.NSImage 9 | /// Alias for NSImage 10 | public typealias Image = NSImage 11 | #else 12 | import UIKit.UIImage 13 | /// Alias for UIImage 14 | public typealias Image = UIImage 15 | #endif 16 | 17 | /// Loads an image into the given target. 18 | /// 19 | /// For more info see `loadImage(with:into:)` method of `Manager`. 20 | public func loadImage(with url: URL, into target: Target) { 21 | Manager.shared.loadImage(with: url, into: target) 22 | } 23 | 24 | /// Loads an image into the given target. 25 | /// 26 | /// For more info see `loadImage(with:into:)` method of `Manager`. 27 | public func loadImage(with request: Request, into target: Target) { 28 | Manager.shared.loadImage(with: request, into: target) 29 | } 30 | 31 | /// Loads an image and calls the given `handler`. The method itself 32 | /// **doesn't do** anything when the image is loaded - you have full 33 | /// control over how to display it, etc. 34 | /// 35 | /// The handler only gets called if the request is still associated with the 36 | /// `target` by the time it's completed. 37 | /// 38 | /// See `loadImage(with:into:)` method for more info. 39 | public func loadImage(with url: URL, into target: AnyObject, handler: @escaping Manager.Handler) { 40 | Manager.shared.loadImage(with: url, into: target, handler: handler) 41 | } 42 | 43 | /// Loads an image and calls the given `handler`. 44 | /// 45 | /// For more info see `loadImage(with:into:handler:)` method of `Manager`. 46 | public func loadImage(with request: Request, into target: AnyObject, handler: @escaping Manager.Handler) { 47 | Manager.shared.loadImage(with: request, into: target, handler: handler) 48 | } 49 | 50 | /// Cancels an outstanding request associated with the target. 51 | public func cancelRequest(for target: AnyObject) { 52 | Manager.shared.cancelRequest(for: target) 53 | } 54 | 55 | public extension Manager { 56 | /// Shared `Manager` instance. 57 | /// 58 | /// Shared manager is created with `Loader.shared` and `Cache.shared`. 59 | public static var shared = Manager(loader: Loader.shared, cache: Cache.shared) 60 | } 61 | 62 | public extension Loader { 63 | /// Shared `Loading` object. 64 | /// 65 | /// Shared loader is created with `DataLoader()`, `DataDecoder()` and 66 | // `Cache.shared`. The resulting loader is wrapped in a `Deduplicator`. 67 | public static var shared: Loading = Deduplicator(loader: Loader(loader: DataLoader(), decoder: DataDecoder(), cache: Cache.shared)) 68 | } 69 | 70 | public extension Cache { 71 | /// Shared `Cache` instance. 72 | public static var shared = Cache() 73 | } 74 | 75 | internal final class Lock { 76 | var mutex = UnsafeMutablePointer.allocate(capacity: 1) 77 | 78 | init() { 79 | pthread_mutex_init(mutex, nil) 80 | } 81 | 82 | deinit { 83 | pthread_mutex_destroy(mutex) 84 | mutex.deinitialize() 85 | mutex.deallocate(capacity: 1) 86 | } 87 | 88 | /// In critical places it's better to use lock() and unlock() manually 89 | func sync(_ closure: (Void) -> T) -> T { 90 | pthread_mutex_lock(mutex) 91 | defer { pthread_mutex_unlock(mutex) } 92 | return closure() 93 | } 94 | 95 | func lock() { pthread_mutex_lock(mutex) } 96 | func unlock() { pthread_mutex_unlock(mutex) } 97 | } 98 | -------------------------------------------------------------------------------- /Pods/Nuke/Sources/Preheater.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Alexander Grebenyuk (github.com/kean). 4 | 5 | import Foundation 6 | 7 | /// Prefetches and caches image in order to eliminate delays when you request 8 | /// individual images later. 9 | /// 10 | /// To start preheating call `startPreheating(with:)` method. When you 11 | /// need an individual image just start loading an image using `Loading` object. 12 | /// When preheating is no longer necessary call `stopPreheating(with:)` method. 13 | /// 14 | /// All `Preheater` methods are thread-safe. 15 | public final class Preheater { 16 | private let loader: Loading 17 | private let scheduler: AsyncScheduler 18 | private let queue = DispatchQueue(label: "com.github.kean.Nuke.Preheater") 19 | private var tasks = [Task]() 20 | 21 | /// Initializes the `Preheater` instance. 22 | /// - parameter loader: `Loader.shared` by default. 23 | /// - parameter scheduler: Throttles preheating requests. `OperationQueueScheduler` 24 | /// with `maxConcurrentOperationCount` 2 by default. 25 | public init(loader: Loading = Loader.shared, scheduler: AsyncScheduler = OperationQueueScheduler(maxConcurrentOperationCount: 2)) { 26 | self.loader = loader 27 | self.scheduler = scheduler 28 | } 29 | 30 | /// Preheats images for the given requests. 31 | /// 32 | /// When you call this method, `Preheater` starts to load and cache images 33 | /// for the given requests. At any time afterward, you can create tasks 34 | /// for individual images with equivalent requests. 35 | public func startPreheating(with requests: [Request]) { 36 | queue.async { 37 | requests.forEach { self.startPreheating(with: $0) } 38 | } 39 | } 40 | 41 | private func startPreheating(with request: Request) { 42 | // FIXME: use OrderedSet when Swift stdlib has one 43 | guard indexOfTask(with: request) == nil else { return } // already exists 44 | 45 | let task = Task(request: request) 46 | scheduler.execute(token: task.cts.token) { [weak self] finish in 47 | self?.loader.loadImage(with: task.request, token: task.cts.token).completion { _ in 48 | self?.complete(task) 49 | finish() 50 | } 51 | task.cts.token.register { finish() } 52 | } 53 | tasks.append(task) 54 | } 55 | 56 | private func complete(_ task: Task) { 57 | queue.async { 58 | if let idx = self.tasks.index(where: { task === $0 }) { 59 | self.tasks.remove(at: idx) 60 | } 61 | } 62 | } 63 | 64 | /// Stops preheating images for the given requests and cancels outstanding 65 | /// requests. 66 | public func stopPreheating(with requests: [Request]) { 67 | queue.async { 68 | requests.forEach { request in 69 | if let index = self.indexOfTask(with: request) { 70 | let task = self.tasks.remove(at: index) 71 | task.cts.cancel() 72 | } 73 | } 74 | } 75 | } 76 | 77 | private func indexOfTask(with request: Request) -> Int? { 78 | let key = Request.loadKey(for: request) 79 | return tasks.index { key == Request.loadKey(for: $0.request) } 80 | } 81 | 82 | /// Stops all preheating tasks. 83 | public func stopPreheating() { 84 | queue.async { 85 | self.tasks.forEach { $0.cts.cancel() } 86 | self.tasks.removeAll() 87 | } 88 | } 89 | 90 | private final class Task { 91 | let request: Request 92 | var cts = CancellationTokenSource() 93 | init(request: Request) { 94 | self.request = request 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Pods/Nuke/Sources/Processor.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2016 Alexander Grebenyuk (github.com/kean). 4 | 5 | import Foundation 6 | 7 | /// Performs image processing. 8 | public protocol Processing: Equatable { 9 | /// Returns processed image. 10 | func process(_ image: Image) -> Image? 11 | } 12 | 13 | /// Composes multiple processors. 14 | public struct ProcessorComposition: Processing { 15 | private let processors: [AnyProcessor] 16 | 17 | /// Composes multiple processors. 18 | public init(_ processors: [AnyProcessor]) { 19 | self.processors = processors 20 | } 21 | 22 | /// Processes the given image by applying each processor in an order in 23 | /// which they were added. If one of the processors fails to produce 24 | /// an image the processing stops and `nil` is returned. 25 | public func process(_ input: Image) -> Image? { 26 | return processors.reduce(input as Image!) { image, processor in 27 | return autoreleasepool { image != nil ? processor.process(image!) : nil } 28 | } 29 | } 30 | 31 | /// Returns true if the underlying processors are pairwise-equivalent. 32 | public static func ==(lhs: ProcessorComposition, rhs: ProcessorComposition) -> Bool { 33 | return lhs.processors.elementsEqual(rhs.processors) 34 | } 35 | } 36 | 37 | /// Type-erased image processor. 38 | public struct AnyProcessor: Processing { 39 | private let _process: (Image) -> Image? 40 | private let _processor: Any 41 | private let _equals: (AnyProcessor) -> Bool 42 | 43 | public init(_ processor: P) { 44 | self._process = { processor.process($0) } 45 | self._processor = processor 46 | self._equals = { ($0._processor as? P) == processor } 47 | } 48 | 49 | public func process(_ image: Image) -> Image? { 50 | return self._process(image) 51 | } 52 | 53 | public static func ==(lhs: AnyProcessor, rhs: AnyProcessor) -> Bool { 54 | return lhs._equals(rhs) 55 | } 56 | } 57 | 58 | #if !os(macOS) 59 | 60 | import UIKit 61 | 62 | /// Decompresses and (optionally) scales down input images. Maintains 63 | /// original aspect ratio. 64 | /// 65 | /// Images are decompressed and scaled in a single pass which is extremely 66 | /// efficient when scaling images down by a large factor. 67 | public struct Decompressor: Processing { 68 | public enum ContentMode { 69 | /// Scales the image so that it completely fills the target size. 70 | /// Doesn't clip images. 71 | case aspectFill 72 | 73 | /// Scales the image so that it fits the target size. 74 | case aspectFit 75 | } 76 | 77 | /// Size to pass to disable resizing. 78 | public static let MaximumSize = CGSize(width: CGFloat.greatestFiniteMagnitude, height: CGFloat.greatestFiniteMagnitude) 79 | 80 | private let targetSize: CGSize 81 | private let contentMode: ContentMode 82 | 83 | /// Initializes `Decompressor` with the given parameters. 84 | /// - parameter targetSize: Size in pixels. `MaximumSize` by default. 85 | /// - parameter contentMode: An option for how to resize the image 86 | /// to the target size. `.aspectFill` by default. 87 | public init(targetSize: CGSize = MaximumSize, contentMode: ContentMode = .aspectFill) { 88 | self.targetSize = targetSize 89 | self.contentMode = contentMode 90 | } 91 | 92 | /// Decompresses and scales the image. 93 | public func process(_ image: Image) -> Image? { 94 | return decompress(image, targetSize: targetSize, contentMode: contentMode) 95 | } 96 | 97 | /// Returns true if both have the same `targetSize` and `contentMode`. 98 | public static func ==(lhs: Decompressor, rhs: Decompressor) -> Bool { 99 | return lhs.targetSize == rhs.targetSize && lhs.contentMode == rhs.contentMode 100 | } 101 | } 102 | 103 | private func decompress(_ image: UIImage, targetSize: CGSize, contentMode: Decompressor.ContentMode) -> UIImage { 104 | guard let cgImage = image.cgImage else { return image } 105 | let bitmapSize = CGSize(width: cgImage.width, height: cgImage.height) 106 | let scaleHor = targetSize.width / bitmapSize.width 107 | let scaleVert = targetSize.height / bitmapSize.height 108 | let scale = contentMode == .aspectFill ? max(scaleHor, scaleVert) : min(scaleHor, scaleVert) 109 | return decompress(image, scale: CGFloat(min(scale, 1))) 110 | } 111 | 112 | private func decompress(_ image: UIImage, scale: CGFloat) -> UIImage { 113 | guard let cgImage = image.cgImage else { return image } 114 | 115 | let size = CGSize(width: round(scale * CGFloat(cgImage.width)), 116 | height: round(scale * CGFloat(cgImage.height))) 117 | 118 | // For more info see: 119 | // - Quartz 2D Programming Guide 120 | // - https://github.com/kean/Nuke/issues/35 121 | // - https://github.com/kean/Nuke/issues/57 122 | let alphaInfo: CGImageAlphaInfo = isOpaque(cgImage) ? .noneSkipLast : .premultipliedLast 123 | 124 | guard let ctx = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: 8, bytesPerRow: 0, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: alphaInfo.rawValue) else { 125 | return image 126 | } 127 | ctx.draw(cgImage, in: CGRect(origin: CGPoint.zero, size: size)) 128 | guard let decompressed = ctx.makeImage() else { return image } 129 | return UIImage(cgImage: decompressed, scale: image.scale, orientation: image.imageOrientation) 130 | } 131 | 132 | private func isOpaque(_ image: CGImage) -> Bool { 133 | let alpha = image.alphaInfo 134 | return alpha == .none || alpha == .noneSkipFirst || alpha == .noneSkipLast 135 | } 136 | #endif 137 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/ale_patron.xcuserdatad/xcschemes/Alamofire.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/ale_patron.xcuserdatad/xcschemes/AlamofireNetworkActivityIndicator.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/ale_patron.xcuserdatad/xcschemes/DZNEmptyDataSet.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/ale_patron.xcuserdatad/xcschemes/Nuke.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/ale_patron.xcuserdatad/xcschemes/Pods-Left.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 66 | 67 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/ale_patron.xcuserdatad/xcschemes/SwiftyDrop.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/ale_patron.xcuserdatad/xcschemes/SwiftyJSON.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 45 | 46 | 52 | 53 | 55 | 56 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/ale_patron.xcuserdatad/xcschemes/UIScrollView-InfiniteScroll.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 66 | 67 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Pods/Pods.xcodeproj/xcuserdata/ale_patron.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | Alamofire.xcscheme 8 | 9 | isShown 10 | 11 | 12 | AlamofireNetworkActivityIndicator.xcscheme 13 | 14 | isShown 15 | 16 | 17 | DZNEmptyDataSet.xcscheme 18 | 19 | isShown 20 | 21 | 22 | Nuke.xcscheme 23 | 24 | isShown 25 | 26 | 27 | Pods-Left.xcscheme 28 | 29 | isShown 30 | 31 | 32 | SwiftyDrop.xcscheme 33 | 34 | isShown 35 | 36 | 37 | SwiftyJSON.xcscheme 38 | 39 | isShown 40 | 41 | 42 | UIScrollView-InfiniteScroll.xcscheme 43 | 44 | isShown 45 | 46 | 47 | 48 | SuppressBuildableAutocreation 49 | 50 | 115FA0D867E07C04838493890DBD8D29 51 | 52 | primary 53 | 54 | 55 | 3E9EB2CF4768F4548F3E209CE656A807 56 | 57 | primary 58 | 59 | 60 | 4621C3F9BE4863F63014320D83FFE6F4 61 | 62 | primary 63 | 64 | 65 | 636D2349F190F866F5DE2A564FA53AD3 66 | 67 | primary 68 | 69 | 70 | 88E9EC28B8B46C3631E6B242B50F4442 71 | 72 | primary 73 | 74 | 75 | 9B72E0CDBAB7245D7233A3D60016E7A4 76 | 77 | primary 78 | 79 | 80 | B2E810A917626E6EC56DBE5AC15A1308 81 | 82 | primary 83 | 84 | 85 | D386C75EFD4F03725E4EFB866B600B9A 86 | 87 | primary 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /Pods/SwiftyDrop/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Morita Naoki 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Pods/SwiftyDrop/README.md: -------------------------------------------------------------------------------- 1 | # SwiftyDrop 2 | 3 | SwiftyDrop is a lightweight pure Swift simple and beautiful dropdown message. 4 | 5 | [![Version](https://img.shields.io/cocoapods/v/SwiftyDrop.svg?style=flat)](http://cocoadocs.org/docsets/SwiftyDrop) [![License](https://img.shields.io/cocoapods/l/SwiftyDrop.svg?style=flat)](http://cocoadocs.org/docsets/SwiftyDrop) [![Platform](https://img.shields.io/cocoapods/p/SwiftyDrop.svg?style=flat)](http://cocoadocs.org/docsets/SwiftyDrop) 6 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 7 | [![Join the chat at https://gitter.im/morizotter/SwiftyDrop](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/morizotter/SwiftyDrop?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 8 | 9 | ## Features 10 | 11 | - Easy to use like: `Drop.down("Message")` 12 | - Message field expands depending on the message. 13 | 14 | ## How it looks 15 | 16 | ### States 17 | ![Default](misc/Default.png) 18 | ![Success](misc/Success.png) 19 | ![Warning](misc/Warning.png) 20 | ![Error](misc/Error.png) 21 | 22 | ### Blurs 23 | ![Light](misc/Light.png) 24 | ![Dark](misc/Dark.png) 25 | 26 | ### iPad 27 | ![iPad](misc/iPadPortlait.png) 28 | 29 | ## Demo 30 | 31 | Git clone or download this repository and open **SwiftyDrop.xcodeproj** . You can try SwiftyDrop in your Mac or iPhone. 32 | 33 | ## Runtime Requirements 34 | 35 | - iOS8.0 or later 36 | - Xcode 7.0 37 | 38 | ## Installation and Setup 39 | 40 | **Note:** Embedded frameworks require a minimum deployment target of iOS 8.1. 41 | 42 | **Information:** To use SwiftyDrop with a project targeting iOS 8.0 or lower, you must include the `SwiftyDrop/Drop.swift` source file directly in your project. 43 | 44 | ###Installing with CocoaPods 45 | 46 | [CocoaPods](http://cocoapods.org) is a centralised dependency manager that automates the process of adding libraries to your Cocoa application. You can install it with the following command: 47 | 48 | ```bash 49 | $ gem update 50 | $ gem install cocoapods 51 | $ pods --version 52 | ``` 53 | 54 | To integrate SwiftyDrop into your Xcode project using CocoaPods, specify it in your `Podfile` and run `pod install`. 55 | 56 | ```bash 57 | platform :ios, '8.0' 58 | use_frameworks! 59 | pod 'SwiftyDrop', '~>2.0' 60 | ``` 61 | 62 | ###Installing with Carthage 63 | 64 | Just add to your Cartfile: 65 | 66 | ```ogdl 67 | github "morizotter/SwiftyDrop" ~> 2.0 68 | ``` 69 | 70 | ###Manual Installation 71 | 72 | To install SwiftyDrop without a dependency manager, please add all of the files in `/SwiftyDrop` to your Xcode Project. 73 | 74 | ##Usage 75 | 76 | ### Basic 77 | 78 | To start using SwiftyDrop, write the following line wherever you want to show dropdown message: 79 | 80 | ```swift 81 | import SwiftyDrop 82 | ``` 83 | 84 | Then invoke SwiftyDrop, by calling: 85 | 86 | ```swift 87 | Drop.down("Message") 88 | ``` 89 | 90 | It is really simple! 91 | 92 | ### States 93 | 94 | SwiftyDrop has states of display. 95 | 96 | **Examples** 97 | 98 | ```swift 99 | Drop.down("Message") 100 | Drop.down("Message", state: .Success) 101 | Drop.down("Message", state: .Color(.orangeColor())) 102 | Drop.down("Message", state: .Blur(.Light)) 103 | ``` 104 | 105 | **Custom states** 106 | 107 | You can customize looks by comforming `DropStatable` protocol. 108 | Examples are: 109 | 110 | ```swift 111 | enum Custom: DropStatable { 112 | case BlackGreen 113 | var backgroundColor: UIColor? { 114 | switch self { 115 | case .BlackGreen: return .blackColor() 116 | } 117 | } 118 | var font: UIFont? { 119 | switch self { 120 | case .BlackGreen: return UIFont(name: "HelveticaNeue-Light", size: 24.0) 121 | } 122 | } 123 | var textColor: UIColor? { 124 | switch self { 125 | case .BlackGreen: return .greenColor() 126 | } 127 | } 128 | var blurEffect: UIBlurEffect? { 129 | switch self { 130 | case .BlackGreen: return nil 131 | } 132 | } 133 | } 134 | 135 | Drop.down(self.sampleText(), state: Custom.BlackGreen) 136 | ``` 137 | 138 | Of course you can use class or struct to make custom state if it is comforming to `DropStatable` protocol. 139 | 140 | **Prepared States** are enum: 141 | - .Default 142 | - .Info 143 | - .Success 144 | - .Warning 145 | - .Error 146 | - .Color: UIColor 147 | - .Blur: UIBlurEffectStyle 148 | 149 | ### Duration 150 | 151 | ```swift 152 | Drop.down("Message", duration: 3.0) 153 | ``` 154 | 155 | You can change duration like this above. Default duration is 4.0. 156 | 157 | ### Action 158 | 159 | ```swift 160 | Drop.down("Message") { 161 | print("Action fired!") 162 | } 163 | ``` 164 | 165 | ## Contribution 166 | 167 | Please file issues or submit pull requests! We're waiting! :) 168 | 169 | ## License 170 | 171 | SwiftyDrop is released under the MIT license. Go read the LICENSE file for more information. 172 | -------------------------------------------------------------------------------- /Pods/SwiftyJSON/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Ruoyu Fu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Alamofire : NSObject 3 | @end 4 | @implementation PodsDummy_Alamofire 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double AlamofireVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char AlamofireVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire.modulemap: -------------------------------------------------------------------------------- 1 | framework module Alamofire { 2 | umbrella header "Alamofire-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Alamofire.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Alamofire 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Alamofire 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Alamofire/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 4.2.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/AlamofireNetworkActivityIndicator/AlamofireNetworkActivityIndicator-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_AlamofireNetworkActivityIndicator : NSObject 3 | @end 4 | @implementation PodsDummy_AlamofireNetworkActivityIndicator 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/AlamofireNetworkActivityIndicator/AlamofireNetworkActivityIndicator-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/AlamofireNetworkActivityIndicator/AlamofireNetworkActivityIndicator-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double AlamofireNetworkActivityIndicatorVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char AlamofireNetworkActivityIndicatorVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/AlamofireNetworkActivityIndicator/AlamofireNetworkActivityIndicator.modulemap: -------------------------------------------------------------------------------- 1 | framework module AlamofireNetworkActivityIndicator { 2 | umbrella header "AlamofireNetworkActivityIndicator-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/AlamofireNetworkActivityIndicator/AlamofireNetworkActivityIndicator.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/AlamofireNetworkActivityIndicator 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 5 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 6 | PODS_BUILD_DIR = $BUILD_DIR 7 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 8 | PODS_ROOT = ${SRCROOT} 9 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/AlamofireNetworkActivityIndicator 10 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 11 | SKIP_INSTALL = YES 12 | -------------------------------------------------------------------------------- /Pods/Target Support Files/AlamofireNetworkActivityIndicator/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 2.1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/DZNEmptyDataSet/DZNEmptyDataSet-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_DZNEmptyDataSet : NSObject 3 | @end 4 | @implementation PodsDummy_DZNEmptyDataSet 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/DZNEmptyDataSet/DZNEmptyDataSet-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/DZNEmptyDataSet/DZNEmptyDataSet-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "UIScrollView+EmptyDataSet.h" 14 | 15 | FOUNDATION_EXPORT double DZNEmptyDataSetVersionNumber; 16 | FOUNDATION_EXPORT const unsigned char DZNEmptyDataSetVersionString[]; 17 | 18 | -------------------------------------------------------------------------------- /Pods/Target Support Files/DZNEmptyDataSet/DZNEmptyDataSet.modulemap: -------------------------------------------------------------------------------- 1 | framework module DZNEmptyDataSet { 2 | umbrella header "DZNEmptyDataSet-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/DZNEmptyDataSet/DZNEmptyDataSet.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/DZNEmptyDataSet 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_LDFLAGS = -framework "UIKit" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/DZNEmptyDataSet 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | -------------------------------------------------------------------------------- /Pods/Target Support Files/DZNEmptyDataSet/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.8.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Nuke/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 4.1.2 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Nuke/Nuke-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Nuke : NSObject 3 | @end 4 | @implementation PodsDummy_Nuke 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Nuke/Nuke-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Nuke/Nuke-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double NukeVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char NukeVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Nuke/Nuke.modulemap: -------------------------------------------------------------------------------- 1 | framework module Nuke { 2 | umbrella header "Nuke-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Nuke/Nuke.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/Nuke 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/Nuke 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Left/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Left/Pods-Left-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_Left : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_Left 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Left/Pods-Left-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 5 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 6 | 7 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 8 | 9 | install_framework() 10 | { 11 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 12 | local source="${BUILT_PRODUCTS_DIR}/$1" 13 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 14 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 15 | elif [ -r "$1" ]; then 16 | local source="$1" 17 | fi 18 | 19 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 20 | 21 | if [ -L "${source}" ]; then 22 | echo "Symlinked..." 23 | source="$(readlink "${source}")" 24 | fi 25 | 26 | # use filter instead of exclude so missing patterns dont' throw errors 27 | echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 28 | rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 29 | 30 | local basename 31 | basename="$(basename -s .framework "$1")" 32 | binary="${destination}/${basename}.framework/${basename}" 33 | if ! [ -r "$binary" ]; then 34 | binary="${destination}/${basename}" 35 | fi 36 | 37 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 38 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 39 | strip_invalid_archs "$binary" 40 | fi 41 | 42 | # Resign the code if required by the build settings to avoid unstable apps 43 | code_sign_if_enabled "${destination}/$(basename "$1")" 44 | 45 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 46 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 47 | local swift_runtime_libs 48 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 49 | for lib in $swift_runtime_libs; do 50 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 51 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 52 | code_sign_if_enabled "${destination}/${lib}" 53 | done 54 | fi 55 | } 56 | 57 | # Signs a framework with the provided identity 58 | code_sign_if_enabled() { 59 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 60 | # Use the current code_sign_identitiy 61 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 62 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS} --preserve-metadata=identifier,entitlements "$1"" 63 | 64 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 65 | code_sign_cmd="$code_sign_cmd &" 66 | fi 67 | echo "$code_sign_cmd" 68 | eval "$code_sign_cmd" 69 | fi 70 | } 71 | 72 | # Strip invalid architectures 73 | strip_invalid_archs() { 74 | binary="$1" 75 | # Get architectures for current file 76 | archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" 77 | stripped="" 78 | for arch in $archs; do 79 | if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then 80 | # Strip non-valid architectures in-place 81 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 82 | stripped="$stripped $arch" 83 | fi 84 | done 85 | if [[ "$stripped" ]]; then 86 | echo "Stripped $binary of architectures:$stripped" 87 | fi 88 | } 89 | 90 | 91 | if [[ "$CONFIGURATION" == "Debug" ]]; then 92 | install_framework "$BUILT_PRODUCTS_DIR/Alamofire/Alamofire.framework" 93 | install_framework "$BUILT_PRODUCTS_DIR/AlamofireNetworkActivityIndicator/AlamofireNetworkActivityIndicator.framework" 94 | install_framework "$BUILT_PRODUCTS_DIR/DZNEmptyDataSet/DZNEmptyDataSet.framework" 95 | install_framework "$BUILT_PRODUCTS_DIR/Nuke/Nuke.framework" 96 | install_framework "$BUILT_PRODUCTS_DIR/SwiftyDrop/SwiftyDrop.framework" 97 | install_framework "$BUILT_PRODUCTS_DIR/SwiftyJSON/SwiftyJSON.framework" 98 | install_framework "$BUILT_PRODUCTS_DIR/UIScrollView-InfiniteScroll/UIScrollView_InfiniteScroll.framework" 99 | fi 100 | if [[ "$CONFIGURATION" == "Release" ]]; then 101 | install_framework "$BUILT_PRODUCTS_DIR/Alamofire/Alamofire.framework" 102 | install_framework "$BUILT_PRODUCTS_DIR/AlamofireNetworkActivityIndicator/AlamofireNetworkActivityIndicator.framework" 103 | install_framework "$BUILT_PRODUCTS_DIR/DZNEmptyDataSet/DZNEmptyDataSet.framework" 104 | install_framework "$BUILT_PRODUCTS_DIR/Nuke/Nuke.framework" 105 | install_framework "$BUILT_PRODUCTS_DIR/SwiftyDrop/SwiftyDrop.framework" 106 | install_framework "$BUILT_PRODUCTS_DIR/SwiftyJSON/SwiftyJSON.framework" 107 | install_framework "$BUILT_PRODUCTS_DIR/UIScrollView-InfiniteScroll/UIScrollView_InfiniteScroll.framework" 108 | fi 109 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 110 | wait 111 | fi 112 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Left/Pods-Left-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | case "${TARGETED_DEVICE_FAMILY}" in 12 | 1,2) 13 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 14 | ;; 15 | 1) 16 | TARGET_DEVICE_ARGS="--target-device iphone" 17 | ;; 18 | 2) 19 | TARGET_DEVICE_ARGS="--target-device ipad" 20 | ;; 21 | 3) 22 | TARGET_DEVICE_ARGS="--target-device tv" 23 | ;; 24 | *) 25 | TARGET_DEVICE_ARGS="--target-device mac" 26 | ;; 27 | esac 28 | 29 | install_resource() 30 | { 31 | if [[ "$1" = /* ]] ; then 32 | RESOURCE_PATH="$1" 33 | else 34 | RESOURCE_PATH="${PODS_ROOT}/$1" 35 | fi 36 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 37 | cat << EOM 38 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 39 | EOM 40 | exit 1 41 | fi 42 | case $RESOURCE_PATH in 43 | *.storyboard) 44 | 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}" 45 | 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} 46 | ;; 47 | *.xib) 48 | 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}" 49 | 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} 50 | ;; 51 | *.framework) 52 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 53 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 54 | echo "rsync -av $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 55 | rsync -av "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 56 | ;; 57 | *.xcdatamodel) 58 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" 59 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 60 | ;; 61 | *.xcdatamodeld) 62 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" 63 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 64 | ;; 65 | *.xcmappingmodel) 66 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" 67 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 68 | ;; 69 | *.xcassets) 70 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 71 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 72 | ;; 73 | *) 74 | echo "$RESOURCE_PATH" 75 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 76 | ;; 77 | esac 78 | } 79 | 80 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 81 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 82 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 83 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 84 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 85 | fi 86 | rm -f "$RESOURCES_TO_COPY" 87 | 88 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 89 | then 90 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 91 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 92 | while read line; do 93 | if [[ $line != "${PODS_ROOT}*" ]]; then 94 | XCASSET_FILES+=("$line") 95 | fi 96 | done <<<"$OTHER_XCASSETS" 97 | 98 | 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}" 99 | fi 100 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Left/Pods-Left-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_LeftVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_LeftVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Left/Pods-Left.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" "$PODS_CONFIGURATION_BUILD_DIR/AlamofireNetworkActivityIndicator" "$PODS_CONFIGURATION_BUILD_DIR/DZNEmptyDataSet" "$PODS_CONFIGURATION_BUILD_DIR/Nuke" "$PODS_CONFIGURATION_BUILD_DIR/SwiftyDrop" "$PODS_CONFIGURATION_BUILD_DIR/SwiftyJSON" "$PODS_CONFIGURATION_BUILD_DIR/UIScrollView-InfiniteScroll" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Alamofire/Alamofire.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/AlamofireNetworkActivityIndicator/AlamofireNetworkActivityIndicator.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/DZNEmptyDataSet/DZNEmptyDataSet.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/Nuke/Nuke.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/SwiftyDrop/SwiftyDrop.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/SwiftyJSON/SwiftyJSON.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/UIScrollView-InfiniteScroll/UIScrollView_InfiniteScroll.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "AlamofireNetworkActivityIndicator" -framework "DZNEmptyDataSet" -framework "Nuke" -framework "SwiftyDrop" -framework "SwiftyJSON" -framework "UIScrollView_InfiniteScroll" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = $BUILD_DIR 9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_ROOT = ${SRCROOT}/Pods 11 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Left/Pods-Left.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_Left { 2 | umbrella header "Pods-Left-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/Pods-Left/Pods-Left.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "$PODS_CONFIGURATION_BUILD_DIR/Alamofire" "$PODS_CONFIGURATION_BUILD_DIR/AlamofireNetworkActivityIndicator" "$PODS_CONFIGURATION_BUILD_DIR/DZNEmptyDataSet" "$PODS_CONFIGURATION_BUILD_DIR/Nuke" "$PODS_CONFIGURATION_BUILD_DIR/SwiftyDrop" "$PODS_CONFIGURATION_BUILD_DIR/SwiftyJSON" "$PODS_CONFIGURATION_BUILD_DIR/UIScrollView-InfiniteScroll" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "$PODS_CONFIGURATION_BUILD_DIR/Alamofire/Alamofire.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/AlamofireNetworkActivityIndicator/AlamofireNetworkActivityIndicator.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/DZNEmptyDataSet/DZNEmptyDataSet.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/Nuke/Nuke.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/SwiftyDrop/SwiftyDrop.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/SwiftyJSON/SwiftyJSON.framework/Headers" -iquote "$PODS_CONFIGURATION_BUILD_DIR/UIScrollView-InfiniteScroll/UIScrollView_InfiniteScroll.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "Alamofire" -framework "AlamofireNetworkActivityIndicator" -framework "DZNEmptyDataSet" -framework "Nuke" -framework "SwiftyDrop" -framework "SwiftyJSON" -framework "UIScrollView_InfiniteScroll" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = $BUILD_DIR 9 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_ROOT = ${SRCROOT}/Pods 11 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SwiftyDrop/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 3.0.3 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SwiftyDrop/SwiftyDrop-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_SwiftyDrop : NSObject 3 | @end 4 | @implementation PodsDummy_SwiftyDrop 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SwiftyDrop/SwiftyDrop-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SwiftyDrop/SwiftyDrop-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double SwiftyDropVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char SwiftyDropVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SwiftyDrop/SwiftyDrop.modulemap: -------------------------------------------------------------------------------- 1 | framework module SwiftyDrop { 2 | umbrella header "SwiftyDrop-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SwiftyDrop/SwiftyDrop.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/SwiftyDrop 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftyDrop 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SwiftyJSON/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 3.1.3 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SwiftyJSON/SwiftyJSON-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_SwiftyJSON : NSObject 3 | @end 4 | @implementation PodsDummy_SwiftyJSON 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SwiftyJSON/SwiftyJSON-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double SwiftyJSONVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char SwiftyJSONVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SwiftyJSON/SwiftyJSON.modulemap: -------------------------------------------------------------------------------- 1 | framework module SwiftyJSON { 2 | umbrella header "SwiftyJSON-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/SwiftyJSON/SwiftyJSON.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/SwiftyJSON 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 5 | PODS_BUILD_DIR = $BUILD_DIR 6 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_ROOT = ${SRCROOT} 8 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/SwiftyJSON 9 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 10 | SKIP_INSTALL = YES 11 | SWIFT_VERSION = 3.0 12 | -------------------------------------------------------------------------------- /Pods/Target Support Files/UIScrollView-InfiniteScroll/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 0.9.1 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Pods/Target Support Files/UIScrollView-InfiniteScroll/UIScrollView-InfiniteScroll-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_UIScrollView_InfiniteScroll : NSObject 3 | @end 4 | @implementation PodsDummy_UIScrollView_InfiniteScroll 5 | @end 6 | -------------------------------------------------------------------------------- /Pods/Target Support Files/UIScrollView-InfiniteScroll/UIScrollView-InfiniteScroll-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Pods/Target Support Files/UIScrollView-InfiniteScroll/UIScrollView-InfiniteScroll-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | #import "UIScrollView+InfiniteScroll.h" 14 | 15 | FOUNDATION_EXPORT double UIScrollView_InfiniteScrollVersionNumber; 16 | FOUNDATION_EXPORT const unsigned char UIScrollView_InfiniteScrollVersionString[]; 17 | 18 | -------------------------------------------------------------------------------- /Pods/Target Support Files/UIScrollView-InfiniteScroll/UIScrollView-InfiniteScroll.modulemap: -------------------------------------------------------------------------------- 1 | framework module UIScrollView_InfiniteScroll { 2 | umbrella header "UIScrollView-InfiniteScroll-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Pods/Target Support Files/UIScrollView-InfiniteScroll/UIScrollView-InfiniteScroll.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/UIScrollView-InfiniteScroll 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Public" 4 | PODS_BUILD_DIR = $BUILD_DIR 5 | PODS_CONFIGURATION_BUILD_DIR = $PODS_BUILD_DIR/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/UIScrollView-InfiniteScroll 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | -------------------------------------------------------------------------------- /Pods/UIScrollView-InfiniteScroll/Classes/UIScrollView+InfiniteScroll.h: -------------------------------------------------------------------------------- 1 | // 2 | // UIScrollView+InfiniteScroll.h 3 | // 4 | // UIScrollView infinite scroll category 5 | // 6 | // Created by Andrej Mihajlov on 9/4/13. 7 | // Copyright (c) 2013-2015 Andrej Mihajlov. All rights reserved. 8 | // 9 | 10 | #import 11 | 12 | NS_ASSUME_NONNULL_BEGIN 13 | 14 | @interface UIScrollView (InfiniteScroll) 15 | 16 | /** 17 | * Flag that indicates whether infinite scroll is animating 18 | */ 19 | @property (nonatomic, readonly, getter=isAnimatingInfiniteScroll) BOOL animatingInfiniteScroll; 20 | 21 | /** 22 | * Infinite scroll activity indicator style (default: UIActivityIndicatorViewStyleGray on iOS, UIActivityIndicatorViewStyleWhite on tvOS) 23 | */ 24 | @property (nonatomic) UIActivityIndicatorViewStyle infiniteScrollIndicatorStyle; 25 | 26 | /** 27 | * Infinite indicator view 28 | * 29 | * You can set your own custom view instead of default activity indicator, 30 | * make sure it implements methods below: 31 | * 32 | * * `- (void)startAnimating` 33 | * * `- (void)stopAnimating` 34 | * 35 | * Infinite scroll will call implemented methods during user interaction. 36 | */ 37 | @property (nonatomic, nullable) UIView *infiniteScrollIndicatorView; 38 | 39 | /** 40 | * Vertical margin around indicator view (Default: 11) 41 | */ 42 | @property (nonatomic) CGFloat infiniteScrollIndicatorMargin; 43 | 44 | /** 45 | * Sets the offset between the real end of the scroll view content and the scroll position, so the handler can be triggered before reaching end. 46 | * Defaults to 0.0; 47 | */ 48 | @property (nonatomic) CGFloat infiniteScrollTriggerOffset; 49 | 50 | /** 51 | * Setup infinite scroll handler 52 | * 53 | * @param handler a handler block 54 | */ 55 | - (void)addInfiniteScrollWithHandler:(void(^)(UIScrollView *scrollView))handler; 56 | 57 | /** 58 | * Set a handler to be called to check if the infinite scroll should be shown 59 | * 60 | * @param handler a handler block 61 | */ 62 | - (void)setShouldShowInfiniteScrollHandler:(nullable BOOL(^)(UIScrollView *scrollView))handler; 63 | 64 | /** 65 | * Unregister infinite scroll 66 | */ 67 | - (void)removeInfiniteScroll; 68 | 69 | /** 70 | * Finish infinite scroll animations 71 | * 72 | * You must call this method from your infinite scroll handler to finish all 73 | * animations properly and reset infinite scroll state 74 | * 75 | * @param handler a completion block handler called when animation finished 76 | */ 77 | - (void)finishInfiniteScrollWithCompletion:(nullable void(^)(UIScrollView *scrollView))handler; 78 | 79 | /** 80 | * Finish infinite scroll animations 81 | * 82 | * You must call this method from your infinite scroll handler to finish all 83 | * animations properly and reset infinite scroll state 84 | */ 85 | - (void)finishInfiniteScroll; 86 | 87 | @end 88 | 89 | /* 90 | Convenience interface to avoid cast from UIScrollView to common subclasses such as UITableView and UICollectionView. 91 | */ 92 | 93 | @interface UITableView (InfiniteScrollConvenienceInterface) 94 | 95 | - (void)addInfiniteScrollWithHandler:(void(^)(UITableView *tableView))handler; 96 | - (void)setShouldShowInfiniteScrollHandler:(BOOL(^)(UITableView *tableView))handler; 97 | - (void)finishInfiniteScrollWithCompletion:(nullable void(^)(UITableView *tableView))handler; 98 | 99 | @end 100 | 101 | @interface UICollectionView (InfiniteScrollConvenienceInterface) 102 | 103 | - (void)addInfiniteScrollWithHandler:(void(^)(UICollectionView *collectionView))handler; 104 | - (void)setShouldShowInfiniteScrollHandler:(BOOL(^)(UICollectionView *collectionView))handler; 105 | - (void)finishInfiniteScrollWithCompletion:(nullable void(^)(UICollectionView *collectionView))handler; 106 | 107 | @end 108 | 109 | NS_ASSUME_NONNULL_END 110 | -------------------------------------------------------------------------------- /Pods/UIScrollView-InfiniteScroll/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2015 Andrei Mihailov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Left 2 | Left is an iOS app that lets you search for recipes that use the ingredients you have "left." Simply input up to 5 ingredients and press 3 | "Search" to see results. Find it on the App Store [here!](https://appsto.re/us/QIpFcb.i) 4 | 5 | #Credits 6 | - [Food2Fork](https://food2fork.com) recipe search API for recipe data. 7 | - [Alamofire](https://github.com/Alamofire/Alamofire) for network requests. 8 | - [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON) for JSON parsing. 9 | 10 |

11 | 12 | 13 |

14 | 15 | # License 16 | 17 | Left is available under the MIT license. See the LICENSE file for more info. 18 | -------------------------------------------------------------------------------- /left1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/left1.png -------------------------------------------------------------------------------- /left2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apatronl/Left/a9e2fb85b035aa102206847f6435b3d980b08a4f/left2.png --------------------------------------------------------------------------------