<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss 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/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>Cocoaphony</title>
	
	<link>http://robnapier.net/blog</link>
	<description>Mac and iPhone, on the brain</description>
	<lastBuildDate>Mon, 30 Jan 2012 04:25:07 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/Cocoaphony" /><feedburner:info uri="cocoaphony" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Drop-in offline caching for UIWebView (and NSURLProtocol)</title>
		<link>http://feedproxy.google.com/~r/Cocoaphony/~3/3JhfAvajDg8/offline-uiwebview-nsurlprotocol-588</link>
		<comments>http://robnapier.net/blog/offline-uiwebview-nsurlprotocol-588#comments</comments>
		<pubDate>Sun, 29 Jan 2012 18:56:11 +0000</pubDate>
		<dc:creator>Rob Napier</dc:creator>
				<category><![CDATA[cocoa]]></category>
		<category><![CDATA[iphone]]></category>

		<guid isPermaLink="false">http://robnapier.net/blog/?p=588</guid>
		<description><![CDATA[Your programs need to deal gracefully with being offline. Mugunth Kumar has built an excellent toolkit that manages REST connections while offline called MKNetworkKit, and Chapter 17 of our book is devoted to the ins-and-outs of this subject. But sometimes you just have a simple UIWebView, and you want to cache the last version of the page.  In this article you'll learn how to implement this using NSURLProtocol]]></description>
			<content:encoded><![CDATA[<p>Your programs need to deal gracefully with being offline. Mugunth Kumar has built an excellent toolkit that manages REST connections while offline called <a href="https://github.com/MugunthKumar/MKNetworkKit">MKNetworkKit</a>, and Chapter 17 of <a href="http://robnapier.net/book">our book</a> is devoted to the ins-and-outs of this subject.</p>

<p>But sometimes you just have a simple <code>UIWebView</code>, and you want to cache the last version of the page. You&#8217;d think that <code>NSURLCache</code> would handle this for you, but it&#8217;s much more complicated than that. <code>NSURLCache</code> doesn&#8217;t cache everything you&#8217;d think it would. Sometimes this is because of Apple&#8217;s decisions in order to save space. Just as often, however, it&#8217;s because the HTTP caching rules explicitly prevent caching a particular resource.</p>

<p>What I wanted was a simple mechanism for the following case:</p>

<ul>
<li>You have a UIWebView that points to a website with embedded images</li>
<li>When you&#8217;re online, you want the normal caching algorithms (nothing fancy)</li>
<li>When you&#8217;re offline, you want to show the last version of the page</li>
</ul>

<p>My test case was simple: a webview that loads cnn.com (a nice complicated webpage with lots of images). Run it once. Quit. Turn off the network. Run it again. CNN should display.</p>

<p><span id="more-588"></span></p>

<h3>Exisiting solutions</h3>

<p>The ever-brilliant Matt Gallagher has <a href="http://cocoawithlove.com/2010/09/substituting-local-data-for-remote.html">some interesting thoughts</a> on how to subclass <code>NSURLCache</code> to handle this, but I find his solution fragile and unreliable, especially on iOS 5. The HTTP caching rules are complicated, and in many cases you need to connect to the server to re-validate your cache before you&#8217;re allowed to use your local copy. Unless everything works out perfectly, his solution may not work when you&#8217;re offline, or may force you to turn off cache validation (which could make your pages go stale).</p>

<p><a href="https://github.com/artifacts/AFCache">AFCache</a> is also promising, using essentially the same approach. I haven&#8217;t found the offline support to work very well, at least in my tests, for the same reasons as Matt&#8217;s solution. It&#8217;s designed to be an advanced HTTP-caching solution. The docs are limited and I couldn&#8217;t get it to pass my CNN test.</p>

<h3>RNCachingURLProtocol</h3>

<p>So, I present <code>RNCachingURLProtocol</code>. It isn&#8217;t a replacement for <code>NSURLCache</code>. It&#8217;s a simple shim for the HTTP protocol (that&#8217;s not nearly as scary as it sounds). Anytime a URL is download, the response is cached to disk. Anytime a URL is requested, if we&#8217;re online then things proceed normally. If we&#8217;re offline, then we retrieve the cached version. The current implementation is extremely simple. In particular, it doesn&#8217;t worry about cleaning up the cache. The assumption is that you&#8217;re caching just a few simple things, like your &#8220;Latest News&#8221; page (which was the problem I was solving). It caches all HTTP traffic, so without some modifications, it&#8217;s not appropriate for an app that has a lot of HTTP connections (see <code>MKNetworkKit</code> for that). But if you need to cache some URLs and not others, that is easy to implement.</p>

<p>First, a quick rundown of how to use it:</p>

<ol>
<li><p>At some point early in the program (<code>application:didFinishLaunchingWithOptions:</code>), call the following:</p>

<p><code>[NSURLProtocol registerClass:[RNCachingURLProtocol class]];</code></p></li>
<li><p>There is no step 2.</p></li>
</ol>

<p>Since <code>RNCachingURLProtocol</code> doesn&#8217;t mess with the existing caching solution, it is compatible with other caches, like <code>AFCache</code>. In fact, the technique used by <code>RNCachingURLProtocol</code> could probably be integrated into <code>AFCache</code> pretty easily.</p>

<p>The cache itself is stored in the <code>Library/Caches</code> directory. In iOS 5, this directory can be purged whenever space is tight. Keep that in mind. You may want to store your caches elsewhere if offline access is critical.</p>

<h3>Understanding NSURLProtocol</h3>

<p>An <code>NSURLProtocol</code> is a handler for <code>NSURLConnection</code>. Each time a request is made, <code>NSURLConnection</code> walks through all the protocols and asks &#8220;Can you handle this request (<code>canInitWithRequest:</code>)?&#8221; The first protocol to return <code>YES</code> is used to handle the connection. Protocols are queried in the reverse order of their registration, so your custom handlers will get a crack at requests before the system handlers do.</p>

<p>Once your handler is selected, the connection will call <code>initWithRequest:cachedResponse:client:</code> and then <code>startLoading</code>. It is then your responsibility to call the connection back with <code>URLProtocol:didReceiveResponse:cacheStoragePolicy:</code>, some number of calls to <code>URLProtocol:didLoadData:</code>, and finally <code>URLProtocolDidFinishLoading:</code>. If these sound similar to the <code>NSURLConnection</code> delegate methods, that&#8217;s no accident.</p>

<p>While online, <code>RNCachingURLProtocol</code> just forwards requests to a new <code>NSURLConnection</code>, making copies of the results, and passing them along to the original connection. When offline, <code>RNCachingURLProtocol</code> loads the previous result from disk, and plays it back to the requesting connection. The whole thing is less than 200 lines of pretty simple code (not counting <code>Reachability</code>, which I include from Apple&#8217;s sample code to determine if we&#8217;re online).</p>

<p>There&#8217;s a subtle problem with the above solution. When <code>RNCachingURLProtocol</code> creates a new <code>NSURLConnection</code>, that new connection has to find a handler. If <code>RNCachingURLProtocol</code> says it can handle it, then you&#8217;ll have an infinite loop. So how do I know not to handle the second request? By adding a custom header (<code>X-RNCache</code>) to the HTTP request. If it&#8217;s there, then we&#8217;ve already seen this one, and the handler returns <code>NO</code>.</p>

<p>Again, this intercepts <em>all</em> HTTP traffic. That could intercept pages you don&#8217;t want. If so, you can modify <code>canInitWithRequest:</code> to select just things you want to cache (for instance, you could turn off caching for URLs that include parameters or POST requests).</p>

<h3>Wrap-up</h3>

<p>This technique isn&#8217;t a replacement for a full caching engine like <code>AFCache</code> or an offline REST engine like <code>MKNetworkKit</code>. It&#8217;s intended to solve a single, simple problem (though it can be extended to solve much more complicated problems). <code>NSURLProtocol</code> is extremely powerful, and I&#8217;ve used it extensively when I need to eavesdrop on network traffic (such as in PandoraBoy&#8217;s several <a href="https://github.com/PandoraBoy/PandoraBoy/blob/master/ProxyURLProtocol.h">ProxyURLProtocol</a> classes). It&#8217;s well-worth adding to your toolkit.</p>

<p>The code is in the attached project. Look in <code>RNCachingURLProtocol.m</code>.</p>

<p><a href='http://robnapier.net/blog/wp-content/uploads/2012/01/CachedWebView.zip'>CachedWebView Example Project</a></p>
<img src="http://feeds.feedburner.com/~r/Cocoaphony/~4/3JhfAvajDg8" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://robnapier.net/blog/offline-uiwebview-nsurlprotocol-588/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		<feedburner:origLink>http://robnapier.net/blog/offline-uiwebview-nsurlprotocol-588</feedburner:origLink></item>
		<item>
		<title>Kindle!</title>
		<link>http://feedproxy.google.com/~r/Cocoaphony/~3/WVSm1xwBpAo/ios5-ptl-kindle-585</link>
		<comments>http://robnapier.net/blog/ios5-ptl-kindle-585#comments</comments>
		<pubDate>Thu, 22 Dec 2011 14:47:36 +0000</pubDate>
		<dc:creator>Rob Napier</dc:creator>
				<category><![CDATA[Pushing The Limits]]></category>
		<category><![CDATA[ios5ptl]]></category>
		<category><![CDATA[kindle]]></category>

		<guid isPermaLink="false">http://robnapier.net/blog/?p=585</guid>
		<description><![CDATA[For those who have asked (and it's the #1 question I'm asked about the book), iOS 5 Programming Pushing the Limits is now available in Kindle format.]]></description>
			<content:encoded><![CDATA[<p>For those who have asked (and it&#8217;s the #1 question I&#8217;m asked about the book), <em>iOS 5 Programming Pushing the Limits</em> is now available in <a href="http://www.amazon.com/Programming-Pushing-Limits-Extraordinary-ebook/dp/B006OP97QI/ref=sr_1_11?s=digital-text&#038;ie=UTF8&#038;qid=1324561899&#038;sr=1-11">Kindle format</a>, along with iBook and Adobe eBook. Enjoy</p>
<img src="http://feeds.feedburner.com/~r/Cocoaphony/~4/WVSm1xwBpAo" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://robnapier.net/blog/ios5-ptl-kindle-585/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://robnapier.net/blog/ios5-ptl-kindle-585</feedburner:origLink></item>
		<item>
		<title>iOS 5 Programming in Adobe E-Book format</title>
		<link>http://feedproxy.google.com/~r/Cocoaphony/~3/cZ8cVXW0GmE/ios-5-programming-adobe-ebook-format-581</link>
		<comments>http://robnapier.net/blog/ios-5-programming-adobe-ebook-format-581#comments</comments>
		<pubDate>Fri, 16 Dec 2011 15:38:47 +0000</pubDate>
		<dc:creator>Rob Napier</dc:creator>
				<category><![CDATA[cocoa]]></category>
		<category><![CDATA[iphone]]></category>

		<guid isPermaLink="false">http://robnapier.net/blog/?p=581</guid>
		<description><![CDATA[For those who have asked, the Adobe E-Book version of iOS 5 Programming is available now from Wiley. I&#8217;m not certain yet when the Kindle version will be out, but I&#8217;m looking into it, along with getting a list of what other formats are planned.]]></description>
			<content:encoded><![CDATA[<p>For those who have asked, the <a href="http://www.wiley.com/WileyCDA/WileyTitle/productCd-1119961580.html">Adobe E-Book version</a> of iOS 5 Programming is available now from Wiley. I&#8217;m not certain yet when the Kindle version will be out, but I&#8217;m looking into it, along with getting a list of what other formats are planned.</p>
<img src="http://feeds.feedburner.com/~r/Cocoaphony/~4/cZ8cVXW0GmE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://robnapier.net/blog/ios-5-programming-adobe-ebook-format-581/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		<feedburner:origLink>http://robnapier.net/blog/ios-5-programming-adobe-ebook-format-581</feedburner:origLink></item>
		<item>
		<title>A moment in the sun</title>
		<link>http://feedproxy.google.com/~r/Cocoaphony/~3/PlPrz3hmn5I/moment-sun-573</link>
		<comments>http://robnapier.net/blog/moment-sun-573#comments</comments>
		<pubDate>Thu, 15 Dec 2011 04:51:54 +0000</pubDate>
		<dc:creator>Rob Napier</dc:creator>
				<category><![CDATA[cocoa]]></category>
		<category><![CDATA[iphone]]></category>

		<guid isPermaLink="false">http://robnapier.net/blog/?p=573</guid>
		<description><![CDATA[I have no idea what Amazon rankings really mean right around a book launch. Since they weight towards recent sales, I&#8217;m sure they spike, so you have to take these things with a grain of salt. But for at least a little while, we have the #1 book in Mobile &#38; Wireless Programming. I promise [...]]]></description>
			<content:encoded><![CDATA[<p>I have no idea what Amazon rankings really mean right around a book launch. Since they weight towards recent sales, I&#8217;m sure they spike, so you have to take these things with a grain of salt. But for at least a little while, we have the <a href="http://www.amazon.com/gp/bestsellers/books/377559011">#1 book in Mobile &amp; Wireless Programming</a>. I promise I won&#8217;t keep spamming you all with book updates, but it&#8217;s the first time I&#8217;ve been at the top of a &#8220;best sellers&#8221; list.</p>

<div id="attachment_574" class="wp-caption alignnone" style="width: 310px"><a href="http://robnapier.net/blog/wp-content/uploads/2011/12/Screen-Shot-2011-12-14-at-11.46.46-PM.png"><img src="http://robnapier.net/blog/wp-content/uploads/2011/12/Screen-Shot-2011-12-14-at-11.46.46-PM-300x128.png" alt="" title="Screen Shot 2011-12-14 at 11.46.46 PM" width="300" height="128" class="size-medium wp-image-574" /></a><p class="wp-caption-text">Amazon&#039;s product details 2011-12-14</p></div>
<img src="http://feeds.feedburner.com/~r/Cocoaphony/~4/PlPrz3hmn5I" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://robnapier.net/blog/moment-sun-573/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		<feedburner:origLink>http://robnapier.net/blog/moment-sun-573</feedburner:origLink></item>
		<item>
		<title>The book is almost here</title>
		<link>http://feedproxy.google.com/~r/Cocoaphony/~3/9UPMVkubEuc/the-book-is-almost-here-568</link>
		<comments>http://robnapier.net/blog/the-book-is-almost-here-568#comments</comments>
		<pubDate>Sun, 11 Dec 2011 19:09:06 +0000</pubDate>
		<dc:creator>Rob Napier</dc:creator>
				<category><![CDATA[cocoa]]></category>
		<category><![CDATA[iphone]]></category>

		<guid isPermaLink="false">http://robnapier.net/blog/?p=568</guid>
		<description><![CDATA[I&#8217;ve mentioned that Mugunth Kumar and I have been working on a book, iOS 5 Programming Pushing the Limits. It&#8217;s available now for pre-order and should be shipping by 12/20. It will be available in several eBook formats, though I don&#8217;t have all the details for that yet. I&#8217;m proud of what we&#8217;ve been able [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve mentioned that Mugunth Kumar and I have been working on a book, <a href="http://www.amazon.com/iOS-Programming-Pushing-Limits-Extraordinary/dp/1119961327">iOS 5 Programming Pushing the Limits</a>. It&#8217;s available now for pre-order and should be shipping by 12/20. It will be available in several eBook formats, though I don&#8217;t have all the details for that yet. I&#8217;m proud of what we&#8217;ve been able to put together here. The target audience are developers who have some iOS development under their belts, and are ready to move to the next level. Rather than just focus on the nuts and bolts of getting things done, we cover  how to do things well. In the process, we cover a lot of more advanced ground, often of frameworks with &#8220;Core&#8221; in their names like Core Animation, Core Text, and Core Foundation. We also address practical issues like how to optimize your application for offline work, and how to best handle REST and JSON.</p>

<p>Please send any comments or corrections to me at <a href="mailto:robnapier@gmail.com">robnapier@gmail.com</a>. In the meantime, you can take a look at the <a href="http://www.wiley.com/WileyCDA/WileyTitle/productCd-1119961327,descCd-DOWNLOAD.html">example code</a>.</p>
<img src="http://feeds.feedburner.com/~r/Cocoaphony/~4/9UPMVkubEuc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://robnapier.net/blog/the-book-is-almost-here-568/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://robnapier.net/blog/the-book-is-almost-here-568</feedburner:origLink></item>
		<item>
		<title>Properly encrypting with AES with CommonCrypto</title>
		<link>http://feedproxy.google.com/~r/Cocoaphony/~3/IgzAUsq79Do/aes-commoncrypto-564</link>
		<comments>http://robnapier.net/blog/aes-commoncrypto-564#comments</comments>
		<pubDate>Thu, 11 Aug 2011 20:43:34 +0000</pubDate>
		<dc:creator>Rob Napier</dc:creator>
				<category><![CDATA[cocoa]]></category>
		<category><![CDATA[iphone]]></category>
		<category><![CDATA[Security]]></category>

		<guid isPermaLink="false">http://robnapier.net/blog/?p=564</guid>
		<description><![CDATA[Encrypting with AES on OS X and iOS isn't very difficult, but most examples do it incorrectly. This article explains how to create a proper salted key, and how to generate and manage your IV.]]></description>
			<content:encoded><![CDATA[<p><strong>Update: You can now download the full example code from my book at the <a href="http://www.wiley.com/WileyCDA/WileyTitle/productCd-1119961327,descCd-DOWNLOAD.html" target="_blank">Wiley site</a>. This comes from Chapter 11, &#8220;Batten the Hatches with Security Services.&#8221;</strong></p>

<p>I see a lot of example code out there showing how to use <code>CCCrypt()</code>, and most of it is unfortunately wrong. Since I just got finished writing about 10 pages of explanation for my upcoming book, I thought I&#8217;d post a shortened form here and hopefully help clear things up a little. This is going to be a little bit of a whirlwind, focused on the simplest case. If you want the gory details including performance improvements for large amounts of data, well, <a href="http://www.wiley.com/WileyCDA/WileyTitle/productCd-1119961327.html">the book</a> will be out later this year. :D</p>

<p><span id="more-564"></span></p>

<p>First and foremost: the key. This is almost always done wrong in the examples you see floating around the Internet. A human-typed password is not an AES key. It has far too little entropy. Using it directly as an AES key opens you up to all kinds of attacks. In particular, lines like this are wrong:</p>

<pre><code>// DO NOT DO THIS
NSString *password = @"P4ssW0rd!";
char keyPtr[kCCKeySizeAES128];
[password getCString:keyPtr maxLength:sizeof( keyPtr ) encoding:NSUTF8StringEncoding];
// DO NOT DO THIS
</code></pre>

<p>This key is susceptible to a variety of attacks. It is neither salted nor stretched. If <code>password</code> is longer than the key size, then the password will be truncated. This is not how you build a key.</p>

<p>First, you need to salt your key. That means adding random data to it so that if the same data is encrypted with the same password, the ciphertext will still be different. The key should then be hashed, so that the final result is the correct length. The correct way to do this is with PKCS #5 (PBKDF2). Unfortunately, prior to 10.7, there wasn&#8217;t an easy function to do that. I&#8217;m going to focus here on 10.7 code, but if you need a simple solution, just add about 8 random characters to the the string, and run it through <code>-hash</code>. Stringify that and run it through <code>-hash</code> again. Repeat 100,000 times. Take the lower &#8220;X&#8221; bits where &#8220;X&#8221; is the number of bits in your key. This isn&#8217;t perfect, but it&#8217;s easy to code and close enough. It works on all versions of OS X and iOS. 100,000 times is based on this taking about 100ms for a MacBookPro to calculate in my quick tests. On an iPhone 4, the same delay is around 10,000 iterations. The goal is to force the attacker to waste some time.</p>

<p>(If someone has a better quick-and-easy key generation algorithm, leave a comment.)</p>

<p>But like I said, don&#8217;t do that way if you can help it. We have PBKDF2 built into <code>CommonCrypto</code> now (well, in 10.7). Hand it your salt, your password, the number of iterations, and the length of your key and it spits out the answer for you. I&#8217;ll show how to do this in the code below.</p>

<p>Did I mention that CommonCrypto is all <a href="http://opensource.apple.com/source/CommonCrypto">open source</a>? So if you needed the PBKDF2 code for other platforms, you could probably get it to work.</p>

<p>OK, now you have a salt. What do you do with it? Save it with the cipertext. You&#8217;ll need it later to decrypt. The salt is considered public information so you don&#8217;t need to protect it.</p>

<p>And now the mystical initialization vector (IV) that confuses everyone. In CBC-mode, each 16-byte encryption influences the next 16-byte encryption. This is a good thing. It makes the encryption much stronger. It&#8217;s also the default. The problem is, what about block 0? The answer is you make up a random block -1. That&#8217;s the IV.</p>

<p>This is listed as &#8220;optional&#8221; in <code>CCCrypt()</code> which is confusing because it isn&#8217;t really optional in CBC mode. If you don&#8217;t provide one, it&#8217;ll automatically generate an all-0 IV for you. That throws away significant protection on the first block. There&#8217;s no reason to do that. IV is simple: it&#8217;s just 16 random bytes. &#8220;Save it with the cipertext. You&#8217;ll need it later to decrypt. The <strike>salt</strike> IV is considered public information so you don&#8217;t need to protect it.&#8221;</p>

<p>OK, now that I&#8217;ve gone on and on about theory, let&#8217;s see this in practice. First, here&#8217;s how you use it. The method returns the encrypted data (<code>nil</code> for error), and returns the IV, salt and error by reference. Slap the data, IV, and salt together in your file in whatever way is easy for you to retrieve them later. The IV has to be 16 bytes long for AES. The salt can be any length, but my code sets it to 8 bytes, which is the PKCS#5 minimum recommended length.</p>

<pre><code>NSData *iv;
NSData *salt;
NSError *error;
NSData *encryptedData = [RNCryptManager encryptedDataForData:plaintextData
                                                    password:password
                                                          iv:&amp;iv
                                                        salt:&amp;salt
                                                       error:&amp;error];
</code></pre>

<p>And here&#8217;s the code. I will leave the decrypt method as an exercise for the reader. It&#8217;s almost identical, and it&#8217;s a good idea to actually understand this code, not just copy it. Don&#8217;t forget to link <code>Security.framework</code>.</p>

<p>Go forth and encrypt stuff.</p>

<pre><code>#import &lt;CommonCrypto/CommonCryptor.h&gt;
#import &lt;CommonCrypto/CommonKeyDerivation.h&gt;

NSString * const
kRNCryptManagerErrorDomain = @"net.robnapier.RNCryptManager";

const CCAlgorithm kAlgorithm = kCCAlgorithmAES128;
const NSUInteger kAlgorithmKeySize = kCCKeySizeAES128;
const NSUInteger kAlgorithmBlockSize = kCCBlockSizeAES128;
const NSUInteger kAlgorithmIVSize = kCCBlockSizeAES128;
const NSUInteger kPBKDFSaltSize = 8;
const NSUInteger kPBKDFRounds = 10000;  // ~80ms on an iPhone 4

// ===================

+ (NSData *)encryptedDataForData:(NSData *)data
                        password:(NSString *)password
                              iv:(NSData **)iv
                            salt:(NSData **)salt
                           error:(NSError **)error {
  NSAssert(iv, @"IV must not be NULL");
  NSAssert(salt, @"salt must not be NULL");

  *iv = [self randomDataOfLength:kAlgorithmIVSize];
  *salt = [self randomDataOfLength:kPBKDFSaltSize];

  NSData *key = [self AESKeyForPassword:password salt:*salt];

  size_t outLength;
  NSMutableData *
  cipherData = [NSMutableData dataWithLength:data.length +
                kAlgorithmBlockSize];

  CCCryptorStatus
  result = CCCrypt(kCCEncrypt, // operation
                   kAlgorithm, // Algorithm
                   kCCOptionPKCS7Padding, // options
                   key.bytes, // key
                   key.length, // keylength
                   (*iv).bytes,// iv
                   data.bytes, // dataIn
                   data.length, // dataInLength,
                   cipherData.mutableBytes, // dataOut
                   cipherData.length, // dataOutAvailable
                   &amp;outLength); // dataOutMoved

  if (result == kCCSuccess) {
    cipherData.length = outLength;
  }
  else {
    if (error) {
      *error = [NSError errorWithDomain:kRNCryptManagerErrorDomain
                                   code:result
                               userInfo:nil];
    }
    return nil;
  }

  return cipherData;
}

// ===================

+ (NSData *)randomDataOfLength:(size_t)length {
  NSMutableData *data = [NSMutableData dataWithLength:length];

  int result = SecRandomCopyBytes(kSecRandomDefault, 
                                  length,
                                  data.mutableBytes);
  NSAssert(result == 0, @"Unable to generate random bytes: %d",
           errno);

  return data;
}

// ===================

// Replace this with a 10,000 hash calls if you don't have CCKeyDerivationPBKDF
+ (NSData *)AESKeyForPassword:(NSString *)password 
                         salt:(NSData *)salt {
  NSMutableData *
  derivedKey = [NSMutableData dataWithLength:kAlgorithmKeySize];

  int 
  result = CCKeyDerivationPBKDF(kCCPBKDF2,            // algorithm
                                password.UTF8String,  // password
                                password.length,  // passwordLength
                                salt.bytes,           // salt
                                salt.length,          // saltLen
                                kCCPRFHmacAlgSHA1,    // PRF
                                kPBKDFRounds,         // rounds
                                derivedKey.mutableBytes, // derivedKey
                                derivedKey.length); // derivedKeyLen

  // Do not log password here
  NSAssert(result == kCCSuccess,
           @"Unable to create AES key for password: %d", result);

  return derivedKey;
}
</code></pre>
<img src="http://feeds.feedburner.com/~r/Cocoaphony/~4/IgzAUsq79Do" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://robnapier.net/blog/aes-commoncrypto-564/feed</wfw:commentRss>
		<slash:comments>23</slash:comments>
		<feedburner:origLink>http://robnapier.net/blog/aes-commoncrypto-564</feedburner:origLink></item>
		<item>
		<title>A PandoraBoy maintainer emerges</title>
		<link>http://feedproxy.google.com/~r/Cocoaphony/~3/XEKlBYMiMfk/a-pandoraboy-maintainer-emerges-562</link>
		<comments>http://robnapier.net/blog/a-pandoraboy-maintainer-emerges-562#comments</comments>
		<pubDate>Wed, 03 Aug 2011 12:54:19 +0000</pubDate>
		<dc:creator>Rob Napier</dc:creator>
				<category><![CDATA[PandoraBoy]]></category>

		<guid isPermaLink="false">http://robnapier.net/blog/?p=562</guid>
		<description><![CDATA[I went out to NSCoder RTP for the first time last night and met up with Josh Johnson, who has been interested in taking up PandoraBoy development. So expect some changes coming up. Probably a move to github. Probably dropping 10.5 support soon. And who knows, it might even really work again.]]></description>
			<content:encoded><![CDATA[<p>I went out to <a href="http://trianglecocoa.com/">NSCoder RTP</a> for the first time last night and met up with <a href="http://jsh.in">Josh Johnson</a>, who has been interested in taking up PandoraBoy development. So expect some changes coming up. Probably a move to github. Probably dropping 10.5 support soon. And who knows, it might even really work again.</p>
<img src="http://feeds.feedburner.com/~r/Cocoaphony/~4/XEKlBYMiMfk" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://robnapier.net/blog/a-pandoraboy-maintainer-emerges-562/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://robnapier.net/blog/a-pandoraboy-maintainer-emerges-562</feedburner:origLink></item>
		<item>
		<title>Looking for a PandoraBoy maintainer</title>
		<link>http://feedproxy.google.com/~r/Cocoaphony/~3/4ydEoaRAEpU/pandoraboy-maintainer-560</link>
		<comments>http://robnapier.net/blog/pandoraboy-maintainer-560#comments</comments>
		<pubDate>Tue, 26 Jul 2011 23:35:56 +0000</pubDate>
		<dc:creator>Rob Napier</dc:creator>
				<category><![CDATA[PandoraBoy]]></category>

		<guid isPermaLink="false">http://robnapier.net/blog/?p=560</guid>
		<description><![CDATA[The whole reason I learned Cocoa in the first place was so I could work on PandoraBoy. It&#8217;s a great project, but I haven&#8217;t really worked on it in quite some time. As Pandora changes, PandoraBoy becomes more and more broken. I barely have time to look at the patches that have been sent it, [...]]]></description>
			<content:encoded><![CDATA[<p>The whole reason I learned Cocoa in the first place was so I could work on <a href="http://code.google.com/p/pandoraboy/">PandoraBoy</a>. It&#8217;s a great project, but I haven&#8217;t really worked on it in quite some time. As Pandora changes, PandoraBoy becomes more and more broken. I barely have time to look at the patches that have been sent it, let alone apply them. My life has moved onto many other projects.</p>

<p>I&#8217;m actively looking for a new maintainer. It&#8217;s a good project, and the code base is in decent repair, though I don&#8217;t know if it builds with Xcode 4. The system basically works, but there&#8217;s a long list of things that could be improved, and some things that are now downright broken. If you&#8217;re interested, let me know and I&#8217;ll help hand it off. I took it over when I knew next to nothing about Macs. It&#8217;s a great place to learn.</p>
<img src="http://feeds.feedburner.com/~r/Cocoaphony/~4/4ydEoaRAEpU" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://robnapier.net/blog/pandoraboy-maintainer-560/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://robnapier.net/blog/pandoraboy-maintainer-560</feedburner:origLink></item>
		<item>
		<title>One of the reasons I like KACE (the new M300)</title>
		<link>http://feedproxy.google.com/~r/Cocoaphony/~3/UKYbQo5h-Qw/kace-m300-557</link>
		<comments>http://robnapier.net/blog/kace-m300-557#comments</comments>
		<pubDate>Thu, 14 Jul 2011 19:05:43 +0000</pubDate>
		<dc:creator>Rob Napier</dc:creator>
				<category><![CDATA[cocoa]]></category>

		<guid isPermaLink="false">http://robnapier.net/blog/?p=557</guid>
		<description><![CDATA[I don&#8217;t usually shill for my company&#8217;s products, but I&#8217;m really impressed with the new M300 we just put out. It&#8217;s an IT management appliance targeted at companies with 50-200 computers. I like how simple it makes setup; reminds me of Apple. Fewer &#8220;fiddly-knobs&#8221; and more &#8220;just works out of the box stop messing with [...]]]></description>
			<content:encoded><![CDATA[<p>I don&#8217;t usually shill for my company&#8217;s products, but I&#8217;m  really impressed with the new <a href="http://www.kace.com/products/asset-management-appliance/">M300</a> we just put out. It&#8217;s an IT management appliance targeted at companies with 50-200 computers. I like how simple it makes setup; reminds me of Apple. Fewer &#8220;fiddly-knobs&#8221; and more &#8220;just works out of the box stop messing with it.&#8221; We&#8217;ve got the K1000 for those companies who need lots of configuration, but I&#8217;m excited to see us get into the SMB space like this. I know most of you don&#8217;t need an SMB IT management appliance, but it&#8217;s just a pretty cool box and I&#8217;m proud to be involved.</p>

<p>Working at Dell KACE has been awesome. Yes, it&#8217;s strange being a &#8220;Mac developer at Dell,&#8221; but KACE is like this little startup inside of Dell. It&#8217;s like having the best parts of a startup, without having to worry about funding. And Mac is a big deal to a lot of our customers, so we take the cross-platform work very seriously. As a low-level guy, it&#8217;s interesting keeping everything working smoothly across Windows, Mac and Linux (and what seems an ever-growing number of Linux distros&#8230;.)</p>

<p>I&#8217;ll try to bring back some Cocoa content soon. When not pushing product out the door at KACE, I&#8217;ve been continuing work on my upcoming book, which takes up most of my non-KACE time. The title is now official: <em>iOS 5 Programming: Pushing the Limits with Mobile Apps for Apple iPhone, iPad, and iPod Touch</em>. It should be out in a few months. I&#8217;m working with Mugunth Kumar, who has been an excellent coauthor, and I&#8217;m really pleased to have added Mithilesh Kumar (no relation) as technical editor. I like how the book is shaping up. We&#8217;re targeting those who want a more advanced iOS book, focused on the non-obvious things you can&#8217;t easily get by skimming the docs. Hope you all like it.</p>
<img src="http://feeds.feedburner.com/~r/Cocoaphony/~4/UKYbQo5h-Qw" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://robnapier.net/blog/kace-m300-557/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		<feedburner:origLink>http://robnapier.net/blog/kace-m300-557</feedburner:origLink></item>
		<item>
		<title>Laying out text with Core Text</title>
		<link>http://feedproxy.google.com/~r/Cocoaphony/~3/SJig_POfQLE/laying-out-text-with-coretext-547</link>
		<comments>http://robnapier.net/blog/laying-out-text-with-coretext-547#comments</comments>
		<pubDate>Fri, 20 May 2011 15:09:12 +0000</pubDate>
		<dc:creator>Rob Napier</dc:creator>
				<category><![CDATA[cocoa]]></category>
		<category><![CDATA[iphone]]></category>

		<guid isPermaLink="false">http://robnapier.net/blog/?p=547</guid>
		<description><![CDATA[Today's question is about laying out text without CTFramesetter. We're going to take a whirlwind tour through some CoreText code to demonstrate this.]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m back in full book-writing mode, now working with <a href="http://mugunthkumar.com/">Mugunth Kumar</a>, who is brilliant. Go check out his stuff. Hopefully we&#8217;ll have something published and in all your hands by the end of the year. The book has taken up most of my writing time, so the blog will continue to be a bit quiet, but sometimes I like to answer a Stackoverflow question a bit more fully than I can there.</p>

<p>Today&#8217;s question is about <a href="http://stackoverflow.com/questions/6062420/core-text-performance/6062816#6062816">laying out text without <code>CTFramesetter</code></a>. We&#8217;re going to take a whirlwind tour through some CoreText code to demonstrate this. It&#8217;s not quite what the OP was asking about, but it shows some techniques and I had it handy. I&#8217;ll be writing a whole chapter on Core Text soon.</p>

<p>The goal of this project was to make &#8220;pinch&#8221; view. It lays out text in a view, and where ever you touch, the text is pinched towards that point. It&#8217;s not meant to be really useful. Everything is done in <code>drawRect:</code>, which is ok in this case, since we only draw when we&#8217;re dirty, and when we&#8217;re dirty we have to redraw everything anyway. But in many cases, you&#8217;d want to do these calculations elsewhere, and only do final drawing in <code>drawRect:</code>.</p>

<p>We start with some basic view layout, and loop until we run out of text or run out of vertical space in the view.
<span id="more-547"></span></p>

<pre><code>- (void)drawRect:(CGRect)rect {      
  [... Basic view setup and drawing the border ...]

  // Work out the geometry
  CGRect insetBounds = CGRectInset([self bounds], 40.0, 40.0);
  CGPoint textPosition = CGPointMake(floor(CGRectGetMinX(insetBounds)),
                                     floor(CGRectGetMaxY(insetBounds)));
  CGFloat boundsWidth = CGRectGetWidth(insetBounds);

  // Calculate the lines
  CFIndex start = 0;
  NSUInteger length = CFAttributedStringGetLength(attributedString);
  while (start &lt; length &amp;&amp; textPosition.y &gt; insetBounds.origin.y)
  {
</code></pre>

<p>Now we ask the typesetter to break off a line for us.</p>

<pre><code>    CTTypesetterRef typesetter = self.typesetter;
    CFIndex count = CTTypesetterSuggestLineBreak(typesetter, start, boundsWidth);
    CTLineRef line = CTTypesetterCreateLine(typesetter, CFRangeMake(start, count));
</code></pre>

<p>And decide whether to full-justify it or not based on whether it&#8217;s at least 85% of a line:</p>

<pre><code>    CGFloat ascent;
    CGFloat descent;
    CGFloat leading;
    double lineWidth = CTLineGetTypographicBounds(line, &amp;ascent, &amp;descent, &amp;leading);

    // Full-justify if the text isn't too short.
    if ((lineWidth / boundsWidth) &gt; 0.85)
    {
      CTLineRef justifiedLine = CTLineCreateJustifiedLine(line, 1.0, boundsWidth);
      CFRelease(line);
      line = justifiedLine;
    }
</code></pre>

<p>Now we start pulling off one <code>CTRun</code> at a time. A run is a series of glyphs within a line that share the same formatting. In our case, we should generally just have one run per line. This is a good point to explain the difference between a glyph and character. A character represents a unit of information. A glyph represents a unit of drawing. In the vast majority of cases in English, these two are identical, but there a few exceptions even in English called ligatures. The most famous is &#8220;fi&#8221; which in some fonts is drawn as a single glyph. Open TextEdit. Choose Lucida Grande 36 point font. Type &#8220;fi&#8221; and see for yourself how it&#8217;s drawn. Compare it to &#8220;ft&#8221; if you think it&#8217;s just drawing the &#8220;f&#8221; too wide. The joining is on purpose.</p>

<p>So the thing to keep in mind is that there can be a different number of glyphs than characters. High-level Core Text objects work in characters. Low-level objects work in glyphs. There are functions to convert character indexes into glyph indexes and vice versa. So, let&#8217;s back to the code. We&#8217;re going to move the Core Graphics text pointer and start looping through our <code>CTRun</code> objects:</p>

<pre><code>    // Move us forward to the baseline
    textPosition.y -= ceil(ascent);
    CGContextSetTextPosition(context, textPosition.x, textPosition.y);

    // Get the CTRun list
    CFArrayRef glyphRuns = CTLineGetGlyphRuns(line);
    CFIndex runCount = CFArrayGetCount(glyphRuns);

    // Saving for later in case we need to use the actual transform. It's faster
    // to just add the translate (see below).
    //      CGAffineTransform textTransform = CGContextGetTextMatrix(context);
    //      CGAffineTransform inverseTextTransform = CGAffineTransformInvert(textTransform);

    for (CFIndex runIndex = 0; runIndex &lt; runCount; ++runIndex)
    {
</code></pre>

<p>Now we have our run, and we&#8217;re going to work out the font so we can draw it. By definition, the entire run will have the same font and other attributes. Note that the code only handles font changes. It won&#8217;t handle decorations like underline (remember: bold is a font, underline is a decoration). You&#8217;d need to add more code if you wanted that.</p>

<pre><code>      CTRunRef run = CFArrayGetValueAtIndex(glyphRuns, runIndex);
      CTFontRef runFont = CFDictionaryGetValue(CTRunGetAttributes(run),
                                               kCTFontAttributeName);

      // FIXME: We could optimize this by caching fonts we know we use.
      CGFontRef cgFont = CTFontCopyGraphicsFont(runFont, NULL);
      CGContextSetFont(context, cgFont);
      CGContextSetFontSize(context, CTFontGetSize(runFont));
      CFRelease(cgFont);
</code></pre>

<p>Now we&#8217;re going to pull out all the glyphs so we can lay them out one at a time. <code>CTRun</code> has one of those annoying <code>Get...Ptr</code> constructs that are common in Core frameworks. <code>CTRunGetPositionsPtr()</code> will very quickly return you the internal pointer to the glyphs locations. But it might fail if the <code>CTRun</code> hasn&#8217;t calculating them yet. If that happens, then you have to call <code>CTRunGetPositions()</code> and hand it a buffer to copy into. To handle this, I keep around a buffer that I <code>realloc()</code> to the largest size I need. This almost never comes up because <code>CTRunGetPositionsPtr()</code> almost always returns a result.</p>

<p>Note the comment about being &#8220;slightly dangerous.&#8221; I&#8217;m grabbing the internal location data structures and modifying them. This works out because we are the only user of this <code>CTRun</code>, but these are really immutable structures. If two <code>CTRun</code> objects are created from the same data, then Apple is free to return us two pointers to the same object. So it&#8217;s within the specs that we&#8217;re actually modifying data that some other part of the program is using for a different layout. That&#8217;s pretty unlikely, but it&#8217;s worth keeping in mind. My early tests of this on a first-generation iPad suggested that this optimization was noticeable in Instruments. On the other hand, I hadn&#8217;t applied some other optimizations yet (like reusing <code>positionsBuffer</code>), so it may be practical to get better safety and performance here. I&#8217;ll have to profile further.</p>

<pre><code>      CFIndex glyphCount = CTRunGetGlyphCount(run);

      // This is slightly dangerous. We're getting a pointer to the internal
      // data, and yes, we're modifying it. But it avoids copying the memory
      // in most cases, which can get expensive.
      CGPoint *positions = (CGPoint*)CTRunGetPositionsPtr(run);
      if (positions == NULL)
      {
        size_t positionsBufferSize = sizeof(CGPoint) * glyphCount;
        if (malloc_size(positionsBuffer) &lt; positionsBufferSize)
        {
          positionsBuffer = realloc(positionsBuffer, positionsBufferSize);
        }
        CTRunGetPositions(run, kRangeZero, positionsBuffer);
        positions = positionsBuffer;
      }

      // This one is less dangerous since we don't modify it, and we keep the const
      // to remind ourselves that it's not to be modified lightly.
      const CGGlyph *glyphs = CTRunGetGlyphsPtr(run);
      if (glyphs == NULL)
      {
        size_t glyphsBufferSize = sizeof(CGGlyph) * glyphCount;
        if (malloc_size(glyphsBuffer) &lt; glyphsBufferSize)
        {
          glyphsBuffer = realloc(glyphsBuffer, glyphsBufferSize);
        }
        CTRunGetGlyphs(run, kRangeZero, (CGGlyph*)glyphs);
        glyphs = glyphsBuffer;
      }
</code></pre>

<p>Now we move around the characters with a little trig. I originally coded this using <code>CGAffineTransforms</code>, but doing the math by hand turned out to be much faster.</p>

<pre><code>      // Squeeze the text towards the touch-point
      if (touchIsActive)
      {
        for (CFIndex glyphIndex = 0; glyphIndex &lt; glyphCount; ++glyphIndex)
        {
          // Text space -&gt; user space
          // Saving the transform in case we ever want it, but just applying
          // the translation by hand is faster.
          // CGPoint viewPosition = CGPointApplyAffineTransform(positions[glyphIndex], textTransform);
          CGPoint viewPosition = positions[glyphIndex];
          viewPosition.x += textPosition.x;
          viewPosition.y += textPosition.y;

          CGFloat r = sqrtf(hypotf(viewPosition.x - touchPoint.x,
                                   viewPosition.y - touchPoint.y)) / 4;
          CGFloat theta = atan2f(viewPosition.y - touchPoint.y, 
                                 viewPosition.x - touchPoint.x);
          CGFloat g = 10;

          viewPosition.x -= floorf(cosf(theta) * r * g);
          viewPosition.y -= floor(sinf(theta) * r * g);

          // User space -&gt; text space
          // Note that this is modifying an internal data structure of the CTRun.
          // positions[glyphIndex] = CGPointApplyAffineTransform(viewPosition, inverseTextTransform);
          viewPosition.x -= textPosition.x;
          viewPosition.y -= textPosition.y;
          positions[glyphIndex] = viewPosition;
        }
      }
</code></pre>

<p>Finally, finally, we draw the glyphs and move down a line. We move down by adding the previous-calculated descent, leading and then +1. The &#8220;+1&#8243; was added because it matches up with how CTFramesetter lays out. Otherwise the descenders of one line exactly touch the ascenders of the next line.</p>

<pre><code>      CGContextShowGlyphsAtPositions(context, glyphs, positions, glyphCount);
    }

    // Move the index beyond the line break.
    start += count;
    textPosition.y -= ceilf(descent + leading + 1); // +1 matches best to CTFramesetter's behavior  
    CFRelease(line);
  }
  free(positionsBuffer);
  free(glyphsBuffer);
}
</code></pre>

<p>So there you have it. It&#8217;s a whirlwind tour showing how to lay glyphs out one-by-one. Attached is an example project showing it in real life.</p>

<p><a href='http://robnapier.net/blog/wp-content/uploads/2011/05/TextDemo.zip'>TextDemo</a></p>
<img src="http://feeds.feedburner.com/~r/Cocoaphony/~4/SJig_POfQLE" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://robnapier.net/blog/laying-out-text-with-coretext-547/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		<feedburner:origLink>http://robnapier.net/blog/laying-out-text-with-coretext-547</feedburner:origLink></item>
	</channel>
</rss><!-- Dynamic page generated in 0.845 seconds. --><!-- Cached page generated by WP-Super-Cache on 2012-01-30 00:39:17 --><!-- Compression = gzip -->

