As my Swarm Chemistry iPad application continues to evolve, I wanted to add a feature that allows users to easily share swarm chemistry recipes with each other. The way I've decided to do this uses links that can be emailed from within the app (which is now named Emergent) that launch Emergent with the desired configuration.
iOS supports Custom URL Schemes which do exactly this. In a nutshell, Emergent creates a URL which looks something like this:
emergent://?r=45673766682117&g=16799217307868&b=87608347801912
...and when a user (who has the application installed) clicks that link, Emergent opens and sets all its parameters accordingly.
The first step is to register the scheme. This is done in Xcode by opening Info.plist and using the Editor menu to add items for the URL scheme and identifier. Mine now looks like this:
My next step was to write the Swift code that generates the URL. To keep the links short, I decided to condense the seven parameter values that form a genome description into a single string. Each parameter is normalised, so I take the value of each with a precision of 2, remove the decimal places and concatenate them together.
For example, if a genome has a radius of 0.7, a cohesion of 0.45 and an alignment of 0.8, the first six characters will be 704580. To do this, I created a small extension to Float to convert, for example, 0.45 to the string "45":
extension Float
{
func decimalPartToString() -> String
{
let formatter = NSNumberFormatter()
formatter.multiplier = 100
formatter.allowsFloats = false
formatter.formatWidth = 2
formatter.paddingCharacter = "0"
return formatter.stringFromNumber(self)!
}
}
...and added a toString() method to my SwarmGenome class that concatenates them together:
func toString() -> String
{
returnradius.decimalPartToString() + c1_cohesion.decimalPartToString() + c2_alignment.decimalPartToString() + c3_seperation.decimalPartToString() + c4_steering.decimalPartToString() + c5_paceKeeping.decimalPartToString() + normalSpeed.decimalPartToString()
}
To create create the URL itself, I use NSUSRLComponents: by creating an NSURLQueryItem for each genome, Swift generates a correctly formed URL so I don't have to think about wrangling with strings, question marks and ampersands:
let urlComponents = NSURLComponents()
urlComponents.scheme = "emergent"
let redQueryItem = NSURLQueryItem(name: "r", value: redGenome.toString())
let greenQueryItem = NSURLQueryItem(name: "g", value: greenGenome.toString())
let blueQueryItem = NSURLQueryItem(name: "b", value: blueGenome.toString())
urlComponents.queryItems = [redQueryItem, greenQueryItem, blueQueryItem]
// urlComponents.URL is now a properly forced URL
The mail is sent using MFMailComposeViewController and I set its body with the URL:
let link = "\(urlComponents.URL!)\">\(urlComponents.URL!)"
picker.setMessageBody(link, isHTML: true)
The next stage is to do this backwards when a user clicks a link. Since Emergent has registered itself, when that link is clicked, the app opens and, inside the AppDelegate, the openURL application() method is invoked. In here, I create an NSURLComponents instance from the supplied URL and loop over its queryItems collection:
func application(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject?) -> Bool
{
iflet swarmChemistryViewController = window?.rootViewControlleras? ViewController
{
let components = NSURLComponents(URL: url, resolvingAgainstBaseURL: false)
iflet queryItems = components?.queryItemsas? [NSURLQueryItem]
{
for (idx: Int, component: NSURLQueryItem) inenumerate(queryItems)
{
swarmChemistryViewController.genomes[idx] = SwarmGenome.fromString(component.value!)
}
}
}
returntrue
}
The static fromString() method on the SwarmGenome class deconstructs the string using a small extension to get characters from a string using a subscript, and divides each by 100 to get the normalised value:
staticfunc fromString(value: String) -> SwarmGenome
{
let r = value[0...1].floatValue / 100.0
let c1 = value[2...3].floatValue / 100.0
let c2 = value[4...5].floatValue / 100.0
let c3 = value[6...7].floatValue / 100.0
let c4 = value[8...9].floatValue / 100.0
let c5 = value[10...11].floatValue / 100.0
let n = value[12...13].floatValue / 100.0
returnSwarmGenome(radius: r, c1_cohesion: c1, c2_alignment: c2, c3_seperation: c3, c4_steering: c4, c5_paceKeeping: c5, normalSpeed: n)
}
And there we have it - users can now share swarm chemistry recipes via email.
Emergent is an open source project and the source code is available in my GitHub repository here.