• Skip to main content
  • Skip to primary sidebar

Ravi Shankar

Tweaking Apps

  • About
  • Portfolio
  • Privacy Policy

Xcode

iPhone is not available error message in Xcode

September 8, 2020 By Ravi Shankar

If you are receiving an error “iPhone is not available” when connecting Xcode with iPhone then check iOS version on your iPhone. Make sure it is compatible with the Xcode version.

iPhone is not available

The conflict in versions can be found under the Xcode – Window – Devices and Simulators. In my case the following error was mentioned.

Errors

“The current device configuration is unsupported. This iPhone 7 (Model 1660, 1778, 1779, 1780) is running iOS 13.6.1 (17G80), which is not supported by Xcode 11.7”

The fix was to upgrade iOS on my iPhone to iOS 13.7.

Filed Under: Xcode Tagged With: iphone

Tab Bar Controller with WebView

April 9, 2020 By Ravi Shankar

In this article, we will see step by step instruction on working of Tab Bar Controller, UIWebView and Activity Indicator by loading couple of web pages using UIWebView in two different Tabs.

Tab Bar Controller

Tab Bar Controller is used for organising list of View Controllers by having seperate tabs for each View Controller. Check out the Apple Documentation for more information.

UIWebView

When you want to allow users to browse website then you can use UIWebView to load those pages. These WebViews needs to be placed inside a container such as UIViewController. Check out Apple Documentation for more information on UIWebView.

Project Setup

Create a new project by selecting Single View Application as project template. You can also choose Tabbed Application but in this demo we will see how to embed a ViewController inside a Tab Bar Controller.

Enter the project details and select the language as Swift.

Embed View Controller inside Tab Bar Controller

Navigate to Main.storyboard in Project Navigator and select View Controller. Now to embed this View Controller inside a Tab bar Controller, click Editor -> Embed In -> Tab Bar Controller

The Interface builder should now display View Controller and Tab Bar Controller as shown below.

Add Second View Controller

Now let us see how to add another tab by setting relationship between second View Controller and Tab Bar Controller. Drag and drop UIViewController from Object library to Interface builder.

Control + Drag from Tab Bar Controller to UIViewController and select view controllers under Relationship Segue. This should add second tab bar item pointing the second View Controller.

Add UIWebView and Activity Indicator View

We are not using Auto Layout or Size Classes for this demo, so disable these features using the option available under File Inspector.

Drag and drop UIWebView from object libary to both the View Controllers. Make sure it is aligned centrally to View Controller and covers the entire View Controller area. Similarly add Activity Indicator View from object libary to top of UIWebView and align to the Centre both vertically and horizontally.

Edit Tab Bar Items

The Tab Bar Controller display two tab bar items which are linked to each View Controller. Add required images for each Tab bar items to the Images.xcassets (Download the images from GitHub project).
To provide a name and image for Tab Bar items, select the item in the corresponding View Controller and enter the details in Attributes Inspector. For the first View Controller, the name and image are set as shown below.

repeat the same for the second View Controller as well.

Link class with UIViewController

The project comes with a default ViewController.swift file which is mapped with the first View Controller. Let us give a proper name to this controller. Unfortnately Xcode Refactor option still does not work for Swift (Xcode version 6.4). Manually rename the file to WorldViewController.swift and also make the corresponding name change in Class.

class WorldController: UIViewController {

Add a second view controller class by right clicking the project, select New File option.

In the choose template screen, select Cocoa Touch Class and provide name as SportsViewController making it a subclass of UIViewController.

Create IBOutelts for UIWebView and Activity Indicator View

Using Assistant Editor, navigate to Main.storyboard in the second window. In the Identity Inspector, set the class name for both ViewControllers to WorldViewController and SportsViewController.

Drag and drop UIWebView from WorldViewController (Interface builder) to WorldViewController.swift to create IBOutlet. Do the same for Activity Indicator View as well.

@IBOutlet weak var webView: UIWebView!

@IBOutlet weak var activityIndicator: UIActivityIndicatorView!

Repeat the same exercise for SportsViewController and create corresponding IBOutlets.

Code Implementation

Navigate to WorldViewController.swift file and add the following code snippet in ViewDidLoad function.

let url = NSURL(string: "http://edition.cnn.com/world")
let urlRequest = NSURLRequest(URL: url!)
webView.loadRequest(urlRequest)

activityIndicator.startAnimating()
activityIndicator.hidesWhenStopped = true

The first three lines of the code create a NSURLRequest and the object is passed to UIWebView loadRequest function. The last two lines starts the Activity Indicator View and sets the property to hide the indicator when it is stopped animating.

We need to tell the Activity Indicator when the page has finished loading. This can be done by implementing UIWebViewDelegate’s webViewDidFinishLoad function.

Add the following piece of code to make the View Controller as a delegate for webView.

class WorldController: UIViewController, UIWebViewDelegate {

and in the viewDidLoad function add webView.delegate = self. This would ensure that the View Contorller can handle any WebView delegate related calls.

Now implement the webViewDidFinishLoad function and stop the animation of Activity Indicator View when the webview has finished loading the page.

//MARK:- WebView Delegate method

    func webViewDidFinishLoad(webView: UIWebView) {
        activityIndicator.stopAnimating()
    }

Repeat the above code implementation for SportsViewController excepy by chaning the URL string.

class SportsViewController: UIViewController, UIWebViewDelegate {
    @IBOutlet var webView: UIWebView!
    @IBOutlet var activityIndicator: UIActivityIndicatorView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let url = NSURL(string: "http://edition.cnn.com/sport")
        let urlRequest = NSURLRequest(URL: url!)
        webView.loadRequest(urlRequest)

        activityIndicator.startAnimating()
        activityIndicator.hidesWhenStopped = true

        webView.delegate = self
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    //MARK:- UIWebView Delegate methods
    func webViewDidFinishLoad(webView: UIWebView) {
        activityIndicator.stopAnimating()
    }

}

When you finally build and run the project, you should see the follwing on iOS simulator.

Download the source code from here

Filed Under: Apple, Programming, Xcode

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, iPhone, Programming, Xcode Tagged With: CoreData, delegation, NSFetchedResultContoller, Swift, Tutorials, UITableView, User Interface, Xcode

Storyboard, Scene and Segue

November 22, 2019 By Ravi Shankar Leave a Comment

Storyboard in iOS is helps you to design the user interface of your App. A storyboard can contain one or more Scenes (View Controllers) and the connection or relationship between two scenes are known as Segue. This is how a typical storyboard with Scenes and Segue look.

The above example storyboard contains two scenes, first and second. These scenes are connected to each other by a Show Segue. Now let us see more about the Storyboard and Segues

Initial ViewController

App needs to know the ViewControler that will be displayed as the initial screen. You can set any controller as the initial scene using the option available as part of the Attributes Inspector

 

The storyboard will display an arrow in front of the Initial View Controller as shown in below screenshot. You can also drag and drop this arrow to the change the Initial View Controller.

Types of Segues

A segue is a relationship set between two view controllers. This can be done by pressing Control then drag and drop from one View Controller to anlther. Listed below are the different types of segues

  • Show 
  • Show Detail
  • Present Modally
  • PopOver Presentation
  • Custom

Check out this excellent Stackoverflow answer on types of Segues

Unwind Segues

Only the Show segue has a back button in the navigation bar of the child controller which takes you back to the Parent controller. For rest of the segues, we can use unwind segues to dismiss the child controller. Unwind segue can be created by first adding a IBAction with parameter type as UIStoryboardSegue to the parent Vuew Controller.

   //MARK:- Unwind Segue
    @IBAction func cancel(segue:UIStoryboardSegue {
        // do nothing
    }

Add a button to the child view controller press Control then drag and drop the button to the Exit button available at the top of the ViewController. Now select the Action Segue which was added to the parent view controller.

Transfering data between ViewControllers

One of the common question that gets asked is “how to transfer data between two view controllers“. Create a property in the child view controller and set the value for this property in the prepareForSegue function of the parent view controller.

//MARK:- ViewController Transition

     override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if  segue.identifier  ==   "detail"   {
            let  childViewController  =  segue.destination  as! SecondViewController
            childViewController.studentName  =  nameTextField.text
        }
    }

In the above prepareForSegue function, we check whether segue identifier is equal to detail. The identifier name can be provided under the Storyboard Segue in Attributes Inspector. Then we set the value for studentName property of the SecondViewController. By this way, you can transfer data between the View Controllers,

In the SecondViewController, you should be able access this property value and perform the required action. The below viewDidLoad code snippet access the property value and displays it in a label.

class SecondViewController: UIViewController {

    var studentName:String?
    @IBOutlet var displayName:UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        if let name = studentName {
            displayName.text = name
        }
    }
}

Non Segue Transition

Another alternate way to transition between the View Controllers is to use instantiateViewController withIdentifier function of storyboard

//MARK:- Non Segue
  @IBAction func nonSegueCall(sender: AnyObject) {
        let childViewContoller  =
            storyboard?.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
        childViewContoller . studentName  =  nameTextField.text
        present( childViewContoller ,  animated: true ,  completion: nil)
    }

The above code snippet provides the same behaviour as prepareForSegue function. This function can be called on tap of a button and this requires an identifier to specified for the View Controller.

Download sample code from here.

Filed Under: Xcode Tagged With: prepareForSegue, Scene, Segue, Storyboard

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, Programming, Xcode Tagged With: Apple, CoreLocation, Swift, Xcode

Swift – WebView demo

November 6, 2019 By Ravi Shankar 24 Comments

Updated for Swift 5

In this short tutorial, we will see an example in Swift programming language using UIWebView. The WebView will load a webpage and provide option to refresh, stop, go back and go forward. This tutorial should give a brief overview on how to declare IBAction, IBOutlets and use Objective-C classes (NSURL and NSURLRequest) in Swift


Interface Design

Step 1: Create a Single View Application Project and make sure to select the programming language as Swift

Choose Swift Language in Xcode

Step 2: Select Main.Storyboard under Project navigator. then drag and drop WebView and Toolbar from Object Library to the View Controller.

Step 3: Now place four bar button items on Toolbar to provide stop, refresh, go back and go forward functionality. You can also use flexible and fixed separators for aligning the bar button items.

Use Suggested Constraints in Xcode 6

Step 4: Make sure to use the SuggestedConstraints for WebView and Toolbar. You can do this by selecting the controls and using the Reset to Suggested Constraints available as part of Resolve Auto Layout option. This would ensure that the controls gets adjusted automatically depending upon the device’s screen width and height.

Reset to Suggested Constraints

Updated – 28/08/2014

Since some users are facing problems with Reset to Suggested Constraints in Xcode 6 Beta 6, you can use Pin option to define the four constraints using the default values. Then click Add 4 Constraints available at the bottom of the screen.

201408281213.jpg

Write Code

Step 5: Navigate to ViewController.swift file on the Project Navigator. Add the following line after class ViewController: UIViewController which defines the IBOutlet element for WebView.

@IBOutlet var webView: UIWebView!

Then connect this IBOutlet element to the WebView on InterfaceBuilder.

Step 6: In the viewDidLoad function, create URL and NSURLRequest for the webpage and associate this with the WebView by calling loadRequest method.

override func viewDidLoad() 
{   
  super.viewDidLoad() 
  let url = NSURL(string: "https://rshankar.com")
  let request = NSURLRequest(url: url! as URL)       
  webView.delegate = self
  activityIndicator.hidesWhenStopped = true
  activityIndicator.startAnimating()
  webView.loadRequest(request as URLRequest) 
}

Step 7: Now add the corresponding IBAction methods for the four buttons stop, refresh, go back and go forward. And connect these IBActions to the buttons on Interface builder.

func webViewDidFinishLoad(_ webView: UIWebView) 
{ 
      activityIndicator.stopAnimating()   
}                                                                                           

@IBAction func doRefresh(_: AnyObject) {                                                              
  webView.reload()
}

@IBAction func goBack(_: AnyObject) 
{   
   webView.goBack()    
}

@IBAction func goForward(_: AnyObject) {
  webView.goForward()
}

@IBAction func stop(_: AnyObject) 
{
   webView.stopLoading()
}

Step 8: Compile and run the project by selecting a suitable simulator.

Download the souce code from GitHub.

Filed Under: ios, Programming, Xcode Tagged With: Swift, WebView, 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, Xcode Tagged With: Assertions, Xcode, XCTest

Content Priority in Auto Layout

March 4, 2016 By Ravi Shankar 1 Comment

Auto Layout brings in lots of good features to ease the life of an iOS developer when designing User Interfaces. In this example, we will see how to use Content Priorities such as Content Hugging and Comrpession Resistance.

I have created a project with Single View Template and dispayed Size Classes as we are going to focus only on iPhone family.

Drag and drop two labels adjacent to each other.

Add Leading Space and Vertical Space constraints for Label A. Similarly add Vertical Space and Trailing Space constraints for Label B

 

Constraints for Label A

Constraints for Label B

Click the Update Frames option under the Resolve AutoLayout Issues menu. This should align the frames to reflect the constraints changes.

Change the background colour of Labels to Yellow and Green respectively. This would help us to identity the growth of each labels. Now add the Horizontal Spacing constraint between these two labels and set constant to 5.

Now Label B has grown and covered the space between both labels. Please note that this would be in random nature some times even Label A can fill up the space between these labels. And in this article, we will see how to control this behaviour. You will also notice the warning message that “2 views are horizontally ambitguous”. This is because Auto Layout does not know which label should grow and shrink in size.

Content Compression Resistance Priority

Let us change the text for Label in left hand side as “Content Hugging”. Simialrly enter the text for right hand side label as “Compression Resistance”. Then Update Frames to refelect the Intrinsic Content Size changes. You will notice that the Compression Resistance has a truncated trail.

If you want to this label to resist shrinking its size then set the Content Compression Resistance Priority (Under Size Inspector) to value higher than the label on left hand side.

After changing the value from 750 to 751, you will notice the warning messages “Frame will be different at run time”. Now Updating the Frames for All Views in Container shoud reflect as shown in the below screenshot.

Content Hugging Priority

Now Content Hugging label is partially hidden and it cannot grow horizontally as right hand side label has a higher compression resistance priority. Let us set the number of lines for this lable to 0 (which means it can grow based on the content size). This should again result in “Content Priority Ambiguity” error. This time we can fix this by telling Auto Layout that the label on left hand side has least resistance for growth. Select the label on left hand side, navigate to Size Inspector and set the Content Hugging Priority to lower than right hand side label i.e from 251 to 250.

Click Update Frames to resolve the warning message “Frame for Content Hugging will be different at run time”. The Content Hugging label in shown in two lines.

If you need any assistance in Auto Layout, check out our new iOS 9 Auto Layout Tutorials 50% Off.

Filed Under: Auto Layout, Xcode Tagged With: Compression Resistance, Content Hugging

  • Go to page 1
  • Go to page 2
  • Go to page 3
  • Interim pages omitted …
  • Go to page 7
  • Go to Next Page »

Primary Sidebar

Recent Posts

  • We have blocked all requests from this device – Firebase Phone Authentication
  • iPhone is not available error message in Xcode
  • Clear CocoaPods cache, re-download and reinstall all pods
  • PDFKit – View, Annotate PDF file in Swift
  • Tab Bar Controller with WebView

Archives

  • September 2020
  • April 2020
  • December 2019
  • November 2019
  • October 2019
  • February 2019
  • October 2017
  • June 2017
  • May 2017
  • March 2017
  • September 2016
  • March 2016
  • February 2016
  • January 2016
  • December 2015
  • November 2015
  • October 2015
  • September 2015
  • August 2015
  • July 2015
  • June 2015
  • May 2015
  • April 2015
  • March 2015
  • February 2015
  • January 2015
  • December 2014
  • November 2014
  • October 2014
  • September 2014
  • August 2014
  • July 2014
  • June 2014
  • April 2014
  • March 2014
  • February 2014
  • January 2014
  • December 2013
  • October 2013
  • August 2013
  • July 2013
  • June 2013
  • April 2013
  • March 2013
  • February 2013
  • January 2013
  • November 2012
  • August 2012
  • July 2012
  • June 2012
  • May 2012
  • March 2012
  • February 2012
  • January 2012
  • December 2011
  • October 2011
  • September 2011
  • August 2011
  • July 2011
  • June 2011
  • April 2011
  • March 2011
  • January 2011
  • September 2010
  • August 2010
  • July 2010
  • June 2010
  • July 2009
  • March 2008

Copyright 2020 © rshankar.com