tag:blogger.com,1999:blog-297154392024-03-16T08:08:04.193+01:000xced[Cocoa hack];Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.comBlogger41125tag:blogger.com,1999:blog-29715439.post-66447716778741322112012-08-18T15:34:00.001+02:002012-08-26T10:20:35.029+02:00Debugging with ARC and Zombies enabledIf you tried to debug your app with zombies enabled under ARC, you may have noticed that you don’t get zombies anymore. That’s most probably because of a bug in the Foundation framework that affects iOS 5 and OS X 10.7. This bug <em>prevents ARC from "cleaning up" instance variables at deallocation-time</em> according to Apple <a href="http://developer.apple.com/library/mac/qa/qa1758/index.html">Technical Q&A QA1758</a>. Apple strongly encourages you to <em>run your app on an iOS 6+, or OS X 10.8+ system when debugging with Zombies</em>.<br />
In the meantime, you can workaround this bug if you are using ARC and trying to debug zombies on older systems. Just compile this <a href="https://raw.github.com/gist/2731698/NSObject+ARCZombie.m">NSObject+ARCZombie.m</a> file in your project and ivars will be automatically deallocated under ARC when zombies are enabled, as it should be.<br />
<script src="https://gist.github.com/2731698.js?file=NSObject+ARCZombie.m" type="text/javascript"></script>
Now you might think <i>Hey, you are swizzling dealloc, isn't that dangerous?</i> Well, yes it is, but note that the dealloc method is only swizzled when the <tt>NSZombieEnabled</tt> environment variable is enabled, so even if this code slips in your release build, il will cause no harm at all.<br />
<br />
<b>UPDATE:</b> After I posted this, <a href="https://twitter.com/gparker/status/236919839715889153">Greg Parker warned me</a> on twitter:<br />
<blockquote>
@0xced That'll cause over-release of associated objects on some systems.</blockquote><br />
So use this workaround with care and remember that the best solution is what Apple recommends: run your app on an iOS 6+ or OS X 10.8+ system when debugging with Zombies.Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com8tag:blogger.com,1999:blog-29715439.post-64004717934025735002012-08-08T01:00:00.001+02:002012-09-13T00:31:40.498+02:00Prepare your apps for the iPhone 5<p>Earlier today, my friend Peter Steinberger <a href="https://twitter.com/steipete/status/232912281724391426">asked</a>:</p>
<blockquote>
<p>Does anyone know what does “special iPhone Simulator tweaks” are? I’d love to test my stuff with different resolutions. <a href="http://9to5mac.com/2012/08/07/upcoming-ios-6-is-scalable-to-taller-640-x-1136-iphone-display-shows-possible-next-generation-device-user-interface/">http://9to5mac.com/2012/08/07/upcoming-ios-6-is-scalable-to-taller-640-x-1136-iphone-display-shows-possible-next-generation-device-user-interface/</a></p>
</blockquote>
<p>So I investigated, and found a pretty elegant solution. Without further ado, here is how to change the size of the iOS simulator in order to test your apps in resolutions never seen before.</p>
<ol>
<li>Download <a href="http://cl.ly/Ib97">File.txt</a> into <tt>~/Library/Application Support/iPhone Simulator</tt> (don't change the name of <tt>File.txt</tt>)</li>
<li>Edit <tt>/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Applications/iPhone Simulator.app/Contents/Resources/Devices/iPhone (Retina).deviceinfo/Info.plist </tt>and add the following keys:
<pre><key>eagle</key>
<string>640</string>
<key>giraffe</key>
<string>1136</string></pre>
</li>
<li>Add <tt>setenv("CLASSIC", "0", 1);</tt> just before <tt>UIApplicationMain</tt> in your <tt>main.m</tt> file in order to support any simulator version.</li>
<li>Quit the <em>iOS Simulator</em> app</li>
</ol>
<p>You are now ready to test your apps on the rumored 640×1136 new iPhone.</p>
<p>This hack works on Xcode 4.4.1+ and the iPhone 5.1+ Simulator with the iPhone (Retina) device.</p>
Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com27tag:blogger.com,1999:blog-29715439.post-77648657278557020022011-05-26T00:40:00.001+02:002011-05-26T00:40:56.917+02:00How to restore Apple Front Row Trailers<p><a href="#english">English version below</a></p>
<p>Comme vous avez pu le constater, <strong>Front Row Trailers</strong> ne fonctionne plus. Étant donné que <a href="http://www.9to5mac.com/53989/front-row-is-missing-from-10-7-lion/">Front Row est absent de Lion</a>, je ne mettrai plus à jour Front Row Trailers.</p>
<p>Voici donc la méthode pour remettre les bandes-annonces originales d'Apple:</p>
<ol>
<li>Téléchargez et décompressez <a href="http://pitaya.ch/trailers/Restore-Apple-Trailers.zip">Restore Apple Trailers</a></li>
<li>Double-cliquez sur le fichier <em>Restore Apple Trailers.command</em></li>
<li>Entrez votre mot de passe</li>
</ol>
<p><a name="english"></a></p>
<hr />
<p>As you probably have noticed, <strong>Front Row Trailers</strong> is not working anymore. Since <a href="http://www.9to5mac.com/53989/front-row-is-missing-from-10-7-lion/">Front Row is missing in Lion</a>, I have no plan to update Front Row Trailers.</p>
<p>So here is how to restore factory Apple trailers:</p>
<ol>
<li>Download and decompress <a href="http://pitaya.ch/trailers/Restore-Apple-Trailers.zip">Restore Apple Trailers</a></li>
<li>Double-click on <em>Restore Apple Trailers.command</em></li>
<li>Enter your password</li>
</ol>Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com2tag:blogger.com,1999:blog-29715439.post-28948864826552740122011-02-14T10:24:00.001+01:002011-02-14T10:24:15.830+01:00Using Ingredients as Xcode documentation browser<p>Xcode 3.2.3 documentation browser was quite broken: ⌘ + ⌥ + double click on a method or function would open the documentation browser but would not scroll to the method you just asked documentation for. So I started to write an Xcode plugin to open <a href="http://fileability.net/ingredients/">Ingredients</a> instead. Then Xcode 3.2.5 was released (I skipped version 3.2.4) and this annoying bug was fixed, so I did not bother to publish my plugin. But recently, I <a href="http://twitter.com/Jamie314/statuses/35780483673489408">read</a> <a href="http://twitter.com/chockenberry/status/36254840791240704">on</a> <a href="http://twitter.com/sbrocket/statuses/36345906722775040">twitter</a> <a href="http://twitter.com/pilky/status/36464940403392512">that</a> [RED4CTED] documentation browser was even more broken, so I decided it was time to release my <a href="https://github.com/0xced/Ingredients">Xcode-Ingredients</a> plugin.<br /><br />Note that the plugin is written for Xcode 3, so things have probably changed in [RED4CTED] (which I have not yet tried). <a href="http://twitter.com/Nyx0uf/status/35359283524796416">If</a> <a href="http://twitter.com/Pwc/statuses/34328778305970177">you</a> <a href="http://twitter.com/rentzsch/status/34807806024491008">are</a> <a href="http://twitter.com/command_tab/status/34822017022824448">brave</a> <a href="http://twitter.com/tonyarnold/status/34846220744663040">enough</a> <a href="http://twitter.com/bwebster/status/35125366972481536">to</a> <a href="http://twitter.com/dannygreg/status/35310086041763840">use</a> [RED4CTED], that you know a bit about Xcode plugins (<a href="twitter:@kodz">@kodz</a>, <a href="twitter:@rentzsch">@rentzsch</a>, <a href="twitter:@tjw">@tjw</a>?) and that you would like to use Ingredients for reading the documentation, please have a look and adapt the plugin for [RED4CTED]. In order to discover the <strong>searchForAPIString:</strong> method, I just set a breakpoint on <strong>makeKeyAndOrderFront:</strong> and looked at the backtrace, but I'm not sure if this technique is still applicable.</p>
<p>I will update this blog post as soon as someone can get the plugin to work with [RED4CTED].</p>Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com0tag:blogger.com,1999:blog-29715439.post-7529439708420854752010-09-11T13:46:00.001+02:002010-09-11T13:46:58.742+02:00CLLocation getDistanceFrom: vs distanceFromLocation:In <a href="http://www.drobnik.com/touch/2010/09/backwards-compatibility-if-apple-starts-polishing/">Backwards Compatibility if Apple Starts Polishing</a>, Oliver Drobnik explains how he solved the <tt><a href="http://developer.apple.com/library/ios/#documentation/CoreLocation/Reference/CLLocation_Class/DeprecationAppendix/AppendixADeprecatedAPI.html%23//apple_ref/occ/instm/CLLocation/getDistanceFrom:">getDistanceFrom:</a></tt> vs <tt><a href="http://developer.apple.com/library/ios/#documentation/CoreLocation/Reference/CLLocation_Class/CLLocation/CLLocation.html%23//apple_ref/doc/uid/TP40007126-CH3-SW18">distanceFromLocation:</a></tt> problem. His solution involves a new method (<tt>distanceBetweenLocation:andLocation:</tt>) and eight lines of code for calling <tt>getDistanceFrom:</tt> through a NSInvocation.<br /><br />Wouldn't it be nice if instead you could use <tt>[aLocation distanceFromLocation:anotherLocation]</tt> everywhere in your code, while still retaining backward compatibility? Enter the Objective-C runtime! In your <tt>main.m</tt> file, first <tt>#import <objc/runtime.h></tt> and at the very beginning of your <tt>main</tt> function, add this:<br /><br /><pre class="brush: objc"><br />Method getDistanceFrom = class_getInstanceMethod([CLLocation class], @selector(getDistanceFrom:));<br />class_addMethod([CLLocation class], @selector(distanceFromLocation:), method_getImplementation(getDistanceFrom), method_getTypeEncoding(getDistanceFrom));<br /></pre><br /><br />Here you go, <tt>-[CLLocation distanceFromLocation:]</tt> available at runtime in any iOS version with only two lines of code. Note that on iOS 3, <tt>class_addMethod</tt> will add the <tt>distanceFromLocation:</tt> method to the <tt>CLLocation</tt> class using <tt> getDistanceFrom:</tt> implementation. On iOS 4, <tt>class_addMethod</tt> will do nothing as the method already exists.Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com12tag:blogger.com,1999:blog-29715439.post-37534115801815204512010-07-02T18:38:00.011+02:002010-09-13T21:13:05.746+02:00Using SDK 3.1.3 with iPhone SDK 4<p>In iPhone SDK 4 Apple has removed all 3.x SDKs. I think that was <a href="http://twitter.com/0xced/status/17401527949">not a very good idea</a> to say the least. I can understand Apple wants developers to adopt the new features of iOS 4, but breaking zillions of Xcode projects is probably not a good way to win the hearts of those developers. Well, this is probably not Apple's goal anyway.</p><p>Sure there is a way to get your projects working again with the iPhone SDK 4, just set the Base SDK of your project to iOS 4:</p><p><img style="display: block; margin-left: auto; margin-right: auto; border:none;" title="BaseSDK4.png" src="http://pitaya.ch/blog/BaseSDK4.png" border="0" alt="Base SDK 4" width="556" height="673" /></p><p>and set the Deployment Target to iOS 3:</p><p><img style="display: block; margin-left: auto; margin-right: auto; border:none;" title="DeploymentTarget3.png" src="http://pitaya.ch/blog/DeploymentTarget3.png" border="0" alt="Deployment Target 3" width="553" height="740" /></p><p>This will get your projects compile and run again on both iOS 3 and 4. That's fine. Well, except when you want to make sure your project do not use any new iOS 4 only API.</p><p>The problem is, code like this will now compile without any warning or error:</p><p style="margin: 0.0px 0.0px 0.0px 0.0px; font: 9.0px ProFontX;"><span style="font-family: Monaco;">[[<span style="color: #7e11ad;">NSUserDefaults</span> <span style="color: #480085;">standardUserDefaults</span>] setURL:defaultURL forKey:<span style="color: #e90000;">@"DefaultURL"</span>];</span></p><p>It's easier than you think to use a shiny new API without even noticing you are using something not available on iOS 3. This setURL:forKey: method is new in iOS 4 so this code will fail with an unrecognized selector exception when run on iOS 3. Unfortunately, the only way to catch this kind of oversight is to run it on an iOS 3 device. Or you can read the documentation of every single method and function you are calling to make sure it was not introduced in iOS 4. But this does not seem very reasonable, does it? The tools should help us detecting these problems at <strong>compile time</strong>, not runtime!</p><p>Here is how to get Xcode 3.2.3 and iPhone SDK 4 working with SDK 3.1.3.</p><ol><li>Log in to the <a href="http://developer.apple.com/devcenter/ios/">iOS Dev Center</a></li><li>Download the <a href="http://developer.apple.com/ios/download.action?path=/iphone/iphone_sdk_3.1.3__final/iphone_sdk_3.1.3_with_xcode_3.2.1__snow_leopard__10m2003a.dmg">iPhone SDK 3.1.3</a> into <tt>~/Downloads</tt></li><li>Run the <a href="http://gist.github.com/577788">install-iphone-sdk-3.1.3.sh</a> script</li><li>Quit and relaunch Xcode</li><li>Duplicate your <em>Debug</em> configuration and rename it to <em>SDK 3.1.3 Check</em></li><li>Set the Base SDK of this new configuration to <em>iPhone Simulator 3.1.3</em></li><li>Add a User-Defined Setting <tt><strong>GCC_OBJC_ABI_VERSION</strong></tt> and set its value to <tt><strong>1</strong></tt> (one)</li><li>Select <tt>GCC_OBJC_ABI_VERSION</tt> and choose <i>Add Build Setting Condition</i> from the gear pop-up button at the lower left of the window.</li><li>Select <i>Any iPhone Simulator</i> and <i>Any Architecture</i></li></ol><p>There you go! By selecting the <em>SDK 3.1.3 Check</em> configuration, you get compile time check for misusing iOS 4 only APIs when targeting iOS 3.</p><p>Note that compiling with the iPhone Simulator 3.1.3 SDK and running in Xcode 3.2.3 simulator is not supported. What you get with this hack is just a compile time check of the APIs you are using. So do not expect to run your app on a 3.1.3 simulator.</p>Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com23tag:blogger.com,1999:blog-29715439.post-65311916845811821732010-06-02T08:51:00.017+02:002013-04-23T14:47:15.651+02:00Fixing -[NSMutableURLRequest setValue:forHTTPHeaderField:]<h4><span style="color: #cc0000;">UPDATE</span></h4>This problem is fixed as of Mac OS X 10.8.3+ and iOS 5.1+. I have no idea when it was actually fixed since radar sucks so much.<h4>The problem</h4>From NSMutableURLRequest <a href="http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSMutableURLRequest_Class/Reference/Reference.html#//apple_ref/occ/instm/NSMutableURLRequest/setValue:forHTTPHeaderField:">setValue:forHTTPHeaderField:</a> documentation:<br /><blockquote>In keeping with the HTTP RFC, HTTP header field names are case-insensitive.</blockquote><br />I see three problems with this.<ol><li>Assuming all HTTP implementations are RFC compliant is foolish to say the least.</li><li>Enforcing case-insensitivity is nonsense.</li><li>This bit of documentation is accurate, the case of header fields is actually changed.</li></ol>Trying this<br /><pre class="brush: objc"> NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.example.com"]];<br /> [request setValue:@"MyValue" forHTTPHeaderField:@"MyField"];<br /> NSLog(@"result: %@", [request allHTTPHeaderFields]);</pre>outputs<br /><pre> result: {<br /> Myfield = MyValue;<br /> }</pre>Notice how the <tt>'F'</tt> of <tt>MyField</tt> was lowercased, that sucks! If the HTTP server you are trying to communicate with is case-sensitive, you are screwed. I filed <a href="http://www.openradar.me/8029516">radar #8029516</a>, which is a duplicate of radar #3131623, which means there is almost no chance to have this fixed anytime soon.<br /><br />When this happens, you should contact the server administrator asking for a case-insensitive implementation. But in the meantime, if you badly need to preserve the case of your headers, read on.<br /><br /><h4>The investigation</h4>First, let's fire <a href="http://otx.osxninja.com">otx</a> to disassemble the Foundation framework in order to have a look at <tt>setValue:forHTTPHeaderField:</tt> implementation. I strongly suggest you to use otx version from subversion <a href="http://otx.osxninja.com/builds/trunk">trunk</a>, as it is better at resolving symbols in tail call optimizations (fixed in r553).<br /><br />Excerpt from otx 0.16b:<br /><pre> +18 [...] movl %eax,0x08(%ebp)<br /> +21 [...] leave<br /> +22 [...] jmpl 0x002c52bb</pre><br />Excerpt from otx trunk:<br /><pre> +18 [...] movl %eax,0x08(%ebp)<br /> +21 [...] leave<br /> +22 [...] jmpl 0x002c52bb _CFURLRequestSetHTTPHeaderFieldValue</pre><br />Much better, isn't it?<br /><br />So let's go through the function calls of <tt>setValue:forHTTPHeaderField:</tt>. With a basic static analysis of the Foundation and CFNetwork disassemblies, we can draw the following call path:<br /><pre>-[NSMutableURLRequest(NSMutableHTTPURLRequest) setValue:forHTTPHeaderField:]<br /> CFURLRequestSetHTTPHeaderFieldValue()<br /> URLRequest::setHTTPHeaderFieldValue()<br /> HTTPRequest::setHeaderFieldValue()<br /> CFHTTPMessageSetHeaderFieldValue()<br /> HTTPMessage::setHeaderFieldValue()<br /> _CFCapitalizeHeader()</pre><br /><tt><strong>_CFCapitalizeHeader</strong></tt> looks like a very good candidate for being the bastard changing the case of our headers. A quick search reveals the <a href="http://opensource.apple.com/source/CFNetwork/CFNetwork-129.20/HTTP/CFHTTPMessage.c">source code</a> of CFNetwork that was open source long time ago. Although the open source implementation does not exactly match what we see in the disassembly (it is now using <tt>toupper</tt> instead of <tt>ch + 'A' - 'a'</tt> for example), we are now absolutely sure that <tt>_CFCapitalizeHeader</tt> is the function responsible for messing with our headers.<br /><br />Now, let's check is if there is a path that will not call <tt>_CFCapitalizeHeader</tt> and if we can somehow influence the condition that would avoid the call to <tt>_CFCapitalizeHeader</tt>. This is quickly checked, especially if you enabled the <em>Separate logical blocks</em> option of otx (-b option for cli).<br /><pre>HTTPMessage::setHeaderFieldValue(__CFString const*, __CFString const*)<br /> +0 000515a4 55 pushl %ebp<br /> +1 000515a5 89e5 movl %esp,%ebp<br /> +3 000515a7 83ec28 subl $0x28,%esp<br /> +6 000515aa 8b4508 movl 0x08(%ebp),%eax<br /> +9 000515ad 8975f8 movl %esi,0xf8(%ebp)<br /> +12 000515b0 8b7510 movl 0x10(%ebp),%esi<br /> +15 000515b3 897dfc movl %edi,0xfc(%ebp)<br /> +18 000515b6 8945f4 movl %eax,0xf4(%ebp)<br /> +21 000515b9 8b450c movl 0x0c(%ebp),%eax<br /> +24 000515bc 890424 movl %eax,(%esp)<br /> +27 000515bf e88a26fbff calll __CFCapitalizeHeader<br /> +32 000515c4 c744240cffffffff movl $0xffffffff,0x0c(%esp)<br /> +40 000515cc 89742408 movl %esi,0x08(%esp)<br /> +44 000515d0 89c7 movl %eax,%edi</pre><br />We see that there is no path that avoid the call to <tt>_CFCapitalizeHeader</tt>. So we are left with the last resort solution: patching <tt>_CFCapitalizeHeader</tt>. With <a href="http://www.unsanity.com/haxies/apesdk/">APE Lite</a>, function patching is very easy. You first use <tt>APEFindSymbol()</tt> to find the address of a non-exported symbol (i.e. <tt>__CFCapitalizeHeader</tt>), then <tt>APEPatchCreate()</tt> to replace a function implementation with your own, while still keeping a reference to the original implementation. On iPhone OS, you can use <a href="http://github.com/0xced/APELite-arm">APE Lite+arm</a> (my implementation of the APE Lite API using MobileSubstrate).<br /><br /><h4>The solution</h4><a href="http://gist.github.com/414439">NSMutableURLRequest+CaseSensitive</a> is a category on NSMutableURLRequest that adds these three methods:<br /><pre class="brush: objc"> - (void) setAllHTTPHeaderFields:(NSDictionary *)headerFields caseSensitive:(BOOL)caseSensitive;<br /> - (void) setValue:(NSString *)value forHTTPHeaderField:(NSString *)field caseSensitive:(BOOL)caseSensitive;<br /> - (void) addValue:(NSString *)value forHTTPHeaderField:(NSString *)field caseSensitive:(BOOL)caseSensitive;</pre><br />Just pass <tt>caseSensitive:YES</tt> for preserving the case of your header fields.<br /><br /><strong><em>Warning</em></strong>: you SHOULD NOT use this in production code. But hey, HTTP implementations SHOULD be case-insensitive ;-)Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com11tag:blogger.com,1999:blog-29715439.post-44736401049863680322010-04-08T10:27:00.005+02:002011-04-18T09:41:39.296+02:00ABGetMe with the iPhone SDKUnlike the Mac Address Book API, the iPhone Address Book API does not come with the <tt><a href="http://developer.apple.com/library/mac/documentation/userexperience/Reference/AddressBook/C/ABAddressBookRef/Reference/reference.html#//apple_ref/c/func/ABGetMe">ABGetMe</a></tt> function. That's a pity, but we can do something about it.<br /><br />Recently, I used <tt>MFMailComposeViewController</tt> for the first time and I realized that it was displaying my e-mail address. Nicolas Seriot already demonstrated how to retrieve all your e-mail account information in <a href="http://github.com/nst/SpyPhone/blob/master/Classes/SPSourceEmailTVC.m">SpyPhone</a>, but I felt there was a more lightweight method for retrieving the e-mail addresses by taking advantage of the MessageUI framework. A class-dump and five minutes later, here is my solution:<br /><script src="http://gist.github.com/356975.js?file=emailAddresses.m"></script>Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com4tag:blogger.com,1999:blog-29715439.post-85678612076196360372010-01-03T11:22:00.011+01:002011-06-03T09:24:08.748+02:00Jasscore pour iPhoneMa première application iPhone, <a href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=333215756&mt=8">Jasscore</a> (lien iTunes) est enfin disponible sur l'AppStore au prix de 1.10 CHF. C'est une ardoise virtuelle qui vous permet de compter vos points et ceux de votre adversaire au jass (chibre, mise, etc.)<br /><h4>Les features</h4><ul><li>En français et en allemand</li><li>Editez le nom des équipes et les scores à atteindre</li><li>Notez les scores et les annonces</li><li>Multiplication des scores de 1x à 7x</li><li>Grand affichage afin que tous les joueurs puissent suivre les scores</li></ul><h4>Les captures d'écran</h4><br /><img style="display:block; margin:0px auto 10px; text-align:center; width: 320px; height: 460px; border:none;" src="http://pitaya.ch/jasscore/_Media/capture1-2.png" alt="Score" /><br /><img style="display:block; margin:0px auto 10px; text-align:center; width: 320px; height: 460px; border:none;" src="http://pitaya.ch/jasscore/_Media/capture2-2.png" alt="Réglage" /><br /><img style="display:block; margin:0px auto 10px; text-align:center; width: 320px; height: 460px; border:none;" src="http://pitaya.ch/jasscore/_Media/capture3-2.png" alt="Grand Affichage" /><br /><br />Je suis ouvert à vos suggestions, alors n'hésitez pas à laisser un commentaire ou à m'envoyer un e-mail.Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com2tag:blogger.com,1999:blog-29715439.post-11027712859440997852009-11-07T17:30:00.002+01:002009-11-07T17:32:49.361+01:00CLAlert: NSAlert done rightFrom NSAlert documentation:<br /><blockquote> Currently, there is no visual difference between informational and warning alerts.</blockquote><br />This was written at the time of Panther and this is still true in Snow Leopard. So why the hell is the <tt> setAlertStyle:</tt> method provided?<br /><br /><blockquote><pre><i>NSCriticalAlertStyle</i></pre> This style causes the icon to be badged with a caution icon.</blockquote><br />A caution icon, for a critical alert? That does not make sense!<br /><br />Here are screenshots of the default behaviours with the three different alert styles:<br /><br /><img src="http://s3.amazonaws.com/ember/E4sAwxdoThOmW8T7g8P9jhfuzW3EqWwr_o.png"><br /><br /><img src="http://s3.amazonaws.com/ember/uCiR6p6OYppncj2LrWuJFw02O0pKyz6O_o.png"><br /><br /><img src="http://s3.amazonaws.com/ember/uTbANc0CnNZrYhsanbIoeKqQ2LRMRkUu_o.png"><br /><br />The documentation is unfortunately right: there is no difference between the informational and the warning style. The critical style is indeed badged with a caution icon.<br /><br />As you probably have guessed from the title of this post, I am not happy with this behavior, so I have written a class, <a href="http://gist.github.com/228140">CLAlert</a> (MIT License) that displays alerts the way I think it should be done. I.e. a note icon for an informational alert, a caution icon for a warning alert and a stop icon for a critical alert as you can see on these screenshots.<br /><br /><img src="http://s3.amazonaws.com/ember/pjP1TKlpfxu3WQs2zongWqpxc6qw3jsU_o.png"><br /><br /><img src="http://s3.amazonaws.com/ember/bN3wTfemTYXRppGWAdQHmpv4R3VwD9EG_o.png"><br /><br /><img src="http://s3.amazonaws.com/ember/g7Cl6JjugWuaUThao03DTv2FC8eZD5ZC_o.png"><br /><br />CLAlert is a drop-in replacement for NSAlert. It requires at lest Mac OS X 10.5.Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com2tag:blogger.com,1999:blog-29715439.post-46279253465408248352009-11-05T21:49:00.007+01:002009-11-05T22:03:20.481+01:00Watch a large number of icons with Preview.appBrowsing .icns files in Preview.app can be painful. If you try to open all the icons of the CoreTypes bundle for example with the following command <br /><pre>open /System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/*icns</pre><br />you will get this view:<br /><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 627px; height: 669px;" src="http://s3.amazonaws.com/ember/pQrNHc5zIGpjGUybRG4sZdwflBIccZo3_o.png"><br /><br />All the different sizes of the .icns are displayed, which makes it quite unpleasant to browse.<br /><br />F-Script Anywhere to the rescue!<br /><br />Install F-Script Anywhere into Preview, then do<br /><ol><br /><li>FSA → New F-Script Workspace</li><br /><li>Click the <i>Browser For Target…</i> button</li><br /><li>Click on one of the icons</li><br /><li>Choose the PVIKImageBrowserView object</li><br /><li>In the F-Script Object Browser, name the PVIKImageBrowserView <b>browserView</b></li><br /></ol><br />Finally, in the F-Script interpreter, paste the following code:<br /><pre>groups := browserView layoutManager groups.<br />count := groups count.<br />0 to:count-1 do: [:i | browserView collapseGroup:(groups objectAtIndex:i)]</pre><br /><br />Tada, a nice browsable view of your icons!<br /><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 627px; height: 669px;" src="http://s3.amazonaws.com/ember/Geyx4AcgixPnWWZt9xPJ1oAIBO0ZQrd5_o.png">Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com1tag:blogger.com,1999:blog-29715439.post-14810692472787724202009-08-19T17:41:00.003+02:002009-08-19T22:30:02.759+02:00Booting from a dmgI was <a href="http://twitter.com/jfroy/status/3334034920">pointed by</a> Jean-Francois Roy that it is possible to boot off a dmg.<br /><br />It's a simple two steps process using <a href="http://www.devklog.net/software/bootfolder.tar.bz2">bootfolder</a>:<br /><br /><tt>./makebootfolder /Path/to/OperatingSystem.dmg</tt><br /><tt>sudo ./blessbootfolder /Path/to/OperatingSystem.bootfolder</tt><br /><br />But beware: <b>you better not have a space in the path to the dmg or in the dmg itself</b>. If you happen to try this trick with a space in the path to the boot folder, then your Mac will brick. In verbose startup, you will get<br /><br /><i>Error loading kernel 'mach_kernel' (0x9)</i><br /><br />If you can't boot anymore, download <a href="http://downloads.sourceforge.net/refit/rEFIt-0.13.cdr.gz">rEFIt</a> and burn it onto a CD. Then boot on the CD by pressing the <i>C</i> key. You should see something looking like this (maybe without the linux and windows partitions):<br /><br /><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 512px; height: 384px;" src="http://refit.sourceforge.net/img/screen2.png" border="0" alt="" /><br /><br />Just press enter and you should be able to boot your Mac.Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com3tag:blogger.com,1999:blog-29715439.post-49765477876776070112009-04-08T22:39:00.008+02:002010-05-03T16:25:26.439+02:00Extract UIKit artworkWhile writing an iPhone application (more about it on this blog soon), I wanted to tint my UIAlertView with the same color I tinted my other controls. Maybe this is not a good idea, but I wanted to try anyway. Unfortunately, only UISearchBar, UISegmentedControl, UINavigationBar and UIToolbar have the tintColor property. So I started digging into UIKit. The first place to look was obviously the <tt>-(void)[UIAlertView(Private) drawRect:]</tt> method. To draw its background, a UIAlertView calls <tt>_popupAlertBackground</tt> which in turn call <tt>_UIImageWithName(@"UIPopupAlertSheetBackground.png")</tt>. This file probably lies inside the UIKit.framework would think a Mac developer. In fact it is not there. Only two pngs are there: DefaultWallpaper-iPhone.png and DefaultWallpaper-iPod.png. So, where is it?<br /><br />It is in fact somehow embedded in the <i>Other.artwork</i> file which contains UIKit artwork. This file is memory mapped at application startup, probably for performance reasons and the content is easily accessed with the private <tt>_UIImageWithName()</tt> function. <tt>_UIImageWithName</tt> looks up in a dictionary that associates a file name to some memory location that contains the actual image data.<br /><br />Now, it would be cool to extract all this content to see what artwork UIKit contains. It turns out to be quite easy. The only trick is to find a reference to the dictionary containing all the file names. Check my <a href="http://github.com/0xced/UIKit-Artwork-Extractor">UIKit Artwork Extractor</a> project on github to see how it works.<br /><br />In an attempt to encourage comments, I'm offering a beer at NSConference to the first person who will clearly explain how to find the magic address of the images dictionary (except for Nicolas who knows the answer already) and I'm offering two beers to the one who provides a <i>cleaner</i> way to obtain a reference to the dictionary that is not UIKit version specific ;-)<br /><br /><b>Edit</b>: I have updated the code so that it is not UIKit version specific, so I won the beers myself that I'm going to drink right away.Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com20tag:blogger.com,1999:blog-29715439.post-22908709208110043602009-03-23T09:34:00.016+01:002009-03-23T10:36:00.241+01:00chmod, ACL and symbolic links<p>Let's say you want to set an ACL on a symbolic link. So you try the <tt>-h</tt> option of <tt>chmod</tt> that is documented to <em>change the mode of the link itself rather than the file that the link points to.</em></p><pre style="background: #e0e0e0; overflow: auto;"> $ ln -s aFileThatDoesNotExist myLink<br /> $ /bin/chmod -h +a "everyone allow delete" myLink<br /> <b><i>chmod: Failed to set ACL on file 'myLink': No such file or directory</i></b><br /></pre><p>So, it seems <tt>myLink</tt> was followed even though we passed the <tt>-h</tt> option. Let's try with a link that points to an existing file:</p><pre style="background: #e0e0e0; overflow: auto;"> $ touch aFileThatExist<br /> $ ln -fs aFileThatExist myLink<br /> $ /bin/chmod -h +a "everyone allow delete" myLink<br /> $ ls -le myLink aFileThatExist<br /> <b><i>-rw-r--r--+ 1 cluthi staff 0 5 mar 18:11 aFileThatExist</i></b><br /> <b><i>0: group:everyone allow delete</i></b><br /> <b><i>lrwxr-xr-x 1 cluthi staff 14 5 mar 18:12 myLink -> aFileThatExist</i></b><br /></pre><p>Our hypothesis is confirmed, the link was followed. Let's investigate and have a look at chmod source code (file_cmds-188 on darwinsource).</p><p><tt>chmod.c:125</tt> variable <tt>hflag</tt> is set to <em>true</em> if the -h argument is passed<br /><tt>chmod.c:388</tt> function <tt>modify_file_acl()</tt> is called, the <tt>hflag</tt> is not passed to this function</p><p>Doh, lazy boys! Let's patch it and add a <tt>follow</tt> argument to the <tt>modify_file_acl()</tt> function:<br /><strong>int modify_file_acl(unsigned int optflags, const char *path, acl_t modifier, int position, int inheritance_level, int follow);</strong><br />and call it with <tt>modify_file_acl(..., !hflag)</tt></p><p>From acl_set(3) man: <em>The acl_set_link_np() function acts on a symlink rather than its target, if the target of the path is a symlink.</em> Perfect, that's what we need: we must call <tt>acl_set_link_np</tt> instead of <tt>acl_set_file</tt> in order not to follow the link. <br><br /><tt>chmod_acl.c:807</tt> acl_set_file is called: <tt>(0 != acl_set_file(path, ACL_TYPE_EXTENDED, oacl))</tt><br />Replace with <tt>(0 != (follow ? acl_set_file(path, ACL_TYPE_EXTENDED, oacl) : acl_set_link_np(path, ACL_TYPE_EXTENDED, oacl)))</tt></p><p>With this small modifications, the <tt>-h</tt> option should be respected. Rebuild chmod and try again:</p><pre style="background: #e0e0e0; overflow: auto;"> $ ./chmod -h +a "everyone allow delete" myLink<br /> <b><i>chmod: Failed to set ACL on file 'myLink': Operation not supported</i></b><br /> $ ln -fs aFileThatDoesNotExist myLink<br /> $ ./chmod -h +a "everyone allow delete" myLink<br /> <b><i>chmod: Failed to set ACL on file 'myLink': Operation not supported</i></b><br /></pre><p>Now, whether the link points to an existing file or not, we get the <em>Operation not supported</em> error. That's better diagnostic, but not exactly what we expected :-( So, why is it not supported? Let's dig a bit more. <tt>acl_set_link_np</tt> implementation is found at <tt>Libc-498.1.5/posix1e/acl_file.c:175</tt> and is:</p><pre style="background: #e0e0e0; overflow: auto;"> return(acl_set_file1(path, acl_type, acl, 0));<br /></pre><p>The last argument (<em>follow</em>) passed to <tt>acl_set_file1</tt> is 0 and the first lines of <tt>acl_set_file1</tt> implementation reads: </p><pre style="background: #e0e0e0; overflow: auto;"> if (follow == 0) {<br /> <b><i>/* XXX this requires some thought - can links have ACLs? */</i></b><br /> errno = ENOTSUP;<br /> return(-1);<br /> }<br /></pre><p>We have the explanation of the <em>Operation not supported</em> error we got earlier. Note that the comment is not mine, it is actually in the libc source code!</p><p>Does it mean it is impossible to set an ACL on a symlink? Does it mean our only option is to file a bug asking some Apple engineer to think harder if links can have ACLs? Fortunately no. There is a third function in the acl_set(3) API: <tt>acl_set_fd</tt>, which acts on a file descriptor. Hopefully, getting the file descriptor of a symlink is as simple as <tt>open(path, O_SYMLINK)</tt>.</p><p>That's it. With this <a href="http://pitaya.ch/radar/6264303/chmod.patch">patch</a>, you'll be able to run <tt>./chmod -h +a "everyone allow delete" myLink</tt> and have the ACL to be set on the symlink!</p><p>If you need to set ACLs on symlinks on a daily basis, I suggest you do not overwrite <tt>/bin/chmod</tt> but install the patched version of chmod in <tt>/usr/local/bin</tt>. Well, if you read that post till there, you probably know that already.</p><p>This bug has been reported to Apple and is known as <a href="rdar://problem/6264303">radar #6264303</a> which is a duplicate of <a href="rdar://problem/5684438">radar #5684438</a>.</p>Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com3tag:blogger.com,1999:blog-29715439.post-54613826970520223302009-01-04T22:38:00.008+01:002010-03-11T22:32:29.346+01:00Using your own address book in the iPhone Simulator<b>Jailbroken iPhone with openssh:</b><br /><pre>scp mobile@iPhone:~/Library/AddressBook/* ~/Library/Application\ Support/iPhone\ Simulator/User/Library/AddressBook</pre><br /><b>Non-jailbroken iPhone:</b><br /><script src="http://gist.github.com/329671.js?file=absim.sh"></script><br />Say bye bye to John Appleseed & Co.Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com7tag:blogger.com,1999:blog-29715439.post-66102657093717333392008-10-15T21:06:00.005+02:002008-10-15T21:17:07.065+02:00Kagi Registration Module (lack of) securityI'm in the process of choosing an eCommerce partner for selling my future shareware. I have narrowed down to <a href="http://www.esellerate.net">eSellerate</a> and <a href="http://www.kagi.com">Kagi</a> as they are widely adopted by Mac shareware developers. After reading their respecting obfuscated pricing policies, I decided to have a look at what they offer for integrating the purchasing process into the application.<br /><br />Kagi offers the <a href="http://kagi.com/kagisolutions/software.php?page=krm">Kagi Registration Module</a> (KRM) which is basically a library that provides an in-application one click purchase experience. Sounds pretty good. I start reading the KRM developer documentation and stumble on the <i>Security</i> section:<br /><blockquote>The ZonicKRM submits orders through an SSL connection for security, however pricing information is currently passed from the application to the ZonicKRM as an XML string.<br /><br />If this data is not checksummed, or otherwise protected, a malicious user may be able to edit the XML string within an application's executable and submit an order with an invalid price.<br /><br />In the long term, this attack will be denied by moving the responsibility for pricing information from the KRM library to the KRM server. When this process is complete, vendors will be able to override the pricing information in shipping copies of an application using their Kagi database entry.</blockquote ><br /><br /><b>WHAT THE FUCK</b> ? The user is able to choose the price he wants to pay ? Can't be true, this part of the documentation must be outdated. Guess what... it's not, long term is long term!<br /><br />Note that <i>checksumming</i> or <i>protecting</i> is pure bullshit as long as the price comes from the application and not Kagi's server.<br /><br />I searched for the first shareware using KRM I found, opened it with an hex editor, did search and replace of the string <b>30.00</b> to <b>01.00</b> and I indeed successfully ordered the shareware for $1.<br /><br />This is totally irresponsible from Kagi. I don't know how this registration scheme could have been designed this way in the first place. No sensible person can design an ordering system where the price is set by the client.<br /><br />Please don't flame me, I'm a good guy. I contacted the $30 shareware author and offered to pay the remaining $29 I owe him. I should have searched a bit longer for a cheaper shareware. $1 + $29 is a bit expensive to demonstrate that KRM sucks ;-)<br /><br /><b>The Bottom Line</b><br />Don't use KRM as long as you can't set the price of your shareware on Kagi's server.Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com18tag:blogger.com,1999:blog-29715439.post-78513991985273746902008-09-03T09:32:00.013+02:002009-05-15T21:39:20.717+02:00QuietXcodeHave you ever noticed the <b>Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x319a380, has non-zero refcount = 1</b> messages in the console ? Chris Espinosa <a href="http://www.cocoabuilder.com/archive/message/xcode/2007/11/7/16932">replied about these warnings</a> to Matt Neuburg:<br /><blockquote><br />The second one is an error deep in some system framework when running under Garbage Collection that we have not been able to track down yet. It simply means that somebody has neglected to do a final release on a memory block that nobody has kept a pointer to (making the final release technically impossible). The Garbage Collector knows the block is inaccessible and is freeing it, but is warning us that somebody forgot to formally release it before the pointer to it went out of scope. Bad form, but no actual harm.<br /></blockquote><br />I think it actually harms. These messages are filling the console so much it becomes unusable. I'm a big fan of <a href="http://projects.tynsoe.org/en/geektool/">GeekTool</a> and I always have the tail of the console on the desktop. Now, it looks always the same and interesting messages from various applications are lost in the mass of <b>free_garbage</b> messages:<br /><blockquote><br />Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x319a380, has non-zero refcount = 1<br />Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x31bf6e0, has non-zero refcount = 1<br />Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x3263da0, has non-zero refcount = 1<br />Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x3274eb0, has non-zero refcount = 1<br />Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x319a380, has non-zero refcount = 1<br />Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x31bf6e0, has non-zero refcount = 1<br />Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x3263da0, has non-zero refcount = 1<br />Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x3274eb0, has non-zero refcount = 1<br />Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x201c8b0, has non-zero refcount = 1<br />Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x3256110, has non-zero refcount = 1<br />Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x201c8b0, has non-zero refcount = 1<br />Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x3256110, has non-zero refcount = 1<br />Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x2045540, has non-zero refcount = 1<br />Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x208ca10, has non-zero refcount = 1<br />Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x2045540, has non-zero refcount = 1<br />Xcode(19838,0xb0103000) malloc: free_garbage: garbage ptr = 0x208ca10, has non-zero refcount = 1<br /></blockquote><br />So I decided to tackle the problem and here is my Solution: <a href="http://github.com/0xced/quietxcode/zipball/1.1.4">QuietXcode 1.1.4</a> (5 KB). This is an Xcode plugin that patches the culpable call to <tt>malloc_printf("free_garbage: garbage ptr = %p, has non-zero refcount = %d", ...)</tt>.<br /><br />You can build the plugin either by typing <tt>xcodebuild</tt> in a terminal or by building it (⌘ + B) in Xcode. Building the plugin will also automatically install it into your <b>~/Library/Application Support/Developer/Shared/Xcode/Plug-ins</b> folder.<br /><br />Once it's installed, you have to relaunch Xcode. You should see the message <b><QuietXcode> loaded successfully</b> in the console and no more <b>free_garbage</b> messages.<br /><br />The plugin performs a <i>safe</i> patch, that is, if it does not find the expected call to <tt>malloc_printf</tt> and the expected Xcode version (greater than or equal to 3.1/1099), it logs a more or less comprehensive error to the console. Have fun browsing the source code, it demonstrates how to use the dyld and mach-o apis to locate non exported symbols and the mach api to dynamically patch code.<br /><br />Note that the plugin is for i386 only, porting it to ppc and/or 64 bits is left as an exercise to the reader.Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com18tag:blogger.com,1999:blog-29715439.post-51163721359142257672008-08-22T20:34:00.006+02:002010-03-11T08:28:29.413+01:00Exploring iPhone OS 2 files<b>Update:</b> This technique also works with iPhone OS 3.x. You will find the VFDecrypt keys on <a href="http://www.theiphonewiki.com/wiki/index.php?title=Firmware">The iPhone Wiki Firmware</a> page. Just select the appropriate iPhone model and Version/Build of your firmware.<br /><br />It turns out to be pretty simple:<ol><br /><li>Download iPhone OS 2.0.2 (5C1)<br /><tt>curl -O http://appldnld.apple.com.edgesuite.net/content.info.apple.com/iPhone/061-5241.20080818.t5Fv3/iPhone1,2_2.0.2_5C1_Restore.ipsw</tt></li><br /><li>Unzip the ipsw which is actually a zip file<br /><tt>unzip iPhone1,2_2.0.2_5C1_Restore.ipsw</tt></li><br /><li>Download vfdecrypt<br /><tt>svn co http://iphone-elite.googlecode.com/svn/trunk iphone-elite</tt></li><br /><li>Compile vfdecrypt<br /><tt>make -C iphone-elite/vfdecrypt_win32</tt></li><br /><li>Decrypt the dmg (key from <a href="http://www.theiphonewiki.com/wiki/index.php?title=VFDecrypt_Keys">The iPhone Wiki</a>)<br /><tt>./iphone-elite/vfdecrypt_win32/vfdecrypt -i 018-3978-1.dmg -k 31e3ff09ff046d5237187346ee893015354d2135e3f0f39480be63dd2a18444961c2da5d -o iPhoneOS-2.0.2.dmg</tt></li><br /><li>Mount iPhone OS dmg and start exploring<br /><tt>open iPhoneOS-2.0.2.dmg</tt></li><br /></ol>Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com4tag:blogger.com,1999:blog-29715439.post-25643140773263930602008-08-22T20:05:00.009+02:002008-08-24T19:01:30.053+02:00Do not buy iQuarantine XFrom iQuarantine X website:<br /><blockquote><ul><br /><li>iQuarantine X is not a background script or a script that gets attached to files or folders.</li><li>iQuarantine X is the first application to make the LEOPARD FILE QUARANTINE ALERTS go away.</li><li>iQuarantine X is the easiest way to rid LEOPARD of all FILE QUARANTINE ALERTS.</li><br /></ul></blockquote >So, if it's not a script, what is it (beside a scam) ?<br />It's a hack that binary patches a system framework (<tt>/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/CarbonCore.framework/Versions/A/CarbonCore</tt>) by short-circuiting the private function <tt>_FSAllocateQuarantineData</tt>.<br /><br />So we have four good reasons not to buy it:<br /><ol><li>It has an unacceptable upgrade policy.</li><li>It binary patches a system framework.</li><li>Developer does not reply to e-mails.</li><li>You can <a href="http://pseudogreen.org/blog/yes_leopard_i_want_to_open_it_already.html">disable the Leopard quarantine</a> for free with an official technique.</li><br /></ol>Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com3tag:blogger.com,1999:blog-29715439.post-25060754856301841852008-07-10T08:09:00.006+02:002008-07-10T08:22:53.693+02:00The missing NSLocale documentation<tt>+ (NSArray *)preferredLanguages</tt><br /><b>Return Value</b><br />The user's language preference order as an array of NSString objects, each of which is a canonicalized IETF BCP 47 language identifier. <b><i>This is defined by the user in System Preferences → International → Language.</i></b><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6k5Jj8WvjM1WbJeVLYwSeOqrWpvYay9kpmZxYaJGFQUs0dpM3jGqxv6i8dwj0hHhIFLepDzFnwSCSdDv0_JztK0O36hNov9HT-j7kGtsEgvAtNMPsGoJPIP4GXSSka0YDw6NN/s1600-h/preferredLanguages.png"><img style="cursor:pointer; cursor:hand;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg6k5Jj8WvjM1WbJeVLYwSeOqrWpvYay9kpmZxYaJGFQUs0dpM3jGqxv6i8dwj0hHhIFLepDzFnwSCSdDv0_JztK0O36hNov9HT-j7kGtsEgvAtNMPsGoJPIP4GXSSka0YDw6NN/s400/preferredLanguages.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5221263800959697874" /></a><br />For your information: this is stored in the gloal domain in the <tt>AppleLanguages</tt> key.<br /><br /><tt>+ (id)currentLocale</tt><br /><b>Return Value</b><br />The logical locale for the current user. The locale is formed from the settings for the current user’s chosen system locale overlaid with any custom settings the user has specified in System Preferences <b><i>→ International → Formats</i></b>.<br />This method may return a retained cached object.<br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlcJhoGRQSONskargvslWtpsRIy8xqunN7fAIBG56wrimV_-BydDV4PMQf_nY9lAVersGQZ0fesXkxd5bCGzDfQ79x2kynoSxiD1VSLp4GD9cmRm5brjHzkklIyi0_51ukay8l/s1600-h/currentLocale.png"><img style="cursor:pointer; cursor:hand;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlcJhoGRQSONskargvslWtpsRIy8xqunN7fAIBG56wrimV_-BydDV4PMQf_nY9lAVersGQZ0fesXkxd5bCGzDfQ79x2kynoSxiD1VSLp4GD9cmRm5brjHzkklIyi0_51ukay8l/s400/currentLocale.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5221264220715946194" /></a><br /><br /><tt>+ (id)autoupdatingCurrentLocale</tt><br />This one I have not understood how it is supposed to be used. If someone knows, please let us know in the comments.<br /><br />Now, if what you want is the <i>current</i> language, you should not use either of these methods. Instead you should use something like <tt>NSLocalizedString(@"ISOLanguageCode", @"iso language code")</tt>. Then you have to define <tt>"ISOLanguageCode" = "en";</tt> etc. in all your Localizable.strings files.Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com1tag:blogger.com,1999:blog-29715439.post-11574186683458853922008-06-10T21:13:00.003+02:002008-06-10T21:22:08.595+02:00Mac OS X bug on non english systems<a href="http://support.apple.com/kb/TS1642">Mac OS X 10.5 (Japanese): Disk Utility "internal error" alert with 7-pass erase or 35-pass erase</a><br /><blockquote><b>Symptoms</b><br />In the Japanese language version of Mac OS X 10.5, when performing a Secure Erase (7-pass or 35-pass erase) in Disk Utility, this alert may appear: "Disk Utility internal error. Disk Utility has lost its connection with Disk Management Tool."<br /><br /><b>Resolution</b><br />Change the Mac OS X language version to English before performing the secure erase.</blockquote><br />I thought this kind of problem was Mac OS 9/Classic history.Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com1tag:blogger.com,1999:blog-29715439.post-21684486733457125742008-02-14T10:42:00.002+01:002008-02-14T11:04:28.565+01:00Upgrading a System Preference paneThe System Preferences application provides a convenient way to install a preference pane. Double-clicking the preference pane will prompt the user if he wants to install it for the current user only or for all users of the computer. Then System Preferences will copy the preference pane to ~/Library/PreferencePanes or /Library/PreferencePanes according to what was chosen asking for administrator password if necessary. Finally the preference pane will be loaded and presented to the user.<br /><br />Now, let's see what happens when a preference pane is upgraded. Again, System Preferences is smart: it is able to detect if an older version of the same preference pane is installed and proposes to replace it <a href="#note1" name="backlink1">[1]</a>. Everything seems alright, but it is actually not! Things are more complicated if the preference pane to upgrade has already been loaded. That is, if the user already clicked the preference pane.<br /><br />Indeed, preference panes are just a special kind of <a href="http://developer.apple.com/documentation/CoreFoundation/Conceptual/CFBundles/index.html">bundle</a> which is loaded into System Preferences with the <tt>-(BOOL)[NSBundle load]</tt> method (cf. -(BOOL)[NSPrefPaneBundle instantiatePrefPaneObject] method of the PreferencePanes framework). The problem is that on Tiger, <b>a NSBundle can not be unloaded</b>. So when upgrading an opened preference pane, the old code is not unloaded and as a consequence the new code is <b>not</b> loaded. This is because System Preferences calls the <tt>-(BOOL)[NSBundle load]</tt> method which returns YES, meaning that the bundle was successfully loaded <b>or that the code has already been loaded</b>. In the case of an already opened preference pane, that's how the result of the <tt>load</tt> method should have been interpreted. Unfortunately, it is interpreted as if the bundle was successfully loaded and System Preferences thinks it has loaded the new bundle, but it has not.<br /><br />This is very problematic because at this point, the resources (nib files, pictures etc.) of the new bundle have already been copied. So we have the old code which is accessing the new resources. I let you imagine the numerous problems this situation can cause. At best, exceptions will raise and your preference pane will be half working. At worst, your preference pane will simply crash.<br /><br />So, how do we fix this problem?<br /><br />First, the preference pane must detect itself when it's upgrading over an older already loaded version as System Preferences does not detect it <a href="#note2" name="backlink2">[2]</a>. This must be done as early as possible, i.e. at the very beginning of the <tt>- (id)initWithBundle:(NSBundle *) bundle</tt> method. It is possible to detect this situation with the help of the <a href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html#//apple_ref/occ/clm/NSObject/version">version</a> of your NSPreferencePane subclass. See my <a href="http://snipplr.com/view/4054/detect-already-loaded-older-version-of-a-preference-pane/">detection snippet</a> to understand how detection works.<br />Once this is detected, we must properly reload the new preference pane. This must be achieved by quitting System Preferences and relaunching System Preferences. This is the not that elegant solution to unload the old preference pane. The elegant solution would be to unload the bundle. This is left as an exercise to the Apple engineers for a future version of System Preferences.<br /><br />Relaunching System Preferences and selecting the preference pane is quite tricky. A second executable must be responsible for relaunching the System Preferences application. Also, it is nicer for the user if the preference pane he just upgraded is automatically selected. Automatic selection of the pref pane is achieved through Apple Script. Please refer to my <a href="http://snipplr.com/view/3924/reload-a-preference-pane/">reload snippet</a> for implementation details. Note that once you have compiled the <tt>reload</tt> executable, you have to place it inside the resources directory of your preference pane. Do not place it inside the executable directory (<tt>Contents/MacOS</tt>) if you do not want to see the reload application popping up in the Dock.<br /><br />With this reload code in place, if the user ever happens to upgrade a preference pane while the older one was loaded, he will experience a <i>System Preferences flicker</i> as it will quit and reopen right away. While this might be surprising to him, this is still better than a half working preference pane or a crash.<br /><br />If anything is unclear, just say it so in the comments and I will try to elaborate. If everything is clear, just pick up my code snippets and implement them in your preference pane as soon as possible ;-)<br /><br /><hr><br />1. <a name="note1"></a>System Preferences uses the <a href="http://developer.apple.com/documentation/CoreFoundation/Reference/CFBundleRef/Reference/reference.html#//apple_ref/doc/uid/20001191-CH201-F14157"><tt>CFBundleGetVersionNumber</tt></a> function to retrieve the version numbers of the new and old bundles as an UInt32 in order to compare them. The documentation says <i>If the bundle’s version number is a number, it is interpreted as the unsigned long integer format defined by the vers resource on Mac OS 9.</i> What every developer understands is that if your Info.plist <tt>CFBundleVersion</tt> key represents a number (e.g. "519"), the value returned by CFBundleGetVersionNumber will be 519. This is not what actually happens. If you want CFBundleGetVersionNumber to return 519, you have to add an undocumented key in your Info.plist file: <tt>CFBundleNumericVersion</tt>. Make sure you define it as <tt><integer>519</integer></tt> and not <tt><string>519</string></tt>.<a href="#backlink1">↩</a><br /><br />2. <a name="note2"></a>Note that on Leopard, this situation is actually detected and a dialog is presented to the user telling he must <i>quit System Preferences and then open it again</i>. Unfortunately, no automatic action is taken to circumvent this annoying behavior. System Preferences could restart itself or unload the bundle (this is possible since Leopard) but as of Mac OS X 10.5.2, none of this action is performed.<a href="#backlink2">↩</a>Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com4tag:blogger.com,1999:blog-29715439.post-49328644596064231212008-01-25T16:40:00.001+01:002008-02-28T12:15:24.710+01:00QuickTime 7.4 and Perian subtitles fixWith QuickTime 7.4, subtitles automatically added by Perian have stopped working. In order to get them back, download and install <a href="http://0xced.blogspot.com/2007/02/bandes-annonces-front-row.html">Front Row Trailers</a>, go to the <i>QuickTime Components</i> tab, and install Perian 1.0.0.2. If the proposed version is below 1.0.0.2, hold the alt (option) key while clicking the <i>Refresh</i> button.<br /><br />For thoses wondering, this is an unofficial build of Perian 1.0 onto which <a href="http://trac.perian.org/changeset/703">two</a> <a href="http://trac.perian.org/changeset/768">patches</a> have been applied. Note that the future Perian 1.1 release will also be able to read subtitles.<br /><br />Enjoy, QuickTime 7.4 can read subtitles again, no need to downgrade to version 7.3.<br /><br /><b>UPDATE:</b> Perian 1.1 is now released and has addressed the problem. Note that subtitles still do not work in Front Row on Leopard.<br /><br /><div id="keywords">keywords: QuickTime 7.4 Perian subtitles srt</div>Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com10tag:blogger.com,1999:blog-29715439.post-65113285675894240932007-11-29T07:28:00.000+01:002007-11-29T08:09:56.813+01:00BetterAuthorizationSampleFinally, Apple posted <a href="http://developer.apple.com/samplecode/BetterAuthorizationSample/index.html">BetterAuthorizationSample</a>, a sample project that demonstrates how to securely use Mac OS X authorization API.<br /><blockquote>Apple's older sample code (AuthSample and MoreAuthSample) used a setuid root privileged helper tool. BAS uses launchd because it's more secure. In the BAS design, an attacker can't directly control the environment which the helper tool inherits, and that prevents a variety of potential attacks.</blockquote><br />This sample code supersedes the four years old Project Builder MoreIsBetter/MoreSecurity sample code that warned: <i>No matter what you do, the current AuthorizationExecuteWithPrivileges model allows for security violations [3093666].</i> It comes as a Xcode project that compiles without tweaking and with three documentation files that look quite complete: Design and Implementation Rationale, Performing Privileged Operations With BetterAuthorizationSampleLib and Read Me About BetterAuthorizationSample.Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com1tag:blogger.com,1999:blog-29715439.post-55809894995095228372007-11-19T10:55:00.001+01:002008-12-03T11:55:14.385+01:00Front Row for TigerLeopard users have the Front Row application in their Applications folder. It may be useful if you want to automatically launch front Row when your computer starts up by adding a login item for example.<br /><br />Now, Tiger users can also use this convenient Front Row application. Leopard users who have accidentally deleted their Front Row application can also use it.<br /><br /><img style="border: none; display:block; margin:0px auto 10px; text-align:center;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh54vNFVCaGJkRJ7MWMITU4jdZIEykXQH8iHUVSLserTamqqgLnJma6kSaaMJ8R1QN0bSNhep2V4QNgDJUq7H2Prg2Zndlx52Q78gcgf_O1npslRQ1QXArMYQDzWZmri-4vdcCR/s200/FrontRow.png" border="0" alt="Front Row" /><div align="center"><a href="http://pitaya.ch/blog/FrontRow_Tiger-1.0.zip">Front Row 1.0</a> (29 Ko)</div><br /><br />I have not tested it on unsupported Macs, i.e. those without an Apple Remote. If you have such a Mac, please report in the comments if it works or if it still requires Front Row Enabler.<br /><br />This Front Row launcher has been written from scratch. Here is the source code:<br /><blockquote><br />int main(int argc, char *argv[])<br />{<br /> BSRemoteUIToggle();<br /> return 0;<br />}<br /></blockquote>Cédric Luthihttp://www.blogger.com/profile/18136024823081920226noreply@blogger.com11