2015年9月14日 星期一

[Android] Implementing Callbacks when JSON operation is done

More often then not we find ourselves in a situation where we must wait for an action to be completed before performing another task, for example wait for JSON to finish parsing before performing a task.

In this case, the easiest way would be to implement a callback: when JSON is done parsing, set a flag to true.

Within your operation class, for example OperationJSON.class
Create an Interface:
1
2
3
interface OnParseDataCompleted{
    void onTaskCompleted();
}


initialize a listener and a setter/getter for isCompleted flag and execute onTaskCompleted when the task is done:

 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
public boolean isCompleted = false;

public boolean isCompleted() {
    return isCompleted;
}

public void setIsCompleted(boolean isCompleted) {
    this.isCompleted = isCompleted;
}

private void parseTheData(String requestUrl, final JSONObject jsonObj) {
    new AsyncTask<String, Void, JSONObject>() {
        private OnParseDataCompleted listener = new OnParseDataCompleted() {
            @Override            
            public void onTaskCompleted() {
                setIsCompleted(true);
            }
        };

        @Override        
        protected JSONObject doInBackground(String... urls) {
            //omitted
        }

        @Override        
        protected void onPostExecute(JSONObject jsonObjRecv) {
                listener.onTaskCompleted();
            }
        }
    }.execute(requestUrl);
}




At this point, whenever this AsyncTask is completed, it will execute listener.onTaskCompleted(), which will set isCompleted to true;

To use this flag, simply check this flag wherever necessary:

2015年8月20日 星期四

[Android] SASLError using PLAIN: not-authorized Error when using Smack 4.1

When implementing XMPP with Android using Smack 4.1, I encounter this error in my logcat. This error has nothing to do with your code, but actually a setting for your google account (assuming you are using your hangout account for testing)

08-20 16:32:21.970  25575-25595/com.example.xmppsample I/System.out﹕ [socket][0] connection talk.google.com/64.233.188.125:5222;LocalPort=34746(30000)
08-20 16:32:21.970  25575-25595/com.example.xmppsample I/System.out﹕ [CDS]connect[talk.google.com/64.233.188.125:5222] tm:30
08-20 16:32:22.170  25575-25595/com.example.xmppsample I/System.out﹕ [socket][/192.168.0.4:34746] connected
08-20 16:32:25.240  25575-25595/com.example.xmppsample W/System.err﹕ org.jivesoftware.smack.sasl.SASLErrorException: SASLError using PLAIN: not-authorized
08-20 16:32:25.250  25575-25595/com.example.xmppsample W/System.err﹕ at org.jivesoftware.smack.SASLAuthentication.authenticationFailed(SASLAuthentication.java:365)
08-20 16:32:25.250  25575-25605/com.example.xmppsample I/System.out﹕ [CDS]close[34746]
08-20 16:32:25.250  25575-25605/com.example.xmppsample I/System.out﹕ close [socket][/0.0.0.0:34746]

Just enable this in:
https://www.google.com/settings/security/lesssecureapps

2015年8月6日 星期四

[Swift] Illiterate Enum members

Sometimes it is useful to illiterate over all members of a enum. Here's hows its done in Swift:

Add a static constant array containing the enum members

1
2
3
4
5
6
7
8
9
    enum Brands: String {
        case Toyota = "Toyota"
        case BMW= "BMW"
        case Tesla = "Tesla"
        case Honda = "Honda"
        case Lexus = "Lexus"
        
        static let allBrands = [Toyota, BMW, Tesla, Honda, Lexus]
    }

To access the data, just illiterate using a for loop:

1
2
3
        for brandNames in Brands.contents {
            println("brandNames.rawValue")
        }

Simple and effective.

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"