├── YouTubeHomeFeed
├── Assets.xcassets
│ ├── Contents.json
│ ├── home.imageset
│ │ ├── home.png
│ │ └── Contents.json
│ ├── menu.imageset
│ │ ├── menu.png
│ │ └── Contents.json
│ ├── dhoni.imageset
│ │ ├── dhoni.jpg
│ │ └── Contents.json
│ ├── account.imageset
│ │ ├── account.png
│ │ └── Contents.json
│ ├── trending.imageset
│ │ ├── trending.png
│ │ └── Contents.json
│ ├── dhoni_profile.imageset
│ │ ├── dhoni_profile.jpg
│ │ └── Contents.json
│ ├── subscriptions.imageset
│ │ ├── subscriptions.png
│ │ └── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Video.swift
├── Utility.swift
├── Info.plist
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── Extensions.swift
├── AppDelegate.swift
├── MenuBar.swift
├── HomeController.swift
└── VideoCell.swift
├── YouTubeHomeFeed.xcodeproj
├── xcuserdata
│ └── VamshiKrishna.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ ├── xcschememanagement.plist
│ │ └── YouTubeHomeFeed.xcscheme
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcuserdata
│ │ └── VamshiKrishna.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── project.pbxproj
└── README.md
/YouTubeHomeFeed/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/YouTubeHomeFeed/Assets.xcassets/home.imageset/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VamshiIITBHU14/YouTubeClone/HEAD/YouTubeHomeFeed/Assets.xcassets/home.imageset/home.png
--------------------------------------------------------------------------------
/YouTubeHomeFeed/Assets.xcassets/menu.imageset/menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VamshiIITBHU14/YouTubeClone/HEAD/YouTubeHomeFeed/Assets.xcassets/menu.imageset/menu.png
--------------------------------------------------------------------------------
/YouTubeHomeFeed/Assets.xcassets/dhoni.imageset/dhoni.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VamshiIITBHU14/YouTubeClone/HEAD/YouTubeHomeFeed/Assets.xcassets/dhoni.imageset/dhoni.jpg
--------------------------------------------------------------------------------
/YouTubeHomeFeed/Assets.xcassets/account.imageset/account.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VamshiIITBHU14/YouTubeClone/HEAD/YouTubeHomeFeed/Assets.xcassets/account.imageset/account.png
--------------------------------------------------------------------------------
/YouTubeHomeFeed/Assets.xcassets/trending.imageset/trending.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VamshiIITBHU14/YouTubeClone/HEAD/YouTubeHomeFeed/Assets.xcassets/trending.imageset/trending.png
--------------------------------------------------------------------------------
/YouTubeHomeFeed/Assets.xcassets/dhoni_profile.imageset/dhoni_profile.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VamshiIITBHU14/YouTubeClone/HEAD/YouTubeHomeFeed/Assets.xcassets/dhoni_profile.imageset/dhoni_profile.jpg
--------------------------------------------------------------------------------
/YouTubeHomeFeed/Assets.xcassets/subscriptions.imageset/subscriptions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VamshiIITBHU14/YouTubeClone/HEAD/YouTubeHomeFeed/Assets.xcassets/subscriptions.imageset/subscriptions.png
--------------------------------------------------------------------------------
/YouTubeHomeFeed.xcodeproj/xcuserdata/VamshiKrishna.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/YouTubeHomeFeed.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/YouTubeHomeFeed.xcodeproj/project.xcworkspace/xcuserdata/VamshiKrishna.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/VamshiIITBHU14/YouTubeClone/HEAD/YouTubeHomeFeed.xcodeproj/project.xcworkspace/xcuserdata/VamshiKrishna.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/YouTubeHomeFeed/Assets.xcassets/dhoni.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "dhoni.jpg",
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 | }
--------------------------------------------------------------------------------
/YouTubeHomeFeed/Assets.xcassets/home.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "home.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/YouTubeHomeFeed/Assets.xcassets/menu.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "menu.png",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/YouTubeHomeFeed/Assets.xcassets/account.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "account.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/YouTubeHomeFeed/Assets.xcassets/trending.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "trending.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/YouTubeHomeFeed/Assets.xcassets/dhoni_profile.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "dhoni_profile.jpg",
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 | }
--------------------------------------------------------------------------------
/YouTubeHomeFeed/Assets.xcassets/subscriptions.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "subscriptions.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/YouTubeHomeFeed/Video.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Video.swift
3 | // YouTubeHomeFeed
4 | //
5 | // Created by Vamshi Krishna on 07/05/17.
6 | // Copyright © 2017 VamshiKrishna. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class Video: NSObject {
12 | var thumbNailImage:String?
13 | var title:String?
14 | var channel:Channel?
15 | var numberOfViews:NSNumber?
16 | var uploadDate:NSDate?
17 | }
18 |
19 | class Channel:NSObject{
20 | var name:String?
21 | var profileImage:String?
22 | }
23 |
--------------------------------------------------------------------------------
/YouTubeHomeFeed.xcodeproj/xcuserdata/VamshiKrishna.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | YouTubeHomeFeed.xcscheme
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 96E770EC1EBE24FF00E3E1EE
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/YouTubeHomeFeed/Utility.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Utility.swift
3 | // YouTubeHomeFeed
4 | //
5 | // Created by Vamshi Krishna on 07/05/17.
6 | // Copyright © 2017 VamshiKrishna. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | final class Utility: NSObject {
12 | private override init() { }
13 | static let shared = Utility()
14 |
15 | func CGRectMake(_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) -> CGRect {
16 | return CGRect(x: x, y: y, width: width, height: height)
17 | }
18 |
19 | func CGSizeMake( _ width:CGFloat, _ height:CGFloat) -> CGSize{
20 | return CGSize(width: width, height: height)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # YouTubeClone
2 | This project is a clone of YouTube. But the main intention is to show how to write clean code, using proper MVC patterns and re-usable coding methodologies.
3 | It includes features like:
4 | 1) Building UICollectionView all from code without using Storyboards
5 | 2) Customisable navigation bar to match that of YouTube
6 | 3) Creating a custom Menu bar using UICollectionView
7 | 4) NSURLSession task to complete a fetch request & iterate through all the objects in this JSON dictionary & construct all video objects
8 | 5) Load images Asynchronously
9 | 6) Creating a Slide-In Menu
10 | 7) Lazy var instantiation explained
11 | 8) JSON parsing in most optimised way
12 | 9) Play a remote video using AVPlayer inside the app
13 | 10) Video player controls like Pause, Play, Progress indicator etc.
14 |
15 |
--------------------------------------------------------------------------------
/YouTubeHomeFeed/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 | "info" : {
45 | "version" : 1,
46 | "author" : "xcode"
47 | }
48 | }
--------------------------------------------------------------------------------
/YouTubeHomeFeed/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | UIViewControllerBasedStatusBarAppearance
8 |
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 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/YouTubeHomeFeed/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 |
--------------------------------------------------------------------------------
/YouTubeHomeFeed/Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Extensions.swift
3 | // YouTubeHomeFeed
4 | //
5 | // Created by Vamshi Krishna on 07/05/17.
6 | // Copyright © 2017 VamshiKrishna. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIColor{
12 | static func returnRGBColor(r:CGFloat, g:CGFloat, b:CGFloat, alpha:CGFloat) -> UIColor{
13 | return UIColor(red: r/255, green: g/255, blue: b/255, alpha: alpha)
14 | }
15 | }
16 |
17 | extension UIView{
18 | func addConstraintsWithFormat(format:String, views: UIView...){
19 |
20 | var viewsDictionary = [String:UIView]()
21 | for (index, view) in views.enumerated(){
22 | let key = "v\(index)"
23 | view.translatesAutoresizingMaskIntoConstraints = false
24 | viewsDictionary[key] = view
25 |
26 | }
27 | addConstraints(NSLayoutConstraint.constraints(withVisualFormat: format, options: NSLayoutFormatOptions(), metrics: nil, views: viewsDictionary))
28 | }
29 | }
30 |
31 | let imageCache = NSCache()
32 |
33 | class CustomImageView : UIImageView{
34 |
35 | var imageURLString:String?
36 | func loadImageFromURLString(urlString:String){
37 | imageURLString = urlString
38 | let url = URL(string: urlString)
39 | image = nil
40 | if let imageFromCache = imageCache.object(forKey: urlString as AnyObject) as? UIImage{
41 | self.image = imageFromCache
42 | return
43 | }
44 | let request = URLRequest(url: url!)
45 | let session = URLSession(configuration: URLSessionConfiguration.default)
46 |
47 | let task = session.dataTask(with: request, completionHandler: {(data, response, error) -> Void in
48 | if error != nil{
49 | print(error!)
50 | return
51 | }
52 | DispatchQueue.main.async(execute: {
53 | let imageToCache = UIImage(data:data!)
54 | if(self.imageURLString == urlString){
55 | self.image = imageToCache
56 | }
57 | imageCache.setObject(imageToCache!, forKey: urlString as AnyObject)
58 |
59 | })
60 | })
61 | task.resume()
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/YouTubeHomeFeed/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // YouTubeHomeFeed
4 | //
5 | // Created by Vamshi Krishna on 06/05/17.
6 | // Copyright © 2017 VamshiKrishna. All rights reserved.
7 | // Source: https://www.letsbuildthatapp.com
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 |
18 | window = UIWindow(frame:UIScreen.main.bounds)
19 | window?.makeKeyAndVisible()
20 | let layout = UICollectionViewFlowLayout()
21 | window?.rootViewController = UINavigationController(rootViewController:HomeController(collectionViewLayout:layout))
22 | application.statusBarStyle = .lightContent
23 |
24 | let statusBarBackgroundView = UIView()
25 | statusBarBackgroundView.backgroundColor = UIColor.returnRGBColor(r: 230, g: 131, b: 31, alpha: 1)
26 | window?.addSubview(statusBarBackgroundView)
27 | window?.addConstraintsWithFormat(format: "H:|[v0]|", views: statusBarBackgroundView)
28 | window?.addConstraintsWithFormat(format: "V:|[v0(20)]|", views: statusBarBackgroundView)
29 |
30 | //get rid of black line under Navigation Bar
31 | UINavigationBar.appearance().shadowImage = UIImage()
32 | UINavigationBar.appearance().setBackgroundImage (UIImage(), for:.default)
33 | return true
34 | }
35 |
36 | func applicationWillResignActive(_ application: UIApplication) {
37 | // 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.
38 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
39 | }
40 |
41 | func applicationDidEnterBackground(_ application: UIApplication) {
42 | // 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.
43 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
44 | }
45 |
46 | func applicationWillEnterForeground(_ application: UIApplication) {
47 | // 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.
48 | }
49 |
50 | func applicationDidBecomeActive(_ application: UIApplication) {
51 | // 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.
52 | }
53 |
54 | func applicationWillTerminate(_ application: UIApplication) {
55 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
56 | }
57 |
58 |
59 | }
60 |
61 |
--------------------------------------------------------------------------------
/YouTubeHomeFeed/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/YouTubeHomeFeed/MenuBar.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MenuBar.swift
3 | // YouTubeHomeFeed
4 | //
5 | // Created by Vamshi Krishna on 07/05/17.
6 | // Copyright © 2017 VamshiKrishna. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class MenuBar:UIView, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout{
12 |
13 | lazy var collectionView:UICollectionView = {
14 | let layout = UICollectionViewFlowLayout()
15 | let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
16 | cv.backgroundColor = UIColor.returnRGBColor(r: 230, g: 32, b: 32, alpha: 1)
17 | cv.dataSource = self
18 | cv.delegate = self
19 | return cv
20 | }()
21 |
22 | let reuseIdentifier = "Cell"
23 | let imageNames = ["home", "trending", "subscriptions", "account"]
24 | override init(frame: CGRect) {
25 | super.init(frame: frame)
26 | collectionView.register(MenuCell.self, forCellWithReuseIdentifier: reuseIdentifier)
27 | addSubview(collectionView)
28 | addConstraintsWithFormat(format: "H:|[v0]|", views: collectionView)
29 | addConstraintsWithFormat(format: "V:|[v0]|", views: collectionView)
30 |
31 |
32 | }
33 |
34 | func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
35 | return 4
36 | }
37 |
38 | func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
39 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! MenuCell
40 | cell.imageView.image = UIImage(named: imageNames[indexPath.item])?.withRenderingMode(.alwaysTemplate)
41 | cell.imageView.tintColor = UIColor.returnRGBColor(r: 91, g: 14, b: 13, alpha: 1)
42 | collectionView.selectItem(at: IndexPath(item:0, section:0), animated: false, scrollPosition:[])
43 | return cell
44 | }
45 |
46 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
47 | return Utility.shared.CGSizeMake(frame.width/4, frame.height)
48 | }
49 |
50 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat {
51 | return 0
52 | }
53 | required init?(coder aDecoder: NSCoder) {
54 | fatalError("init(coder:) has not been implemented")
55 | }
56 | }
57 |
58 | class MenuCell:BaseCell{
59 |
60 | var imageView:UIImageView = {
61 | let iv = UIImageView()
62 | return iv
63 | }()
64 |
65 | override var isHighlighted: Bool{
66 | didSet{
67 | imageView.tintColor = isHighlighted ? UIColor.white : UIColor.returnRGBColor(r: 91, g: 14, b: 13, alpha: 1)
68 | }
69 | }
70 |
71 | override var isSelected: Bool{
72 | didSet{
73 | imageView.tintColor = isSelected ? UIColor.white : UIColor.returnRGBColor(r: 91, g: 14, b: 13, alpha: 1)
74 | }
75 | }
76 |
77 | override func setUpViews() {
78 | super.setUpViews()
79 | addSubview(imageView)
80 | addConstraintsWithFormat(format: "H:[v0(28)]", views: imageView)
81 | addConstraintsWithFormat(format: "V:[v0(28)]", views: imageView)
82 | addConstraint(NSLayoutConstraint(item: imageView, attribute: .centerX, relatedBy: .equal, toItem: self, attribute: .centerX, multiplier: 1, constant: 0))
83 | addConstraint(NSLayoutConstraint(item: imageView, attribute: .centerY, relatedBy: .equal, toItem: self, attribute: .centerY, multiplier: 1, constant: 0))
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/YouTubeHomeFeed.xcodeproj/xcuserdata/VamshiKrishna.xcuserdatad/xcschemes/YouTubeHomeFeed.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/YouTubeHomeFeed/HomeController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HomeController.swift
3 | // YouTubeHomeFeed
4 | //
5 | // Created by Vamshi Krishna on 06/05/17.
6 | // Copyright © 2017 VamshiKrishna. All rights reserved.
7 | // Source: https://www.letsbuildthatapp.com
8 |
9 | import UIKit
10 |
11 | private let reuseIdentifier = "Cell"
12 |
13 | class HomeController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
14 |
15 | var videos:[Video]?
16 |
17 | func fetchVideos(){
18 | let url = URL(string: "https://s3-us-west-2.amazonaws.com/youtubeassets/home.json")
19 | let request = URLRequest(url: url!)
20 | let session = URLSession(configuration: URLSessionConfiguration.default)
21 |
22 | let task = session.dataTask(with: request, completionHandler: {(data, response, error) -> Void in
23 | if error != nil{
24 | print(error!)
25 | return
26 | }
27 | do{
28 | let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
29 | self.videos = [Video]()
30 | for dictionary in json as! [[String:AnyObject]]{
31 | let video = Video()
32 | video.title = dictionary["title"] as? String
33 | video.thumbNailImage = dictionary["thumbnail_image_name"] as? String
34 | video.numberOfViews = dictionary["number_of_views"] as? NSNumber
35 | let channelDictionary = dictionary["channel"] as! [String:AnyObject]
36 | let channel = Channel()
37 | channel.name = channelDictionary["name"] as? String
38 | channel.profileImage = channelDictionary["profile_image_name"] as? String
39 | video.channel = channel
40 | self.videos?.append(video)
41 | }
42 | DispatchQueue.main.async(execute: {
43 | self.collectionView?.reloadData()
44 | })
45 |
46 | } catch let jsonError{
47 | print(jsonError)
48 | }
49 | })
50 | task.resume()
51 | }
52 | override func viewDidLoad() {
53 | super.viewDidLoad()
54 | fetchVideos()
55 | navigationController?.navigationBar.isTranslucent = false
56 | navigationController?.navigationBar.barTintColor = UIColor(red: 230/255, green: 32/255, blue: 32/255, alpha: 1)
57 |
58 | let titleLabel = UILabel(frame:Utility.shared.CGRectMake(0, 0, view.frame.width-32, view.frame.height))
59 | titleLabel.textColor = UIColor.white
60 | titleLabel.text = "Home"
61 | titleLabel.font = UIFont.systemFont(ofSize: 22)
62 | navigationItem.titleView = titleLabel
63 |
64 | self.collectionView?.backgroundColor = UIColor.white
65 | self.collectionView!.register(VideoCell.self, forCellWithReuseIdentifier: reuseIdentifier)
66 | self.collectionView?.contentInset = UIEdgeInsetsMake(50, 0, 0, 0)
67 | self.collectionView?.scrollIndicatorInsets = UIEdgeInsetsMake(50, 0, 0, 0)
68 |
69 | setupNavBarButtons()
70 | setupMenuBar()
71 | }
72 |
73 | func setupNavBarButtons(){
74 | let searchImage = UIImage(named: "subscriptions")?.withRenderingMode(.alwaysOriginal)
75 | let menuImage = UIImage(named: "menu")?.withRenderingMode(.alwaysOriginal)
76 | let searchBarButton = UIBarButtonItem(image: searchImage, style: .plain, target: self, action: #selector(handleSearch))
77 | let menuBarButton = UIBarButtonItem(image: menuImage, style: .plain, target: self, action: #selector(handleMore))
78 | navigationItem.rightBarButtonItems = [searchBarButton, menuBarButton]
79 | }
80 |
81 | func handleMore(){
82 |
83 | }
84 | func handleSearch(){
85 |
86 | }
87 |
88 | let menuBar:MenuBar = {
89 | let mb = MenuBar()
90 | return mb
91 | }()
92 |
93 | private func setupMenuBar(){
94 | view.addSubview(menuBar)
95 | view.addConstraintsWithFormat(format: "H:|[v0]|", views: menuBar)
96 | view.addConstraintsWithFormat(format: "V:|[v0(50)]|", views: menuBar)
97 |
98 | }
99 |
100 | override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
101 | return videos?.count ?? 0
102 | }
103 |
104 | override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
105 | let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! VideoCell
106 | cell.video = videos?[indexPath.item]
107 |
108 |
109 | return cell
110 | }
111 |
112 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
113 | return 0
114 | }
115 |
116 | func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
117 | let height = (view.frame.width - 16 - 16) * 9/16
118 | return Utility.shared.CGSizeMake(view.frame.width, height+88)
119 | }
120 |
121 |
122 | }
123 |
124 |
125 |
--------------------------------------------------------------------------------
/YouTubeHomeFeed/VideoCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VideoCell.swift
3 | // YouTubeHomeFeed
4 | //
5 | // Created by Vamshi Krishna on 07/05/17.
6 | // Copyright © 2017 VamshiKrishna. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class BaseCell:UICollectionViewCell{
12 | override init(frame: CGRect) {
13 | super.init(frame: frame)
14 | setUpViews()
15 | }
16 |
17 | func setUpViews(){
18 |
19 | }
20 | required init?(coder aDecoder: NSCoder) {
21 | fatalError("init(coder:) has not been implemented")
22 | }
23 | }
24 |
25 | class VideoCell:BaseCell{
26 |
27 | var video :Video?{
28 | didSet{
29 | titleLabel.text = video?.title
30 | setupThumbNailImage()
31 | setupProfileImage()
32 | let numberFormatter = NumberFormatter()
33 | numberFormatter.numberStyle = .decimal
34 |
35 | if let channelName = video?.channel?.name, let numberOfViewsAfterFormatting = numberFormatter.string(from: (video?.numberOfViews)!){
36 | subTitleView.text = "\(channelName) : \(numberOfViewsAfterFormatting)" + "\n" + "2 Years ago"
37 | }
38 |
39 | //measuringTitleLabelTextHeight
40 | if let title = video?.title{
41 | let size = Utility.shared.CGSizeMake(frame.width - 16 - 8 - 44 - 16 , 1000)
42 | let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
43 | let estimatedRect = NSString(string: title).boundingRect(with: size, options: options, attributes: [NSFontAttributeName:UIFont.systemFont(ofSize: 13)], context: nil)
44 |
45 | if estimatedRect.size.height > 20{
46 | titleLabelHeightConstraint?.constant = 44
47 | }
48 | else{
49 | titleLabelHeightConstraint?.constant = 20
50 | }
51 | }
52 | }
53 | }
54 |
55 | func setupProfileImage(){
56 | if let profileImageURL = video?.channel?.profileImage{
57 | userProfileImageView.loadImageFromURLString(urlString: profileImageURL)
58 | }
59 | }
60 |
61 | func setupThumbNailImage(){
62 | if let thumbNailImageURL = video?.thumbNailImage{
63 | self.thumbnailImageView.loadImageFromURLString(urlString: thumbNailImageURL)
64 | }
65 | }
66 | let thumbnailImageView:CustomImageView = {
67 | let imageView = CustomImageView()
68 | imageView.contentMode = .scaleAspectFill
69 | imageView.clipsToBounds = true
70 | return imageView
71 | }()
72 |
73 | let titleLabel:UILabel = {
74 | let label = UILabel()
75 | label.translatesAutoresizingMaskIntoConstraints = false
76 | label.numberOfLines = 2
77 | return label
78 | }()
79 |
80 | let subTitleView: UITextView = {
81 | let textView = UITextView()
82 | textView.textContainerInset = UIEdgeInsetsMake(0, -4, 0, 0)
83 | textView.textColor = UIColor.lightGray
84 | textView.translatesAutoresizingMaskIntoConstraints = false
85 | textView.isEditable = false
86 | textView.isSelectable = false
87 | return textView
88 | }()
89 |
90 | let userProfileImageView: CustomImageView = {
91 | let imageView = CustomImageView()
92 | imageView.layer.cornerRadius = 22
93 | imageView.layer.masksToBounds = true
94 | imageView.contentMode = .scaleAspectFill
95 | return imageView
96 | }()
97 |
98 | let separatorView:UIView = {
99 | let view = UIView()
100 | view.backgroundColor = UIColor(red: 230/255, green: 230/255, blue: 230/255, alpha: 1)
101 | return view
102 | }()
103 |
104 | var titleLabelHeightConstraint:NSLayoutConstraint?
105 |
106 | override func setUpViews(){
107 | addSubview(thumbnailImageView)
108 | addSubview(separatorView)
109 | addSubview(userProfileImageView)
110 | addSubview(titleLabel)
111 | addSubview(subTitleView)
112 |
113 | addConstraintsWithFormat(format: "H:|-16-[v0]-16-|", views: thumbnailImageView)
114 | addConstraintsWithFormat(format: "H:|-16-[v0(44)]|", views: userProfileImageView)
115 | //vertical constraints
116 | addConstraintsWithFormat(format: "V:|-16-[v0]-8-[v1(44)]-36-[v2(1)]|", views: thumbnailImageView,userProfileImageView, separatorView)
117 | addConstraintsWithFormat(format: "H:|[v0]|", views: separatorView)
118 |
119 | //top constraint
120 | addConstraint(NSLayoutConstraint(item: titleLabel, attribute: .top, relatedBy: .equal, toItem: thumbnailImageView, attribute: .bottom, multiplier: 1, constant: 8))
121 | //left constraint
122 | addConstraint(NSLayoutConstraint(item: titleLabel, attribute: .left, relatedBy: .equal, toItem: userProfileImageView, attribute: .right, multiplier: 1, constant: 8))
123 | //right constraint
124 | addConstraint(NSLayoutConstraint(item: titleLabel, attribute: .right, relatedBy: .equal, toItem: thumbnailImageView, attribute: .right, multiplier: 1, constant: 0))
125 | //height constraint
126 | titleLabelHeightConstraint = NSLayoutConstraint(item: titleLabel, attribute: .height, relatedBy: .equal, toItem: self, attribute: .height, multiplier: 0, constant: 20)
127 | addConstraint(titleLabelHeightConstraint!)
128 |
129 | //left constraint
130 | addConstraint(NSLayoutConstraint(item: subTitleView, attribute: .left, relatedBy: .equal, toItem: titleLabel, attribute: .left, multiplier: 1, constant: 0))
131 | //right constraint
132 | addConstraint(NSLayoutConstraint(item: subTitleView, attribute: .right, relatedBy: .equal, toItem: titleLabel, attribute: .right, multiplier: 1, constant: 0))
133 | //top constraint
134 | addConstraint(NSLayoutConstraint(item: subTitleView, attribute: .top, relatedBy: .equal, toItem: titleLabel, attribute: .bottom, multiplier: 1, constant: 4))
135 | //height constraint
136 | addConstraint(NSLayoutConstraint(item: subTitleView, attribute: .height, relatedBy: .equal, toItem: self, attribute: .height, multiplier: 0, constant: 30))
137 | }
138 |
139 | func CGRectMake(_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) -> CGRect {
140 | return CGRect(x: x, y: y, width: width, height: height)
141 | }
142 |
143 | }
144 |
--------------------------------------------------------------------------------
/YouTubeHomeFeed.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 9614B6441EBED6FB008CFA46 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9614B6431EBED6FB008CFA46 /* Extensions.swift */; };
11 | 9614B6471EBED78C008CFA46 /* VideoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9614B6461EBED78C008CFA46 /* VideoCell.swift */; };
12 | 9614B64A1EBEDE72008CFA46 /* MenuBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9614B6491EBEDE72008CFA46 /* MenuBar.swift */; };
13 | 9614B64E1EBEED41008CFA46 /* Utility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9614B64D1EBEED41008CFA46 /* Utility.swift */; };
14 | 9614B6501EBF5B0B008CFA46 /* Video.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9614B64F1EBF5B0B008CFA46 /* Video.swift */; };
15 | 96E770F11EBE24FF00E3E1EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E770F01EBE24FF00E3E1EE /* AppDelegate.swift */; };
16 | 96E770F61EBE24FF00E3E1EE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 96E770F41EBE24FF00E3E1EE /* Main.storyboard */; };
17 | 96E770F81EBE24FF00E3E1EE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 96E770F71EBE24FF00E3E1EE /* Assets.xcassets */; };
18 | 96E770FB1EBE24FF00E3E1EE /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 96E770F91EBE24FF00E3E1EE /* LaunchScreen.storyboard */; };
19 | 96E771051EBE267800E3E1EE /* HomeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96E771041EBE267800E3E1EE /* HomeController.swift */; };
20 | /* End PBXBuildFile section */
21 |
22 | /* Begin PBXFileReference section */
23 | 9614B6431EBED6FB008CFA46 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; };
24 | 9614B6461EBED78C008CFA46 /* VideoCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoCell.swift; sourceTree = ""; };
25 | 9614B6491EBEDE72008CFA46 /* MenuBar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuBar.swift; sourceTree = ""; };
26 | 9614B64D1EBEED41008CFA46 /* Utility.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utility.swift; sourceTree = ""; };
27 | 9614B64F1EBF5B0B008CFA46 /* Video.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Video.swift; sourceTree = ""; };
28 | 96E770ED1EBE24FF00E3E1EE /* YouTubeHomeFeed.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = YouTubeHomeFeed.app; sourceTree = BUILT_PRODUCTS_DIR; };
29 | 96E770F01EBE24FF00E3E1EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
30 | 96E770F51EBE24FF00E3E1EE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
31 | 96E770F71EBE24FF00E3E1EE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
32 | 96E770FA1EBE24FF00E3E1EE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
33 | 96E770FC1EBE24FF00E3E1EE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
34 | 96E771041EBE267800E3E1EE /* HomeController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeController.swift; sourceTree = ""; };
35 | /* End PBXFileReference section */
36 |
37 | /* Begin PBXFrameworksBuildPhase section */
38 | 96E770EA1EBE24FF00E3E1EE /* Frameworks */ = {
39 | isa = PBXFrameworksBuildPhase;
40 | buildActionMask = 2147483647;
41 | files = (
42 | );
43 | runOnlyForDeploymentPostprocessing = 0;
44 | };
45 | /* End PBXFrameworksBuildPhase section */
46 |
47 | /* Begin PBXGroup section */
48 | 9614B6421EBED6ED008CFA46 /* Helpers */ = {
49 | isa = PBXGroup;
50 | children = (
51 | 9614B6431EBED6FB008CFA46 /* Extensions.swift */,
52 | 9614B64D1EBEED41008CFA46 /* Utility.swift */,
53 | );
54 | name = Helpers;
55 | sourceTree = "";
56 | };
57 | 9614B6451EBED777008CFA46 /* View */ = {
58 | isa = PBXGroup;
59 | children = (
60 | 9614B6461EBED78C008CFA46 /* VideoCell.swift */,
61 | );
62 | name = View;
63 | sourceTree = "";
64 | };
65 | 9614B6481EBED7D7008CFA46 /* Controller */ = {
66 | isa = PBXGroup;
67 | children = (
68 | 96E771041EBE267800E3E1EE /* HomeController.swift */,
69 | );
70 | name = Controller;
71 | sourceTree = "";
72 | };
73 | 96E770E41EBE24FF00E3E1EE = {
74 | isa = PBXGroup;
75 | children = (
76 | 96E770EF1EBE24FF00E3E1EE /* YouTubeHomeFeed */,
77 | 96E770EE1EBE24FF00E3E1EE /* Products */,
78 | );
79 | sourceTree = "";
80 | };
81 | 96E770EE1EBE24FF00E3E1EE /* Products */ = {
82 | isa = PBXGroup;
83 | children = (
84 | 96E770ED1EBE24FF00E3E1EE /* YouTubeHomeFeed.app */,
85 | );
86 | name = Products;
87 | sourceTree = "";
88 | };
89 | 96E770EF1EBE24FF00E3E1EE /* YouTubeHomeFeed */ = {
90 | isa = PBXGroup;
91 | children = (
92 | 96E770F01EBE24FF00E3E1EE /* AppDelegate.swift */,
93 | 9614B6491EBEDE72008CFA46 /* MenuBar.swift */,
94 | 9614B64F1EBF5B0B008CFA46 /* Video.swift */,
95 | 9614B6451EBED777008CFA46 /* View */,
96 | 9614B6481EBED7D7008CFA46 /* Controller */,
97 | 9614B6421EBED6ED008CFA46 /* Helpers */,
98 | 96E770F41EBE24FF00E3E1EE /* Main.storyboard */,
99 | 96E770F71EBE24FF00E3E1EE /* Assets.xcassets */,
100 | 96E770F91EBE24FF00E3E1EE /* LaunchScreen.storyboard */,
101 | 96E770FC1EBE24FF00E3E1EE /* Info.plist */,
102 | );
103 | path = YouTubeHomeFeed;
104 | sourceTree = "";
105 | };
106 | /* End PBXGroup section */
107 |
108 | /* Begin PBXNativeTarget section */
109 | 96E770EC1EBE24FF00E3E1EE /* YouTubeHomeFeed */ = {
110 | isa = PBXNativeTarget;
111 | buildConfigurationList = 96E770FF1EBE24FF00E3E1EE /* Build configuration list for PBXNativeTarget "YouTubeHomeFeed" */;
112 | buildPhases = (
113 | 96E770E91EBE24FF00E3E1EE /* Sources */,
114 | 96E770EA1EBE24FF00E3E1EE /* Frameworks */,
115 | 96E770EB1EBE24FF00E3E1EE /* Resources */,
116 | );
117 | buildRules = (
118 | );
119 | dependencies = (
120 | );
121 | name = YouTubeHomeFeed;
122 | productName = YouTubeHomeFeed;
123 | productReference = 96E770ED1EBE24FF00E3E1EE /* YouTubeHomeFeed.app */;
124 | productType = "com.apple.product-type.application";
125 | };
126 | /* End PBXNativeTarget section */
127 |
128 | /* Begin PBXProject section */
129 | 96E770E51EBE24FF00E3E1EE /* Project object */ = {
130 | isa = PBXProject;
131 | attributes = {
132 | LastSwiftUpdateCheck = 0830;
133 | LastUpgradeCheck = 0830;
134 | ORGANIZATIONNAME = VamshiKrishna;
135 | TargetAttributes = {
136 | 96E770EC1EBE24FF00E3E1EE = {
137 | CreatedOnToolsVersion = 8.3.1;
138 | DevelopmentTeam = BJBC582Q56;
139 | ProvisioningStyle = Automatic;
140 | };
141 | };
142 | };
143 | buildConfigurationList = 96E770E81EBE24FF00E3E1EE /* Build configuration list for PBXProject "YouTubeHomeFeed" */;
144 | compatibilityVersion = "Xcode 3.2";
145 | developmentRegion = English;
146 | hasScannedForEncodings = 0;
147 | knownRegions = (
148 | en,
149 | Base,
150 | );
151 | mainGroup = 96E770E41EBE24FF00E3E1EE;
152 | productRefGroup = 96E770EE1EBE24FF00E3E1EE /* Products */;
153 | projectDirPath = "";
154 | projectRoot = "";
155 | targets = (
156 | 96E770EC1EBE24FF00E3E1EE /* YouTubeHomeFeed */,
157 | );
158 | };
159 | /* End PBXProject section */
160 |
161 | /* Begin PBXResourcesBuildPhase section */
162 | 96E770EB1EBE24FF00E3E1EE /* Resources */ = {
163 | isa = PBXResourcesBuildPhase;
164 | buildActionMask = 2147483647;
165 | files = (
166 | 96E770FB1EBE24FF00E3E1EE /* LaunchScreen.storyboard in Resources */,
167 | 96E770F81EBE24FF00E3E1EE /* Assets.xcassets in Resources */,
168 | 96E770F61EBE24FF00E3E1EE /* Main.storyboard in Resources */,
169 | );
170 | runOnlyForDeploymentPostprocessing = 0;
171 | };
172 | /* End PBXResourcesBuildPhase section */
173 |
174 | /* Begin PBXSourcesBuildPhase section */
175 | 96E770E91EBE24FF00E3E1EE /* Sources */ = {
176 | isa = PBXSourcesBuildPhase;
177 | buildActionMask = 2147483647;
178 | files = (
179 | 96E771051EBE267800E3E1EE /* HomeController.swift in Sources */,
180 | 9614B6471EBED78C008CFA46 /* VideoCell.swift in Sources */,
181 | 9614B64E1EBEED41008CFA46 /* Utility.swift in Sources */,
182 | 9614B6441EBED6FB008CFA46 /* Extensions.swift in Sources */,
183 | 96E770F11EBE24FF00E3E1EE /* AppDelegate.swift in Sources */,
184 | 9614B6501EBF5B0B008CFA46 /* Video.swift in Sources */,
185 | 9614B64A1EBEDE72008CFA46 /* MenuBar.swift in Sources */,
186 | );
187 | runOnlyForDeploymentPostprocessing = 0;
188 | };
189 | /* End PBXSourcesBuildPhase section */
190 |
191 | /* Begin PBXVariantGroup section */
192 | 96E770F41EBE24FF00E3E1EE /* Main.storyboard */ = {
193 | isa = PBXVariantGroup;
194 | children = (
195 | 96E770F51EBE24FF00E3E1EE /* Base */,
196 | );
197 | name = Main.storyboard;
198 | sourceTree = "";
199 | };
200 | 96E770F91EBE24FF00E3E1EE /* LaunchScreen.storyboard */ = {
201 | isa = PBXVariantGroup;
202 | children = (
203 | 96E770FA1EBE24FF00E3E1EE /* Base */,
204 | );
205 | name = LaunchScreen.storyboard;
206 | sourceTree = "";
207 | };
208 | /* End PBXVariantGroup section */
209 |
210 | /* Begin XCBuildConfiguration section */
211 | 96E770FD1EBE24FF00E3E1EE /* Debug */ = {
212 | isa = XCBuildConfiguration;
213 | buildSettings = {
214 | ALWAYS_SEARCH_USER_PATHS = NO;
215 | CLANG_ANALYZER_NONNULL = YES;
216 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
217 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
218 | CLANG_CXX_LIBRARY = "libc++";
219 | CLANG_ENABLE_MODULES = YES;
220 | CLANG_ENABLE_OBJC_ARC = YES;
221 | CLANG_WARN_BOOL_CONVERSION = YES;
222 | CLANG_WARN_CONSTANT_CONVERSION = YES;
223 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
224 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
225 | CLANG_WARN_EMPTY_BODY = YES;
226 | CLANG_WARN_ENUM_CONVERSION = YES;
227 | CLANG_WARN_INFINITE_RECURSION = YES;
228 | CLANG_WARN_INT_CONVERSION = YES;
229 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
230 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
231 | CLANG_WARN_UNREACHABLE_CODE = YES;
232 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
233 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
234 | COPY_PHASE_STRIP = NO;
235 | DEBUG_INFORMATION_FORMAT = dwarf;
236 | ENABLE_STRICT_OBJC_MSGSEND = YES;
237 | ENABLE_TESTABILITY = YES;
238 | GCC_C_LANGUAGE_STANDARD = gnu99;
239 | GCC_DYNAMIC_NO_PIC = NO;
240 | GCC_NO_COMMON_BLOCKS = YES;
241 | GCC_OPTIMIZATION_LEVEL = 0;
242 | GCC_PREPROCESSOR_DEFINITIONS = (
243 | "DEBUG=1",
244 | "$(inherited)",
245 | );
246 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
247 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
248 | GCC_WARN_UNDECLARED_SELECTOR = YES;
249 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
250 | GCC_WARN_UNUSED_FUNCTION = YES;
251 | GCC_WARN_UNUSED_VARIABLE = YES;
252 | IPHONEOS_DEPLOYMENT_TARGET = 10.3;
253 | MTL_ENABLE_DEBUG_INFO = YES;
254 | ONLY_ACTIVE_ARCH = YES;
255 | SDKROOT = iphoneos;
256 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
257 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
258 | };
259 | name = Debug;
260 | };
261 | 96E770FE1EBE24FF00E3E1EE /* Release */ = {
262 | isa = XCBuildConfiguration;
263 | buildSettings = {
264 | ALWAYS_SEARCH_USER_PATHS = NO;
265 | CLANG_ANALYZER_NONNULL = YES;
266 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
267 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
268 | CLANG_CXX_LIBRARY = "libc++";
269 | CLANG_ENABLE_MODULES = YES;
270 | CLANG_ENABLE_OBJC_ARC = YES;
271 | CLANG_WARN_BOOL_CONVERSION = YES;
272 | CLANG_WARN_CONSTANT_CONVERSION = YES;
273 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
274 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
275 | CLANG_WARN_EMPTY_BODY = YES;
276 | CLANG_WARN_ENUM_CONVERSION = YES;
277 | CLANG_WARN_INFINITE_RECURSION = YES;
278 | CLANG_WARN_INT_CONVERSION = YES;
279 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
280 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
281 | CLANG_WARN_UNREACHABLE_CODE = YES;
282 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
283 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
284 | COPY_PHASE_STRIP = NO;
285 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
286 | ENABLE_NS_ASSERTIONS = NO;
287 | ENABLE_STRICT_OBJC_MSGSEND = YES;
288 | GCC_C_LANGUAGE_STANDARD = gnu99;
289 | GCC_NO_COMMON_BLOCKS = YES;
290 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
291 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
292 | GCC_WARN_UNDECLARED_SELECTOR = YES;
293 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
294 | GCC_WARN_UNUSED_FUNCTION = YES;
295 | GCC_WARN_UNUSED_VARIABLE = YES;
296 | IPHONEOS_DEPLOYMENT_TARGET = 10.3;
297 | MTL_ENABLE_DEBUG_INFO = NO;
298 | SDKROOT = iphoneos;
299 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
300 | VALIDATE_PRODUCT = YES;
301 | };
302 | name = Release;
303 | };
304 | 96E771001EBE24FF00E3E1EE /* Debug */ = {
305 | isa = XCBuildConfiguration;
306 | buildSettings = {
307 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
308 | DEVELOPMENT_TEAM = BJBC582Q56;
309 | INFOPLIST_FILE = YouTubeHomeFeed/Info.plist;
310 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
311 | PRODUCT_BUNDLE_IDENTIFIER = VamshiKrishna.YouTubeHomeFeed;
312 | PRODUCT_NAME = "$(TARGET_NAME)";
313 | SWIFT_VERSION = 3.0;
314 | };
315 | name = Debug;
316 | };
317 | 96E771011EBE24FF00E3E1EE /* Release */ = {
318 | isa = XCBuildConfiguration;
319 | buildSettings = {
320 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
321 | DEVELOPMENT_TEAM = BJBC582Q56;
322 | INFOPLIST_FILE = YouTubeHomeFeed/Info.plist;
323 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
324 | PRODUCT_BUNDLE_IDENTIFIER = VamshiKrishna.YouTubeHomeFeed;
325 | PRODUCT_NAME = "$(TARGET_NAME)";
326 | SWIFT_VERSION = 3.0;
327 | };
328 | name = Release;
329 | };
330 | /* End XCBuildConfiguration section */
331 |
332 | /* Begin XCConfigurationList section */
333 | 96E770E81EBE24FF00E3E1EE /* Build configuration list for PBXProject "YouTubeHomeFeed" */ = {
334 | isa = XCConfigurationList;
335 | buildConfigurations = (
336 | 96E770FD1EBE24FF00E3E1EE /* Debug */,
337 | 96E770FE1EBE24FF00E3E1EE /* Release */,
338 | );
339 | defaultConfigurationIsVisible = 0;
340 | defaultConfigurationName = Release;
341 | };
342 | 96E770FF1EBE24FF00E3E1EE /* Build configuration list for PBXNativeTarget "YouTubeHomeFeed" */ = {
343 | isa = XCConfigurationList;
344 | buildConfigurations = (
345 | 96E771001EBE24FF00E3E1EE /* Debug */,
346 | 96E771011EBE24FF00E3E1EE /* Release */,
347 | );
348 | defaultConfigurationIsVisible = 0;
349 | defaultConfigurationName = Release;
350 | };
351 | /* End XCConfigurationList section */
352 | };
353 | rootObject = 96E770E51EBE24FF00E3E1EE /* Project object */;
354 | }
355 |
--------------------------------------------------------------------------------