• Skip to main content
  • Skip to primary sidebar

Ravi Shankar

Tweaking Apps

  • Swift
  • Tech Tips

Develop

CoreData tutorial in Swift 5 using NSFetchedResultsController

December 25, 2019 By Ravi Shankar 38 Comments

Let us see an example TaskManager app using CoreData written in Swift Programming language. This app provides the following functionality

  • Allow users to enter new task
  • Update existing task
  • delete task

TaskManager using CoreData201407121030.jpg

You can download source code for this project from Github. If you are familiar with user interface then move on to the Core Data implementation in Swift section

Create a new File -> New -> Project and select template Single View Application

201407121032.jpg

Enter Product Name, Language as Swift and select “Use Core Data” for the new Project.

201407121037.jpg

User Interface Implementation

 

Add new TableViewController for managing tasks

 

Delete ViewController.swift and Add new view controller which will be used for displaying the list of tasks.

201407121059.jpg

Right click on the Project and select New File

201407121101.jpg

Choose the template as Cocoa Touch under iOS -> Source

201407121103.jpg

Enter name of the file as TaskManagerViewController with Subclass as UITableViewController and Language as Swift.

201407121104.jpg

Add new UITableViewController to the Storyboard

 

Navigate to Main.storyboard, delete the ViewController and add new TableViewController to the Storyboard

201407121108.jpg

Embed this TableViewController inside a navigation controller. You can do this by clicking Editor menu -> Embed In -> Navigation Controller.

201407121110.jpg

Navigate to Storyboard file and select Table View Controller. Click Identity Inspector and set the Class to the TaskManagerViewController.

Add button to the navigation bar

 

Users can navigate to new task screen by tapping the button on the TaskManager. Drag and drop the button bar item to the navigation bar.

201407121116.jpg

In the attributes inspector, set the identifier for button bar item as Add. Also enter title as “Task Manager” in the navigation bar.

Add View Controller for entering task detail

 

Now to enter task detail, let us add new View Controller. From the Objects library, drag and drop View Controller on to storyboard. Then drag and drop a navigation item to this View Controller. Add a Done bar button item for saving the changes, Cancel bar button item for dismissing the screen. Also provide a title for the screen as Task Detail.

201407121138.jpg

Then add a textfield to the View Controller to enter details about the task.

201407121540.jpg

Now to connect the Task Manager screen to Task Detail screen, select the Add button in Task Manager screen, hold the control button and make a connection to the Task Detail screen. Select type of Action Segue as Show.

201407121125.jpg201407121127.jpg

Select each View Controller and Click on the Resolve Auto Layout Issues option and pick Reset to Suggested Constraints. This would ensure that the controls alignment and display remains same in different screen sizes.

 

201407121557.jpg

Add View Controller Class

Right click on the Project, select New File from menu list.

201407130919.jpg

Select Cocoa Touch Class as template.

201407130920.jpg

Enter the Class name as TaskDetailViewController, Subclass of UIViewController and Language as Swift.

201407130921.jpg

Navigate to Storyboard file and select Task Detail View Controller. Click Identity Inspector and set the Class to the TaskDetailViiewController.

201407141329.jpg

Now let us add the function required for dismissing the View Controller. This gets called when user taps done and cancel buttons. Navigate to TaskDetailViewController.swift file and add the following functions.

    
   @IBAction func done(sender: AnyObject) {
        if task != nil {
            editTask()
        } else {
            createTask()
        }
        dismissViewController()
    }
    
    @IBAction func cancel(sender: AnyObject) {
         dismissViewController()
    }
    
    // MARK:- Dismiss ViewControllers
    
    func dismissViewController() {
        navigationController?.popViewController(animated: true)
    }

 

Connect the IBActions with the corresponding done and cancel buttons

201407130930.jpg

 

Try to compile and run the app on the Simulator. You should see the following Task Manager screen and tapping + sign should display the Task Detail screen with TextField, Cancel and Done button.

201407130933.jpg201407130935.jpg

 

When you tap the Cancel or Done button should take you to the Task Manager screen.

Create IBOutlet element for UITextField Element

Add the following piece of code after the Class TaskDetailViewController: UIViewController

@IBOutlet var txtDesc: UITextField!

Now use the connection inspector to connect the IBOutlet variable to the textfield on the User Interface.

Core Data Implementation

 

Click Show Project Navigator and select CoreData model file (ending with extension .xcdatamodelid)

201407121042.jpg

Click Add Entity option available at the bottom of Xcode Editor window.

201407121045.jpg

Then enter the name of the entity as Tasks.

201407121047.jpg

Click Add Attribute then enter name as desc and choose the type as String. This attribute will be used for storing the task detail information.

201407121049.jpg

Now to generate the CoreData mapping class, click Editor and select Create NSManagedObject Subclass.

201407121051.jpg

Select the data models and click Next

201407121052.jpg

Then select entities and in this example it is Tasks

201407121053.jpg

Make sure to select Language for NSManagedObject class as Swift. Click Create to the create new class.

201407121055.jpg

Click Yes to to configure an Objective-C birding header for the Swift class.

201407121056.jpg

This should create a new class called Tasks.swfit with variable for corresponding attribute defined in Entities

Write code to save new task details

Add the following line at the top of TaskDetailViewController class

  let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).managedObjectContext

The above line defines a variable to store the ManagedObjectContext. In order to save the task information entered in the UITextField, add the following code to TaskDetailViewController.swift. Then call this function in the @IBAction done.

   func createTask() {
        let entityDescripition = NSEntityDescription.entity(forEntityName: "Tasks", in: managedObjectContext)
        let task = Tasks(entity: entityDescripition!, insertInto: managedObjectContext)
        task.desc = txtDesc.text!
        do {
            try managedObjectContext.save()
        } catch _ {
        }
    }
@IBAction func done(sender: AnyObject) {
	createTask()
	dismissViewController()
}

 

The createTask function uses the Tasks Entity class and ManagedObjectContext class to save the information entered in the text field in to SQLite using CoreData.

Now if you try to run this app in Xcode simulator, you should be able to enter the details in textfield and save the task. But there is no way to see the saved tasks.

Write code to retrieve information from SQLite using NSFetchedResultsController

We will be using NSFetchedResultsController to retrieve and manage information returned from Core Data. Write the following code after the class declaration of TaskManagerViewController class

 let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).managedObjectContext
    
var fetchedResultController: NSFetchedResultsController = NSFetchedResultsController<NSFetchRequestResult>()
    

201407141923.jpg

 

You might see the above error message “Use of undeclared type ’NSFetchedResultsContoller”. This can be fixed by importing CoreData module and making your class a delegate of NSFetchedResultsControllerDelegate.

import CoreData

class TaskManagerViewController: UITableViewController, NSFetchedResultsControllerDelegate {

Then add the following function to populate fetchedResultController.

  
    func getFetchedResultController() -> NSFetchedResultsController<NSFetchRequestResult> {
        fetchedResultController = NSFetchedResultsController(fetchRequest: taskFetchRequest(),  managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
        return fetchedResultController
    }
      

 

Update the ViewDidLoad method of TableViewController to populate fetchedResultController variable and set the delegate to self and call the function to retrieve the results.

override func viewDidLoad() {
        super.viewDidLoad()

       fetchedResultController = getFetchedResultController()
        fetchedResultController.delegate = self
        do {
            try fetchedResultController.performFetch()
        } catch _ {
        }
    }

Implement the TableView related functions to display the data

Navigate to Main.storyoard then to TaskManagerViewController and set Identifier for Prototype Cells as “Cell”

201407142004.jpg

 

Add the following functions required for population of rows in tableView and to refresh the tableView when content is changed.

 

And for refreshing the tableview content add the following piece of code snippet.

  func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {          tableView.reloadData()
    }

Swift uses namespaces for classes, so the downcast of NSManagedObject class to Tasks class will work only when you append the module name with the class for Entity

  let task = fetchedResultController.object(at: indexPath as IndexPath) as! Tasks

Navigate to TaskManager.xcdatamodeld, select Tasks under Entities and using the Data Model Inspector append Module name i.e TaskManager with the class.

201407142038.jpg

 

 

Now you should be able to see the data added via TaskDetailScreen in the TaskManager Screen.

201407142059.jpg

Implementation of deleting rows from tableView and CoreData entity

 

Copy and paste the following code to provide the users with the option to delete the rows.

 override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        let managedObject:NSManagedObject = fetchedResultController.object(at: indexPath as IndexPath) as! NSManagedObject
        managedObjectContext.delete(managedObject)
        do {
            try managedObjectContext.save()
        } catch _ {
        }
    }

201407142116.jpg

Implementation of editing task details

Now let us see how to edit a task in the TaskManager screen. We will re-use the same TaskDetail screen for editing the task information. First let create segue from UITableViewCell to TaskDetailViewController. You can do this by holding the Control key and drag and drop the connection to TaskDetailViewController.

201407142127.jpg

 

Make sure to set the Segue identifier as edit using Attributes Inspector

201407142128.jpg

 

Add the following piece of code in TaskManagerViewController to populate the task detail for the edited row.

      override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "edit" {
            let cell = sender as! UITableViewCell
            let indexPath = tableView.indexPath(for: cell)
            let taskController:TaskDetailViewController = segue.destination as! TaskDetailViewController
            let task:Tasks = fetchedResultController.object(at: indexPath!) as! Tasks
            taskController.task = task
        }
    }
    

 

Navigate to TaskDetailViewController and variable which used to pass the task details across the ViewControllers

var task: Task? = nil

In viewDidLoad fund, populate the textField with the task details for edit operation i.e task is not nil

 override func viewDidLoad() {
        super.viewDidLoad()
     if task != nil {
            txtDesc.text = task?.desc
        }
    }

 

Then add a new function to save the edited task. Also modify the done function to handle create and edit task functionality

func editTask() {
       task?.desc = txtDesc.text!
        do {
            try managedObjectContext.save()
        } catch _ {
        }
    }
@IBAction func done(sender: AnyObject) {
        if task != nil {
            editTask()
        } else {
            createTask()
        }
        dismissViewController()
    }

 

Now when you run the app in Xcode simulator, you should be able to edit the task.

The final piece of code left is to add background colour to the navigation bar for both the controllers. Click AppDelegate.swift file and add the following function. Then call this function from the didFinishLaunchingWithOptions.

    func setupAppearance() {
        let navigationBarAppearance = UINavigationBar.appearance()
        navigationBarAppearance.barTintColor = UIColor(red: 51.0/255.0, green: 104.0/255.0, blue: 121.0/255.0, alpha: 1.0)
        navigationBarAppearance.tintColor = UIColor.white
        navigationBarAppearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor:UIColor.white]
    }

 

Running the app on the iOS simulator should change the appearance for the navigation bar.

201407142233.jpg

 

Hope you found this beginner tutorial useful. Please use the comments section to share your thoughts and suggestion

Download the source code for this tutorial from GitHub

Filed Under: Develop, ios, iPhone, Programming, Swift, Xcode Tagged With: CoreData, delegation, NSFetchedResultContoller, Swift, Tutorials, UITableView, User Interface, Xcode

Get your current address in Swift (Swift 5)

November 7, 2019 By Ravi Shankar 22 Comments

In this Swift tutorial, we will see the steps required for using CoreLocation framework and retrieve the latitude and longitude of the location. Then use the CLGeocoder to reverse geocode the latitude and longitude details. This is a very basic tutorial that retrieves the location details on tap of a button and displays the information on the screen.

Updated :- The source has now been to updated to show the address details in TextFields.

Click File -> New and select Project sub menu list.

201407251656.jpg

In the Choose a template screen, select Single View Application and click Next button.

201407251657.jpg

Enter the product name as WhereAmI and select the language as Swift then click Next. Select a folder and create the new project and that folder.

201407251658.jpg

Navigate to ViewController.swift file and add a new function with name as findMyLocation after viewDidLoad function.

@IBAction func findMyLocation(sender: AnyObject) {

}

Navigate to Main.storyboard file, drag and drop Button from the Object Library on to the View Controller. Using the attributes inspector, change the name of the button to “Where Am I?”. If you need then change the background and text colour for the button. Then centre align the button both vertically and horizontally. Use the Resolve Auto Layout Option and pick Reset to Suggested Constraints.

201407251707.jpg

Navigate to Connections Inspector and connect the button to the findMyLocation action under Received Actions (Touch Up Inside event).

201407251711.jpg

Click the Project under Project Navigator and navigate to Build Phases. Click the + sign under Link Binary With Libraries and pick CoreLocation.framework from the list.

Add CoreLocation framework to Swift project201407251718.jpg

CoreLocation in Swift

Now navigate back to ViewController.swift file, add import CoreLocation after UIKit. Then we need to assign the current class as the delegate for CLLocationManagerDelegate. This is required because we will be using couple of delegate methods from CLLocationManager.

class ViewController: UIViewController, CLLocationManagerDelegate {

Define a constant for CLLocationManager after the class declaration.

let locationManager = CLLocationManager()

Navigate to IBAction function findMyLocation and add the following code

locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()

The above lines sets the class as delegate for locationManager, specifies the location accuracy and starts receiving location updates from CoreLocation. In order to get the location updates we need to implement the delegate functions of CLLocationManager, didUpdateLocations and didFailWithError

func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
}

func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
}

didUpdateLocations function is triggered when new location updates are available. Similarly didFailWithError is called when there is a problem receiving the location updates. Let us first start implementing the simpler one i.e didFailWithError function. When there is an error, we are going to log the message to the console.

func locationManager(manager: CLLocationManager!, didFailWithError error: NSError!) {
	println("Error while updating location " + error.localizedDescription)
}

Then update didUpdateLocations function with the following code, also add a new function for printing the location details.

func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
CLGeocoder().reverseGeocodeLocation(manager.location, completionHandler: {(placemarks, error)->Void in
		if error {
			println("Reverse geocoder failed with error" + error.localizedDescription)
			return
		}

		if placemarks.count > 0 {
			let pm = placemarks[0] as CLPlacemark
			self.displayLocationInfo(pm)
		} else {
			println("Problem with the data received from geocoder")
		}
	})
}

func displayLocationInfo(placemark: CLPlacemark) {
	if placemark != nil {
		//stop updating location to save battery life
		locationManager.stopUpdatingLocation()
		println(placemark.locality ? placemark.locality : "")
		println(placemark.postalCode ? placemark.postalCode : "")
		println(placemark.administrativeArea ? placemark.administrativeArea : "")
		println(placemark.country ? placemark.country : "")
	}
}

in the didUpdateLocations function, we pass the location co-ordinates to

 CLGeocoder().reverseGeocodeLocation

. Then check for error and process the location array (placemarks). Then for displaying the location details, we pass the placemark detail to displayLocationInfo function.

Add key to Info.plist for request permission to User’s location

In order to use the user’s location you need to request permission from the user by adding the keys “NSLocationWhenInUseUsageDescription” or “NSLocationAlwaysUsageDescription” to your Info.plist file, with the value blank or optionally a message included in the prompt to the user. One of those two key is required in iOS 8 to ask for your location.

Testing Location in Simulator

It is not possible to test current location with simulator. So we need to define a custom location or use the location already defined for debug purpose. When an app tries to access the location services on a simulator it displays the following popup.

Current Location Simulator Xcode 6

Here is a workaround to test the app using Xcode 11. Run the app on the a Simulator and tap “Find My Location” button. Nothing will happen as the app has not been given permission to use Location Services on Simulator.

201407251956.jpg

Click Debug menu then Location and select Apple from the sub menu list. Still nothing happens with the app and no messages are written to the console.

201407251957.jpg

Now click Hardware menu and select Home from menu list.

201407251959.jpg

Tap the Settings icon on Simulator -> Privacy -> Location -> Tap , select Always to allow the app to access the location.

201407252003.jpg201407252005.jpg

Now you should see Apple address details in the Console Output.

201407252008.jpg

You can also try out other location using the Simulate Location option.

Simulate location in Xcode 6 simulator

Hopefully the testing should be lot easier after issue with “Allow to use Current Location” is fixed.

Source from GitHub

Filed Under: Apple, Develop, ios, Programming, Xcode Tagged With: Apple, CoreLocation, Swift, Xcode

Assertions supported in XCTest

March 23, 2017 By Ravi Shankar Leave a Comment

Here you can find the list of Assertions supported by XCTest and it is essential to know all these assertion if you are practicing Test Driven Development in IOS. You can get this list from XCTestAssertions.h

  • XCTFail(<#format…#>) – This unconditionally fails the test.
  • XCTAssertNil(<#a1#>, <#format…#>) – Failure message when object is not nil.
  • XCTAssertNotNil(<#a1#>, <#format…#>) – Failure message when object is nil
  • XCTAssertEqual(<#a1#>, <#a2#>, <#format…#>) – Failure message when expressions(a1 & a2) are not equal.
  • XCTAssertNotEqual(<#a1#>, <#a2#>, <#format…#>) – Failure message when expressions(a1 & a2) are equal.
  • XCTAssertEqualObjects(<#a1#>, <#a2#>, <#format…#>) – Failure message when objects(a1 & a2) are not equal.
  • XCTAssertNotEqualObjects(<#a1#>, <#a2#>, <#format…#>) – Failure message when objects(a1 & a2) are not equal.
  • XCTAssertEqualWithAccuracy(<#a1#>, <#a2#>, <#accuracy#>, <#format…#>) – Failure message when a1 is not equal to a2 with + or – accuracy.
  • XCTAssertNotEqualWithAccuracy(<#a1#>, <#a2#>, <#accuracy#>, <#format…#>) – Failure message when a1 is equal to a2 with + or – accuracy.
  • XCTAssertNoThrow(<#expression#>, <#format…#>) – Failure message when expression does throw exception.
  • XCTAssertNoThrowSpecific(<#expression#>, <#specificException#>, <#format…#>) – Failure message when expression throws specific exception.
  • XCTAssertNoThrowSpecificNamed(<#expression#>, <#specificException#>, <#exception_name#>, <#format…#>) – Failure message when expression throws specific class with specific name.
  • XCTAssertThrows(<#expression#>, <#format…#>) – Failure message when expression does not throw exception.
  • XCTAssertThrowsSpecific(<#expression#>, <#specificException#>, <#format…#>) – Failure message when expression does not throw specific exception.
  • XCTAssert(<#expression#>, <#format…#>) – Failure message when expression is false.
  • XCTAssertTrue(<#expression#>, <#format…#>) – Failure message when expression is false.
  • XCTAssertFailure(<#expression#>, <#format…#>) – Failure message when expression is true.

Filed Under: Develop, ios, Swift, Xcode Tagged With: Assertions, Xcode, XCTest

Closures, Extensions and Generics in Swift

May 22, 2015 By Ravi Shankar Leave a Comment

Closures

Closures are self contained lines of code that can be passed around the application and similar to blocks in Objective-C. A typical closure syntax in Swift looks as shown below

Closure Syntax

[code language=”swift”]{ (parameters) -> return type in
statements
}[/code]

Example closure in Swift

[code language=”swift”]{ (name:String, message:String -> (String) in
message + ” ” + name + ” !!!”
}
greetings(“Ravi”,”Welcome”)
[/code]

 

In the above code example, a closure has been assigned to a variable. The purpose of this closure is to concatenate the string parameters and return the appended message as return parameter.

Type Inference

The example closure can modified to ignore to the parameter types and closure supports type inference.

[code language=”swift”]var greetings = { (name, message) -> (String) in
return message + ” ” + name + ” !!!”
}

greetings(“Ravi”,”Welcome”)[/code]

Implicit return

In single expression closure, you can omit the return keyword.

[code language=”swift”]var numbers = [23,45,67,89,89,78]
numbers.sort { (number1, number2) -> Bool in
return number1 < number2
}

// numbers.sort { number1, number2 in return number1 < number2 }
numbers.sort { number1, number2 in number1 < number2 } // Shorthand argument syntax
numbers.sort { $0 < swift }[/code]

Shorthand Argument Syntax

Swift supports shorthand argument names for inline closures. In the above example used for implicit returns, the two parameters can be removed and represented in shorthand arguments as shown below.

[code language=”swift”]numbers.sort { $0 < swift }
[/code]

Trailing Closure

In a function with closure as the last parameter, the closure can be treated as trailing closures i.e closures outside the function parenthesis call. This is quite helpful in reducing the long closure expression. For example, the sorted function has closure as the last parameter and with trailing closure this becomes as shown below.

[code language=”swift”]var numbers = [23,45,67,89,89,78]
var sortedNumbers = sorted(numbers, {$0 > swift}) // Without trailing closure
// var sortedNumbers = sorted(numbers) {$0 > swift} // represented as trailing closure

sortedNumbers[/code]

Extensions

Swift extensions are similar to category in Objective-C which adds new functionally to existing class, enumeration or Struct. Extension does not require the source code of original class or enumeration type or struct to extend their functionality.

Listed below is an example which extends String class. A new function fromDouble has been added to String class which takes a double value and returns String.

[code language=”swift”] extension String {
static func fromDouble(doubleValue: Double) -> String {
var temp = String(format: “%.2f”, doubleValue)
return temp as String
}
}
String.fromDouble(24.50)[/code]

Generics

Generics are code that produces the same result irrespective of the data type. Listed below is a function that accepts an array of type string and reverses the array items.

[code language=”swift”]let strTemp = [“Deepak”,”John”,”Steve”,”Ravi”,”Ganesh”]

// reverse array with String
func reverseString(items: Array) -> Array {
var temp = Array()
return items.reverse()
}
reverseString(strTemp)[/code]

Now the below function accepts array of number and reverses the items

[code language=”swift”] let numbers = [23,45,56,78,98]
// reverse array with numbers
func reverseNumber(items: Array) -> Array {
return items.reverse()
}
reverseNumber(numbers)[/code]

Generics solves the problem of having different set of code for different data types by implemting the functionality for a generic type.

[code language=”swift”]let strTemp = [“Deepak”,”John”,”Steve”,”Ravi”,”Ganesh”]
let numbers = [23,45,56,78,98]

func reverseItems(items:[T])-> [T] {
return items.reverse()
}
reverseItems(strTemp)
reverseItems(numbers)[/code]

You can also implement a Generic class with this function as shown below.

[code language=”swift”]let strTemp = [“Deepak”,”John”,”Steve”,”Ravi”,”Ganesh”]

class ReverseDemo {
func reverseItems(items:[P])-> [P] {
return items.reverse()
}
}
let reverseDemo = ReverseDemo()
reverseDemo.reverseItems(strTemp)
[/code]

Swift Operations with Generics

Let us say you want to create a generic function that returns the square value of the given number. The immediate solution that comes to your mind is

[code language=”swift”]func squareOfNumber(number:M -> M{
return number * number
}
[/code]

This is not as straight forward as we think, you would notice an error – “Binary operator ‘*’ cannot be applied to two M operands”. The generic data type does not recogonize the operator ‘*’. We can fix this by creating a new protocol that tells about this operator ‘*’ and Generic type should conform to this protocol as shown below.

[code language=”swift”]protocol Multipliable {
func *(lhs:Self, rhs: Self) -> Self
}

func squareOfNumber(number:M -> M{
return number * number
}
[/code]

Then add an extension to Int, Float and other required types and make them conform to Multipliable protocol.

[code language=”swift”]extension Int: Multipliable {}
extension Float: Multipliable {}

squareOfNumber(20)[/code]

Download source code from gitHub (Generic)

Filed Under: Apple, Develop, ios, Programming Tagged With: Closures, Extensions

Tuples, Enums and Protocols in Swift

May 14, 2015 By Ravi Shankar Leave a Comment

Tuples in Swift allows user to assign group of values to a variable, constant and even return parameter of a function. In the below example, a employee constant is assigned Int and String values. And to access these parameters, we need to use .0 and .1

[code language=”swift”]let employee = (103, “Deepak”)
employee.0
employee.1[/code]

Now let us say you want to assign proper name for these parameters so that you could access these values using those names instead of 0 and 1.

[code language=”swift”]let employee = (id:103, name:“Deepak”)
employee.id
employee.name[/code]

Here id and name are parameter names provided for employee id and employee name. You can also declare the data types for the tuple values like Int and String

[code language=”swift”]let employee:(id:Int, name:String = (102, “Deepak”)
employee.id
employee.name[/code]

Tuples and switch cases are powerful combination, look at an example below where Tuple has been used with switch cases. The _ is used for matching any values.

[code language=”swift”]let employee:(id:Int, name:String) = (102, “Deepak”)
switch (employee) {
case (103…105,_):
println(“developer”)
case (106…108,_):
println(“tester”)
case (_,“Deepak”):
println(“CEO”)
default:
println(“Contractor”)
}[/code]

Enums

Enum in Swift allows users to group related values to a single data type. Swift enum has lot of new features compared to its predecessor Objective-C. Let us see this with an example enum type for all Months in a year.

[code language=”swift”]enum Months {
case January, February, March, April, May, June, July, August, September, October, November, December
}

enum Month {
case January, February, March, April, May, June, July, August, September, October, November, December
}[/code]

Now you can define a variable or a constant of type month as shown below.

[code language=”swift”]let currentMonth = Month.May[/code]

And variable or constant is declared with the data type then you can use the short form.

[code language=”swift”]let currentMonth:Month = .May[/code]

Enum with Raw Value

Enum in Swift can be assigned a RawValue when declaring and the value can be of any data type. Let us day you want to specify the String value of each Month

[code language=”swift”]enum Month: String {
case January = “January”, February = “February”, March = “March”, April = “April”, May = “May”, June = “June”, July = “July”, August = “August”, September = “September”, October = “October”, November = “November”, December = “December”
}[/code]

The RawValue can be printed by accessing .rawValue on the enum variable or constant.

[code language=”swift”]let currentMonth:Month = .May
currentMonth.rawValue[/code]

Enum with Associated Value

Same like RawValue, enum can also have associated value and of data type.

[code language=”swift”]enum Month {
case January(String), February(String), March(String), April(String), May(String), June(String), July(String), August(String), September(String), October(String), November(String), December(String)
}

let currentMonth:Month = .May(“Summer Vacation”)

switch currentMonth {
case .May(let message):
println(message)
default:
println(“No Values”)
}[/code]

In the above example, Month enum values are declared with an associated value of data type String. And while assign the enum value the associated value is also provided to the currentMonth constant. Using the switch the associated value is retrieved.

Enum can have member function

Enum in Swift can also have member function. In the below example code, we have declared the Month enum with rawValue of type Int. Now add a member function to calculate the number of months left from the currently assigned value.

[code language=”swift”]enum Month: Int {
case January = 1, February, March, April, May, June, July, August, September, October, November, December func monthsLeftForYearEnd() -&gt; Int {
return Month.December.rawValue – self.rawValue
}
}

let month: Month = .May
month.monthsLeftForYearEnd()[/code]

Constant month is assigned a enum value of .May and you can find out the number of months left for the year end by accessing the monthsLeftForYearEnd.

Enum can have Initialiser

Swift’s enum can have initialiser too where you can set the default enum value or do some initial processing. If you add init function as shown below, the default enum value will be set to July.

[code language=”swift”]enum Month: Int {
case January = 1, February, March, April, May, June, July, August, September, October, November, December
init() {
self = .July
}

func monthsLeftForYearEnd() -&gt; Int {
return Month.December.rawValue – self.rawValue
}
}

let month = Month()
month.monthsLeftForYearEnd()[/code]

Protocols

Protocol in Swift defines the contract for a class or struct. This is similar to the interface in other languages like java. Protocol only defines the member function or variables and the actual implementation should be done in the conforming class or struct. Protocol can be be useful in the following ways

  • To add common behaviour across related or unrelated classes.
  • Supports in the implementation of delegation pattern

Let us see a typical example of protocol in Swift where we defining a Protocol called LivingThings with one method eat.

[code language=”swift”]protocol LivingThings {
func eat() -&gt; String
}[/code]

Any class or struct conforming to this Protocol should add implementation to eat method. In the below code example, we have got two classes Animal and Human which conform to LivingThings. implementation for the eat method has been added to both the classes.

[code language=”swift”]class Animal: LivingThings {
func eat() -&gt; String {
return “Animal Food"
}
}

class Human: LivingThings {
func eat() -&gt; String {
return "Human Food"
}
}

let john = Human()
john.eat()

let cat = Animal()
cat.eat()[/code]

If a class conforming to a Protocol does not implement the required methods then you will find error message “error: type ‘Human’ does not conform to protocol ‘LivingThings’”

Protocol with Optional Methods

In Swift, Protocol with optional method can be defined by specifying the optional before the method definition and adding @objc keyword before protocol. Listed below is an example with Speakable protocol that has an optional method speak

[code language=”swift”]
protocol LivingThings {
func eat() -&gt; String
}

@objc protocol Speakable {
optional func speak() -&gt; String
}

class Human: LivingThings, Speakable {
func eat() -&gt; String {
return "Human Food"
}

func speak() -&gt; String {
return "Human can Speak"
}
}

let john = Human()
john.eat()
john.speak()[/code]

Protocol and Delegation Pattern

A delegate pattern is used when you want a class to act on behalf of another class or for a callback mechanism. In the below code example we have defined a protocol (ImportDataDelegate) with two methods startImport and finishedImport. The DataImport class conforms to this Protocol and adds implementation to these methods. The DemoImport class acts on behalf of DataImport class by declaring a variable delegate of type ImportDataDelegate.

[code language=”swift”]protocol ImportDataDelegate {
func startImport()
func finishedImport()
}

class DataImport: ImportDataDelegate {
func startImport() {
println("Import started")
}

func finishedImport() {
println("Import finished")
}
}

class DemoImport {
var delegate: ImportDataDelegate?

func startProcess() {
delegate?.startImport()
println("Doing some work …")
delegate?.finishedImport()
}
}[/code]

This is how you pass the DataImport class to delegate variable of the DemoImport.

[code language=”swift”]let demoImport = DemoImport()
demoImport.delegate = DataImport()
demoImport.startProcess()[/code]

Filed Under: Apple, Develop, ios, Programming Tagged With: Apple, delegate pattern, Optional Methods, Protocol

How to display line numbers in Xcode

April 30, 2015 By Ravi Shankar Leave a Comment

Listed below are the steps to display line number in Xcode editor window. This is quite useful when you are working in a ground and want to communicate the line number of statement to other members.

Click Xcode menu option and select Preferences from the menu list

201504301024.jpg

In the Preferences window, click Text Editing tab.

201504301026.jpg

Then mark the check box with caption as Show Line numbers. Now you should be able to see the line numbers in Xcode editor window.

201504301027.jpg

Filed Under: Develop, ios, Xcode Tagged With: Line numbers, Xcode

How to record and play sound in Swift

April 12, 2015 By Ravi Shankar 10 Comments

In this tutorial, we are going to see the required steps to record and play sound in Swift Programming language using AVAudioRecorder and AVAudioPlayer

Download source code from github (SoundController.swift)

User Interface

The user interface for this demo is simple with two buttons, one for recording and another for playing sound.

201504121626.jpg

Create corresponding IBAction and IBOutlets from the two button in the View Controller.

@IBOutlet weak var recordButton: UIButton!

@IBOutlet weak var playButton: UIButton!

@IBAction func recordSound(sender: UIButton)

@IBAction func playSound(sender: UIButton)

Dual Purpose Buttons

The Record button will be used to record and stop recording. Similarly the Play button will used to Play and Stop the audio.

Class level var and let declaration

  var soundRecorder: AVAudioRecorder!

var soundPlayer:AVAudioPlayer!

  

let fileName = “demo.caf”

Declare two class level variable for holding an instance of AVAudioRecorder and AvAudioPlayer. Also create constant for holding the file name.

Create Sound file path

For this demo, the sound file will be created under the Cache directory using the name defined in the constant. Add these two function to your class

  func getCacheDirectory() -> String {

  

let paths = NSSearchPathForDirectoriesInDomains(.CachesDirectory,.UserDomainMask, true) as [String]

  

return paths[0]

}

  

func getFileURL() -> NSURL {

  

let path = getCacheDirectory().stringByAppendingPathComponent(fileName)

let filePath = NSURL(fileURLWithPath: path)

  

return filePath!

}

The getCacheDirectory retrieves the caches directory and getFileURL appends the file name with the cache directory.

Setup Recorder

Create a new function that prepares the recorder and this function will be called during the viewDidLoad operation

func setupRecorder() {

  

//set the settings for recorder

  

var recordSettings = [

AVFormatIDKey: kAudioFormatAppleLossless,

AVEncoderAudioQualityKey : AVAudioQuality.Max.rawValue,

AVEncoderBitRateKey : 320000,

AVNumberOfChannelsKey: 2,

AVSampleRateKey : 44100.0

]

  

var error: NSError?

  

soundRecorder = AVAudioRecorder(URL: getFileURL(), settings: recordSettings, error: &error)

  

if let err = error {

println(“AVAudioRecorder error: \(err.localizedDescription)“)

} else {

soundRecorder.delegate = self

soundRecorder.prepareToRecord()

}

}

To create an AVAudioRecorder instance, you need to pass three parameters,

URL– Specifies the path and name of the file where the sound will be recorded.

settings – Configure AVRecorder settings

error – Capture error while creating AVRecorder instance.

If there is no error while creating Audio Recorder, the delegate it set to the View Controller and recorder is prepared for recording. Make sure the View Controller conforms to AVAudioRecorderDelegate

class SoundController: UIViewController, AVAudioRecorderDelegate {

Prepare Player

Add the following function that prepares the AVAudiPlayer to play the recorded sound

func preparePlayer() {

var error: NSError?

  

soundPlayer = AVAudioPlayer(contentsOfURL: getFileURL(), error: &error)

  

if let err = error {

println(“AVAudioPlayer error: \(err.localizedDescription)“)

} else {

soundPlayer.delegate = self

soundPlayer.prepareToPlay()

soundPlayer.volume = 1.0

}

}

AVAudioPlayer instance is created by passing sound file that needs to be played along with pointer to capture errors while creating AVAudioPlayer instance. Make sure the View Controller conforms to AVAudioPlayerDelegate. Then call the prepareToPlay function on the AVAudioPlayer instance.

Implement IBActions function to record and play sound

Add the following implementation to the IBAction functions which records and plays sound.

@IBAction func recordSound(sender: UIButton) {

if (sender.titleLabel?.text == “Record”){

soundRecorder.record()

sender.setTitle(“Stop”, forState: .Normal)

playButton.enabled = false

} else {

soundRecorder.stop()

sender.setTitle(“Record”, forState: .Normal)

}

}

  

@IBAction func playSound(sender: UIButton) {

if (sender.titleLabel?.text == “Play”){

recordButton.enabled = false

sender.setTitle(“Stop”, forState: .Normal)

preparePlayer()

soundPlayer.play()

} else {

soundPlayer.stop()

sender.setTitle(“Play”, forState: .Normal)

}

}

When the Record button is tapped, the name of the button is changed to Stop and starts recording the sound. Similarly when the title is Stop, the recording the stopped and play button is enabled. The same is done when the user taps the play button.

class SoundController: UIViewController, AVAudioRecorderDelegate, AVAudioPlayerDelegate {

Implement AVAudioRecorderDelegate methods

  func audioRecorderDidFinishRecording(recorder: AVAudioRecorder!, successfully flag: Bool) {

playButton.enabled = true

recordButton.setTitle(“Record”, forState: .Normal)

}

  

func audioRecorderEncodeErrorDidOccur(recorder: AVAudioRecorder!, error: NSError!) {

println(“Error while recording audio \(error.localizedDescription)“)

}

Implement AVAudioPlayerDelegate methods

  func audioPlayerDidFinishPlaying(player: AVAudioPlayer!, successfully flag: Bool) {

recordButton.enabled = true

playButton.setTitle(“Play”, forState: .Normal)

}

  

func audioPlayerDecodeErrorDidOccur(player: AVAudioPlayer!, error: NSError!) {

println(“Error while playing audio \(error.localizedDescription)“)

}

Now you are good to go and try this demo. You can download the source code for recording and playing audio in swift from github.

Filed Under: Develop, ios, Programming, Xcode Tagged With: AvAudioPlayer, AVAudioRecorder, Xcode

UITextFieldDelegate in Swift

April 7, 2015 By Ravi Shankar Leave a Comment

This is a beginners tutorial on UITextFieldDelegate in Swift. We are going to see how to use UITextFieldDelegate by writing a Simple Interest Calculator.

201504071303.jpg

Download the source code from here

This calculator uses UILabels and TextFields for displaying and accepting amount and interest. We are going to use the UITextFieldDelegate method to navigate from “Principal Amount” to “Rate of Interest”. And when the user taps done on “Rate of Interest” UITextField, the interest is calculated and displayed on the corresponding label.

Source Code

import UIKit

class TextFieldDemoController: UIViewController, UITextFieldDelegate {

@IBOutlet weak var amountTextField: UITextField!

  

@IBOutlet weak var rateTextField: UITextField!

  

@IBOutlet weak var interestLabel: UILabel!

  

var textFields:[UITextField] = []

  

override func viewDidLoad() {

super.viewDidLoad()

  

self.amountTextField.delegate = self

self.rateTextField.delegate = self

  

textFields = [amountTextField, rateTextField]

  

}

override func didReceiveMemoryWarning() {

super.didReceiveMemoryWarning()

// Dispose of any resources that can be recreated.

}

  

//MARK :- TextField Delegate

  

func textFieldShouldReturn(textField: UITextField) -> Bool {

  

var currentTextField = textFields[0]

  

if (currentTextField == textField) {

currentTextField = textFields[1]

currentTextField.becomeFirstResponder()

} else {

currentTextField.resignFirstResponder()

interestLabel.text = “\(calculateInterest())“

}

  

return true

}

  

//MARK :- Calculation

  

func calculateInterest() -> Double {

let amount: Double = (amountTextField.text as NSString).doubleValue

let rate:Double = (rateTextField.text as NSString).doubleValue

  

return amount * rate

}

}

Step 1: Make sure your view controller class conforms to UITextFieldDelegate

Step 2: In ViewDidLoad method, set the delegate for the textfields to self (ViewController)
Step 3: Implement textFieldShouldReturn(textField: UITextField) -> Bool(UITextFieldDelegate method) and the code that makes the UITextField FirstResponder and calculates the Interest once the values are entered.
Download the source code from here

Filed Under: Apple, Develop, ios, Programming, Xcode Tagged With: Apple, textFieldShouldReturn, UITextFieldDelegate, Xcode

  1. Pages:
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. »
Next Page »

Primary Sidebar

TwitterLinkedin

Recent Posts

  • How to block keywords in Jio broadband
  • How to disable opening an app automatically at login in Mac
  • How to set preferred Wifi network on Mac
  • Attribute Unavailable: Estimated section warning before iOS 11.0
  • How to recover Firefox password from Time Machine backup

Pages

  • About
  • Privacy Policy
  • Terms and Conditions

Copyright 2022 © rshankar.com

Terms and Conditions - Privacy Policy