<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><description></description><title>Chris Eidhof</title><generator>Tumblr (3.0; @chriseidhof)</generator><link>https://chriseidhof.tumblr.com/</link><item><title>Deckset</title><description>&lt;p&gt;One of the things I love is Markdown. Recently, I&amp;rsquo;ve also been giving a lot more presentations. Working in Keynote can be really nice, and is very powerful. But most of the time, I don&amp;rsquo;t need that power.&lt;/p&gt;

&lt;p&gt;Therefore, &lt;a href="http://floriankugler.com"&gt;Florian&lt;/a&gt;, me and our awesome friends at &lt;a href="http://www.acolorbright.com"&gt;A Color Bright&lt;/a&gt; have spent the last months building an app that changes the way you write your presentations. You write a markdown file, separate your slides with dashes, and you get a beautiful presentation out of it. Update your markdown file, and your presentation updates automatically.&lt;/p&gt;

&lt;p&gt;Over the last weeks, we&amp;rsquo;ve given multiple presentations with the app, and it&amp;rsquo;s awesome. For me, it&amp;rsquo;s great that I can just write. Rearranging text is so much easier than rearranging slides. The default themes we have are beautiful by default, and it&amp;rsquo;s really hard to mess it up. Changing to a different theme is immediate: you can do this minutes before the presentation, if you feel like it.&lt;/p&gt;

&lt;p&gt;We are not building a replacement for Keynote or Powerpoint, but a different way of creating presentations. In some use cases, traditional tools are the way to go. But for many of the presentations, we think Deckset is much easier.&lt;/p&gt;

&lt;p&gt;Currently, we&amp;rsquo;re putting the finishing touches on our app, and are getting it ready for private beta. We are planning to launch early 2014. If this sounds interesting to you, go ahead and sign up for the mailing list at &lt;a href="http://www.decksetapp.com"&gt;decksetapp.com&lt;/a&gt;, and we&amp;rsquo;ll keep you posted.&lt;/p&gt;

&lt;p&gt;Have a great start of the year!&lt;/p&gt;</description><link>https://chriseidhof.tumblr.com/post/71339039431</link><guid>https://chriseidhof.tumblr.com/post/71339039431</guid><pubDate>Fri, 27 Dec 2013 21:54:37 +0100</pubDate></item><item><title>2013</title><description>&lt;p&gt;It&amp;rsquo;s the time of the year to write a recap about the things I&amp;rsquo;ve done this year. 2013 has been a really awesome year, maybe my best year so far. I learned tons, worked with really awesome people, made many new friends and travelled a lot.&lt;/p&gt;

&lt;h2&gt;Client work&lt;/h2&gt;

&lt;p&gt;This year, I worked for three big clients (and some smaller ones). The smallest of three was an old employer who wanted a prototype of a new idea (in Ruby on Rails). This prototype evolved into a bigger website, and things got a bit more serious. Because I am not a real Rails developer, and because there was a base level of trust missing, I decided to not continue working on this project. I had written behavior tests using &lt;a href="http://cukes.info"&gt;Cucumber&lt;/a&gt; for the entire project: this was awesome once I decided to transfer it, because the other developer was able to quickly take it over.&lt;/p&gt;

&lt;p&gt;My other two big clients were &lt;a href="http://www.aprendo.nl"&gt;Aprendo&lt;/a&gt; and &lt;a href="http://wearezeta.com"&gt;Zeta&lt;/a&gt;. For Aprendo, we built an in-house app for development of elite athletes. I worked together with &lt;a href="http://floriankugler.com"&gt;Florian&lt;/a&gt; on this, and our collaboration was so nice we decided to make more things together (more on that later). For Zeta, I can&amp;rsquo;t yet tell what I built, but keep an eye on them. It&amp;rsquo;ll be awesome.&lt;/p&gt;

&lt;p&gt;I really liked having few bigger clients (rather than a more smaller clients). I&amp;rsquo;m currently still wide open in 2014 for client projects, if you have a bigger project that needs a serious developer (or a team): get in touch.&lt;/p&gt;

&lt;h2&gt;Conferences and Events&lt;/h2&gt;

&lt;p&gt;This year was the year of the conferences for me. Almost exactly one year ago, I realized that there&amp;rsquo;s no English iOS conference in Berlin, and with all the awesome talent in the city we definitely needed one. So I teamed up with &lt;a href="http://www.thewavingcat.com"&gt;Peter&lt;/a&gt;, &lt;a href="http://werkstatt.io"&gt;Matt&lt;/a&gt; and &lt;a href="http://about.me/max.krueger"&gt;Max&lt;/a&gt;, and we decided to organize &lt;a href="http://www.uikonf.com"&gt;UIKonf&lt;/a&gt;. This was a lot of hard work, a lot of things learned, and went way better than expected. One of my first lessons in how working in a team can make you achieve that much more. In fact, it was so awesome we decided to do it again, tickets for 2014 are now &lt;a href="http://www.uikonf.com"&gt;on sale&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Because the awesome &lt;a href="http://nshipster.com"&gt;NSHipster&lt;/a&gt; was in town, we decided to put on an event together with Mattt, and put on an &lt;a href="http://2013.uikonf.com/2013/03/25/nshipster-quiz-night.html"&gt;NSHipster night&lt;/a&gt;, which turned out to be a great succes.&lt;/p&gt;

&lt;p&gt;Also, after UIKonf, &lt;a href="http://www.solid-perspective-vision.com"&gt;Stijn Oomes&lt;/a&gt; approached me with the idea of doing a &lt;a href="http://2013.uikonf.com/2013/06/11/uikonf-training.html"&gt;3D programming workshop&lt;/a&gt;. This went well, we even had two days of workshops. We went over the basics of 3D programming, and took different approaches in getting 3D scenes on the screen, from high-level with SceneKit to low-level-everything-in-OpenGL.&lt;/p&gt;

&lt;p&gt;Finally, a while ago Apple announced &lt;a href="https://developer.apple.com/tech-talks/"&gt;Tech Talks&lt;/a&gt; in Berlin. After learning that I didn&amp;rsquo;t get a ticket, I decided to put my frustration to good use and started organizing &lt;a href="http://www.alt-tech-talks.com"&gt;Alt Tech Talks&lt;/a&gt;, a free event for everyone who did or did not get a ticket. On Wednesday night I asked on Twitter, on Thursday I had the first 4 speakers confirmed and on Friday I rented a movie theater. The rest isn&amp;rsquo;t history yet, because they&amp;rsquo;ll happen in a few days from now.&lt;/p&gt;

&lt;h3&gt;Talks&lt;/h3&gt;

&lt;p&gt;Because of starting &lt;a href="http://www.objc.io"&gt;objc.io&lt;/a&gt; (more about this later), Florian, &lt;a href="https://twitter.com/danielboedewadt"&gt;Daniel&lt;/a&gt; and me gained a bit of visibility in the iOS and Mac community. And that lead to conferences asking us if we&amp;rsquo;d be interested in speaking. I like speaking, so off we went.&lt;/p&gt;

&lt;p&gt;First, there was &lt;a href="http://nsspain.com/2013"&gt;NSSpain&lt;/a&gt;, which was an English-speaking conference in Logroño. We were quite impressed: it was well organized, great weather, and lots of nice food and wine. I spoke about &lt;a href="https://speakerdeck.com/chriseidhof/lighter-view-controllers"&gt;Lighter View Controllers&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then, the talk I was most afraid of this year: an hour-long presentation in German (!) at &lt;a href="http://www.macoun.de"&gt;Macoun&lt;/a&gt;. Fortunately, my German is finally at a level where people don&amp;rsquo;t have problems understanding me, and with enough preparation, I think my talk came across well. The subject (&lt;a href="https://speakerdeck.com/chriseidhof/schlanke-view-controller"&gt;Schlanke View Controller&lt;/a&gt;) was the the same as in Spain, and it seems to be very relevant for all iOS developers.&lt;/p&gt;

&lt;p&gt;About a month later, I spoke at &lt;a href="http://pragmamark.org"&gt;pragma mark&lt;/a&gt;, an iOS/OS X conference in Milan, Italy. Here, I spoke about &lt;a href="https://speakerdeck.com/chriseidhof/pragma-mark-simple-concurrent-programming"&gt;async programming&lt;/a&gt;, which went okay. It is quite a hard topic to talk about, but luckily, writing &lt;a href="http://www.objc.io/issue-2/common-background-practices.html"&gt;the article for objc.io&lt;/a&gt; forced me to dive deep into the topic, and giving me plenty of raw material for the talk.&lt;/p&gt;

&lt;p&gt;The hardest talk format this year was at &lt;a href="http://igniteberlin.com/category/ignite04/"&gt;Ignite Berlin&lt;/a&gt;; every presentation is 20 slides, and every slide auto-advances every 15 seconds. I prepared this the other way around: normally, I write an outline for a talk, expand that a couple of times and finally make slides. This time, however, I recorded myself telling the story I wanted to tell, and made some slides that loosely reinforced what I wanted to tell.&lt;/p&gt;

&lt;p&gt;I also talked at &lt;a href="https://speakerdeck.com/chriseidhof/parallele-programmierung-de"&gt;CocoaHeads Dresden&lt;/a&gt; and twice at the &lt;a href="http://berlin.compilermeet.org"&gt;Berlin Compiler Meetup&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Other&lt;/h2&gt;

&lt;p&gt;In the beginning of the year, I joined &lt;a href="http://kantberlin.com"&gt;KANT&lt;/a&gt;, a studio full of great people. I spent a couple of months working there, but after being on-site with my client a lot, and having other commitments that didn&amp;rsquo;t allow me to be there a lot, I decided to cancel my desk there. Being among them, I learned to appreciate coffee even more.&lt;/p&gt;

&lt;p&gt;I started lots of small new projects, some succesful, most failed. I often spend a day or two building something (sometimes more). It&amp;rsquo;s a lot of fun, and in doing so I learned a lot. I hope that I can use some of those ideas in future projects.&lt;/p&gt;

&lt;p&gt;Continuing from last year, we try to meet regularly for our German course. With my fellow Dutchies &lt;a href="http://alper.nl/dingen/"&gt;Alper&lt;/a&gt; and &lt;a href="http://iksi.cc"&gt;IKSI&lt;/a&gt; we take lessons from &lt;a href="https://twitter.com/fraulea"&gt;@fraulea&lt;/a&gt;. I will probably spend the next 10 years improving my German while never getting close to being near-native. But that&amp;rsquo;s okay, it&amp;rsquo;s a lot of fun and a beautiful language to discover. For the German speakers that read this: if you ever hear me make mistakes, please correct me.&lt;/p&gt;

&lt;p&gt;After having spent most of the year freelancing, I&amp;rsquo;ve been working on a product together with Florian and &lt;a href="http://www.acolorbright.com"&gt;A Color Bright&lt;/a&gt;. We&amp;rsquo;re not quite ready to tell you what it is, but are getting close to release a private beta towards the end of this year. And hopefully can submit to the store early January. It&amp;rsquo;s going to be a native Mac app (our first) and it&amp;rsquo;s going to be very beautiful. Building your own products is lots of fun, I hope to do a lot more of that in 2014.&lt;/p&gt;

&lt;h2&gt;objc.io&lt;/h2&gt;

&lt;p&gt;The last work-related topic: &lt;a href="http://www.objc.io"&gt;objc.io&lt;/a&gt;. On the morning after UIKonf, on the hackday, I sat outside in the sun, together with Florian, Daniel and &lt;a href="http://petersteinberger.com"&gt;Peter&lt;/a&gt;. Instead of hacking, we were talking and drinking coffee, and I felt a bit guilty about that. Florian, Daniel and me decided that we should write a blog with the three of us. A few days later, I tried to convince them that we should instead make a magazine: one topic each month, and in-depth articles. We asked &lt;a href="http://iksi.cc"&gt;IKSI&lt;/a&gt; to make a beautiful design, and asked &lt;a href="http://deutsch-bitte.net"&gt;Natalye&lt;/a&gt; to do the copy-editing. We started writing and tried to put a lot of effort into the first issue. &lt;a href="http://www.rickigregersen.com"&gt;Ricki&lt;/a&gt; wrote an article too, as our first guest-writer. After a few weeks, Florian and me sat outside on my balcony, and pressed publish. We posted the link on Twitter, waited a bit, and saw a huge stream of compliments coming in.&lt;/p&gt;

&lt;p&gt;Each month, each of us spends a few days researching and writing my article, as well as coordinating the other things. It&amp;rsquo;s a lot of work, especially because we really have to investigate each topic very deeply, as we don&amp;rsquo;t want to spread the wrong ideas. But it&amp;rsquo;s oh so rewarding. The writing and researching in itself make me a lot more knowledgeable. Before writing, I sometimes don&amp;rsquo;t know too much about a topic, and having the article and its deadline force me to learn. It&amp;rsquo;s just great. And of course, getting all the positive reactions from our readers is very flattering.&lt;/p&gt;

&lt;h2&gt;Leisure&lt;/h2&gt;

&lt;p&gt;This year, I visited Spain three times: once for running, once for a conference and the third time to spend some time by myself (I&amp;rsquo;m writing this article from Seville). I learned how to do exercises at the gym, which was so much fun: I never expected that lifting heavy stuff could be fun, but it&amp;rsquo;s great. I ran a new PB on the 10K: 37:03. I ran my first marathon: the &lt;a href="http://www.skogsmaraton.no"&gt;Oslo Forest Marathon&lt;/a&gt;. It was way too hilly for me to run fast, and while the first 27K went great, the last 15 were very hard. I visited friends and family in The Netherlands, but not as much as I wanted. I ran up the &lt;a href="http://en.wikipedia.org/wiki/Brocken"&gt;Brocken&lt;/a&gt; a couple of times. Together with Frank, I walked &lt;a href="http://en.wikipedia.org/wiki/Hadrian's_Wall"&gt;Hadrian&amp;rsquo;s wall&lt;/a&gt;, and spend the week after that running up mountains in the Lake District. I went sailing and camping in Sweden.&lt;/p&gt;

&lt;p&gt;One of my disappointments was not being able to run the Berlin Marathon. I trained for months, aiming to run a fast first road-marathon. However, during the last week of tapering, I got quite sick, and had to stay in bed for a few days. I might have caught a bug, or maybe I trained too hard. It took me a while to recover, and I still haven&amp;rsquo;t gotten back to running 5 times a week.&lt;/p&gt;

&lt;h2&gt;Recap&lt;/h2&gt;

&lt;p&gt;It was awesome. I had a blast. I did work too much, and want to change that next year.&lt;/p&gt;</description><link>https://chriseidhof.tumblr.com/post/69278327963</link><guid>https://chriseidhof.tumblr.com/post/69278327963</guid><pubDate>Sat, 07 Dec 2013 17:47:19 +0100</pubDate></item><item><title>Lightweight Key-Value Observing</title><description>&lt;p&gt;In this article, I&amp;rsquo;d like to implement a simple class I use for
key-value observing. I think KVO is great, however, for most of what I
do, there are two problems:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I don&amp;rsquo;t like the dispatching in &lt;code&gt;observeValueForKeyPath:ofObject:change:context:&lt;/code&gt;. I think it gets messy and confusing if you observe more than one object.&lt;/li&gt;
&lt;li&gt;You have to balance each &lt;em&gt;add observer&lt;/em&gt; with a &lt;em&gt;remove observer&lt;/em&gt;,
it would be nice if this can be done automatically.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, off we go. The trick we will use is one I first saw in
&lt;a href="https://github.com/th-in-gs/THObserversAndBinders"&gt;THObserversAndBinders&lt;/a&gt;,
and this post is basically a description of what they did, but in the
most minimalistic way.&lt;/p&gt;

&lt;p&gt;First, we&amp;rsquo;ll define the interface for our object:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@interface Observer : NSObject
+ (instancetype)observerWithObject:(id)object
                           keyPath:(NSString*)keyPath
                            target:(id)target
                          selector:(SEL)selector;
@end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The observer takes four parameters, which are hopefully self-explanatory. I chose
to use the target/action pattern: an alternative would have been blocks,
but then you would have to do the weakSelf/strongSelf dance, and it&amp;rsquo;s
often nice to have a separate method anyway.&lt;/p&gt;

&lt;p&gt;What we will do is set up the KVO inside the initializer, and remove it
in the &lt;code&gt;dealloc&lt;/code&gt; method. What this means is that as long as the
&lt;code&gt;Observer&lt;/code&gt; object is retained, we will have an observer. The way I
typically use this, for example, in a view controller:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;self.usernameObserver = [Observer observerWithObject:self.user
                                             keyPath:@"name"
                                              target:self
                                            selector:@selector(usernameChanged)];
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;By putting it in a property, we are making sure it gets retained. As
soon as our view controller deallocates, it&amp;rsquo;ll set the property to nil
and the observer will stop observing.&lt;/p&gt;

&lt;p&gt;In the implementation, it&amp;rsquo;s important that we keep a weak reference to
both the observed object and the target. If one of the two gets nil, we
want to stop sending messages:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@interface Observer ()
@property (nonatomic, weak) id target;
@property (nonatomic) SEL selector;
@property (nonatomic, weak) id observedObject;
@property (nonatomic, copy) NSString* keyPath;
@end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The initializer sets up the KVO notifications. It uses &lt;code&gt;self&lt;/code&gt; as the
context. This is necessary if we would ever have a subclass that adds a
similar observer:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- (id)initWithObject:(id)object keyPath:(NSString*)keyPath target:(id)target selector:(SEL)selector
{
  if (self) {
    self.target = target;
    self.selector = selector;
    self.observedObject = object;
    self.keyPath = keyPath;
    [object addObserver:self forKeyPath:keyPath options:0 context:self];
  }
  return self;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When a change happens, we just notify our target, if it still exists:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context
{
  if (context == self) {
    id strongTarget = self.target;
    if ([strongTarget respondsToSelector:self.selector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
      [strongTarget performSelector:self.selector];
#pragma clang diagnostic pop
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And finally, in the &lt;code&gt;dealloc&lt;/code&gt; we remove the observer:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- (void)dealloc
{
    id strongObservedObject = self.observedObject;
    if (strongObservedObject) {
        [strongObservedObject removeObserver:self forKeyPath:self.keyPath];
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That&amp;rsquo;s all there is to it. There are a lot of ways this could be
extended: add blocks support, or my favorite trick: another convenience
constructor that call the action directly the first time. However, I
wanted to show the core of the technique, adjust it to your needs.&lt;/p&gt;

&lt;p&gt;By using this technique you don&amp;rsquo;t have to remember too much when doing KVO.
Just retain the observers, and set them to nil when you&amp;rsquo;re done. The
rest will happen automatically.&lt;/p&gt;</description><link>https://chriseidhof.tumblr.com/post/63590250009</link><guid>https://chriseidhof.tumblr.com/post/63590250009</guid><pubDate>Thu, 10 Oct 2013 00:12:14 +0200</pubDate></item><item><title>Status Update</title><description>&lt;p&gt;Lots of stuff happened over the last months, so here&amp;rsquo;s a partial list:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I finished my project with &lt;a href="http://www.aprendo.nl"&gt;Aprendo&lt;/a&gt;. We&amp;rsquo;ve
been working on an in-house iPad app for almost a year. At this point,
I can&amp;rsquo;t demo anything publicly, but I&amp;rsquo;d be happy to talk about it in
private.&lt;/li&gt;
&lt;li&gt;I organized &lt;a href="http://www.uikonf.com"&gt;UIKonf&lt;/a&gt; together with
&lt;a href="http://twitter.com/peterbihr"&gt;Peter&lt;/a&gt; and
&lt;a href="http://twitter.com/fidothe"&gt;Matt&lt;/a&gt;. I think it&amp;rsquo;s safe to say that
the conference was a resounding success. We learned a lot of things
that we can do better next year, but all in all, we&amp;rsquo;re very happy.&lt;/li&gt;
&lt;li&gt;I started &lt;a href="http://objc.io"&gt;objc.io&lt;/a&gt;, a new online periodical about
best practices and techniques in Objective-C. The response so far
has been very good, and matches the amount of work we&amp;rsquo;ve put in.&lt;/li&gt;
&lt;li&gt;I started work on a small app to track your food intake, based on the
Diet Quality Score system.&lt;/li&gt;
&lt;li&gt;I started planning to build independent apps together with
&lt;a href="http://floriankugler.com"&gt;Florian&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The running is going well, too, I improved my PB on the 10K in bad
circumstances to 37:03, and will run my &lt;a href="http://www.skogsmaraton.no"&gt;first
marathon&lt;/a&gt; this weekend (although I have a
slight injury, so depending on how that feels I might pull out at the
last minute).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Depending on the contracting work I might have more time to blog. I have
a lot of topics in my head, some might appear here and some on
&lt;a href="http://objc.io"&gt;objc.io&lt;/a&gt;.&lt;/p&gt;</description><link>https://chriseidhof.tumblr.com/post/52615792078</link><guid>https://chriseidhof.tumblr.com/post/52615792078</guid><pubDate>Mon, 10 Jun 2013 10:51:21 +0200</pubDate></item><item><title>DONE</title><description>&lt;p&gt;In case you haven&amp;rsquo;t noticed yet, I&amp;rsquo;m organizing an English-speaking iOS
Conference in Berlin called &lt;a href="http://www.uikonf.com"&gt;UIKonf&lt;/a&gt;, together with my
fellow organizers &lt;a href="http://twitter.com/fidothe"&gt;Matt&lt;/a&gt; and
&lt;a href="http://twitter.com/peterbihr"&gt;Peter&lt;/a&gt;. If you are in or around Berlin on May 2nd,
get on the &lt;a href="http://iosinberlin.launchrock.com"&gt;mailing list&lt;/a&gt; and come join us!&lt;/p&gt;

&lt;p&gt;Organizing this conference is really awesome. It started with a question: why doesn&amp;rsquo;t this
conference exist? Then I realized that the only way to make it happen is do it yourself.
Starting something like this feels really empowering: we can definitely have an impact
on our direct environment by just doing stuff. I asked some people that are
more awesome and experienced than myself, then we executed.&lt;/p&gt;

&lt;p&gt;While UIKonf is still a few months away, I am getting better at shipping, and I can
recommend you to do the same. Instead of reading this, write an article. Package
up some software or libraries you have lying around. Organize a meetup.
You&amp;rsquo;ll see that it&amp;rsquo;ll make you happy!&lt;/p&gt;

&lt;p&gt;A good recommendation from my friend &lt;a href="http://alper.nl/dingen/"&gt;Alper&lt;/a&gt; is the &lt;a href="http://www.brepettis.com/blog/2009/3/3/the-cult-of-done-manifesto.html"&gt;Cult
of
Done&lt;/a&gt;
manifesto. Take a minute or two to read it, and then get back at building stuff
and shipping things!&lt;/p&gt;</description><link>https://chriseidhof.tumblr.com/post/44000163392</link><guid>https://chriseidhof.tumblr.com/post/44000163392</guid><pubDate>Mon, 25 Feb 2013 21:01:45 +0100</pubDate></item><item><title>How to keep your classes small and clean</title><description>&lt;p&gt;I&amp;rsquo;m a little obsessed with trying to write maintainable, clean code. I&amp;rsquo;m not
yet really good at it, but try to become better every day. It started to become
more of an obsession than usual when I realized that a lot of the projects I
work on will someday be taken over by other people, and I don&amp;rsquo;t want to be embarassed.&lt;/p&gt;

&lt;p&gt;This explains my recent interest in testing (&lt;a href="http://iosunittesting.com/"&gt;some&lt;/a&gt;
&lt;a href="http://blog.securemacprogramming.com/"&gt;interesting&lt;/a&gt;
&lt;a href="http://qualitycoding.org/"&gt;blogs&lt;/a&gt; on that).&lt;/p&gt;

&lt;p&gt;Although I&amp;rsquo;m not yet very good at testing everything, I have two other heuristics
that I use to keep my code maintainable: I try to keep my &lt;code&gt;.m&lt;/code&gt; files under a hundred lines (this is hard), and 
try to keep the number of imports small.&lt;/p&gt;

&lt;p&gt;Here are two scripts that I use to achieve this. The first one shows the &lt;code&gt;.m&lt;/code&gt; files
in your project with their line counts, and sorts them by line count. The bottom of the list
(largest files) are top candidates for refactoring:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;find . -path './Pods' -prune -o -name "*.m" -exec wc -l "{}" \; | sort -n
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The other script I wrote generates a &lt;code&gt;.dot&lt;/code&gt; file from your imports, and you can
open it with an app like &lt;a href="http://www.graphviz.org"&gt;GraphViz&lt;/a&gt; to get a quick overview of how your imports are.
(I tried to keep it under 140 characters so it&amp;rsquo;s tweetable).&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;echo "digraph G {";grep "import \"" **/*.m|sed "s/.m:#import \"/ /;s/.*\///;s/+/_/g;s/.h\"//"|awk "{print \$2,\"-&amp;gt; \"\$1\";\"}";echo "}"
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Put the above line in a file or in a shell alias, run it and pipe the output to
a file that you can open with GraphViz. I&amp;rsquo;m pretty sure this can be done even more effectively using just &lt;code&gt;awk&lt;/code&gt;, but
I&amp;rsquo;m not an expert yet. Bonus points for the shortest solution!&lt;/p&gt;</description><link>https://chriseidhof.tumblr.com/post/43925321331</link><guid>https://chriseidhof.tumblr.com/post/43925321331</guid><pubDate>Sun, 24 Feb 2013 22:40:00 +0100</pubDate></item><item><title>New Things</title><description>&lt;p&gt;Over Christmas, I read &lt;a href="http://www.amazon.de/Antifragile-Things-That-Gain-Disorder/dp/1400067820"&gt;Antifragile&lt;/a&gt; by Taleb. The book has had quite an impact on me, and I decided to change some things. Long story short: I want to be open to positive black swans. A positive black swan is an event that will happen with a low probability, but if it does happen, it has a big impact.&lt;/p&gt;

&lt;p&gt;Taleb&amp;rsquo;s strategy for investing is to invest mostly in very safe things, and
make small bets with a limited downside, but an unlimited upside. He calls this
the dumbbell strategy.  Applied to my own business as a freelance software
developer, this means that I will be continuing and expanding my  freelancing
business for most of the time.  But I am also spending time on projects that
have a limited cost, but an unlimited upside. I&amp;rsquo;m not expecting that most of
these projects will be a wild success, but you only have to win big once&amp;hellip;&lt;/p&gt;

&lt;p&gt;One of these projects is organizing a conference. This will definitely not have an unlimited financial upside (best case, we might make a little bit of money, but it would be way more effective to spend that time freelancing). However, I do think that very nice things will happen because of this: meet more people, get known a bit better in the tech scene, and work together with really interesting people.&lt;/p&gt;

&lt;p&gt;Also, I have a renewed focus on building and maintaining my own apps. I&amp;rsquo;m working on an update for &lt;a href="http://eidhof.nl/trackme"&gt;TrackMe&lt;/a&gt; (I know it needs a better website and UI). I have some other interesting ideas for new apps too, hopefully I can move some of these projects forward so they can be released publicly.&lt;/p&gt;

&lt;p&gt;On a personal level, I am planning to go to more parties. This is directly copied from Taleb. At parties, you have a chance of meeting very interesting people. Probably at most parties you&amp;rsquo;ll have a good time, but won&amp;rsquo;t meet anybody new who&amp;rsquo;s interesting. However, when you do, all the other parties will have been worth it.&lt;/p&gt;

&lt;p&gt;Also, I want to work together with more people. One of the ways this will happen is having an office instead of working from home. Another way will be by hiring more people. If you&amp;rsquo;re interested in learning iOS Development, or if you&amp;rsquo;re a copywriter, get in touch.&lt;/p&gt;

&lt;p&gt;I wonder how this strategy will work. I see it as something long-term, the payoff might only come in a decade. Let&amp;rsquo;s see.&lt;/p&gt;</description><link>https://chriseidhof.tumblr.com/post/41949318189</link><guid>https://chriseidhof.tumblr.com/post/41949318189</guid><pubDate>Thu, 31 Jan 2013 17:11:45 +0100</pubDate></item><item><title>Lighter UIViewControllers </title><description>&lt;p&gt;Recently, I&amp;rsquo;ve been a bit obsessed with writing simpler code. It
happened because I passed on some projects to other programmers. I would
like to feel minimally embarrassed when doing so.&lt;/p&gt;

&lt;p&gt;In the projects I&amp;rsquo;ve taken over from other people, one of the most
common problems is that view controller code is big and complicated.
In this article, we&amp;rsquo;ll look at some ways to make the view controller
smaller and thus simpler. In a project I&amp;rsquo;m working on currently, we have
about 150 classes, and almost none of them is larger than 100 lines. The
ones that are larger than that are on the top of my refactoring list.&lt;/p&gt;

&lt;h1&gt;Separate out UITableViewDataSource and UITableViewDelegate&lt;/h1&gt;

&lt;p&gt;One of the first things I often do is create a separate class for the
tableView&amp;rsquo;s data source and delegate. Just create a new class, take all
the methods that start with &lt;code&gt;tableView:&lt;/code&gt; and put them in there. The
compiler will then show you which things are missing in that class (e.g.
instance variables), and you pass them on. Then you can remove them from
the view controller.&lt;/p&gt;

&lt;h1&gt;Don&amp;rsquo;t do domain logic&lt;/h1&gt;

&lt;p&gt;Most of my apps are backed by a Core Data stack (but this is true for any
kind of data store). I Try to do as little domain logic as possible. For
Core Data, my favorite trick is to add a category to the generated
models where I put my logic. For example, instead of doing this in your
view controller:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- (Department*)getRootDepartment:(Department*)department {
  if(department.parent == nil) return department;
  return [self getRootDepartment:department.parent];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Do this in &lt;code&gt;Department+Extensions.m&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- (Department*)rootDepartment {
  if(self.parent == nil) return self;
  return [self rootDepartment];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Even for small methods like this, it helps a lot to keep your view
controllers small. You also have the added benefit that it&amp;rsquo;s much easier
to reuse this code.&lt;/p&gt;

&lt;h1&gt;Don&amp;rsquo;t do webservice logic&lt;/h1&gt;

&lt;p&gt;Don&amp;rsquo;t call and parse a webservice request in your view controller.&lt;/p&gt;

&lt;p&gt;I always create a separate set of classes that do the dealing with
webservices. It depends how complicated your webservice / persistency
is, but I mostly have a class called &lt;code&gt;Webservice&lt;/code&gt; that gives you
high-level methods to call the webservice. Either the methods accept
callbacks or I use KVO (more about that later). It uses the &amp;ldquo;fire and
forget&amp;rdquo; approach: you tell the webservice that you want something and
then forget, assume it will get back to you at some point.&lt;/p&gt;

&lt;p&gt;The webservice class calls the API and turns the API results into domain
objects. This functionality is often achieved by multiple classes that
are used by the webservice, but that&amp;rsquo;s a topic for a later post.&lt;/p&gt;

&lt;h1&gt;Don&amp;rsquo;t create complicated view hierarchies&lt;/h1&gt;

&lt;p&gt;Don&amp;rsquo;t set up complicated view hierarchies in your view controller.
Personally, I&amp;rsquo;m a big fan of designing my views in a nib/xib/storyboard.
But even if you don&amp;rsquo;t, I think it&amp;rsquo;s better to compose several views into
a new view. For example, if you want a date slider, it&amp;rsquo;s better to
create the view hierarchy for that (the labels at the axes, a label
representing the current date and the UISlider) in a separate class.&lt;/p&gt;

&lt;h1&gt;Create child view controllers&lt;/h1&gt;

&lt;p&gt;Since iOS 5, it&amp;rsquo;s very easy to create child view controllers. Use this
functionality. It makes your life easier. This is mostly useful for iPad
apps, but might be useful for iPhone apps as well. A classical example
of this is the split view, but in any screen where you have components
that are almost independent it might be useful.&lt;/p&gt;

&lt;h1&gt;Use KVO&lt;/h1&gt;

&lt;p&gt;Key value observing can make your life a lot easier. It&amp;rsquo;s also a bit
tricky sometimes, so you do need to spend some time to learn it, and you
will probably make some mistakes (I still get tripped up every now and then).&lt;/p&gt;

&lt;p&gt;One example
where KVO really shines is the &lt;code&gt;NSFetchedResultsController&lt;/code&gt;. It listens
to changes in core data objects and can be used to implement a
&lt;code&gt;UITableViewDataSource&lt;/code&gt; or &lt;code&gt;UICollectionViewDataSource&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, suppose you have a list of
&lt;code&gt;Departments&lt;/code&gt;. Then you want to refresh that list from the API. In your
view controller, you fire a off a request like &lt;code&gt;[webservice
reloadDepartments]&lt;/code&gt; and then forget. Once the webservice updates the
Core Data managed objects, magic happens: Because you implemented your
&lt;code&gt;UITableViewDataSource&lt;/code&gt; using an &lt;code&gt;NSFetchedResultsController&lt;/code&gt;, it gets
refreshed automatically.&lt;/p&gt;

&lt;p&gt;KVO by itself can be a bit hairy sometimes, and there are small
libraries like
&lt;a href="https://github.com/th-in-gs/THObserversAndBinders"&gt;THObserversAndBinders&lt;/a&gt;
that make things easier.&lt;/p&gt;

&lt;h1&gt;Other ideas?&lt;/h1&gt;

&lt;p&gt;This is just a list of things that I could come up with and common
refactorings I use. I&amp;rsquo;m sure there are a lot of things I forgot, let me
know your tips and I&amp;rsquo;ll add them to the article.&lt;/p&gt;</description><link>https://chriseidhof.tumblr.com/post/38139449686</link><guid>https://chriseidhof.tumblr.com/post/38139449686</guid><pubDate>Mon, 17 Dec 2012 11:45:47 +0100</pubDate></item><item><title>Implementing Value Objects in Objective C </title><description>&lt;p&gt;&lt;a href="http://en.wikipedia.org/wiki/Value_object"&gt;Value Objects&lt;/a&gt; are objects that hold simple data. This article is about creating such value objects. I use them a lot in my code, because they are robust and keep the code simple. Note that it&amp;rsquo;s not about &lt;code&gt;NSValue&lt;/code&gt;, but about simple objects with simple data.&lt;/p&gt;

&lt;p&gt;Implementing value objects should be easy, but there are some slightly
tricky bits. So let&amp;rsquo;s look at the requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We want to create value objects quickly (i.e. an &lt;code&gt;initWith:&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The created objects should be immutable&lt;/li&gt;
&lt;li&gt;The created objects should be equal when they have equal values&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Suppose we want to create &lt;code&gt;Person&lt;/code&gt; objects with properties &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;birthDate&lt;/code&gt;, then our interface looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@interface Person : NSObject
- (id)initWithName:(NSString*)name birthDate:(NSDate*)birthDate;
@property (nonatomic, copy, readonly) NSString* name;
@property (nonatomic, strong, readonly) NSDate* birthDate;
@end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The important thing to notice here is that the properties are
&lt;code&gt;readonly&lt;/code&gt;. However, the modern runtime still generates instance
variables for us, that are prefixed by a &lt;code&gt;_&lt;/code&gt;. Our implementation looks
like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@implementation Person

- (id)initWithName:(NSString*)name birthDate:(NSDate*)birthDate {
  self = [super init];
  if(self) {
    _name = [name copy];
    _birthDate = birthDate;
  }
  return self;
}
@end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In the modern runtime, you don&amp;rsquo;t have to use &lt;code&gt;synthesize&lt;/code&gt;. If you do,
then your instance variables get different names (without the
underscore).&lt;/p&gt;

&lt;p&gt;Now for the equality, we implement the method &lt;code&gt;isEqual:&lt;/code&gt;. There is an
&lt;a href="http://www.mikeash.com/pyblog/friday-qa-2010-06-18-implementing-equality-and-hashing.html"&gt;excellent article&lt;/a&gt; by Mike Ash, however, there is a mistake in there. Following his advice, our first (incorrect) implementation looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- (BOOL)isEqual:(id)obj {
  if(![obj isKindOfClass:[Person class]]) return NO;

  Person* other = (Person*)obj;
  BOOL nameIsEqual = [_name isEqual:other-&amp;gt;_name];
  BOOL dateIsEqual = [_date isEqual:other-&amp;gt;_date];
  return nameIsEqual &amp;amp;&amp;amp; dateIsEqual;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;There is a problem here: if one of the two properties is &lt;code&gt;nil&lt;/code&gt;, then
&lt;code&gt;isEqual:&lt;/code&gt; will return &lt;code&gt;NO&lt;/code&gt;. This is because methods sent to &lt;code&gt;nil&lt;/code&gt;
always return &lt;code&gt;NO&lt;/code&gt;, &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;nil&lt;/code&gt;. Even &lt;code&gt;[nil isEqual:nil]&lt;/code&gt; returns &lt;code&gt;NO&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Therefore, our second, correct implementation looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- (BOOL)isEqual:(id)obj {
  if(![obj isKindOfClass:[Person class]]) return NO;

  Person* other = (Person*)obj;
  BOOL nameIsEqual = _name == _other-&amp;gt;_name || [_name isEqual:other-&amp;gt;_name];
  BOOL dateIsEqual = _date == _other-&amp;gt;_date || [_date isEqual:other-&amp;gt;_date];
  return nameIsEqual &amp;amp;&amp;amp; dateIsEqual;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To implement the hashing function, I would like to recommend following Mike&amp;rsquo;s advice.&lt;/p&gt;

&lt;h1&gt;Bonus&lt;/h1&gt;

&lt;p&gt;Finally, as a bonus, let&amp;rsquo;s also implement &lt;code&gt;NSCoding&lt;/code&gt;, so we can
serialize our objects. First change the interface of &lt;code&gt;Person&lt;/code&gt; to this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;@interface Person : NSObject &amp;lt;NSCoding&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The implementation is now very simple:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- (id)initWithCoder:(NSCoder*)aDecoder {
  self = [super init];
  if(self) {
    _name = [aDecoder decodeObjectForKey:kName]; 
    _date = [aDecoder decodeObjectForKey:kDate]; 
  }
  return self;
}

- (void)encodeWithCoder:(NSCoder*)aCoder {
  [aCoder encodeObject:_name forKey:kName];
  [aCoder encodeObject:_date forKey:kDate];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The two constants &lt;code&gt;kName&lt;/code&gt; and &lt;code&gt;kDate&lt;/code&gt; are declared in the implementation
file, above the &lt;code&gt;@implementation&lt;/code&gt; directive:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;static NSString* const kName = @"name";
static NSString* const kDate = @"date";
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Voila, now we can create objects, read their properties, serialize them
to disk and read them back in. Some catches: when you add a new
property, you have to make sure to update the code in lots of places:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add the property to the interface file&lt;/li&gt;
&lt;li&gt;Add the parameter to &lt;code&gt;initWith:&lt;/code&gt;, and also update the callers of that
method&lt;/li&gt;
&lt;li&gt;Add a comparison to &lt;code&gt;isEqual:&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Update the &lt;code&gt;hash&lt;/code&gt; function&lt;/li&gt;
&lt;li&gt;Add the method to both &lt;code&gt;initWithCoder:&lt;/code&gt; and &lt;code&gt;encodeWithCoder:&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It helps to have some tests in place that check this for you.&lt;/p&gt;

&lt;p&gt;The full code of the examples (without the &lt;code&gt;hash&lt;/code&gt; function) is &lt;a href="https://gist.github.com/78b3ce0edbcdf0d202e2"&gt;on github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update&lt;/em&gt;: changed the &lt;code&gt;name&lt;/code&gt; attribute to be &lt;code&gt;copy&lt;/code&gt; instead of &lt;code&gt;strong&lt;/code&gt;, &lt;a href="https://twitter.com/CocoaPimper/status/280335607971074050"&gt;thanks to Christian Kienle&lt;/a&gt;&lt;/p&gt;</description><link>https://chriseidhof.tumblr.com/post/38065014421</link><guid>https://chriseidhof.tumblr.com/post/38065014421</guid><pubDate>Sun, 16 Dec 2012 16:22:00 +0100</pubDate></item><item><title>How to add a new Unit Test target and OCMock to an existing XCode project </title><description>&lt;p&gt;I had some trouble today trying to add Unit Tests to an existing project that uses &lt;a href="http://cocoapods.org"&gt;CocoaPods&lt;/a&gt;. So I decided to do a quick writeup of how I got it working:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a new project called &amp;lsquo;BetterTodoList&amp;rsquo;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a new Podfile with the following contents&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;pre&gt;&lt;code&gt;platform :ios, 6.0
pod 'AFNetworking'&lt;/code&gt;&lt;/pre&gt;

&lt;ol start="3"&gt;
&lt;li&gt;&lt;p&gt;Close BetterTodoList in XCode, and open the workspace (as instructed by CocoaPods)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Run, and see that everything still works&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Write some legacy code&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Realize you need Unit Tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;File -&amp;gt; New -&amp;gt; Target&lt;/li&gt;
&lt;li&gt;Choose Other -&amp;gt; Cocoa Touch Unit Test Bundle&lt;/li&gt;
&lt;li&gt;Name it &amp;ldquo;BetterTodoListTests&amp;rdquo;&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Press Cmd+U. If nothing happens, check if Product &amp;gt; Test is grayed out in the menu. In that case, press Cmd+&amp;lt;, select Test and add the test using the + button.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add the following lines to your Podfile, and re-run &amp;ldquo;pod install&amp;rdquo;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;pre&gt;&lt;code&gt;target 'BetterTodoListTests', :exclusive =&amp;gt; true do
    pod 'OCMock' 
  end&lt;/code&gt;&lt;/pre&gt;

&lt;ol start="9"&gt;
&lt;li&gt;&lt;p&gt;Import &lt;code&gt;OCMock.h&lt;/code&gt; in one of your files and see that it&amp;rsquo;s working.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Profit&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Addendum: If you still have problems because the test target cannot access the compiled objects (e.g. &amp;ldquo;Symbol Not Found&amp;rdquo; errors), select the project in the organizer, then select the Test target, go to &amp;ldquo;Bundle Loader&amp;rdquo; and add the following values for both Debug and Release: &lt;code&gt;$(BUILT_PRODUCTS_DIR)/BetterTodoList.app/BetterTodoList&lt;/code&gt;&lt;/p&gt;</description><link>https://chriseidhof.tumblr.com/post/37128203165</link><guid>https://chriseidhof.tumblr.com/post/37128203165</guid><pubDate>Mon, 03 Dec 2012 21:09:00 +0100</pubDate></item><item><title>My presentation at the Berlin HUG</title><description>&lt;p&gt;Last week I gave a short presentation about lenses and &lt;a href="http://hackage.haskell.org/package/fclabels"&gt;fclabels&lt;/a&gt; at the Berlin Haskell Users Group. The source for the slides and some accompanying literate Haskell files are now in a &lt;a href="https://github.com/chriseidhof/presentation-lenses"&gt;github repo&lt;/a&gt;.&lt;/p&gt;</description><link>https://chriseidhof.tumblr.com/post/34822267972</link><guid>https://chriseidhof.tumblr.com/post/34822267972</guid><pubDate>Fri, 02 Nov 2012 11:28:06 +0100</pubDate></item><item><title>Adding push notifications to an app</title><description>&lt;p&gt;Last week, I&amp;rsquo;ve built an app that&amp;rsquo;s quite simple: there are categories, and in the categories are messages. People can subscribe to a category and get a push notification when there&amp;rsquo;s a new message in the category, or when a significant change was made to the message. The badge icon should update appropriately, showing the number of unread messages. There are lots of tricky things when implementing this, and the point of this article is to explore them.&lt;/p&gt;

&lt;p&gt;In a naive approach, you can start by implement the application delegate&amp;rsquo;s &lt;code&gt;application:didReceiveRemoteNotification&lt;/code&gt;. This method is called when you receive a push notification. However, it is only called when your application is running. When your application is not running, this method doesn&amp;rsquo;t get called. You also need to implement &lt;code&gt;application:didFinishLaunchingWithOptions:&lt;/code&gt;, and check if there is a push notification in the options (this dictionary gets set when you open the app by clicking on the notification).&lt;/p&gt;

&lt;p&gt;The problem, however, is that not all push notifications arrive. For example, if you get 5 push notifications, but open the app via the home screen, you will not get any notification in the options dictionary, and you won&amp;rsquo;t get a &lt;code&gt;didReceiveRemoteNotification&lt;/code&gt; either.&lt;/p&gt;

&lt;p&gt;To do push notifications correctly, you will need to keep a list on the server, and on application startup your app needs to synchronise with the server. That way, you can mark the right and also tell the server that you have seen the push notification.&lt;/p&gt;

&lt;p&gt;Now, when you exit the app you can set the badge count based on the number of unread push notification. However, the server needs to know this number as well, so that it can correctly set the badge number in the next push notification that is sent. When you read a message that was marked by the push notification, you need to tell the server as well. (Edge-case: when you read the message in offline mode, you will need to tell the server once you&amp;rsquo;re back online).&lt;/p&gt;

&lt;p&gt;In order to do push notifications right, the client and server need to communicate a lot. Hopefully, in your next project, you can read this article so you don&amp;rsquo;t have to find these things out while you are programming.&lt;/p&gt;

&lt;p&gt;In a next article, I will sketch the application architecture I used for the app above.&lt;/p&gt;</description><link>https://chriseidhof.tumblr.com/post/34762419310</link><guid>https://chriseidhof.tumblr.com/post/34762419310</guid><pubDate>Thu, 01 Nov 2012 16:20:26 +0100</pubDate></item><item><title>More Interface Builder</title><description>&lt;p&gt;When I started iPhone programming, I built most of my interfaces in
code. It was much easier: full control over your interface. Sometimes I
would start in Interface Builder, but once I stumbled upon limitations I
would rewrite the whole thing in code.&lt;/p&gt;

&lt;p&gt;After learning some more things about Interface Builder, I started using
it more and more. These days, I try to do almost every UI in Interface
Builder. Apple has made it a lot easier for us, too: designing your
table view cells in Interface Builder was quite easy already, but is even
easier with Storyboards.&lt;/p&gt;

&lt;p&gt;Here&amp;rsquo;s one example where I used to revert to code, but now use nibs. One
of the projects I worked on required a horizontally paging scrollview.
Each page was for answering a question, and contained a description and
a slider. If you want to play with the example, please download &lt;a href="https://github.com/chriseidhof/interface-builder-test"&gt;the
project&lt;/a&gt; from
github. Let&amp;rsquo;s see how to do this using mostly IB:&lt;/p&gt;

&lt;p&gt;Start by creating a view controller and adding a scroll view to it.
Create an outlet scrollView on the view controller.
Then, create a new empty XIB. We add a single UIView to the XIB
with nested subviews (the text label and the slider). Then, in the view
controller, add a method to setup the pages and call it from
&lt;code&gt;viewDidLoad&lt;/code&gt;. Note that the method is a bit long, normally you would
probably factor it into components:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;- (void)setupPages {
    pages = [NSMutableArray arrayWithCapacity:NUM_PAGES];

    CGFloat pageWidth = self.scrollView.bounds.size.width;
    CGRect pageFrame = self.scrollView.bounds;

    self.scrollView.contentSize = CGSizeMake(pageWidth*NUM_PAGES, self.scrollView.bounds.size.height);

    for(int i = 0; i &amp;lt; NUM_PAGES; i++) {
        NSArray* topLevelObjects = [[NSBundle mainBundle] loadNibNamed:pageNibName owner:nil options:nil];
        UIView* pageView = [topLevelObjects lastObject];
        pageFrame.origin.x = pageWidth * i;
        pageView.frame = pageFrame;
        [self.scrollView addSubview:pageView];
        [pages addObject:pageView];
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;For this code to work, it&amp;rsquo;s important that there is only one view at the
top-level of the xib.  The above code creates the pages and adds them to
the subviews. However, the separate pages don&amp;rsquo;t have a way to access the
label and slider. A crude way to do this is give both of them a tag, and
use something like &lt;code&gt;[pageView viewWithTag:LABEL_TAG]&lt;/code&gt;. Indeed, for small
views this can work, but it gets messy and hard to maintain quickly.&lt;/p&gt;

&lt;p&gt;A better way is to add a new subclass of UIView, named &lt;code&gt;CEPageView&lt;/code&gt; and
change the root-element of PageView.xib to that &lt;code&gt;UIView&lt;/code&gt; subclass in the
Identity Inspector. If you open the Assistant Editor, you can create
outlets on that newly created class and connect them to the XIB. Note
that normally, you would drag from a view to the File&amp;rsquo;s Owner, now you
drag to the root element in the Top Level Objects. Change the body of
the for-loop to this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    NSArray* topLevelObjects = [[NSBundle mainBundle] loadNibNamed:pageNibName owner:nil options:nil];
    CEPageView* pageView = [topLevelObjects lastObject]; // change
    pageFrame.origin.x = pageWidth * i;
    pageView.frame = pageFrame;
    [self.scrollView addSubview:pageView];
    [pages addObject:pageView];
    // Add these lines:
    pageView.title.text = [NSString stringWithFormat:@"Page %d", i+1];
    pageView.slider.value = i / 3.0;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can also override the layoutSubviews of the UIView if you want some
programmatic control over the layout.&lt;/p&gt;

&lt;p&gt;I hope that this technique will make you more productive. I&amp;rsquo;m no expert
on Interface Builder, so if you have any tips let me know!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Update&lt;/em&gt;: Ole Begemann recommends using UINib instead of &lt;code&gt;loadNibNamed:owner&lt;/code&gt;, it should be faster when loading a nib multiple times. I haven&amp;rsquo;t tested it yet, but will check it out. See also: &lt;a href="http://oncocoa.blogspot.de/2011/02/nsbundle-vs-uinib-performance.html"&gt;NSBundle vs. UINib performance
&lt;/a&gt;&lt;/p&gt;</description><link>https://chriseidhof.tumblr.com/post/32141364792</link><guid>https://chriseidhof.tumblr.com/post/32141364792</guid><pubDate>Sun, 23 Sep 2012 21:07:00 +0200</pubDate></item><item><title>Traveling alone</title><description>&lt;p&gt;I like traveling alone. Here are some things I found out when traveling alone:&lt;/p&gt;

&lt;p&gt;Eating alone in restaurants is fine. I used to really dislike it, because I felt stared at. It got on my nerves. Once I went for mexican food in a Denver restaurant on the tech campus, and on Saturday night the restaurant was filled with families. I was seated in the middle of the room, and had a really big table just for me. This was quite uncomfortable, but I&amp;rsquo;ve gotten used to it now, and it&amp;rsquo;s very refreshing. As a personal rule, I try not to use my phone when having dinner (either with friends or alone). If I do want to do something while waiting for my meal, I&amp;rsquo;ll read a book or write in a notebook.&lt;/p&gt;

&lt;p&gt;The freedom of traveling alone is great. Especially when you&amp;rsquo;re backpacking: once you hit the road you can go wherever you want to go. Many songs have been sung about this feeling, and many books have been written. If you&amp;rsquo;ve never experienced it: do it!&lt;/p&gt;

&lt;p&gt;For some reason, I used to feel the pressure to socialize. However, it&amp;rsquo;s fine not to do that. Most of the time, I like to meet people and talk to them. However, I also like spending a day sipping café con leche and eating tapas without really talking to somebody. It clears my mind.&lt;/p&gt;

&lt;p&gt;The way you travel and the places you stay can also have an important effect on socializing: I prefer to use public transport. In a rental car, you can be pretty sure to not meet anybody. Likewise, in a hostel it&amp;rsquo;s much easier to meet new people than in a hotel. And joining organized activities is a sure way to meet new people: even if you&amp;rsquo;re really picky, if you meet enough people you&amp;rsquo;re bound to meet somebody you really like. I made some great friends over the years by joining walking tours or pub crawls.&lt;/p&gt;

&lt;p&gt;There is the obligatory quote: &amp;ldquo;happiness is only real when shared&amp;rdquo;, which is very true. Traveling with friends is just as awesome. However, you&amp;rsquo;ll meet people along the way with who you will share the moments. For me, it&amp;rsquo;s easier to meet people when I&amp;rsquo;m traveling alone than when I&amp;rsquo;m traveling with friends: with friends, I tend to isolate a bit more, and don&amp;rsquo;t make many new friends.&lt;/p&gt;

&lt;p&gt;Safe travels!&lt;/p&gt;</description><link>https://chriseidhof.tumblr.com/post/28042435218</link><guid>https://chriseidhof.tumblr.com/post/28042435218</guid><pubDate>Thu, 26 Jul 2012 09:30:47 +0200</pubDate></item><item><title>Finding My Most Influential Twitter Followers</title><description>&lt;p&gt;I spent an afternoon trying to find out who of my Twitter followers is the most influential. I wrote the script to fetch the data in Ruby, and stored the data in MongoDB.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, we&amp;rsquo;ll need to set up some things. Make sure you have a recent version of Ruby installed, and install the following gems:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;gem install twitter mongo bson_ext
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Although the &lt;code&gt;bson_ext&lt;/code&gt; isn&amp;rsquo;t really necessary, it will make things a bit faster.&lt;/p&gt;

&lt;p&gt;Also install mongodb. One of the easier ways to do this is using &lt;a href="http://mxcl.github.com/homebrew/"&gt;homebrew&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;brew install mongodb
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Make sure to fire up mongodb, and we&amp;rsquo;re all set on the client.&lt;/p&gt;

&lt;p&gt;The last step is to get an API key at Twitter. Go to &lt;a href="https://dev.twitter.com/"&gt;https://dev.twitter.com/&lt;/a&gt; and create a new application. Don&amp;rsquo;t worry about the details too much.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fetching a list of Twitter follower IDs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First, we will write a file called &lt;code&gt;twitter-config.rb&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Twitter.configure do |config|
  config.consumer_key = 'CONSUMER_KEY'
  config.consumer_secret = 'CONSUMER_SECRET'
  config.oauth_token = 'OAUTH_TOKEN'
  config.oauth_token_secret = 'O_AUTH_TOKEN_SECRET'
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Make sure to replace the right-hand sides by the appropriate values (they should match the twitter application you just created).&lt;/p&gt;

&lt;p&gt;The script for fetching your Twitter follower ids is now extremely simple:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'rubygems'
require 'twitter'
require 'twitter-config.rb'

follower_ids = Twitter.follower_ids
File.open("followers", 'w') { |f| 
  f.write(follower_ids.collection.join("\n")) 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We are just using the Twitter API to fetch the follower ids and store them in a file. The last part is essential when working with APIs and larger datasets: make sure you store intermediate results. Often, computations can take a long time, and by storing intermediate results you can resume where you&amp;rsquo;ve left off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Write the script to fetch the data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Again, this script is very simple. We iterate over the list of followers, and make the corresponding Twitter API call. One thing to note here is that you will hit the rate limit if you have more than 350 followers. Therefore, the script checks if you already have fetched the data for a follower, and skips fetching the data. If you hit the rate limit, simply wait some minutes and run the script again. Eventually, it will finish.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'rubygems'
require 'twitter'
require 'database-config.rb'
require 'twitter-config.rb'

File.open("followers").each_line {|follower_id_s| 
  follower_id = follower_id_s.to_i

  if COLL.find("id" =&amp;gt; follower_id).count &amp;gt; 0 then
    puts "Already have data for #{follower_id}"
  else
    puts "Fetching data for #{follower_id}"
    follower_hash = Twitter.user(follower_id).attrs
    COLL.insert follower_hash
  end
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The &lt;code&gt;database-config.rb&lt;/code&gt; file looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'mongo'

connection = Mongo::Connection.new
db = Mongo::Connection.new.db("twitterinsight")
COLL = db.collection("followers")
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It sets up a global variable &lt;code&gt;COLL&lt;/code&gt; that you can use in the scripts for analyzing and populating the database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Analyze the data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once you have filled your database with data, you can analyze it by running queries on it. The reason I wrote this code is because I wanted to know who my most influential followers were:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;require 'rubygems'
require 'database-config.rb'

COLL.find().
  sort(['followers_count', :desc]).
  limit(50).
  to_a.map { |x| 
  ratio = (100 * (x['friends_count'].to_f / 
                x['followers_count'].to_f)).to_i
  statuses = x['statuses_count']
  puts "#{x['screen_name']} - " +
       "#{x['followers_count']} / #{x['friends_count']}" +
       ", #{ratio} [#{statuses}] @ #{x['location']}" 
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;As you can see, this script prints the screen name, number of followers, number of friends, the
friends/followers ratio, the amount they tweet and the location. Here&amp;rsquo;s some example output:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;copumpkin - 22397 / 368, 1 [3597] @ Boston, MA
DotSauce - 15641 / 12400, 79 [7377] @ NC, USA
dominiek - 14487 / 1085, 7 [5342] @ AMS, SFO, TYO
TivoliUtrecht - 10935 / 442, 4 [3950] @ Utrecht, The Netherlands
theSeanCook - 10006 / 1137, 11 [2345] @ San Francisco
Duetschpire - 8194 / 584, 7 [2112] @ Australia
Polledemaagt - 7518 / 2563, 34 [37721] @ Gent, London, Amsterdam
NetBlueWeb - 6651 / 5520, 82 [9] @ British Columbia
andreisavu - 6648 / 6497, 97 [11496] @ Bucharest, Romania
StackMob - 5571 / 1835, 32 [1015] @ SF
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The major takeaway for this post is to cache all intermediate data. First make sure you have all the data you wanted to analyze in one place on your computer (or your cluster). Optionally massage it into a format you want, and then run the analysis without hitting external servers all the time.&lt;/p&gt;</description><link>https://chriseidhof.tumblr.com/post/23990176111</link><guid>https://chriseidhof.tumblr.com/post/23990176111</guid><pubDate>Tue, 29 May 2012 12:03:00 +0200</pubDate></item><item><title>Changing Tech Education</title><description>&lt;p&gt;Sometimes I talk to my friends about why I want to become rich. It&amp;rsquo;s to solve the money problem, and once it&amp;rsquo;s solved, I hope to have the time, connections  and status to work on bigger problems. I already know exactly what I want to try once I&amp;rsquo;m there: change tech education.&lt;/p&gt;

&lt;p&gt;When I did my bachelor&amp;rsquo;s degree in computer science, I was quite bored most of the time. Officially, you were supposed to work on it at least 40 hours per week, but for me and most of my friends it was closer to 10 hours. The other time we would spend working, or having a hangover.&lt;/p&gt;

&lt;p&gt;One of the causes why I didn&amp;rsquo;t have to work so hard at university is the different levels of experience. When I started, I already had a few years of building websites under my belt. For example: I worked as the main developer at one of biggest soccer sites, and at the same time I had to take a course on the basics of HTML. Obviously, this disconnect was a problem for me, and for a lot of other motivated students around me. I want to solve this problem.&lt;/p&gt;

&lt;p&gt;My solution is a program similar to a bachelor&amp;rsquo;s degree in length, but a lot more intense. In order to enter this program, you have to show your aptitude at programming. The program will have teachers from both academia and industry, because I think both theory and practice are very important. Only the best students and teachers can participate. Students have to study theory, but will also spent a lot of time doing internships and real projects. They will learn standard theory, but also topics such as node.js, statistics for big data analysis, native mobile programming, HTML5, git internals, et cetera. For each batch of students, I want to get financing from big companies who then get access to the talent.&lt;/p&gt;

&lt;p&gt;To pull this off will be a huge and hard job, but I look forward to it. I&amp;rsquo;m still in the exploratory phase, and not sure whether I will actually do it, but at this moment in time I&amp;rsquo;m talking with people in both industry and academia to see whether it&amp;rsquo;s feasible. Instead of a bachelor&amp;rsquo;s program, a very good first step might be to organize a Hacker&amp;rsquo;s Summer School: 6 weeks of learning and hacking together with other talented hackers and awesome teachers.&lt;/p&gt;

&lt;p&gt;Part of me thinks I&amp;rsquo;m too young, inexperienced and not connected enough to do this. But I still think I should at least explore whether it&amp;rsquo;s possible and should try. I have no idea in which form exactly. I don&amp;rsquo;t know what the topics are going to be, who the teachers are or even what the team should look like. I&amp;rsquo;ll keep you posted!&lt;/p&gt;</description><link>https://chriseidhof.tumblr.com/post/23157023687</link><guid>https://chriseidhof.tumblr.com/post/23157023687</guid><pubDate>Wed, 16 May 2012 10:28:42 +0200</pubDate></item><item><title>Radical Friendship</title><description>&lt;p&gt;A few months ago I wrote about &lt;a href="http://chris.eidhof.nl/post/14911964453/radical-honesty"&gt;radical honesty&lt;/a&gt;. I tried being more honest, but in practice it&amp;rsquo;s quite awkward. Our society isn&amp;rsquo;t really used to being completely honest and open. For example, if a friend wears something I really dislike, I won&amp;rsquo;t tell him. It&amp;rsquo;s just my opinion, and by expressing it I don&amp;rsquo;t think I&amp;rsquo;ll help anybody.&lt;/p&gt;

&lt;p&gt;In &lt;a href="http://en.wikipedia.org/wiki/How_to_Win_Friends_and_Influence_People"&gt;How To Win Friends and Influence People&lt;/a&gt;, Dale Carnegie says things like &amp;ldquo;
&amp;quot;Don&amp;rsquo;t criticize, condemn or complain&amp;rdquo;, &amp;ldquo;The only way to get the best of an argument is to avoid it&amp;rdquo; and &amp;ldquo;Show respect for the other person&amp;rsquo;s opinions. Never say, &amp;lsquo;You&amp;rsquo;re wrong.&amp;rsquo;&amp;rdquo;. In a way, you could interpret these statements as: don&amp;rsquo;t be honest when you disagree with somebody.&lt;/p&gt;

&lt;p&gt;Recently, I was reading &lt;a href="http://www.amazon.com/How-Proust-Change-Your-Life/dp/0679779159"&gt;How Proust Can Change Your Life&lt;/a&gt; by my favorite modern philosopher Alain de Botton. He writes that Proust deliberately tried to avoid truth in friendship, lavishing his friends with compliments, sincere or not. This is pretty much the exact opposite of radical honesty, and equally exciting for me. A very interesting approach, although I don&amp;rsquo;t think I&amp;rsquo;ll use it.&lt;/p&gt;</description><link>https://chriseidhof.tumblr.com/post/23100394298</link><guid>https://chriseidhof.tumblr.com/post/23100394298</guid><pubDate>Tue, 15 May 2012 14:23:48 +0200</pubDate></item><item><title>Starting new projects</title><description>&lt;p&gt;One of the most exciting things for me is starting new projects. Each time I start to build a new app, I get way too excited and see rainbows and ponies everywhere. &amp;ldquo;This time it&amp;rsquo;s going to beautiful, easy, quick to finish and lots of fun&amp;rdquo;. There is a blank slate and in my mind everything fits together perfectly.&lt;/p&gt;

&lt;p&gt;However, in reality, after I&amp;rsquo;ve spent some time on a project, I start to see the design mistakes I made. Sometimes I even realize that the product/project itself isn&amp;rsquo;t a good idea. The longer I work on something, the harder it is to finish it. It definitely helps to work with a team, as you can pull each other through the hard times. When working with clients, it&amp;rsquo;s a lot easier to push yourself to finish something.&lt;/p&gt;

&lt;p&gt;I might have found a way to utilize the starting of new projects in order to be more productive. In an &lt;a href="http://chris.eidhof.nl/post/9624114465/lots-of-test-projects"&gt;earlier post&lt;/a&gt;, I wrote about how I create lots of test projects. I still do this, and sometimes when I am not looking forward to implementing some new feature, I instead start implementing it in a blank slate test project. As an additional advantage, the new code will probably be nicely isolated and loosely coupled.&lt;/p&gt;

&lt;p&gt;Anyway, to get back to starting new projects: I think I will struggle with this throughout the rest of my life. It will always be more attractive to start something new than to finish something. However, realizing this problem is the first step in combatting it. And by using tricks like the one above I think it could even be bent to an advantage.&lt;/p&gt;</description><link>https://chriseidhof.tumblr.com/post/21905960830</link><guid>https://chriseidhof.tumblr.com/post/21905960830</guid><pubDate>Fri, 27 Apr 2012 12:06:52 +0200</pubDate></item><item><title>Berlin Tech Meetups</title><description>&lt;p&gt;I&amp;rsquo;m trying to come up with a list of tech-related meetups in Berlin. This list
is far from complete: please help me to add more meetups, and improve the
descriptions. Just &lt;a href="mailto:chris@eidhof.nl"&gt;send me an email&lt;/a&gt; with corrections and/or suggestions.  A next step would be to create a Google Calendar just like Peter
Robinett did for &lt;a href="http://www.bubblefoundry.com/dutch-tech-meetups/"&gt;Dutch Tech
Meetups&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For people who don&amp;rsquo;t speak German: Germans are very friendly and speak
English, especially the techies. Sometimes the announcements, webpages or conversations are in German, but Germans are very polite and quickly switch to English when
necessary.&lt;/p&gt;

&lt;p&gt;Here&amp;rsquo;s the preliminary list I&amp;rsquo;ve come up with:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.meetup.com/berlin-gtug/"&gt;Berlin Google Technology User Group&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We meet regularly every 6 -  8 weeks to talk about recent developments in Google
technologies or to spread practical knowledge through presentations. Next
meeting: March 26th.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.meetup.com/Berlin-HTML5-User-Group/"&gt;Berlin HTML5 User Group&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This group was created to gather the HTML5 community in Berlin. Meetups/Webinars will be organized every month once moment we will have a certain number of people. All HTML5, CSS or Javascript enthusiasts (Developers, designers, webmasters&amp;hellip;) are welcome to join.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.meetup.com/The-Berlin-Semantic-Web-Meetup-Group/events/56299712/"&gt;The Berlin Semantic Web Meetup Group&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Meet local people interested in standardized and future (Semantic Web) Internet technologies aimed at turning the Web into a huge repository of cross-referenced, machine-understandable data and executable artifacts, processes and services.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.meetup.com/Berlin-CouchDB-Meetup/"&gt;CouchDB User Group&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We talk about everything couchdb, its future, our future, related technology and (our) projects connected to it.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://berlindevops.org/"&gt;Berlin DevOps&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The group aims to bring Berlin’s system administrators, system engineers, system developers, web operators, software developers, test engineers, software architects and alike together and create a media for information and knowledge exchange.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.agile42.com/blog/categories/scrumtisch/"&gt;Scrumtisch Berlin&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Talking about everything scrum and agile.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.jug-bb.de/"&gt;Java User Group Berlin Brandenburg&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Talk about Java.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://cocoaheads-berlin.github.com/"&gt;CocoaHeads Berlin&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CocoaHeads is a group devoted to discussion of Apple Computer&amp;rsquo;s Cocoa Framework for programming on MacOS X (including the iPhone).&lt;/p&gt;

&lt;p&gt;&lt;a href="http://groups.google.com/group/erlang-berlin"&gt;Erlang Berlin&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Talk about everything Erlang.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.meetup.com/Clojure-Berlin/"&gt;Clojure User Group Berlin&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Clojure User Group Berlin is a get together of people interested in Clojure&amp;ndash;a Lisp dialect for the JVM. Come and discuss what you&amp;rsquo;re working on, have questions, and meet other like-minded people.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://djangode.pbworks.com/w/page/4861687/Django%20User%20Group%20Berlin"&gt;Django User Group
Berlin&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wir sind eine Gruppe von 10 bis 20 Personen, die sich regelmäßig monatlich zusammenfinden, um über Django und die Welt zu philosophieren. Wir freuen uns über jeden, der Spaß an Django hat oder sich dafür interessiert!&lt;/p&gt;

&lt;p&gt;&lt;a href="http://wiki.fsfe.org/groups/Berlin"&gt;FSFE Berlin&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Wir sind eine aktive Gruppe von Fellows der Free Software Foundation Europe und Freie-Software-Interessierte. Wir machen auf die gesellschaftlichen Aspekte Freier Software aufmerksam.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://berlinjs.org/"&gt;Berlin.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Berlin.JS is a usergroup focused on Javascript and related topics. We meet regularly on the 3rd Thursday each month at 7p.m. at co.up Offices, Adalbertstraße 7-8 in Berlin-Kreuzberg.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.meetup.com/b-tech/"&gt;Berlin Tech Meetup&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every month we will have 4-10 companies do a short (3-5 min.) demo of something cool to Berlin&amp;rsquo;s tech community (geeks, investors, entrepreneurs, hackers, etc). We&amp;rsquo;ve seen it in New York and want to bring it to Berlin.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.facebook.com/siliconallee?sk=events"&gt;Silicon Allee Breakfast Meetup&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;German tech news in English - Berlin-based Community of Entrepreneurs. Not just
tech, but all kinds of people involved in the startup scene.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.meetup.com/Scala-Berlin-Brandenburg/"&gt;Scala Meetup&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Talk, Demo, Hack, Breathe, and just about anything else - Scala. In and around Berlin.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Credits&lt;/em&gt;: I&amp;rsquo;ve added the meetups that Isabel wrote about a few months ago: &lt;a href="http://blog.isabel-drost.de/index.php/archives/344/berlin-tech-meetups"&gt;Berlin Tech Meetups&lt;/a&gt;.&lt;/p&gt;</description><link>https://chriseidhof.tumblr.com/post/19948332455</link><guid>https://chriseidhof.tumblr.com/post/19948332455</guid><pubDate>Mon, 26 Mar 2012 12:07:29 +0200</pubDate></item><item><title>Entitled</title><description>&lt;p&gt;This post is inspired by Marco&amp;rsquo;s excellent piece: &lt;a href="http://www.marco.org/2012/02/25/right-vs-pragmatic"&gt;Right versus pragmatic&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I grew up in a small village. When I was 13, I didn&amp;rsquo;t really have money to buy
music, but I still saved up and went to the record store so I could buy
&lt;a href="http://en.wikipedia.org/wiki/Americana_(The_Offspring_album)"&gt;Americana&lt;/a&gt;. When
I was 15, I got my first computer-job (as a php programmer). One month, I even made 1000
guilders (which was a lot for me). On Saturdays, I would cycle to Germany with
my brother to buy CD&amp;rsquo;s at the big electronics store just across the border. For about two years, I bought a couple of CD&amp;rsquo;s per week.&lt;/p&gt;

&lt;p&gt;However, around that time, my musical taste started developing to slightly more
unknown bands, and the easiest way for me to get the music was by downloading it
(I vividly remember using Kazaa and Napster, those were the days). The nearest
record store where they had a decent selection of music was a 2 hour bike ride,
which was not really an option.&lt;/p&gt;

&lt;p&gt;For movies, it was the same. Once bandwidth started increasing, it just became
easier to download movies. At the movie rental, they did not have the kind of
movies I wanted to watch. I would buy them sometimes, but more often than not I
decided that I wanted to see a movie after dinner, and downloading it enabled me to watch it
the same night, rather than the next day (or even a few days later, if I had to
order it online). TV shows generally lagged behind a whole season, if they would
even broadcast them at all.&lt;/p&gt;

&lt;p&gt;Pirating music, movies and also software became second nature. I didn&amp;rsquo;t think
twice about it. Money was never the reason, it&amp;rsquo;s just that it&amp;rsquo;s so much easier
to download something off of the Pirate Bay than order it online. However, after
doing this throughout my student time, money did start to matter. Once you&amp;rsquo;re
really accustomed to getting something for free, you start to feel entitled to
getting it for free. And of course, virtually all my peers did it.&lt;/p&gt;

&lt;p&gt;After graduating, I hardly ever used any pirated software, except for a
certain graphics editing application. I still pirated music and movies (by the
way, in The Netherlands you are allowed to download music and movies).&lt;/p&gt;

&lt;p&gt;Recently, I had some hardware problems with my hard drive. For the first time in
ever, I did a clean install. I copied back my old files, but not the pirated
music, movies or software. My laptop is 100% free of pirated content. It feels
good. My mindset has changed too: I realized I am not entitled to download it
for free.&lt;/p&gt;

&lt;p&gt;I think there are lots of opportunities for entrepreneurs because of this
problem, especially in Europe. Spotify is one example, but we Europeans want to
watch movies and the latest TV shows too. At this moment, I will refrain from watching movies that are not in the cinema or available in local stores.&lt;/p&gt;

&lt;p&gt;An affordable graphics editor is definitely an opportunity too.  Or maybe some
subscription-based business models: there are some expensive applications that I
would like to use for half an hour each month. For example, about once a year a
client&amp;rsquo;s designer sends me a Fireworks file. The only thing I want to do with it
is slice it up and produce some png&amp;rsquo;s.  Instead of asking the designer to do it
for me, I would rather rent an hour of Fireworks: it saves time and energy for
both me and the designer, and Adobe would make more money. Everybody would
win.&lt;/p&gt;</description><link>https://chriseidhof.tumblr.com/post/18373294261</link><guid>https://chriseidhof.tumblr.com/post/18373294261</guid><pubDate>Mon, 27 Feb 2012 09:55:18 +0100</pubDate></item></channel></rss>
