顯示具有 Swift 標籤的文章。 顯示所有文章
顯示具有 Swift 標籤的文章。 顯示所有文章

2015年7月31日 星期五

[Swift] Using NSNetService for Multiple Device Resolution

Apple Official Docs suggest that to persist a bonjour connection, we should save the service name/domain/type and use NSNetService to resolve the IP Address of this service. Obviously this is to cope with IP being dynamic.

NSNetService is actually pretty simple to use after some research. New an NSNetService Object and initiate a search with resolveWithTimeout.

1
2
3
4
var service: NSNetService!
service = NSNetService(domain: serviceDomain, type: serviceType, name: serviceName)
service.delegate = self
service.resolveWithTimeout(3)

Implement the following protocol according to what you what to do in each case:
netServiceDidResolveAddress(sender: NSNetService)
netServiceDidStop(sender: NSNetService)
netService(sender: NSNetService, didNotResolve errorDict: [NSObject : AnyObject])

When a device is detected, the App will enter netServiceDidResolveAddress. After 3 Seconds, it will enter netServiceDidStop. If the resolve address process failed, it will enter the last protocol function.

The above code will work for only 1 device because service is created within a function and it will go out of scope after this function has been executed. If the App is to be able to resolve multiple device at the same time, we must somehow preserve this object and prevent it from going out of scope. The most common method is to save the NSNetService in an Array:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
var allService: NSMutableArray = NSMutableArray()

func populateDevicesList(deviceArray:Array<Array<String>>)
{
    //omitted
    for deviceSet in 0..<deviceArray.count{
        var name = deviceArray[deviceSet][0]
        var type = deviceArray[deviceSet][1]
        var domain = deviceArray[deviceSet][2]
        
        var service: NSNetService!
        service = NSNetService(domain: serviceDomain, type: serviceType, name: serviceName)
        service.delegate = self
        service.resolveWithTimeout(GlobalVariable.TIMEOUT)
        
        allService.addObject(service as NSNetService)
    }
    //omitted
}


The key to multiple device resolution is line 17, saving the NSNetService into an NSMutableArray.

I have also removed the NSNetService object on netServiceDidStop and netService(:didNotResolve:) by using allService.removeLastObject().

2015年7月22日 星期三

[Swift] Use NSCoding to Persist a Bonjour Service

Regarding storing a bonjour service, Apple stated in the documents:

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.

2015年7月13日 星期一

[Swift] Using Swift Protocol in Objective-C

I have a ListView that is populated with detected device information when my scanning service has successfully scanned nearby devices. My scanning service is in Objective C and my App is in Swift, so I will have to use Swift protocol in Objective C functions.

The goal is simple: when device is found, notify the ListView controller. The ListView controller will then retrieve detected device information and then populate the ListView. Note that in the following snippet I have omitted how to populate data into ListView

If you follow the following steps you should have no problem using Swift protocol in Objective C files.

2015年7月9日 星期四

[Swift] Calling Swift Function in Objective-C Class

I have a service written in Objective-C and my App in Swift. Here's how to call Swift Functions from Objective-C files.

Step 1:
In your Objective-C .m file where you want to add the Swift function, make sure you import the Swift Header: 

1
#import "{Poduct Module Name}-Swift.h"

You can check your Product Module Name in the project Build Settings. For Example if your Product Module Name is "Hello_World", you will need to #import "Hello_World-Swift.h"

Step 2 Sample Swift Code:

1
2
3
4
5
@objc class testClass: UITableViewController {
    func printTest(){
        println("Hello From Swift") 
    }
}

Step 3, Call Swift Class in Objective-C method like so:
1
2
3
4
5
6
- (void) actionCompleted {
    [[NSNotificationCenter defaultCenter] postNotificationName:@"actionCompleted" object:self];
    
    testClass *daTestClass = [[testClass alloc] init];
    [daTestClass printTest];
}


You cannot import {PMN}-Swift.h into Objective C Header. To do that you must forward declaration as  mentioned here under "Referencing a Swift Class or Protocol in an Objective-C Header"

2015年7月7日 星期二

[Swift] Parsing nested Dictionary

I have a network discovery service that will output the following data in debugger:
{
    Device1 =     {
        date = "2015-07-07 08:11:56 +0000";
        name = Device1;
        recent = 0;
        url = "http://192.168.1.10:80";
    };
}

This data is in the form of:
Dictionary(String, Dictionary(String:String,String:String,String:String,String:String))

To extract this nested dictionary in Swift, I will run a for loop over each key. Translated into english would be, for each device in the dictionary. 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
for (key,value) in devices
{
    var deData: ImageTitleCellData = ImageTitleCellData()
    var deDevice: AnyObject? = devices.objectForKey(key)
    var url: NSString = deDevice?.objectForKey("url") as! NSString
    url  = url.stringByReplacingOccurrencesOfString(":80", withString: "") 
    deData.Name = devices.objectForKey(key)?.objectForKey("name") as! String
    deData.Subtitle = url.stringByReplacingOccurrencesOfString("http://", withString: "") 
    deData.ImgSrc = "ic_device.png"
    println("\(deData.Name) -> \(deData.Subtitle)")
}

This will have the following output in console:
Device1 -> 192.168.1.10