Advertise

Thursday, March 16, 2017

PART 1: iOS Create custom calendar and manage Events with EventKit in Swift



Introduction:


Reminder, Alarms and Event manager, these are very well known and useful tools for mobile generation. You don't have to remember events or your friend's birthday etc. As a developer, idea may come up to your mind to use and access those events in your application. Well, fortunately apple has given permission to access system calendar to developer via EventKit. EventKit is not so well known framework but it is a middleware between system event database and your application. Your application can access all events and calendars via this framework only.

With help of EventKit framework you can create, update or delete calendar, events and reminders. Before access the system Calendar database, keep in mind you must grant access to Calendar or reminder app. Moreover you need to declare privacy of calendar usage in your project's Info.plist file. User will decide to give calendar access to your app.

In this post, we are not going to do each and every steps for integrating the calendar into app. Rather than that I am going to give you feature code for grant access to use calendar, Creating calendar etc. We can see how to manage Events in next post.

I assume you have knowledge about how to create project and basics of swift:

Ask user to grant Access Calendar


Below code shows you how ask request access to user and know the current authorisation status:

import UIKit
import EventKit

class AskPermissionController: UIViewController {
    let eventStore = EKEventStore()
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    override func viewWillAppear(_ animated: Bool) {
        checkCalendarAuthorizationStatus()
    }
    func checkCalendarAuthorizationStatus() {
        let status = EKEventStore.authorizationStatus(for: EKEntityType.event)
        switch (status) {
        case EKAuthorizationStatus.notDetermined:
            // This happens on first-run
            requestAccessToCalendar()
        case EKAuthorizationStatus.authorized:
            // Yo!! You got a access to use Calendar now go on and create/load all calendar list.
            //loadCalendars()
            //refreshTableView()
            break
        case EKAuthorizationStatus.restricted, EKAuthorizationStatus.denied:
            // We need to create another view which helps user to give us permission
            break
        }
    }
    func requestAccessToCalendar() {
        eventStore.requestAccess(to: EKEntityType.event, completion: {
            (accessGranted: Bool, error: Error?) in
            if accessGranted == true {
                DispatchQueue.main.async(execute: {
                    // Yo!! You got a access to use Calendar now go on and create/load all calendar list.
                    //loadCalendars()
                    //refreshTableView()
                })
            } else {
                DispatchQueue.main.async(execute: {
                    // We need to create another view which helps user to give us permission
                })
            }
        })
    }
}

Add below line to Info.plist:





IMPORTANT: If you forget to declare in plist file, your app will be crashed so don't forget to add above line. It is necessary to give user in knowledge that your app will use their personal calendar data. 

Load All Calendars names into TableView


I assume you have knowledge about TableView in Swift, if not go to this link : Table View in Swift

1. Import EventKit on the top of your class and declare calendar variable as below

var calendars: [EKCalendar]?



2. Create method for load all calendars into given calendars array and call this method to viewDidLoad method:

 func loadCalendars() {
        self.calendars = eventStore.calendars(for: EKEntityType.event)
        //If you want to sort Calendars by name, use below line
//        self.calendars = EKEventStore().calendars(for: EKEntityType.event).sorted() { (cal1, cal2) -> Bool in
//            return cal1.title < cal2.title
//        }
    }


3. Update cellForRowAt method of TableView as below:


func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if let calendars = self.calendars {
            return calendars.count
        }
        return 0
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
        if let calendars = self.calendars {
            let calendarName = calendars[(indexPath as NSIndexPath).row].title
            print("Calendar Name : \(calendarName)")
            cell.textLabel?.text = calendarName
        } else {
            print("Unknown Calendar Name")
            cell.textLabel?.text = "Unknown Calendar Name"
        }
        return cell
    } 

DONE ! Above code will display list of all Calendar names. Next Step is to create your own Calendar through your application.

Create own calendar

Simple few line of code will create a new calendar as below: If you want to set user defined calendar name then,
1. Create another class named AddCalendarController.swift. 
2. Add UITextField and declare it as a newCalendarField into swift file
3. on Save button, paste below code.

@IBAction func saveNewCalendarAction(_ sender: UIBarButtonItem) {
        // Create an Event Store instance
        let eventStore = EKEventStore();
        
        // Use Event Store to create a new calendar instance
        // Configure its title
        let newCalendar = EKCalendar(for: .event, eventStore: eventStore)
        
        // Probably want to prevent someone from saving a calendar
        // if they don't type in a name...
        newCalendar.title = newCalendarField.text ?? "Some Calendar Name"
        
        // Access list of available sources from the Event Store
        let sourcesInEventStore = eventStore.sources
        
        // Filter the available sources and select the "Local" source to assign to the new calendar's
        // source property
        newCalendar.source = sourcesInEventStore.filter{
            (source: EKSource) -> Bool in
            source.sourceType.rawValue == EKSourceType.local.rawValue
            }.first!
        
        // Save the calendar using the Event Store instance
        do {
            try eventStore.saveCalendar(newCalendar, commit: true)
            UserDefaults.standard.set(newCalendar.calendarIdentifier, forKey: "EventTrackerPrimaryCalendar")
            self.navigationController?.popViewController(animated: true)
            //self.dismiss(animated: true, completion: nil)
        } catch {
            let alert = UIAlertController(title: "Calendar could not save", message: (error as NSError).localizedDescription, preferredStyle: .alert)
            let OKAction = UIAlertAction(title: "OK", style: .default, handler: nil)
            alert.addAction(OKAction)
            self.navigationController?.popViewController(animated: true)
            //self.present(alert, animated: true, completion: nil)
        }
    }

NOTE: Do not forget to import EventKit in the swift file

Create, manage, update, delete events using EventKit. Go To PART 2: Manage Events using EventKit Framework in Swift


If you want to download full source code, click below link. Demo code has a feature of:
  • Ask permission and grant access to use Calendar
  • Create Calendar and get a list of all Calendars
  • Create,Update,Delete,Get Events


Download Full Source Code




PART 2: Manage Events using EventKit Framework in Swift





Before you manage events if you missed how to create custom calendar then take a look PART 1

What we will do in this post?


We are going to add custom event to calendar, get all list of events, update event details and delete event. In this post you will get all concept of event management in calendar using EventKit framework.

Create New Event


If you want user to add event start time, end time and name then create a new swift controller class and add following components 1 UITextField, 2 DatePicker. If you want to know all source code with features then download the source code from link given at the end of the post. Here, I am going to give you simple code which creates new event in calendar.

Here is a code,

 func addEvent(){

        
        // Create an Event Store instance
        let eventStore = EKEventStore()
        
        // Use Event Store to create a new calendar instance
        if let calendarForEvent = eventStore.calendar(withIdentifier: self.calendar.calendarIdentifier)
        {
            var newEvent = EKEvent(eventStore: eventStore)
            newEvent.calendar = calendarForEvent
            newEvent.title = self.eventNmField.text ?? "Some Event Name"
            newEvent.startDate = self.startDatePicker.date
            newEvent.endDate = self.endDatePicker.date
            newEvent.addAlarm(.init(relativeOffset: 0))
            // Save the event using the Event Store instance
            do {
                try eventStore.save(newEvent, span: .thisEvent, commit: true)                
                self.navigationController?.popViewController(animated: true)
            } catch {
                let alert = UIAlertController(title: "Event could not save", message: (error as NSError).localizedDescription, preferredStyle: .alert)
                let OKAction = UIAlertAction(title: "OK", style: .default, handler: nil)
                alert.addAction(OKAction)
                
                self.present(alert, animated: true, completion: nil)
            }
        }
    }

Note that, You need a Calendar identifier in which you are going to add event. For that you can transfer Calendar object from one controller to another OR you can save into UserDefaults and get it when required to add event. Here, assume that self.calendar is a calendar object in which I have created a event. Title, startDate and endDate fields are user defined, you can give it static for demo purpose.

Update event


func updateEvent(){
        let eventStore = EKEventStore()
        eventStore.requestAccess(to: .event, completion: {
        granted, error in
            if(!granted){
                print("Access to store not granted")
                print(error?.localizedDescription)
            }else{
                let savedEvent = eventStore.event(withIdentifier: self.event4Update.eventIdentifier)
                if(savedEvent != nil){
                    savedEvent?.title = self.eventNmField.text ?? "Some Event Name"
                    savedEvent?.addAlarm(.init(relativeOffset: 0))
                    savedEvent?.startDate = self.startDatePicker.date
                    savedEvent?.endDate = self.endDatePicker.date
                    do{
                        try eventStore.save(savedEvent!, span: .thisEvent)
                        DispatchQueue.main.async {
                            self.navigationController?.popViewController(animated: true)
                        }
                    }catch{
                        print("Event couldnot update : \(error.localizedDescription)")
                    }
                }else{
                    print("Event not available")
                }
            }
        })
    }

Above code updates selected event, for that you must have a event object which you want to update. Above code is clearly mentioned that we have created a new object with the event4Update identifier, set new parameters and then we commit to the event.


Delete event


It is very simple to delete/remove event from calendar. You just need to call one method to remove event from eventStore. Here is the code to remove event, to delete you just pass that event to this method.

    func deleteEntry(event : EKEvent){
        do{
            try eventStore.remove(event, span: EKSpan.thisEvent, commit: true)
            self.loadEvents()
            self.tableView.reloadData()
        }catch{
            print("Error while deleting event: \(error.localizedDescription)")
        }
    }

Get list of all events



As we saw in Part 1, we get all calendar in a array and place it into TableView. Same way we are going to get all events, store it into array and display in tableView. So, We start step by step.

1. Import EventKit on header and declare objects as below:

    var events: [EKEvent]?
    var eventStore : EKEventStore!

2. Create method to load events of selected calendar for specific time period and call this method in viewDidLoad.

 func loadEvents() {
        // Create a date formatter instance to use for converting a string to a date
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd"
        
        // Create start and end date NSDate instances to build a predicate for which events to select
        let startDate = dateFormatter.date(from: "2017-01-01")
        let endDate = dateFormatter.date(from: "2017-12-31")
        
        if let startDate = startDate, let endDate = endDate {
            eventStore = EKEventStore()
            
            // Use an event store instance to create and properly configure an NSPredicate
            let eventsPredicate = eventStore.predicateForEvents(withStart: startDate, end: endDate, calendars: [calendar])
            
            // Use the configured NSPredicate to find and return events in the store that match
            self.events = eventStore.events(matching: eventsPredicate).sorted(){
                (e1: EKEvent, e2: EKEvent) -> Bool in
                return e1.startDate.compare(e2.startDate) == ComparisonResult.orderedAscending
            }
        }
    }

3. Update TableView Methods as given below:

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if let events = events {
            return events.count
        }
        return 0
    }
    
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "Cell")!
        cell.textLabel?.text = events?[(indexPath as NSIndexPath).row].title
        cell.detailTextLabel?.text = "\(formatDate(events?[(indexPath as NSIndexPath).row].startDate)) TO \(formatDate(events?[(indexPath as NSIndexPath).row].endDate))"
        return cell
    }

Done! It will load all events of specific calendar to tableview. You can download full source code from given link. It has following features:
  • Ask permission and grant access to use Calendar
  • Create Calendar and get a list of all Calendars
  • Create,Update,Delete,Get Events

Download Full Source Code


Wednesday, March 8, 2017

iOS: Use CocoaPods in your swift project - pod file installation

If you are a iOS developer and haven't use pod file / CocoaPod yet, you can understand the pain how you need to integrate frameworks manually. Yes, in this post I am talking about dependency management. All developers know very well that stand alone project requires some 3rd Party framework for animations, monetisation, analysis, or to create app more attractive and eye catching. Earlier, you need to add all dependency manually with drag and drop framework files into your project, this work is very boring, time taking and sometimes get integration error as well. To overcome this issues CocoaPod come into picture. So, If you are unaware about CocoaPod and dependency management tool then this small post will help you to understand and trust me after reading this post you will stop using manual process and start using it.


What is CocoaPods?

It is a dependency manager for Objective C and Swift projects that manages all framework into your project and integrate all required frameworks automatically. So, instead of import file with drag and drop CocoaPods take care everything.

Let's take an example, If you are developing a security application and you want to integrate cryptography(encryption / decryption) then what will you do? You will simply google it, find some useful framework / repository which fulfil your goal and drag and drop those files into your project.. right? We will integrate same repository/framework with CocoaPods in this post. I have found a good Cryptography repository CryptoSwift which we are going to import in our project.


How to install CocoaPods in my mac?

First, you can check whether your system already has CocoaPods or not. For that, open terminal and enter below command into root directory.

pod --help

If you get response like below image then your system already has installed CocoaPods but if not, you need to install it only for once.



To install, enter below command:

sudo gem install cocoapods

It may prompt to enter password, enter your system password and hit enter. It will take some time to download and display below message in your terminal screen.




Done ! Now you can use CocoaPods into your project. Isn't it easy like cup of tea?


Use CocoaPod into Main Project

Once CocoaPod installed into system, create test project and name it CocoaPodTest. Close the xCode and open terminal again. Go to your project directory path. For example I have created project on desktop. Just hit below command in terminal:

cd ~/desktop/CocoaPodTest

It is because pod file which we are going to create is Project specific and we need to create it into our project root directory. Above command will redirect terminal to CocoaPodTest directory. So, now whatever command will fire in terminal will apply to this directory only. I hope it will clear to you.

We need to create podfile in project root directory. It will track all pods which you want to install. When you ask CocoaPod to install/update podfile this file will execute. To do so type below commands step by step.

1. To create podfile:

pod init

2. Open Podfile:

open Podfile

IMPORTANT: Podfile is now generate with below lines of code: (You can open it into text editor)

# Uncomment the next line to define a global platform for your project
# platform :ios, '9.0'

target 'CocoaPodTest' do
  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
  use_frameworks!

  # Pods for CocoaPodTest

  target 'CocoaPodTestTests' do
    inherit! :search_paths
    # Pods for testing
  end

  target 'CocoaPodTestUITests' do
    inherit! :search_paths
    # Pods for testing
  end

end

3. As described above, we want to install CryptoSwift in our project. You will find it's pod name from repository link. Enter pod 'CryptoSwift' just below use_frameworks! line. Save the file and close the editor.

4. Type below command to install CryptoSwift pod:

pod install

Done ! Your terminal will show as shown below:


And your Podfile looks like,



Now, you have noticed that CocoaPodTest.xcworkspace file is created automatically into your project root directory. Now, on words you have to open this file instead of using CocoaPodTest.xcodeporj. It has installed given pods into project and ready to use. No bridge file is required or no more drag & drops to integrate framework.

You can import it with simply adding import CryptoSwift code in swift file. It is as simple as I told you initially !!!


Add another Pod into existed Podfile

Most of the cases we need to integrate Podfile/ 3rd party frameworks as per our requirement and it is not predefined before project creates. On the middle of the project if you want to add another pod then it very simple. If you have xCode open with the same project,

1. Just click Podfile and open it.
2. Add pod name as described above Step No 3 and save the file.
3. Open Terminal and go to project root directory and type below command:

pod update

4. This command will install missing pods. It will not reinstall pods which are already exist into project.

Xcode Project CocoaPodTest.xcworkspace will looks like:






This way you can update project podfile, I hope this post will clear all concept of CocoaPods and you will start using it.... Happy coding... :)



Monday, March 6, 2017

iOS: Keyboard hides TextField Solution in Swift 3.0

Problem:




Solution:




In iOS, it is a headache that you need to adjust each and every component  manually as per visibility. Most of the cases developer confused when he designed form and textfield is added at the bottom of the screen. In this case, System keyboard hides the textfield and user cannot see what he is entering.

To overcome this issue developer has to move up textfield while editing and also set it its default place when editing is done. If more textfield is added in single screen then this work gonna headache for every developer. 

Here, is the simple code to overcome this issue. You just need to add code for below steps:

1. IBOutlet textfield into the swift file (reference textfield in swift file)
2. add UITextFieldDelegate methods
3. add code for View move up and move down code

I have added complete code to over come the issue and will give you a exact solution as shown above.


ViewController.swift

class ViewController: UIViewController,UITextFieldDelegate {

    @IBOutlet weak var nameField: UITextField!
    override func viewDidLoad() {
        super.viewDidLoad()
        nameField.delegate = self
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    func textFieldDidBeginEditing(_ textField: UITextField) {
            animateViewMoving(up: true, moveValue: 250)
    }
    func textFieldDidEndEditing(_ textField: UITextField) {
            animateViewMoving(up: false, moveValue: 250)
    }
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        nameField.resignFirstResponder()
        return true
    }
    
    func animateViewMoving (up:Bool, moveValue :CGFloat){
        let movementDuration: TimeInterval = 0.3
        let movement:CGFloat = ( up ? -moveValue : moveValue)
        UIView.beginAnimations( "animateView", context: nil)
        UIView.setAnimationBeginsFromCurrentState(true)
        UIView.setAnimationDuration(movementDuration )
        self.view.frame = self.view.frame.offsetBy(dx: 0,  dy: movement)
        UIView.commitAnimations()
    }
}

When user click "return" button from keyboard, textFieldShouldReturn method is called. So, it is recommended to resign keyboard thats why we have added this method.

Download Full Source Code