swift element is empty - swift

I am trying to get the first or current exercise from my core data but swift keeps telling me that the element is empty. When i run the app and set the break points the debugger shows that the element is empty but no errors. here are the functions i am using to get the element data.
func currentWorkout() -> Workout? {
let client = currentClient()
return (appointment?.workouts as? Set<Workout>)?.first(where: { $0.client == client })
}
private func currentCard() -> Card? {
return currentWorkout()?.card
}
private func currentClientPlannedExercises() -> [ExerciseInfo] {
if let currentCard = currentCard(), let template = currentCard.template, let exerciseSets = template.exerciseSets?.array as? [ExerciseSet] {
let numCardsWithThis = (template.cardsWithThisTemplate as? Set<Card>)?.filter { $0.client != currentClient() }.count ?? 0
let exercsiseSetNumber = numCardsWithThis % exerciseSets.count
if let result = exerciseSets[exercsiseSetNumber].exercises?.array as? [ExerciseInfo] {
return result
}
}
return [ExerciseInfo]()
}
private func currentExercise() -> Exercise? {
// we can't have an exercise without a selection
guard let selectedExercise = currentExerciseInfo(), let currentCard = currentCard() else{
return nil
}
// get the first exercise on the current card that has the same exercise info as the one selected
if let exercises = currentWorkout()?.exercises as? Set<Exercise>{
return exercises.first(where: { $0.exerciseInfo == selectedExercise })
}
let exercise = Exercise(context: context)
exercise.workout = currentWorkout()
exercise.exerciseInfo = selectedExercise
//TODO: Set Seat
return exercise
}
private func currentExerciseInfo() -> ExerciseInfo? {
guard let selectedRow = exercisesTableView.indexPathForSelectedRow else {
return nil
}
return currentClientPlannedExercises()[selectedRow.row]
}

if the Issue is fetching then You can use this Code:
For Fetching the data from Core Data
var tasks: [Task] = [] //Your Entity Name in Bracket
func getData() {
do {
tasks = try context.fetch(Task.fetchRequest()) //Instead of Task your Entity Name
} catch {
print("Fetching Failed")
}
}
And use it like:
for data in tasks{
print(data.name) // All the Attributes name after data.attributename
print(data.address)
}
If it is in tableView:
let data = tasks[indexPath.row]
print(data.name)
You will get the data if it is there.
Edit to Check if Data entered or not
Print the Path like this:
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
print(paths[0])
Go to sqlite file and open and check if there is Data or not inside that.
Edit If you are facing the issue in Adding Data to Core Data
Simple code to add Data
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let task = Task(context: context) //Entity Name here instead of Task
task.name = taskTextField.text! // Attribute name here after task.attributename
// Save the data to coredata
(UIApplication.shared.delegate as! AppDelegate).saveContext()
Hope this help.

I found the issue was in the currentExercise function it wasn't calling the first exercise until the it had an exercise. I fixed by rewriting the function
private func currentExercise() -> Exercise? {
// we can't have an exercise without a selection
guard let selectedExercise = currentExerciseInfo() else{
return nil
}
// get the first exercise on the current card that has the same exercise info as the one selected
if let exercises = currentWorkout()?.exercises as? Set<Exercise>, let firstExercise = exercises.first(where: { $0.exerciseInfo == selectedExercise }) {
return firstExercise
}
let exercise = Exercise(context: context)
exercise.workout = currentWorkout()
exercise.exerciseInfo = selectedExercise
//TODO: Set Seat
return exercise
}

Related

save string over saved string in core data

In my swift code below the code saves an item in core data. The goal is to overwrite that item. I am getting a runtime error at
CoreDataHandler.changeName(user: fetchUser!\[indexNumber\], jessica: "jo")
Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value
I don't know how to wrap in the index number. The goal is it to print judo then jo
import UIKit;import CoreData
class ViewController: UIViewController {
var fetchUser: [UserInfo]? = nil
var indexNumber : Int = 0
override func viewDidLoad() {
super.viewDidLoad()
CoreDataHandler.saveObject2( name: "judo")
getText(textNo: indexNumber)
saveTheItem()
}
#objc func saveTheItem(){
CoreDataHandler.changeName(user: fetchUser![indexNumber], jessica: "jo")
}
func getText(textNo:Int) {
// first check the array bounds
let info = helpText.shareInstance.fetchText()
if info.count > textNo {
if let imageData = info[textNo].name
{
print(imageData)
} else {
// no data
print("data is empty Textss")
}
} else {
// image number is greater than array bounds
print("you are asking out of bounds")
}
}
}
class CoreDataHandler : NSManagedObject {
class func saveObject2( name: String) -> Bool {
let context = getContext()
let entity = NSEntityDescription.entity(forEntityName: "UserInfo", in: context)
let managedObject = NSManagedObject(entity: entity!, insertInto: context)
managedObject.setValue(name, forKey: "name")
do{
try context.save()
return true
}
catch {
return false
}
}
private class func getContext() -> NSManagedObjectContext{
let appD = UIApplication.shared.delegate as! AppDelegate
return appD.persistentContainer.viewContext
}
class func changeName(user: UserInfo,jessica : String) -> Bool
{
let context = getContext()
user.name = jessica
print(jessica)
do{
try context.save()
return true
}
catch{
return false
}
}
}
class helpText: UIViewController{
private class func getContext() -> NSManagedObjectContext {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
static let shareInstance = helpText()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
func saveName(data: String) {
let imageInstance = UserInfo(context: context)
imageInstance.name = data
do {
try context.save()
} catch {
print(error.localizedDescription)
}
}
func fetchText() -> [UserInfo] {
var fetchingImage = [UserInfo]()
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "UserInfo")
do {
fetchingImage = try context.fetch(fetchRequest) as! [UserInfo]
} catch {
print("Error while fetching the image")
}
return fetchingImage
}
}
No offense but your code is a mess.
And there is a big misunderstanding. Core Data records are unordered, there is no index. To update a record you have to fetch it by a known attribute, in your example by name, update it and save it back.
This is a simple method to do that. It searches for a record with the given name. If there is one, update the attribute with newName and save the record.
The code assumes that there is a NSManagedObject subclass UserInfo with implemented class method fetchRequest.
func changeName(_ name: String, to newName: String) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request : NSFetchRequest<UserInfo> = UserInfo.fetchRequest()
request.predicate = NSPredicate(format: "name == %#", name)
do {
let records = try context.fetch(request)
guard let foundRecord = records.first else { return }
foundRecord.name = newName
try context.save()
} catch {
print(error)
}
}
Regarding your confusing code:
Create CoreDataHandler as singleton (and it must not be a subclass of NSManagedObject). Move the Core Data related code from AppDelegate and the methods to read and write in this class.

Core data does not save the data properly

EDITED
I have a UITableView which displays multiple social media posts. I use the Prefetch Source Delegate for prefetching Posts and I use Core Data to be stored after being fetched from web server. Problem that I have is that I get no error, but the data does not stay saved between launches in CoreData.
Snip of code
func configureDataSourceTable(){
self.dataSource = UITableViewDiffableDataSource<String,String>(tableView: self.table_view){
(tableView, indexPath, ref) -> UITableViewCell? in
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! CheckInCell
if !self.postData.currentPostFetching(ref: ref){
self.postData.fetchPost(ref: ref) { post in
DispatchQueue.main.async {
self.setPostNeedsUpdate(ref: ref)//reconfigure items
}
return cell
}
func fetchPost(ref: String,completion : (PostStruct)->Void) {
if self.dataContainer.postIsPartOfCoreData(ref: ref){
var postStruct = self.dataContainer.getPostStructFromDB(ref: ref)
completion(postStruct)
}else{ //has to be downloaded from web server
Task {
let post = await getPostFromServer()//not relevant in understanding problem
var postStruct = self.convertDataToPost(post)
completion(postStruct)
self.dataContainer.savePostStruct(post: postStruct)
}
}
}
Class of the DataContainer subclass of NsPersistentStore
func savePostStruct(post_struct : PostStruct,image_data : String){
Task.detached {
var postObject = PostCore.init(context : self.viewContext)
postObject.name = "Test"
var image = ImageCore.init(context: self.viewContext)
imageObject.data = image_data
postObject.image = imageObject
do {
if Thread.isMainThread {
print("Running on the main thread parent")
}else {
print("Other thread")
}
try self.viewContext.save()
print("Reference \(post_struct.ref) child saved")
}catch {
print("Error that produced \(post.ref) catched while trying to savethe data on child: \(error.localizedDescription),number : \(error)");
}
if post_struct.ref == "AAAA"{
var post = PostCore.fetchRequest()
var predicate = NSPredicate.init(format: "ref like %#", post.ref!)
fetchCheckIn.predicate = predicate
fetchCheckIn.fetchLimit = 1
fetchCheckIn.includesPropertyValues = true
let result = try! self.viewContext.fetch(fetchCheckIn)
print(result)
//This line returns the Object
print(self. checkPostExistHasPictures(refPost: post.ref))
//This is the line where always come (false, false) meaning that the Post is not saved
}
}
func getPostStructFromDB(refPost : String)async->PostStruct {
return await self.viewContext.perform{
var fetchPost = PostCore.fetchRequest()
var predicate = NSPredicate.init(format: "ref like %#", refPost)
fetchPost.predicate = predicate
fetchPost.fetchLimit = 1
fetchPost.includesSubentities = true
fetchPost.returnsObjectsAsFaults = false
fetchPost.includesPropertyValues = true
let result = try? self.viewContext.fetch(fetchCheckIn)
var refPost = result.first.ref
return PostStruct(ref : ref, image: refPost.image.data)
}
}
}
func checkPostExistHasPictures(refPost : String)->(Bool,Bool){
var fetchCheckIn = CheckInCore.fetchRequest()
var predicate = NSPredicate.init(format: "ref like %#", refPost)
fetchCheckIn.predicate = predicate
fetchCheckIn.fetchLimit = 1
var exist = false
var hasPicture = false
self.viewContext.performAndWait {
do{
let result = try? self.viewContext.fetch(fetchCheckIn)
if result?.first == nil {
}else {
print("Exists with reference \(reference_checkin)")
if result!.first!.pic_ref == nil {
exist = true
hasPicture = false
}else if result!.first!.image!.count != 0 {
exist = true
hasPicture = true
}
}
}catch {
print("error catched")
}
}
return(exist, hasPicture)
}
}
Relation between PostCore and ImageCore is zero to many.
I don't get any error code or error message. I commented the line where I get the error. I have tried all possible ways using a backGroundContext each time a save is made to not block the main thread and still is the same problem.
Your code is extremely confusing.
Why have you got Task scattered everywhere, you are doing no async/await work?
Your savePostStruct method takes a parameter called post which contains your data, then you immediately replace it with a value of the same name of type PostCore, which is presumably a managed object, then you only set one value on it.
Then, when you come to fetch an item, there's nothing there, because you haven't written anything to the managed object besides the name "Test".
At the very least, you have to change this line:
var post = PostCore.init(context : self.viewContext)
To
let postObject = PostCore(context: self.viewContext)
Then you won't get confused between the managed object and the struct you're passing in.
You are also saving the context before you've written any of the values to it.

Child references in Firebase being changed for unknown reason

I am currently working on a project that involves uploading cards to a database. I am using Swift 4 and using google's firebase as the database. I have made the code function for placing cards from your hand onto the playing area, but when they are moved from one tile on the playing area to another tile it creates a duplicate of the card instead of moving the card that is already there. From closer inspection I have found that this is because the snapshot.ref in the retrieveUpdates method does not retrieve the correct reference value of the card that is already in the database. That causes it to call the code to create a new card.
My question is thus: why are the values of the snapshot.ref changing between when they passed by one used and received by another?
//Sends a card and location to the database when a card is moved
func updateDatabase(playingCard: PlayingCard) {
let cardUpdate = Database.database().reference().child("Updates")
//This takes the location that the sender dropped the card.
if let location = game.location(from: playingCard.card) {
if case .battlefield(let field, let stack) = location {
//If the card is already in the database it should keep its old ID but if it is new it should be assigned a new ID
let reference = playingCard.databaseRef ?? cardUpdate.childByAutoId()
let updateDictionary = ["Sender": String(playerNumber),"Card": playingCard.card.name, "Field": String(field), "Stack": String(stack)]
if playingCard.databaseRef == nil {
playingCard.databaseRef = reference
reference.setValue(updateDictionary)
}
else {
reference.updateChildValues(updateDictionary)
}
}
}
}
func retrieveUpdates() {
let cardUpdate = Database.database().reference().child("Updates")
cardUpdate.observe(.childAdded) { (snapshot) in
if snapshot.childrenCount != 0 {
self.processUpdate(snapshot: snapshot)
}
}
cardUpdate.observe(.childChanged, with: { (snapshot) in
self.processUpdate(snapshot: snapshot)
})
}
func processUpdate(snapshot: DataSnapshot) {
let snapshotValue = snapshot.value as! Dictionary<String,String>
let cardName = snapshotValue["Card"]!
let sender = Int(snapshotValue["Sender"]!)
let stack = Int(snapshotValue["Stack"]!)
let relativeField = snapshotValue["Field"]!
if sender != self.playerNumber {
let fieldNumber = (4 + sender! - self.playerNumber + Int(relativeField)!) % 4
var foundCard = false
for playingCard in self.gameGraphics.cards.reversed() {
if let databaseRef = playingCard.databaseRef, databaseRef == snapshot.ref {
foundCard = true
self.currentPlayingCard = CurrentPlayingCard(playingCard: playingCard, startPosition: playingCard.position, touchPoint: playingCard.position, location: Location.dataExtract())
}
}
if foundCard == false {
let card = self.gameGraphics.addFromDatabase(name: cardName, field: fieldNumber, stack: stack!, scene: self)
card.databaseRef = snapshot.ref
self.currentPlayingCard = CurrentPlayingCard(playingCard: card, startPosition: card.position, touchPoint: card.position, location: Location.dataExtract())
}
self.touchUp(atPoint: self.gameGraphics.allBattlefields[fieldNumber][stack!].position)
}
}
Any help would be greatly appreciated. This is one of my first programs written in swift so my coding practices are still not very good.
Here is the scene did load.
override func sceneDidLoad() {
super.sceneDidLoad()
anchorPoint = CGPoint(x: 0, y: 1)
FirebaseApp.configure()
let login = loginInfo()
Auth.auth().signIn(withEmail: login.username, password: login.password) { (user, error) in
if error != nil {
print(error!)
} else {
print("login Successful")
}
}
retrieveUpdates()
}

How to work with Firebase Asynchronously? Database reading giving odd results

I have written the following function to search through my Firebase database and I have also looked into using debug statements and tested with breakpoints to see this function is pulling the correct data and it is. But when I return the array at the end, the array is empty. As far as I understand this is due to the asynchronous nature of firebase. The function is getting to the end before the data is being added to the array. How do I fix this so it can work as intended, I want to return an array of items which I can then use for other functions.
static func SearchPostsByTags(tags: [String]) -> [Post]{
var result = [Post]()
let dbref = FIRDatabase.database().reference().child("posts")
dbref.observeSingleEvent(of: .value, with: { snap in
let comps = snap.value as! [String : AnyObject]
for(_, value) in comps {
let rawTags = value["tags"] as? NSArray
let compTags = rawTags as? [String]
if compTags != nil {
for cTag in compTags! {
for tag in tags {
if (tag == cTag) {
let foundPost = Post()
foundPost.postID = value["postID"] as! String
foundPost.title = value["title"] as! String
result.append(foundPost)
}
}
}
}
}
})
return result
}
}
You are returning your array before the async call ends. You should fill your array inside the async call and call then another method, which provides the results.
static func SearchPostsByTags(tags: [String]) {
let dbref = FIRDatabase.database().reference().child("posts")
dbref.observeSingleEvent(of: .value, with: { snap in
let comps = snap.value as! [String : AnyObject]
var result = [Post]()
for(_, value) in comps {
let rawTags = value["tags"] as? NSArray
let compTags = rawTags as? [String]
if compTags != nil {
for cTag in compTags! {
for tag in tags {
if (tag == cTag) {
let foundPost = Post()
foundPost.postID = value["postID"] as! String
foundPost.title = value["title"] as! String
result.append(foundPost)
}
}
}
}
}
// Call some func to deliver the finished result array
// You can also work with completion handlers - if you want to try have a look at callbacks / completion handler section of apples documentation
provideTheFinishedArr(result)
})
}

Cannot convert value of type 'String?!' to expected argument type 'Notifications'

I am trying to check the id of a record before I put it into the array, using xcode swift
here is the code. But, i get the following error
Notifications.swift:50:46: Cannot convert value of type 'String?!' to expected argument type 'Notifications'
on this line
*if (readRecordCoreData(result["MessageID"])==false)*
Please can some one help to explain this error
import CoreData
struct Notifications{
var NotifyID = [NSManagedObject]()
let MessageDesc: String
let Messageid: String
init(MessageDesc: String, Messageid:String) {
self.MessageDesc = MessageDesc
self.Messageid = Messageid
// self.MessageDate = MessageDate
}
static func MessagesWithJSON(results: NSArray) -> [Notifications] {
// Create an empty array of Albums to append to from this list
var Notification = [Notifications]()
// Store the results in our table data array
if results.count>0 {
for result in results {
//get fields from json
let Messageid = result["MessageID"] as! String
let MessageDesc = result["MessageDesc"] as? String
let newMessages = Notifications(MessageDesc: MessageDesc!, Messageid:Messageid)
//check with id's from core data
if (readRecordCoreData(result["MessageID"])==false)
{
Notification.append(newMessages)
}
}
}
return Notification
}
//check id
func readRecordCoreData(Jsonid: String) -> Bool {
var idStaus = false
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
//2
let fetchRequest = NSFetchRequest(entityName: "ItemLog")
//3
do {
let resultsCD = try! managedContext.executeFetchRequest(fetchRequest)
if (resultsCD.count > 0) {
for i in 0 ..< resultsCD.count {
let match = resultsCD[i] as! NSManagedObject
let id = match.valueForKey("notificationID") as! String
if (Jsonid as String! == id)
{
idStaus = true
}
else{
idStaus = false
}
}
}
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
return idStaus
}
One of your methods is static and the other one is not :
func readRecordCoreData(Jsonid: String) -> Bool
static func MessagesWithJSON(results: NSArray) -> [Notifications]
Depending on what you want to accomplish you could declare both static, none, or replace
//check with id's from core data
if (readRecordCoreData(result["MessageID"])==false)
{
Notification.append(newMessages)
}
By
//check with id's from core data
if (Notifications.readRecordCoreData(Messageid)==false)
{
Notification.append(newMessages)
}
Not sure if the code will work past compilation however as there are many readability issues