I've been running a web site that acts as a wrapper for Amazon's aStore. This was started as an experiment a few years ago, yet it didn't accomplish much, beyond a learning experience in how to use PHP to perform some on-the-fly screen scrapping and content embellishing. It was also something I could use when experimenting with PPC advertising. Having been pulled in some lucrative projects I didn't pay any more attention to this. In fact functionality that used to work, like search engine friendly URLs (SEFU), is now broken...
Anyway, glancing over my analytics account today I see a smashing 338% traffic increase during the last month:
Wow ! All this traffic coming from Google. To what do I owe the honour ? No inbound links, none of the usual favourable auspices. The only logical explanation is Amazon dropping their affiliates form California, about at the same time as my jump in traffic, which likely caused the poor folks to pull the plug on their sites. Or perhaps a glitch in Google's secret sauce for bestowing wealth in cyberspace ? Anyway, the people are coming in, goals as measured by analytics are fulfilled, meaning people are adding stuff to their carts, good times rolling.
Of course the earnings exploded :)
One thing that strikes me is the conversion rate, which at 7.69% stands much higher than industry average (which I believe stands at less than 2%). So yeah, this passive income thingy is not entirely hype. If you can bring people over, the money will follow.
Saturday, July 30, 2011
Wednesday, June 29, 2011
phpMyAdmin as a potential security vulnerability
I really like phpMyAdmin. I use it all the time when developing web apps, to configure my databases. There are probably better tools out there, but this fits the bill for me. It allows doing things like adding fields to a table or configuring indexes using a web-based interface, with no need to remember complex SQL syntax. More importantly, by presenting you all options available for an operation it forces you to think through the operation you're about to perform. It also comes bundled in the MAMP tool that I use as an environment for web development on my Mac.
While it's perfectly fine to use phpMyAdmin in a development environment, I don't think it's a good idea to use it on a production server. It makes it easy for an attacker to brute-force their way into your database. What's the use of properly configuring your MySQL server so that it can be accessed only by scripts running on your web server if you're going to provide them a backdoor that can be accessed with a web app ? Probably that's why you won't find this tool available to install with package managers like yum.
You may think you're going to outsmart the attackers and rename the app to a different name, to make it hard to find, which is essentially relying on security by obscurity. If you really go that route I'd recommend you keep the app available on your site only for a short duration, while you're using it, then move it to a folder that's not accessible via the web. And choose a hard to guess name. I once did that and chose to name it "pma" instead of "phpMyAdmin". Going through the server log the other day I found this trail of probes:
Luckily by that time I've grown wiser and had that removed long before the attempted attack. So they'll come on knocking, make no mistakes about it.
A better approach to interact with MySQL is using the mysql command prompt tool, that you'd access once you're logged into your server, via a SSH connection. The only thing is that now you need to enter SQL commands. phpMyAdmin comes to the rescue here with its ability to display the equivalent SQL commands that produce the same effect as the command you performed via its web interface. So you can simply copy and paste those commands at the command prompt. So you're running phpMyAdmin in your development environment, copy the SQL commands and paste them into your command prompt tool running on your production server.
Alternatively, you can also use the "Create PHP Code" link to build up a configuration/install script that sets up your application on the web server. You'll find this all the time with off-the-shelf apps, and such a script also helps distributing your app to the users and repeating the installation on many servers. Just one caveat, make sure you remove that script once you're done configuring, or code it in such a way that subsequent invocations wouldn't cause harm. The last thing you want is a wise guy calling that script and bringing your whole database to a clean slate :).
While it's perfectly fine to use phpMyAdmin in a development environment, I don't think it's a good idea to use it on a production server. It makes it easy for an attacker to brute-force their way into your database. What's the use of properly configuring your MySQL server so that it can be accessed only by scripts running on your web server if you're going to provide them a backdoor that can be accessed with a web app ? Probably that's why you won't find this tool available to install with package managers like yum.
You may think you're going to outsmart the attackers and rename the app to a different name, to make it hard to find, which is essentially relying on security by obscurity. If you really go that route I'd recommend you keep the app available on your site only for a short duration, while you're using it, then move it to a folder that's not accessible via the web. And choose a hard to guess name. I once did that and chose to name it "pma" instead of "phpMyAdmin". Going through the server log the other day I found this trail of probes:
[Mon Jun 27 12:25:39 2011] [error] [client 69.164.2.14] File does not exist: /var/www/html/pubpit/phpMyAdmin
[Mon Jun 27 12:25:39 2011] [error] [client 69.164.2.14] File does not exist: /var/www/html/pubpit/phpmyadmin
[Mon Jun 27 12:25:39 2011] [error] [client 69.164.2.14] File does not exist: /var/www/html/pubpit/pma
[Mon Jun 27 12:25:39 2011] [error] [client 69.164.2.14] File does not exist: /var/www/html/pubpit/myadmin
[Mon Jun 27 12:25:40 2011] [error] [client 69.164.2.14] File does not exist: /var/www/html/pubpit/MyAdmin
Luckily by that time I've grown wiser and had that removed long before the attempted attack. So they'll come on knocking, make no mistakes about it.
A better approach to interact with MySQL is using the mysql command prompt tool, that you'd access once you're logged into your server, via a SSH connection. The only thing is that now you need to enter SQL commands. phpMyAdmin comes to the rescue here with its ability to display the equivalent SQL commands that produce the same effect as the command you performed via its web interface. So you can simply copy and paste those commands at the command prompt. So you're running phpMyAdmin in your development environment, copy the SQL commands and paste them into your command prompt tool running on your production server.
Alternatively, you can also use the "Create PHP Code" link to build up a configuration/install script that sets up your application on the web server. You'll find this all the time with off-the-shelf apps, and such a script also helps distributing your app to the users and repeating the installation on many servers. Just one caveat, make sure you remove that script once you're done configuring, or code it in such a way that subsequent invocations wouldn't cause harm. The last thing you want is a wise guy calling that script and bringing your whole database to a clean slate :).
Monday, May 23, 2011
Good books for the Software Engineering field
I used to rely on Amazon bestseller reports every time I approached a new field of study, to find out the books that are vetted by the readers, with their wallets.
I came across a list of Top 100 Software Engineering books, which attempts to find the best books by using a criterion derived form ratings on Amazon, Jolt awards and number of search results in Google. It could make a good reading list.
I came across a list of Top 100 Software Engineering books, which attempts to find the best books by using a criterion derived form ratings on Amazon, Jolt awards and number of search results in Google. It could make a good reading list.
Friday, May 20, 2011
How to deal with EXC_BAD_ACCESS triggered by _CFAutoreleasePoolPop
Getting crashes due to EXC_BAD_ACCESS errors means that you're trying to access memory through an invalid pointer. While there can be many ways of messing things up like that, a common one in Objective-C is to over-release an object allocated on the heap. That means that the object is released more times than it is retained.
What makes this an insidious problem is that sometimes those crashes happen at random, are not always reproducible, and the stack trace you get doesn't offer clear clues of what portion of the code is at fault. As you can see from the picture below, all code in the stack trace is system code, none in your control:
Sometimes the stack trace is slightly different, here's another one:
The presence of _CFAutoreleasePoolPop in the trace is a strong indication of the fact that an object that's already been deallocated, as a result of over-release, is now attempted to be released again, during the draining of an autorelease pool.
In such cases you should be looking for 2 pieces of information. What's the address of the object being released, its invalid pointer, and where did that pointer come from in the first place, that is, where in your code it was first allocated. Getting to know the variable name in your code that holds that offending pointer, as it appears at the point of allocation, helps tremendously in solving the issue. All you need to do is search in your code where that variable is being used, where it gets released, and so on. Often times you'll find that you're releasing an object that's already autoreleased (it hasn't been created with a call to a method that starts with "alloc" or "new").
You should be able to somehow reproduce the problem, if you can do that, you're good to go. If reproducing the issue is hard, you might want to try placing breakpoints that stop the execution for a while. In some instances that alters the timing of execution in the system and helps reproducing the issue. Sometimes your only recourse is to run the same code repeatedly until it crashes.You can also try running on the simulator and device. Update: I used this method only on simulator. When running on device you'll get an error when running info malloc-history in GDB. If you can only reproduce the crash on a device you'll need to investigate a way of analyzing the allocation logs, which presumably get stored on the device (e.g. malloc: stack logs being written into /private/var/mobile/Applications/DF97FB1F-546A-4B9A-92AB-B6979B4E28A9/tmp/stack-logs.6413.PubPit.index).
Once you can reproduce the crash, the first thing you need to do is set some environment variables to aid in debugging. In the left pane of Xcode, locate the executable group, and double-click the name of the executable for your project:
Choose the "Arguments" tab in the window that opens and set the NSZombieEnabled, NSDebugEnabled, NSAutoreleaseFreedObjectCheckEnabled and MallocStackLoggingNoCompact like so:
Then run your code, if it crashes, look in the debugger console and you should see something like this:
The key info here is:
*** -[CFDictionary release]: message sent to deallocated instance 0x652e280
In my case I have an instance of CFDictionary that was initially allocated at address 0x652e280, which now is deallocated once too many times. The question is where did I ever allocate that object. To find that I do:
BINGO ! My code that allocates that object and the variable used to point to that object is in: -[Model loadData] at /Users/bhapca/WORK/Cocoa/Pubpit/Classes/Model.m:171
Now all I need is too look at that code and figure out where that object gets released more than needed.
It's important to remove those NSZombieEnabled, NSDebugEnabled, NSAutoreleaseFreedObjectCheckEnabled and MallocStackLoggingNoCompact environment variables, once you're done with debugging, to prevent them to affect the released version of your app.
Credit goes to this site, where I first learned about MallocStackLoggingNoCompact.
Update: Apparently the same investigation can be performed using the Instruments tool that comes with Xcode 4, by choosing the Zombies trace template. I believe that is the method now recommended by Apple for dealing with this type of issues.
What makes this an insidious problem is that sometimes those crashes happen at random, are not always reproducible, and the stack trace you get doesn't offer clear clues of what portion of the code is at fault. As you can see from the picture below, all code in the stack trace is system code, none in your control:
Sometimes the stack trace is slightly different, here's another one:
The presence of _CFAutoreleasePoolPop in the trace is a strong indication of the fact that an object that's already been deallocated, as a result of over-release, is now attempted to be released again, during the draining of an autorelease pool.
In such cases you should be looking for 2 pieces of information. What's the address of the object being released, its invalid pointer, and where did that pointer come from in the first place, that is, where in your code it was first allocated. Getting to know the variable name in your code that holds that offending pointer, as it appears at the point of allocation, helps tremendously in solving the issue. All you need to do is search in your code where that variable is being used, where it gets released, and so on. Often times you'll find that you're releasing an object that's already autoreleased (it hasn't been created with a call to a method that starts with "alloc" or "new").
You should be able to somehow reproduce the problem, if you can do that, you're good to go. If reproducing the issue is hard, you might want to try placing breakpoints that stop the execution for a while. In some instances that alters the timing of execution in the system and helps reproducing the issue. Sometimes your only recourse is to run the same code repeatedly until it crashes.
Once you can reproduce the crash, the first thing you need to do is set some environment variables to aid in debugging. In the left pane of Xcode, locate the executable group, and double-click the name of the executable for your project:
Choose the "Arguments" tab in the window that opens and set the NSZombieEnabled, NSDebugEnabled, NSAutoreleaseFreedObjectCheckEnabled and MallocStackLoggingNoCompact like so:
Then run your code, if it crashes, look in the debugger console and you should see something like this:
[Session started at 2011-05-20 11:14:57 +0300.]
GNU gdb 6.3.50-20050815 (Apple version gdb-1510) (Wed Sep 22 02:45:02 UTC 2010)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "x86_64-apple-darwin".sharedlibrary apply-load-rules all
Attaching to process 40114.
PubPit(40114) malloc: recording malloc stacks to disk using standard recorder
PubPit(40114) malloc: stack logging compaction turned off; size of log files on disk can increase rapidly
PubPit(40114) malloc: process 40076 no longer exists, stack logs deleted from /tmp/stack-logs.40076.PubPit.p5tTh9.index
PubPit(40114) malloc: stack logs being written into /tmp/stack-logs.40114.PubPit.Vpmlum.index
2011-05-20 11:14:59.890 PubPit[40114:207] *** -[CFDictionary release]: message sent to deallocated instance 0x652e280
Data Formatters temporarily unavailable, will re-try after a 'continue'. (Not safe to call dlopen at this time.)
(gdb)
The key info here is:
*** -[CFDictionary release]: message sent to deallocated instance 0x652e280
In my case I have an instance of CFDictionary that was initially allocated at address 0x652e280, which now is deallocated once too many times. The question is where did I ever allocate that object. To find that I do:
(gdb)
(gdb) info malloc-history 0x652e280
Alloc: Block address: 0x0652e280 length: 48
Stack - pthread: 0xa057c540 number of frames: 42
0: 0x949d617c in malloc_zone_malloc
1: 0x132080d in _CFRuntimeCreateInstance
2: 0x132040a in CFBasicHashCreate
3: 0x1321ebb in __CFDictionaryCreateGeneric
4: 0x1321bd1 in CFDictionaryCreateMutable
5: 0x13462ac in parseXMLElement
6: 0x13469fe in parsePListTag
7: 0x1345891 in parseXMLElement
8: 0x1344a94 in _CFPropertyListCreateFromXMLStringError
9: 0x13435a8 in _CFPropertyListCreateWithData
10: 0x1399957 in _CFPropertyListCreateFromXMLData
11: 0xf92ef in +[NSPropertyListSerialization propertyListFromData:mutabilityOption:format:errorDescription:]
12: 0xfa9c in -[Model loadData] at /Users/bhapca/WORK/Cocoa/Pubpit/Classes/Model.m:171
13: 0x102b8 in -[Model init] at /Users/bhapca/WORK/Cocoa/Pubpit/Classes/Model.m:251
14: 0x103fe in +[Model sharedModel] at /Users/bhapca/WORK/Cocoa/Pubpit/Classes/Model.m:268
15: 0x6c54 in -[PagingViewController loadView] at /Users/bhapca/WORK/Cocoa/Pubpit/Classes/PagingViewController.m:716
16: 0x4275e3 in -[UIViewController view]
17: 0x425a57 in -[UIViewController contentScrollView]
18: 0x436201 in -[UINavigationController _computeAndApplyScrollContentInsetDeltaForViewController:]
19: 0x434831 in -[UINavigationController _layoutViewController:]
20: 0x435b4c in -[UINavigationController _startTransition:fromViewController:toViewController:]
21: 0x430606 in -[UINavigationController _startDeferredTransitionIfNeeded]
22: 0x548e01 in -[UILayoutContainerView layoutSubviews]
23: 0xd6c451 in -[CALayer layoutSublayers]
24: 0xd6c17c in CALayerLayoutIfNeeded
25: 0xd6537c in _ZN2CA7Context18commit_transactionEPNS_11TransactionE
26: 0xd650d0 in _ZN2CA11Transaction6commitEv
27: 0x37c19f in -[UIApplication _reportAppLaunchFinished]
28: 0x37c659 in -[UIApplication _runWithURL:payload:launchOrientation:statusBarStyle:statusBarHidden:]
29: 0x386db2 in -[UIApplication handleEvent:withNewEvent:]
30: 0x37f202 in -[UIApplication sendEvent:]
31: 0x384732 in _UIApplicationHandleEvent
32: 0x1cdda36 in PurpleEventCallback
33: 0x13f2064 in __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__
34: 0x13526f7 in __CFRunLoopDoSource1
35: 0x134f983 in __CFRunLoopRun
36: 0x134f240 in CFRunLoopRunSpecific
37: 0x134f161 in CFRunLoopRunInMode
38: 0x37bfa8 in -[UIApplication _run]
39: 0x38842e in UIApplicationMain
40: 0x1e74 in main at /Users/bhapca/WORK/Cocoa/Pubpit/main.m:14
41: 0x1e05 in start
(gdb)
BINGO ! My code that allocates that object and the variable used to point to that object is in: -[Model loadData] at /Users/bhapca/WORK/Cocoa/Pubpit/Classes/Model.m:171
Now all I need is too look at that code and figure out where that object gets released more than needed.
It's important to remove those NSZombieEnabled, NSDebugEnabled, NSAutoreleaseFreedObjectCheckEnabled and MallocStackLoggingNoCompact environment variables, once you're done with debugging, to prevent them to affect the released version of your app.
Credit goes to this site, where I first learned about MallocStackLoggingNoCompact.
Update: Apparently the same investigation can be performed using the Instruments tool that comes with Xcode 4, by choosing the Zombies trace template. I believe that is the method now recommended by Apple for dealing with this type of issues.
Wednesday, April 27, 2011
When things blow up in Objective-C with a stack trace like "objc_msgSend ??"
I got an "Program received signal: “EXC_BAD_ACCESS”." error after a small change I made to some code:
As you can see, the format descriptor passed to NSLog was expecting to be followed by an NSObject parameter and I failed to provide one. The result is unforgiving:
The call stack appears to have been corrupted, leaving very little to aid in debugging the issue. Here's where it pays to test often, after small changes and to use SCM, that would allow you to see recent changes and hopefully find the problem.
It's pretty disappointing that NSLog fails so badly. According to the docs, %@ placeholders in the format specifier string are meant to be filled with the content returned by "
I would have thought that the static analyzer invoked with "Build and Analyze" would catch it, but it doesn't either. So your best bet is to code in small increments, test often and use your intuition when cornered.
NSLog(@"downloadModel from %@, modelPlistUrl");
The call stack appears to have been corrupted, leaving very little to aid in debugging the issue. Here's where it pays to test often, after small changes and to use SCM, that would allow you to see recent changes and hopefully find the problem.
It's pretty disappointing that NSLog fails so badly. According to the docs, %@ placeholders in the format specifier string are meant to be filled with the content returned by "
descriptionWithLocale: if available, or description otherwise. Also works with CFTypeRef objects, returning the result of the CFCopyDescription function". So the runtime obviously takes whatever happens to be on the stack frame where the expected parameter should have been and it manages to trash the stack in the process.I would have thought that the static analyzer invoked with "Build and Analyze" would catch it, but it doesn't either. So your best bet is to code in small increments, test often and use your intuition when cornered.
Monday, April 11, 2011
Possible cause for empty response.products passed to productsRequest:didReceiveResponse: when using Apple's StoreKit
Some time has passed since I first blogged about my first experience with Apple's in app purchase mechanism for iOS, and the pitfalls I encountered.
Since then, I published several apps in the App Store, and now, when the time to update some of them has come, I came across a new issue. When testing the updates to my apps, I'm getting an empty response.products passed to - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response.
It's like Apple's servers doen't recognize my product ids. The same ids that are functioning properly on a live version of the app. WTF ?
Looking more closely on the issue reveals that I've been trying to install my update, built using a dev certificate, on top of the existent version of the app available in the app store, which of course was built with a different certificate. In any case, the ids placed in the request sent to Apple's servers are clearly legitimate. In fact, if I uninstall the app from my device and re-install the app update from scratch everything works fine. This can be used as a workaround for the issue.
This is more likely a bug on Apple's side, although one could argue that the issue is with my code, that perhaps botches the update process. I'll need to run some more tests to rule this out, but in the meantime, if you're having such issues you can try the workaround described above.
Since then, I published several apps in the App Store, and now, when the time to update some of them has come, I came across a new issue. When testing the updates to my apps, I'm getting an empty response.products passed to - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response.
It's like Apple's servers doen't recognize my product ids. The same ids that are functioning properly on a live version of the app. WTF ?
Looking more closely on the issue reveals that I've been trying to install my update, built using a dev certificate, on top of the existent version of the app available in the app store, which of course was built with a different certificate. In any case, the ids placed in the request sent to Apple's servers are clearly legitimate. In fact, if I uninstall the app from my device and re-install the app update from scratch everything works fine. This can be used as a workaround for the issue.
This is more likely a bug on Apple's side, although one could argue that the issue is with my code, that perhaps botches the update process. I'll need to run some more tests to rule this out, but in the meantime, if you're having such issues you can try the workaround described above.
Tuesday, February 15, 2011
how to rebuild PHP on CentOS or other RedHat derivatives
I was faced with the prospect of rebuilding the PHP interpreter on a server, to add support for GD.
Googling for info yielded this article with some good overview of the process.
After I downloaded the sources and started the configure process I was hit with a slew of errors related to the fact that there were missing some dependencies. Stuff like "xml2-config not found. Please check your libxml2 installation."
One cure for that seems to install the development version of those components, like so:
yum install component-devel
For me googling the error messages pointed me, in order, to the following resources that helped me solve the issues, here, here, here and finally here.
Googling for info yielded this article with some good overview of the process.
After I downloaded the sources and started the configure process I was hit with a slew of errors related to the fact that there were missing some dependencies. Stuff like "xml2-config not found. Please check your libxml2 installation."
One cure for that seems to install the development version of those components, like so:
yum install component-devel
For me googling the error messages pointed me, in order, to the following resources that helped me solve the issues, here, here, here and finally here.
Wednesday, February 09, 2011
how to work around the "Upload Aborted or Timed Out." errors in iTunes Connect
Lately I've been getting this error when trying to upload screen shots of some apps running on iPad.
It seems to happen for large files, over 1MB. One of my apps does magazine publishing, as such if we take shots of the app showing the cover of the magazine, those usually exceeds that limit and the upload fails.
One way to get around that is to save the large PNG files as smaller JPEG files, of lower quality. You can do that using a tool like Gimp. If you go below 1MB the issue disappears.
It seems to happen for large files, over 1MB. One of my apps does magazine publishing, as such if we take shots of the app showing the cover of the magazine, those usually exceeds that limit and the upload fails.
One way to get around that is to save the large PNG files as smaller JPEG files, of lower quality. You can do that using a tool like Gimp. If you go below 1MB the issue disappears.
Wednesday, February 02, 2011
beware of mysqli_stmt_execute clogging your database connection
There are multiple advantages in using prepared statements with MySQL. Improved performance and escaping of parameters passed to queries, to prevent SQL injection attacks come first to mind.
However, you need to be careful with mysqli_stmt_execute, especially if you run queries that produce result sets. If for some reason (e.g. alternate code path taken due to some error) you don't fetch the content of the result set, and if you don't explicitly close the prepared statement, any new queries sent over the database connection associated with the prepared statement that has results pending will fail.
That is, until you fetch the results with mysqli_stmt_fetch, or close the statement with mysqli_stmt_close, you won't be able to reuse the database connection to issue new queries.
This is intuitive enough, albeit a bit surprising, as it behaves very different from the regular mysqli_query that uses non-prepared statements, which is not subject to such constrains.
However, you need to be careful with mysqli_stmt_execute, especially if you run queries that produce result sets. If for some reason (e.g. alternate code path taken due to some error) you don't fetch the content of the result set, and if you don't explicitly close the prepared statement, any new queries sent over the database connection associated with the prepared statement that has results pending will fail.
That is, until you fetch the results with mysqli_stmt_fetch, or close the statement with mysqli_stmt_close, you won't be able to reuse the database connection to issue new queries.
This is intuitive enough, albeit a bit surprising, as it behaves very different from the regular mysqli_query that uses non-prepared statements, which is not subject to such constrains.
Monday, January 03, 2011
How to convert m4v files to ogg
I've been experimenting with the video element introduced by HTML 5, in an attempt to replace my current solution that uses Flash.
It soon became evident that the improved performance provided by the native video element came at lack of cross platform compatibility. Videos like m4v files would play nicely on Safari, but wouldn't play on Firefox. Some search on Google led me to http://www.wyodor.net/_Demo/html5/HTML5Video.html, which revealed that Firefox recognizes only video/ogg format.
The next hurdle was how to convert the m4v files to the video ogg format required by Firefox. I found this utility particularly useful for that.
It soon became evident that the improved performance provided by the native video element came at lack of cross platform compatibility. Videos like m4v files would play nicely on Safari, but wouldn't play on Firefox. Some search on Google led me to http://www.wyodor.net/_Demo/html5/HTML5Video.html, which revealed that Firefox recognizes only video/ogg format.
The next hurdle was how to convert the m4v files to the video ogg format required by Firefox. I found this utility particularly useful for that.
Monday, December 27, 2010
Tips for starting a consulting business as a professional in the computing field
I came across this interesting article, covering some essential points for people thinking of jumping into the consulting fray.
Monday, October 04, 2010
how to fix framework names being displayed in red in Xcode
Sometimes you'll find that the framework names in some of your Xcode projects show up in red. This happens in versions as new as 3.2.4, it may be fixed in future versions. I wasn't able to find a definitive reason of why that happens, some people were suggesting that Xcode was not able to find those frameworks, but if that were the case, my project should have never built, and I had no issues building.
I think it's more likely a bug in Xcode, which stems from the fact that you can change the build settings, namely the base SDK, from multiple places.
One way is to select the current target from the "Groups & Files" pane, and do a File->Get Info, then select the "Build" tab, then pick the configuration you want to change, from the "Configuration:" list.
Another way is to go to Project->Edit Project Settings, then select the "Build" tab and do the same as above.
Yet another way to change the base SDK is to go to Project->Edit Project Settings, then select the "General" tab, then at the bottom of the screen there is a "Base SDK for All Configurations".
The bottom line is that you should make sure you have the proper base SDK set in all 3 places, for all configurations of interest. In my case I had a "missing SDK" on the third place, and that was painting the frameworks name in red, evan as the project would build fine.
I think it's more likely a bug in Xcode, which stems from the fact that you can change the build settings, namely the base SDK, from multiple places.
One way is to select the current target from the "Groups & Files" pane, and do a File->Get Info, then select the "Build" tab, then pick the configuration you want to change, from the "Configuration:" list.
Another way is to go to Project->Edit Project Settings, then select the "Build" tab and do the same as above.
Yet another way to change the base SDK is to go to Project->Edit Project Settings, then select the "General" tab, then at the bottom of the screen there is a "Base SDK for All Configurations".
The bottom line is that you should make sure you have the proper base SDK set in all 3 places, for all configurations of interest. In my case I had a "missing SDK" on the third place, and that was painting the frameworks name in red, evan as the project would build fine.
Friday, October 01, 2010
Apple's in-app purchase pitfalls
I've been working on my first project for an iPhone app that uses in-app purchasing capabilities. In the process I encountered and fell through quite a few pitfalls. This is my attempt to document my ordeal, and help those who are experiencing the same issues.
When trying to test with the sandboxed App Store I got errors like : "Cannot connect to iTunes Store", or "Invalid Product Id". Getting things working in that environment is quite tricky, and you need to follow exactly the steps that Apple expects. So in other words, RTFM. The problem is that you need to have the right manual. I had followed the instructions from a book, which turned out to be incomplete, out-of-date and in some cases flat out wrong. Your best bet is to read the latest docs coming straight from Apple, as this is their technology and is subject to change.
I found a fine doc that helped me tremendously at http://developer.apple.com/library/ios/#technotes/tn2009/tn2259.html. You may also check out the dev forums at http://devforums.apple.com, for any current issues with the sandbox, or good advice.
Turns out that the root cause of my issues was the fact that:
1. I didn't have all contracts and banking/tax details set up in my iTunes Connect account
2. I was using a provisioning profile tied to a wildcard app id, which is not supported for in-app purchase
I wish Apple's tools were able to catch such errors, or the error messages at run time were more explicitly pointing to the real issue.
Getting iTunes Connect find my bank was tricky too. They're trying to look up banks based on transit number. It turns out that's not what's commonly referred to as transit numbers in Canada (usually 5 digits). No, to that you need to prepend the bank number (3 digits in my case). Your best bet is to search for the bank name and location, just make sure to use the full name instead of the commonly used abbreviations made up of initials.
When trying to test with the sandboxed App Store I got errors like : "Cannot connect to iTunes Store", or "Invalid Product Id". Getting things working in that environment is quite tricky, and you need to follow exactly the steps that Apple expects. So in other words, RTFM. The problem is that you need to have the right manual. I had followed the instructions from a book, which turned out to be incomplete, out-of-date and in some cases flat out wrong. Your best bet is to read the latest docs coming straight from Apple, as this is their technology and is subject to change.
I found a fine doc that helped me tremendously at http://developer.apple.com/library/ios/#technotes/tn2009/tn2259.html. You may also check out the dev forums at http://devforums.apple.com, for any current issues with the sandbox, or good advice.
Turns out that the root cause of my issues was the fact that:
1. I didn't have all contracts and banking/tax details set up in my iTunes Connect account
2. I was using a provisioning profile tied to a wildcard app id, which is not supported for in-app purchase
I wish Apple's tools were able to catch such errors, or the error messages at run time were more explicitly pointing to the real issue.
Getting iTunes Connect find my bank was tricky too. They're trying to look up banks based on transit number. It turns out that's not what's commonly referred to as transit numbers in Canada (usually 5 digits). No, to that you need to prepend the bank number (3 digits in my case). Your best bet is to search for the bank name and location, just make sure to use the full name instead of the commonly used abbreviations made up of initials.
Saturday, September 18, 2010
how to handle "Timed out fetching data. Variable display may be inaccurate." in Xcode while running GDB
I've been getting this error message in the debugger console of Xcode, while trying to debug some iPhone apps. At the same time it appears that GDB is freezing, after hitting a breakpoint and trying to step through the code, with the app being debugged eating up the available CPU juice.
Even if my app were very buggy, this still wouldn't be a reason to hang GDB, so I was starting to suspect that it may be a GDB issue. I discovered that if I moved my breakpoints the problem would disappear.
For example in the screenshot below, if I set the breakpoint anywhere on the lines 30-34 the problem appears, if I set it to line 36 the problem goes away.
While I don't know the root cause of this, a workaround is to move your breakpoint past the trouble zone.
Even if my app were very buggy, this still wouldn't be a reason to hang GDB, so I was starting to suspect that it may be a GDB issue. I discovered that if I moved my breakpoints the problem would disappear.
For example in the screenshot below, if I set the breakpoint anywhere on the lines 30-34 the problem appears, if I set it to line 36 the problem goes away.
While I don't know the root cause of this, a workaround is to move your breakpoint past the trouble zone.
Thursday, August 05, 2010
how to fix The 'view' outlet of 'File's Owner' is connected to 'View' but 'view' is no longer defined on ...
I've been getting this warning lately, following adding a new view controller, with its companion NIB file to an iPhone OS project built with Xcode 3.2.3.
While this warning could be legitimate, if someone removed an outlet definition from the source files, without updating the NIB, in this case it happened without me making any change. So the project would compile and run properly, then I'd add this controller, build and get this error.
Turns out this is a bug in Xcode, and it happens when you build the project while the app is still running in the simulator. Just quit the app, build and the warning goes away.
While this warning could be legitimate, if someone removed an outlet definition from the source files, without updating the NIB, in this case it happened without me making any change. So the project would compile and run properly, then I'd add this controller, build and get this error.
Turns out this is a bug in Xcode, and it happens when you build the project while the app is still running in the simulator. Just quit the app, build and the warning goes away.
Thursday, July 22, 2010
how to duplicate a project in Xcode
These steps seem to work for me in Xcode 3.2:
1. In the Groups & Files pane, control-click on the project name (e.g. "ExampleProj") and choose the "Reveal in Finder" option.
2. Make a copy of that folder (e.g. "ExampleProj copy") and rename it to the name you desire for your duplicate project (e.g. "ExampleProjDuplicate").
3. Go inside the new folder and rename the .pch and .xcodeproj entries (e.g. rename "ExampleProj_Prefix.pch" to "ExampleProjDuplicate_Prefix.pch" and "ExampleProj.xcodeproj" to "ExampleProjDuplicate.xcodeproj".
4. The .xcodeproj is actually a folder, you can see its content in Finder by control-click it and choose "Show Package Content". You should see a project.pbxproj file in there.
5. Open the project.pbxproj file in a text editor and replace all old references to the .pch file to the new one that you renamed at step 3 (e.g. replace all "ExampleProj_Prefix.pch" to "ExampleProjDuplicate_Prefix.pch")
6. Load the new project file (e.g. "ExampleProjDuplicate") in XCode and choose the "Build"->"Clean all Targets".
7. Done.
Sunday, July 18, 2010
how to run PHP in interactive mode
One way is to invoke the interpreter from command line like so:
php -a
That seemed to be acting up lately for me, especially in failing to display any output sent with echo.
So I was looking for an alternative replacement, which I found here, courtesy of Facebook. It has all functionality I need.
It depends on Python to be installed on your machine. Once installed you invoke it by simply typing phpsh at the command line.
php -a
That seemed to be acting up lately for me, especially in failing to display any output sent with echo.
So I was looking for an alternative replacement, which I found here, courtesy of Facebook. It has all functionality I need.
It depends on Python to be installed on your machine. Once installed you invoke it by simply typing phpsh at the command line.
Sunday, July 04, 2010
how to use your own domain name to point to Amazon S3 content
If you use S3 to store content viewable on the web, it may make sense to keep your options open, for a possible switch to a different provider later on, if need be, that wouldn't result in broken links.
To do that you need to control the URL of your content, the full URL, including the host name, hence its full domain. I've been looking for a solution to this problem and I found an excellent post.
The gist is to create a subdomain for your content, e.g. content.example.com and map that subdomain through a CNAME DNS record to the Amazon's S3 domain. The essential thing is to have the name of your S3 bucket containing your content match exactly the name of your subdomain, that is, your bucket name for our example should be "content.example.com".
The post above mentions adding a trailing dot at the end of Amazon's domain. I guess that's dependent on your domain management console. In my case I'm using Godaddy's tools and found that trailing dot not necessary.
To do that you need to control the URL of your content, the full URL, including the host name, hence its full domain. I've been looking for a solution to this problem and I found an excellent post.
The gist is to create a subdomain for your content, e.g. content.example.com and map that subdomain through a CNAME DNS record to the Amazon's S3 domain. The essential thing is to have the name of your S3 bucket containing your content match exactly the name of your subdomain, that is, your bucket name for our example should be "content.example.com".
The post above mentions adding a trailing dot at the end of Amazon's domain. I guess that's dependent on your domain management console. In my case I'm using Godaddy's tools and found that trailing dot not necessary.
Thursday, July 01, 2010
how to upload in bulk to Amazon S3
Recently I had to upload a folder containing many files (over 60000) to Amazon S3 storage service.
Amazon doesn't have a tool for this, and their APIs seem to support transferring only one file at the time. I was looking for a solution that wouldn't tie up my computer for a long duration while doing the transfer piecemeal. So I thought why not make a tarball with my files and transfer it in one fell swoop via ftp on a Linux box running as an instance of Amazon elastic computing cloud EC2. Then the plan was to run some scripts to unpack the thing and transfer the files fast between EC2 and S3 servers, through Amazon's internal network.
Assuming you already have a subscription to Amazon's services, and know your way around EC2, here are the steps to take (you may need to change the things in pink to match your needs) :
1. look for a suitable AMI to base your instance on:
ec2-describe-images -o self -o amazon | grep getting-started
2. pick and run an instance
ec2-run-instances ami-3c47a355 -k gsg-keypair
2.5 wait until your instance is running, check on its status with
ec2-describe-instances
3. log in to the instance
ssh -i id_rsa-gsg-keypair root@ec2-184-73-124-245.compute-1.amazonaws.com
4. on the instance, look for and install ftp
yum list vsftpd
yum install vsftpd.i386
5. configure the ftp server (uncomment "#anon_upload_enable=YES") in
/etc/vsftpd/vsftpd.conf
6. start the ftp server
/etc/init.d/vsftpd start
7. prepare the destination for anonymous ftp (with total disregard for the security implications)
mkdir /var/ftp/pub/upload
chmod 777 /var/ftp/pub/upload
8. from your computer ftp the tarball to the server (ftp anonymous@ec2-184-73-124-245.compute-1.amazonaws.com/pub/upload)
9. after the ftp transfer completes, go back to the instance and unpack the tarball
10. install an awesome script from http://timkay.com/aws/
cd
curl timkay.com/aws/aws -o aws
11. configure the tool with your Amazon credentials, by creating/editing an .awssecret file and placing your Access Key ID on the first line, followed by your Secret Access Key on the second line
12. save the .awssecret file and set its permissions
chmod 600 .awssecret
13. install the tool
perl aws --install
14. give it a try, make a destination bucket to S3
s3mkdir tartordesign
15. go where your files are
cd /var/ftp/pub/upload
16. prepare an awk script to do the upload and, optionally, set the visibility of your files to public (I needed mine to be available on the web), edit a vi do.awk file with the following content:
{
printf "putting %s\n", $1
cmd = "aws put tartordesign/ " $1
system(cmd)
cmd = "aws put tartordesign/" $1 "?acl --set-acl=public-read"
system(cmd)
}
17. run the script that does the job
ls | grep .jpg | awk -f do.awk -
note: sometime the piping construct above may fail because "ls | grep .jpg |" produces truncated names. A more robust approach is this:
ls | grep .jpg > list
awk -f do.awk list
18. go about your business until it's finished (you may choose to send the output of that script to a file and later check on the progress by tailing on that file, that way you may end your ssh connection established at step 3, so you're not tied up to anything, instead you reconnect only periodically to check the progress)
19. once the script is done terminate your running EC2 instance, the one you started at step 2
ec2-terminate-instances i-2b57bf41
That's it folks.
Amazon doesn't have a tool for this, and their APIs seem to support transferring only one file at the time. I was looking for a solution that wouldn't tie up my computer for a long duration while doing the transfer piecemeal. So I thought why not make a tarball with my files and transfer it in one fell swoop via ftp on a Linux box running as an instance of Amazon elastic computing cloud EC2. Then the plan was to run some scripts to unpack the thing and transfer the files fast between EC2 and S3 servers, through Amazon's internal network.
Assuming you already have a subscription to Amazon's services, and know your way around EC2, here are the steps to take (you may need to change the things in pink to match your needs) :
1. look for a suitable AMI to base your instance on:
ec2-describe-images -o self -o amazon | grep getting-started
2. pick and run an instance
ec2-run-instances ami-3c47a355 -k gsg-keypair
2.5 wait until your instance is running, check on its status with
ec2-describe-instances
3. log in to the instance
ssh -i id_rsa-gsg-keypair root@ec2-184-73-124-245.compute-1.amazonaws.com
4. on the instance, look for and install ftp
yum list vsftpd
yum install vsftpd.i386
5. configure the ftp server (uncomment "#anon_upload_enable=YES") in
/etc/vsftpd/vsftpd.conf
6. start the ftp server
/etc/init.d/vsftpd start
7. prepare the destination for anonymous ftp (with total disregard for the security implications)
mkdir /var/ftp/pub/upload
chmod 777 /var/ftp/pub/upload
8. from your computer ftp the tarball to the server (ftp anonymous@ec2-184-73-124-245.compute-1.amazonaws.com/pub/upload)
9. after the ftp transfer completes, go back to the instance and unpack the tarball
10. install an awesome script from http://timkay.com/aws/
cd
curl timkay.com/aws/aws -o aws
11. configure the tool with your Amazon credentials, by creating/editing an .awssecret file and placing your Access Key ID on the first line, followed by your Secret Access Key on the second line
12. save the .awssecret file and set its permissions
chmod 600 .awssecret
13. install the tool
perl aws --install
14. give it a try, make a destination bucket to S3
s3mkdir tartordesign
15. go where your files are
cd /var/ftp/pub/upload
16. prepare an awk script to do the upload and, optionally, set the visibility of your files to public (I needed mine to be available on the web), edit a vi do.awk file with the following content:
{
printf "putting %s\n", $1
cmd = "aws put tartordesign/ " $1
system(cmd)
cmd = "aws put tartordesign/" $1 "?acl --set-acl=public-read"
system(cmd)
}
17. run the script that does the job
ls | grep .jpg | awk -f do.awk -
note: sometime the piping construct above may fail because "ls | grep .jpg |" produces truncated names. A more robust approach is this:
ls | grep .jpg > list
awk -f do.awk list
18. go about your business until it's finished (you may choose to send the output of that script to a file and later check on the progress by tailing on that file, that way you may end your ssh connection established at step 3, so you're not tied up to anything, instead you reconnect only periodically to check the progress)
19. once the script is done terminate your running EC2 instance, the one you started at step 2
ec2-terminate-instances i-2b57bf41
That's it folks.
Friday, June 11, 2010
be mindful of possible glitches in Apple's Xcode tools
I really like Apple's development tools, including the SDK and frameworks, especially as a developer coming from different platforms for mobile development. Yet, nice as they are, they're still not infallible.
Today I was having issues running a project in the debug mode, in the emulator. In some instances the emulator would not recognize my touchy input. In other case, a UITextView, which although clearly marked as not editable in the IB, it would show me the text cursor. All these abnormal behaviours were sporadic, not always happening. And in both cases, launching the app directly from the springboard, in the emulator would cause it to work properly. It may be a debugger issue.
Also, for some reason the runtime would ignore some of my settings regarding some struts, springs and autoresizing for some elements as I specified them in IB, and would mess up the layout upon changing the orientation of the device. To fix that I had to manually enforce them in didRotateFromInterfaceOrientation:.
Today I was having issues running a project in the debug mode, in the emulator. In some instances the emulator would not recognize my touchy input. In other case, a UITextView, which although clearly marked as not editable in the IB, it would show me the text cursor. All these abnormal behaviours were sporadic, not always happening. And in both cases, launching the app directly from the springboard, in the emulator would cause it to work properly. It may be a debugger issue.
Also, for some reason the runtime would ignore some of my settings regarding some struts, springs and autoresizing for some elements as I specified them in IB, and would mess up the layout upon changing the orientation of the device. To fix that I had to manually enforce them in didRotateFromInterfaceOrientation:.
Subscribe to:
Posts (Atom)







