Mixing Objective-C and Swift - Using Components and Subcomponents - Swift For Dummies (2015)

Swift For Dummies (2015)

Part IV. Using Components and Subcomponents

Chapter 19. Mixing Objective-C and Swift

In This Chapter

arrow Comparing frameworks in Objective-C and Swift

arrow Calling an Objective-C method in Swift

As this book is written in the beginning of 2015, the topic of mixing Objective-C and Swift isn’t an if: it’s a now (and, in this chapter, a how). The Cocoa and Cocoa Touch frameworks are written in Objective-C and have been since the first days of NeXTSTEP (and later OpenStep and still later Rhapsody; later still OS X and Cocoa, even later yet iPhone OS, and, most recently, iOS and Cocoa Touch). This is quite a long run for a programming language, but the languages of proven value in supporting operating systems do tend to stick around for a while. (These languages include Objective-C with the versions of our operating system just listed as well as C with UNIX and its offshoots.)

They stick around for several reasons — starting with the fact that they work. People have learned to work with them over the years, and they have proven (both the people and the languages) to be able to handle the problems that are thrown at them.

Additionally, these languages are the building blocks of major operating systems, but they’re also the building blocks of many application programs. When used to build operating systems, languages like C and Objective-C have been able to support application programs written in a wide variety of languages.

The investment in these languages is enormous, and the benefits of using languages such as C and Objective-C are great.

With Swift, we have a new language that promises to make it easier for developers — particularly developers new to iOS and OS X — to write apps for Macs and iOS devices. These apps rely on the Cocoa and Cocoa Touch frameworks as well as C libraries, all of which have evolved over the years to meet changing needs of developers, users, and hardware designers.

When it comes to the libraries in C and the frameworks in Objective-C, there’s little reason to undertake a massive conversion effort to Swift. In both cases (but more so in Objective-C) what happens inside a framework or library shouldn’t matter to the software engineer who wants to use that framework or library: In fact, one of the main advantages of frameworks and software libraries is to generate code that can be used and reused by people who don’t know the specifics of how that code works. These people only need to understand the interfaces to an unknown and maybe unknowable library or framework. Thus, we are looking at a composite world of both Swift and Objective-C.

That said, over the course of time it is likely that the Cocoa and Cocoa Touch frameworks will be converted to Swift. How long this will take isn’t known. In fact, even if there is a schedule taped to the back of a door somewhere in Cupertino (Apple’s headquarters), the transition to Swift will likely take several years and will be modified several times on the way to completion.

In the meantime, the tools we have from Apple and its engineers make it easy to mix and match Objective-C and Swift. Writing Objective-C apps is something that has been done for decades (albeit under the names of various operating systems). Writing a totally Swift app today isn’t possible — if by “Swift” you mean to include the frameworks of Cocoa and/or Cocoa Touch rewritten in Swift. On the other hand, writing a Swift app today that builds on the opaque Cocoa and Cocoa Touch frameworks, which happen to be written in Objective-C, is an everyday occurrence.

Swift has been designed for the interoperability, and some tweaks to Objective-C in the last few years make that language more interoperable with Swift. (Whether that was done deliberately or not is unknown. It’s probably a bit of both: part coincidence and part planned.)

I’ve already discussed some of the interoperable features of Swift (refer to the discussion of backing variables and properties in Chapter 16, for example).

This chapter is the final chapter in the main body of the book, so by now you should be pretty comfortable with Swift, and along the way you should have picked up a little bit about Objective-C. If you have followed along with the Locatapp example described in this book, you have an app that runs and that uses both Objective-C (in the frameworks) and Swift (in the Master-Detail Application iOS template in Xcode).

This chapter goes into the details of three interactions between Objective-C methods in frameworks and Swift in your own code. These interactions are good examples of similar interactions you’ll do in your own apps.

Comparing Frameworks in Objective-C and Swift

The Master-Detail Application template uses a split-view controller in some cases and a navigation controller in others.

Originally (that is, with the launch of iPad), the split-view controller was intended for iPad, and the navigation controller was intended for iPhone. In 2014, with the advent of iPhone 6 Plus, the implementation changed. The split-view controller is now used for larger-screen devices and the navigation controller is used for smaller screen devices. The dividing line is no longer iPad vs. iPhone: It now starts with iPhone 6 Plus, which uses a split-view controller along with iPad. Other iPhone models use the navigation controller. This has caused a revision to the code, so even if you’ve used it for years, you should take a look at the code in this section in both languages.

The split view controller is set up in AppDelegate (AppDelegate.swift or the combination of AppDelegate.h and AppDelegate.m in Objective-C). This section shows you the declaration and the implementation in both Swift and Objective-C. As noted, you’ll encounter this approach repeatedly with legacy Objective-C frameworks.

Identifying the key method

The key to the split-view controller is a protocol — UISplitViewControllerDelegate. Within that protocol, one of the most important methods is the one that manages collapsing the secondary view controller (the master list, in most cases). Even the name of this method differs in the two languages. In the jump bar, here is how it is identified in Swift:

splitViewController (_:collapseSecondaryViewController:
  ontoPrimaryViewController)

Here is how it is identified in the jump bar in Objective-C:

-splitViewController:collapseSecondaryViewController:
  ontoPrimaryViewController:

The first point to notice is that in Objective-C, the – at the beginning identifies this as an instance method as opposed to a class method. This can be done in Swift, but only in a different way. The name of the method in Swift doesn’t reflect its class- or instance-ness.

Note that in the documentation, the Objective-C version is listed by the title. Following that, the Swift and Objective-C interfaces are shown (in that order). This pattern appears to be followed in all of the frameworks.

Comparing declarations

The actual declarations for these methods are shown here. First Swift:

optional func splitViewController(
  _ splitViewController: UISplitViewController,
  collapseSecondaryViewController
    secondaryViewController: UIViewController!,
  ontoPrimaryViewController primaryViewController :
    UIViewController!)
  -> Bool

Next, the Objective-C declaration:

- (BOOL)splitViewController:
    (UISplitViewController *) splitViewController
  collapseSecondaryViewController:
    (UIViewController *) secondaryViewController
  ontoPrimaryViewController:
    (UIViewController *) primaryViewController

Now that you’re looking at the actual code, you can see there are more differences than just the – that marks this as an instance method in Objective-C. Here are the major differences:

· In Objective-C, the return result is shown in parentheses at the beginning of the function, as in

- (BOOL)splitViewController:

· In Swift, the return result is shown at the end of the function, as in

-> Bool

· In Objective-C, the parameters (except the first) are shown in this order: external name, colon, type (in parenthesis and asterisk), internal name, as in

collapseSecondaryViewController:(UIViewController *)
  secondaryViewController

· In Swift, the parameters (including the first) are shown in a different order: external name, internal name, colon, type, as in

collapseSecondaryViewController secondaryViewController:
    UIViewController!

· Types in Swift can include ! and ? as postfix operators to indicate unwrapping or optional status. In addition, the external name can be missing and replaced by an underscore, as in

_ splitViewController: UISplitViewController

Calling an Objective-C Method in Objective-C within Swift to Set a Pin on the Map

If you have followed along with the changes to Locatapp, you know that adding stored locations to the map is very simple. The code you have already written does the job of storing the locations. All you need to do is to add a pin for the selected location.

The code that follows uses Swift, but it uses the MapKit and Core Location frameworks even more than the Swift language.

The first change is in MasterViewController.swift. It’s in prepareForSegue (_:sender:) Here are the steps:

1. Locate the following line.

let object =
  self.fetchedResultsController.
    objectAtIndexPath(indexPath)
    as NSManagedObject

2. Change it to this:

let object =
  self.fetchedResultsController.
    objectAtIndexPath(indexPath)
    as Event

The other changes need to take place in the DetailViewController class. Listing 19-1 shows the new version. Previous deletions are commented out in that listing.

Listing 19-1: Updating DetailViewController

class DetailViewController: UIViewController {

  //@IBOutlet weak var detailDescriptionLabel: UILabel!
  @IBOutlet var mapView: MKMapView!
  @IBAction func actionButton(sender: AnyObject) {
  }

    var detailItem: Event? = nil { //AnyObject? {
      didSet {
        // Update the view.
        self.configureView()
      }
    }

    func configureView() {
      // Update the user interface for the detail item.
      
      var pin = MKPointAnnotation ()
      
      var long:Double = detailItem!.longitude as Double
      var lat:Double = detailItem!.latitude as Double
      
      var myCoordinate:CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: lat as CLLocationDegrees, longitude: long as CLLocationDegrees)
      
      pin.coordinate = myCoordinate
      pin.title = “Test Title”;
      pin.subtitle = “Test Subtitle”;
      
      if var myMapView = self.mapView {
        myMapView.addAnnotation(pin)
      }
    }

    override func viewDidLoad() {
      super.viewDidLoad()
      // Do any additional setup after loading the view, typically from a nib.
      self.configureView()
    }

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

Here are the steps to take to update the existing code in configureView from the version you have at the end of Chapter 16:

1. Change detailItem to type Event.

That’s how it’s described in the data model, but until now it’s been used as a generic NSManagedObject or AnyObject. Change the declaration as shown here (the previous code is commented out).

var detailItem: Event? = nil { //AnyObject? {

2. Change the configureView function to add a map pin.

This has several steps. The first is to create a pin variable of type MKPointAnnotation (part of the MapKit framework).

var pin = MKPointAnnotation ()

3. Create a Double from the stored longitude value which is an NSNumber at this point.

You do that in Chapter 9, “Functioning Successfully.”

var long:Double = detailItem!.longitude as Double

4. Do the same with the stored latitude value.

var lat:Double = detailItem!.latitude as Double

5. Create a CLLocationCoordinate2D called myCoordinate.

This combines the latitude and longitude.

var myCoordinate:CLLocationCoordinate2D =
  CLLocationCoordinate2D(
    latitude: lat as CLLocationDegrees,
    longitude: long as CLLocationDegrees)

6. Set the coordinate property of pin to myCoordinate.

pin.coordinate = myCoordinate

7. Set the title property to a title.

pin.title = "Test Title";

8. Set the subtitle property to a subtitle.

pin.subtitle = "Test Subtitle";

9. Convert self.mapView to a var called myMapView, if possible.

10. If successful, call addAnnotation with pin.

myMapView.addAnnotation(pin)

Your app now lets you select a location and see it on the map. The updated code appears in Listing 19-1.

In the declarations and in the method/function calls in Listing 19-1, most of what you’ve done is directly translated from Objective-C code to Swift. In both languages, the syntax that you use is repeated over and over. There are differences between the two languages, but you get used to the patterns you need to use to work with both languages over time.

Bridging between Objective-C and Swift

There are cases where you need to mix and match code between the two languages. When it comes to the frameworks, the engineers at Apple are working through the interfaces to provide Swift interfaces alongside the Objective-C versions so that you can use either one to get to the framework in your own app.

Sometimes you need to use a bridge to get to code that you need. A typical example occurs when you use Core Data with relationships. Given a data model for Core Data (often provided as part of a template), you can use Editor⇒Create NSManagedObject Subclass to create files to add to your project. Choose the option to create Objective-C files. At the bottom of the .h file that’s created you’ll find declarations of methods for members of relationships such as these:

@interface WhereCategory (CoreDataGeneratedAccessors)

- (void)addNecklaceObject:(Necklace *)value;
- (void)removeNecklaceObject:( Necklace *)value;
- (void)addNecklaces:(NSSet *)values;
- (void)removNecklaceses:(NSSet *)values;

@end

This code lets you add or remove individual related objects or the whole set of related objects. When you try to create the files, you’ll see an alert asking you if you’d like to create a bridging header.

The file that is created will be named MyProject-Bridging-Header.h. Simply add import statements to that file for the Objective-C .h files, as shown here, and you’ll be ready to build your mix-and-match project.

// Use this file to import your target’s public headers
// that you would like to expose to Swift.

#import “Bracelet.h”
#import “Pendant.h”