Swift display previous entered item in array - arrays

I am trying to display the pervious entered exercise attributes in the current workout. If I go to the pervious workout everything shows up but when I go to my current workout the previous exercise attributes don't show and the date label only shows today's date and not the previous workout date. Here are the two functions for the issue. Let me know if i need to post more.
func lastWorkout() -> Workout? {
if let client = currentClient(), let currentWorkout = currentWorkout(), let workouts = client.workouts as? Set<Workout> {
// get all this client's workouts in cronological order
let sortedWorkouts = workouts.sorted { (one, two) -> Bool in
let scheduledTimeOfOne = one.appointment?.scheduled ?? Date(timeIntervalSince1970: 0)
let scheduledTimeOfTwo = two.appointment?.scheduled ?? Date(timeIntervalSince1970: 0)
return scheduledTimeOfOne > scheduledTimeOfTwo
}
// get the index of this workout
let indexOfTodaysWorkout = sortedWorkouts.index(of: currentWorkout) ?? 0
// go back one workout to find the last workout completed
let lastWorkout: Workout? = (indexOfTodaysWorkout - 1) < 0 ? nil : sortedWorkouts[indexOfTodaysWorkout - 1]
// and return
return lastWorkout
}
return nil
}
/// Last Exercise Info to load previous exercise data
func lastExercise() -> Exercise? {
guard let selectedExercise = currentExerciseInfo() else{
return nil
}
if let exercises = lastWorkout()?.exercises as? Set<Exercise>, let pastExercise = exercises.first(where: { $0.exerciseInfo == selectedExercise }) {
return pastExercise
}
return nil
}

So the array count was off in last workout function. Here is what the working function looks like. I am still not displaying the proper date. it just gives today's date.
func lastWorkout() -> Workout? {
if let client = currentClient(), let currentWorkout = currentWorkout(), let workouts = client.workouts as? Set<Workout> {
// get all this client's workouts in cronological order
let sortedWorkouts = workouts.sorted { (one, two) -> Bool in
let scheduledTimeOfOne = one.appointment?.scheduled ?? Date(timeIntervalSince1970: 0)
let scheduledTimeOfTwo = two.appointment?.scheduled ?? Date(timeIntervalSince1970: 0)
return scheduledTimeOfOne > scheduledTimeOfTwo
}
// get the index of this workout
let indexOfTodaysWorkout = sortedWorkouts.index(of: currentWorkout) ?? 0
// go back one workout to find the last workout completed
let lastWorkout: Workout? = sortedWorkouts.count < 2 ? nil : sortedWorkouts[indexOfTodaysWorkout + 1]
// and return
return lastWorkout
}
return nil
}

Related

Swift average of items in database

I am trying to calculate the average daily rating for workouts in my app. my results keeps coming back as NaN. My data base shows that there are ratings in there. Here is my average rating function
extension Appointment {
func averageReview() -> Double {
guard let workouts = workouts?.allObjects as? [Workout] else {
return 0
}
let total = Double(workouts.reduce(0) { $0 + $1.review})
let results = Double(total) / Double(workouts.count)
return results
}
}
You are dividing by workouts.count without making sure it's not equal to zero.
extension Appointment {
func averageReview() -> Double {
guard let workouts = workouts?.allObjects as? [Workout], workouts.count > 0 else {
return 0
}
let total = Double(workouts.reduce(0) { $0 + $1.review})
let results = Double(total) / Double(workouts.count)
return results
}
}

Random photos in 12 views – Fatal error: Index out of range

I've got Thread 1: Fatal error: Index out of range looping my 7 photos from array in NSViews.
How to fix it?
let url = URL(fileURLWithPath: NSHomeDirectory()).appendingPathComponent("Desktop/ArrayOfElements")
do {
let fileURLs = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]).reversed()
let photos = fileURLs.filter { $0.pathExtension == "jpg" }
for index in photos {
let image = [NSImage(data: try Data(contentsOf: index))]
for view in arrayOfViews {
let i = Int(arc4random_uniform(UInt32(photos.count-1)))
view.image = image[i]
}
}
} catch {
print(error)
}
It seems that this line is wrong:
view.image = image[i]
image array has length = 1
Use view.image = image[0] instead
EDIT
let url = URL(fileURLWithPath: NSHomeDirectory()).appendingPathComponent("Desktop/ArrayOfElements")
do {
let fileURLs = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: [.skipsHiddenFiles]).reversed()
let photos = fileURLs.filter { $0.pathExtension == "jpg" }
for view in arrayOfViews {
let i = Int(arc4random_uniform(UInt32(photos.count-1)))
let image = NSImage(data: try Data(contentsOf: photos[i]))
view.image = image
}
} catch {
print(error)
}
Try to create a category to Collection
extension Collection where Index == Int {
/**
Gives a random element of the collection.
- returns: A random element of the collection.
*/
func randomElement() -> Iterator.Element? {
return isEmpty ? nil : self[Int(arc4random_uniform(UInt32(endIndex)))]
}
}
Usage
let numbers = [1,2,3,4,5,6,7,8,9,10]
let randomNumber = numbers.randomElement()
print(randomNumber!)
Edit:
I guess you are making mistake in the following code
for view in arrayOfViews {
let i = Int(arc4random_uniform(UInt32(photos.count-1)))
view.image = image[i]
}
Let's assume photos objects having 10 elements and image object have less than 10 elements, so in such case, you will get this type of error. So change it to the following
for view in arrayOfViews {
let i = Int(arc4random_uniform(UInt32(image.endIndex)))
view.image = image[i]
}

Parsing JSON array to label

I am trying to parse the JSON below (actual data is 20x the format listed)
{
message = "";
result = (
{
Ask = "4.8e-05";
BaseVolume = "32.61025363";
Bid = "4.695e-05";
Created = "2017-06-06T01:22:35.727";
High = "5.44e-05";
Last = "4.69e-05";
Low = "4.683e-05";
MarketName = "BTC-1ST";
OpenBuyOrders = 293;
OpenSellOrders = 4186;
PrevDay = "4.76e-05";
TimeStamp = "2018-02-20T00:00:31.863";
Volume = "662575.93818332";
},
This is the code that I have right now. It successfully prints the value "Last" to the console but when I incorporate the Dispatch.Queue, I get a Thread 1: signal SIGBRT not printing the value to the label.
let myJson = try JSONSerialization.jsonObject(with: content) as! [String:Any]
if let info = myJson["result"] as! [[String:Any]]?
{
for i in 0..<20 {
if i == 1
{
if let dict = info[i] as? [String:Any]
{
if let price = dict["Last"]
{
print(price)
//DispatchQueue.main.async
//{
// self.label1.text = price as String
//}
}
}
}
Any help is greatly appreciated!
Most likely your self.label1 outlet isn't connected. Fix that connection.
You should also update the if let that gets the value for the "Last" key as follows:
if let price = dict["Last"] as? String{
print(price)
DispatchQueue.main.async {
self.label1.text = price
}
}
There is some other cleanup you can do as well:
if let myJson = try JSONSerialization.jsonObject(with: content) as? [String:Any] {
if let info = myJson["result"] as? [[String:Any]] {
for (index, dict) in info.enumerated() {
if index == 1 {
if let price = dict["Last"] as? String {
print(price)
DispatchQueue.main.async {
self.label1.text = price
}
} // else no "Last" or not a String
}
}
} // else "result" doesn't contain expected array of dictionary
} // else content isn't a valid JSON dictionary
Avoid all of those forced casts. Especially avoid force casting to an optional.
JSON doesn't use the = sign or the semicolon. Change every = to a colon and every semicolon to a comma, so that
Ask = "4.8e-05";
BaseVolume = "32.61025363";
Bid = "4.695e-05";
Becomes
Ask: "4.8e-05",
BaseVolume: "32.61025363",
Bid: "4.695e-05",

CloudKit nil optional error

I am trying to load, then modify and resave an array.
Here is code, modify func is the top one:
func modifyUserGroupsRequestee(){
print("step2")
acceptedUsersArray.append(groupNameLbl.text!)
//error
userGroupRecordToUpdate.setObject(acceptedUsersArray as CKRecordValue?, forKey: "userGroups")
database.save(recordToUpdate) { (savedRecord, error) in
if error != nil{
print(error.debugDescription)
}else{
print("SAVED RECORD")
}
}
}
func resaveUserGroups(){
print(addedUser)
print("step1")
// add group to requestees user groups
let pred = NSPredicate(format: "username = %#", "\(addedUser)")
let query = CKQuery(recordType: "PersonalUser", predicate: pred)
let operation = CKQueryOperation(query: query)
//operation.resultsLimit = CKQueryOperationMaximumResults
operation.recordFetchedBlock = { (record: CKRecord!) in
if record != nil{
self.userGroupRecordToUpdate = record
// self.acceptedUsersArray = (record.object(forKey: "userGroups") as! Array)
print("usergroup names:: \(self.acceptedUsersArray)")
if let acceptedUserArrays = record.object(forKey: "userGroups") as? [String] {
// self.feedTableView.reloadData()
self.acceptedUsersArray = acceptedUserArrays
print("looks like we r going forward")
self.modifyUserGroupsRequestee()
// }
//self.feedTableView.reloadData()
print(groupNames.count)
print(self.acceptedUsersArray)
}
}
database.add(operation)
//self.tableView.reloadData()
// print(leaderboardInfo.count)
}
}
The function prints step1 but never gets to step2. In the bottom function, I have an if let statement I tried to create to solve my nil issue (I commented my previous code above that line- self.acceptedUsersArray... Anyway, I believe I am implementing the if let statement incorrectly, because no data is loaded, even though there is data in cloud kit.
And I do have my personal user cloudKit records set up, here's a pic:
You should try to keep your code always indented consistently.
(In Xcode editor, Cmd+A (Select All), then Ctrl+I (Re-Indent).)
With confusing comments removed, your resaveUserGroups shows as:
func resaveUserGroups() {
print(addedUser)
print("step1")
// add group to requestees user groups
let pred = NSPredicate(format: "username = %#", "\(addedUser)")
let query = CKQuery(recordType: "PersonalUser", predicate: pred)
let operation = CKQueryOperation(query: query)
operation.recordFetchedBlock = { (record: CKRecord!) in
if record != nil {
self.userGroupRecordToUpdate = record
print("usergroup names:: \(self.acceptedUsersArray)")
if let acceptedUserArrays = record.object(forKey: "userGroups") as? [String] {
self.acceptedUsersArray = acceptedUserArrays
print("looks like we r going forward")
self.modifyUserGroupsRequestee()
print(groupNames.count)
print(self.acceptedUsersArray)
}
}
database.add(operation)
}
}
Omitting some parts to clarify:
func resaveUserGroups() {
//...
operation.recordFetchedBlock = { (record: CKRecord!) in
if record != nil {
//...
}
database.add(operation)
}
}
The line database.add(operation) exists inside the recordFetchedBlock.
You may need to fix some more parts (that's another story), but at least, you need to move the line out of the closure to execute the operation you have created:
func resaveUserGroups() {
//...
operation.recordFetchedBlock = { (record: CKRecord!) in
if record != nil {
//...
}
//database.add(operation)
} //↓
database.add(operation)
}
I just solved it. Apparently there always needs to be some kind of value inside the array even if it was just created. I was trying to query an array that existed in the recordType, but not yet under the specific record.

How to search the minimum positive value in an array?

In my code, checkTheNextTime() function's array contains the strings 00.00 to 23.59. By writing this function I want to find the nearest future time. But when I tried with timeTable(shown in code) it returns 23.30 instead of 23.32(Now is 22.24). I guess the compiler search the array right to left. How can I find the nearest future time?
var timeTable = ["09.00","10.20","10.35","11.55","12.00","12.40","13.20","14.40","14.50", "23.00", "23.30", "23.31", "23.32"]
func checkTheNextTime(array array: Array<String>) -> String{
var nextTime: String?
for index in array {
let generatedString:String = getTimeAsMinToCheck(finalTime: index)
let indexInt = Int(generatedString)
if indexInt > 0{
nextTime = index
}
}
return nextTime!
}
func getTimeAsMinToCheck(finalTime finalTime: String) -> String{
let date = NSDate()
let formatter = NSDateFormatter()
formatter.timeStyle = NSDateFormatterStyle.NoStyle
formatter.dateStyle = NSDateFormatterStyle.ShortStyle
let now = formatter.stringFromDate(date)
formatter.locale = NSLocale.systemLocale()
formatter.dateFormat = "M/dd/yy HH.mm"
let datetofinish = formatter.dateFromString("\(now) \(finalTime)")
let finishDate: NSDate = datetofinish!
let secondsFromNowToFinish = finishDate.timeIntervalSinceNow
let minutes = Int(secondsFromNowToFinish / 60)
return String(minutes)
}
Assuming 23 is the right answer (its not clear from the comments above), here a a solution using swift 2.0 and closures
map your timeTable array into an array of delta's from the current
time (invalid entries are mapped to 0)
add the minimum delta to the time now
let timeNow: Float = 22.24
let timeTable = ["09.00","10.20","10.35","11.55","12.00","12.40","13.20","14.40","14.50", "23.00", "23.30", "23.31", "23.32"]
let minDelta = timeTable
.map { Float(NSNumberFormatter().numberFromString($0) ?? 0.0) - timeNow }
.filter { $0 > 0 }
.minElement()
let nextTime = (minDelta ?? 0) + timeNow
print(nextTime) // 23.0
This code should work for your requirement:
Done in Swift 2.0:
var timeTable = ["09.00","10.20","10.35","11.55","12.00","12.40","13.20","14.40","14.50", "23.00", "23.30", "23.31", "23.32"]
func checkTheNextTime(array array: Array<String>) -> String{
let currentTime:String = getTimeAsMinToCheck(finalTime: "23.24") // set this value by calculating from current time
let currentTimeInt = Int(currentTime)// Int value of currentTime
var nextTime: String? //this will hold the nearest future value
var minDiff: Int = 24*60 //lets start with maximum value
for index in timeTable {
let generatedString:String = getTimeAsMinToCheck(finalTime: index)
let indexInt = Int(generatedString)
if (indexInt > currentTimeInt) { //checking for future time only
let timeDiff = indexInt - currentTimeInt // this will be positive
if (timeDiff < minDiff) {
minDiff = timeDiff //update minDiff as timeDiff is less than minDiff
nextTime = index
}
}
}
return nextTime!
}

Resources