I'm trying to access CMPedometer data in iOS 10 and I've followed a few tutorials and I just can't seem to get anything to work. I've never tried to access this information before so don't really know where to start. I'd love some help on getting something setup. Any help you can provide will be greatly appreciated.
I want to do both live updates and also access information form the passed but only within the same day.
This currently doesn't open and prints a massive error.
import UIKit
import CoreMotion
class ViewController: UIViewController {
var days:[String] = []
var stepsTaken:[Int] = []
let activityManager = CMMotionActivityManager()
let pedoMeter = CMPedometer()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let cal = Calendar.current
var comps = cal.components([.year, .month, .day, .hour, .minute, .second], from: Date())
comps.hour = 0
comps.minute = 0
comps.second = 0
let timeZone = TimeZone.system
cal.timeZone = timeZone
let midnightOfToday = cal.date(from: comps)!
if(CMPedometer.isStepCountingAvailable()){
self.pedoMeter.startUpdates(from: midnightOfToday) { (data: CMPedometerData?, error) -> Void in
DispatchQueue.main.async(execute: { () -> Void in
if(error == nil){
print("\(data!.numberOfSteps)")
//self.step.text = "\(data!.numberOfSteps)"
}
})
}
}
}
}
I've been playing around with some things and realised that if the simulator is open as well a big error message appears. However if its closed and running only on my phone the error is:
2016-07-16 18:13:38.054710 test[419:37870] [access] private
You are using a beta version of Xcode, the log is common for all users. Nothing to worry about it. Edit your code lil bit.
import UIKit
import CoreMotion
class ViewController: UIViewController {
var days:[String] = []
var stepsTaken:[Int] = []
let activityManager = CMMotionActivityManager()
let pedoMeter = CMPedometer()
override func viewDidLoad() {
super.viewDidLoad()
let cal = Calendar.current
var comps = cal.components([.year, .month, .day, .hour, .minute, .second], from: Date())
comps.hour = 0
comps.minute = 0
comps.second = 0
let timeZone = TimeZone.system
cal.timeZone = timeZone
let midnightOfToday = cal.date(from: comps)!
#if arch(i386) || arch(x86_64) && os(iOS)
// Simulator
#else
// Run only in Physical Device, iOS
if(CMPedometer.isStepCountingAvailable()){
self.pedoMeter.startUpdates(from: midnightOfToday) { (data: CMPedometerData?, error) -> Void in
DispatchQueue.main.async(execute: { () -> Void in
if(error == nil){
print("\(data!.numberOfSteps)")
//self.step.text = "\(data!.numberOfSteps)"
}
})
}
}
#endif
}
}
Add key Privacy - Motion Usage Description in info.plist, with String value YES.
Try running the app again in iPhone and check.
Related
I wrote a working function for the application, but the error came out "The nil value was unexpectedly found when an optional value was implicitly deployed" limit Limit label.the text I can't fix.
Properties:
#IBOutlet weak var limitLabel: UILabel!
Function:
func leftLabels(){
let limit = self.realm.objects(Limit.self)
guard limit.isEmpty == false else {return}
limitLabel.text = limit[0].limitSum //Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
let calendar = Calendar.current
let formatter = DateFormatter()
formatter.dateFormat = "yyyy/MM/dd HH:mm"
let firstDay = limit[0].limitDate as Date
let lastDay = limit[0].limitLastDate as Date
let firstComponent = calendar.dateComponents([.year, .month, .day], from: firstDay)
let lastComponent = calendar.dateComponents([.year, .month, .day], from: lastDay)
let startDate = formatter.date(from: "\(firstComponent.year!)/\(firstComponent.month!)/\(firstComponent.day!) 00:00")
let endDate = formatter.date(from: "\(lastComponent.year!)/\(lastComponent.month!)/\(lastComponent.day!) 23:59")
let filterLimit: Int = realm.objects(SpendingDB.self).filter("self.date >= %# && self.date <= %#", startDate ?? "", endDate ?? "").sum(ofProperty: "cost")
ForThePeriod.text = "\(filterLimit)"
let a = Int(limitLabel.text!)!
let b = Int(ForThePeriod.text!)!
let c = a - b
availableForSpending.text = "\(c)"
I will be glad if you tell me the correct code
As from comments if appears that your view is not yet loaded and some of your views are still nil. Your app crashes because in line limitLabel.text = limit[0].limitSum the limitLabel is nil. It would crash regardless of Realm even by calling limitLabel.text = "Hello world!"
You can always guard data that you need to avoid changes in your code. Simply add
guard let limitLabel = limitLabel else { return nil }
guard let ForThePeriod = ForThePeriod else { return nil }
and so on.
I tried to clean up your code a bit. It is hard to understand what exactly are you trying to achieve but something like the following may seem a bit more appropriate:
func leftLabels() {
// Elements needed for method to execute.
guard let limitLabel = limitLabel else { return }
guard let forThePeriodLabel = forThePeriodLabel else { return }
guard let availableForSpendingLabel = availableForSpendingLabel else { return }
// Items that will be reused throughout the method later on
let limits: [Limit]
let firstLimit: Limit
let dates: (start: Date?, end: Date?)
let filterLimit: Int
limits = self.realm.objects(Limit.self)
guard limits.isEmpty == false else { return }
firstLimit = limits[0]
// limitLabel
limitLabel.text = firstLimit.limitSum
// Date components
dates = {
let calendar = Calendar.current
let formatter = DateFormatter()
formatter.dateFormat = "yyyy/MM/dd HH:mm"
let firstDay = firstLimit.limitDate as Date
let lastDay = firstLimit.limitLastDate as Date
let firstComponent = calendar.dateComponents([.year, .month, .day], from: firstDay)
let lastComponent = calendar.dateComponents([.year, .month, .day], from: lastDay)
let startDate = formatter.date(from: "\(firstComponent.year!)/\(firstComponent.month!)/\(firstComponent.day!) 00:00")
let endDate = formatter.date(from: "\(lastComponent.year!)/\(lastComponent.month!)/\(lastComponent.day!) 23:59")
return (startDate, endDate)
}()
// forThePeriodLabel
filterLimit = realm.objects(SpendingDB.self).filter("self.date >= %# && self.date <= %#", startDate ?? "", endDate ?? "").sum(ofProperty: "cost")
forThePeriodLabel.text = String(filterLimit)
// availableForSpendingLabel
availableForSpendingLabel.text = {
guard let a = Int(firstLimit.limitSum) else { return "" }
let b = filterLimit
let c = a - b
return String(c)
}()
}
Note some practices which help you better to structure and solve your code.
Guard dangerous data at first
Create a list of reusable items for your method (there should be as fewer as possible, in most cases none). Note how these can be later assigned to. And if you try using it before assigning to it, you will be warned by your compiler.
Wrap as much code into closed sections such as availableForSpendingLabel.text = { ... code here ... }()
Use tuples such as let dates: (start: Date?, end: Date?)
Don't be afraid of using long names such as availableForSpendingLabel
I would even further try and break this down into multiple methods. But I am not sure what this method does and assume that you have posted only part of it...
========== EDIT: Adding alternate approach ==========
From comments this is a financial application so probably at least dealing with Decimal numbers would make sense. Also introducing approach with adding a new structure which resolves data internally. A formatter is also used to format the number. And some other improvements:
struct Limit {
let amount: Decimal
let startDate: Date
let endDate: Date
}
struct Spending {
let cost: Decimal
let date: Date
}
struct LimitReport {
let limitAmount: Decimal
let spendingSum: Decimal
let balance: Decimal
init(limit: Limit) {
let limitAmount: Decimal = limit.amount
let spendingSum: Decimal = {
let calendar = Calendar.autoupdatingCurrent // Is this OK or should it be some UTC or something?
func beginningOfDate(_ date: Date) -> Date {
let components = calendar.dateComponents([.day, .month, .year], from: date)
return calendar.date(from: components)!
}
let startDate = beginningOfDate(limit.startDate)
let endDate = calendar.date(byAdding: .day, value: 1, to: startDate)
let spendings: [Spending] = realm.objects(Spending.self).filter { $0.date >= startDate && $0.date < endDate }
return spendings.reduce(0, { $0 + $1.cost })
}()
let balance = limitAmount - spendingSum
self.limitAmount = limitAmount
self.spendingSum = spendingSum
self.balance = balance
}
}
func leftLabels() {
// Elements needed for method to execute.
guard let limitLabel = limitLabel else { return }
guard let forThePeriodLabel = forThePeriodLabel else { return }
guard let availableForSpendingLabel = availableForSpendingLabel else { return }
guard let limit = self.realm.objects(Limit.self).first else { return }
let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.currencySymbol = "$"
let report = LimitReport(limit: limit)
limitLabel.text = formatter.string(from: report.limitAmount)
forThePeriodLabel.text = formatter.string(from: report.spendingSum)
availableForSpendingLabel.text = formatter.string(from: report.balance)
}
Matic provided a good, comprehensive answer to your question (voted). I thought I'd provide an answer narrowly focused on your crash and a "short and sweet" way to fix it:
The line in question could crash 2 different ways:
limitLabel.text = limit[0].limitSum //Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value
Your limitLabel IBOutlet is declared as an "implicitly unwrapped Optional" (Note the ! after the type, UILabel:
#IBOutlet weak var limitLabel: UILabel!
An implicitly unwrapped Optional is an Optional where, essentially, the compiler adds a hidden "!" force-unwrap every time you try to reference that object.
That means that
limitLabel.text = //something
Is compiled as
limitLabel!.text = //something
and if limitLabel is nil, you crash.
If you call your leftLabels() function before your view has been loaded, or if that outlet is never connected, you will crash.
You can fix that by adding an optional unwrap to the statement:
limitLabel?.text = //something
(That construct is known as "optional chaining".)
Given that the crash message you're getting mentions "implicitly unwrapping an Optional value" it's likely that that is what is crashing in your case. However, you should fix the other issue as well.
The second way you can crash is in your array indexing.
limitLabel.text = limit[0].limitSum
When you fetch an object from an array by index, your app will crash if the array does not contain an item at that index. The expression limit[0] will crash if the limit array is empty.
The array type has a computed property first that will return an optional if the array is empty.
You should change that to limit.first?.limitSum.
Change the whole line to be:
limitLabel?.text = limit.first()?.limitSum
And it won't crash any more.
Objective: Read from HealthKit My Activity Data ( i.e. activity ring data - move calories, workout minutes, and stands )
Problem Experienced: Result Handler returns 'nil' HKActivitySummary Array
What I have Attempted:
I have added the HealthKit Capability to the project and added the two info.plist requirements:
Privacy - Health Share Usage Description
Privacy - Health Update Usage Description
And ensured I tapped "allow all" for permissions when the permission request popup appears for HealthKit within the iPhone.
I also have many weeks of activity data recorded so that should also be fine.
How can I retrieve the array of HKActivitySummary objects in order to replicate the rings within my iOS app.
My Code:
override func viewDidLoad() {
super.viewDidLoad()
startQueryForActivitySummary(view: self.view)
}
func startQueryForActivitySummary(view: UIView) {
let calendar = NSCalendar.current
let endDate = Date()
guard let startDate = calendar.date(byAdding: .day, value: -7, to: endDate) else {
fatalError("*** Unable to create the start date ***")
}
let units: Set<Calendar.Component> = [.day, .month, .year, .era]
var startDateComponents = calendar.dateComponents(units, from: startDate)
startDateComponents.calendar = calendar
var endDateComponents = calendar.dateComponents(units, from: endDate)
endDateComponents.calendar = calendar
let queryPredicate = HKQuery.predicate(forActivitySummariesBetweenStart: startDateComponents,
end: endDateComponents)
let query = HKActivitySummaryQuery(predicate: queryPredicate) { (query, summaries, error) -> Void in
if let summaries = summaries { // print(summaries) before this line will always return nil.
if let summary = summaries.first {
let activeEnergyBurned = summary.activeEnergyBurned.doubleValue(for: HKUnit.kilocalorie())
let activeEnergyBurnedGoal = summary.activeEnergyBurnedGoal.doubleValue(for: HKUnit.kilocalorie())
let activeEnergyBurnGoalPercent = round(activeEnergyBurned/activeEnergyBurnedGoal)
let frame = CGRect(x: 0, y: 0, width: 200, height: 200)
let ringView = HKActivityRingView(frame: frame)
view.addSubview(ringView)
ringView.setActivitySummary(summary, animated: true)
}
}
}
let allTypes = Set([HKObjectType.workoutType(),
HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!])
let healthStore = HKHealthStore()
healthStore.requestAuthorization(toShare: allTypes, read: allTypes) { (success, error) in
healthStore.execute(query)
}
}
I think you are missing permission HKObjectType.activitySummaryType()
Extend you types like this:
let allTypes = Set([
HKObjectType.workoutType(),
HKObjectType.quantityType(forIdentifier: .activeEnergyBurned)!,
HKObjectType.activitySummaryType()
])
Here is the documentation: https://developer.apple.com/documentation/healthkit/hkobjecttype/1615319-activitysummarytype
I'm populating a twodimensional array with HealthKit data. It's working fine as long as the function loops through the 30 days. outside of that function the array is empty because HealthKit data is loaded in background and everything else executes before.
How can I wait until the data is loaded so I can access the data inside the arrays?
Here's what's happening inside my ViewController
print("function call starting") // 1
// Populate 2DArrays
self.hkManager.getTotalCalories(forDay: 30) {caloriesValue, date in
//check if caloriesValue is nil, only do something if it's not
guard let caloriesValue = caloriesValue else { return }
let dateValue = DateFormatter.localizedString(from: date!, dateStyle: .short, timeStyle: .none)
print("will append") // 7 (31x)
self.caloriesArray.append([(dateValue,caloriesValue)])
}
print("function call done") // 2
print(self.caloriesArray.count) // 3
hkManager.getWeightData(forDay: 30) {bodyMassValue, date in
// Check if bodyMassValue is nil, only do something, if it's not
guard let bodyMassValue = bodyMassValue else { return }
let dateValue = DateFormatter.localizedString(from: date!, dateStyle: .short, timeStyle: .none)
self.bodyMassArray.append([(dateValue,bodyMassValue)])
}
do {
self.age = try hkManager.getAge()
} catch let error {
print("Error calculating age: \(error)")
}
print(bodyMassArray) // 4
print(caloriesArray) // 5
print(age!) // 6
}
I've added numbers behind the print statements to make clear what get's executed when.
The functions I'm calling look like this:
func getTotalCalories(forDay days: Int, completion: #escaping ((_ calories: Int?, _ date: Date?) -> Void)) {
// Getting quantityType as .dietaryCaloriesConsumed
guard let calories = HKObjectType.quantityType(forIdentifier: .dietaryEnergyConsumed) else {
print("*** Unable to create a dietaryEnergyConsumed type ***")
return
}
let now = Date()
let startDate = Calendar.current.date(byAdding: DateComponents(day: -days), to: now)!
var interval = DateComponents()
interval.day = 1
var anchorComponents = Calendar.current.dateComponents([.day, .month, .year], from: now)
anchorComponents.hour = 0
let anchorDate = Calendar.current.date(from: anchorComponents)!
let query = HKStatisticsCollectionQuery(quantityType: calories,
quantitySamplePredicate: nil,
options: [.cumulativeSum],
anchorDate: anchorDate,
intervalComponents: interval)
query.initialResultsHandler = { _, results, error in
guard let results = results else {
print("ERROR")
return
}
results.enumerateStatistics(from: startDate, to: now) { statistics, _ in
DispatchQueue.main.async {
if let sum = statistics.sumQuantity() {
let calories = Int(sum.doubleValue(for: HKUnit.kilocalorie()).rounded())
completion(calories, statistics.startDate)
return
}
}
}
}
healthStore.execute(query)
}
Anyone any ideas what I need to do?
Thanks! :)
I'm building an app for personal use, and I am currently stuck on how to accurately get yesterdays steps from the healthkit. And then from there, placing it into a variable (should be easy, I know).
I have a HealthKitManager class that calls the function from inside a view, and then appends that to a variable from that same view.
I have scoured most of the healthKit questions, and I get back data, but I don't think it is accurate data. My phone data from yesterday is 1442 steps, but it's returning 2665 steps. On top of that, when I try to put the data is a variable it prints out as 0.
HealthKitManagerClass
import Foundation
import HealthKit
class HealthKitManager {
let storage = HKHealthStore()
init()
{
checkAuthorization()
}
func checkAuthorization() -> Bool
{
// Default to assuming that we're authorized
var isEnabled = true
// Do we have access to HealthKit on this device?
if HKHealthStore.isHealthDataAvailable()
{
// We have to request each data type explicitly
let steps = NSSet(object: HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)!)
// Now we can request authorization for step count data
storage.requestAuthorizationToShareTypes(nil, readTypes: steps as? Set<HKObjectType>) { (success, error) -> Void in
isEnabled = success
}
}
else
{
isEnabled = false
}
return isEnabled
}
func yesterdaySteps(completion: (Double, NSError?) -> ())
{
// The type of data we are requesting (this is redundant and could probably be an enumeration
let type = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount)
// Our search predicate which will fetch data from now until a day ago
// (Note, 1.day comes from an extension
// You'll want to change that to your own NSDate
let calendar = NSCalendar.currentCalendar()
let yesterday = calendar.dateByAddingUnit(.Day, value: -1, toDate: NSDate(), options: [])
//this is probably why my data is wrong
let predicate = HKQuery.predicateForSamplesWithStartDate(yesterday, endDate: NSDate(), options: .None)
// The actual HealthKit Query which will fetch all of the steps and sub them up for us.
let query = HKSampleQuery(sampleType: type!, predicate: predicate, limit: 0, sortDescriptors: nil) { query, results, error in
var steps: Double = 0
if results?.count > 0
{
for result in results as! [HKQuantitySample]
{
steps += result.quantity.doubleValueForUnit(HKUnit.countUnit())
}
}
//I'm unsure if this is correct as well
completion(steps, error)
print("\(steps) STEPS FROM HEALTH KIT")
//this adds the steps to my character (is this in the right place)
Player.User.Gold.addSteps(Int(steps))
}
//not 100% on what this does, but I know it is necessary
storage.executeQuery(query)
}}
ViewControllerClass
import UIKit
import Foundation
class UpdateViewController: UIViewController {
#IBOutlet var back: UIButton!
let HKM = HealthKitManager()
var stepsFromPhone = Double()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
back.transform = CGAffineTransformMakeRotation(CGFloat(M_PI_2))
HKM.yesterdaySteps(){ steps, error in
self.stepsFromPhone = steps
}
Player.User.Gold.addSteps(Int(stepsFromPhone))
print(Player.User.Gold.getSteps(), "IN PLAYER")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The output from
print(Player.User.Gold.getSteps(), "IN PLAYER")
is
0 IN PLAYER
The output from
print("\(steps) STEPS FROM HEALTH KIT")
is
2665.0 STEPS FROM HEALTH KIT
so, basically my questions are:
what NSDate() do I need for the whole of yesterday?
how do I take the steps from the yesterdaySteps() and correctly place them into a variable in the UpdateViewController?
Thank you for any help!
This is the method I am using in my healthStore class
func TodayTotalSteps(completion: (stepRetrieved: Double) -> Void) {
let type = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount) // The type of data we are requesting
let calendar = NSCalendar.currentCalendar()
let interval = NSDateComponents()
interval.day = 1
let anchorComponents = calendar.components([.Day , .Month , .Year], fromDate: NSDate())
anchorComponents.hour = 0
let anchorDate = calendar.dateFromComponents(anchorComponents)
let stepsQuery = HKStatisticsCollectionQuery(quantityType: type!, quantitySamplePredicate: nil, options: .CumulativeSum, anchorDate: anchorDate!, intervalComponents: interval)
stepsQuery.initialResultsHandler = {query, results, error in
let endDate = NSDate()
var steps = 0.0
let startDate = calendar.dateByAddingUnit(.Day, value: 0, toDate: endDate, options: [])
if let myResults = results{ myResults.enumerateStatisticsFromDate(startDate!, toDate: endDate) { statistics, stop in
if let quantity = statistics.sumQuantity(){
let date = statistics.startDate
steps = quantity.doubleValueForUnit(HKUnit.countUnit())
// print("\(date): steps = \(steps)")
}
completion(stepRetrieved: steps)
}
} else {
completion(stepRetrieved: steps)
}
}
executeQuery(stepsQuery)
}
and here is How I am using it
func getStepsData() {
// I am sendng steps to my server thats why using this variable
var stepsToSend = 0
MyHealthStore.sharedHealthStore.todayManuallyAddedSteps({ (steps , error) in
if error != nil{
// handle error
}
else{
// truncating manuall steps
MyHealthStore.sharedHealthStore.TodayTotalSteps({ (stepRetrieved) in
stepsToSend = Int(stepRetrieved - steps)
})
}
})
}
and here is the function used above for manually added steps which we are truncating in order to get exact steps
func todayManuallyAddedSteps(completion: (Double, NSError?) -> () )
{
let type = HKSampleType.quantityTypeForIdentifier(HKQuantityTypeIdentifierStepCount) // The type of data we are requesting
let date = NSDate()
let cal = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)!
let newDate = cal.startOfDayForDate(date)
let predicate = HKQuery.predicateForSamplesWithStartDate(newDate, endDate: NSDate(), options: .None) // Our search predicate which will fetch all steps taken today
// The actual HealthKit Query which will fetch all of the steps and add them up for us.
let query = HKSampleQuery(sampleType: type!, predicate: predicate, limit: 0, sortDescriptors: nil) { query, results, error in
var steps: Double = 0
if results?.count > 0
{
for result in results as! [HKQuantitySample]
{
// checking and adding manually added steps
if result.sourceRevision.source.name == "Health" {
// these are manually added steps
steps += result.quantity.doubleValueForUnit(HKUnit.countUnit())
}
else{
// these are auto detected steps which we do not want from using HKSampleQuery
}
}
completion(steps, error)
} else {
completion(steps, error)
}
}
executeQuery(query)
}
I hope it helps. Let me know if you face any issue.
You can use HKStatisticsQuery
let quantityType = HKSampleType.quantityType(forIdentifier: .stepCount)!
let predicate = HKQuery.predicateForSamples(
withStart: startDate,
end: endDate,
options: [.strictStartDate, .strictEndDate]
)
let query = HKStatisticsQuery(
quantityType: quantityType,
quantitySamplePredicate: predicate,
options: .cumulativeSum) { (query, result, error) in
guard let result = result, error == nil else {
print("HeathService error \(String(describing: error))")
return
}
callback(result)
}
Need to sort on iOS by date using Parse.com but I need to order them based on the next birthday for a specific person. I tried to add the day and month with the current or next year and that helped but that will be a manual process, any recommendations to do this in an automated way.
As a side note I want to implement it in swift
Thanks all in advance.
The exact code will depend on your personal setup (I can imagine you've made a subclass of PFObject or similar), but try to experiment with the following code. I've also made another function called filterFriendsAfterBirthdays that might be useful for somebody reading this post.
//
// ViewController.swift
// ParseFun
//
// Created by Stefan Veis Pennerup on 20/06/15.
// Copyright (c) 2015 Kumuluzz. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
createDummyData()
queryForFriends() {
println("Unsorted birthdays: \($0)")
println("Sorted birthdays: \(self.filterFriendsAfterBirthdays($0))")
println("Closets upcoming birthday: \(self.filterFriendsBasedOnClosetsUpcomingBirthday($0))")
}
}
func createDummyData() {
for var i = 0; i < 25; i++ {
let myFriend = PFObject(className: "Friends")
let interval = Double(arc4random_uniform(UInt32.max))
myFriend["birthday"] = NSDate(timeIntervalSince1970: interval)
myFriend.saveInBackground()
}
}
func queryForFriends(completionHandler: ([PFObject]) -> ()) {
let friendsQuery = PFQuery(className: "Friends")
friendsQuery.findObjectsInBackgroundWithBlock { (result, error) in
if (error != nil) { return }
let pfArray = result as! [PFObject]
completionHandler(pfArray)
}
}
func filterFriendsAfterBirthdays(friends: [PFObject]) -> [PFObject] {
return friends.sorted {
return ($0["birthday"] as! NSDate).compare($1["birthday"] as! NSDate) == .OrderedAscending
}
}
func filterFriendsBasedOnClosetsUpcomingBirthday(friends: [PFObject]) -> [PFObject] {
let cal = NSCalendar.currentCalendar()
// Specifies the day unit
let dayUnit: NSCalendarUnit = .CalendarUnitDay
// Gets todays year
let today = NSDate()
let yearUnit: NSCalendarUnit = .CalendarUnitYear
let yearToday = cal.components(yearUnit, fromDate: today)
// Combined days and year units
let combinedUnits: NSCalendarUnit = .CalendarUnitYear | .CalendarUnitDay
return friends.sorted {
// Gets the birthday components since today
// Also uses the year unit to ensure that the day will be between -365 and 365
let birth1Components = cal.components(combinedUnits, fromDate: today, toDate: ($0["birthday"] as! NSDate), options: nil)
let birth2Components = cal.components(combinedUnits, fromDate: today, toDate: ($1["birthday"] as! NSDate), options: nil)
// Updates the days to a positive integer
if (birth1Components.day < 0) { birth1Components.day += 365 }
if (birth2Components.day < 0) { birth2Components.day += 365 }
return birth1Components.day <= birth2Components.day
}
}
}