- Creating a Shared App Group
- Creating the iOS Application
- Coding the Apple Watch App
- Using a Framework to Contain Repetitive Code
- Summary
Using a Framework to Contain Repetitive Code
The ViewController.swift file contains the following block of code:
var defaults = NSUserDefaults( suiteName: "group.net.learn2develop.articles.sharingdata") ... //---read a value from the setting, given a key--- func readSettingValue(key:String) -> String? { return defaults?.objectForKey(key) as? String } //---save a setting value, given the key--- func saveSettingValue(key:String, value:String) { defaults?.setObject(value, forKey: key) defaults?.synchronize() }
The InterfaceController.swift file has something similar:
var defaults = NSUserDefaults( suiteName: "group.net.learn2develop.articles.sharingdata") ... //---read a value from the setting, given a key--- func readSettingValue(key:String) -> String? { return defaults?.objectForKey(key) as? String }
In both cases, you need the code to read and write to the settings (although for this exercise the Watch App extension only reads from the settings). Since the code is repetitive, a better idea would be to wrap the method within a framework. Then you can write the code once and call the framework in both targets. This technique would promote code reuse and make your code safer. Follow these steps to wrap the method in a framework:
To add a framework to the project, select File > New > Target. Under the iOS category, select Framework & Library and then select Cocoa Touch Framework (see Figure 10). Click Next.
Figure 10 Adding a framework to the project.
Name the target MySettings (see Figure 11) and then click Finish.
Figure 11 Naming the framework.
Notice that the MySettings and MySettingsTests targets have been added to the project (see Figure 12).
Figure 12 The framework has been added to the project.
The framework that you have just created is automatically added to the SharingData project. Verify this by selecting the project name, selecting the SharingData target (see Figure 13), and observing that MySettings.Framework has been added to the project in the Build Phases tab (under the Linked Frameworks and Libraries section).
Figure 13 The Framework is automatically referenced in the iOS app.
You need to add the framework to the WatchKit Extension target if you intend to use it. In the Build Phases tab, click on the plus (+) button located under the Linked Frameworks and Libraries section (see Figure 14).
Figure 14 Adding a reference to the framework in the WatchKit extension target.
Select MySettings.framework (see Figure 15).
Figure 15 Selecting the framework to add it to the project.
Add a new Swift file to the MySettings target and name it MySettings.swift (see Figure 16).
Figure 16 Adding a new Swift file to the project.
Populate the MySettings.swift file with the following statements in bold:
import Foundation public class MySettings: NSObject { var defaults = NSUserDefaults( suiteName: "group.net.learn2develop.articles.sharingdata") var countries: [String]! //---read a value from the setting, given a key--- public func readSettingValue(key:String) -> String? { return defaults?.objectForKey(key) as? String } //---save a setting value, given the key--- public func saveSettingValue(key:String, value:String) { defaults?.setObject(value, forKey: key) defaults?.synchronize() } }
Notice that you need to use the public keyword for the class and function names so that they can be called from both the iOS and WatchKit Extension targets.
The ViewController.swift file can now be simplified, using the MySettings framework:
import UIKit import MySettings class ViewController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate { @IBOutlet weak var pickerCountries: UIPickerView! var countries: [String]! var mySettings = MySettings() override func viewDidLoad() { super.viewDidLoad() countries = ["Singapore", "Norway", "Japan", "Thailand", "Hong Kong"] self.pickerCountries.delegate = self self.pickerCountries.dataSource = self } func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int { return 1 } func pickerView(pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return countries.count } func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! { return countries[row] } //---when the user selects a country--- func pickerView(pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { mySettings.saveSettingValue("country", value: countries[row]) } //---when the view appears--- override func viewDidAppear(animated: Bool) { if let countrysSelected = mySettings.readSettingValue("country") { //---set the picker to select the previously selected country--- self.pickerCountries.selectRow( find(countries, countrysSelected)!, inComponent: 0, animated: true) } } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } }
Likewise, the InterfaceController.swift file can be simplified by using the MySettings framework:
import WatchKit import Foundation import MySettings class InterfaceController: WKInterfaceController { @IBOutlet weak var label: WKInterfaceLabel! var mySettings = MySettings() override func awakeWithContext(context: AnyObject?) { super.awakeWithContext(context) // Configure interface objects here. if let countrySelected = mySettings.readSettingValue("country") { self.label.setText(countrySelected) } } override func willActivate() { // This method is called when watch view controller is about to be // visible to user super.willActivate() } override func didDeactivate() { // This method is called when watch view controller is no longer // visible super.didDeactivate() } }