Since then, the sleep timer functionality has been used thousands of times by hundreds of people. During that same time, the Roku remote control provided by Roku has increased in usefulness adding some of the features I found most useful in Romote.
Considering those facts, I decided to take the Roku sleep timer functionality from Romote and make it a stand alone app:
Ro To Sleep adds the same great sleep timer capability to your Roku with a simple interface that lets you get your timer set without any fuss. On devices that support it, Ro To Sleep lets you set a timer right from your home screen using 3D Touch.
Ro To Sleep is free to use up to 5 times - you can make sure the sleep timer meets your needs before having to commit any money. After that, a one-time in app purchase unlocks unlimited use of the sleep timer. Use of the timer has the same caveats as the Romote timer:
For anyone that has purchased Romote, the timer built-in timer capability will continue to work for at least the next 6 months, but Romote itself won’t continue to be developed.
Head on over to the iTunes store and get your copy of Ro To Sleep and enjoy falling asleep knowing that your Roku won’t be playing all night.
]]>To get started using SNS, you first need to create an app in SNS to represent your application. The Amazon documentation is pretty easy to follow - you will need the APNS certificate and private key from Apple for setting up push to iOS devices.
Once you have an app created, you can start registering devices with it. This is accomplished using the CreatePlatformEndpoint API and requires the device token for the iOS device. I make the registration call from the application:didRegisterforRemoteNotificationsWithDeviceToken:
callback in my application’s delegate.
I proxy all of the calls to SNS through a server that I run, so devices never communicate directly with SNS. This allows me to not expose any SNS credentials in my iOS apps as well as gives me the flexibility to change the communication with SNS (or remove SNS completely) without resubmitting the iOS apps.
Registering a device is easy using the Ruby aws-sdk:
1 2 3 4 5 6 |
|
The endpoint_arn
can then be sent back to the device to use in future push notifications (or stored for later use at the server).
Once a device is registered, you can send direct push notifications to that device. This is done using the Publish API. The Publish
API supports sending a different message to each supported SNS transport as well as specifying a default message; when using device-specific mobile push notifications this isn’t necessary as each device will only be using a single transport (APNS or APNS_SANDBOX is the case of iOS).
SNS supports specifying all of the keys that Apple expects in a push notification using transport-specific messages. This requires setting the MessageStructure
parameter of the Publish
call to “json” and supplying a JSON formatted object in the Message
parameter. One thing the documentation doesn’t make clear is that these need to be JSON-encoded strings and not raw JSON. An example will make this clearer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Notice the to_json
calls on lines 5, 9, and 14. Each transport key needs to be a JSON string inside the Message
parameter. Additionally, the Message
parameter itself needs to be a JSON string. Getting this wrong (by having actual JSON instead of a JSON string) leads to SNS not being able parse the Message
parameter and generates an error of “InvalidParameter: Invalid parameter: JSON must contain an entry for ‘default’ or ‘APNS_SANDBOX’”.
The value of the other_data
key is available in the userInfo
dictionary provided to the application:didReceiveRemoteNotification:
callback in your iOS application:
1 2 3 |
|
Using just a few straightforward calls, it’s possible to send push notifications to your iOS apps. SNS also supports other mobile platforms (including Google Cloud Messaging and Amazon Device Messaging) as well as broadcasting messages to subscribed devices (using topics) instead of targeting individual devices.
]]>END UPDATE
I’ve been using two Roku streaming players for just over two years now. Channels are added regularly and the addition of WatchESPN recently added some sports - something that’s noticeably missing from many streaming solutions.
But one thing that the Roku lacks is the ability to set a timer that will cause playback to stop (a sleep timer). Falling asleep in bed while watching the Roku is something that I do a few times a week. The lack of a timer isn’t so bad if you’re watching a channel that streams episodes or movies (like Hulu Plus and Netflix) since the playback will eventually end when the end of the current item is reached. But for channels that play a live stream (like WatchESPN), the Roku will happily keep streaming long after you’ve fallen asleep.
So I decided to do something about the missing timer.
The latest version of Romote now includes a sleep timer for your Roku! Set the timer before you fall asleep and Romote will send your Roku to the home screen (thereby stopping playback) at the time you choose. There are a few caveats:
Head on over to the iTunes store and get your copy of Romote and enjoy falling asleep knowing that your Roku won’t be playing all night.
]]>I’ve posted a copy of my slides on SlideShare. Here’s a list of resources that I’ve used in setting up nginx, Unicorn, and Capistrano.
Here’s the Github repository for the small example I showed at the end of the presentation. In the sample_config_files
directory there are a few files that are not actually part of the Rails project but instead are config files that would be found on the server:
/etc/init.d
and used to start Unicorn on machine startupunicorn_init
.examples
directory has sample configuration files for Unicorn, nginx, and logrotate with lots of comments.Capistrano recently released a new version (3.0) that makes some changes to how Capistrano is configured and runs. I’ve only used the 2.x version and that’s what I covered in my presentation. The Capistrano website has information on the new version; I’ve used the Github wiki when setting up version 2.x.
These are the Linux hosts I mentioned during my presentation; there are certainly a lot more.
Thanks to all of the other speakers and the conference organizers for an enjoyable day. I also had a fun lunch discussion with Scott, Stefan, Samrat, Hector, Jeff, and Nathan (sorry if I missed anyone!).
If you have any questions about my presentation, feel free to reach out on Twitter or by email.
]]>Since the entire site will be served over SSL, I configured nginx to do the SSL redirects for me:
1 2 3 4 5 |
|
Now whenever a non-SSL request comes in, nginx will redirect to the same path using SSL. My SSL server configuration is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
This is just the same configuration I previously had (before adding SSL) with the listen
directive changed to 443
and the ssl
directives added.
After making these changes and restarting nginx, everything seemed to be working correctly. Any requests to the non-SSL site were redirected with a 301
response code as expected.
After a day or so I noticed that URLs being generated by Rails were not using the https scheme. I originally noticed it with named routes like so:
1 2 3 4 5 6 7 8 9 10 |
|
Anywhere Rails was generating a full URL, the non-SSL version was being generated. It took me a bit to notice this since in most places I was using the _path
version of the named routes which generates just a relative path. So on a page that was served over SSL, those relative paths of course led to SSL pages. I should note that the site continued to “work” since the generated non-SSL URL was then redirected to the SSL version by nginx, but I definitely did not want to continue to generate extra requests (it also caused a problem with some URLs when accessed via jQuery Mobile).
It took quite a lot of searching to finally find the answer; most of the things written about Rails and SSL are in relation to either 1) having Rails do the SSL redirection using something like config.force_ssl = true
or 2) managing a site with some pages served over SSL and some not. The few references I found mentioned Rails using whatever scheme was in use by the current page when generating URLs (which intuitively made sense to me since it got the host name correct).
I finally ended up debugging in to the Rails source to see how it makes the decision on what scheme to use when generating URLs. After a few layers, it comes down to this code in Rack:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
Rails uses the ssl?
method to determine what scheme to use in generated URLs. When I debugged my site running under SSL, none of the env variables were set, causing Rails to treat the request as if it came in over regular http.
The solution turns out to be simple (as they often are when you can track down the root cause). Add an extra header in the location
configuration in nginx:
1 2 3 4 5 6 7 |
|
After adding that single line and restarting nginx, Rails is generating URLs using https instead of http. In hindsight, it makes sense that Rails was not seeing the right scheme since the SSL is being terminated at nginx, but it never occurred to me that that’s what might be happening.
]]>When I developed my first iOS application (Easy Pitch Counter) I signed up for an individual iOS developer account. Later when my wife and I started our company, I signed up for a company/organization iOS developer account so that we could keep company-related revenue/paperwork/etc. separate.
At the time, Apple didn’t allow you to transfer apps between developers, so I was stuck leaving my original two apps under my personal account while new apps went under the company account. Now that transfer is enabled, I moved all of my apps to the company account. The process is quite straightforward.
The first step is to log in to iTunes Connect using the account that currently houses the application to be transferred. Head to the app details page and click the “Transfer App” button. This brings you to a summary page that lists all of the requirements to enable app transfer as well as whether or not the current app meets them.
The next thing you need is the information for the developer you’ll be transferring the app to. The recipient’s Apple ID is the easy part, but you’ll also need the recipient’s team ID. You can get this value by logging into the iOS Dev Center using the account that will receive the transferred application. Then proceed to the Member Center and click the organization name near the top of the page. This will display the account’s profile, where you can find the necessary team ID.
After entering the recipient information, you’re presented with a transfer agreement that you must agree to. Once you agree, the work is done for the account initiating the transfer. The app detail page for this app will now display a banner indicating that a transfer has been initiated:
The next steps relate to accepting the inbound transfer using the destination account in iTunes Connect. Once you log in using the destination account, head to the “Contracts, Tax, and Banking” section to view any inbound transfers.
Select the pending transfer to see the incoming transfer agreement. You’re also presented with the opportunity to update some of the app’s metadata (the items I saw were the support and marketing URLs and the app review contact info). After filling in the metadata and accepting the agreement the actual transfer process is started.
Apple says it may take up to 3 hours for the transfer to complete (or up to 2 days in some cases) but both apps I transferred were completed in a matter of minutes.
This new ability to transfer apps may not seem like a big deal if you’re not selling any of your apps but having one active iOS developer account simplifies things for me. In addition to the financial/paperwork aspect, having only one set of certificates/provisioning profiles/etc. is a big win for me.
]]>The main entry point to using Haml from a command line is the Haml::Engine class. The documentation on using Haml directly like this is a little light, so I’ve created an example using it and made it available on Github.
In addition to just generating HTML from a Haml file, I also wanted to use some of the conveniences that are available when using Haml in a web setting. For me, this meant being able to use a layout file as a “wrapper” around content and being able to use partials to pull out repeated code.
The generate.rb file in my example has a fair number of comments, so I’ll just hit some of the high points here.
1 2 3 4 5 6 |
|
The first step is to instantiate an instance of Haml::Engine
pointing to the layout template. Then we need an instance of the Context
class to provide data to the templates as well as functions that can be called from within the templates. Next is a call to render
on the engine instance. We pass in the context object and a block - the block will be called when a yield
statement is encountered in the layout template. This is the core of what enables the use of a layout.
Inside the render block, a new Haml::Engine
instance is instantiated pointing to the “main” template being rendered. Then it’s just a call to render
on that instance, passing along the same Context
instance.
A quick word about the way I’m using partials. I wanted the ability to render common markup on multiple pages (or render page-specific markup in the layout); the same way you would use partials on the web.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
The implementation here is pretty simple and uses just a few conventions. Partials live in the partials
subfolder of the views
folder. The default filename for a partial is just the name of the partial. We also check for a file prefixed with the name of the enclosing template and use that if found. That allows us to use different content depending on the “main” template that’s being rendered (check out the “title” partials in the example). If neither the template-specific nor default partial is found, nil
is returned and nothing is rendered in the enclosing template.
1 2 3 4 5 |
|
Calling the partials from a template is straightforward. One thing to note is that the name of the function (render_partial
) is arbitrary - as long as the function exists on the Context
object that get passed to the render
method it can be called from within a template.
My example certainly doesn’t cover everything that could be done using Haml from the command line. But hopefully it’s enough of a start to give you some ideas.
]]>The purpose of Armadillo Group Text is to allow organizers of small groups to send text announcements to their members. It’s ideal for coaches to notify their players of changes to a game or for a business association’s director to let members know that a meeting has been moved. Any group that needs to notify members in a timely fashion can benefit from using Armadillo Group Text.
This is my first time using Twilio and it’s been a joy so far. The API is simple to use (especially with the help of the twilio-ruby gem) and has been solid. The ease of use has me wondering what kind of project I could do to utilize their voice services.
If you need to send a message to a group, give Armadillo Group Text a try.
]]>I wholeheartedly agree with the points Lex makes in his article. The pressure to price an app cheaply is real. This is detrimental to the health of the App Store since the lower prices make it more and more difficult for professional developers to make a living selling apps.
The other thing I’ve noticed is a sense of entitlement on the part of app buyers even when they’ve purchased an inexpensive app. I have no problem with and will gladly support an app that I’ve written; the part that bothers me is the expectation on the part of the user of instant, in-depth support on something they’ve paid a dollar for. It’s unfortunate but true that the $0.70 I receive from each purchase does not account for very much support time (even using the low rate of $50/hr as an example, $0.70 only buys 50 seconds of support).
Here’s a real world example: My Romote app currently sells for $0.99. It needs to communicate with your Roku device via your home network. It attemps to auto-detect any Rokus you have, but this is a hit-or-miss proposition since many times the Roku devices will not respond to detection requests. You can input the IP address of your Roku manually, but of course that means you need to find out what it is. In addition, the variety of setups involved in users’ home networks complicates matters.
Taking all of this together, it’s not surprising that I receive regular requests for help with Romote. I’m happy to provide whatever help I can, which usually involves offering some suggestions around auto-detection and determining a Roku’s IP address. But some people aren’t interested in even giving me a chance:
Please respond I spent $ and would like results
don’t charge my account for that$$$ this app stinks as far as working on my roku. Your services did not work for me so I will not pay.
So since I didn’t respond within 8 minutes, I have no chance to respond at all. I understand that it’s frustrating when an app doesn’t work immediately, but I do take pride in my work and use the app regularly myself so I know that it does work.
Occasionally I wonder if raising the price of the app would cut down on these types of angry requests. Having fewer customers would also allow me to spend more time helping those that need it. Of course, it may also reduce my sales to nothing since there are free competitors in the App Store.
]]>This additional functionality may take the form of including a text field on an alert or showing a progress indicator on the alert dialog. Or, you may want to make the visual style of the alert match the rest of your app more closely.
A great library for assisting with these kinds of things is CODialog by Erik Aigner. The Github project page has some screenshots showing the kinds of things you can do with only a few lines of code. The library is easy to include in your project since it consists of a single header file and a single implementation file.
In addition to the out-of-the-box examples, CODialog is easy to customize in other ways as well. While I was working on Baseball Field Layout I wanted to create alert dialogs that matched the style of the app. Using CODialog and customizing the implementation slightly, I was able to get alerts that looked just the way I wanted.
This can certainly be taken too far and you should always keep the iOS Human Interface Guidelines in mind. But for adding that missing piece of functionality or adapting the display of alerts, trying something a bit different can have a big effect on your app.
]]>I decided to try to make it easier to lay out a basic diamond when practicing at one of these spots. Baseball Field Layout is the result. To use the app, you just stand where you want home plate to be and let the iPhone’s GPS record your location. Then you walk in the direction of the pitcher’s mound and Baseball Field Layout takes care of the rest, giving you the location of the pitcher’s mound and all three bases. Field size is selectable at 60′, 70′, or 90′.
This was also an opportunity to try out some iOS frameworks that I haven’t used before. I got to use Core Location, MapKit, In-App Purchase, and iAd for the first time. While In-App Purchase and iAd aren’t essential to this app, it was a nice way to try them out.
Due to the limits of the accuracy obtainable with the iPhone GPS you should only use this app for practicing or goofing around.
Baseball Field Layout is designed for the iPhone and requires a GPS to get reasonable accuracy.
]]>One of my apps (Romote) is a WiFi remote for Roku 2 streaming devices. I wrote this app mostly for my family’s use and only put it in the App Store since I already had an iOS developer account. Much to my surprise, the app has been a consistent seller at $0.99 (with no outside promotion) even against free competition (there are a few features that differentiate it from its competitors like correct handling of dication in search fields and an iPad optimized layout). While the volume of the sales were nothing special, they were consistent - at least until they disappeared entirely.
Around the end of last year (2012) the sales regularly dropped to zero per day and stayed like that for multiple days at a time. At the time I just assumed that people were no longer interested in paying $0.99 for an app when there were free alternatives. Then while searching for something in the App Store I happened to try a search for “roku remote” and was quite surprised when Romote did not appear at all in the results. Searching for the app name itself returned the app in approximately the 150th position, behind such apps as “Fart Studio - Revolutionary New Farting Surface!” and “NASA Earth as Art”, as well as several of its competitors.
I contacted Apple developer support about the problem and received a predictably vague response about many factors affecting an app’s search ranking. While I understand the need for circumspection in these matters to prevent gaming and abuse I was frustrated that my app was not appearing for what I considered very relevant searches. Whether as a result of this interaction or just via an update to the search ranking algorithm, Romote soon began appearing in results again and the graph above clearly shows the impact on sales.
I think this illustrates a problem for apps that aren’t receiving promotion outside the App Store (via reviews, blogs, etc.). The discoverability of the app in the store will greatly impact its sales - this is even more true with the iOS 6 App Store redesign that shows fewer search results at a time. The part that is a problem is the fact that you have almost no recourse when this occurs. Adjusting app names and keywords in an attempt to appear in results for truly relevant searches is a Sisyphean task. This is analogous to Google controlling the majority of search traffic on the web (and with the same games being played by unscrupulous players) - if your product/site isn’t returned in Google searches your traffic will suffer.
I don’t think that there is a real solution to this problem (other than outside promotion) since Apple controls the App Store - that’s the price of admission for making your app available to the millions of iOS users out there. But it is something to keep an eye on and be aware of, especially for smaller developers.
]]>distanceFromLocation:
) it doesn’t have one for doing the opposite: calculating a second location based on a distance (and bearing) from the first.
So I added a category on CLLocation that performs this calculation:
1 2 3 4 5 6 |
|
The other need I had was to calculate the bearing between two points:
1 2 3 4 5 6 7 |
|
Both of these calculations use the formulas provided on the page Calculate distance, bearing and more between Latitude/Longitude points. This page has lots of information on the formulas and provides them in mathematical notation as well as examples translated to Javascript.
My Objective-C category incorporating these calculations is available from my Github account.
]]>Basic scraping can be done by simply requesting the page and looking through the returned string for the information you want. For simple data extraction, that may be all you need. But if the data you’re looking for is harder to find, a library that provides some structured ways to access the data is more appropriate.
One such library in Ruby is Nokogiri. Although I usually think of Nokogiri as an XML parser, it also handles HTML. Grabbing a page and finding some text is a few simple commands away:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
If reading data is the only thing you need to do, then Nokogiri gets the job done.
Sometimes you may need to send some data in addition to just reading it. One common situation is needing to log in to a site before being able to read the data you’re interested in. In that case, another library comes to the rescue: Mechanize.
Mechanize can handle all of the “bookkeeping” aspects of logging in: setting and sending cookies, following redirects, etc. Mechanize also provides methods for dealing with links, forms, and other structural elements of a page. And when it comes time to extract the data, Mechanize uses Nokogiri under the hood so that you can use the same CSS and XPath selectors that Nokogiri provides.
Mechanize provides some nice examples in their documentation so I won’t repeat them here. Definitely check it out if you need to do any more “advanced scraping”.
While someday there may be an API available to get at any data that anyone finds interesting, until then don’t discount scraping as a viable alternative.
]]>UISwitch
inside a table cell to represent an on/off setting:
So how do you actually go about making this type of cell? The method I’m going to describe here doesn’t require a custom UITableViewCell subclass so that makes it easy to use in just about any iOS project.
The first step is to configure your table cell to contain a UISwitch
and a UILabel
(for the description):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
This looks like a lot of code, but most of it is related to positioning the label and the switch in the table cell.
First, the switch. On line 7, we get the size needed to display the switch. Then the switch’s frame can be calculated (lines 8 - 11). The x
position of the switch is calculated by subtracting the switch’s width (plus a little padding) from the cell’s contentView
width. This places the switch against the right-hand side of the cell (inset by the padding amount). The y
position of the switch is calculated to center the switch vertically in the cell.
The label is a little simpler. On line 19 CGRectInset
is used to give us a CGRect
with a horizontal padding of 10 and a vertical padding of 8. Then the width of the label is shrunk on line 20 to be half the width of the cell.
Once the cell is configured, we set the switch’s value (finding it via the tag assigned on line 13) and we’re off to the races.
Now we have a cell that displays a label and a switch with the switch’s value set appropriately. How do we handle the user tapping the switch? On line 14 in the setup code above, we added an action to be called when the switch’s value is changed. We need to implement that action method:
1 2 3 4 5 |
|
There’s one additional step we can take to improve the cell’s behavior. Right now, if the user taps anywhere in the cell that isn’t the switch, nothing happens. We should make it so that tapping the cell anywhere (the label, the padding, etc.) triggers the switch. That can be handled in the table view’s delegate:
1 2 3 4 5 6 7 |
|
First we get a reference to the cell that was tapped, then the switch in that cell. Line 5 animates toggling of the switch’s value. Then we call the same action method that was assigned to the switch to process the value.
There are other ways to include a UISwitch
in a UITableViewCell
. One method is to subclass UITableViewCell
and implement similar logic to that shown here. That method is useful for encapsulating the switch and label related code. If you take that route, you will probably want to create a delegate for your new subclass to allow the consuming code to be notified of switch value changes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|
This is fairly straightforward reflection code that loops through all of the public properties on ClassWithAttribute
and returns the names of any that have the ExampleAttribute
applied. In the example shown, on line 29 the properties
variable will be a list containing the words “Name” and “Age”.
But what happens if we use a class derived from ClassWithAttribute
?
1 2 3 4 5 6 7 8 |
|
Perhaps surprisingly, the properties
variable on line 7 contains the single word “Age”. This surprised me since I’m supplying true
as the second parameter to GetCustomAttributes()
on line 21 of the first example. When the second parameter is true, the member’s inheritance chain should be searched for attributes. So why didn’t it find the ExampleAttribute
declared in ClassWithAttribute
? The answer is in the documentation, although I missed it the first few times I read it (emphasis mine):
inherit: true to search this member’s inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events; see Remarks.
The second parameter to GetCustomAttributes()
is ignored if the method is being called on a property or an event (we have a property in this case). Luckily the “Remarks” section of the documentation has a solution (emphasis mine):
This method ignores the inherit parameter for properties and events. To search the inheritance chain for attributes on properties and events, use the appropriate overloads of the Attribute.GetCustomAttributes method.
So in this case we can simply replace line 21 in AttributeChecker.PropertiesWithAttribute()
with the following:
1
|
|
Now running the code for DerivedClassWithAttribute
will correctly return both “Name” and “Age”. A simple change, but something that tripped me up for a while none the less.
A couple of Google searches led to a few posts mentioning the fact that the pager looks like it’s invisible when shown on a light background. This is a consequence of the default colors being variations of white. I dismissed this as being my issue since I had set the colors to be dark in the XIB and everything looked fine there.
After much messing around with positioning, colors, etc. (everything I could think of) I went back to Apple’s documentation. The currentPageIndicatorTintColor
and pageIndicatorTintColor
properties are only available in iOS 6.0 and above. Since I was developing with the iOS 6 SDK, the properties were settable in the XIB. But I was running the app on an iOS 5.1 device which didn’t have the properties, so the pager was in fact white. I solved the problem by setting the pager colors back to the default in the XIB and displaying the pager on a dark background.
Two lessons here: 1) When something doesn’t seem to be working, it never hurts to give the documentation another look, and 2) It’s important to test your app on whatever iOS/device versions you’ll actually be deploying to. While this particular issue doesn’t result in a runtime crash, having the pager be invisible to anyone running iOS 5 would not have been a good outcome.
]]>UISwipeGestureRecognizer is easier to configure than some of the more complicated recognizers since there are only two configurable properties: direction
and numberOfTouchesRequired
. I was only interested in single touch swipes, so the default value of 1
was fine for numberOfTouchesRequired
. The direction
property consists of a bit flag of UISwipeGestureRecognizerDirection
values. My first attempt was to just include the values for left and right gestures:
1 2 3 4 |
|
This works fine and swipes in both directions are recognized. The problem comes when it’s time to figure out which direction was swiped in the delegate method:
1 2 3 |
|
There is no way to find out which swipe direction triggered the recognizer when the delegate message arrives.
The solution here is straightforward once you realize the problem: you just need to use two recognizers, each configured for a single direction:
1 2 3 4 5 6 7 |
|
Now a different method (swipeRight
or swipeLeft
) will be called depending on which direction the user swipes. This makes it easy to take the appropriate action in response.
But it turns out that getting started with development is harder than I expected. First off, I’m using almost entirely Macs now (even my wife’s main computer is a MacBook Pro). I thought I’d just use a Parallels VM (like I do in my day-to-day work). Unfortunately, the Windows Phone 8 emulator is actually a Hyper-V virtual machine so that adds some constraints to a development setup:
Since Parallels doesn’t pass through any processor virtualization support I won’t be able to do development in a VM (there are reports of it working in an unsupported way in VMWare Fusion). My next idea was to use a Windows 7 computer I have around and install Windows 8 in a VHD (per Scott Hanselman’s guide). But the computer I was going to do that on has an Intel Core 2 Quad processor which doesn’t support SLAT, so that’s out.
I guess I’ll end up installing Bootcamp on my newest Mac (which has an Intel Core i5) and doing the development there. That’s suboptimal since then I’ll need to reboot to switch between Windows Phone development and doing anything else on my machine.
This decision on the emulator seems odd considering that Windows Phone 8 is starting from behind in terms of developer adoption. One way to get some apps in the store is to entice developers from other platforms to write for Windows Phone (either new apps or ports of existing ones). But many developers (certainly iOS developers but I think most Android developers as well) are not running Windows natively on new hardware which makes the need for hardware supported virtualization cumbersome. In addition, I know many Windows developers use VMs for development in order to manage separate environments, etc. So this requirement is a problem for them as well.
The emulator requirements are also a problem if Microsoft is attempting to lure hobbyist developers. Making a Windows Phone 8 version of Visual Studio Express free is a great way to get people interested (Visual Studio is a very full-featured IDE). But the requirement for Windows 8 Pro means that basic machines being sold at retail stores won’t be able to develop Windows 8 Phone apps without an upgrade. The SLAT requirement also means that only machines purchased in the last 2-3 years (since the release of the Core i3 and i5) are capable of running the emulator.
Obviously iOS development has a high financial bar for entry since you need a Mac; this kind of requirement isn’t unprecented. But Apple isn’t trying to kickstart a new platform at this point. In addition, a 4 year old MacBook Pro is capable of doing development for iOS 6 so at least you don’t need to go out and buy a brand new Mac if you have one around.
It’s probably too late at this point but it would be great if Microsoft could remove the Hyper-V requirement from the Windows Phone 8 emulator. Making it as easy as possible to develop for their platform can only help in the long run.
]]>This works fine by default if you reference all of your javascript files from application.js
which serves as a manifest of included files. In the development environment the files are served individually and un-minified (which is useful for debugging) and in production they are concatenated and minified. But what do you do if you want to have page (or section)-specific files?
Your first approach might be to just add a new file to the app/assets/javascripts/
folder in your project and then reference it from your page:
1 2 |
|
This will work fine in development. The file will be pre-processed and served separately like all the others and life will be good. The problem is that this will fail in production if you’re precompiling your assets (which you should be). The reason is that only application.js
is considered as a manifest during precompilation. So while separated_code.js.coffee
exists in the assets directory it won’t be compiled and minified for production.
You can add additional manifest files to the list considered during precompilation fairly simply by modifying application.rb
. You can modify the precompile
property of the assets configuration like so:
1
|
|
And add a new manifest file that requires
the actual code:
1
|
|
This works but it’s pretty tedious to add every new file like this. So instead, I decided to make it more generic. I modified my precompile
property like this:
1
|
|
Now I can add as many additional javascript manifest files as I need and include whatever individual files in those that I want to. I’ve been using directories to organize the files that go in each bundle. For example:
1
|
|
And in my actual page:
1 2 |
|
One additional step is to remove the require_tree
directive from application.js
. Otherwise (since require_tree
is recursive) the files in each bundle will also be included in the main application file which isn’t what you want.
Now I’m free to organize my javascript files in whatever way makes sense but still get the advantages of the asset pipeline (precompliation/concatentation/minification). I’ve done a similar thing with my CSS files (although I’ve found there’s less reason to separate my CSS as much as my javascript).
I try to avoid putting anything that is shared into more than one of these bundles. Placing the same file in more than one bundle will result in the contents of that file being downloaded more than once since it will be concatenated with the rest of the files in each bundle. So I try to keep shared stuff required from application.js
.