Excessive Data Usage with iPhone 4S / iOS5

Got a new iPhone 4S or recently upgraded your iPhone 4 or 3GS to IOS5 and noticing unusually high cellular data usage? Are you close to exceeding your data usage limit when you never have before? Have you already exceeded it? You are not alone.

My wife recently traded in her old iPhone 3G for a shiny new iPhone 4S, and within 10 days had exceeded her 200MB AT&T data plan limit when she has never exceeded it before. So what gives? It’s a new phone, and Siri does transmit voice data back to Apple’s servers, but could that really have caused the usage?

Fixing The Data Usage Problem

Turns out the problem is that Apple ships iOS5 with iCloud data syncing over cellular networks ON by default. This means all the documents and data stored on your phone for all the applications you have installed on your phone will be automatically uploaded to iCloud, decimating your puny cellular data plan you thought you’d never burn through.

If you are experiencing super high data usage on iOS5, just follow these steps:

  1. Navigate to: Settings -> iCloud -> Documents & Data -> Use Cellular
  2. Turn it OFF
  3. Fight with AT&T (or other wireless carrier) about your excessive data usage charges
Why Apple would ever think its a good default to sync data and documents for all your phone’s applications over a cellular network in the world of severely limited data plans and usage based billing is beyond me. This, and the widespread battery issues with the iPhone 4S and iOS5 seem to indicate Apple didn’t do nearly enough field testing with the new phone or OS. Let’s home they learn from this in the future.

Usage Before iPhone 4S

Some months have clearly higher data usage than others depending on how long we were away from home or what we did while we were out with out phones, but we were never in any real danger of going over the low 200MB limit before.

Data Usage after 10 days with the iPhone 4S


On the left (green) is my data usage with an Android device, Right (red) is my wife’s data usage this month with the new iPhone 4S.

My wife got a text alert for 65% data plan usage on Monday, and then one on Friday for 80%, and one the day after on Saturday saying she had gone over her limit (exceeding 100% of the plan) and that we would be billed an additional $15 by AT&T for the overage.

 

 

 

 

How to add Photos to the iPhone Simulator

Building an app that needs to access the photo library but don’t have any photos in the iPhone simulator? No problem. Follow these simple steps to import photos into the iPhone Simulator:

  1. Open the iPhone Simulator
  2. Browse to the photo you want to put into the simulator (Finder or web browser)
  3. Click and drag the photo over the simulator window. A green “plus” icon should appear under your cursor with the simulator frame highlighted. Drop the photo.
  4. Mobile Safari should open on the simulator to the location of the image you just dragged and dropped over it
    1. Note that if the image you dropped is linked in a webpage, it will open the link instead of the image URL.
  5. Click the mouse down over the image and hold it until a popup window appears.
  6. Click “Save Image”
The photo will now reside in the “Saved Photos” album on the iPhone Simulator. Rince and repeat as many times as you need to get all your photos in the album.

Easier Titanium XHR and AJAX Requests

One question I see a lot on the Appcelerator Titanium Developer Q&A is how to perform AJAX requests and/or work with APIs, etc. There is a built-in way to do this with the Ti.Network.HTTPClient module that is pretty easy, but it does have a few drawbacks and “gotchas”, like executing the “success” event for ANY returned status code – even 500 errors. Since working with APIs is so common with mobile apps, I made a wrapper function modeled after jQuery’s $.ajax method that I use in all my apps. It shortens the syntax quite a bit and is much more familiar to those who are used to using jQuery.

The usage looks like this:

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
// Geocode input text location
app.utils.ajax({
    url: 'http://maps.googleapis.com/maps/api/geocode/json?address=' + txtLocation.value +'&region=us&sensor=true',
    method: 'get',
    success: function(xhr) {
        var data = JSON.parse(xhr.responseText);
        Ti.API.info(data);
 
        if("OK" == data.status) {
            var res = data.results[0];
            if(res) {
                alert("Location: " + res.geometry.location.lat + ', ' + res.geometry.location.lng);
            }
            // Do something with coordinates
        } else {
            Ti.UI.createAlertDialog({
                title: 'Geocode Error',
                message: 'Unable to geocode location input'
            }).show();
        }
    },
    error: function(xhr) {
        Ti.UI.createAlertDialog({
            title: 'Geocode Error',
            message: 'No location matches found. Please try something else.'
        }).show();
    }
});

And the actual code for the utility function:

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/**
 * Application Utilities and Helper Methods
 **/
(function(_app) {
    _app.utils = {};
 
    // AJAX method that mimmicks jQuery's
    _app.utils.ajax = function(_props) {
 
        // Merge with default props
        var o = _app.combine({
            method: 'GET',
            url: null,
            data: false,
            contentType: 'application/json',
 
            // Ti API Options
            async: true,
            autoEncodeUrl: true,
 
            // Callbacks
            success: null,
            error: null,
            beforeSend: null,
            complete: null
        }, _props);
 
        Ti.API.info("XHR " + o.method + ": \n'" + o.url + "'...");
        var xhr = Ti.Network.createHTTPClient({
            autoEncodeUrl: o.autoEncodeUrl,
            async: o.async
        });
 
        // URL
        xhr.open(o.method, o.url);
 
        // Request header
        xhr.setRequestHeader('Content-Type', o.contentType);
 
        if(o.beforeSend) {
            o.beforeSend(xhr);
        }
 
        // Errors
        xhr.setTimeout(10000);
        xhr.onerror = function() {
            Ti.API.info('XHR "onerror" ['+this.status+']: '+this.responseText+'');
            if(null !== o.error) {
                return o.error(this);
            }
        };
 
        // Success
        xhr.onload = function() {
            // Log
            Ti.API.info('XHR "onload" ['+this.status+']: '+this.responseText+'');
 
            // Success = 1xx or 2xx (3xx = redirect)
            if(this.status < 400) {
                try {
                    if(null !== o.success) {
                        return o.success(this);
                    }
                } catch(e) {
                    Ti.API.info('XHR success function threw Exception: ' + e + '');
                    return;
                }
            // Error = 4xx or 5xx
            } else {
                Ti.API.info('XHR error ['+this.status+']: '+this.responseText+'');
                if(null !== o.error) {
                    return o.error(this);
                }
            }
        };
 
        // Send
        if(o.data) {
            Ti.API.info(o.data);
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xhr.send(o.data);
        } else {
            xhr.send();
        }
 
        // Completed
        if(null !== o.complete) {
            return o.complete(this);
        }
    };
 
    // And it does depend on this code below to combine object properties:
 
    // Extend an object with the properties from another 
    // (thanks Dojo - http://docs.dojocampus.org/dojo/mixin)
    var empty = {};
    function mixin(/*Object*/ target, /*Object*/ source){
        var name, s, i;
        for(name in source){
            s = source[name];
            if(!(name in target) || (target[name] !== s && (!(name in empty) || empty[name] !== s))){
                target[name] = s;
            }
        }
        return target; // Object
    };
    _app.mixin = function(/*Object*/ obj, /*Object...*/ props){
        if(!obj){ obj = {}; }
        for(var i=1, l=arguments.length; i<l; i++){
            mixin(obj, arguments[i]);
        }
        return obj; // Object
    };
 
    // Create a new object, combining the properties of the passed objects with the last arguments having
    // priority over the first ones
    _app.combine = function(/*Object*/ obj, /*Object...*/ props) {
        var newObj = {};
        for(var i=0, l=arguments.length; i<l; i++){
            mixin(newObj, arguments[i]);
        }
        return newObj;
    };
})(app);

The particular organization of this utilities file assumes that your app structure follows the organization model shown in the Tweetanium example app (using the ‘app’ namespace within a single window context), but is easy to adapt if you are not.

Count the Number of Object keys/properties in Node.js

When using the excellent formidable library to handle file uploads, I needed to get a count of the number of files unloaded in a multi-part form. Javascript arrays have a .length property that you can use, but objects do not. I instinctively typed:

1
files.length

Which returned undefined. So if there is no length property present, an easy way to count the number of keys or properties of an object in ES5-compliant javascript environments like Node.js is to use the Object prototype directly:

1
Object.keys(files).length

A little more typing, but it is fast, efficient, and most importantly: already built-in.

php|tek 2011

php|tek 2011 Speaker
Chicago – May 24-27

php|tek in Chicago was fun as always. It is the best PHP conference I have ever been to, which makes sense, given that it is focused solely on PHP and surrounding technologies. The best thing about the conference is the community feeling in general. You get a real sense that everyone there really cares about PHP and is heavily invested in it, which is good for moving the whole language and ecosystem forward.

I myself gave two talks – one about Stackbox CMS, a new CMS project I have been working on, and one about Apppcelerator Titanium Mobile since I have been working with it a lot lately. The presentations are embedded below. Read More »

Android+iPhone SEO App

I just released a new iPhone SEO app and Android SEO app called SEMTab SEO Pro. The basic idea is to keep a list of domains saved, and check SEO/SEM stats like Google PageRank (PR), backlinks, Alexa rank, etc. and Social share information from Twitter, Facebook, and Delicious. Read More »

Protected vs Private Scope: Arrogance, Fear, and Handcuffs

The age old private vs protected debate has been re-ignited in the PHP community recently following the decision of Doctrine2 and Symfony2 to make all class methods private until there is a very clear and proven reason to change them to protected or public. The intention is a good one – to ensure they are providing a clear and stable API through intentional and known extension points that they can better test and support. Fabien (the creator of Symfony) points to this benefit in an article of his own explaining the thinking behind the decision. The primary started point of Fabien’s article and the driving thought behind this whole change and philosophy is (emphasis his):

Having a few well defined extension points force the developer to extend your library the right way instead of hacking your code.

The problem is that this kind of thinking is a slippery slope that kills the spirit of programming. It alienates the more pragmatic developers within communities. Telling other developers that you are going to force them to work with your code in some pre-determined “right way” is an incredibly arrogant statement to make. Read More »

Self Employed

This post comes with a bit of a delay, as it is already three months into the year, but as of January 2011, I have been full time self-employed. I made the difficult decision to voluntarily leave an excellent job with Company52 at the beginning of the year to venture out on my own. Running my own company with full-time focus has always been one of my dreams, and several side projects I have started and been involved with up until now have built up towards that goal.

There is a lot to consider in a decision like this, and I did not weigh it lightly. Somehow after running through all the options, now still seemed like the best time to take the leap. Here’s to a bright future in 2011 and beyond.

MySQL Error: 1033 Incorrect information in file

I recently encountered this error on Disposeamail – a free disposable email site of mine that uses MySQL heavily for storing all incoming mail through an email pipe script.

I did a lot of researching, and basically, there are a few primary culprits I was able to identify that will hopefully save you some time.

Read More »

Listing Aliases Inside an Android Keystore File With Keytool

If you lose or forget your Android keystore file alias that is used to build APK files for distribution (like I did when trying to package Autoridge Lite for the Android Market), here is a quick and easy way to see them:

  1. Open a Terminal Window, Run This Command:

    1
    
    keytool -list -keystore /location/of/your/com.example.keystore

    Make sure “keytool” is either in your PATH, or “cd” into the “tools” directory where your Android SDK files are.

  2. Enter your keystore password when prompted (you didn’t forget that too, did you? Did you?)
  3. See results!

You should see something like the picture below if you did everything right. The alias is circled in yellow. If you have multiple aliases in your keystore, they will all be listed, one per line.
Read More »

All content copyright © 2012 Vance Lucas | Powered by WordPress | Entries (RSS) | Comments (RSS)