How to fetch YouTube Videos using YouTube API in Swift (Part1)

How to fetch YouTube Videos using YouTube API in Swift (Part1)

Let's fetch Dragon Ball videos from YouTube!

·

4 min read

Almost a year ago, I made a fitness app that allows users to schedule their fitness activities. One of the features is that they can find fitness videos on Youtube and save the video for later use.

Today, I'll be sharing a way of fetching YouTube API with SwiftUI. In this article, you can learn the following:
・Fetching Youtube videos via Youtube API with search feature (Part1)
・Paginating (Part2)
・Play Video (Part3)

1 - Go to Google Cloud Platform, create a new project and enable YouTube API Data API v3

screenshot.10.08.png

You can name your project whatever you like. In this case, I name it "SampleProject" screenshot_10_25_2.png

Go to NavigationBar > APIs & Services > Library and choose YouTube API Data V3

screenshot_10_25_3.png

Click "Create Credential" & "API Key"

screenshot_10_25_4.png

screensho_10_25_5.png You will need the API key inside the Swift code.

2 - Create a model
The YouTube API returns the response with the structure below.

struct YoutubeSearchList: Codable {
    let kind: String
    let etag: String
    let nextPageToken: String
    let regionCode: String
    let items: [YouTubeSearchItem]
}

struct YouTubeSearchItem: Codable {
    let id: YouTubeId
    let snippet: Snippet
}

struct YouTubeId: Codable {
    let kind: String
    let videoId: String
}

struct Snippet: Codable {
    let title: String
    let description: String
    let thumbnails: ThumbnailInfo
}

struct ThumbnailInfo: Codable {
    let `default`: ThumbDefaultInfo?
    let high: ThumbHighInfo?
}

struct ThumbDefaultInfo: Codable {
    let url: String
    let width: Int
    let height: Int
}

struct ThumbHighInfo: Codable {
    let url: String
    let width: Int
    let height: Int
}

3 - Create a View Model for Youtube Video

class YoutubeVideoViewModel: ObservableObject {

    let youtubeApiService = YoutubeApiService()
    @Published var youtubeSearchList: YoutubeSearchList?
    @Published var youtubeSearchItems = [YouTubeSearchItem]()

    func fetchVideoWithSeach(searchText: String) {
        youtubeApiService.fetchYouTubeVideos(searchText: searchText) { [weak self] youtubeSearchList in
            self?.youtubeSearchItems = youtubeSearchList.items
        }
    }
}
struct YoutubeApiService {

    public func fetchYouTubeVideos(searchText: String, completion: @escaping (YoutubeSearchList) -> Void) {
        // As default, api returns only 5 results so I added a "maxResults" parameter to 15 to fetch more results
        let apiKey = "Your API Key" 
        let urlString = "https://www.googleapis.com/youtube/v3/search?part=snippet&q=\(searchText)&regionCode=US&maxResults=15&type=video&key=\(apiKey)"
        guard let url = URL(string: urlString) else {return}

        let task = URLSession.shared.dataTask(with: url) { jsonData, response, err in
            if let err = err {
                print("failed to get json data", err.localizedDescription)
                return
            }

            guard let jsonData = jsonData else {return}

            do {
                let youtubeSeachList = try JSONDecoder().decode(YoutubeSearchList.self, from: jsonData)
                DispatchQueue.main.async {
                    completion(youtubeSeachList)
                }
            } catch let jsonError {
                print("json serialization error", jsonError)
            }
        }
        task.resume()
    }
}

4 - Create a custom image for loading URL.
Since Youtube API returns the image URL, let's create an observable object that can be converted to an image object.

class ImageDownloader : ObservableObject {
    @Published var downloadData: Data? = nil

    func downloadImage(url: String) {

        guard let imageURL = URL(string: url) else { return }

        DispatchQueue.global().async {
            let data = try? Data(contentsOf: imageURL)
            DispatchQueue.main.async {
                self.downloadData = data
            }
        }
    }
}
struct URLImage: View {

    let url: String
    @ObservedObject private var imageDownloader = ImageDownloader()

    init(url: String) {
        self.url = url
        self.imageDownloader.downloadImage(url: self.url)
    }

    var body: some View {

        if let imageData = self.imageDownloader.downloadData {
            let img = UIImage(data: imageData)
            return VStack {
                Image(uiImage: img!).resizable()
            }
        } else {
            return VStack {
                let img = UIImage()
                Image(uiImage: img).resizable()
            }
        }
    }
}

5 - Create a View with Search text
After fetching Youtube API, I displayed the title, the description, and the image of the video.

struct ContentView: View {

    @ObservedObject var youtubeVideoViewModel = YoutubeVideoViewModel()
    @State private var searchText = ""

    var body: some View {
        NavigationView {
            VStack {
                TextField("Search", text: $searchText) { isEditing in
                    print("isEditing", isEditing)
                } onCommit: {
                    print("onCommit do something when hitting enter", searchText)
                    youtubeVideoViewModel.fetchVideoWithSeach(searchText: searchText)
                }
                .padding()
                .textFieldStyle(RoundedBorderTextFieldStyle())
                List {
                    ForEach(0..<youtubeVideoViewModel.youtubeSearchItems.count, id: \.self) { index in
                        HStack {
                            URLImage(url: youtubeVideoViewModel.youtubeSearchItems[index].snippet.thumbnails.high?.url ?? "")
                                .aspectRatio(contentMode: .fit).background(Color.blue)
                            VStack {
                                Text(youtubeVideoViewModel.youtubeSearchItems[index].snippet.title)
                                    .bold()
                                    .padding(EdgeInsets(top: 5, leading: 5, bottom: 0, trailing: 5))
                                Text(youtubeVideoViewModel.youtubeSearchItems[index].snippet.description)
                                    .padding(EdgeInsets(top: 5, leading: 5, bottom: 5, trailing: 5))
                            }
                        }.frame(width: .none, height: 150, alignment: .center).background(Color.green)
                    }
                }
            }
            .navigationTitle("Anime")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Let's see how it looks and works! ezgif.com-gif-maker (5).gif

In this example, I only showed 15 results from the API, meaning showing only 15 results even after scrolling till the end.
I will show you how to paginate more results in Part 2 of this series so stay tuned!

Reference:
Youtube Data API