Installation artist Doug Fishbone’s most ambitious projects have involved up to 40 000 bananas piled up in public places.
Tron in frames, for no reason.
Terminator in frames, Sarah’s perspective. As opposed to the Terminator’s.
With iPhone OS 3.0, Apple introduced in-app purchasing. The idea is that applications can charge for additional functionality (or game levels), content subscriptions, or pay-per-use features.
There are two interesting caveats, though:
…
- An app can only offer in-app purchasing if the app isn’t free.
If you want to charge money for your app, you have to jump through a lot of paperwork-shaped hoops with Apple about tax and other very boring things. The same will obviously be the case for apps that want to offer in-app purchasing. But charging money for an app up-front is part of the iTunes Store process, and is hooked into your developer account. Using the ‘paid content download’ API is part of the developer tools, and is probably very hard to detect without using debugging tools, which aren’t part of the iTunes Store process.
This feels like a legal hack. It’s a short-term way to make sure that developers have done the paperwork required to collect money. Once the problem is solved properly, I’d expect this restriction to be relaxed. Maybe even you’ll be allowed to charge money for downloads if you have any paid-for app in the store, that might be easier to implement first.
I’m guessing that Apple got to shave a chunk of time off the release date of a feature by hacking their own license agreement system.
I guess iPhone push notifications might be annoying. But you can always turn them off. This isn’t what worries me.
The biggest problem with this Twitter/push thing is that we’re not going to get it for Twitter apps any time soon. Obviously, Twitter won’t do it natively, it’ll be left for third parties. So some third party will have to run a server that polls Twitter for your updates, and pushes them to your phone.
This will have scaling issues. The first person to launch this will get all the users (because shiny!!1), and their server will melt. Unless it’s huge.
They will have to charge money for this service. Probably monthly.
The leap from ‘I have written a pretty Twitter client’ to ‘I have to run infrastructure and bill monthly for it’ is huge. Push isn’t just a bullet point feature. It’s almost a harder problem than writing the iPhone app in the first place.
Also you’re now polling Twitter for all of your users all the time. and holding auth credentials for them on your central server. So
Twitter API usage lurches upwards again, because now this service is polling them every 5 mins for every user, and all these users are polling Twitter from their phones (unless the phone client is polling your server for updates, in which case now you have to scale for that as well. At least if the only thing you do is poll/push, it doesn’t matter if you fall over for 5 minutes. or indeed for an hour).
This third party server now has usernames/passwords or oauth tokens for all of their users, rather than these tokens staying only on the client apps. yay security!
Now, Twitter seem to be growing some sort of streaming API. Not sure if this helps. If I have 100,000 users, I hope I don’t have to hold 100,000 simultaneous HTTP connections open to my server, that might be tricky.
Wrote this for work, threw it away again in favour of using an actual gem that someone else will maintain, but I thought I’d put it here anyway, because it might be useful. Also, the gem is written in C and therefore hard to deploy sometimes.
#!/usr/bin/env ruby
# pure-ruby geohash decoding function
# default is the example from http://en.wikipedia.org/wiki/Geohash
geohash = ARGV[0] || "ezs42"
# convert geohash into a bit sequence
map = "0123456789bcdefghjkmnpqrstuvwxyz" # silly custom base32 mapping
bits = geohash.split("").map{|c|
i = map.index(c) or raise("bad geohash (#{c} not permitted)")
sprintf("%05s", i.to_s(2)).gsub(" ","0").split("")
}.flatten
# even bits are longitude, odd bits are latitude.
# probably a better way of doing this part, feels non-ruby-like..
lat_bits = []
lng_bits = []
bits.each_with_index{|b,i|
if i % 2 == 1
lat_bits << b
else
lng_bits << b
end
}
# subdivide the world according to the bit sequences
def decode(bits, range)
range = [ range.to_f * -1, range.to_f ]
for b in bits
if b == "1"
range[0] = (range[0] + range[1])/2
else
range[1] = (range[0] + range[1])/2
end
end
return range
end
lat_range = decode( lat_bits, 90 )
lng_range = decode( lng_bits, 180 )
puts "lat is range #{ lat_range.inspect }"
puts "lng is range #{ lng_range.inspect }"
This is a script I use on a few sites to automatically count the number of delicious links to various urls. There are a few of these that I’ve found, but I quite like writing JavaScript like this. Also, mine will combine as many urls as possible into a single request to the delicious API, because it’s faster that way.
<!-- jquery -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" type="text/javascript"></script>
<!-- this is md5 implemented in javascript -->
<script src="http://pajhome.org.uk/crypt/md5/md5.js" type="text/javascript"></script>
<script type="text/javascript">
$(function() {
// This function gets called per link, this depends on the template.
// In my case, I'm walking up to the surrounding 'post' object, then
// down to a pre-prepared p tag I want to populate with link data.
// You will want to change this if you use this code.
//
// Markup is approximately:
//
// <div class="post">
// <a href="..." class="delicious">..</a>
// <p class="deliciousinfo"></p>
// </div>
function markup_delicious_link( element, data ) {
var html = "<a href='http://delicious.com/url/" + data.hash + "'>";
html += data.total_posts + " delicious link(s)";
html += "</a>";
element.parents(".post").find(".deliciousinfo").html(html);
}
// Find all links with class 'delicious'
var hashes = [];
var elements = {}
$('a.delicious').each(function(index, link) {
if ($(link).attr("href")) { // sanity check
var hash = hex_md5( $(link).attr("href") );
elements[ hash ] = $(link);
hashes.push( "hash=" + hash );
}
});
// Make calls to the delicious feed API to get details
while (hashes.length) {
// delicious permit a maximum of 15 hashes per request
var subsection = hashes.splice(0, 15);
var url = "http://badges.del.icio.us/feeds/json/url/data?";
url += subsection.join("&") + "&callback=?";
$.getJSON( url, function(data) {
for (var i=0; i<data.length; i++) {
var $link = elements[ data[i].hash ];
markup_delicious_link( $link, data[i] );
}
});
}
});
</script>