Showing posts with label replace. Show all posts
Showing posts with label replace. Show all posts

Thursday, May 14, 2015

I ripped out RestKit and replaced it NSURLSession and Mantle

When I started at my current job I knew I would be doing a lot of REST interaction with the server. I decided to use RestKit as I had seen it mentioned a number of times in various developer forums.

It worked for some time but I finally hit the breaking point and ripped it all out changing 265 to convert them to NSURLSession and Mantle. Was I insane? Pretty much as in the end it did not solve the problem I was originally attempting to address but I am happy with the change.

First off Mantle makes it much easier and cleaner to handle JSON. It is not as easy as GSON on the Android side but it is not too bad. That was a lot of my file conversions and I do find it easier to add a new JSON object due to a new REST call to the server. The one downfall is each of your JSON receiving objects has to be derived from a Mantle class. Objective C does not support multiple inheritance which could make this a very limiting factor for you.

The other area I am not too happy about with Mantle is it handles converting NSDictionary and NSArray to JSON. That means I get a big JSON string from REST and then I use NSJSONSerialization to convert it into a NSDictionary and then convert that via Mantle to Objective C objects. Waste of memory and processing to double convert things. Good thing the JSON data is reasonably small.

The other side was getting rid of RestKit calls. This helped the code a lot. I had one REST call manager class that tried to handle all the server calls with delegate callbacks. Now all the calls are inline where they are called in the code and that code has block processing to handle the results. This lets you see what is going on easier. Plus if it is a PUT call I only have to reference PUT in one line of code whereas I had to do that in three places when using RestKit. Setting up each call in RestKit took a boatload of error prone boilerplate code. I sure as heck don't miss that.

So why not go with AFNetworking? Seems to also be popular but in the end even AFNetworking is using NSURLSession in the end. Apple seems to put together a decent networking package learning from others. One less pod to include in my build and hopefully using the official Apple networking that appeared in iOS 7 will keep me in the safe zone.

On the Android side I am using Volley and GSON, both from Google but not part of the base Android SDK. Volley is very similar to NSURLSession which is good. GSON is easier to work with than Mantle in there is less boilerplate code. If your variable names match the property names in the JSON you get from the server there really is no boilerplate at all. If they don't match a simple annotation gets them to sync up.

So what was the original issue I was attempting to fix? I want to have two type of NSURLSessions going in the code, one to do the normal user interaction processing - they tap something, I make a server request, I get server result and I show new data in the current view or in a new view. The second was to be a background session for uploading video via Amazon Web Services.

I am using AWS but I am using the straight API from them. Yes, there is sample upload in the background code out there and it works when you don't have the other NSURLSession doing other work which really stinks. I want the user to be able to start a video upload and have it run in the background until complete even if they leave my app or turn off the phone screen. Works like a champ on the Android side where I set up a very basic service.

I realize Apple really does not want you do to anything in the background. They trust no one. They do have a new thing that lets you get maybe 10 minutes of time to do some background work. That is one of the things NSURLSession is set to allow. But I just can't get it to work. It will start, send up some % of the video then restart at 0% and do that over and over again. On the simulator, which does not run like a real device, it can get the whole video uploaded never triggering the restart you see on the iPhone or iPad. I have posted a message on Stack Overflow describing this issue but no answers.

I really wish Amazon would handle this for you in their API. Seems like background uploading would be a really common thing for a programmer to want to do with them. Maybe Apple could handle it in their API as well but I doubt that as they are anti-background as it is.

Thursday, September 6, 2012

Convert Numeric keypad [ENTER] to [TAB] in java

Our data entry staff does a lot of numeric entry. Java uses the [TAB] key to move between entry fields. I was asked if we could have the NumPad [ENTER] key act like the [TAB] key greatly speeding up data entry. I was able to pull it off with a little bit of code.


import java.awt.AWTEvent;
import java.awt.AWTException;
import java.awt.EventQueue;
import java.awt.Robot;
import java.awt.event.KeyEvent;

/**
 * Special event queue to convert NUMPAD + ENTER into TAB
 *
 * @author kevin.peck
 * @date Sep 4, 2012
 */
public class CustomEventQueue extends EventQueue {
    private Robot robot;
    private boolean swapKeys;

    /**
     * Constructor
     *
     * Get robot running so we can fake key events
     */
    public CustomEventQueue() {
        super();
        try {
            robot = new Robot();
        } catch (AWTException e) {
            System.out.println("Unable to get robot running " + e);
        }
    }

    /**
     * @param swapKeys  true to force key swap processing
     */
    public void setSwapKeys(boolean swapKeys) {
        this.swapKeys = swapKeys;
    }

    /**
     * @return  true if we are doing key swap processing
     */
    public boolean areKeysSwapped() {
        return swapKeys;
    }

    /**
     * Watch for keyevents and convert NUMPAD ENTER key into TAB key
     */
    @Override
    protected void dispatchEvent(AWTEvent event) {
        if (swapKeys && event instanceof KeyEvent) {
            KeyEvent keyEvent = (KeyEvent)event;
            if (keyEvent.getKeyLocation() == KeyEvent.KEY_LOCATION_NUMPAD && keyEvent.getKeyCode() == KeyEvent.VK_ENTER) {
                if (keyEvent.getID() == KeyEvent.KEY_PRESSED) {
                    robot.keyPress(KeyEvent.VK_TAB);
                    robot.keyRelease(KeyEvent.VK_TAB);
                }
                return;
            }
        }
        super.dispatchEvent(event);
    }
}

Add this to your main code (class derived from JFrame)
    private CustomEventQueue eventQueue = new CustomEventQueue();

Add this to the constructor or initialization routine of the class derived from JFrame
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                EventQueue ev = Toolkit.getDefaultToolkit().getSystemEventQueue();
                eventQueue.setSwapKeys(true);
                ev.push(eventQueue);
            }
        });

Why is there a setSwapKeys() method? So you can turn off this functionality. I set enabled the sample above but in my code it is actually reading a Preference setting. You can enable / disable the [ENTER] = [TAB] at any time in your code by invoking the swap keys method.

If swap keys is enabled I eat the VK_ENTER from the numeric keypad for both PRESSED and RELEASED events. I use the Robot object to send out a VK_TAB press and release on the PRESSED event. Nothing is sent on the VK_ENTER RELEASED event. 

Why am I installing the queue invokeLater? I was not doing that an it ran just fine on Java 1.7 but on Java 1.6 I would get an exception. Putting it on the EDT solved that issue.

So far it seems to be working like a champ. With minimal modifications you can watch for any event you need to perform special actions. Don't go crazy but have fun.