├── LICENSE ├── P1 PitchPerfect ├── .gitignore ├── Pitch Perfect.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── Pitch Perfect │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── 29.png │ │ │ ├── Contents.json │ │ │ ├── Icon-40.png │ │ │ ├── Icon-40@2x-1.png │ │ │ ├── Icon-40@2x.png │ │ │ ├── Icon-40@3x.png │ │ │ ├── Icon-41.png │ │ │ ├── Icon-42.png │ │ │ ├── Icon-43.png │ │ │ ├── Icon-60.png │ │ │ ├── Icon-60@2x.png │ │ │ ├── Icon-60@3x.png │ │ │ ├── Icon-76.png │ │ │ ├── Icon-76@2x.png │ │ │ ├── Icon-83.5pt 1.51.51 PM.png │ │ │ ├── Icon-83.5pt.png │ │ │ ├── Icon-Small@2x-1.png │ │ │ ├── Icon-Small@2x.png │ │ │ └── Icon-Small@3x.png │ │ ├── Contents.json │ │ ├── Echo.imageset │ │ │ ├── Contents.json │ │ │ ├── Echo.png │ │ │ ├── Echo@2x.png │ │ │ └── Echo@3x.png │ │ ├── Fast.imageset │ │ │ ├── Contents.json │ │ │ ├── Fast.png │ │ │ ├── Fast@2x.png │ │ │ └── Fast@3x.png │ │ ├── HighPitch.imageset │ │ │ ├── Contents.json │ │ │ ├── HighPitch.png │ │ │ ├── HighPitch@2x.png │ │ │ └── HighPitch@3x.png │ │ ├── Launch.imageset │ │ │ ├── Contents.json │ │ │ ├── Icon-83.5.png │ │ │ ├── Icon-83.5@2x.png │ │ │ └── Icon-83.5@3x.png │ │ ├── LowPitch.imageset │ │ │ ├── Contents.json │ │ │ ├── LowPitch.png │ │ │ ├── LowPitch@2x.png │ │ │ └── LowPitch@3x.png │ │ ├── Pause.imageset │ │ │ ├── Contents.json │ │ │ ├── Pause.png │ │ │ ├── Pause@2x.png │ │ │ └── Pause@3x.png │ │ ├── Record.imageset │ │ │ ├── Contents.json │ │ │ ├── Record.png │ │ │ ├── Record@2x.png │ │ │ └── Record@3x.png │ │ ├── Reverb.imageset │ │ │ ├── Contents.json │ │ │ ├── Reverb.png │ │ │ ├── Reverb@2x.png │ │ │ └── Reverb@3x.png │ │ ├── Slow.imageset │ │ │ ├── Contents.json │ │ │ ├── Slow.png │ │ │ ├── Slow@2x.png │ │ │ └── Slow@3x.png │ │ └── Stop.imageset │ │ │ ├── Contents.json │ │ │ ├── Stop.png │ │ │ ├── Stop@2x.png │ │ │ └── Stop@3x.png │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Info.plist │ ├── PlaySoundViewController+Audio.swift │ ├── PlaySoundViewController.swift │ └── RecordSoundViewController.swift ├── README.md └── Screenshots │ ├── HomeScreen-Idle.png │ ├── HomeScreen-Rec.png │ └── PlaySoundScreen.png ├── P2 MemeMe ├── .gitignore ├── MemeMe.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── MemeMe │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Meme-Me 2-1.png │ │ │ ├── Meme-Me 2-10.png │ │ │ ├── Meme-Me 2-11.png │ │ │ ├── Meme-Me 2-12.png │ │ │ ├── Meme-Me 2-13.png │ │ │ ├── Meme-Me 2-14.png │ │ │ ├── Meme-Me 2-15.png │ │ │ ├── Meme-Me 2-16.png │ │ │ ├── Meme-Me 2-2.png │ │ │ ├── Meme-Me 2-3.png │ │ │ ├── Meme-Me 2-4.png │ │ │ ├── Meme-Me 2-5.png │ │ │ ├── Meme-Me 2-6.png │ │ │ ├── Meme-Me 2-7.png │ │ │ ├── Meme-Me 2-8.png │ │ │ ├── Meme-Me 2-9.png │ │ │ ├── Meme-Me 2.png │ │ │ └── Meme-Me.png │ │ ├── Contents.json │ │ ├── camera.imageset │ │ │ ├── Contents.json │ │ │ ├── camera@2x.png │ │ │ └── camera@3x.png │ │ ├── gallary.imageset │ │ │ ├── Contents.json │ │ │ ├── gallary@2x.png │ │ │ └── gallary@3x.png │ │ ├── grid.imageset │ │ │ ├── Contents.json │ │ │ ├── grid.png │ │ │ ├── grid@2x.png │ │ │ └── grid@3x.png │ │ ├── launcher.imageset │ │ │ ├── Contents.json │ │ │ ├── launch-icon.png │ │ │ ├── launch-icon@2x.png │ │ │ └── launch-icon@3x.png │ │ └── list.imageset │ │ │ ├── Contents.json │ │ │ ├── list.png │ │ │ ├── list@2x.png │ │ │ └── list@3x.png │ ├── Editor │ │ └── MemeEditorViewController.swift │ ├── Home │ │ ├── GridViewController.swift │ │ ├── ListViewController.swift │ │ ├── MemeCollectionViewCell.swift │ │ ├── MemeTableViewCell.swift │ │ └── MemeTableViewCell.xib │ ├── Info.plist │ ├── MemeDetailsViewController.swift │ ├── Model │ │ ├── MemeObject.swift │ │ └── MemeStorage.swift │ └── View │ │ └── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard ├── README.md └── Screenshots │ ├── .DS_Store │ ├── HomeGridFilled.png │ ├── HomeListEmpty.png │ ├── HomeListFilled.png │ ├── MemeEditorEmpty.png │ └── MemeEditorFilled.png ├── P3 OnTheMap ├── .gitignore ├── On The Map.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── On The Map │ ├── Account │ │ └── AccountViewController.swift │ ├── AddNew │ │ ├── AddLocationViewController.swift │ │ └── AddProfileViewController.swift │ ├── Api │ │ ├── ApiConstants.swift │ │ ├── AuthHandler.swift │ │ ├── BaseNetworkHandler.swift │ │ └── ParseHandler.swift │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-40.png │ │ │ ├── Icon-40@2x.png │ │ │ ├── Icon-40@3x.png │ │ │ ├── Icon-60@2x.png │ │ │ ├── Icon-60@3x.png │ │ │ ├── Icon-76.png │ │ │ ├── Icon-76@2x.png │ │ │ ├── Icon-83.5@2x.png │ │ │ ├── Icon-Small.png │ │ │ ├── Icon-Small@2x.png │ │ │ └── Icon-Small@3x.png │ │ ├── Contents.json │ │ ├── LaunchLogo.imageset │ │ │ ├── Contents.json │ │ │ └── LaunchLogo.pdf │ │ ├── account.imageset │ │ │ ├── Contents.json │ │ │ ├── account.png │ │ │ ├── account@2x.png │ │ │ └── account@3x.png │ │ ├── icon_listview-deselected.imageset │ │ │ ├── Contents.json │ │ │ ├── icon_listview-deselected.png │ │ │ ├── icon_listview-deselected@2x.png │ │ │ └── icon_listview-deselected@3x.png │ │ ├── icon_listview-selected.imageset │ │ │ ├── Contents.json │ │ │ ├── icon_listview-selected.png │ │ │ ├── icon_listview-selected@2x.png │ │ │ └── icon_listview-selected@3x.png │ │ ├── icon_mapview-deselected.imageset │ │ │ ├── Contents.json │ │ │ ├── icon_mapview-deselected.png │ │ │ ├── icon_mapview-deselected@2x.png │ │ │ └── icon_mapview-deselected@3x.png │ │ ├── icon_mapview-selected.imageset │ │ │ ├── Contents.json │ │ │ ├── icon_mapview-selected.png │ │ │ ├── icon_mapview-selected@2x.png │ │ │ └── icon_mapview-selected@3x.png │ │ ├── icon_world.imageset │ │ │ ├── Contents.json │ │ │ ├── icon_world.png │ │ │ ├── icon_world@2x.png │ │ │ └── icon_world@3x.png │ │ ├── loading.imageset │ │ │ ├── Contents.json │ │ │ ├── loading.png │ │ │ ├── loading@2x.png │ │ │ └── loading@3x.png │ │ └── map-pin.imageset │ │ │ ├── Contents.json │ │ │ ├── map-pin.png │ │ │ ├── map-pin@2x.png │ │ │ └── map-pin@3x.png │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Home │ │ ├── Map │ │ │ ├── MapViewController+MapViewDelegate.swift │ │ │ └── MapViewController.swift │ │ ├── StudentLocationViewController.swift │ │ └── Table │ │ │ ├── TableViewController+TableViewDelegate.swift │ │ │ └── TableViewController.swift │ ├── Info.plist │ ├── Login │ │ ├── LoginViewController+Extras.swift │ │ └── LoginViewController.swift │ ├── Model │ │ ├── Response │ │ │ └── UdacityAuthResponse.swift │ │ ├── StudentLocation.swift │ │ └── UserInfo.swift │ ├── Storage │ │ ├── Cache.swift │ │ └── Storage.swift │ └── Utils │ │ ├── Errors.swift │ │ └── Extensions.swift ├── README.md └── Screenshots │ ├── Account.png │ ├── AddPin.png │ ├── AddProfile.png │ ├── List.png │ ├── Login.png │ ├── Map-Pin.png │ └── Map.png ├── P4 VirtualTourist ├── .gitignore ├── Podfile ├── Podfile.lock ├── README.md ├── Screenshots │ ├── Album.png │ ├── Map-DeletePin.png │ └── Map-ViewPin.png ├── Virtual Tourist.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── Virtual Tourist.xcworkspace │ └── contents.xcworkspacedata └── Virtual Tourist │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── PrimaryColor.colorset │ │ └── Contents.json │ ├── icon_delete.imageset │ │ ├── Contents.json │ │ ├── icon_delete.png │ │ ├── icon_delete@2x.png │ │ └── icon_delete@3x.png │ ├── icon_delete_dark.imageset │ │ ├── Contents.json │ │ ├── icon_delete_dark.png │ │ ├── icon_delete_dark@2x.png │ │ └── icon_delete_dark@3x.png │ ├── icon_done.imageset │ │ ├── Contents.json │ │ ├── icon_done.png │ │ ├── icon_done@2x.png │ │ └── icon_done@3x.png │ ├── icon_edit.imageset │ │ ├── Contents.json │ │ ├── icon_edit.png │ │ ├── icon_edit@2x.png │ │ └── icon_edit@3x.png │ ├── icon_info_dark.imageset │ │ ├── Contents.json │ │ ├── icon_info_dark.png │ │ ├── icon_info_dark@2x.png │ │ └── icon_info_dark@3x.png │ ├── icon_refresh.imageset │ │ ├── Contents.json │ │ ├── icon_refresh.png │ │ ├── icon_refresh@2x.png │ │ └── icon_refresh@3x.png │ └── placeholder_image.imageset │ │ ├── Contents.json │ │ ├── placeholder_image.png │ │ ├── placeholder_image@2x.png │ │ └── placeholder_image@3x.png │ ├── Data │ ├── DataController.swift │ ├── FlickrApiHandler.swift │ └── MapViewDefaults.swift │ ├── Fonts │ ├── Raleway-Light.ttf │ └── Raleway-Regular.ttf │ ├── Info.plist │ ├── Model │ └── Virtual_Tourist.xcdatamodeld │ │ ├── .xccurrentversion │ │ └── Virtual_Tourist.xcdatamodel │ │ └── contents │ ├── Storyboard │ └── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── UI │ ├── Album │ │ ├── PhotoAlbumViewController+Extras.swift │ │ └── PhotoAlbumViewController.swift │ ├── Map │ │ ├── TravelMapViewController+Extras.swift │ │ └── TravelMapViewController.swift │ └── View │ │ ├── PhotoViewCell.swift │ │ └── PhotoViewCell.xib │ └── Utils │ ├── Errors.swift │ └── Extensions.swift ├── P5 JSON-Feed-Reader ├── .gitignore ├── JSON Feed Reader.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── JSON Feed Reader.xcworkspace │ └── contents.xcworkspacedata ├── JSON Feed Reader │ ├── AppDelegate.swift │ ├── Assets │ │ └── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── json-feed.imageset │ │ │ ├── Contents.json │ │ │ ├── json-feed.png │ │ │ ├── json-feed@2x.png │ │ │ └── json-feed@3x.png │ │ │ ├── tab_feeds.imageset │ │ │ ├── Contents.json │ │ │ ├── tab_feeds.png │ │ │ ├── tab_feeds@2x.png │ │ │ └── tab_feeds@3x.png │ │ │ └── tab_home.imageset │ │ │ ├── Contents.json │ │ │ ├── tab_home.png │ │ │ ├── tab_home@2x.png │ │ │ └── tab_home@3x.png │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── Data │ │ ├── DataRepository.swift │ │ ├── Local │ │ │ ├── DataController.swift │ │ │ ├── JSON_Feed_Reader.xcdatamodeld │ │ │ │ ├── .xccurrentversion │ │ │ │ └── JSON_Feed_Reader.xcdatamodel │ │ │ │ │ └── contents │ │ │ ├── LocalRepository.swift │ │ │ └── PrimaryFeed.swift │ │ ├── Remote │ │ │ ├── ModelExtensions.swift │ │ │ └── RemoteRepository.swift │ │ └── RepositoryProtocol.swift │ ├── Info.plist │ ├── UI │ │ ├── BaseViewController.swift │ │ ├── HomeViewController.swift │ │ ├── PostCell │ │ │ ├── PostViewCell.swift │ │ │ └── PostViewCell.xib │ │ ├── PostContentViewController.swift │ │ └── PostsViewController.swift │ └── Utils │ │ ├── Constants.swift │ │ ├── Extensions.swift │ │ └── Formatter.swift ├── Podfile ├── Podfile.lock └── README.md └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Rajanikant Deshmukh 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 | -------------------------------------------------------------------------------- /P1 PitchPerfect/.gitignore: -------------------------------------------------------------------------------- 1 | ## Build generated 2 | build/ 3 | DerivedData/ 4 | 5 | ## Various settings 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | 16 | ## Other 17 | *.moved-aside 18 | *.xccheckout 19 | *.xcscmblueprint 20 | 21 | ## Obj-C/Swift specific 22 | *.hmap 23 | *.ipa 24 | *.dSYM.zip 25 | *.dSYM 26 | 27 | ## Playgrounds 28 | timeline.xctimeline 29 | playground.xcworkspace 30 | 31 | # Swift Package Manager 32 | .build/ 33 | 34 | # CocoaPods 35 | Pods/ 36 | -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Pitch Perfect 4 | // 5 | // Created by Rajanikant Deshmukh on 27/11/17. 6 | // Copyright © 2017 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // 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. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // 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. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // 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. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/29.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-41.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-60.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-Small@2x.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-Small@3x.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-40@2x.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-40@3x.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-60@2x.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-60@3x.png", 49 | "scale" : "3x" 50 | }, 51 | { 52 | "size" : "20x20", 53 | "idiom" : "ipad", 54 | "filename" : "Icon-43.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-42.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "29x29", 65 | "idiom" : "ipad", 66 | "filename" : "29.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-Small@2x-1.png", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "40x40", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-40.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-40@2x-1.png", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "size" : "76x76", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-76.png", 91 | "scale" : "1x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-76@2x.png", 97 | "scale" : "2x" 98 | }, 99 | { 100 | "size" : "83.5x83.5", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-83.5pt.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "1024x1024", 107 | "idiom" : "ios-marketing", 108 | "filename" : "Icon-83.5pt 1.51.51 PM.png", 109 | "scale" : "1x" 110 | } 111 | ], 112 | "info" : { 113 | "version" : 1, 114 | "author" : "xcode" 115 | } 116 | } -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-40.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-40@2x-1.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-41.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-42.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-43.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-43.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-60.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-76.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-83.5pt 1.51.51 PM.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-83.5pt 1.51.51 PM.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-83.5pt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-83.5pt.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x-1.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Echo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Echo.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Echo@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Echo@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Echo.imageset/Echo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Echo.imageset/Echo.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Echo.imageset/Echo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Echo.imageset/Echo@2x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Echo.imageset/Echo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Echo.imageset/Echo@3x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Fast.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Fast.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Fast@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Fast@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Fast.imageset/Fast.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Fast.imageset/Fast.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Fast.imageset/Fast@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Fast.imageset/Fast@2x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Fast.imageset/Fast@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Fast.imageset/Fast@3x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/HighPitch.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "HighPitch.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "HighPitch@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "HighPitch@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/HighPitch.imageset/HighPitch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/HighPitch.imageset/HighPitch.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/HighPitch.imageset/HighPitch@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/HighPitch.imageset/HighPitch@2x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/HighPitch.imageset/HighPitch@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/HighPitch.imageset/HighPitch@3x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Launch.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Icon-83.5.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Icon-83.5@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Icon-83.5@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Launch.imageset/Icon-83.5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Launch.imageset/Icon-83.5.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Launch.imageset/Icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Launch.imageset/Icon-83.5@2x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Launch.imageset/Icon-83.5@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Launch.imageset/Icon-83.5@3x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/LowPitch.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LowPitch.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LowPitch@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LowPitch@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/LowPitch.imageset/LowPitch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/LowPitch.imageset/LowPitch.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/LowPitch.imageset/LowPitch@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/LowPitch.imageset/LowPitch@2x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/LowPitch.imageset/LowPitch@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/LowPitch.imageset/LowPitch@3x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Pause.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Pause.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Pause@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Pause@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Pause.imageset/Pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Pause.imageset/Pause.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Pause.imageset/Pause@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Pause.imageset/Pause@2x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Pause.imageset/Pause@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Pause.imageset/Pause@3x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Record.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Record.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Record@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Record@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Record.imageset/Record.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Record.imageset/Record.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Record.imageset/Record@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Record.imageset/Record@2x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Record.imageset/Record@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Record.imageset/Record@3x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Reverb.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Reverb.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Reverb@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Reverb@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Reverb.imageset/Reverb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Reverb.imageset/Reverb.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Reverb.imageset/Reverb@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Reverb.imageset/Reverb@2x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Reverb.imageset/Reverb@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Reverb.imageset/Reverb@3x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Slow.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Slow.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Slow@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Slow@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Slow.imageset/Slow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Slow.imageset/Slow.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Slow.imageset/Slow@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Slow.imageset/Slow@2x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Slow.imageset/Slow@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Slow.imageset/Slow@3x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Stop.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "Stop.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "Stop@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "Stop@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Stop.imageset/Stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Stop.imageset/Stop.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Stop.imageset/Stop@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Stop.imageset/Stop@2x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Stop.imageset/Stop@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Pitch Perfect/Assets.xcassets/Stop.imageset/Stop@3x.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSMicrophoneUsageDescription 6 | Pitch Perfect will need access to Microphone to record audio. 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/PlaySoundViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PlaySoundViewController.swift 3 | // Pitch Perfect 4 | // 5 | // Created by Rajanikant Deshmukh on 01/12/17. 6 | // Copyright © 2017 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AVFoundation 11 | 12 | class PlaySoundViewController: UIViewController { 13 | 14 | // MARK: Outlets 15 | 16 | @IBOutlet weak var snailButton: UIButton! 17 | @IBOutlet weak var chipmunkButton: UIButton! 18 | @IBOutlet weak var rabbitButton: UIButton! 19 | @IBOutlet weak var vaderButton: UIButton! 20 | @IBOutlet weak var echoButton: UIButton! 21 | @IBOutlet weak var reverbButton: UIButton! 22 | @IBOutlet weak var stopButton: UIButton! 23 | 24 | var recordedAudioUrl: URL! 25 | var audioFile:AVAudioFile! 26 | var audioEngine:AVAudioEngine! 27 | var audioPlayerNode: AVAudioPlayerNode! 28 | var stopTimer: Timer! 29 | 30 | enum ButtonType: Int {case slow = 0, fast, chipmunk, vader, echo, reverb } 31 | 32 | 33 | override func viewWillAppear(_ animated: Bool) { 34 | super.viewWillAppear(animated) 35 | configureUI(.notPlaying) 36 | } 37 | 38 | override func viewDidLoad() { 39 | super.viewDidLoad() 40 | setupAudio() 41 | snailButton.imageView?.contentMode = .scaleAspectFit 42 | chipmunkButton.imageView?.contentMode = .scaleAspectFit 43 | rabbitButton.imageView?.contentMode = .scaleAspectFit 44 | vaderButton.imageView?.contentMode = .scaleAspectFit 45 | echoButton.imageView?.contentMode = .scaleAspectFit 46 | reverbButton.imageView?.contentMode = .scaleAspectFit 47 | stopButton.imageView?.contentMode = .scaleAspectFit 48 | } 49 | 50 | // MARK: Actions 51 | 52 | @IBAction func playSoundForButton(_ sender: UIButton) { 53 | switch(ButtonType(rawValue: sender.tag)!) { 54 | case .slow: 55 | playSound(rate: 0.5) 56 | case .fast: 57 | playSound(rate: 1.5) 58 | case .chipmunk: 59 | playSound(pitch: 1000) 60 | case .vader: 61 | playSound(pitch: -1000) 62 | case .echo: 63 | playSound(echo: true) 64 | case .reverb: 65 | playSound(reverb: true) 66 | } 67 | 68 | configureUI(.playing) 69 | } 70 | 71 | @IBAction func stopButtonPressed(_ sender: AnyObject) { 72 | stopAudio() 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /P1 PitchPerfect/Pitch Perfect/RecordSoundViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RecordSoundViewController.swift 3 | // Pitch Perfect 4 | // 5 | // Created by Rajanikant Deshmukh on 27/11/17. 6 | // Copyright © 2017 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AVFoundation 11 | 12 | class RecordSoundViewController: UIViewController, AVAudioRecorderDelegate { 13 | 14 | // MARK: IBOutlets 15 | 16 | @IBOutlet weak var recordLabel: UILabel! 17 | @IBOutlet weak var recordButton: UIButton! 18 | @IBOutlet weak var stopButton: UIButton! 19 | 20 | let recordingSegueId = "stopRecording" 21 | var audioRecorder: AVAudioRecorder! 22 | 23 | // MARK: UI Functions 24 | 25 | override func viewDidLoad() { 26 | super.viewDidLoad() 27 | configureUI(recording: false) 28 | } 29 | 30 | func configureUI(recording: Bool) { 31 | recordButton.isEnabled = !recording 32 | stopButton.isEnabled = recording 33 | recordLabel.text = recording ? "Recording.." : "Tap To Record" 34 | } 35 | 36 | // MARK: IBActions 37 | 38 | @IBAction func startRecording(_ sender: Any) { 39 | configureUI(recording: true) 40 | 41 | let dirPath = NSSearchPathForDirectoriesInDomains(.documentDirectory,.userDomainMask, true)[0] as String 42 | let recordingName = "recordedVoice.wav" 43 | let pathArray = [dirPath, recordingName] 44 | let filePath = URL(string: pathArray.joined(separator: "/")) 45 | 46 | let session = AVAudioSession.sharedInstance() 47 | try! session.setCategory(AVAudioSessionCategoryPlayAndRecord, with:AVAudioSessionCategoryOptions.defaultToSpeaker) 48 | 49 | try! audioRecorder = AVAudioRecorder(url: filePath!, settings: [:]) 50 | audioRecorder.delegate = self 51 | audioRecorder.isMeteringEnabled = true 52 | audioRecorder.prepareToRecord() 53 | audioRecorder.record() 54 | } 55 | 56 | @IBAction func stopRecording(_ sender: Any) { 57 | configureUI(recording: false) 58 | 59 | audioRecorder.stop() 60 | let session = AVAudioSession.sharedInstance() 61 | try! session.setActive(false) 62 | } 63 | 64 | // MARK: AVAudioRecorderDelegate Functions 65 | 66 | func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) { 67 | // Called after recording and saving file is finished 68 | if flag { 69 | performSegue(withIdentifier: recordingSegueId, sender: audioRecorder.url) 70 | } else { 71 | print("Recording Audio Failed") 72 | let alert = UIAlertController(title: "Reording Failed", message: "Racording audio failed due to some unknown error.", preferredStyle: .alert) 73 | alert.addAction(UIAlertAction(title: "Dismiss", style: .default, handler: nil)) 74 | self.present(alert, animated: true, completion: nil) 75 | } 76 | } 77 | 78 | // MARK: Segue Functions 79 | 80 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 81 | if (segue.identifier == recordingSegueId) { 82 | let playSoundVC = segue.destination as! PlaySoundViewController 83 | let recordedAudioUrl = sender as! URL 84 | playSoundVC.recordedAudioUrl = recordedAudioUrl 85 | } 86 | } 87 | } 88 | 89 | -------------------------------------------------------------------------------- /P1 PitchPerfect/README.md: -------------------------------------------------------------------------------- 1 | # Pitch Perfect 2 | Ever wondered what you’d sound like as a Chipmunk or Darth Vader? Wonder no more! 3 | Pitch Perfect is an app that records a user’s voice and then plays the modulated audio through a variety of filters. 4 | 5 | ## Screenshots 6 | | Home Screen: Idle | Home Screen: Recording | Play Sounds Screen | 7 | | ----------------- | ---------------------- | ------------------ | 8 | | ![HomeScreen-Idle.png](Screenshots/HomeScreen-Idle.png) | ![HomeScreen-Rec.png](Screenshots/HomeScreen-Rec.png) | ![PlaySoundScreen.png](Screenshots/PlaySoundScreen.png) 9 | 10 | ## Frameworks Used 11 | 1. [Foundation](https://developer.apple.com/documentation/foundation) 12 | 2. [UIKit](https://developer.apple.com/documentation/uikit) 13 | 3. [AVFoundation](https://developer.apple.com/documentation/avfoundation) 14 | 15 | ## How to Build 16 | 1. Download or fork & clone project on your desktop. 17 | 2. Open `Pitch Perfect.xcodeproj` file in Xcode. 18 | 3. Now you can build and run the app. 19 | 20 | ## Licence 21 | This project is a part of Master Projects repository. For other projects and licensing information, please see [iOS Nanodegree Projects](https://github.com/rajanikantdeshmukh/iOS-Nanodegree-Projects). 22 | -------------------------------------------------------------------------------- /P1 PitchPerfect/Screenshots/HomeScreen-Idle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Screenshots/HomeScreen-Idle.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Screenshots/HomeScreen-Rec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Screenshots/HomeScreen-Rec.png -------------------------------------------------------------------------------- /P1 PitchPerfect/Screenshots/PlaySoundScreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P1 PitchPerfect/Screenshots/PlaySoundScreen.png -------------------------------------------------------------------------------- /P2 MemeMe/.gitignore: -------------------------------------------------------------------------------- 1 | ## Build generated 2 | build/ 3 | DerivedData/ 4 | 5 | ## Various settings 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | 16 | ## Other 17 | *.moved-aside 18 | *.xccheckout 19 | *.xcscmblueprint 20 | 21 | ## Obj-C/Swift specific 22 | *.hmap 23 | *.ipa 24 | *.dSYM.zip 25 | *.dSYM 26 | 27 | ## Playgrounds 28 | timeline.xctimeline 29 | playground.xcworkspace 30 | 31 | # Swift Package Manager 32 | .build/ 33 | 34 | # CocoaPods 35 | Pods/ 36 | -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // MemeMe 4 | // 5 | // Created by Rajanikant Deshmukh on 29/12/17. 6 | // Copyright © 2017 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | return true 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Meme-Me 2-12.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Meme-Me 2-7.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Meme-Me 2-10.png", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Meme-Me 2-5.png", 25 | "scale" : "3x" 26 | }, 27 | { 28 | "size" : "40x40", 29 | "idiom" : "iphone", 30 | "filename" : "Meme-Me 2-6.png", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Meme-Me 2-3.png", 37 | "scale" : "3x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "Meme-Me 2-4.png", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Meme-Me 2-1.png", 49 | "scale" : "3x" 50 | }, 51 | { 52 | "size" : "20x20", 53 | "idiom" : "ipad", 54 | "filename" : "Meme-Me 2-16.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Meme-Me 2-13.png", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "size" : "29x29", 65 | "idiom" : "ipad", 66 | "filename" : "Meme-Me 2-15.png", 67 | "scale" : "1x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Meme-Me 2-11.png", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "40x40", 77 | "idiom" : "ipad", 78 | "filename" : "Meme-Me 2-14.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Meme-Me 2-8.png", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "size" : "76x76", 89 | "idiom" : "ipad", 90 | "filename" : "Meme-Me 2-9.png", 91 | "scale" : "1x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Meme-Me 2-2.png", 97 | "scale" : "2x" 98 | }, 99 | { 100 | "size" : "83.5x83.5", 101 | "idiom" : "ipad", 102 | "filename" : "Meme-Me 2.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "1024x1024", 107 | "idiom" : "ios-marketing", 108 | "filename" : "Meme-Me.png", 109 | "scale" : "1x" 110 | } 111 | ], 112 | "info" : { 113 | "version" : 1, 114 | "author" : "xcode" 115 | } 116 | } -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-1.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-10.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-11.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-12.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-13.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-14.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-15.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-16.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-2.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-3.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-4.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-5.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-6.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-7.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-8.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2-9.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me 2.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/AppIcon.appiconset/Meme-Me.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/camera.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "camera@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "camera@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/camera.imageset/camera@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/camera.imageset/camera@2x.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/camera.imageset/camera@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/camera.imageset/camera@3x.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/gallary.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "gallary@2x.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "gallary@3x.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/gallary.imageset/gallary@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/gallary.imageset/gallary@2x.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/gallary.imageset/gallary@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/gallary.imageset/gallary@3x.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/grid.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "grid.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "grid@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "grid@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/grid.imageset/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/grid.imageset/grid.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/grid.imageset/grid@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/grid.imageset/grid@2x.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/grid.imageset/grid@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/grid.imageset/grid@3x.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/launcher.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "launch-icon.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "launch-icon@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "launch-icon@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/launcher.imageset/launch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/launcher.imageset/launch-icon.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/launcher.imageset/launch-icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/launcher.imageset/launch-icon@2x.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/launcher.imageset/launch-icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/launcher.imageset/launch-icon@3x.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/list.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "list.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "list@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "list@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/list.imageset/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/list.imageset/list.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/list.imageset/list@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/list.imageset/list@2x.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Assets.xcassets/list.imageset/list@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/MemeMe/Assets.xcassets/list.imageset/list@3x.png -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Home/GridViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GridViewController.swift 3 | // MemeMe 4 | // 5 | // Created by Rajanikant Deshmukh on 17/02/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class GridViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { 12 | 13 | @IBOutlet weak var emptyTextView: UILabel! 14 | @IBOutlet weak var gridView: UICollectionView! 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | gridView.delegate = self 19 | gridView.dataSource = self 20 | } 21 | 22 | override func viewWillAppear(_ animated: Bool) { 23 | super.viewWillAppear(animated) 24 | gridView.reloadData() 25 | emptyTextView.isHidden = (MemeStorage.getCount() != 0) 26 | } 27 | 28 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 29 | return MemeStorage.getCount() 30 | } 31 | 32 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 33 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "MemeCellView", for: indexPath) as! MemeCollectionViewCell 34 | cell.imageView.image = MemeStorage.get(indexPath.item).memedImage 35 | return cell 36 | } 37 | 38 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 39 | // Process selection 40 | let detailViewController: MemeDetailsViewController = storyboard?.instantiateViewController(withIdentifier: "MemeDetailsViewController") as! MemeDetailsViewController 41 | detailViewController.position = indexPath.item 42 | navigationController?.pushViewController(detailViewController, animated: true) 43 | } 44 | 45 | // MARK: UICollectionViewDelegateFlowLayout Methods 46 | 47 | let itemsPerRow: CGFloat = 3 48 | let sectionInsets = UIEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) 49 | 50 | func collectionView(_ collectionView: UICollectionView, 51 | layout collectionViewLayout: UICollectionViewLayout, 52 | sizeForItemAt indexPath: IndexPath) -> CGSize { 53 | let paddingSpace = sectionInsets.left * (itemsPerRow + 1) 54 | let availableWidth = view.frame.width - paddingSpace 55 | let widthPerItem = availableWidth / itemsPerRow 56 | 57 | return CGSize(width: widthPerItem, height: widthPerItem) 58 | } 59 | 60 | func collectionView(_ collectionView: UICollectionView, 61 | layout collectionViewLayout: UICollectionViewLayout, 62 | insetForSectionAt section: Int) -> UIEdgeInsets { 63 | return sectionInsets 64 | } 65 | 66 | func collectionView(_ collectionView: UICollectionView, 67 | layout collectionViewLayout: UICollectionViewLayout, 68 | minimumLineSpacingForSectionAt section: Int) -> CGFloat { 69 | return sectionInsets.left 70 | } 71 | 72 | } 73 | 74 | -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Home/ListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListViewController.swift 3 | // MemeMe 4 | // 5 | // Created by Rajanikant Deshmukh on 17/02/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { 12 | 13 | @IBOutlet weak var emptyView: UILabel! 14 | @IBOutlet weak var tableView: UITableView! 15 | 16 | override func viewWillAppear(_ animated: Bool) { 17 | super.viewWillAppear(animated) 18 | if MemeStorage.getCount() == 0 { 19 | tableView.isHidden = true 20 | emptyView.isHidden = false 21 | } else { 22 | tableView.isHidden = false 23 | emptyView.isHidden = true 24 | tableView.reloadData() 25 | } 26 | } 27 | 28 | override func viewDidLoad() { 29 | super.viewDidLoad() 30 | 31 | // Register XIB file to UITableView 32 | tableView.register(UINib(nibName: "MemeTableViewCell", bundle: nil), forCellReuseIdentifier: "MemeCellView") 33 | 34 | tableView.delegate = self 35 | tableView.dataSource = self 36 | } 37 | 38 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 39 | return MemeStorage.getCount() 40 | } 41 | 42 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 43 | let memeObject = MemeStorage.get(indexPath.row) 44 | let titleText = memeObject.topText + " " + memeObject.bottomText 45 | 46 | let cell: MemeTableViewCell = tableView.dequeueReusableCell(withIdentifier: "MemeCellView") as! MemeTableViewCell 47 | 48 | cell.memeTitleLabel.text = titleText 49 | cell.memeImageView.image = memeObject.memedImage 50 | return cell 51 | } 52 | 53 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 54 | return 128.0 55 | } 56 | 57 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 58 | let detailViewController: MemeDetailsViewController = storyboard?.instantiateViewController(withIdentifier: "MemeDetailsViewController") as! MemeDetailsViewController 59 | detailViewController.position = indexPath.item 60 | navigationController?.pushViewController(detailViewController, animated: true) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Home/MemeCollectionViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MemeCollectionViewCell.swift 3 | // MemeMe 4 | // 5 | // Created by Rajanikant Deshmukh on 17/02/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class MemeCollectionViewCell: UICollectionViewCell { 12 | 13 | @IBOutlet weak var imageView: UIImageView! 14 | 15 | } 16 | -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Home/MemeTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MemeTableViewCell.swift 3 | // MemeMe 4 | // 5 | // Created by Rajanikant Deshmukh on 16/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class MemeTableViewCell: UITableViewCell { 13 | 14 | @IBOutlet weak var memeImageView: UIImageView! 15 | @IBOutlet weak var memeTitleLabel: UILabel! 16 | } 17 | -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSCameraUsageDescription 6 | MemeMe would like to use camera for taking pictures. 7 | NSPhotoLibraryAddUsageDescription 8 | MemeMe would like to access Photo Library for pictures. 9 | CFBundleDevelopmentRegion 10 | $(DEVELOPMENT_LANGUAGE) 11 | CFBundleExecutable 12 | $(EXECUTABLE_NAME) 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | $(PRODUCT_NAME) 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | 1.0 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UISupportedInterfaceOrientations 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationLandscapeLeft 39 | UIInterfaceOrientationLandscapeRight 40 | 41 | UISupportedInterfaceOrientations~ipad 42 | 43 | UIInterfaceOrientationPortrait 44 | UIInterfaceOrientationPortraitUpsideDown 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/MemeDetailsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MemeDetailsViewController.swift 3 | // MemeMe 4 | // 5 | // Created by Rajanikant Deshmukh on 18/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class MemeDetailsViewController: UIViewController { 13 | 14 | @IBOutlet weak var memedImageView: UIImageView! 15 | 16 | var position: Int! 17 | 18 | override func viewWillAppear(_ animated: Bool) { 19 | super.viewWillAppear(animated) 20 | 21 | if (position != nil) { 22 | let meme = MemeStorage.get(position) 23 | memedImageView.image = meme.memedImage 24 | } 25 | } 26 | 27 | override func viewDidLoad() { 28 | super.viewDidLoad() 29 | if (position == nil) { 30 | let alertController = UIAlertController(title: "No Meme Selected", message: "", preferredStyle: UIAlertControllerStyle.alert) 31 | alertController.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Default action"), style: .default, handler: nil)) 32 | 33 | present(alertController, animated: true, completion: nil) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Model/MemeObject.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MemeObject.swift 3 | // MemeMe 4 | // 5 | // Created by Rajanikant Deshmukh on 14/02/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | struct MemeObject { 13 | var topText: String 14 | var bottomText: String 15 | var originalImage: UIImage 16 | var memedImage: UIImage 17 | } 18 | -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/Model/MemeStorage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MemeStorage.swift 3 | // MemeMe 4 | // 5 | // Created by Rajanikant Deshmukh on 16/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class MemeStorage: NSObject { 13 | 14 | private static var memes = [MemeObject]() 15 | 16 | private override init() { 17 | } 18 | 19 | static func getMemes() -> [MemeObject] { 20 | return memes 21 | } 22 | 23 | static func addMeme(_ meme: MemeObject) { 24 | memes.append(meme) 25 | } 26 | 27 | static func get(_ position: Int) -> MemeObject { 28 | return memes[position] 29 | } 30 | 31 | static func getCount() -> Int { 32 | return memes.count 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /P2 MemeMe/MemeMe/View/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 | -------------------------------------------------------------------------------- /P2 MemeMe/README.md: -------------------------------------------------------------------------------- 1 | # Meme Me 2 | Meme Me is an app that creates memes from images! The Meme Editor enables the user to take a picture, and add text at the top and bottom to form a meme, and share the meme with friends. The Collection screen shows sent memes in both a table and collection view. 3 | 4 | ## Screenshots 5 | 6 | #### Meme Collection 7 | | Empty Screen | Collection | Table | 8 | | ------------ | ---------- | ----- | 9 | | ![HomeListEmpty.png](Screenshots/HomeListEmpty.png) | ![HomeGridFilled.png](Screenshots/HomeGridFilled.png) | ![HomeListFilled.png](Screenshots/HomeListFilled.png) | 10 | 11 | #### Meme Editor 12 | | Empty | Editing | 13 | | ----- | ------- | 14 | | ![MemeEditorEmpty.png](Screenshots/MemeEditorEmpty.png) | ![MemeEditorFilled.png](Screenshots/MemeEditorFilled.png) | 15 | 16 | ## Frameworks Used 17 | 1. [Foundation](https://developer.apple.com/documentation/foundation) 18 | 2. [UIKit](https://developer.apple.com/documentation/uikit) 19 | 20 | ## How to Build 21 | 1. Download zip or fork & clone project on your desktop. 22 | 2. Open `MemeMe.xcodeproj` file in Xcode. 23 | 24 | ## Licence 25 | This project is a part of Master Projects repository. For other projects and licensing information, please see [iOS Nanodegree Projects](https://github.com/rajanikantdeshmukh/iOS-Nanodegree-Projects). 26 | -------------------------------------------------------------------------------- /P2 MemeMe/Screenshots/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/Screenshots/.DS_Store -------------------------------------------------------------------------------- /P2 MemeMe/Screenshots/HomeGridFilled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/Screenshots/HomeGridFilled.png -------------------------------------------------------------------------------- /P2 MemeMe/Screenshots/HomeListEmpty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/Screenshots/HomeListEmpty.png -------------------------------------------------------------------------------- /P2 MemeMe/Screenshots/HomeListFilled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/Screenshots/HomeListFilled.png -------------------------------------------------------------------------------- /P2 MemeMe/Screenshots/MemeEditorEmpty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/Screenshots/MemeEditorEmpty.png -------------------------------------------------------------------------------- /P2 MemeMe/Screenshots/MemeEditorFilled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P2 MemeMe/Screenshots/MemeEditorFilled.png -------------------------------------------------------------------------------- /P3 OnTheMap/.gitignore: -------------------------------------------------------------------------------- 1 | ## Build generated 2 | build/ 3 | DerivedData/ 4 | 5 | ## Various settings 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | 16 | ## Other 17 | *.moved-aside 18 | *.xccheckout 19 | *.xcscmblueprint 20 | 21 | ## Obj-C/Swift specific 22 | *.hmap 23 | *.ipa 24 | *.dSYM.zip 25 | *.dSYM 26 | 27 | ## Playgrounds 28 | timeline.xctimeline 29 | playground.xcworkspace 30 | 31 | # Swift Package Manager 32 | .build/ 33 | 34 | # CocoaPods 35 | Pods/ 36 | -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Account/AccountViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AccountViewController.swift 3 | // On The Map 4 | // 5 | // Created by Rajanikant Deshmukh on 23/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class AccountViewController: UIViewController { 12 | 13 | @IBOutlet weak var userImage: UIImageView! 14 | @IBOutlet weak var nicknameLabel: UILabel! 15 | @IBOutlet weak var nameLabel: UILabel! 16 | @IBOutlet weak var logoutButton: UIButton! 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | 21 | let userInfo = Cache.shared.userInfo 22 | 23 | nicknameLabel.text = userInfo?.nickName 24 | nameLabel.text = "\(userInfo?.firstName ?? "") \(userInfo?.lastName ?? "")" 25 | 26 | DispatchQueue.global(qos: .background).async { 27 | let image: UIImage 28 | do { 29 | let url = URL(string: "https:" + (userInfo?.imageUrl ?? "")) 30 | let data = try Data(contentsOf: url!) 31 | image = UIImage(data: data)! 32 | } catch { 33 | image = UIImage(named: "icon_world")! 34 | } 35 | 36 | DispatchQueue.main.async { 37 | self.userImage.image = image 38 | } 39 | } 40 | } 41 | 42 | @IBAction func onLogoutClick(_ sender: Any) { 43 | AuthHandler.shared.makeLogoutCall({error in 44 | if error != nil { 45 | self.showAlertDialog(title: "Error", message: (error?.rawValue)!, dismissHandler: nil) 46 | return 47 | } 48 | 49 | // Dismiss current view controller 50 | self.tabBarController?.dismiss(animated: true, completion: nil) 51 | }) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/AddNew/AddProfileViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddProfileViewController.swift 3 | // On The Map 4 | // 5 | // Created by Rajanikant Deshmukh on 24/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import MapKit 11 | 12 | class AddProfileViewController: UIViewController, UITextFieldDelegate { 13 | 14 | var locationString: String? 15 | var lat: Double? 16 | var lng: Double? 17 | 18 | @IBOutlet weak var nameLabel: UILabel! 19 | @IBOutlet weak var locationLabel: UILabel! 20 | @IBOutlet weak var profileLinkInput: UITextField! 21 | @IBOutlet weak var mapView: MKMapView! 22 | @IBOutlet weak var addPinButton: UIButton! 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | let userInfo = Cache.shared.userInfo 27 | nameLabel.text = "\(userInfo?.firstName ?? "") \(userInfo?.lastName ?? "")" 28 | locationLabel.text = locationString 29 | 30 | profileLinkInput.delegate = self 31 | 32 | addPinButton.isEnabled = false 33 | 34 | let annotation = MKPointAnnotation() 35 | annotation.coordinate.latitude = lat! 36 | annotation.coordinate.longitude = lng! 37 | mapView.addAnnotation(annotation) 38 | mapView.centerCoordinate = annotation.coordinate 39 | mapView.region = MKCoordinateRegion(center: annotation.coordinate, span: MKCoordinateSpan(latitudeDelta: 3, longitudeDelta: 3)) 40 | } 41 | 42 | @IBAction func addPinButtonClick(_ sender: Any) { 43 | let mediaUrl = profileLinkInput.text 44 | ParseHandler.shared.postStudentLocation(locationString: locationString!, mediaUrl: mediaUrl!, lat: lat!, lng: lng!, onComplete: {error in 45 | DispatchQueue.main.async { 46 | if error != nil { 47 | self.showAlertDialog(title: "Error", message: (error)!.rawValue, dismissHandler: nil) 48 | return 49 | } 50 | self.showAlertDialog(title: "Location Posted", message: "Now other students can see where you are studying.", dismissHandler: { _ in 51 | self.navigationController?.popToRootViewController(animated: true) 52 | }) 53 | } 54 | }) 55 | } 56 | 57 | func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { 58 | addPinButton.isEnabled = ((profileLinkInput.text?.count ?? 0) > 0) 59 | return true 60 | } 61 | 62 | func textFieldShouldReturn(_ textField: UITextField) -> Bool { 63 | addPinButton.isEnabled = ((profileLinkInput.text?.count ?? 0) > 0) 64 | profileLinkInput.resignFirstResponder() 65 | return true 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Api/ApiConstants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ApiConstants.swift 3 | // On The Map 4 | // 5 | // Created by Rajanikant Deshmukh on 18/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct ApiConstants { 12 | 13 | static let PARSE_ENPOINT_URL = "https://parse.udacity.com/parse/classes" 14 | static let PARSE_APP_ID = "QrX47CA9cyuGewLdsL7o5Eb8iug6Em8ye0dnAbIr" 15 | static let REST_API_KEY = "QuWThTdiRmTux3YaDseUSEpUKo7aBYM737yKd4gY" 16 | 17 | struct Method { 18 | static let GET: String = "GET" 19 | static let POST: String = "POST" 20 | static let DELETE: String = "DELETE" 21 | } 22 | 23 | struct HeaderKey { 24 | static let CONTENT_TYPE = "Content-Type" 25 | static let ACCEPT = "Accept" 26 | } 27 | 28 | struct HeaderValue { 29 | static let MIME_TYPE_JSON = "application/json" 30 | } 31 | 32 | struct UdacityAuth { 33 | static let KEY_UDACITY = "udacity" 34 | static let KEY_USERNAME = "username" 35 | static let KEY_PASSWORD = "password" 36 | 37 | static let KEY_STATUS = "status" 38 | static let KEY_ACCOUNT = "account" 39 | static let KEY_ACC_KEY = "key" 40 | static let KEY_SESSION = "session" 41 | static let KEY_ID = "id" 42 | static let KEY_EXPIRATION = "expiration" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Api/BaseNetworkHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseNetworkHandler.swift 3 | // On The Map 4 | // 5 | // Created by Rajanikant Deshmukh on 26/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class BaseNetworkHandler: NSObject { 12 | 13 | typealias ErrorBlock = ((Errors?) -> Void) 14 | 15 | func handleError(error: Error!, onComplete: @escaping ErrorBlock) { 16 | if let urlError = error as? URLError { 17 | switch urlError.code { 18 | case .notConnectedToInternet: 19 | onComplete(Errors.NetworkError) 20 | break 21 | default: 22 | onComplete(Errors.ServerError) 23 | } 24 | } 25 | onComplete(Errors.ServerError) 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // On The Map 4 | // 5 | // Created by Rajanikant Deshmukh on 08/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // 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. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // 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. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // 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. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/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 | "size" : "29x29", 15 | "idiom" : "iphone", 16 | "filename" : "Icon-Small@2x.png", 17 | "scale" : "2x" 18 | }, 19 | { 20 | "size" : "29x29", 21 | "idiom" : "iphone", 22 | "filename" : "Icon-Small@3x.png", 23 | "scale" : "3x" 24 | }, 25 | { 26 | "size" : "40x40", 27 | "idiom" : "iphone", 28 | "filename" : "Icon-40@2x.png", 29 | "scale" : "2x" 30 | }, 31 | { 32 | "size" : "40x40", 33 | "idiom" : "iphone", 34 | "filename" : "Icon-40@3x.png", 35 | "scale" : "3x" 36 | }, 37 | { 38 | "size" : "60x60", 39 | "idiom" : "iphone", 40 | "filename" : "Icon-60@2x.png", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "size" : "60x60", 45 | "idiom" : "iphone", 46 | "filename" : "Icon-60@3x.png", 47 | "scale" : "3x" 48 | }, 49 | { 50 | "idiom" : "ipad", 51 | "size" : "20x20", 52 | "scale" : "1x" 53 | }, 54 | { 55 | "idiom" : "ipad", 56 | "size" : "20x20", 57 | "scale" : "2x" 58 | }, 59 | { 60 | "size" : "29x29", 61 | "idiom" : "ipad", 62 | "filename" : "Icon-Small.png", 63 | "scale" : "1x" 64 | }, 65 | { 66 | "size" : "29x29", 67 | "idiom" : "ipad", 68 | "filename" : "Icon-Small@2x.png", 69 | "scale" : "2x" 70 | }, 71 | { 72 | "size" : "40x40", 73 | "idiom" : "ipad", 74 | "filename" : "Icon-40.png", 75 | "scale" : "1x" 76 | }, 77 | { 78 | "size" : "40x40", 79 | "idiom" : "ipad", 80 | "filename" : "Icon-40@2x.png", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "size" : "76x76", 85 | "idiom" : "ipad", 86 | "filename" : "Icon-76.png", 87 | "scale" : "1x" 88 | }, 89 | { 90 | "size" : "76x76", 91 | "idiom" : "ipad", 92 | "filename" : "Icon-76@2x.png", 93 | "scale" : "2x" 94 | }, 95 | { 96 | "size" : "83.5x83.5", 97 | "idiom" : "ipad", 98 | "filename" : "Icon-83.5@2x.png", 99 | "scale" : "2x" 100 | }, 101 | { 102 | "idiom" : "ios-marketing", 103 | "size" : "1024x1024", 104 | "scale" : "1x" 105 | } 106 | ], 107 | "info" : { 108 | "version" : 1, 109 | "author" : "xcode" 110 | } 111 | } -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-40.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-76.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-Small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-Small.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/LaunchLogo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchLogo.pdf", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/LaunchLogo.imageset/LaunchLogo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/LaunchLogo.imageset/LaunchLogo.pdf -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/account.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "account.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "account@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "account@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/account.imageset/account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/account.imageset/account.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/account.imageset/account@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/account.imageset/account@2x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/account.imageset/account@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/account.imageset/account@3x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/icon_listview-deselected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icon_listview-deselected.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icon_listview-deselected@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icon_listview-deselected@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/icon_listview-deselected.imageset/icon_listview-deselected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/icon_listview-deselected.imageset/icon_listview-deselected.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/icon_listview-deselected.imageset/icon_listview-deselected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/icon_listview-deselected.imageset/icon_listview-deselected@2x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/icon_listview-deselected.imageset/icon_listview-deselected@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/icon_listview-deselected.imageset/icon_listview-deselected@3x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/icon_listview-selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icon_listview-selected.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icon_listview-selected@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icon_listview-selected@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/icon_listview-selected.imageset/icon_listview-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/icon_listview-selected.imageset/icon_listview-selected.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/icon_listview-selected.imageset/icon_listview-selected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/icon_listview-selected.imageset/icon_listview-selected@2x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/icon_listview-selected.imageset/icon_listview-selected@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/icon_listview-selected.imageset/icon_listview-selected@3x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/icon_mapview-deselected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icon_mapview-deselected.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icon_mapview-deselected@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icon_mapview-deselected@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/icon_mapview-deselected.imageset/icon_mapview-deselected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/icon_mapview-deselected.imageset/icon_mapview-deselected.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/icon_mapview-deselected.imageset/icon_mapview-deselected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/icon_mapview-deselected.imageset/icon_mapview-deselected@2x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/icon_mapview-deselected.imageset/icon_mapview-deselected@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/icon_mapview-deselected.imageset/icon_mapview-deselected@3x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/icon_mapview-selected.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icon_mapview-selected.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icon_mapview-selected@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icon_mapview-selected@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/icon_mapview-selected.imageset/icon_mapview-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/icon_mapview-selected.imageset/icon_mapview-selected.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/icon_mapview-selected.imageset/icon_mapview-selected@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/icon_mapview-selected.imageset/icon_mapview-selected@2x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/icon_mapview-selected.imageset/icon_mapview-selected@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/icon_mapview-selected.imageset/icon_mapview-selected@3x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/icon_world.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icon_world.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icon_world@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icon_world@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/icon_world.imageset/icon_world.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/icon_world.imageset/icon_world.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/icon_world.imageset/icon_world@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/icon_world.imageset/icon_world@2x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/icon_world.imageset/icon_world@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/icon_world.imageset/icon_world@3x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/loading.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "loading.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "loading@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "loading@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/loading.imageset/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/loading.imageset/loading.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/loading.imageset/loading@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/loading.imageset/loading@2x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/loading.imageset/loading@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/loading.imageset/loading@3x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/map-pin.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "map-pin.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "map-pin@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "map-pin@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/map-pin.imageset/map-pin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/map-pin.imageset/map-pin.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/map-pin.imageset/map-pin@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/map-pin.imageset/map-pin@2x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Assets.xcassets/map-pin.imageset/map-pin@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/On The Map/Assets.xcassets/map-pin.imageset/map-pin@3x.png -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/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 | -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Home/Map/MapViewController+MapViewDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapViewController+MapViewDelegate.swift 3 | // On The Map 4 | // 5 | // Created by Rajanikant Deshmukh on 19/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import MapKit 11 | 12 | extension MapViewController: MKMapViewDelegate { 13 | 14 | func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { 15 | 16 | let reuseId = "pin" 17 | 18 | var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) as? MKPinAnnotationView 19 | 20 | if pinView == nil { 21 | pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId) 22 | pinView!.canShowCallout = true 23 | pinView!.pinTintColor = UIColor.red 24 | pinView!.rightCalloutAccessoryView = UIButton(type: .detailDisclosure) 25 | } else { 26 | pinView!.annotation = annotation 27 | } 28 | 29 | return pinView 30 | } 31 | 32 | func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) { 33 | if control == view.rightCalloutAccessoryView { 34 | openUrl(url: view.annotation?.subtitle!) 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Home/Map/MapViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapViewController.swift 3 | // On The Map 4 | // 5 | // Created by Rajanikant Deshmukh on 18/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import MapKit 12 | 13 | class MapViewController: StudentLocationViewController { 14 | 15 | @IBOutlet weak var mapView: MKMapView! 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | mapView.delegate = self 20 | } 21 | 22 | override func studentLocationLoaded(error: Errors?) { 23 | if error != nil { 24 | showAlertDialog(title: "Error", message: (error)!.rawValue, dismissHandler: nil) 25 | return 26 | } 27 | 28 | // Set annotations on map 29 | var annotations = [MKPointAnnotation]() 30 | for studentLocation in Cache.shared.studentLocations { 31 | annotations.append(studentLocation.annotation()) 32 | } 33 | self.mapView.addAnnotations(annotations) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Home/StudentLocationViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StudentLocationViewController.swift 3 | // On The Map 4 | // 5 | // Created by Rajanikant Deshmukh on 26/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class StudentLocationViewController: UIViewController { 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | 17 | ParseHandler.shared.loadStudentLocations(limit: 100, skip: 0, onComplete: {error in 18 | DispatchQueue.main.async(execute: { 19 | self.studentLocationLoaded(error: error) 20 | }) 21 | }) 22 | } 23 | 24 | func studentLocationLoaded(error: Errors?) { 25 | // To be overridden 26 | } 27 | 28 | func openUrl(url: String?) { 29 | if let url = URL(string: url!) { 30 | let app = UIApplication.shared 31 | app.open(url, options: [:], completionHandler: nil) 32 | } else { 33 | showAlertDialog(title: "Ooops!", message: "The media URL provided by this student is not a valid URL", dismissHandler: nil) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Home/Table/TableViewController+TableViewDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewController+TableViewDelegate.swift 3 | // On The Map 4 | // 5 | // Created by Rajanikant Deshmukh on 20/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | extension TableViewController: UITableViewDelegate, UITableViewDataSource { 13 | 14 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 15 | return Cache.shared.studentLocations.count 16 | } 17 | 18 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 19 | let studentLocation = Cache.shared.studentLocations[indexPath.row] 20 | let cell = tableView.dequeueReusableCell(withIdentifier: TableViewCellReuseIdentifier) 21 | cell?.textLabel?.text = "\(studentLocation.firstName) \(studentLocation.lastName)" 22 | cell?.detailTextLabel?.text = studentLocation.mapString 23 | return cell! 24 | } 25 | 26 | func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 27 | let studentLocation = Cache.shared.studentLocations[indexPath.row] 28 | openUrl(url: studentLocation.mediaURL) 29 | // Deselect the selected row 30 | tableView.deselectRow(at: indexPath, animated: true) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Home/Table/TableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TableViewController.swift 3 | // On The Map 4 | // 5 | // Created by Rajanikant Deshmukh on 18/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class TableViewController: StudentLocationViewController { 13 | 14 | let TableViewCellReuseIdentifier = "StudentLocationCell" 15 | 16 | @IBOutlet weak var tableView: UITableView! 17 | 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | tableView.delegate = self 21 | tableView.dataSource = self 22 | } 23 | 24 | override func studentLocationLoaded(error: Errors?) { 25 | if error != nil { 26 | showAlertDialog(title: "Error", message: (error)!.rawValue, dismissHandler: nil) 27 | return 28 | } 29 | tableView.reloadData() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Login/LoginViewController+Extras.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginViewController+TextFieldDelegate.swift 3 | // On The Map 4 | // 5 | // Created by Rajanikant Deshmukh on 17/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | extension LoginViewController : UITextFieldDelegate { 13 | 14 | // MARK: UITextFieldDelegate Methods 15 | 16 | func textFieldShouldReturn(_ textField: UITextField) -> Bool { 17 | if (textField == emailTextField) { 18 | passwordTextField.becomeFirstResponder() 19 | } else { 20 | textField.resignFirstResponder() 21 | startLogin() 22 | } 23 | return true 24 | } 25 | 26 | // MARK: Keyboard Methods 27 | 28 | func subscribeToKeyboardNotifications() { 29 | // Use UIKeyboard WillChangeFrame instead of WillShow for supporting multiple keyboards 30 | NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillChange(_:)), name: .UIKeyboardWillChangeFrame, object: nil) 31 | NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: .UIKeyboardWillHide, object: nil) 32 | } 33 | 34 | func unsubscribeFromKeyboardNotifications() { 35 | // Removes all notification observers 36 | NotificationCenter.default.removeObserver(self) 37 | } 38 | 39 | @objc func keyboardWillChange(_ notification:Notification) { 40 | // Bottom Y point of the stack view 41 | let safeBottom = self.stackView.frame.maxY + 16 42 | 43 | // Top Y point of keyboard 44 | let keyboardTop = self.view.frame.height - getKeyboardHeight(notification) 45 | 46 | let offset = safeBottom - keyboardTop 47 | 48 | // If the stackview is completely visible, no need to shift view 49 | if (offset <= 0) { 50 | return 51 | } 52 | 53 | UIView.animate(withDuration: 0.3, animations: { 54 | self.view.frame.origin.y = 0 55 | self.view.frame.origin.y -= offset 56 | }) 57 | } 58 | 59 | @objc func keyboardWillHide(_ notification:Notification) { 60 | // Reset the view to it's original position 61 | UIView.animate(withDuration: 0.3, animations: { 62 | self.view.frame.origin.y = 0 63 | }) 64 | } 65 | 66 | func getKeyboardHeight(_ notification:Notification) -> CGFloat { 67 | let userInfo = notification.userInfo 68 | let keyboardSize = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue 69 | return keyboardSize.cgRectValue.height 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Model/Response/UdacityAuthResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UdacityAuthResponse.swift 3 | // On The Map 4 | // 5 | // Created by Rajanikant Deshmukh on 26/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class UdacityAuthResponse { 12 | 13 | var sessionId: String 14 | var expiration: Date 15 | var accountKey: String 16 | 17 | init(sessionId: String, expirationString: String, accountKey: String) { 18 | self.sessionId = sessionId 19 | self.accountKey = accountKey 20 | 21 | // Create DateFormatter to format String 22 | let dateFormatter = DateFormatter() 23 | dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'" 24 | dateFormatter.timeZone = TimeZone.current 25 | 26 | // Set default session expiray to one hour after current time 27 | let defaultExpiration = Calendar.current.date(byAdding: .hour, value: 1, to: Date()) 28 | 29 | // Parse session expiration date 30 | self.expiration = dateFormatter.date(from: expirationString) ?? defaultExpiration! 31 | } 32 | 33 | init(_ dictionary: NSDictionary) { 34 | let account: NSDictionary = dictionary[ApiConstants.UdacityAuth.KEY_ACCOUNT] as! NSDictionary 35 | self.accountKey = account[ApiConstants.UdacityAuth.KEY_ACC_KEY] as! String 36 | 37 | let session: NSDictionary = dictionary[ApiConstants.UdacityAuth.KEY_SESSION] as! NSDictionary 38 | self.sessionId = session[ApiConstants.UdacityAuth.KEY_ID] as! String 39 | 40 | let expirationString: String = session[ApiConstants.UdacityAuth.KEY_EXPIRATION] as! String 41 | 42 | // Create DateFormatter to format String 43 | let dateFormatter = DateFormatter() 44 | dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'" 45 | dateFormatter.timeZone = TimeZone.current 46 | 47 | // Set default session expiray to one hour after current time 48 | let defaultExpiration = Calendar.current.date(byAdding: .hour, value: 1, to: Date()) 49 | 50 | // Parse session expiration date 51 | self.expiration = dateFormatter.date(from: expirationString) ?? defaultExpiration! 52 | } 53 | 54 | func dateString() -> String { 55 | let dateFormatter = DateFormatter() 56 | dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSS'Z'" 57 | dateFormatter.timeZone = TimeZone.current 58 | 59 | return dateFormatter.string(from: self.expiration) 60 | } 61 | 62 | func isExpired() -> Bool { 63 | return Date() > self.expiration 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Model/StudentLocation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StudentLocation.swift 3 | // On The Map 4 | // 5 | // Created by Rajanikant Deshmukh on 26/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class StudentLocation { 12 | 13 | var objectId : String 14 | var uniqueKey : String 15 | 16 | var firstName : String 17 | var lastName : String 18 | var latitude : Double 19 | var longitude : Double 20 | var mapString : String 21 | var mediaURL : String 22 | 23 | var createdAt : Date 24 | var updatedAt : Date 25 | 26 | let dateFormatter = ISO8601DateFormatter() 27 | 28 | init?(_ dictionary: NSDictionary) { 29 | // These keys are compulsory for Parse and every object will have it 30 | self.objectId = (dictionary["objectId"] as! String) 31 | self.uniqueKey = (dictionary["uniqueKey"] as! String) 32 | // TODO Parse Date 33 | self.createdAt = dateFormatter.date(from: dictionary["createdAt"] as! String) ?? Date() 34 | self.updatedAt = Date() 35 | 36 | // Since the Parse data is not being validated on ServerSide 37 | // We implemement sanity checks on our side 38 | 39 | // Sanity check for name 40 | if dictionary["firstName"] == nil || dictionary["lastName"] == nil { 41 | return nil 42 | } 43 | self.firstName = dictionary["firstName"] as! String 44 | self.lastName = (dictionary["lastName"] as! String) 45 | 46 | // Sanity check for locations 47 | if dictionary["latitude"] == nil || dictionary["longitude"] == nil { 48 | return nil 49 | } 50 | self.latitude = (dictionary["latitude"] as! Double) 51 | self.longitude = (dictionary["longitude"] as! Double) 52 | 53 | // Sanity check for address and media URL 54 | if dictionary["mapString"] == nil || dictionary["mediaURL"] == nil { 55 | return nil 56 | } 57 | self.mapString = (dictionary["mapString"] as! String) 58 | self.mediaURL = (dictionary["mediaURL"] as! String) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Model/UserInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserInfo.swift 3 | // On The Map 4 | // 5 | // Created by Rajanikant Deshmukh on 26/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class UserInfo { 12 | 13 | var firstName: String 14 | var lastName: String 15 | var nickName: String 16 | var imageUrl: String 17 | 18 | init(_ dictionary: NSDictionary) { 19 | let user: NSDictionary = dictionary["user"] as! NSDictionary 20 | 21 | self.firstName = user["first_name"] as? String ?? "" 22 | self.lastName = user["last_name"] as? String ?? "" 23 | self.nickName = user["nickname"] as? String ?? "" 24 | self.imageUrl = user["_image_url"] as? String ?? "" 25 | } 26 | 27 | func isValid() -> Bool { 28 | return !self.firstName.isEmpty && !self.lastName.isEmpty 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Storage/Cache.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Cache.swift 3 | // On The Map 4 | // 5 | // Created by Rajanikant Deshmukh on 23/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class Cache { 12 | 13 | static let shared = Cache() 14 | 15 | var userInfo: UserInfo? 16 | var studentLocations: [StudentLocation] 17 | 18 | private init() { 19 | studentLocations = [StudentLocation]() 20 | } 21 | 22 | func clear() { 23 | userInfo = nil 24 | studentLocations.removeAll() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Storage/Storage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Storage.swift 3 | // On The Map 4 | // 5 | // Created by Rajanikant Deshmukh on 23/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class UserAuthStorage { 12 | 13 | static let shared: UserAuthStorage = UserAuthStorage() 14 | 15 | private init() {} 16 | 17 | func getStoredUserAuth() -> UdacityAuthResponse? { 18 | // Return from UserDefaults 19 | let sessionId = UserDefaults.standard.string(forKey: "UdacityAuthResponse.sessionId") 20 | let accountKey = UserDefaults.standard.string(forKey: "UdacityAuthResponse.accountKey") 21 | let expirationString = UserDefaults.standard.string(forKey: "UdacityAuthResponse.dateString") 22 | if (sessionId != nil && accountKey != nil && expirationString != nil) { 23 | return UdacityAuthResponse(sessionId: sessionId!, expirationString: expirationString!, accountKey: accountKey!) 24 | } 25 | return nil 26 | } 27 | 28 | func storeUserAuth(_ auth: UdacityAuthResponse) { 29 | // Store in UserDefaults 30 | UserDefaults.standard.set(auth.sessionId, forKey: "UdacityAuthResponse.sessionId") 31 | UserDefaults.standard.set(auth.accountKey, forKey: "UdacityAuthResponse.accountKey") 32 | UserDefaults.standard.set(auth.dateString(), forKey: "UdacityAuthResponse.dateString") 33 | } 34 | 35 | func clearUserAuth() { 36 | // Clear UserDefaults 37 | UserDefaults.standard.removeObject(forKey: "UdacityAuthResponse.sessionId") 38 | UserDefaults.standard.removeObject(forKey: "UdacityAuthResponse.accountKey") 39 | UserDefaults.standard.removeObject(forKey: "UdacityAuthResponse.dateString") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Utils/Errors.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Errors.swift 3 | // On The Map 4 | // 5 | // Created by Rajanikant Deshmukh on 21/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum Errors: String { 12 | case WrongCredentialError = "Account not found or invalid credentials." 13 | case CredentialExpiredError = "Login expired. Please login again." 14 | case NetworkError = "Please check your network and try again." 15 | case ServerError = "Server messed up. Please contact developer." 16 | case UnknownError = "The app is messed up. Please contact developer." 17 | } 18 | -------------------------------------------------------------------------------- /P3 OnTheMap/On The Map/Utils/Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // On The Map 4 | // 5 | // Created by Rajanikant Deshmukh on 19/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import MapKit 11 | 12 | extension Dictionary { 13 | func json() -> String { 14 | do { 15 | let jsonData = try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted) 16 | return String(bytes: jsonData, encoding: String.Encoding.utf8) ?? "" 17 | } catch { 18 | return "" 19 | } 20 | } 21 | } 22 | 23 | extension StudentLocation { 24 | 25 | func annotation() -> MKPointAnnotation { 26 | let annotation = MKPointAnnotation() 27 | annotation.coordinate.latitude = self.latitude 28 | annotation.coordinate.longitude = longitude 29 | annotation.title = "\(self.firstName) \(self.lastName)" 30 | annotation.subtitle = self.mediaURL 31 | return annotation 32 | } 33 | } 34 | 35 | extension UIViewController { 36 | 37 | func showAlertDialog(title: String, message: String, dismissHandler: ((UIAlertAction) -> Void)?) { 38 | let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) 39 | alert.addAction(UIAlertAction(title: "OK", style: .default, handler: dismissHandler)) 40 | self.present(alert, animated: true) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /P3 OnTheMap/README.md: -------------------------------------------------------------------------------- 1 | # On The Map 2 | On The Map displays information posted by other Udacity students, by interacting with Udacity's Authentication and Parse APIs. The app has a map view with pins representing student locations, and by tapping a pin, the app will display a custom URL posted by a student. 3 | 4 | # Screenshots 5 | 6 | | Login Screen | Student Pin | List | 7 | | ------------ | ----------- | ---- | 8 | | ![Login.png](Screenshots/Login.png) | ![Map-Pin.png](Screenshots/Map-Pin.png) | ![List.png](Screenshots/List.png) 9 | 10 | | Account | Add Pin | Add Profile | 11 | | ------- | ------- | ----------- | 12 | | ![Account.png](Screenshots/Account.png) | ![AddPin.png](Screenshots/AddPin.png) | ![AddProfile.png](Screenshots/AddProfile.png) | 13 | 14 | ## Frameworks Used 15 | 1. [Foundation](https://developer.apple.com/documentation/foundation) 16 | 2. [UIKit](https://developer.apple.com/documentation/uikit) 17 | 3. [MapKit](https://developer.apple.com/documentation/mapkit) 18 | 19 | ## How to Build 20 | 1. Download zip or fork & clone project on your desktop. 21 | 2. Open `On The Map.xcodeproj` file in Xcode. 22 | 3. Now you can build and run the app. 23 | 24 | ## Licence 25 | This project is a part of Master Projects repository. For other projects and licensing information, please see [iOS Nanodegree Projects](https://github.com/rajanikantdeshmukh/iOS-Nanodegree-Projects). 26 | -------------------------------------------------------------------------------- /P3 OnTheMap/Screenshots/Account.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/Screenshots/Account.png -------------------------------------------------------------------------------- /P3 OnTheMap/Screenshots/AddPin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/Screenshots/AddPin.png -------------------------------------------------------------------------------- /P3 OnTheMap/Screenshots/AddProfile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/Screenshots/AddProfile.png -------------------------------------------------------------------------------- /P3 OnTheMap/Screenshots/List.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/Screenshots/List.png -------------------------------------------------------------------------------- /P3 OnTheMap/Screenshots/Login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/Screenshots/Login.png -------------------------------------------------------------------------------- /P3 OnTheMap/Screenshots/Map-Pin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/Screenshots/Map-Pin.png -------------------------------------------------------------------------------- /P3 OnTheMap/Screenshots/Map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P3 OnTheMap/Screenshots/Map.png -------------------------------------------------------------------------------- /P4 VirtualTourist/.gitignore: -------------------------------------------------------------------------------- 1 | ## Build generated 2 | build/ 3 | DerivedData/ 4 | 5 | ## Various settings 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | 16 | ## Other 17 | *.moved-aside 18 | *.xccheckout 19 | *.xcscmblueprint 20 | 21 | ## Obj-C/Swift specific 22 | *.hmap 23 | *.ipa 24 | *.dSYM.zip 25 | *.dSYM 26 | 27 | ## Playgrounds 28 | timeline.xctimeline 29 | playground.xcworkspace 30 | 31 | # Swift Package Manager 32 | .build/ 33 | 34 | # CocoaPods 35 | Pods/ 36 | -------------------------------------------------------------------------------- /P4 VirtualTourist/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'Virtual Tourist' do 5 | pod 'Kingfisher', '~> 4.0' 6 | # Comment the next line if you're not using Swift and don't want to use dynamic frameworks 7 | use_frameworks! 8 | 9 | # Pods for Virtual Tourist 10 | 11 | end 12 | -------------------------------------------------------------------------------- /P4 VirtualTourist/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Kingfisher (4.6.4) 3 | 4 | DEPENDENCIES: 5 | - Kingfisher (~> 4.0) 6 | 7 | SPEC CHECKSUMS: 8 | Kingfisher: 68498a706e46c2a050b0c9572f100e5db407f6e0 9 | 10 | PODFILE CHECKSUM: 6741e6c76d9bb14d4f8b3b94d6715c292cfd7184 11 | 12 | COCOAPODS: 1.4.0 13 | -------------------------------------------------------------------------------- /P4 VirtualTourist/README.md: -------------------------------------------------------------------------------- 1 | # Virtual Tourist 2 | Tour the world without leaving the comforts of your couch! Virtual Tourist lets you tour the world from the comfort of your own couch. Users is able to drop pins on a map and download pictures for the location. 3 | 4 | ## Screenshots 5 | 6 | | Map | Album | Map: Delete Mode | 7 | | --- | ----- | ---------------- | 8 | | ![Map-ViewPin.png](Screenshots/Map-ViewPin.png) | ![Album.png](Screenshots/Album.png) | ![Map-DeletePin.png](Screenshots/Map-DeletePin.png) 9 | 10 | ## Libraries and Frameworks Used 11 | **iOS Frameworks**: 12 | 1. [Foundation](https://developer.apple.com/documentation/foundation) 13 | 2. [UIKit](https://developer.apple.com/documentation/uikit) 14 | 3. [MapKit](https://developer.apple.com/documentation/mapkit) 15 | 4. [Core Data](https://developer.apple.com/documentation/coredata) to store data and images. 16 | 17 | **External Library** 18 | 6. [Kingfisher](https://github.com/onevcat/Kingfisher/) for easy image loading. 19 | 20 | ## How to Build 21 | The build system uses [CocoaPods](https://cocoapods.org) to integrate dependencies. You should be familiar with CocoaPods and API key and secret from Flickr account. 22 | 1. Download zip or fork & clone project on your desktop. 23 | 2. Open Terminal and `cd` into project folder. 24 | 3. Run `pods install` to install dependencies. 25 | 4. Open `Virtual Tourist/Virtual Tourist.xcworkspace` with Xcode. 26 | 5. Now you can build and run the app. 27 | 28 | ## Licence 29 | This project is a part of Master Projects repository. For other projects and licensing information, please see [iOS Nanodegree Projects](https://github.com/rajanikantdeshmukh/iOS-Nanodegree-Projects). 30 | -------------------------------------------------------------------------------- /P4 VirtualTourist/Screenshots/Album.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Screenshots/Album.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Screenshots/Map-DeletePin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Screenshots/Map-DeletePin.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Screenshots/Map-ViewPin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Screenshots/Map-ViewPin.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Virtual Tourist 4 | // 5 | // Created by Rajanikant Deshmukh on 22/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreData 11 | 12 | @UIApplicationMain 13 | class AppDelegate: UIResponder, UIApplicationDelegate { 14 | 15 | var window: UIWindow? 16 | 17 | let dataController = DataController() 18 | var coreDataEnabled = false 19 | 20 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 21 | 22 | dataController.load(completion: { 23 | print("DataController Loaded successfullys") 24 | self.coreDataEnabled = true 25 | }) 26 | 27 | // Inject data controller into first ViewController 28 | let navigationController = window?.rootViewController as! UINavigationController 29 | let mapViewController = navigationController.topViewController as! TravelMapViewController 30 | mapViewController.dataController = self.dataController 31 | 32 | return true 33 | } 34 | 35 | func applicationWillTerminate(_ application: UIApplication) { 36 | self.saveContext() 37 | } 38 | 39 | func applicationDidEnterBackground(_ application: UIApplication) { 40 | self.saveContext() 41 | } 42 | 43 | // MARK: - Core Data Saving support 44 | 45 | func saveContext () { 46 | let context = dataController.viewContext 47 | if context.hasChanges { 48 | do { 49 | try context.save() 50 | } catch { 51 | let nserror = error as NSError 52 | print("Unresolved error \(nserror), \(nserror.userInfo)") 53 | } 54 | } 55 | } 56 | } 57 | 58 | -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/PrimaryColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | }, 6 | "colors" : [ 7 | { 8 | "idiom" : "universal", 9 | "color" : { 10 | "color-space" : "srgb", 11 | "components" : { 12 | "red" : "0.000", 13 | "alpha" : "1.000", 14 | "blue" : "0.533", 15 | "green" : "0.588" 16 | } 17 | } 18 | } 19 | ] 20 | } -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_delete.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icon_delete.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icon_delete@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icon_delete@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_delete.imageset/icon_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_delete.imageset/icon_delete.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_delete.imageset/icon_delete@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_delete.imageset/icon_delete@2x.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_delete.imageset/icon_delete@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_delete.imageset/icon_delete@3x.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_delete_dark.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icon_delete_dark.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icon_delete_dark@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icon_delete_dark@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_delete_dark.imageset/icon_delete_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_delete_dark.imageset/icon_delete_dark.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_delete_dark.imageset/icon_delete_dark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_delete_dark.imageset/icon_delete_dark@2x.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_delete_dark.imageset/icon_delete_dark@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_delete_dark.imageset/icon_delete_dark@3x.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_done.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icon_done.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icon_done@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icon_done@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_done.imageset/icon_done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_done.imageset/icon_done.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_done.imageset/icon_done@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_done.imageset/icon_done@2x.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_done.imageset/icon_done@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_done.imageset/icon_done@3x.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_edit.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icon_edit.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icon_edit@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icon_edit@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_edit.imageset/icon_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_edit.imageset/icon_edit.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_edit.imageset/icon_edit@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_edit.imageset/icon_edit@2x.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_edit.imageset/icon_edit@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_edit.imageset/icon_edit@3x.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_info_dark.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icon_info_dark.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icon_info_dark@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icon_info_dark@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_info_dark.imageset/icon_info_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_info_dark.imageset/icon_info_dark.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_info_dark.imageset/icon_info_dark@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_info_dark.imageset/icon_info_dark@2x.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_info_dark.imageset/icon_info_dark@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_info_dark.imageset/icon_info_dark@3x.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_refresh.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "icon_refresh.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "icon_refresh@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "icon_refresh@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_refresh.imageset/icon_refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_refresh.imageset/icon_refresh.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_refresh.imageset/icon_refresh@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_refresh.imageset/icon_refresh@2x.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_refresh.imageset/icon_refresh@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Assets.xcassets/icon_refresh.imageset/icon_refresh@3x.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/placeholder_image.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "placeholder_image.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "placeholder_image@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "placeholder_image@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/placeholder_image.imageset/placeholder_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Assets.xcassets/placeholder_image.imageset/placeholder_image.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/placeholder_image.imageset/placeholder_image@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Assets.xcassets/placeholder_image.imageset/placeholder_image@2x.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Assets.xcassets/placeholder_image.imageset/placeholder_image@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Assets.xcassets/placeholder_image.imageset/placeholder_image@3x.png -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Data/DataController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataController.swift 3 | // Virtual Tourist 4 | // 5 | // Created by Rajanikant Deshmukh on 28/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | class DataController { 13 | 14 | let persistanceContainer: NSPersistentContainer 15 | 16 | init(modelName: String = "Virtual_Tourist") { 17 | self.persistanceContainer = NSPersistentContainer(name: modelName) 18 | } 19 | 20 | var viewContext: NSManagedObjectContext { 21 | return self.persistanceContainer.viewContext 22 | } 23 | 24 | func load(completion: (() -> Void)? = nil) { 25 | self.persistanceContainer.loadPersistentStores(completionHandler: { 26 | storeDescription, error in 27 | guard error == nil else { 28 | fatalError(error!.localizedDescription) 29 | } 30 | 31 | completion?() 32 | }) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Data/MapViewDefaults.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapViewDefaults.swift 3 | // Virtual Tourist 4 | // 5 | // Created by Rajanikant Deshmukh on 27/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import MapKit 11 | 12 | class MapViewDefaults { 13 | 14 | private init() {} 15 | 16 | class func setDefaults(toSetMapView: MKMapView) { 17 | // For first time launch, the hasPreviousState will be false 18 | let hasPreviousState = UserDefaults.standard.bool(forKey: "hasPreviousState") 19 | 20 | // If the flag is set, values are saved in UserDefaults and we are free to use those 21 | if hasPreviousState { 22 | let latDouble = UserDefaults.standard.double(forKey:"lat") 23 | let lngDouble = UserDefaults.standard.double(forKey:"lng") 24 | let altInt = UserDefaults.standard.integer(forKey:"alt") 25 | 26 | toSetMapView.centerCoordinate.latitude = CLLocationDegrees(latDouble) 27 | toSetMapView.centerCoordinate.longitude = CLLocationDegrees(lngDouble) 28 | toSetMapView.camera.altitude = CLLocationDistance(altInt) 29 | } 30 | } 31 | 32 | class func saveDefaults(toSaveMapView: MKMapView) { 33 | // Get current state values from map 34 | let lat = toSaveMapView.centerCoordinate.latitude 35 | let lng = toSaveMapView.centerCoordinate.longitude 36 | let alt = toSaveMapView.camera.altitude 37 | 38 | // Save them in user defaults 39 | UserDefaults.standard.set(Double(lat), forKey: "lat") 40 | UserDefaults.standard.set(Double(lng), forKey: "lng") 41 | UserDefaults.standard.set(Int64(alt), forKey: "alt") 42 | // Set flag to show that the app have a previous state saved 43 | UserDefaults.standard.set(true, forKey: "hasPreviousState") 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Fonts/Raleway-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Fonts/Raleway-Light.ttf -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Fonts/Raleway-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P4 VirtualTourist/Virtual Tourist/Fonts/Raleway-Regular.ttf -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Model/Virtual_Tourist.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | Virtual_Tourist.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Model/Virtual_Tourist.xcdatamodeld/Virtual_Tourist.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/UI/Album/PhotoAlbumViewController+Extras.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhotoAlbumViewController+Extras.swift 3 | // Virtual Tourist 4 | // 5 | // Created by Rajanikant Deshmukh on 28/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | extension PhotoAlbumViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { 13 | 14 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 15 | return album.count 16 | } 17 | 18 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 19 | // Load Image 20 | let photo = album[indexPath.row] 21 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PhotoViewCell", for: indexPath) as! PhotoViewCell 22 | cell.setPhoto(photo, onComplete: { 23 | self.loadedImageCount! += 1 24 | if self.loadedImageCount == self.album.count { 25 | self.refreshButton.isEnabled = true 26 | } 27 | }) 28 | return cell 29 | } 30 | 31 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 32 | // Delete photo 33 | let photo = album.remove(at: indexPath.row) 34 | dataController.viewContext.delete(photo) 35 | collectionView.reloadData() 36 | } 37 | 38 | // MARK:- UICollectionViewDelegateFlowLayout Methods 39 | 40 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 41 | let width = UIScreen.main.bounds.width 42 | let spacificWidth = (width - (2*margin + internalSpacing * CGFloat(numberOfItems - 1)))/CGFloat(numberOfItems) 43 | return CGSize(width: spacificWidth, height: spacificWidth) 44 | } 45 | 46 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { 47 | return UIEdgeInsets(top: margin, left: margin, bottom: margin, right: margin) 48 | } 49 | 50 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { 51 | return internalSpacing 52 | } 53 | 54 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { 55 | return internalSpacing 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/UI/View/PhotoViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhotoViewCell.swift 3 | // Virtual Tourist 4 | // 5 | // Created by Rajanikant Deshmukh on 02/04/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Kingfisher 11 | 12 | class PhotoViewCell: UICollectionViewCell { 13 | 14 | 15 | @IBOutlet weak var imageView: UIImageView! 16 | 17 | override func awakeFromNib() { 18 | super.awakeFromNib() 19 | } 20 | 21 | func setPhoto(_ photo: Photo, onComplete: @escaping () -> Void) { 22 | if let data = photo.image { 23 | imageView.image = UIImage(data: data) 24 | onComplete() 25 | } else { 26 | imageView.kf.setImage( 27 | with: URL(string: photo.url!), 28 | placeholder: #imageLiteral(resourceName: "placeholder_image"), 29 | options: nil, 30 | progressBlock: nil, 31 | completionHandler: { (image, error, _, _) in 32 | if let thisImage = image { 33 | photo.image = UIImagePNGRepresentation(thisImage) 34 | } 35 | onComplete() 36 | }) 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/UI/View/PhotoViewCell.xib: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Utils/Errors.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Errors.swift 3 | // Virtual Tourist 4 | // 5 | // Created by Rajanikant Deshmukh on 02/04/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum Errors: String { 12 | case NetworkError = "Please check your network and try again." 13 | case ServerError = "Server messed up. Please contact developer." 14 | case DatabaseError = "Database Error." 15 | case UnknownError = "The app is messed up. Please contact developer." 16 | } 17 | -------------------------------------------------------------------------------- /P4 VirtualTourist/Virtual Tourist/Utils/Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // Virtual Tourist 4 | // 5 | // Created by Rajanikant Deshmukh on 28/03/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import MapKit 12 | 13 | extension UIViewController { 14 | 15 | func showAlertDialog(title: String, message: String, dismissHandler: ((UIAlertAction) -> Void)?) { 16 | let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) 17 | alert.addAction(UIAlertAction(title: "OK", style: .default, handler: dismissHandler)) 18 | self.present(alert, animated: true) 19 | } 20 | } 21 | 22 | extension Place { 23 | 24 | func getAnnotation() -> MKAnnotation { 25 | let annotation = MKPointAnnotation() 26 | annotation.title = self.locationString 27 | annotation.coordinate = CLLocationCoordinate2DMake(self.latitude, self.longitude) 28 | return annotation 29 | } 30 | } 31 | 32 | extension Photo { 33 | 34 | convenience init?(_ dictionary: NSDictionary) { 35 | self.init() 36 | self.id = (dictionary["id"] as! String) 37 | self.title = (dictionary["title"] as! String) 38 | self.url = (dictionary["url_m"] as! String) 39 | self.width = (dictionary["width_m"] as! Int16) 40 | self.height = (dictionary["height_m"] as! Int16) 41 | } 42 | 43 | func setFrom(_ dictionary: NSDictionary) { 44 | self.id = (dictionary["id"] as! String) 45 | self.title = (dictionary["title"] as! String) 46 | self.url = (dictionary["url_m"] as? String) ?? "" 47 | self.width = Int16(dictionary["width_m"] as? String ?? "0")! 48 | self.height = Int16(dictionary["height_m"] as? String ?? "0")! 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/.gitignore: -------------------------------------------------------------------------------- 1 | ## Build generated 2 | build/ 3 | DerivedData/ 4 | 5 | ## Various settings 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | 16 | ## Other 17 | *.moved-aside 18 | *.xccheckout 19 | *.xcscmblueprint 20 | 21 | ## Obj-C/Swift specific 22 | *.hmap 23 | *.ipa 24 | *.dSYM.zip 25 | *.dSYM 26 | 27 | ## Playgrounds 28 | timeline.xctimeline 29 | playground.xcworkspace 30 | 31 | # Swift Package Manager 32 | .build/ 33 | 34 | # CocoaPods 35 | Pods/ 36 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/json-feed.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "json-feed.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "json-feed@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "json-feed@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/json-feed.imageset/json-feed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/json-feed.imageset/json-feed.png -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/json-feed.imageset/json-feed@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/json-feed.imageset/json-feed@2x.png -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/json-feed.imageset/json-feed@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/json-feed.imageset/json-feed@3x.png -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/tab_feeds.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "tab_feeds.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "tab_feeds@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "tab_feeds@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/tab_feeds.imageset/tab_feeds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/tab_feeds.imageset/tab_feeds.png -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/tab_feeds.imageset/tab_feeds@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/tab_feeds.imageset/tab_feeds@2x.png -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/tab_feeds.imageset/tab_feeds@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/tab_feeds.imageset/tab_feeds@3x.png -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/tab_home.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "tab_home.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "tab_home@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "tab_home@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/tab_home.imageset/tab_home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/tab_home.imageset/tab_home.png -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/tab_home.imageset/tab_home@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/tab_home.imageset/tab_home@2x.png -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/tab_home.imageset/tab_home@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/ae6711bc43d7e81c30456bf4da95d7423515491e/P5 JSON-Feed-Reader/JSON Feed Reader/Assets/Assets.xcassets/tab_home.imageset/tab_home@3x.png -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Data/DataRepository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataRepository.swift 3 | // JSON Feed Reader 4 | // 5 | // Created by Rajanikant Deshmukh on 02/04/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class DataRepository: RepositoryProtocol { 12 | 13 | private let localRepository: LocalRepository 14 | private let remoteRepository: RemoteRepository 15 | 16 | init(local: LocalRepository, remote: RemoteRepository) { 17 | self.localRepository = local 18 | self.remoteRepository = remote 19 | } 20 | 21 | func loadPosts(feed: Feed, onError: @escaping ErrorCallback, onPostsLoaded: @escaping PostsCallback) { 22 | self.localRepository.loadPosts(feed: feed, onError: { 23 | error in 24 | // Load content from remote 25 | self.remoteRepository.loadPosts(feed: feed, onError: { 26 | error in 27 | DispatchQueue.main.async { onError(error) } 28 | }, onPostsLoaded: { 29 | posts in 30 | DispatchQueue.main.async { onPostsLoaded(posts) } 31 | }) 32 | }, onPostsLoaded: { 33 | posts in 34 | DispatchQueue.main.async { onPostsLoaded(posts) } 35 | }) 36 | } 37 | 38 | func loadContent(postId: String, onError: @escaping ErrorCallback, onContentLoaded: @escaping ContentCallback) { 39 | self.localRepository.loadContent(postId: postId, onError: { 40 | error in 41 | // Load content from remote 42 | self.remoteRepository.loadContent(postId: postId, onError: { 43 | error in 44 | DispatchQueue.main.async { onError(error) } 45 | 46 | }, onContentLoaded: { 47 | content in 48 | DispatchQueue.main.async { onContentLoaded(content) } 49 | }) 50 | }, onContentLoaded: { 51 | content in 52 | DispatchQueue.main.async { onContentLoaded(content) } 53 | }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Data/Local/DataController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataController.swift 3 | // JSON Feed Reader 4 | // 5 | // Created by Rajanikant Deshmukh on 02/04/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | class DataController { 13 | 14 | let persistanceContainer: NSPersistentContainer 15 | var isLoaded: Bool! 16 | 17 | init(modelName: String = "JSON_Feed_Reader") { 18 | self.persistanceContainer = NSPersistentContainer(name: modelName) 19 | isLoaded = false 20 | } 21 | 22 | var viewContext: NSManagedObjectContext { 23 | return self.persistanceContainer.viewContext 24 | } 25 | 26 | func load(completion: (() -> Void)? = nil) { 27 | self.persistanceContainer.loadPersistentStores(completionHandler: { 28 | storeDescription, error in 29 | guard error == nil else { 30 | self.isLoaded = false 31 | print("Persistenace Store not Loaded!!!") 32 | fatalError(error!.localizedDescription) 33 | } 34 | 35 | print("Persistance Store loaded successfully") 36 | self.isLoaded = true 37 | PrimaryFeed.shared.insertIfDoNotExist(dataController: self) 38 | completion?() 39 | }) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Data/Local/JSON_Feed_Reader.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | JSON_Feed_Reader.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Data/Local/JSON_Feed_Reader.xcdatamodeld/JSON_Feed_Reader.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Data/Local/LocalRepository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocalRepository.swift 3 | // JSON Feed Reader 4 | // 5 | // Created by Rajanikant Deshmukh on 02/04/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | class LocalRepository: RepositoryProtocol { 13 | 14 | var dataController: DataController 15 | 16 | init(dataController: DataController) { 17 | self.dataController = dataController 18 | } 19 | 20 | func loadPosts(feed: Feed, onError: @escaping ErrorCallback, onPostsLoaded: @escaping PostsCallback) { 21 | if !dataController.isLoaded { 22 | onError(Errors.LocalDatabaseError) 23 | return 24 | } 25 | 26 | let fetchRequest: NSFetchRequest = Post.fetchRequest() 27 | do { 28 | let results = try dataController.viewContext.fetch(fetchRequest) 29 | print("Loaded posts from local db with count \(results.count)") 30 | if results.count <= 0 { 31 | throw Errors.EmptyDatabaseError 32 | } 33 | onPostsLoaded(results) 34 | } catch { 35 | print("Fetch Error") 36 | onError(Errors.LocalDatabaseError) 37 | } 38 | } 39 | 40 | func loadContent(postId: String, onError: @escaping ErrorCallback, onContentLoaded: @escaping ContentCallback) { 41 | if !dataController.isLoaded { 42 | onError(Errors.LocalDatabaseError) 43 | return 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Data/Local/PrimaryFeed.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PrimaryFeed.swift 3 | // JSON Feed Reader 4 | // 5 | // Created by Rajanikant Deshmukh on 03/04/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CoreData 11 | 12 | class PrimaryFeed { 13 | 14 | static let shared = PrimaryFeed() 15 | 16 | private init() {} 17 | 18 | func insertIfDoNotExist(dataController: DataController) { 19 | let feed = Feed(context: dataController.viewContext) 20 | 21 | feed.version = "https://jsonfeed.org/version/1" 22 | feed.title = "JSON Feed" 23 | feed.author = "Brent Simmons and Manton Reece" 24 | feed.desc = "JSON Feed is a pragmatic syndication format for blogs, microblogs, and other time-based content." 25 | feed.favicon_url = "https://jsonfeed.org/graphics/icon.png" 26 | feed.feed_url = "https://jsonfeed.org/feed.json" 27 | 28 | try? dataController.viewContext.save() 29 | } 30 | 31 | func get(dataController: DataController) -> Feed? { 32 | let fetchRequest: NSFetchRequest = Feed.fetchRequest() 33 | if let feeds = try? dataController.viewContext.fetch(fetchRequest) { 34 | if feeds.count <= 0 { 35 | return nil 36 | } 37 | 38 | return feeds[0] 39 | } 40 | 41 | return nil 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Data/Remote/ModelExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModelExtensions.swift 3 | // JSON Feed Reader 4 | // 5 | // Created by Rajanikant Deshmukh on 03/04/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import JSONFeed 11 | 12 | extension Feed { 13 | 14 | func setFrom(_ jsonFeed: JSONFeed) { 15 | self.version = String(describing: jsonFeed.version) 16 | self.title = jsonFeed.title 17 | self.author = jsonFeed.author?.name 18 | self.desc = jsonFeed.feedDescription 19 | self.feed_url = String(describing: jsonFeed.url) 20 | self.favicon_url = String(describing: jsonFeed.favicon) 21 | } 22 | } 23 | 24 | extension Post { 25 | 26 | func setFrom(_ jsonFeedItem: JSONFeedItem) { 27 | self.id = jsonFeedItem.id 28 | self.title = jsonFeedItem.title 29 | self.url = String(describing: jsonFeedItem.url) 30 | self.content_text = jsonFeedItem.contentText 31 | self.content_html = jsonFeedItem.contentHtml 32 | self.date_published = jsonFeedItem.date 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Data/Remote/RemoteRepository.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RemoteRepository.swift 3 | // JSON Feed Reader 4 | // 5 | // Created by Rajanikant Deshmukh on 02/04/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import JSONFeed 11 | 12 | class RemoteRepository: RepositoryProtocol { 13 | 14 | var dataController: DataController 15 | 16 | init(dataController: DataController) { 17 | self.dataController = dataController 18 | } 19 | 20 | func loadPosts(feed: Feed, onError: @escaping ErrorCallback, onPostsLoaded: @escaping PostsCallback) { 21 | let feedUrl = URL(string: feed.feed_url!) 22 | let request = URLRequest(url: feedUrl!) 23 | let task = URLSession.shared.dataTask(with: request) { 24 | data, response, error in 25 | 26 | if error != nil { 27 | self.handleError(error, onError: onError) 28 | return 29 | } 30 | 31 | do { 32 | let jsonFeed = try JSONFeed(data: data!) 33 | var posts = [Post]() 34 | let jsonFeedItems = jsonFeed.items 35 | for jsonFeedItem in jsonFeedItems { 36 | let post = Post(context: self.dataController.viewContext) 37 | post.setFrom(jsonFeedItem) 38 | posts.append(post) 39 | } 40 | onPostsLoaded(posts) 41 | } catch { 42 | self.handleError(error, onError: onError) 43 | } 44 | } 45 | task.resume() 46 | } 47 | 48 | func loadContent(postId: String, onError: @escaping ErrorCallback, onContentLoaded: @escaping ContentCallback) { 49 | // No need to implement in Remote 50 | onError(Errors.UnknownError) 51 | } 52 | 53 | private func handleError(_ error: Error!, onError: @escaping ErrorCallback) { 54 | if let urlError = error as? URLError { 55 | switch urlError.code { 56 | case .notConnectedToInternet: 57 | onError(Errors.NetworkError) 58 | return 59 | default: 60 | onError(Errors.ServerError) 61 | return 62 | } 63 | } 64 | onError(Errors.ServerError) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Data/RepositoryProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepositoryProtocol.swift 3 | // JSON Feed Reader 4 | // 5 | // Created by Rajanikant Deshmukh on 02/04/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | protocol RepositoryProtocol { 12 | 13 | typealias ErrorCallback = ((Errors) -> Void) 14 | 15 | typealias PostsCallback = (([Post]) -> Void) 16 | typealias PostCallback = ((Post) -> Void) 17 | typealias ContentCallback = ((String) -> Void) 18 | 19 | func loadPosts(feed: Feed, onError: @escaping ErrorCallback, onPostsLoaded: @escaping PostsCallback) 20 | 21 | func loadContent(postId: String, onError: @escaping ErrorCallback, onContentLoaded: @escaping ContentCallback) 22 | } 23 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/UI/BaseViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseViewController.swift 3 | // JSON Feed Reader 4 | // 5 | // Created by Rajanikant Deshmukh on 03/04/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class BaseViewController: UIViewController { 13 | 14 | var dataRespository: DataRepository! 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | // Innitialize data repository, to be accessed by other UIViewControllers 20 | let appDelegate = UIApplication.shared.delegate as! AppDelegate 21 | dataRespository = appDelegate.dataRepository 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/UI/HomeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeViewController.swift 3 | // JSON Feed Reader 4 | // 5 | // Created by Rajanikant Deshmukh on 02/04/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class HomeViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | } 16 | 17 | @IBAction func openWebsite(_ sender: UIButton) { 18 | openUrl(url: JsonFeedOrg.WEBSITE) 19 | } 20 | 21 | @IBAction func openSpecs(_ sender: UIButton) { 22 | openUrl(url: JsonFeedOrg.SPECIFICATIONS) 23 | } 24 | 25 | func openUrl(url: String?) { 26 | if let url = URL(string: url!) { 27 | let app = UIApplication.shared 28 | app.open(url, options: [:], completionHandler: nil) 29 | } else { 30 | showAlertDialog(title: "Ooops!", message: "The URL is not a valid URL", dismissHandler: nil) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/UI/PostCell/PostViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PostViewCell.swift 3 | // JSON Feed Reader 4 | // 5 | // Created by Rajanikant Deshmukh on 03/04/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class PostViewCell: UITableViewCell { 12 | 13 | static let POST_VIEW_CELL = "PostViewCell" 14 | 15 | @IBOutlet weak var titleLabel: UILabel! 16 | @IBOutlet weak var dateLabel: UILabel! 17 | @IBOutlet weak var previewLabel: UILabel! 18 | 19 | override func awakeFromNib() { 20 | super.awakeFromNib() 21 | // Initialization code 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/UI/PostContentViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PostContentViewController.swift 3 | // JSON Feed Reader 4 | // 5 | // Created by Rajanikant Deshmukh on 02/04/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class PostContentViewController: BaseViewController { 12 | 13 | static let STORYBOARD_ID = "PostContentViewController" 14 | 15 | var post: Post! 16 | 17 | @IBOutlet weak var doneButton: UIBarButtonItem! 18 | @IBOutlet weak var webView: UIWebView! 19 | @IBOutlet weak var navItem: UINavigationItem! 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | navItem.title = post.title 24 | webView.scrollView.bounces = false 25 | 26 | // Check if THML contents are available 27 | let htmlContent = post.content_html 28 | if htmlContent == nil || (htmlContent?.isEmpty)! { 29 | 30 | // Switch to text 31 | let textContent = post.content_text 32 | if textContent == nil || (textContent?.isEmpty)! { 33 | // Show Error 34 | } else { 35 | let contents = "\(textContent!)" 36 | webView.loadHTMLString(contents, baseURL: URL(string: post.url!)) 37 | } 38 | 39 | 40 | } else { 41 | webView.loadHTMLString(htmlContent!, baseURL: URL(string: post.url!)) 42 | } 43 | } 44 | 45 | @IBAction func onDoneClick(_ sender: Any) { 46 | dismiss(animated: true, completion: nil) 47 | } 48 | 49 | 50 | class func instantiate(caller: UIViewController, post: Post) -> PostContentViewController { 51 | // Instantiate VC from storyboard 52 | let contentViewController: PostContentViewController = caller.storyboard?.instantiateViewController(withIdentifier: STORYBOARD_ID) as! PostContentViewController 53 | // Set required parameters and return 54 | contentViewController.post = post 55 | return contentViewController 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Utils/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // JSON Feed Reader 4 | // 5 | // Created by Rajanikant Deshmukh on 02/04/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class JsonFeedOrg { 12 | static let WEBSITE = "https://jsonfeed.org/" 13 | static let SPECIFICATIONS = "https://jsonfeed.org/version/1" 14 | static let FEED = "https://jsonfeed.org/feed.json" 15 | } 16 | 17 | enum Errors: Error { 18 | case NetworkError 19 | case ServerError 20 | case LocalDatabaseError 21 | case EmptyDatabaseError 22 | case UnknownError 23 | } 24 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Utils/Extensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Extensions.swift 3 | // JSON Feed Reader 4 | // 5 | // Created by Rajanikant Deshmukh on 02/04/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | extension UIViewController { 13 | 14 | func showAlertDialog(title: String, message: String, dismissHandler: ((UIAlertAction) -> Void)?) { 15 | let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) 16 | alert.addAction(UIAlertAction(title: "OK", style: .default, handler: dismissHandler)) 17 | self.present(alert, animated: true) 18 | } 19 | 20 | func showError(_ error: Errors) { 21 | showAlertDialog(title: "Error", message: error.localizedDescription, dismissHandler: nil) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/JSON Feed Reader/Utils/Formatter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Formatter.swift 3 | // JSON Feed Reader 4 | // 5 | // Created by Rajanikant Deshmukh on 03/04/18. 6 | // Copyright © 2018 Rajanikant Deshmukh. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class UserFriendlyDateFormatter { 12 | 13 | private init(){} 14 | 15 | class func format(dateToFormat: Date) -> String { 16 | let dateFormatter = DateFormatter() 17 | dateFormatter.dateFormat = "dd MMM, yyyy" 18 | dateFormatter.timeZone = TimeZone.current 19 | return dateFormatter.string(from: dateToFormat) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'JSON Feed Reader' do 5 | use_frameworks! 6 | 7 | # Pods for JSON Feed Reader 8 | pod 'JSONFeed' 9 | end 10 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - JSONFeed (0.8.4) 3 | 4 | DEPENDENCIES: 5 | - JSONFeed 6 | 7 | SPEC CHECKSUMS: 8 | JSONFeed: cf6dbae6ceb7691eaf2bd17453bc533b709e2c53 9 | 10 | PODFILE CHECKSUM: 93f4ab2899f83207100ba4e2eb42e3507475f404 11 | 12 | COCOAPODS: 1.4.0 13 | -------------------------------------------------------------------------------- /P5 JSON-Feed-Reader/README.md: -------------------------------------------------------------------------------- 1 | # JSON feed reader 2 | 3 | 4 | ## Origin 5 | This app is my Capstone project for the iOS Developer Nanodegree offered by Udacity. This Nanodegree course focuses on iOS app development using Swift, including UIKit Fundamentals, Networking, and Persistence with Code Data. 6 | The app uses JSON Feeds to get contents. I read about JSON feeds in May 2017 and instantly I fall in love with it. I decided to write a dedicated app for it and finally I made it here. 7 | 8 | ## About the App 9 | JSON fed reader in current context was s simple app that subscribed to blog feed from jsonfeed.org and let users read through it. 10 | Next releases will have subscription to multiple feeds. 11 | 12 | ## Libraries and Frameworks Used 13 | **iOS Frameworks**: 14 | 1. [Foundation](https://developer.apple.com/documentation/foundation) 15 | 2. [UIKit](https://developer.apple.com/documentation/uikit) 16 | 3. [Core Data](https://developer.apple.com/documentation/coredata) to store images. 17 | 18 | **External Library** 19 | 6. [JSONFeed](https://github.com/totocaster/JSONFeed) to parse feed data. 20 | 21 | ## How to Build 22 | The build system uses [CocoaPods](https://cocoapods.org) to integrate dependencies. You should be familiar with CocoaPods and API key and secret from Flickr account. 23 | 1. Download zip or fork & clone project on your desktop. 24 | 2. Open Terminal and `cd` into project folder. Most of the times the path will be `~/Downloads/JSON-Feed-Reader`. 25 | 3. Run `pods install` to install dependencies. 26 | 4. Open `JSON Feed Reader.xcworkspace` with Xcode. 27 | 5. Now you can build and run the app. 28 | 29 | ## Licence 30 | 31 | ``` 32 | This project was submitted by Rajanikant Deshmukh as part of the iOS Developer Nanodegree At Udacity. 33 | As part of Udacity Honor code, your submissions must be your work, hence submitting this project 34 | as yours will cause you to break the Udacity Honor Code moreover, the suspension of your account. 35 | Me, the author of the project, allows you to check the code as a reference, but if you submit it, 36 | it is your responsibility if you get expelled. 37 | Besides the above notice, the following license applies, and this license notice must be included 38 | in all works derived from this project. 39 | 40 | MIT License 41 | 42 | Copyright (c) 2018 Rajanikant Deshmukh 43 | 44 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software 45 | and associated documentation files (the "Software"), to deal in the Software without restriction, 46 | including without limitation the rights to use, copy, modify, merge, publish, distribute, 47 | sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 48 | furnished to do so, subject to the following conditions: 49 | 50 | The above copyright notice and this permission notice shall be included in all copies or 51 | substantial portions of the Software. 52 | 53 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 54 | NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 55 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 56 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 57 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 58 | ``` 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iOS Nanodegree Projects 2 | This is master repository for my course of iOS Nanodegree with Udacity. Each project covers important aspect of iOS app development and is hosted as subdirectory. For more details on each project, check the project's directory. 3 | 4 | | # | Project | Aspect Covered | Preview | Description | 5 | | - | --------------- | -------------- | ------- | --------------- | 6 | | 1 | Pitch Perfect | Intro to iOS App Development with Swift | ![Pitch Perfect Preview](https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/master/P1%20PitchPerfect/Screenshots/PlaySoundScreen.png) | iOS app for recording voice and playing it through different voices. | 7 | | 2 | MemeMe | UIKit Fundamentals | ![MemeMe Preview](https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/master/P2%20MemeMe/Screenshots/MemeEditorFilled.png) | Meme creator app on iOS platform. | 8 | | 3 | On the Map | iOS Networking with Swift | ![On the Map Preview](https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/master/P3%20OnTheMap/Screenshots/Map-Pin.png) | An iOS application to find Udacity Students studying across the world. | 9 | | 4 | Virtual Tourist | iOS Persistence and Core Data | ![Virtual Tourist Preview](https://raw.githubusercontent.com/aruke/iOS-Nanodegree-Projects/master/P4%20VirtualTourist/Screenshots/Map-ViewPin.png) | An iOS application to download and view photos specific to selected locations. | 10 | 11 | ## Certificate 12 | You can check my Nanodegree Certificate [here](https://confirm.udacity.com/35KLMLRH). 13 | 14 | ## Licence 15 | The following notice is applicable to each project mentioned above. 16 | ``` 17 | This project was submitted by Rajanikant Deshmukh as part of the iOS Developer Nanodegree At Udacity. 18 | As part of Udacity Honor code, your submissions must be your work, hence submitting this project 19 | as yours will cause you to break the Udacity Honor Code moreover, the suspension of your account. 20 | Me, the author of the project, allows you to check the code as a reference, but if you submit it, 21 | it is your responsibility if you get expelled. 22 | ``` 23 | 24 | Besides the above notice, the MIT License must be included 25 | in all works derived from these projects. See [LICENCE](LICENCE) file for details. 26 | --------------------------------------------------------------------------------