<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>nanoANT</title>
	<atom:link href="http://www.nanoant.com/feed" rel="self" type="application/rss+xml" />
	<link>http://www.nanoant.com</link>
	<description>Yet another self-employee site &#38; blog</description>
	<lastBuildDate>Sun, 22 Apr 2012 18:33:52 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	
		<item>
		<title>LiveChat for Mac &amp; iOS behind the scenes</title>
		<link>http://www.nanoant.com/programming/livechat-for-mac-ios-behind-the-scenes</link>
		<comments>http://www.nanoant.com/programming/livechat-for-mac-ios-behind-the-scenes#comments</comments>
		<pubDate>Thu, 09 Feb 2012 12:05:17 +0000</pubDate>
		<dc:creator>Adam</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[C/C++]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[Mac OS X]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://www.nanoant.com/?p=709</guid>
		<description><![CDATA[The purpose of this article is to present several technical design concepts implemented and proven to work well for over one year, since the first release of the Mac version of LiveChat operator application. Back in 2010 when I started implementing the Mac version, the main task was to reach the feature level of the Windows version (developed [...]]]></description>
			<content:encoded><![CDATA[<p>The purpose of this article is to present several technical design concepts implemented and proven to work well for over one year, since the first release of the <a href="http://www.livechatinc.com/product/livechat-for-mac/"><em>Mac</em> version of <em>LiveChat</em></a> operator application.</p>
<p>Back in 2010 when I started implementing the <em>Mac</em> version, the main task was to reach the feature level of the <em>Windows</em> version (developed for several years) as fast as possible, and also to prepare ground for an upcoming <em>iOS</em> version (<a href="http://itunes.apple.com/us/app/livechat/id402466708?mt=8">available now in <em>App Store</em></a>). At that time I neither had access to the <em>Windows</em> version&#8217;s source code, nor was going to reuse any of it. Therefore I have settled the following constraints that yielded the following results (described in detail in following sections):</p>
<p style="text-align: center;"><img title="Architecture" src="http://www.nanoant.com/wp/wp-content/uploads/2012/02/Architecture.png" alt="" width="539" height="523" /></p>
<p><span id="more-709"></span></p>
<p><strong>(1)</strong> Source code must be divided into a framework part and GUI part, where the framework part should contain most of the application logic and use API available on both <em>Mac</em> &amp; <em>iOS</em> platforms, while the GUI part should be reduced to a minimum. Altogether it should match <em>Model-View-Controller</em> architecture.</p>
<ul>
<li><em>Protocol.framework</em> is now 60% of both platforms total source code, manages server communication and models such as <em>LCVisitor</em> or <em>LCChat.</em></li>
<li><em>Protocol.framework</em> does not use any platform specific API and relies only on <em>Foundation.framework.</em></li>
<li><em>Mac</em> &amp; <em>iOS</em> use separate source code for views &amp; controllers driving GUI, however these contain almost no application logic.</li>
</ul>
<p><strong>(2)</strong> Source code should be self-describing and object-oriented, utilizing (where possible) dynamic facilities of <em>Objective-C</em>.</p>
<ul>
<li>All model classes visible to controllers are <em>Objective-C</em> classes, controllers have no direct access to protocol.</li>
<li>Application logic talks with the server through an OO proxy object using dynamic message dispatch to translate messages back and forth into text protocol commands. This makes the protocol layer really small in comparison to the whole application logic.</li>
</ul>
<h2><em>Protocol.framework</em></h2>
<p><em>Protocol.framework</em> contains most of the application logic, manages creating and releasing model objects of <em><tt>LCVisitor</tt></em>, <em><tt>LCOperator</tt></em> &amp; <em><tt>LCChat</tt></em> classes, and supplies GUI controllers with them. It contains <em><tt>LCServer</tt></em> private class which is responsible for &#8220;message to protocol command mapping&#8221;. Applications&#8217; controllers are delegates of <em><tt>LCAccount</tt></em> class.</p>
<p><em>Mac</em> version may dynamically follow model object&#8217;s state updates via <em>Cocoa</em> bindings (not available for <em>iOS</em>) without a need to manually process delegate methods. Therefore, for example inspector pane uses only bindings to update its contents, without actually knowing that it is part of complicated program logic. Inspector view contains just a layout and names for model properties presented to the user.</p>
<p style="text-align: center;"><img class="size-full wp-image-726 aligncenter" title="Inspector" src="http://www.nanoant.com/wp/wp-content/uploads/2012/02/Inspector.png" alt="" width="334" height="415" /></p>
<h4>Strengths:</h4>
<ul>
<li>Putting whole application logic into <em>Protocol.framework</em> makes platform dependent GUI code reduced to minimum and responsible only for seamless translation of logical objects into GUI views.</li>
<li><em>Protocol.framework</em> relies only on <em>Foundation.framework</em> that is a OO bridge for <em>CoreFoundation</em> and <em>CFNetwork</em> C libraries. These libraries were <a href="http://opensource.apple.com/source/CF/">published open-source</a> and are known to build on several other platforms than <em>OSX</em> &amp; <em>iOS</em>. Therefore in theory this code is portable to other platforms as well. Unfortunately source code of <em>CFNetwork</em> is <a href="http://opensource.apple.com/source/CFNetwork/">no longer updated</a>. Last available version matches <em>OSX 10.4.11 Tiger</em>. Moreover <em>CoreFoundation</em> on other platforms is stripped from some functionality (named as <em>CFLite</em>) and latest releases have serious build problem on non-OSX platforms. There are several efforts to bring OSX API to other platforms, i.e.: <a href="http://sourceforge.net/projects/opencflite/">OpenCFLite</a> &amp; <a href="http://code.google.com/p/purefoundation/">PureFoundation</a>  fixing and extending CF &amp; CFNetwork sources, <a href="http://cocotron.org/">Cocotron</a> redoing whole Cocoa API.</li>
</ul>
<h4>Weaknesses:</h4>
<ul>
<li>Application must present same logic on all platforms. It is hard to code any exceptions.</li>
<li>Reusing <em>Platform.framework</em> to other platforms is possible in theory but problematic in practice. If all <em>LiveChat</em> applications were to use single protocol framework, C++ would be a better choice.</li>
</ul>
<h2>Server OO Proxy</h2>
<p>While <em><tt>LCAccount</tt></em> translates messages received into model objects, <em><tt>LCServer</tt></em> class is responsible for translating textual protocol commands to <em>Objective-C</em> messages sent and received by <tt>LCAccount</tt>. <em><tt>LCServer</tt></em> class implementation is really small in comparison to other classes.</p>
<p>Once <em><tt>LCAccount</tt> </em>wants to initiate server communication using <em><tt>LCServer</tt></em>, it needs first to provide protocol command to selector maps: <strong>(1)</strong> incoming map mapping to <em><tt>LCAccount</tt></em> methods, and <strong>(2)</strong> outgoing map mapping to <em><tt>LCOutgoingMethods</tt></em> protocol. <em><tt>LCServer</tt></em> is the lowest level class that works directly with sockets. Without going into details <em>LiveChat</em> protocol resembles MSN and thanks to its linear (non-structural) construction it is possible to implement such mapping.</p>
<h3>Command Reception</h3>
<p>Given that following incoming map is provided:</p>
<pre class="textmate-source"><span class="source source_objc"><span class="storage storage_modifier storage_modifier_c">static</span> LCCommandMapping incomingMap<span class="meta meta_bracketed meta_bracketed_objc">[]</span> = <span class="meta meta_block meta_block_c">{
 …
 <span class="meta meta_block meta_block_c">{ <span class="string string_quoted string_quoted_double string_quoted_double_objc">@"R0004"</span>, <span class="meta meta_selector meta_selector_objc"><span class="storage storage_type storage_type_objc">@selector</span>(<span class="meta meta_selector meta_selector_method-name meta_selector_method-name_objc"><span class="support support_function support_function_any-method support_function_any-method_name-of-parameter support_function_any-method_name-of-parameter_objc">didReceiveFromClientWithIdentifier:message:senderNick:conferenceIdentifier:hidden:</span></span>)</span>}</span>,
 …
 <span class="meta meta_block meta_block_c">{ <span class="constant constant_language constant_language_objc">nil</span> }</span>
}</span>;
</span></pre>
<p>Upon each command reception <em><tt>LCServer</tt></em>, parses <em><tt>NSMethodSignature</tt></em> of mapped method and marshals all incoming command arguments into method arguments doing <em><tt>NSString</tt></em> to argument type conversion.</p>
<p>It supports all basic types such as <tt>BOOL</tt>, <tt>NSInteger</tt> and <tt>NSString</tt>. It also supports <tt>NSArray</tt>s for more advanced callback based commands (beyond the scope of this article). Altogether, calls when receiving following message: <tt>R0004|12341|Hello!|Joe|2134|0&lt;LF&gt;</tt></p>
<pre class="textmate-source"><span class="source source_objc"><span class="meta meta_bracketed meta_bracketed_objc">[account <span class="meta meta_function-call meta_function-call_objc"><span class="support support_function support_function_any-method support_function_any-method_objc">didReceiveFromClientWithIdentifier:</span><span class="constant constant_numeric constant_numeric_c">12341</span> <span class="support support_function support_function_any-method support_function_any-method_name-of-parameter support_function_any-method_name-of-parameter_objc">message:</span><span class="string string_quoted string_quoted_double string_quoted_double_objc">@"Hello!"</span> <span class="support support_function support_function_any-method support_function_any-method_name-of-parameter support_function_any-method_name-of-parameter_objc">senderNick:</span><span class="string string_quoted string_quoted_double string_quoted_double_objc">@"Joe"</span> <span class="support support_function support_function_any-method support_function_any-method_name-of-parameter support_function_any-method_name-of-parameter_objc">conferenceIdentifier:</span><span class="constant constant_numeric constant_numeric_c">2134</span> <span class="support support_function support_function_any-method support_function_any-method_name-of-parameter support_function_any-method_name-of-parameter_objc">hidden:</span><span class="constant constant_language constant_language_objc">NO</span></span>]</span>;</span></pre>
<p>Where called method has the following signature:</p>
<pre class="textmate-source"><span class="source source_objc"><span class="meta meta_implementation meta_implementation_objc"><span class="meta meta_scope meta_scope_implementation meta_scope_implementation_objc"><span class="meta meta_function-with-body meta_function-with-body_objc"><span class="meta meta_function meta_function_objc">- <span class="meta meta_return-type meta_return-type_objc">(<span class="storage storage_type storage_type_c">void</span>)<span class="entity entity_name entity_name_function entity_name_function_objc">didReceiveFromClientWithIdentifier</span></span><span class="meta meta_argument-type meta_argument-type_objc"><span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc">:</span>(<span class="support support_type support_type_cocoa support_type_cocoa_leopard">NSInteger</span>)<span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc">clientIdentifier
</span></span><span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc">                                   message</span><span class="meta meta_argument-type meta_argument-type_objc"><span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc">:</span>(<span class="support support_class support_class_cocoa">NSString</span> *)<span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc">message
</span></span><span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc">                                senderNick</span><span class="meta meta_argument-type meta_argument-type_objc"><span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc">:</span>(<span class="support support_class support_class_cocoa">NSString</span> *)<span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc">senderNick
</span></span><span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc">                      conferenceIdentifier</span><span class="meta meta_argument-type meta_argument-type_objc"><span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc">:</span>(<span class="support support_type support_type_cocoa support_type_cocoa_leopard">NSInteger</span>)<span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc">conferenceIdentifier
</span></span><span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc">                                    hidden</span><span class="meta meta_argument-type meta_argument-type_objc"><span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc">:</span>(<span class="storage storage_type storage_type_objc">BOOL</span>)<span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc">hidden</span></span>
</span></span></span></span></span></pre>
<p>Of course this method is not called directly (like presented above), but in fact <em><tt>LCServer</tt></em>:</p>
<ol>
<li>Constructs proper <tt>NSInvocation</tt> object via <tt>[<em>NSInvocation</em> <strong>invocationWithMethodSignature</strong>:methodSignature]</tt></li>
<li>Does marshaling and type conversion of received string arguments via <tt>[methodSignature <strong>getArgumentTypeAtIndex</strong>:index]</tt>; example for <tt>NSUInteger</tt> argument:
<pre class="textmate-source"><span class="source source_objc"><span class="meta meta_implementation meta_implementation_objc"><span class="meta meta_scope meta_scope_implementation meta_scope_implementation_objc"><span class="meta meta_function-with-body meta_function-with-body_objc"><span class="meta meta_block meta_block_c"><span class="meta meta_block meta_block_c"><span class="meta meta_block meta_block_c"><span class="meta meta_block meta_block_c"><span class="meta meta_block meta_block_c"><span class="meta meta_block meta_block_c"><span class="keyword keyword_control keyword_control_c">if</span>(!<span class="support support_function support_function_C99 support_function_C99_c">strcmp</span>(type, <span class="string string_quoted string_quoted_double string_quoted_double_c">"Q"</span>)) <span class="meta meta_block meta_block_c">{
<span class="support support_type support_type_cocoa support_type_cocoa_leopard">  NSUInteger</span> value = <span class="meta meta_bracketed meta_bracketed_objc">[string <span class="meta meta_function-call meta_function-call_objc"><span class="support support_function support_function_any-method support_function_any-method_objc">integerValue</span></span>]</span>;
<span class="meta meta_bracketed meta_bracketed_objc">  [invocation <span class="meta meta_function-call meta_function-call_objc"><span class="support support_function support_function_any-method support_function_any-method_objc">setArgument:</span>&amp;value <span class="support support_function support_function_any-method support_function_any-method_name-of-parameter support_function_any-method_name-of-parameter_objc">atIndex:</span>index + <span class="constant constant_numeric constant_numeric_c">2</span></span>]</span>;
}</span></span></span></span></span></span></span></span></span></span></span></pre>
</li>
<li>finally calls <tt>[invocation <strong>invoke</strong>]</tt>.</li>
</ol>
<p><em><tt>LCServer</tt></em> also handles situations when there are too many or too few arguments received from the server:</p>
<ol>
<li>In case there are too few, we may be working with an older server and all missing method arguments get <tt>nil</tt>, numeric get <tt>0</tt>.</li>
<li>When there are too many arguments, then we are working with a server more recent than the application. In such case the application ignores extra arguments and emits a warning in the log.</li>
</ol>
<h3>Command Issue</h3>
<p>Sending protocol commands to server is done in an opposite way. This time <em><tt>LCServer</tt></em> acts as OO proxy to <em><tt>LCAccount</tt></em> that provides methods mapped to protocol command and handled via <em>Objective-C</em> message forwarding mechanism implemented by:</p>
<pre class="textmate-source"><span class="source source_objc">- (<span class="support support_class support_class_cocoa">NSMethodSignature</span> *)methodSignatureForSelector:(<span class="storage storage_type storage_type_objc">SEL</span>)selector
<span class="meta meta_block meta_block_c">{
<span class="storage storage_type storage_type_c">  struct</span> objc_method_description mdesc =<span class="meta meta_function-call meta_function-call_c"><span class="support support_function support_function_any-method support_function_any-method_c">protocol_getMethodDescription</span>(</span>outgoingProtocol, selector, <span class="constant constant_language constant_language_objc">YES</span>, <span class="constant constant_language constant_language_objc">YES</span>);
<span class="keyword keyword_control keyword_control_c">  if</span>(mdesc<span class="-access -access_c">.</span><span class="variable variable_other variable_other_dot-access variable_other_dot-access_c">types</span> == <span class="constant constant_language constant_language_c">NULL</span>) <span class="meta meta_block meta_block_c">{
    <span class="keyword keyword_control keyword_control_c">return</span> <span class="constant constant_language constant_language_objc">nil</span>;
  }</span>
  <span class="keyword keyword_control keyword_control_c">return</span> <span class="meta meta_bracketed meta_bracketed_objc">[<span class="support support_class support_class_cocoa">NSMethodSignature</span> <span class="meta meta_function-call meta_function-call_objc"><span class="support support_function support_function_any-method support_function_any-method_objc">signatureWithObjCTypes:</span>mdesc.types</span>]</span>;
}</span>

- (<span class="storage storage_type storage_type_c">void</span>)forwardInvocation:(<span class="support support_class support_class_cocoa">NSInvocation</span> *)invocation
<span class="meta meta_block meta_block_c">{
<span class="support support_class support_class_cocoa">  NSString</span> *commandName = (<span class="support support_class support_class_cocoa">NSString</span> *)<span class="meta meta_function-call meta_function-call_c"><span class="support support_function support_function_any-method support_function_any-method_c">CFDictionaryGetValue</span>(</span>outgoingCommandMap, (<span class="storage storage_modifier storage_modifier_c">const</span> <span class="storage storage_type storage_type_c">void</span> *)invocation<span class="-access -access_c">.</span><span class="variable variable_other variable_other_dot-access variable_other_dot-access_c">selector</span>);
  … <em>converts all arguments to string representation and send command over socket</em>
}</span></span></pre>
<p>Once <em><tt>LCAccount</tt></em> provides <em><tt>LCServer</tt></em> an outgoing map and proxy interface:</p>
<pre class="textmate-source"><span class="source source_objc"><span class="storage storage_modifier storage_modifier_c">static</span> LCCommandMapping outgoingMap<span class="meta meta_bracketed meta_bracketed_objc">[]</span> = <span class="meta meta_block meta_block_c">{
  …
<span class="meta meta_block meta_block_c">  { <span class="string string_quoted string_quoted_double string_quoted_double_objc">@"S0004"</span>, <span class="meta meta_selector meta_selector_objc"><span class="storage storage_type storage_type_objc">@selector</span>(<span class="meta meta_selector meta_selector_method-name meta_selector_method-name_objc"><span class="support support_function support_function_any-method support_function_any-method_name-of-parameter support_function_any-method_name-of-parameter_objc">sendFromIdentifier:toConferenceWithIdentifier:message:messageType:</span></span>)</span> }</span>,
  …
<span class="meta meta_block meta_block_c">  { <span class="constant constant_language constant_language_objc">nil</span> }</span>
}</span></span>;

<span class="meta meta_interface-or-protocol meta_interface-or-protocol_objc"><span class="storage storage_type storage_type_objc">@protocol</span> <span class="entity entity_name entity_name_type entity_name_type_objc">LCOutgoingCommands</span>
<span class="meta meta_scope meta_scope_interface meta_scope_interface_objc">…
<span class="meta meta_function meta_function_objc">- <span class="meta meta_return-type meta_return-type_objc">(<span class="storage storage_type storage_type_c">void</span>)<span class="entity entity_name entity_name_function entity_name_function_objc">sendFromIdentifier</span></span><span class="meta meta_argument-type meta_argument-type_objc"><span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc">:</span>(<span class="support support_type support_type_cocoa support_type_cocoa_leopard">NSInteger</span>)<span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc">senderIdentifier</span></span>
<span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc">toConferenceWithIdentifier</span><span class="meta meta_argument-type meta_argument-type_objc"><span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc">:</span>(<span class="support support_type support_type_cocoa support_type_cocoa_leopard">NSInteger</span>)<span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc">conferenceIdentifier</span></span>
<span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc">                   message</span><span class="meta meta_argument-type meta_argument-type_objc"><span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc">:</span>(<span class="support support_class support_class_cocoa">NSString</span> *)<span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc">message</span></span>
<span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc">                    hidden</span><span class="meta meta_argument-type meta_argument-type_objc"><span class="entity entity_name entity_name_function entity_name_function_name-of-parameter entity_name_function_name-of-parameter_objc">:</span>(<span class="storage storage_type storage_type_objc">BOOL</span>)<span class="variable variable_parameter variable_parameter_function variable_parameter_function_objc">hidden</span></span>;</span>
…
</span><span class="storage storage_type storage_type_objc">@end</span></span></pre>
<p>It can cast internally <em><tt>LCServer</tt></em> to <tt><strong>id</strong>&lt;<em>LCOutgoingCommands</em>&gt;</tt> and send message to <em><tt>LCServer</tt></em>:</p>
<pre class="textmate-source"><span class="source source_objc"><span class="meta meta_bracketed meta_bracketed_objc">[server <span class="meta meta_function-call meta_function-call_objc"><span class="support support_function support_function_any-method support_function_any-method_objc">sendFromIdentifier:</span><span class="constant constant_numeric constant_numeric_c">431</span> <span class="support support_function support_function_any-method support_function_any-method_name-of-parameter support_function_any-method_name-of-parameter_objc">toConferenceWithIdentifier:</span><span class="constant constant_numeric constant_numeric_c">5488</span> <span class="support support_function support_function_any-method support_function_any-method_name-of-parameter support_function_any-method_name-of-parameter_objc">message:</span><span class="string string_quoted string_quoted_double string_quoted_double_objc">@"Hello to you too!"</span> <span class="support support_function support_function_any-method support_function_any-method_name-of-parameter support_function_any-method_name-of-parameter_objc">hidden:</span><span class="constant constant_language constant_language_objc">NO</span></span>]</span>;
</span></pre>
<h4>Strengths:</h4>
<ol>
<li><em>Simplicity:</em> No need to maintain long <tt>switch</tt> &amp; <tt>case</tt> statements.</li>
<li><em>Separation:</em> Only simple <em><tt>LCServer</tt></em> private class has direct access to the protocol.</li>
<li>Argument type casting done automatically at method invocation level.</li>
<li><em>Easy to debug:</em> If program crashes we can deduce command sent by the server from stack backtrace.</li>
</ol>
<h4>Weaknesses:</h4>
<ol>
<li>All protocol commands must follow the same rules for formatting and escaping arguments. It is really problematic to handle any exceptions in this model.</li>
<li>Protocol commands themselves must be linear (non-structural). Some recently added <em>LiveChat</em> protocol commands have structural arguments that must be parsed separately via external parsers, like XML parser for add-on action commands.</li>
</ol>
<h1>Conclusion</h1>
<p>I believe it would not be possible to easily implement solutions described in this article with other API and languages such as <em>C++</em> or <em>Java.</em> Unfortunately I observe that <em>Objective-C</em> dynamic facilities are less and less utilized by recent <em>Mac OS X (Cocoa)</em> and <em>iOS (Mobile Cocoa)</em> releases and newcomer developers. For example <em>iOS (Mobile Cocoa)</em>, which is kind of a rewrite of <em>OSX Cocoa</em> does not implement bindings, like if they were considered deprecated.</p>
<p>Altogether sparse documentation for <em>Objective-C</em> principles, lack of solid examples (for bindings) and finally company politics that intentionally turn this powerful, dynamic language &amp; its APIs into close environment supporting selected products only, deny original ideas of keeping <em>NeXTStep</em> and <em>Objective-C</em> solutions for modern applications running on all platforms.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.nanoant.com/programming/livechat-for-mac-ios-behind-the-scenes/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>iGotuję cooking experience with Ewa Wachowicz</title>
		<link>http://www.nanoant.com/apple/igotuje-cooking-experience</link>
		<comments>http://www.nanoant.com/apple/igotuje-cooking-experience#comments</comments>
		<pubDate>Fri, 26 Nov 2010 15:05:28 +0000</pubDate>
		<dc:creator>Adam</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://www.nanoant.com/?p=669</guid>
		<description><![CDATA[iGotuję created by nanoANT together with Alternative Studio is first Polish application branded by celebrity. Ewa Wachowicz, Miss Polonia 1992 and 3rd Vice-miss World, presents 60 original recipes and 15 movies. Click the icon to get more information and see the UI in action.]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.igotuje.pl/" class="appstore"><img src="http://www.nanoant.com/wp/wp-content/uploads/2010/11/Icon@2x1.png" alt="" title="Icon@2x" width="114" height="114" class="aligncenter size-full wp-image-673" /></a><em>iGotuję</em> created by <em>nanoANT</em> together with <em><a href="http://www.alternativestudio.pl/">Alternative Studio</a></em> is first Polish application branded by celebrity. <a href="http://www.ewawachowicz.pl/">Ewa Wachowicz</a>, Miss Polonia 1992 and 3rd Vice-miss World, presents 60 original recipes and 15 movies. Click the icon to get more information and see the UI in action.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.nanoant.com/apple/igotuje-cooking-experience/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>LiveChat for iPhone now at AppStore</title>
		<link>http://www.nanoant.com/apple/livechat-for-iphone-now-at-appstore</link>
		<comments>http://www.nanoant.com/apple/livechat-for-iphone-now-at-appstore#comments</comments>
		<pubDate>Thu, 18 Nov 2010 21:01:36 +0000</pubDate>
		<dc:creator>Adam</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[App Store]]></category>
		<category><![CDATA[chat]]></category>
		<category><![CDATA[IM]]></category>
		<category><![CDATA[LiveChat]]></category>

		<guid isPermaLink="false">http://www.nanoant.com/?p=657</guid>
		<description><![CDATA[Yesterday yet another nice little app produced by me was accepted into App Store. You may read original release note at LiveChat blog. This app is not just a companion app for desktop LiveChat version, but is full fledged client for iPhone, letting you monitor your website activity live and chat with customers. Of course [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://itunes.apple.com/us/app/livechat/id402466708?mt=8" class="appstore"><img src="http://www.nanoant.com/wp/wp-content/uploads/2010/11/Icon@2x.png" alt="" title="LiveChat for iPhone" width="114" height="114" class="aligncenter size-full wp-image-658" /></a></p>
<p>Yesterday yet another nice little app produced by me was accepted into <em>App Store</em>. You may read <a href="http://blog.livechatinc.com/2010/11/livechat-for-iphone-now-at-appstore/">original release note at <em>LiveChat</em> blog</a>.</p>
<p>This app is not just a companion app for desktop <em>LiveChat</em> version, but is full fledged client for <em>iPhone</em>, letting you monitor your website activity live and chat with customers. Of course this is just a beginning, so expect new features coming soon with the updates.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.nanoant.com/apple/livechat-for-iphone-now-at-appstore/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Streaming thumbnails smoothly using HTTP in your iPhone app</title>
		<link>http://www.nanoant.com/programming/streaming-thumbnails-smoothly-using-http-in-your-iphone-app</link>
		<comments>http://www.nanoant.com/programming/streaming-thumbnails-smoothly-using-http-in-your-iphone-app#comments</comments>
		<pubDate>Wed, 06 Oct 2010 13:44:44 +0000</pubDate>
		<dc:creator>Adam</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[Mac OS X]]></category>
		<category><![CDATA[Programming]]></category>

		<guid isPermaLink="false">http://www.nanoant.com/?p=629</guid>
		<description><![CDATA[Network enabled apps such as news aggregators, blogging clients often load lots of small files, such as thumbnails, trough HTTP connection. One of them is my app &#8211; Kozaczek, that loads thumbnails for every UITableView item when it is about to be displayed. Usually we expect that such application is: (1) responsive (does not block scrolling [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: center;"><img class="size-full wp-image-637  aligncenter" title="HTTP" src="http://www.nanoant.com/wp/wp-content/uploads/2010/10/HTTP-smooth.png" alt="" width="649" height="343" /></p>
<p>Network enabled apps such as news aggregators, blogging clients often load lots of small files, such as thumbnails, trough HTTP connection. One of them is my app &#8211; <a href="/apple/browse-gossips-and-latest-paparazzi-shots-on-your-iphone">Kozaczek</a>, that loads thumbnails for every <tt>UITableView</tt> item when it is about to be displayed. Usually we expect that such application is: <strong>(1)</strong> responsive (does not block scrolling while loading), <strong>(2)</strong> it does load thumbnails almost realtime.</p>
<p>I suppose the first thing everybody tries is calling <tt>[<strong>UIImage</strong> imageWithData:[<strong>NSData</strong> dataWithContentsOfURL:<em>someURL</em>]]</tt>. There is nothing wrong with it, except it make your UI stutter and load images painfully slow. After trying that one may think of using background threads, <tt>NSOperationQueue</tt>, or something like that, but still any modern web browser loads those images much faster when they are embedded into regular web page than your application. Wonder why?</p>
<p><span id="more-629"></span></p>
<h2>Man behind the bar at HTTP server</h2>
<p>HTTP 1.1 introduces feature called persistent connections &#8211; later backported also to HTTP 1.0 in the form of an extra header <tt>Connection: keep-alive</tt> sent by the client. This is the clue of the solution here.</p>
<p>You may think of HTTP server working thread serving you as bartender taking your order and bring back drinks. First your connection is waiting in the queue to the bar, once bartender dispatches all orders of clients in the front of you, he asks for yours. Then (during the connection) you have the bartender (almost) exclusively serving you. Once you disconnect, the bartender focuses his attention on the next client, so if you decide to make another request short while afterwards you need to go back in the queue and wait again for your turn.</p>
<p>Back in HTTP 1.0 times, server was taking only one order, sending back response and disconnecting. But since HTTP 1.1 you may send many orders to the server one after the other (or even parallel). You can also stay connected while you are non sending requests, just in case you want something extra. All modern web browsers follow simple rule of trying to execute all the requests using existing persistent connection while not making more than 2-4 connections to single server.</p>
<p>Thats why putting your thumbnails download in background threads or <tt>NSOperationQueue</tt> does not do any better, but just pollutes remote HTTP server with many connections, that anyway will not be served altogether. Moreover the overall time you gonna spend waiting for response for all requests will be significantly longer than time use with single persistent connection sending requests one-by-one.</p>
<h2>CFNetwork HTTP connection pooling</h2>
<p>It is better then to keep your application single threaded, but instead use good event driven download mechanism based on persistent connections. The first candidate for that is <tt>NSURLConnection</tt> which unfortunately has some problems with keeping persistent connections to HTTP 1.0 servers via <tt>keep-alive</tt> header, even it uses internally <tt>CFNetwork</tt>. This is the reason I will focus on more low level solution directly based on <em>CFNetwork</em>&#8216;s <tt>CFReadStream</tt> object and <tt>CFReadStreamCreateForHTTPRequest</tt> function.</p>
<p><em>CFNetwork</em> documentation states that this framework tries to pool and reuse existing HTTP connections for newly created <tt>CFReadStream</tt> if there is still other <tt>CFReadStream</tt> connected to the same server. This implies two facts: <strong>(1)</strong> <em>CFNetwork</em> does queue all read requests to the same server where possible trough single connection <strong>(2)</strong> if only you keep the <tt>CFReadStream</tt> reference to the server (i.e. using global variable), so every time you open new <tt>CFReadStream</tt>, it will reuse connection kept inside previously used <tt>CFReadStream</tt>.</p>
<p>Below I attach ready to use implementation of <tt>Fetch</tt> class (under MIT-like license) that does all of that for you. All you need to do is to <tt>[<strong>Fetch</strong> fetchURL:<em>someURL</em> delegate:<strong>self</strong> tag:<em>someTagForYourComfort</em>]</tt>. The connection itself does retain <tt>Fetch</tt> instance and releases it once the request if over, so you do not need to retain it yourself. The delegate (also retained in case it is deallocated before request is over) should implement 3 methods:</p>
<pre>- (<strong>void</strong>)fetch:(<strong>Fetch</strong> *)<em>fetch</em> didFailWithError:(NSError *)<em>error</em>;
- (<strong>void</strong>)fetchDidFinishLoading:(<strong>Fetch</strong> *)<em>connection</em>;
- (<strong>void</strong>)fetch:(<strong>Fetch</strong> *)<em>fetch</em> didReceiveStatusCode:(<strong>NSInteger</strong>)<em>statusCode</em> contentLength:(<strong>NSInteger</strong>)<em>contentLength</em>;</pre>
<p><strong>HINT #1</strong>: Once you receive <tt>didReceiveStatusCode</tt>, you should assign <tt>fetch.data</tt> property (retained) some <tt>NSMutableData</tt> instance otherwise it will not store downloaded data anywhere.</p>
<p><strong>HINT #2</strong>: If you wish to cancel <tt>Fetch</tt> request you may use <tt>-[Fetch cancel]</tt>.</p>
<ul class="projects">
<li class="doc"><a href="/download/classes/Fetch.h">Fetch.h</a> downloader class interface</li>
<li class="doc"><a href="/download/classes/Fetch.m">Fetch.m</a> and implementation</li>
</ul>
<p>Since <em>iPhone/iPod Touch</em> is pretty quick to do image decoding in main thread once you get the data, creating image with <tt>[UIImage imageWithData:fetch.data]</tt> in <tt>fetchDidFinishLoading</tt> and binding the image to <tt>UITableView</tt> cell works for me fine.</p>
<p>Implementation part that deserves the attention is initialization part:</p>
<pre class="textmate-source"><span class="source source_objc">- (<span class="storage storage_type storage_type_id storage_type_id_objc">id</span>)initWithURL:(<span class="support support_class support_class_cocoa">NSURL</span> *)_url
         delegate:(<span class="meta meta_id-with-protocol meta_id-with-protocol_objc"><span class="storage storage_type storage_type_objc">id</span><span class="meta meta_protocol-list meta_protocol-list_objc"><span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_begin punctuation_section_scope_begin_objc">&lt;</span>FetchDelegate<span class="punctuation punctuation_section punctuation_section_scope punctuation_section_scope_end punctuation_section_scope_end_objc">&gt;</span></span></span>)_delegate
              tag:(<span class="support support_type support_type_cocoa support_type_cocoa_leopard">NSInteger</span>)_tag
<span class="meta meta_block meta_block_c">{
    <span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> Copy properties
</span>    self<span class="variable variable_other variable_other_dot-access variable_other_dot-access_c">.delegate</span> = _delegate;
    tag = _tag;

    CFHTTPMessageRef request =<span class="meta meta_function-call meta_function-call_c"><span class="punctuation punctuation_whitespace punctuation_whitespace_function-call punctuation_whitespace_function-call_leading punctuation_whitespace_function-call_leading_c"> </span><span class="support support_function support_function_any-method support_function_any-method_c">CFHTTPMessageCreateRequest</span>(</span>
                                                          <span class="constant constant_other constant_other_variable constant_other_variable_mac-classic constant_other_variable_mac-classic_c">kCFAllocatorDefault</span>,
<span class="meta meta_function-call meta_function-call_c"><span class="punctuation punctuation_whitespace punctuation_whitespace_function-call punctuation_whitespace_function-call_leading punctuation_whitespace_function-call_leading_c">                                                          </span><span class="support support_function support_function_any-method support_function_any-method_c">CFSTR</span>(</span><span class="string string_quoted string_quoted_double string_quoted_double_c"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_c">"</span>GET<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_c">"</span></span>),
                                                          (CFURLRef)_url,
                                                          <span class="constant constant_other constant_other_variable constant_other_variable_mac-classic constant_other_variable_mac-classic_c">kCFHTTPVersion1_1</span>);
<span class="meta meta_function-call meta_function-call_c"><span class="punctuation punctuation_whitespace punctuation_whitespace_function-call punctuation_whitespace_function-call_leading punctuation_whitespace_function-call_leading_c">    </span><span class="support support_function support_function_any-method support_function_any-method_c">CFHTTPMessageSetHeaderFieldValue</span>(</span>request,<span class="meta meta_function-call meta_function-call_c"><span class="punctuation punctuation_whitespace punctuation_whitespace_function-call punctuation_whitespace_function-call_leading punctuation_whitespace_function-call_leading_c"> </span><span class="support support_function support_function_any-method support_function_any-method_c">CFSTR</span>(</span><span class="string string_quoted string_quoted_double string_quoted_double_c"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_c">"</span>Keep-Alive<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_c">"</span></span>),<span class="meta meta_function-call meta_function-call_c"><span class="punctuation punctuation_whitespace punctuation_whitespace_function-call punctuation_whitespace_function-call_leading punctuation_whitespace_function-call_leading_c"> </span><span class="support support_function support_function_any-method support_function_any-method_c">CFSTR</span>(</span><span class="string string_quoted string_quoted_double string_quoted_double_c"><span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_begin punctuation_definition_string_begin_c">"</span>30<span class="punctuation punctuation_definition punctuation_definition_string punctuation_definition_string_end punctuation_definition_string_end_c">"</span></span>));

    stream =<span class="meta meta_function-call meta_function-call_c"><span class="punctuation punctuation_whitespace punctuation_whitespace_function-call punctuation_whitespace_function-call_leading punctuation_whitespace_function-call_leading_c"> </span><span class="support support_function support_function_any-method support_function_any-method_c">CFReadStreamCreateForHTTPRequest</span>(</span><span class="constant constant_other constant_other_variable constant_other_variable_mac-classic constant_other_variable_mac-classic_c">kCFAllocatorDefault</span>, request);
<span class="meta meta_function-call meta_function-call_c"><span class="punctuation punctuation_whitespace punctuation_whitespace_function-call punctuation_whitespace_function-call_leading punctuation_whitespace_function-call_leading_c">    </span><span class="support support_function support_function_any-method support_function_any-method_c">CFRelease</span>(</span>request);
    CFStreamClientContext context = <span class="meta meta_block meta_block_c">{
        <span class="constant constant_numeric constant_numeric_c">0</span>, (<span class="storage storage_type storage_type_c">void</span> *)self,
        CFClientRetain,
        CFClientRelease,
        CFClientDescribeCopy
    }</span>;
<span class="meta meta_function-call meta_function-call_c"><span class="punctuation punctuation_whitespace punctuation_whitespace_function-call punctuation_whitespace_function-call_leading punctuation_whitespace_function-call_leading_c">    </span><span class="support support_function support_function_any-method support_function_any-method_c">CFReadStreamSetClient</span>(</span>stream,
                          <span class="constant constant_other constant_other_variable constant_other_variable_mac-classic constant_other_variable_mac-classic_c">kCFStreamEventHasBytesAvailable</span> |
                          <span class="constant constant_other constant_other_variable constant_other_variable_mac-classic constant_other_variable_mac-classic_c">kCFStreamEventErrorOccurred</span> |
                          <span class="constant constant_other constant_other_variable constant_other_variable_mac-classic constant_other_variable_mac-classic_c">kCFStreamEventEndEncountered</span>,
                          FetchReadCallBack,
                          &amp;context);
<span class="meta meta_function-call meta_function-call_c"><span class="punctuation punctuation_whitespace punctuation_whitespace_function-call punctuation_whitespace_function-call_leading punctuation_whitespace_function-call_leading_c">    </span><span class="support support_function support_function_any-method support_function_any-method_c">CFReadStreamScheduleWithRunLoop</span>(</span>stream,
<span class="meta meta_function-call meta_function-call_c"><span class="punctuation punctuation_whitespace punctuation_whitespace_function-call punctuation_whitespace_function-call_leading punctuation_whitespace_function-call_leading_c">                                    </span><span class="support support_function support_function_any-method support_function_any-method_c">CFRunLoopGetCurrent</span>(</span>),
                                    <span class="constant constant_other constant_other_variable constant_other_variable_mac-classic constant_other_variable_mac-classic_c">kCFRunLoopCommonModes</span>);

    <span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> In meantime our persistent stream may be closed, check that.
</span>    <span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> If we won't do it, our new stream will raise an error on startup
</span>    <span class="comment comment_line comment_line_double-slash comment_line_double-slash_c++"><span class="punctuation punctuation_definition punctuation_definition_comment punctuation_definition_comment_c">//</span> FIXME: This is a bug in CFNetwork!
</span>    <span class="keyword keyword_control keyword_control_c">if</span>(persistentStream != <span class="constant constant_language constant_language_c">NULL</span>) <span class="meta meta_block meta_block_c">{
        CFStreamStatus status =<span class="meta meta_function-call meta_function-call_c"><span class="punctuation punctuation_whitespace punctuation_whitespace_function-call punctuation_whitespace_function-call_leading punctuation_whitespace_function-call_leading_c"> </span><span class="support support_function support_function_any-method support_function_any-method_c">CFReadStreamGetStatus</span>(</span>persistentStream);
        <span class="keyword keyword_control keyword_control_c">if</span>(status == <span class="constant constant_other constant_other_variable constant_other_variable_mac-classic constant_other_variable_mac-classic_c">kCFStreamStatusNotOpen</span> || status == <span class="constant constant_other constant_other_variable constant_other_variable_mac-classic constant_other_variable_mac-classic_c">kCFStreamStatusClosed</span> || status == <span class="constant constant_other constant_other_variable constant_other_variable_mac-classic constant_other_variable_mac-classic_c">kCFStreamStatusError</span>) <span class="meta meta_block meta_block_c">{
<span class="meta meta_function-call meta_function-call_c"><span class="punctuation punctuation_whitespace punctuation_whitespace_function-call punctuation_whitespace_function-call_leading punctuation_whitespace_function-call_leading_c">            </span><span class="support support_function support_function_any-method support_function_any-method_c">CFReadStreamClose</span>(</span>persistentStream);
<span class="meta meta_function-call meta_function-call_c"><span class="punctuation punctuation_whitespace punctuation_whitespace_function-call punctuation_whitespace_function-call_leading punctuation_whitespace_function-call_leading_c">            </span><span class="support support_function support_function_any-method support_function_any-method_c">CFRelease</span>(</span>persistentStream);
            persistentStream = <span class="constant constant_language constant_language_c">NULL</span>;
        }</span>
    }</span>

<span class="meta meta_function-call meta_function-call_c"><span class="punctuation punctuation_whitespace punctuation_whitespace_function-call punctuation_whitespace_function-call_leading punctuation_whitespace_function-call_leading_c">    </span><span class="support support_function support_function_any-method support_function_any-method_c">CFReadStreamSetProperty</span>(</span>stream, <span class="constant constant_other constant_other_variable constant_other_variable_mac-classic constant_other_variable_mac-classic_c">kCFStreamPropertyHTTPAttemptPersistentConnection</span>, <span class="constant constant_other constant_other_variable constant_other_variable_mac-classic constant_other_variable_mac-classic_c">kCFBooleanTrue</span>);
<span class="meta meta_function-call meta_function-call_c"><span class="punctuation punctuation_whitespace punctuation_whitespace_function-call punctuation_whitespace_function-call_leading punctuation_whitespace_function-call_leading_c">    </span><span class="support support_function support_function_any-method support_function_any-method_c">CFReadStreamOpen</span>(</span>stream);

    <span class="keyword keyword_control keyword_control_c">if</span>(persistentStream != <span class="constant constant_language constant_language_c">NULL</span>) <span class="meta meta_block meta_block_c">{
<span class="meta meta_function-call meta_function-call_c"><span class="punctuation punctuation_whitespace punctuation_whitespace_function-call punctuation_whitespace_function-call_leading punctuation_whitespace_function-call_leading_c">        </span><span class="support support_function support_function_any-method support_function_any-method_c">CFReadStreamClose</span>(</span>persistentStream);
<span class="meta meta_function-call meta_function-call_c"><span class="punctuation punctuation_whitespace punctuation_whitespace_function-call punctuation_whitespace_function-call_leading punctuation_whitespace_function-call_leading_c">        </span><span class="support support_function support_function_any-method support_function_any-method_c">CFRelease</span>(</span>persistentStream);
        persistentStream = <span class="constant constant_language constant_language_c">NULL</span>;
    }</span>

    streamCount++;

    <span class="keyword keyword_control keyword_control_c">return</span> self;
}</span>
</span></pre>
<p>Few notes about initialization part of <tt>Fetch</tt> instance:</p>
<ol>
<li>It forces using 30 seconds <tt>keep-alive</tt> trough <tt>CFHTTPMessageSetHeaderFieldValue</tt></li>
<li>It retains itself using <tt>CFReadStreamSetClient</tt></li>
<li>It uses main thread (current) runloop with <tt>CFReadStreamScheduleWithRunLoop</tt></li>
<li>It maintains <tt>persistentStream</tt> global reference to last used stream, and replaces it by its own connection (so <tt>persistentStream</tt> always points to the last one)</li>
<li>Finally it forces persistent connections, even they may be default (or not?) using <tt>CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanTrue);</tt></li>
</ol>
<p>So this is all folks. Waiting for your comments. I hope you will find this post useful.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.nanoant.com/programming/streaming-thumbnails-smoothly-using-http-in-your-iphone-app/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>nanoANT creates LiveChat for Mac</title>
		<link>http://www.nanoant.com/apple/nanoant-creates-livechat-for-mac</link>
		<comments>http://www.nanoant.com/apple/nanoant-creates-livechat-for-mac#comments</comments>
		<pubDate>Fri, 13 Aug 2010 17:41:12 +0000</pubDate>
		<dc:creator>Adam</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[Mac OS X]]></category>

		<guid isPermaLink="false">http://www.nanoant.com/?p=573</guid>
		<description><![CDATA[Update: Public Beta has been just released for download on LiveChat for Mac page. I am proud to announce my company was hired to create Mac version of LiveChat operator software. The application just entered alpha-phase and today closed tests were started. I hope too that the new refreshing LiveChat icon (on the right) designed [...]]]></description>
			<content:encoded><![CDATA[<p><a class="appstore" href="http://blog.livechatinc.com/2010/07/livechat-for-mac-sneak-peak-1/"><img title="LiveChat Icon" src="http://www.nanoant.com/wp/wp-content/uploads/2010/08/LiveChat-Icon.png" alt="LiveChat Icon" width="128" height="128" /></a><strong>Update:</strong> Public Beta has been just released for download on <a href="http://www.livechatinc.com/en/overview/live-chat-for-mac-os-x/">LiveChat for Mac</a> page.</p>
<p><a class="appstore" href="http://blog.livechatinc.com/2010/07/livechat-for-mac-sneak-peak-1/"></a>I am proud to announce my company was hired to create <a href="http://www.livechatinc.com/en/overview/live-chat-for-mac-os-x/">Mac version of LiveChat</a> operator software. The application just entered alpha-phase and today closed tests were started. I hope too that the new refreshing <em>LiveChat</em> icon (on the right) designed by me will be the cherry on the project&#8217;s pie.</p>
<p><a href="http://www.livechatinc.com/">LiveChat</a> provides innovative web based customer support solutions including live web statistics and live customer chat. The company has great potential and impressive <a href="http://www.livechatinc.com/en/customers/">customer base</a>, so you may be interested to follow their Twitter feed too <a href="http://twitter.com/livechatinc">here</a>.</p>
<p><span id="more-573"></span></p>
<p>Checkout new L&amp;F from just released alpha and tell me what do you think:</p>
<p style="text-align: center;"><img class="size-full wp-image-582 aligncenter" title="LiveChat Mac Client List" src="http://www.nanoant.com/wp/wp-content/uploads/2010/08/LiveChat-Mac-Client-List.jpg" alt="" width="623" height="399" /></p>
<p>Now I am heading seaside to get some rest, because upcoming September will be busy for sure.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.nanoant.com/apple/nanoant-creates-livechat-for-mac/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Browse gossips and latest paparazzi shots on your iPhone</title>
		<link>http://www.nanoant.com/apple/browse-gossips-and-latest-paparazzi-shots-on-your-iphone</link>
		<comments>http://www.nanoant.com/apple/browse-gossips-and-latest-paparazzi-shots-on-your-iphone#comments</comments>
		<pubDate>Tue, 20 Jul 2010 18:07:45 +0000</pubDate>
		<dc:creator>Adam</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://www.nanoant.com/?p=589</guid>
		<description><![CDATA[I should say it first &#8211; mostly Polish gossips, in Polish language. But anyway Kozaczek.pl is another nanoANT&#8217;s piece of craft (mind the spelling). After one week of its premier it topped 2nd in Polish AppStore. We had greater expectations, but some iPhone users here in Poland are really great bigots. I have seen many [...]]]></description>
			<content:encoded><![CDATA[<div class="appstore">
<a href="http://itunes.apple.com/pl/app/kozaczek-pl/id381493919?mt=8"><img title="Kozaczek" src="http://www.nanoant.com/wp/wp-content/uploads/2010/08/Kozaczek.png" alt="" width="114" height="114" /></a></div>
<p>I should say it first &#8211; mostly Polish gossips, in Polish language. But anyway Kozaczek.pl is another nanoANT&#8217;s piece of craft (mind the spelling). After one week of its premier it topped 2nd in Polish AppStore. We had greater expectations, but some iPhone users here in Poland are really great bigots. I have seen many comments accusing some <em>iPhone</em> news editors that they have put a little news about this app, because this app ill befits there. Come on!</p>
<p>Anyway doing this app was a great fun and I believe using it is a great fun too, especially as an alternative to those grave TV news everyday. Checkout official release news <a href="http://www.kozaczek.pl/plotka.php?id=24423">here</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.nanoant.com/apple/browse-gossips-and-latest-paparazzi-shots-on-your-iphone/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>DrawIt &#8211; power of Photoshop in non-destructive form</title>
		<link>http://www.nanoant.com/apple/drawit-power-of-photoshop-in-non-destructive-form</link>
		<comments>http://www.nanoant.com/apple/drawit-power-of-photoshop-in-non-destructive-form#comments</comments>
		<pubDate>Thu, 15 Jul 2010 12:16:57 +0000</pubDate>
		<dc:creator>Adam</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[Design]]></category>
		<category><![CDATA[DrawIt]]></category>
		<category><![CDATA[mac]]></category>
		<category><![CDATA[Photoshop]]></category>
		<category><![CDATA[Vector]]></category>

		<guid isPermaLink="false">http://www.nanoant.com/?p=547</guid>
		<description><![CDATA[I should have written something about DrawIt long time ago, as this small but powerful non-destructive vector based bitmap design tool really deserves the attention. DrawIt was created and is maintained by its author Pieter Omvlee. While doing initial design of this application Pieter asked for help French designer Laurent Baumann (working now for Apple). [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.nanoant.com/wp/wp-content/uploads/2010/07/Domek.png"><img class="alignright size-full wp-image-550" title="Domek" src="http://www.nanoant.com/wp/wp-content/uploads/2010/07/Domek.png" alt="" width="234" height="218" /></a>I should have written something about <a href="http://www.bohemiancoding.com/drawit">DrawIt</a> long time ago, as this small but powerful non-destructive vector based bitmap design tool really deserves the attention.</p>
<p><em>DrawIt</em> was created and is maintained by its author <a href="http://pieteromvlee.net/me/">Pieter Omvlee</a>. While doing initial design of this application Pieter asked for help French designer <a href="http://lbaumann.com/">Laurent Baumann</a> (working now for <em>Apple</em>). Pieter&#8217;s skills and Laurent&#8217;s talent joint together created something extraordinary, the great UI of <em>DrawIt</em> made from scratch to give you an ability to quickly create web/bitmap designs with vector tools and stack of effects. I will not get into details here, as you can find few good articles about <em>DrawIt</em> over the Internet, but I will rather describe little experiment of mine I have made today and try to answer the question if <em>DrawIt</em> can be good <em>Photoshop</em> replacement for icon and web design.</p>
<p>Since lately I was forced to use <em>Photoshop</em>, I decided to give <em>DrawIt</em> a try and recreate one of outstanding <em>Photoshop</em> tutorial icons using <em>DrawIt</em>. My choice went for <a href="http://psd.tutsplus.com/tutorials/icon-design/how-to-create-a-basic-house-icon-in-photoshop/">Basic House Icon</a> from PSDTuts+ site. You can see the result of my work above and you can download the <em>DrawIt</em> source file <a href="http://www.nanoant.com/download/drawit/Domek.drawit.zip">here</a>.</p>
<p><span id="more-547"></span>First of all <em>DrawIt</em> is non-destructive vector based program. Which means you can always revise any step and decision you have made during design process. The idea is based on shapes and effects applied onto them, something you could find in <em>Macromedia Fireworks, Freehand</em> (RiP) or <em>Adobe Photoshop</em>&#8216;s layer&#8217;s effects.</p>
<div id="attachment_552" class="wp-caption alignright" style="width: 185px"><img class="size-full wp-image-552" title="Photoshop" src="http://www.nanoant.com/wp/wp-content/uploads/2010/07/Photoshop.png" alt="" width="175" height="93" /><p class="wp-caption-text">Photoshop Layer With Effects</p></div>
<p>As seen on many <em>Photoshop</em> icon design tutorials, the starting point is a path filled with some gradient or simply filled with black color.  Then most of the folks apply effects created out of the path&#8217;s filling. The effects panel used all over again unfortunately is buried in Layers panel and requires extra interaction to pop it up.</p>
<p>In my opinion this really makes me wonder what made <em>Photoshop</em> to be considered better than <em>Macromedia Fireworks</em> or <em>Illustrator</em> for icon design when you are working constantly applying effect, closing and opening Effects window which is simply uncomfortable.</p>
<div id="attachment_554" class="wp-caption alignright" style="width: 158px"><img class="size-full wp-image-554" title="DrawIt" src="http://www.nanoant.com/wp/wp-content/uploads/2010/07/DrawIt.png" alt="" width="148" height="207" /><p class="wp-caption-text">DrawIt Effect Stack</p></div>
<p>Here in <em>DrawIt</em> it is much easier to set effects for particular shape. First of all you are working only shapes, so you can always touch and redefine the shape&#8217;s shape using vector tools, while <em>Photoshop</em> it takes a bit more to redefine layer&#8217;s path, also when you did some manual touch to the layer then redefining its path will kill your modifications.</p>
<p>Also <em>DrawIt</em> effects may be applied in random user&#8217;s defined order. You can also double same effect, disable and reenable them on fly.</p>
<h2>Photoshop&#8217;s touch to the image</h2>
<p>There&#8217;s one thing that you can do in <em>Photoshop</em> but you can&#8217;t in vector based programs. It is some human touch to the color or shade using tools like burn, dodge. Such a &#8220;touch&#8221; can sometimes drastically improve  overall effect. Unfortunately this is impossible simply using <em>DrawIt</em>, as everything is an object here, so dodging particular area of an object is simply impossible.</p>
<p>Nevertheless there is always alternative ways to achieve same effect as you would get using hand drawing <em>Photoshop</em> tools in <em>DrawIt</em>. You just need &#8220;think vector&#8221;. Sometimes experimenting with various settings can result with amazing overall look of an object.</p>
<h2>DrawIt&#8217;s perfection killed by some flaws</h2>
<p>Unfortunately some minor but longstanding bugs are <strong>flies in the DrawIt&#8217;s ointment</strong>. <em>Undo/redo</em> is the basic thing you expect to work well in any program. It does work well in Photoshop, TextMate, iWork, but NOT in <em>DrawIt</em>, that tends to forget some actions in its undo chain, which makes unpleasant surprise when playing with effects.</p>
<p>Another thing that makes you suffer it a way you change colors of gradients and other various objects, sometimes you need to click few times to make DrawIt change the color of selected property. Again, color panel tends to forget what it was attached to, especially when you change switch objects Color panel is still on screen but attached to nothing.</p>
<p>Resizing objects can be very painful too. First, changing objects height makes it change its vertical position, this isn&#8217;t right! Second, trying to resize the document with all objects usually leads to misplacement of those objects.</p>
<p>Finally vector (shape) editing mode deserves more polish, as sometimes you may get crazy if you want to delete several points (no way to drag/select few points), or if you move the point out of the canvas you cannot control it anymore its handle disappears (clipped by canvas rectangle), while you expect shape&#8217;s vector handles should be visible outside of the canvas too.</p>
<h2>Is it worth a price?</h2>
<p><em>DrawIt</em> is absolutely worth its price and I would glad pay for it again if those few annoying bugs were removed. The author is a very kind nice young fellow willing to listen for the new ideas for his project. One of such ideas is slicing functionality that is probably a result IM chat I did with the author while ago.</p>
<p>However peeking recently into author&#8217;s blog it seems he is more focused on his other applications &#8211; too bad, but that&#8217;s life &#8211; you are working on something that gives you best revenue, which may be not <em>DrawIt</em> in this case.</p>
<p>What about <em>Photoshop</em>? In my opinion it is definitely NOT worth a price if you want just an icon design software. I really regret all competing software producers such as <em>Corel</em>, <em>ACD</em>, <em>Xara</em> dropped support for <em>Mac OS X,</em> leaving us in hands of <em>Adobe</em>. Thank God there is <em>DrawIt</em> (and maybe <em>Vectordesigner</em> ;P).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.nanoant.com/apple/drawit-power-of-photoshop-in-non-destructive-form/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Wanna be 3.x compatible? Not so simple!</title>
		<link>http://www.nanoant.com/apple/wanna-be-3-x-compatible-not-so-simple</link>
		<comments>http://www.nanoant.com/apple/wanna-be-3-x-compatible-not-so-simple#comments</comments>
		<pubDate>Thu, 08 Jul 2010 15:45:46 +0000</pubDate>
		<dc:creator>Adam</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://www.nanoant.com/?p=527</guid>
		<description><![CDATA[So we got the new shiny iOS 4 with the new not-so-shiny SDK 4. Most desirable aspect of using SDK 4 and iOS 4 functions is to be backward compatible with iPhone OS 3.x. This is where you should set your iPhone OS Deployment Target to iPhone OS 3.0 or anything else you want to [...]]]></description>
			<content:encoded><![CDATA[<p><img class="alignright size-full wp-image-541" title="iOS4" src="http://www.nanoant.com/wp/wp-content/uploads/2010/07/iOS4.gif" alt="" hspace="20" width="100" height="74" />So we got the new shiny <em>iOS 4</em> with the new not-so-shiny <em>SDK 4</em>. Most desirable aspect of using SDK 4 and iOS 4 functions is to be backward compatible with <em>iPhone OS 3.x</em>. This is where you should set your <strong>iPhone OS Deployment Target</strong> to <strong>iPhone OS 3.0</strong> or anything else you want to be compatible with.</p>
<p style="text-align: center;"><img class="size-full wp-image-528  aligncenter" title="Xcode" src="http://www.nanoant.com/wp/wp-content/uploads/2010/07/Xcode.png" alt="" width="423" height="29" /></p>
<p>This is the official method, and since SDK 4 does NOT come with 3.x headers, the only method to make your app run on 3.x. But it is not so simple, because now in your <em>Xcode</em> you are using <em>iOS 4</em> API. So how do you know you are not using classes or methods that do not exist in 3.x and putting them into your code will crash your app on 3.x device? You don&#8217;t!</p>
<p><span id="more-527"></span>Unfortunately <em>Xcode</em> will warn you about deprecated API, but will NOT warn you about API that does not exist on your deployment target. <strong>I buy you a beer if you can find such warning option in Xcode project configuration.</strong></p>
<p><strong></strong>This would be acceptable if you could test your application on iPhone 3.x OS Simulator, but there is no any in SDK 4 (neither as an option), hence you can&#8217;t absolutely tell your app won&#8217;t gonna blow on <em>iPhone OS 3.x</em> if you upgraded your TESTING devices to <em>iOS 4</em>.</p>
<p>I have spent a while trying to figure our what was Apple&#8217;s engineers official statement about that on <em>devforums</em>. And they say if you want to test 3.x compatibility you need get a device with <em>iPhone OS 3.x</em>. Huh? So I shall buy an new iPhone expecting it won&#8217;t have iOS 4 on board, because there is no (legal) way to downgrade existing device to 3.x.</p>
<p>Trying to find out a more reasonable way I have ended up with installing an OLD SDK 3.2 on /<tt>Developer-3.2</tt>. Finally tested my application&#8230; but fiddling with SDK 4 was not over. (NOTE: Obviously this method isn&#8217;t really free as you gonna lost 4GB for old SDK installation and 2.5GB for download, but it far less expensive than getting a new old 3.x device.)</p>
<h2>Still there are few gotchas with this method</h2>
<p>And here is one of them. If you happen to use iOS4 API in your code, Apple says that your shall check functions (and class) pointers against <tt>nil</tt>.</p>
<p>One of the new classes introduced by iOS4 API is <strong>UITapGestureRecognizer</strong>. I happened to know that because 3.x SDK complained about unknown <strong>UITapGestureRecognizer</strong> class. So I tried in my code:</p>
<pre class="textmate-source"><strong>UITapGestureRecognizer</strong> *singleTap = [[<strong>UITapGestureRecognizerClass</strong> alloc]
    initWithTarget:self action:<strong>@selector</strong>(handleSingleTap:)];
<strong>if</strong>( singleTap != <strong>nil</strong> ) {
    <em>// do something, we shall be there only on iOS4</em></pre>
<p>Should be enough to be compatible with 3.x device compiling on SDK 4, since <strong>UITapGestureRecognizer</strong> is introduced by iOS4 API I expected that on 3.x it will be <tt>nil</tt> so <tt>singleTap</tt> will be <tt>nil</tt> too! (BTW. It won&#8217;t compile with SDK 3.x I have just installed ;P)</p>
<p>I have sent my app to beta tester with 3.x device, that replied the app is <span style="text-decoration: underline;">still crashing</span>. So my reasoning about <tt>nil</tt> was NOT true. Okay, fine, let&#8217;s try with:</p>
<pre class="textmate-source"><strong>Class</strong> TapGestureRecognizerClass = NSClassFromString(@"<em>UITapGestureRecognizer</em>");
if( TapGestureRecognizerClass != <strong>nil</strong> ) {
    // do something, we shall be there only on iOS4</pre>
<p>Again crashes on 3.x device.</p>
<p>I have lost one day on communication with beta tester and scratching my head. You know why? Because <strong>UITapGestureRecognizer </strong>actually do EXIST in <em>iPhone OS 3.x</em> but it is NOT in the public API. Moreover it misses some methods that do exist in iOS 4 API, so finally I ended up with something like this:</p>
<pre class="textmate-source"><strong>Class</strong> TapGestureRecognizerClass = NSClassFromString(@"<em>UITapGestureRecognizer</em>");
<strong>if</strong>( TapGestureRecognizerClass != <strong>nil</strong> ) {
    UITapGestureRecognizer *singleTap = [[TapGestureRecognizerClass alloc] initWithTarget:self action:<strong>@selector</strong>(handleSingleTap:)];
    <strong>if</strong>( singleTap != <strong>nil </strong>) {
        <strong>if</strong>( [singleTap respondsToSelector:<strong>@selector</strong>(setNumberOfTapsRequired:)] ) {
            [singleTap setNumberOfTapsRequired:1];
        }
        <em>// .. and checking other selectors</em></pre>
<p>And this was my WORKING method to be compatible with 3.x and using some of <em>iOS 4 API</em>. Crazy enough?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.nanoant.com/apple/wanna-be-3-x-compatible-not-so-simple/feed</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>iPad is a pretty cool docs browser</title>
		<link>http://www.nanoant.com/apple/ipad-is-a-pretty-cool-docs-browser</link>
		<comments>http://www.nanoant.com/apple/ipad-is-a-pretty-cool-docs-browser#comments</comments>
		<pubDate>Tue, 22 Jun 2010 10:57:41 +0000</pubDate>
		<dc:creator>Adam</dc:creator>
				<category><![CDATA[Apple]]></category>

		<guid isPermaLink="false">http://www.nanoant.com/?p=503</guid>
		<description><![CDATA[If you have one, check out http://developer.apple.com/iphone/library/ on it. Do you like my iPad stand? It is a table easel, paid 25zł (about $10) and it is far more flexible and sexy than all those overpriced dedicated stands. By the way, iPad screen seems to be more blueish than MacBook&#8217;s. Both are led backlit, so [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: center;"><img class="size-full wp-image-515  aligncenter" title="iPad-docs" src="http://www.nanoant.com/wp/wp-content/uploads/2010/06/iPad-docs.jpg" alt="" width="500" height="307" /></p>
<p>If you have one, check out <a href="http://developer.apple.com/iphone/library/">http://developer.apple.com/iphone/library/</a> on it.</p>
<p>Do you like my <em>iPad</em> stand? It is a table easel, paid 25zł (about $10) and it is far more flexible and sexy than all those overpriced dedicated stands. By the way, <em>iPad</em> screen seems to be more blueish than <em>MacBook&#8217;s</em>. Both are led backlit, so I wonder why?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.nanoant.com/apple/ipad-is-a-pretty-cool-docs-browser/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Wezwij Taxi no.1 free app in PL AppStore after 1 day</title>
		<link>http://www.nanoant.com/apple/wezwij-taxi-no-1-free-app-in-pl-appstore-after-1-day</link>
		<comments>http://www.nanoant.com/apple/wezwij-taxi-no-1-free-app-in-pl-appstore-after-1-day#comments</comments>
		<pubDate>Tue, 01 Jun 2010 12:34:37 +0000</pubDate>
		<dc:creator>Adam</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[iPhone]]></category>

		<guid isPermaLink="false">http://www.nanoant.com/?p=487</guid>
		<description><![CDATA[Recently released Wezwij Taxi is surprisingly no.1 free application in Polish AppStore since few days, and became no.1 one day after release. I have just learned that AppStore is amazing phenomenon. It is quite impossible to predict impact of an application before it gets released. Some &#8220;silly&#8221; apps stay on top for weeks, while some [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.wezwijtaxi.pl/" class="appstore"><img class="alignright" title="Wezwij Taxi" src="http://www.nanoant.com/wp/wp-content/uploads/2010/06/Icon@2x.png" alt="" width="114" height="114" /></a>Recently released <a href="http://www.wezwijtaxi.pl/">Wezwij Taxi</a> is surprisingly no.1 free application in <em>Polish AppStore</em> since few days, and became no.1 one day after release.</p>
<p>I have just learned that <em>AppStore</em> is amazing phenomenon. It is quite impossible to predict impact of an application before it gets released. Some <em>&#8220;silly&#8221;</em> apps stay on top for weeks, while some real gems are quickly forgotten. So it is worth to test even most crazy wicked ideas against iPhone users putting them into life as <em>AppStore</em> app.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.nanoant.com/apple/wezwij-taxi-no-1-free-app-in-pl-appstore-after-1-day/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

