Opening and using a database from another view controller - sqlite.swift

I have created a database in one view controller and I would like to open it and access it in another view controller. I was wondering how to open up a existing database from one view controller in another view controller. I intend to open up the database and be able to update a row, so I would also need access to the "parts" (ex. id, name, email). Could you help me? Anything would be helpful. Thanks!

You can access your DB from any VC. Code (which you will probably use anywhere you want to access your DB from) would be like
let path = NSSearchPathForDirectoriesInDomains(
.documentDirectory, .userDomainMask, true
).first!
do {
let db = try Connection("\(path)/<your.db.filename>")
let recordsTable = Table("records")
//or whatever you'd like to do, extract, modify, insert or delete
}
catch let error {
print("Data operation failed for records table: \(error)")
}
Instead of copying same code all over your project, you can separately create your own class for any of your DB operations and access it from anywhere:
class DatabaseOps {
//..
func getHighscoreTable() -> Array<HighScoreDataArray> {
let path = NSSearchPathForDirectoriesInDomains(
.documentDirectory, .userDomainMask, true
).first!
var highscores = [HighScoreDataArray]()
do {
let db = try Connection("\(path)/tasksolver.db")
let score = Expression<Int>("Score")
let username = Expression<String>("Username")
let recordsTable = Table("records").order(score.desc).limit(10)
for row in try db.prepare(recordsTable) {
let highscore = HighScoreDataArray(score: Int(row[score]), username: String(row[username]))
highscores += [highscore]
}
} catch let error {
print("Data operation failed for records table: \(error)")
}
return highscores
}
//..
}
class RecordsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var myDb : DatabaseOps = DatabaseOps()
//...
super.viewDidLoad()
//get the data from database by using myDB method
myHighScoreTable = myDb.getHighscoreTable()
Also, before you try it in different VCs - don't forget to make sure you have your DB file in place. If it is not, let db = try Connection will create an empty database for you, which will not contain any of your data or tables you want to access.

Related

Can I grade assignments "logged as student" with Google Classroom API?

I have an app using Google Classroom API. When connected as the teacher I can create course works and assignments. When connected as a student I can list my assignments and I can turn in a specific assignment.
I am using the REST API:
https://developers.google.com/classroom/reference/rest
When (logged as student) I turn in an assignment but I would like to include a draft grade.
I know if I were logged as the teacher I could set the grade, but what I want is the app calculating the draft grade based on some specific built-in logic, so that the teacher does not have to do it on their own for each student.
According to the documentation, both "draftGrade" and "assignedGrade" can only be updated by the teacher.
https://developers.google.com/classroom/reference/rest/v1/courses.courseWork.studentSubmissions#StudentSubmission
Any ideas about how to automate setting grades for submissions?
I think that is not possible: you cannot update the draftGrade with student privileges.
What you can do:
From the "student" session you save a draft grade in the application DB, associated to the Submission ID.
From the "teacher" session, and hence "teacher" permissions, you get the grade from the application DB and I call the Path query to set the draftGrade.
Some code (Swift, using GoogleAPIClientForREST) for step 2:
func executeQuery_GradeSubmission(studentSubmission: GTLRClassroom_StudentSubmission) -> GTLRServiceTicket? {
guard let courseID = self.myClassroom?.courseID,
let courseWorkID = self.selectedCourseWorkID else { return nil }
if let grade = self.gradesForSelectedWorkID?[studentSubmission.identifier!] {
studentSubmission.draftGrade = NSNumber(floatLiteral: Double(grade))
}
let query = GTLRClassroomQuery_CoursesCourseWorkStudentSubmissionsPatch.query(withObject: studentSubmission,
courseId: courseID,
courseWorkId: courseWorkID,
identifier: studentSubmission.identifier!)
query.updateMask = "draftGrade"
return self.myClassroom?.service.executeQuery(query,
delegate: self,
didFinish: #selector(displayGradeSubmissionResult(ticket:finishedWithObject:error:)))
}
#objc func displayGradeSubmissionResult(ticket: GTLRServiceTicket, finishedWithObject: GTLRObject, error: Any?){
let classroomSubmissionResponse = finishedWithObject as? GTLRClassroom_StudentSubmission
if let classroomError = error as? NSError {
print("displayGradeSubmissionResult. ERROR: \(classroomError.description)")
// TODO: inform something went wrong
} else {
if let submissionItems = self.classroomSubmissionsResponse?.studentSubmissions {
for submissionItem in submissionItems {
if submissionItem.identifier == classroomSubmissionResponse?.identifier {
submissionItem.draftGrade = classroomSubmissionResponse?.draftGrade
}
}
}
}
}

SwiftUI List .onDelete not working with filtered Core Data

I have a SwiftUI application that is basically a master/detail app with data persisted in Core Data. The basic application works perfectly, however I have added a search bar (through UIViewRepresentable) and I had difficulty with the ForEach.onDelete functionality when the list is filtered.
If there is text in the search bar, I execute a fetch request on the Core Data entity which includes a predicate to search all of the text fields that I want to include. Then the filtered list is presented. Again, this works as expected. However, attempting to delete a record causes a crash. I believe this is because the IndexSet becomes invalid but I don't understand why that should be. At any rate, if I re-issue the call
to the filtered list in the .onDelete code (see the first .onDelete below), then the deletions work as expected. Even with this secondary search however, changes made on the detail screen are not shown when returning to the filtered list, though the changes are saved in Core Data.
While I guess it is not a big penalty to have to re-fetch, I am not confident that this is the proper procedure, and changes do not cause the List to update. Is there a way to use the #FetchRequest wrapper and dynamically provide a predicate? I have seen several SO posts on dynamic filters in Core Data but none of them have worked for me.
Any guidance would be appreciated.
struct ContentView: View {
#Environment(\.managedObjectContext) var managedObjectContext
#FetchRequest(fetchRequest: Patient.getAllPatients()) var patients: FetchedResults<Patient>
#State private var searchTerm: String = ""
#State var myFilteredPatients = Patient.filterForSearchPatients(searchText: "")
//more properties
var body: some View {
NavigationView {
List {
MySearchBar(sText: $searchTerm)
if !searchTerm.isEmpty {
ForEach(Patient.filterForSearchPatients(searchText: searchTerm)) { patient in
NavigationLink(destination: EditPatient(patient: patient, photoStore: self.photoStore, myTextViews: MyTextViews())) {
//configure the cell etc.
}//NavigationLink
}//for each
.onDelete { indexSet in
//This recreation was necessary:
self.myFilteredPatients = Patient.filterForSearchPatients(searchText: self.searchTerm)
let deleteItem = self.myFilteredPatients[indexSet.first!]
self.managedObjectContext.delete(deleteItem)
do {
try self.managedObjectContext.save()
} catch {
print(error)//put up an alert here
}
}
} else {
ForEach(self.patients) { patient in
NavigationLink(destination: EditPatient(patient: patient, photoStore: self.photoStore, myTextViews: MyTextViews())) {
//configure the cell etc
}//nav link
}//for each
.onDelete { indexSet in
let deleteItem = self.patients[indexSet.first!]
self.managedObjectContext.delete(deleteItem)
do {
try self.managedObjectContext.save()
} catch {
print(error)//put up and alert here
}
}
}//if else
}//List
//buttons and setup
}//NavigationView
}//body
Xcode Version 11.2 (11B52)

How to bind FloatRatingView rating to variable using RxSwift

I am attempting to integrate this library with my RxSwift project, https://github.com/glenyi/FloatRatingView
I am unable to get the updated rating.
Here is how I have created the FloatRatingView in the Controller,
let starRater : FloatRatingView = {
let floatRatingView = FloatRatingView()
floatRatingView.emptyImage = UIImage(named: "EmptyStar")
return floatRatingView
}()
The Model contains the following,
let my_rating = Variable<Float?>(nil)
What I want to be able to do is to update the value in the my_rating variable when a user changes the rating by tapping on a star. Here is what I've written for this,
_ = starRater.rx.rating
.asObservable()
.bindTo(viewModel.my_rating.rx_value)
But here is the error I'm receiving.
Value of type 'Reactive FloatRatingView' has no member 'rating'
Here is how I will retrieve the value from my_rating variable,
let stars = self.my_rating.value
Kindly help me out. Thanks.
You need to add a bindable sink for the property, it would be something like this:
extension Reactive where Base: FloatRatingView {
/// Bindable sink for `rating` property
public var progress: UIBindingObserver<Base, Float> {
return UIBindingObserver(UIElement: self.base) { floatRatingView, rating in
floatRatingView.rating = rating
}
}
}

How do you update objects in a Parse.com database using Xamarin?

I have an online Parse.com database and I can create objects and query them but not update. In the Xamarin section of the Parse.com documentation it only tells you how to update an object directly after you've created it which I don't want to do. I tried adapting what the documentation says for other platforms but it hasn't worked, I have also tried querying the database and entering the new field values directly after that but it treats them as separate functions. Does anyone have any help?
Parse.com documentation:
// Create the object.
var gameScore = new ParseObject("GameScore")
{
{ "score", 1337 },
{ "playerName", "Sean Plott" },
{ "cheatMode", false },
{ "skills", new List<string> { "pwnage", "flying" } },
};
await gameScore.SaveAsync();
// Now let's update it with some new data. In this case, only cheatMode
// and score will get sent to the cloud. playerName hasn't changed.
gameScore["cheatMode"] = true;
gameScore["score"] = 1338;
await gameScore.SaveAsync();
What I tried most recently:
ParseQuery<ParseObject> query = ParseObject.GetQuery("cust_tbl");
IEnumerable<ParseObject> customers = await query.FindAsync();
customers["user"] = admin;
record["score"] = 1338;
await record;
In your example, you are getting an list (IEnumerable) of objects instead of single object. Instead, try something like this:
ParseQuery<ParseObject> query = ParseObject.GetQuery("cust_tbl");
// you need to keep track of the ObjectID assigned when you first saved,
// otherwise you will have to query by some unique field like email or username
ParseObject customer = await query.GetAsync(objectId);
customer["user"] = admin;
customer["score"] = 1338;
await customer.SaveAsync();

How do I query Realm database based on current user ID?

I have a Realm database which holds records for different users with distinct userID's.
I also have a UITableViewController which displays the results of a Realm query. I would like the query to return only the Passages for the current user.
class PassageListController: UITableViewController {
var currentUserID: Int = NSUserDefaults.standardUserDefaults().objectForKey("currentUserID") as! Int
lazy var dataArray: Results<Passage> = { return try! Realm().objects(Passage).filter("userID == \(currentUserID)")}
The problem is that I'm getting the following error message:
Instance member 'currentUserID' cannot be used on type 'PassageListController'
This is despite trying to lazily load the Realm query. What is the best practice method to solve this problem?
Anticipating some solutions:
I can't load the query in viewDidAppear() as there is no way to define an empty Realm Result and it needs to be accessible throughout the controller.
I could set the currentUserID as a global variable but that violates my principles.
It looks like it's having some trouble working out the computed value of currentUserID before even getting to the Realm query.
Looking around on SO, it looks like this sort of issue can be solved by changing the definition of currentUserID to a read-only accessor:
var currentUserId: Int {
get {
return NSUserDefaults.standardUserDefaults().objectForKey("currentUserID") as! Int
}
}
I hope that helped!
TiM's answer clued me in to the right approach:
class PassageListController: UITableViewController {
var currentUserID: Int = NSUserDefaults.standardUserDefaults().objectForKey("currentUserID") as! Int
var dataArray: Results<Passage> {
get {
return try! Realm().objects(Passage).filter("userID == \(currentUserID)")
}
}
The project builds now but I'm not sure whether this is the best approach with Realm because
now the query will potentially be run every time dataArray is accessed, and
I'm not sure whether Realm's notifications will work.

Resources