Search code examples
iosswiftcore-datapersistenceencapsulation

Encapsulated core data object


I have a json data save in core data and save it in an array, and I want to encapsulated the array I have save in core data. the problem is when I want to display it in app it only retrieve 1 data in array. which is suppose I have a 5. here I show my code

this is a my function fetch request my json data

APIServices.shared.fetchData(url: APIServices.youtubeBaseURL, params: params, of: Item.self) { (items) in
            let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
            privateContext.parent = CoreDataManager.shared.persistenceContainer.viewContext

            var newVideos = [Video]()
            items.forEach({ (item) in
                let video = Video(context: privateContext)
                video.title = item.snippet.title
                video.videoId = item.id.videoId

                newVideos.append(video)

                do {
                    try privateContext.save()
                    try privateContext.parent?.save()

                } catch let saveErr {
                    print("Failed to save json data:", saveErr)
                }
            })
            DispatchQueue.main.async {
                self.videos = newVideos
                self.collectionView.reloadData()
                self.showLoadingHUD()
            }
        }

this is a code in my encapsulated in my collectionViewCell

var video: Video? {
    didSet{
        let context = CoreDataManager.shared.persistenceContainer.viewContext
        let fetchRequest = NSFetchRequest<Video>(entityName: "Video")

        do {
           let videos = try context.fetch(fetchRequest)
            videos.forEach { (video) in
                titleLabel.text = video.title
                guard let videoId = video.videoId else { return }
                DispatchQueue.main.async {
                    let playerVars: [String : Any] = [
                        "playsinline" : 0,
                        "enablejsapi": 1,
                        "wmode": "transparent",
                        "controls": 1,
                        "showinfo": 0,
                        "rel": 0,
                        "modestbranding": 1,
                        "iv_load_policy": 3 //annotations
                    ]
                    self.player.load(withVideoId: videoId, playerVars: playerVars)
                }
            }
        } catch let fetchErr {
            print("Failed to fetch video:", fetchErr)
        }
    }
}

after I fetch the core data and try to print video inside videos.forEach the value is there not only one value. but when I try to load with youtube player, it only display one value.

only one object return


Solution

  • For your solution,

    Create a videoModel variable in your UICollectionViewCell class like, and remove fetchRequest call in UICollectionViewCell class which is causing the issue,

    var videoModel: VideoModel? {
            didSet {
                titleLabel.text = videoModel?.videoTitle
                let playerVars: [String : Any] = [
                    "playsinline" : 0,
                    "enablejsapi": 1,
                    "wmode": "transparent",
                    "controls": 1,
                    "showinfo": 0,
                    "rel": 0,
                    "modestbranding": 1,
                    "iv_load_policy": 3 //annotations
                ]
                self.player.load(withVideoId: videoModel?.videoId ?? "", playerVars: playerVars)
            }
        }
    

    ViewModel file should look like,

    import Foundation
    
    struct VideoModel {
        var videoTitle: String?
        var videoId: String?
    
        init(title: String, id: String) {
            videoTitle = title
            videoId = id
        }
    }
    

    Then in SearchViewController, create a viewModels array and assign the videoModel your your cell in cellForItemAt datasource method,

    var videoModels = [VideoModel]()
    
    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: videoId, for: indexPath) as! SearchVideoCell
    
            let video = videos[indexPath.row]
            cell.delegate = self
            cell.video = video
            cell.videoModel = videoModels[indexPath.row]
    
            return cell
        }
    

    Finally in fetchVideosWith append viewModel to your viewModels array,

    //MARK:- Fetch Data
        fileprivate func fetchVideosWith(title name: String) {
            let params = ["part": "snippet", "q": "tausiyah \(name)", "key": "AIzaSyC2mn0PTL8JmSWEthvksdJLvsnwo5Tu9BA"]
    
                APIServices.shared.fetchData(url: APIServices.youtubeBaseURL, params: params, of: Item.self) { (items) in
                    let privateContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
                    privateContext.parent = CoreDataManager.shared.persistenceContainer.viewContext
    
                    var newVideos = [Video]()
                    items.forEach({ (item) in
                        let video = Video(context: privateContext)
                        video.title = item.snippet.title
                        video.videoId = item.id.videoId
    
                        newVideos.append(video)
    
                        do {
                            try privateContext.save()
                            try privateContext.parent?.save()
                        } catch let saveErr {
                            print("Failed to save json data:", saveErr)
                        }
                        let videoModel = VideoModel(title: item.snippet.title, id: item.id.videoId)
                        self.videoModels.append(videoModel)
                    })
                    DispatchQueue.main.async {
                        self.videos = newVideos
                        self.collectionView.reloadData()
                        self.showLoadingHUD()
                    }
                }
        }
    

    Hope it helps.