Persisting a Bonjour Service
If your app needs to persistently store a reference to a Bonjour service, such as in a printer chooser, store only the service name, type, and domain. By persisting only the domain, type, and name information, you ensure that your app can find the relevant service even if its IP addresses or port number has changed.
When you connect to the service later, initialize an
NSNetService
object with this information by calling its initWithDomain:type:name:
method.
After you have initialized the service object, connect to the service by calling the
getInputStream:outputStream:
method or by passing the result of a hostName
call to a connect-by-name function or method, as described earlier in this chapter. When you do this, Bonjour automatically resolves the hostname or domain, type, and name values into the current IP addresses and port number for the service.Since the method of storing is not specific (and I assume its not stated in the documents for added flexibility to the developer), I will attempt to use NSCoding to store a Bonjour service.
First I create a new class to store a device's service name, type and domain. I call this class DeviceInfoPersistence:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | class DeviceInfoPersistence: NSObject, NSCoding { var serviceName: String var serviceType: String var serviceDomain: String static let DocumentsDirectory: AnyObject = NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first! static let ArchiveURL = DocumentsDirectory.URLByAppendingPathComponent("deviceInfoPersistence") struct PropertyKeys { static let serviceNameKey = "serviceName" static let serviceTypeKey = "serviceType" static let serviceDomainKey = "serviceDomainKey" } init(serviceName:String, serviceType: String, serviceDomain:String){ self.serviceName = serviceName self.serviceType = serviceType self.serviceDomain = serviceDomain super.init() } func encodeWithCoder(aCoder: NSCoder) { aCoder.encodeObject(serviceName, forKey: PropertyKeys.serviceNameKey) aCoder.encodeObject(serviceType, forKey: PropertyKeys.serviceTypeKey) aCoder.encodeObject(serviceDomain, forKey: PropertyKeys.serviceDomainKey) } required convenience init(coder aDecoder: NSCoder) { let serviceName = aDecoder.decodeObjectForKey(PropertyKeys.serviceNameKey) as! String let serviceType = aDecoder.decodeObjectForKey(PropertyKeys.serviceTypeKey) as! String let serviceDomain = aDecoder.decodeObjectForKey(PropertyKeys.serviceDomainKey) as! String self.init(serviceName:serviceName, serviceType:serviceType, serviceDomain:serviceDomain) } } |
A couple of notes:
- When extending NSCoding to the class, we must implement both init(coder aDecoder: NSCoder) and encodeWithCoder(aCoder: NSCoder).
- Outside of this class, we can access the data file path by using DeviceInfoPersistence.ArchiveURL
After we create the data class, we will access it in the ViewController.
Before using the data class, we need to first initialize it: var deviceInfoPersistence = [DeviceInfoPersistence]()
We will create a save function to save data into the deviceInfoPersistence array:
1 2 3 4 5 6 7 8 | func saveDeviceInfoPersistence(){ println("save device info \(deviceInfoPersistence.count)") let isSuccessfulSave = NSKeyedArchiver.archiveRootObject(deviceInfoPersistence, toFile: DeviceInfoPersistence.ArchiveURL.path!) if !isSuccessfulSave { print("Failed to save Device Info...") } } |
Then we create a load function to load the saved devices:
1 2 3 | func loadDeviceInfoPersistence() -> [DeviceInfoPersistence]? { return NSKeyedUnarchiver.unarchiveObjectWithFile(DeviceInfoPersistence.ArchiveURL.path!) as? [DeviceInfoPersistence] } |
Basically at this moment we have all the necessary mechanism we need for persisting a bonjour service. Now we will need to save/load the data at the right place at the right time. Below I demo a simple save and load when loading my list view:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | func populateDevicesList() { ////Omitted for (key,value) in devices { var deviceKey: AnyObject? = devices.objectForKey(key) var deviceInfo = DeviceInfoPersistence(serviceName: deviceKey?.objectForKey("name") as! String, serviceType: deviceKey?.objectForKey("type") as! String, serviceDomain: deviceKey?.objectForKey("domain") as! String) deviceInfoPersistence.append(deviceInfo) saveDeviceInfoPersistence() } let deviceList = NSKeyedUnarchiver.unarchiveObjectWithFile(DeviceInfoPersistence.ArchiveURL.path!) as! [DeviceInfoPersistence] for theDevice in deviceList { println("Name: \(theDevice.serviceName), Domain: \(theDevice.serviceDomain), Type: \(theDevice.serviceType)") } ////Omitted } |
The output of this will be:
Name: Device1, Domain: local., Type: _http._tcp.
Name: Device2, Domain: local., Type: _http._tcp.
沒有留言:
張貼留言