// // PersistentStorage.swift // WOKA // // Created by MacBook Pro on 05/08/24. // import Foundation import CoreData enum PersistentStorageEnum : String{ case UserClicks case click_counts case category_id case post_id case post_type } enum PostType: Int { case series = 1 case season = 2 case episode = 3 case video = 4 // case paint = 5 case game = 6 case audio = 7 case karaokeVideo = 8 case shopProduct = 9 // case parentalVideo = 10 // case article = 11 case liveTV = 12 case FM = 13 case teaser = 14 case others = 15 case home = 16 } struct UserClickData { let clickCounts: Int let categoryId: Int let postId: Int let postType: Int } // MARK: - Clicks struct ClicksAnalytics : Codable { let postID, postType, numberOfClicks, deviceType: Int? let categoryID: Int? enum CodingKeys: String, CodingKey { case postID = "post_id" case postType = "post_type" case numberOfClicks = "number_of_clicks" case deviceType = "device_type" case categoryID = "category_id" } } // MARK: - ADs Impressions & Clicks struct AdsClickImpressionsData : Codable { let adID, noOfClick, noOfOmpression: Int enum CodingKeys: String, CodingKey { case adID = "ad_id" case noOfClick = "no_of_click" case noOfOmpression = "no_of_impression" } } final class PersistentStorage { private init(){} static let shared = PersistentStorage() // MARK: - Core Data stack lazy var persistentContainer: NSPersistentContainer = { let container = NSPersistentContainer(name: "WOKA") container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { fatalError("Unresolved errorsss \(error), \(error.userInfo)") } }) return container }() lazy var context = persistentContainer.viewContext // MARK: - Core Data Saving support func saveContext() { if context.hasChanges { do { try context.save() } catch { let nserror = error as NSError fatalError("Unresolved errorsss \(nserror), \(nserror.userInfo)") } } } func createData(data : UserClickData){ //We need to create a context from this container let managedContext = PersistentStorage.shared.context let share = UserClicks(context: managedContext) share.click_counts = Int64(data.clickCounts) share.category_id = Int64(data.categoryId) share.post_id = Int64(data.postId) share.post_type = Int64(data.postType) do { try managedContext.save() retrieveData(postID: data.postId, catID: data.categoryId, postType: data.postType) } catch let error as NSError { print("Could not save. \(error), \(error.userInfo)") } } func checkIfExist( key : PersistentStorageEnum , clicksData : UserClickData) { let managedContext = PersistentStorage.shared.context let fetchRequest = NSFetchRequest(entityName: PersistentStorageEnum.UserClicks.rawValue) // fetchRequest.fetchLimit = 1 // fetchRequest.predicate = NSPredicate(format: "id == %d" ,id) fetchRequest.predicate = NSPredicate(format: "\(key.rawValue) == %@ AND post_id == %@" ,clicksData.postType.toString(),clicksData.postId.toString()) do { guard let result = try managedContext.fetch(fetchRequest) as? [UserClicks] else {return} if result.isEmpty{ //create PersistentStorage.shared.createData(data: clicksData) print("create, In Exist") }else{ //update let objectUpdate = result[0] as NSManagedObject print("Update, In Exist") objectUpdate.setValue(result.first!.click_counts + Int64(clicksData.clickCounts), forKey: "click_counts") do{ try managedContext.save() retrieveData(postID: clicksData.postId, catID: clicksData.categoryId, postType: clicksData.postType) } catch { print(error) } } // result.forEach { clicks in // print("Counts" , clicks.click_counts) // } }catch let error as NSError { print("Could not fetch. \(error), \(error.userInfo)") } } func checkWebSeries(clicksData : UserClickData) { let managedContext = PersistentStorage.shared.context let fetchRequest = NSFetchRequest(entityName: PersistentStorageEnum.UserClicks.rawValue) fetchRequest.fetchLimit = 1 fetchRequest.predicate = NSPredicate(format: "post_id == %@ AND category_id == %@" ,clicksData.postId.toString(), clicksData.categoryId.toString()) do { guard let result = try managedContext.fetch(fetchRequest) as? [UserClicks] else {return} if result.isEmpty{ //create PersistentStorage.shared.createData(data: clicksData) print("create Main") }else{ //update let objectUpdate = result[0] as NSManagedObject print("Update Main") objectUpdate.setValue(result.first!.click_counts + Int64(clicksData.clickCounts), forKey: "click_counts") do{ try managedContext.save() retrieveData(postID: clicksData.postId, catID: clicksData.categoryId, postType: clicksData.postType) } catch { print(error) } } }catch let error as NSError { print("Could not fetch. \(error), \(error.userInfo)") } } func retrieveData(postID : Int?, catID : Int?, postType : Int) { //We need to create a context from this container let managedContext = PersistentStorage.shared.context let path = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask) // debugPrint(path[0]) let fetchRequest:NSFetchRequest = NSFetchRequest.init(entityName: "UserClicks") fetchRequest.predicate = NSPredicate(format: "post_id == %@ AND category_id == %@ AND post_type == %@" ,postID?.toString() ?? "0", catID?.toString() ?? "0" ,postType.toString()) // fetchRequests.fetchLimit = 1 // fetchRequests.sortDescriptors = [NSSortDescriptor.init(key: "uuid", ascending: false)] do { guard let result = try managedContext.fetch(fetchRequest) as? [UserClicks] else {return} result.forEach { clicks in print("ID:-" , PostType(rawValue: Int(clicks.post_type))!, "CatID:- ", clicks.category_id, "PostID:- ", clicks.post_id , "Count:-", clicks.click_counts) } } catch let error { debugPrint(error) } } //Sending Clicks data to our server func sendDataToServer(isLogout : Bool = false) { //We need to create a context from this container let managedContext = PersistentStorage.shared.context let fetchRequest:NSFetchRequest = NSFetchRequest.init(entityName: "UserClicks") fetchRequest.fetchLimit = 15 do { guard let result = try managedContext.fetch(fetchRequest) as? [UserClicks] else {return} var userClicks = [ClicksAnalytics]() print("UserClicks Count from SendData to server :- ", result.count) //if data is less , dont keep sending the data to server if isLogout == false,result.count < 3 { print("Not Enough Clicks Data") return } result.forEach { clicks in // device type 1- android , 2 - iOS userClicks.append(ClicksAnalytics(postID: Int(clicks.post_id), postType: Int(clicks.post_type), numberOfClicks: Int(clicks.click_counts), deviceType: 2, categoryID: Int(clicks.category_id))) print("ID:-" , PostType(rawValue: Int(clicks.post_type))!, "CatID:- ", clicks.category_id, "PostID:- ", clicks.post_id , "Count:-", clicks.click_counts) } // send data to server NetworkManager.shareInstance.nwCallRawJSON(clicksData: userClicks) { isDone in if isDone{ // if data is send to server and we get success callback then delete that data from coredata entity. self.deleteData(result,isLogout: isLogout) } } } catch let error { debugPrint(error) } } //Delete data from CoreData func deleteData(_ data: [UserClicks],isLogout : Bool = false) { let managedContext = PersistentStorage.shared.context data.forEach { clicks in managedContext.delete(clicks) } do { try managedContext.save() //after deleting check if more data exist. getAllData(isLogout: isLogout) print("Deleted data") } catch let error { debugPrint("Failed to delete data:", error) } } //Get all data from DB func getAllData(isLogout : Bool = false) { //We need to create a context from this container let managedContext = PersistentStorage.shared.context let fetchRequest:NSFetchRequest = NSFetchRequest.init(entityName: "UserClicks") fetchRequest.fetchLimit = 15 do { guard let result = try managedContext.fetch(fetchRequest) as? [UserClicks] else {return} print("UserClicks Count from getAllData :- ", result.count) //if data is less , dont keep sending the data to server if isLogout == false,result.count < 5 { print("Not Enough Data") return } else{ // send data again to server sendDataToServer() print("Data sent Again.") } result.forEach { clicks in // device type 1- android , 2 - iOS print("ID:-" , PostType(rawValue: Int(clicks.post_type))!, "CatID:- ", clicks.category_id, "PostID:- ", clicks.post_id , "Count:-", clicks.click_counts) } } catch let error { debugPrint(error) } } // MARK: - Handle Clicks For UserClicks func addOthersCount(){ let userClicks = UserClickData(clickCounts: 1, categoryId: 0, postId: 0, postType: PostType.others.rawValue) PersistentStorage.shared.checkIfExist( key: .post_type,clicksData: userClicks) } func addKaraokeCount(postID : Int){ let userClicks = UserClickData(clickCounts: 1, categoryId: 0, postId: postID, postType: PostType.karaokeVideo.rawValue) PersistentStorage.shared.checkIfExist( key: .post_type,clicksData: userClicks) } func addAudioCount(postID : Int){ let userClicks = UserClickData(clickCounts: 1, categoryId: 0, postId: postID, postType: PostType.audio.rawValue) PersistentStorage.shared.checkIfExist( key: .post_type,clicksData: userClicks) } func addGamesCount(postID : Int, count : Int = 1){ let userClicks = UserClickData(clickCounts: count, categoryId: 0, postId: postID, postType: PostType.game.rawValue) PersistentStorage.shared.checkIfExist( key: .post_type,clicksData: userClicks) } func addShopCount(postID : Int){ let userClicks = UserClickData(clickCounts: 1, categoryId: 0, postId: postID, postType: PostType.shopProduct.rawValue) PersistentStorage.shared.checkIfExist( key: .post_type,clicksData: userClicks) } func addRadioCount(){ guard let postID = AuthFunc.shareInstance.staticURLs?.liveFmData?.id else{return} let userClicks = UserClickData(clickCounts: 1, categoryId: 0, postId: postID, postType: PostType.FM.rawValue) PersistentStorage.shared.checkIfExist( key: .post_type,clicksData: userClicks) } func addLiveTVCount(){ guard let postID = AuthFunc.shareInstance.staticURLs?.liveData?.first?.id else{return} let userClicks = UserClickData(clickCounts: 1, categoryId: 0, postId: postID, postType: PostType.liveTV.rawValue) PersistentStorage.shared.checkIfExist( key: .post_type,clicksData: userClicks) } func addTrailerCount(){ let userClicks = UserClickData(clickCounts: 1, categoryId: 0, postId: 0, postType: PostType.teaser.rawValue) PersistentStorage.shared.checkIfExist( key: .post_type,clicksData: userClicks) } func addWebSeries(catID : Int, postID : Int, postType : PostType){ let userClicks = UserClickData(clickCounts: 1, categoryId: catID, postId: postID, postType: postType.rawValue) PersistentStorage.shared.checkWebSeries(clicksData: userClicks) } } // MARK: - Handling ADS extension PersistentStorage{ func sendAdsData(isLogout : Bool = false) { //create a context from this container let managedContext = PersistentStorage.shared.context let fetchRequest:NSFetchRequest = NSFetchRequest.init(entityName: "AdClicksImpressions") fetchRequest.fetchLimit = 15 do { guard let result = try managedContext.fetch(fetchRequest) as? [AdClicksImpressions] else {return} print("userImpressions Count from SendAdsData to server :- ", result.count) //if data is less , dont keep sending the data to server if isLogout == false,result.count < 2 { print("Not Enough Ads Data") return } var userImpressions = [AdsClickImpressionsData]() //map the impressions result.forEach { ads in // device type 1- android , 2 - iOS userImpressions.append(AdsClickImpressionsData(adID: Int(ads.ad_id), noOfClick: Int(ads.no_of_click), noOfOmpression: Int(ads.no_of_impression))) print("ADs Data :- ","ID:-" , ads.ad_id, "No Of Clicks :- ", ads.no_of_click, "Impressions :- ", ads.no_of_impression) } //Send it to server and delete from our DB NetworkManager.shareInstance.nwCallRawJSONAds(adsData: userImpressions) { isDone in if isDone{ self.deleteAdsData(result) } } } catch let error { debugPrint(error) } } func deleteAdsData(_ data: [AdClicksImpressions],isLogout : Bool = false) { let managedContext = PersistentStorage.shared.context data.forEach { clicks in managedContext.delete(clicks) } do { try managedContext.save() getAllAdsData(isLogout: isLogout) print("Deleted data") } catch let error { debugPrint("Failed to delete data:", error) } } func getAllAdsData(isLogout : Bool = false) { //We need to create a context from this container let managedContext = PersistentStorage.shared.context let fetchRequest:NSFetchRequest = NSFetchRequest.init(entityName: "AdClicksImpressions") do { guard let result = try managedContext.fetch(fetchRequest) as? [AdClicksImpressions] else {return} print("UserClicks Count from getAllData :- ", result.count) //if data is less , dont keep sending the data to server if isLogout == false,result.count < 2 { print("Not Enough Ads Data") return }else{ // send ads data again to server sendAdsData() print("Ads Data sent Again.") } // result.forEach { clicks in // // device type 1- android , 2 - iOS // print("ID:-" , PostType(rawValue: Int(clicks.post_type))!, "CatID:- ", clicks.category_id, "PostID:- ", clicks.post_id , "Count:-", clicks.click_counts) // } } catch let error{ debugPrint(error) } } func checkIfAdExist(adsData : AdsClickImpressionsData) { let managedContext = PersistentStorage.shared.context let fetchRequest = NSFetchRequest(entityName: "AdClicksImpressions") fetchRequest.predicate = NSPredicate(format: "ad_id == %@" ,adsData.adID.toString()) do { guard let result = try managedContext.fetch(fetchRequest) as? [AdClicksImpressions] else {return} if result.isEmpty{ //create PersistentStorage.shared.createAdsData(data: adsData) print("create, In Exist") }else{ //update let objectUpdate = result[0] as NSManagedObject print("Update, In Exist") objectUpdate.setValue(result.first!.no_of_click + Int64(adsData.noOfClick), forKey: "no_of_click") objectUpdate.setValue(result.first!.no_of_impression + Int64(adsData.noOfOmpression), forKey: "no_of_impression") do{ try managedContext.save() retrieveAdsData(adID: adsData.adID) } catch { print(error) } } }catch let error as NSError { print("Could not fetch. \(error), \(error.userInfo)") } } // MARK: - Add Ads to CoreData func createAdsData(data : AdsClickImpressionsData){ //We need to create a context from this container let managedContext = PersistentStorage.shared.context let share = AdClicksImpressions(context: managedContext) share.ad_id = Int64(data.adID) share.no_of_click = Int64(data.noOfClick) share.no_of_impression = Int64(data.noOfOmpression) do { try managedContext.save() //show the ad saved in cored data retrieveAdsData(adID: data.adID) } catch let error as NSError { print("Could not save. \(error), \(error.userInfo)") } } func retrieveAdsData(adID : Int?) { let managedContext = PersistentStorage.shared.context let fetchRequest:NSFetchRequest = NSFetchRequest.init(entityName: "AdClicksImpressions") fetchRequest.predicate = NSPredicate(format: "ad_id == %@",adID?.toString() ?? 0) do { guard let result = try managedContext.fetch(fetchRequest) as? [AdClicksImpressions] else {return} result.forEach { ads in print("ID:-" , ads.ad_id, "No Of Clicks :- ", ads.no_of_click, "Impressions :- ", ads.no_of_impression) } } catch let error { debugPrint(error) } } func addAdsCount(adID : Int, impressions : Int = 0, clicks : Int = 0){ let adsData = AdsClickImpressionsData(adID: adID, noOfClick: clicks, noOfOmpression: impressions) PersistentStorage.shared.checkIfAdExist( adsData: adsData) } } // MARK: - Sync CoreData to server extension PersistentStorage{ func getEntityDataCount() -> (Int,Int){ let managedContext = PersistentStorage.shared.context let adsFetchReq:NSFetchRequest = NSFetchRequest.init(entityName: "AdClicksImpressions") let clicksFetchReq:NSFetchRequest = NSFetchRequest.init(entityName: "UserClicks") do { // Perform the count request let adsCount = try managedContext.count(for: adsFetchReq) let clicksCount = try managedContext.count(for: clicksFetchReq) return (adsCount,clicksCount) } catch let error as NSError { print("Error fetching count: \(error), \(error.userInfo)") return (0,0) } } func checkLastSync(){ /* check ad timestamp from gvar show ads if the saved timestamp time and current time has diffrenece of 30 minutes */ if let timeStamp = K.GVar.lastDataSync{ /* check if timestamp difference is equal and greater than 60 minutes i.e 3600 seconds */ let duration = DateFormatterLib.dateDifferenceINT(date1: timeStamp, date2: Date()) print("Last Sync Duration :- ", duration, " Seconds.") if duration <= 3600{ //if last data sync has not // dont sync data. print("Last sync has not passed 1 hour") }else{ //Sync data to server if the last sync time has been changed. syncData() } }else{ /* Assign the date and sync the data to server */ syncData() } } func syncData(){ K.GVar.lastDataSync = Date() self.sendAdsData() self.sendDataToServer() print("Data Synced to Server.") } }