Adding a google calendar event in swift - ios

I am trying to create a Google Calendar event using the API in Swift. I am kind of lost at the moment in how to go about that. More specifically creating a GTLRCalendar_Event object to pass through GTLRCalendarQuery_EventsInsert.query(). Any way to go about this?
I've written the following code
var newEvent: GTLRCalendar_Event = GTLRCalendar_Event()
newEvent.summary = name
//set GTLRDateTimes
var startTime: GTLRDateTime = GTLRDateTime(date:startTimeObject!, offsetMinutes: offsetMinutes)
var endTime: GTLRDateTime = GTLRDateTime(date:endTimeObject!, offsetMinutes: offsetMinutes)
newEvent.reminders?.useDefault = 0
newEvent.start?.dateTime = startTime
newEvent.end?.dateTime = endTime
let service: GTLRCalendarService = GTLRCalendarService()
let query:GTLRCalendarQuery_EventsInsert = GTLRCalendarQuery_EventsInsert.query(withObject: newEvent, calendarId:"primary")
service.executeQuery(query, completionHandler: {(_ callbackTicket: GTLRServiceTicket, _ event: GTLRCalendar_Event, _ callbackError: Error?) -> Void in
print("executed query")
if callbackError == nil {
print("added")
print(newEvent.summary);
}
else {
print("add failed")
print(callbackError)
}
} as? GTLRServiceCompletionHandler)

I got this to work in Swift 4. I based it on the Java code example that Google has because that one was the most similar. I hope this answers all of your questions. I am sure there is a prettier way to do this, but I don't know it. :)
//Declares the new event
var newEvent: GTLRCalendar_Event = GTLRCalendar_Event()
//this is setting the parameters of the new event
newEvent.summary = ("Google I/O 2015")
newEvent.location = ("800 Howard St., San Francisco, CA 94103")
//I ran into some problems with the date formatting and this is what I ended with.
//Start Date. The offset adds time to the current time so if you run the program at 12:00 then it will record a time of 12:05 because of the 5 minute offset
let startDateTime: GTLRDateTime = GTLRDateTime(date: Date(), offsetMinutes: 5)
let startEventDateTime: GTLRCalendar_EventDateTime = GTLRCalendar_EventDateTime()
startEventDateTime.dateTime = startDateTime
newEvent.start = startEventDateTime
print(newEvent.start!)
//Same as start date, but for the end date
let endDateTime: GTLRDateTime = GTLRDateTime(date: Date(), offsetMinutes: 50)
let endEventDateTime: GTLRCalendar_EventDateTime = GTLRCalendar_EventDateTime()
endEventDateTime.dateTime = endDateTime
newEvent.end = endEventDateTime
print(newEvent.end!)
let service: GTLRCalendarService = GTLRCalendarService()
//The query
let query =
GTLRCalendarQuery_EventsInsert.query(withObject: newEvent, calendarId:"Past your calendar ID here this is specific to the calendar you want to edit and can be found under the google calendar settings")
//This is the part that I forgot. Specify your fields! I think this will change when you add other perimeters, but I need to review how this works more.
query.fields = "id";
//This is actually running the query you just built
self.service.executeQuery(
query,
completionHandler: {(_ callbackTicket:GTLRServiceTicket,
_ event:GTLRCalendar_Event,
_ callbackError: Error?) -> Void in}
as? GTLRServiceCompletionHandler
)
}

I was facing the same problem during the lack of resources at this topic, those are the steps
->configure your app with google calendar account
1-go to https://console.developers.google.com/ add a new project with app bundle id and name
2- go to dashboard click Enable APIS AND SERVICES then choose a calendar API Service and enable It.
3-choose credentials from the side menu and click CREATE CREDENTIALS Link from top of the page and add OAuth Client ID
4-open firebase console https://console.firebase.google.com/u/0/
5- click add project and choose your existing app and continue
6- follow the steps here https://firebase.google.com/docs/ios/setup until download "GoogleService-Info.plist" and add it to your app
-> write code to add an event to google calendar
1-follow those steps to add google sign-in
https://developers.google.com/identity/sign-in/ios/sign-in?ver=swift
2-
// Create an event to the Google Calendar's user
func addEventoToGoogleCalendar(summary : String, description :String, startTime : String, endTime : String) {
let calendarEvent = GTLRCalendar_Event()
calendarEvent.summary = "\(summary)"
calendarEvent.descriptionProperty = "\(description)"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd/MM/yyyy HH:mm"
let startDate = dateFormatter.date(from: startTime)
let endDate = dateFormatter.date(from: endTime)
guard let toBuildDateStart = startDate else {
print("Error getting start date")
return
}
guard let toBuildDateEnd = endDate else {
print("Error getting end date")
return
}
calendarEvent.start = buildDate(date: toBuildDateStart)
calendarEvent.end = buildDate(date: toBuildDateEnd)
let insertQuery = GTLRCalendarQuery_EventsInsert.query(withObject: calendarEvent, calendarId: "primary")
service.executeQuery(insertQuery) { (ticket, object, error) in
if error == nil {
print("Event inserted")
} else {
print(error)
}
}
}
// Helper to build date
func buildDate(date: Date) -> GTLRCalendar_EventDateTime {
let datetime = GTLRDateTime(date: date)
let dateObject = GTLRCalendar_EventDateTime()
dateObject.dateTime = datetime
return dateObject
}
// Helper for showing an alert
func showAlert(title : String, message: String) {
let alert = UIAlertController(
title: title,
message: message,
preferredStyle: UIAlertController.Style.alert
)
let ok = UIAlertAction(
title: "OK",
style: UIAlertAction.Style.default,
handler: nil
)
alert.addAction(ok)
present(alert, animated: true, completion: nil)
}
here is the GitHub link https://github.com/emanShedeed/writeEventToGoogleCalendar

Related

Using Swift, how do I add a new event to a previously created calendar?

I've created a function in swift that checks through all the Calendars currently saved on an iOS device, then creates a new one if a calendar with the specific title "TESTCAL" (in this case) doesn't exist.
func checkCal(){
print("checkCal")
let calendars = eventStore.calendars(for: EKEntityType.event) as [EKCalendar]
var exists = false
for calendar in calendars {
if calendar.title == "TESTCAL"{
exists = true
print("FOUND CAL: TESTCAL")
print(calendar.title)
print(calendar.calendarIdentifier)
self.calIdent = calendar.calendarIdentifier
} else{
print("NO CAL: ", calendar.title)
}
}
if exists == false {
let newCal = EKCalendar(for: EKEntityType.event, eventStore: eventStore)
newCal.title = "TESTCAL"
newCal.source = eventStore.defaultCalendarForNewEvents?.source
_ = try? eventStore.saveCalendar(newCal, commit: true)
print("CAL CREATED: ", newCal.title)
print("With Ident: ", newCal.calendarIdentifier)
self.calIdent = newCal.calendarIdentifier
}
addToCal()
}
Then when I come to add the event later, in the following way
func addToCal()
{
let eventVC = EKEventEditViewController()
eventVC.editViewDelegate = self
eventVC.eventStore = EKEventStore()
let date = self.passedDate
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "dd-MM-yy"
let dateForCal = dateFormatter.date(from: date)
let event = EKEvent(eventStore: eventVC.eventStore)
event.title = self.passedTitle
event.notes = self.passedDetail
event.startDate = dateForCal
event.endDate = dateForCal
event.isAllDay = true
event.url = URL(string: passedWebLink) }
The event gets added to the iOS default calendar.
I've tried adding the following code to the addCal() function:
event.calendar = eventStore.calendar(withIdentifier: self.calIdent)
and
let calendars = eventStore.calendars(for: EKEntityType.event) as [EKCalendar]
for calendar in calendars {
if calendar.title == "TESTCAL"{
event.calendar = calendar
print("YES")
} else{
print("NO")
}
}
Both of these give me error message in the AppDelegate and cause a crash:
[EventKit] Error getting shared calendar invitaions for entity types 3 from daemon: Error Domain=EKCADErrorDomain Code 1014 "(null)
Any ideas?
I see more possible causes for the issue you are experiencing:
The TESTCAL calendar is not created successfully. You can validate the calendar creation by catching the possible error thrown on saving the calendar:
do {
try eventStore.saveCalendar(newCal, commit: true)
} catch {
print(error)
}
How the event saving is happening is not clear from the code you posted.
If you are presenting the EKEventEditViewController instance for the user to
complete the event creation, you are missing setting the event on the view
controller in the addToCal method:
eventVC.event = event
If you want to save the event directly, then you don't need to use
EKEventEditViewController, but instead call save on the eventStore:
do {
try eventStore.save(event, span: .thisEvent)
} catch {
print(error)
}
For shared calendars, an additional privacy key for contacts accessing is needed in the .plist file of your target, namely NSContactsUsageDescription. Without this key, accessing shared calendars causes a crash.
With everything from the above checked/taken care of, your solution of setting the calendar on the event should work:
event.calendar = eventStore.calendar(withIdentifier: self.calIdent)
Also, make sure to have in place the code for requesting event kit store access from the user before any calendar/event manipulation. For more info on requesting access, check https://developer.apple.com/documentation/eventkit/ekeventstore/1507547-requestaccess

Healthkit not returning latest data on iphone. Using healthkit between iPhone and Apple watch

I am creating an app that provides workout information. On apple watch i have three labels that actively shows HEARTRATE ,DISTANCE TRAVELLED and CALORIES BURNED. Things work fine with apple watch, however the area where things go bad is when i try to show this data on iphone in realtime.
APPLE WATCH PART :-
I started workout on apple watch and saved it using folling code
func startWorkoutSession() {
// Start a workout session with the configuration
if let workoutConfiguration = configuration {
do {
workoutSession = try HKWorkoutSession(configuration: workoutConfiguration)
workoutSession?.delegate = self
workoutStartDate = Date()
healthStore.start(workoutSession!)
} catch {
// ...
}
}
}
Then i save that workout on apple watch using following code :-
func saveWorkout() {
// Create and save a workout sample
let configuration = workoutSession!.workoutConfiguration
let isIndoor = (configuration.locationType == .indoor) as NSNumber
print("locationType: \(configuration)")
let workout = HKWorkout(activityType: configuration.activityType,
start: workoutStartDate ?? Date(),
end: workoutEndDate ?? Date(),
workoutEvents: workoutEvents,
totalEnergyBurned: totalEnergyBurned,
totalDistance: totalDistance,
metadata: [HKMetadataKeyIndoorWorkout:isIndoor]);
healthStore.save(workout) { success, _ in
if success {
self.addSamples(toWorkout: workout)
}
}
// Pass the workout to Summary Interface Controller
// WKInterfaceController.reloadRootControllers(withNames: ["StopPauseInterfaceController"], contexts: [workout])
if #available(watchOSApplicationExtension 4.0, *){
// Create the route, save it, and associate it with the provided workout.
routeBuilder?.finishRoute(with: workout, metadata: metadata) { (newRoute, error) in
guard newRoute != nil else {
// Handle the error here...
return
}
}
}
else {
// fallback to earlier versions
}
}
Then i added samples to those workouts using following code :-
func addSamples(toWorkout workout: HKWorkout) {
// Create energy and distance samples
let totalEnergyBurnedSample = HKQuantitySample(type: HKQuantityType.activeEnergyBurned(),
quantity: totalEnergyBurned,
start: workoutStartDate!,
end: workoutEndDate ?? Date())
let totalDistanceSample = HKQuantitySample(type: HKQuantityType.distanceWalkingRunning(),
quantity: totalDistance,
start: workoutStartDate!,
end: workoutEndDate ?? Date())
// Add samples to workout
healthStore.add([totalEnergyBurnedSample, totalDistanceSample], to: workout) { (success: Bool, error: Error?) in
if success {
// Samples have been added
}
}
}
IPHONE PART :-
This is where things get worse. I try to access the healthkit store using HKSampleObjectQuery and It just returns static values. P.S - I am fetching healthkit store every one second for latest data using NSTimer.
The code which is called every second is :-
func extractDataFromHealthStore(identifier : HKQuantityTypeIdentifier,completion: #escaping (String) -> ()) {
let heightType = HKSampleType.quantityType(forIdentifier:identifier)!
let distantPastDate = Date.distantPast
let endDate = Date()
let predicate = HKQuery.predicateForSamples(withStart: distantPastDate, end: endDate, options: .strictStartDate)
// Get the single most recent Value
let sortDescriptor = NSSortDescriptor(key: HKSampleSortIdentifierStartDate, ascending: true)
let query = HKSampleQuery(sampleType: heightType, predicate: predicate, limit: 1, sortDescriptors: [sortDescriptor]) { (query, results, error) in
if let result = results?.first as? HKQuantitySample{
print("Height => \(result.quantity)")
completion("\(result.quantity)")
}else{
print("OOPS didnt get height \nResults => \(String(describing: results)), error => \(error)")
completion("")
}
}
self.healthStore.execute(query)
}
Please guide Me if my whole approach is wrong, or if it is right then what is the mistake at my end.

Update child values in Firebase with Swift

I am trying to implement updating Firebase database once a user taps on a button. When the user logs in (with Facebook in my case), a data structure is created successfully with the initial values in the data tree created.
What I want to accomplish is once the user is in the app and they create a new item it saves it to the database, hence updates the values under one of the already created child values. See my code and screenshot for reference - thanks for any help!
// user taps button to send item to be updated in Firebase data tree
func confirmAddPlace() {
// add place to tableview array
let accessToken = FBSDKAccessToken.current()
guard let accessTokenString = accessToken?.tokenString else { return }
let credentials = FIRFacebookAuthProvider.credential(withAccessToken: accessTokenString)
FIRAuth.auth()?.signIn(with: credentials, completion: { (user, error) in
if error != nil {
print("Something went wrong with our FB user: ", error ?? "")
return
}
guard let uid = user?.uid else {
return
}
// here is where i am having issues
let ref = FIRDatabase.database().reference().root.child("Users").child(uid).child("Places")
let values = ["place": self.placeNameLabel.text]
ref.updateChildValues(values)
})
animateOut()
}
func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
let placeID = place.placeID
placesClient.lookUpPlaceID(placeID, callback: { (place, error) -> Void in
if let error = error {
print("lookup place id query error: \(error.localizedDescription)")
return
}
guard let place = place else {
return
}
})
let selectedPlace = place.formattedAddress
if let name = selectedPlace as String!
{
self.placeNameLabel.text = "Are you sure you would like to add \(name) to your places?"
}
}
You want to change the value of Places, which is the value in the child of child(uid).
let values = ["place": self.placeNameLabel.text]
let ref = FIRDatabase.database().reference().root.child("users").child(uid).updateChildValues(["Places": values])
user3708224 - You could try this:
let values = ["place": self.placeNameLabel.text]
// create a child reference that uses a date as the key
let date = Date()
let ref = FIRDatabase.database().reference().root.child("users").child(uid).child(date).updateChildValues(["Places": values])
If you want more control of what components are in the date object try this:
let calendar = Calendar.current
let year = calendar.component(.year, from: date)
// you can do the same with [.month, .day, .hour, .minute, and more]
// This will allow you to have control of how frequently they can update the DB
// And it will allow you to sort by date
If you want to upload it to Firebase as a String try this:
/**
This function returns the Date as a String
- "Year-Month-Day"
If the character ' / ' is used in place of ' - ' Firebase will make each component child of the previous component.
*/
func getDate() -> String {
let date = Date()
let calendar = Calendar.current
// hours + min: -\(calendar.component(.hour, from: date))-\(calendar.component(.minute, from: date))
return "\(calendar.component(.year, from: date))-\(calendar.component(.month, from: date))-\(calendar.component(.day, from: date))"
}
let values = ["place": self.placeNameLabel.text]
let ref = FIRDatabase.database().reference().root.child("users").child(uid).child(getDate()).updateChildValues(["Places": values])

Is it possible to read Apple Watch goals (move, step and stand) from HealthKit?

Is it possible to read Apple Watch move goal from HealthKit?
I can retrieve the Move value by using the Quantity Identifier HKQuantityTypeIdentifier.activeEnergyBurned. I could not find a similar identifier for the move goal.
//Declared globally
var healthStore: HKHealthStore?
func prepareHealthKit() {
guard HKHealthStore.isHealthDataAvailable() else {
return
}
var readTypes = Set<HKObjectType>()
readTypes.insert(HKObjectType.activitySummaryType())
healthStore = HKHealthStore()
healthStore!.requestAuthorization(toShare: nil, read: readTypes) { (isSuccess, error) in
/*
Assuming you know the following steps:
1. Start workout session: i.e. "HKWorkoutSession"
2. Wait for delegate: i.e "workoutSession(_:didChangeTo:from:date:)"
3. Start Query for Activity Summary in the delegate:
i.e our "startQueryForActivitySummary()"
*/
}
}
func startQueryForActivitySummary() {
func createPredicate() -> NSPredicate? {
let calendar = Calendar.autoupdatingCurrent
var dateComponents = calendar.dateComponents([.year, .month, .day],
from: Date())
dateComponents.calendar = calendar
let predicate = HKQuery.predicateForActivitySummary(with: dateComponents)
return predicate
}
let queryPredicate = createPredicate()
let query = HKActivitySummaryQuery(predicate: queryPredicate) { (query, summaries, error) -> Void in
if let summaries = summaries {
for summary in summaries {
let activeEnergyBurned = summary.activeEnergyBurned.doubleValue(for: HKUnit.kilocalorie())
let activeEnergyBurnedGoal = summary.activeEnergyBurnedGoal.doubleValue(for: HKUnit.kilocalorie())
let activeEnergyBurnGoalPercent = round(activeEnergyBurned/activeEnergyBurnedGoal)
print(activeEnergyBurnGoalPercent)
}
}
}
healthStore?.execute(query)
}
References:
https://crunchybagel.com/accessing-activity-rings-data-from-healthkit
https://developer.apple.com/documentation/healthkit/hkactivitysummary
https://developer.apple.com/documentation/healthkit/hkactivitysummaryquery
I got the answer. The move goal is accessible from HKActivitySummary.
You should request permission to read HKActivitySummaryType:
let activitySummaryType = HKActivitySummaryType.activitySummaryType()
let readDataTypes: Set<HKObjectType> = [activitySummaryType]
healthStore.requestAuthorization(toShare: nil, read: readDataTypes, completion: myCompletionHandler)
Then use HKActivitySummaryQuery to read the summary information
let query = HKActivitySummaryQuery(predicate: myPredicate) { (query, summaries, error) -> Void in
if error != nil {
fatalError("*** Did not return a valid error object. ***")
}
if let activitySummaries = summaries {
for summary in activitySummaries {
print(summary.activeEnergyBurnedGoal)
//do something with the summary here...
}
}
}
healthStore.execute(query)
Other activity summary data that is accessible from HKActivitySummary is available here.

WatchOS2 Event Complication, how to step to next event at endTime of prior event? (getTimelineEntriesForComplication)

As title says, at end of a watch Event complication, ModularLarge, as Apple's Calendar complication does, say Event is 7-7:10pm, then next event is 8pm. So at Time Travel, or even on watch face itself live, when Event end Time occurs, I do not step/display the next event(I have event in the array note). Think I am just missing something. Note: I am a bit new at this. Here is the code I have.
func getTimelineEntriesForComplication(complication: CLKComplication, afterDate date: NSDate, limit: Int, withHandler handler: (([CLKComplicationTimelineEntry]?) -> Void)) {
// Call the handler with the timeline entries after to the given date
var timeLineEntryArray = [CLKComplicationTimelineEntry]()
if allEvents.count > 0 {
for (index, title) in allEvents.enumerate() {
let item = allEvents[index]
let startDate = item.startDate
let endDate = item.endDate
let title = item.title
//let entry = createTimeLineEntry2(timeString, body1Text: timeLineText[index], body2Text: timeUntilArray[index], date: nextDate)
let entry = createTimeLineEntry2(timeString, body1Text: title, body2Text: timeUntil, startDate: startDate)
timeLineEntryArray.append(entry)
} //end for loop...
} //end If allEvents.count > 0 we have events
handler(timeLineEntryArray)
}
so at watch time, and TimeTravel of 7:11 should display the 8pm Event, and then I desire to delta time to shot time until that event. so "-49 minutes" with this code that works:
func createTimeLineEntry2(headerText: String, body1Text: String, body2Text: String, startDate: NSDate) -> CLKComplicationTimelineEntry {
template.body2TextProvider = CLKRelativeDateTextProvider(date: startDate,
style: .Offset,
units: NSCalendarUnit.Hour.union(.Minute))
template.body2TextProvider!.tintColor = UIColor.yellowColor()
template.tintColor = UIColor.yellowColor()
let entry = CLKComplicationTimelineEntry(date: startDate, complicationTemplate: template)
return(entry)
}
Lastly, can we set the tint(color) of the body2TextProvider?
thank you so much. Mike
here is a link to picture, this event ended at 9:10 am, so watch shodld show the 12 pm event now! http://pics.derr.ws/watch.png

Resources