Adding your own GPS data to the iOS Simulator

If you want to simulate GPS data in the iOS simulator you have two choices, Apples ‘real’ data (City run, freeway drive ect) or you can choose a single lat,long to simulate being in a static location, but… what if we want to simulate our own motorcycle ride or boat journey and work with ‘real’ GPS data in the simulator?

My open source tool ‘XJourney‘ (Available HERE) achieves just this. Using XJourney you can put your own GPS data into the iOS simulator to simulate a full GPS Journey just like Apple’s own.

So this article’s aim is to shed some light on how Apple’s GPS test data is stored in the simulator and describe the process that XJourney now automates.

Hit ‘Read More‘ for the full article.


In the iOS simulator we are given a few options to simulate GPS activity.
StockSelection

There isCustom Location‘ but this option only allows us to specify a single lat,long and I want to simulate an entire journey! So I set out to find out how Apple’s test data gets loaded into the simulator.

After a bit of digging we find Apple’s test data is stored as plists inside the iOS simulator bundle,

The location of these is:

Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreLocation.framework/Support/SimulationScenarios/

AppleFolder

Popping open the ‘Freeway Drive’ plist gave me the following (I’ve removed a few hundred ‘Data’ objects)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>Locations</key>
	<array>
		<data>
		YnBsaXN0MDDUAQIDBAUIKClUJHRvcFgkb2JqZWN0c1gkdmVyc2lvblkkYXJj
		aGl2ZXLRBgdUcm9vdIABowkKIVUkbnVsbNsLDA0ODxAREhMUFRYXGBkaGxwd
		Hh8gXxAma0NMTG9jYXRpb25Db2RpbmdLZXlDb29yZGluYXRlTGF0aXR1ZGVf
		ECRrQ0xMb2NhdGlvbkNvZGluZ0tleVZlcnRpY2FsQWNjdXJhY3lfEB1rQ0xM
		b2NhdGlvbkNvZGluZ0tleVRpbWVzdGFtcF8QJmtDTExvY2F0aW9uQ29kaW5n
		S2V5SG9yaXpvbnRhbEFjY3VyYWN5XxAca0NMTG9jYXRpb25Db2RpbmdLZXlM
		aWZlc3Bhbl8QJ2tDTExvY2F0aW9uQ29kaW5nS2V5Q29vcmRpbmF0ZUxvbmdp
		dHVkZV8QGmtDTExvY2F0aW9uQ29kaW5nS2V5Q291cnNlXxAca0NMTG9jYXRp
		b25Db2RpbmdLZXlBbHRpdHVkZV8QGWtDTExvY2F0aW9uQ29kaW5nS2V5U3Bl
		ZWRWJGNsYXNzXxAYa0NMTG9jYXRpb25Db2RpbmdLZXlUeXBlI0BCqupIiK/n
		I7/wAAAAAAAAI0Gy071oTZFoI0AUAAAAAAAAI0A+AAAAAAAAI8BeghVAI1ul
		I0BmR64UeuFIIwAAAAAAAAAAIz/6uFHrhR64gAIQAdIiIyQnWCRjbGFzc2Vz
		WiRjbGFzc25hbWWiJSZaQ0xMb2NhdGlvblhOU09iamVjdFpDTExvY2F0aW9u
		EgABhqBfEA9OU0tleWVkQXJjaGl2ZXIACAARABYAHwAoADIANQA6ADwAQABG
		AF0AhgCtAM0A9gEVAT8BXAF7AZcBngG5AcIBywHUAd0B5gHvAfgCAQIKAgwC
		DgITAhwCJwIqAjUCPgJJAk4AAAAAAAACAQAAAAAAAAAqAAAAAAAAAAAAAAAA
		AAACYA==
		</data>
		<data>
		YnBsaXN0MDDUAQIDBAUIKClUJHRvcFgkb2JqZWN0c1gkdmVyc2lvblkkYXJj
		aGl2ZXLRBgdUcm9vdIABowkKIVUkbnVsbNsLDA0ODxAREhMUFRYXGBkaGxwd
		Hh8gXxAma0NMTG9jYXRpb25Db2RpbmdLZXlDb29yZGluYXRlTGF0aXR1ZGVf
		ECRrQ0xMb2NhdGlvbkNvZGluZ0tleVZlcnRpY2FsQWNjdXJhY3lfEB1rQ0xM
		b2NhdGlvbkNvZGluZ0tleVRpbWVzdGFtcF8QJmtDTExvY2F0aW9uQ29kaW5n
		S2V5SG9yaXpvbnRhbEFjY3VyYWN5XxAca0NMTG9jYXRpb25Db2RpbmdLZXlM
		aWZlc3Bhbl8QJ2tDTExvY2F0aW9uQ29kaW5nS2V5Q29vcmRpbmF0ZUxvbmdp
		dHVkZV8QGmtDTExvY2F0aW9uQ29kaW5nS2V5Q291cnNlXxAca0NMTG9jYXRp
		b25Db2RpbmdLZXlBbHRpdHVkZV8QGWtDTExvY2F0aW9uQ29kaW5nS2V5U3Bl
		ZWRWJGNsYXNzXxAYa0NMTG9jYXRpb25Db2RpbmdLZXlUeXBlI0BCqumnIxoL
		I7/wAAAAAAAAI0Gy071pTxqgI0AUAAAAAAAAI0A+AAAAAAAAI8BeghVFzOjR
		I0BmgAAAAAAAIwAAAAAAAAAAI0AAPXCj1wo9gAIQAdIiIyQnWCRjbGFzc2Vz
		WiRjbGFzc25hbWWiJSZaQ0xMb2NhdGlvblhOU09iamVjdFpDTExvY2F0aW9u
		EgABhqBfEA9OU0tleWVkQXJjaGl2ZXIACAARABYAHwAoADIANQA6ADwAQABG
		AF0AhgCtAM0A9gEVAT8BXAF7AZcBngG5AcIBywHUAd0B5gHvAfgCAQIKAgwC
		DgITAhwCJwIqAjUCPgJJAk4AAAAAAAACAQAAAAAAAAAqAAAAAAAAAAAAAAAA
		AAACYA==
		</data>
     
          SNIP!.......<MUCH MORE DATA>.......

	</array>
	<key>Options</key>
	<dict>
		<key>LocationDeliveryBehavior</key>
		<real>0.0</real>
		<key>LocationRepeatBehavior</key>
		<real>2</real>
	</dict>
</dict>
</plist>

From this we can see that the plist contains a ‘Locations’ array with what looks to be Base 64 encoded entries. Many Base 64 decoders returned messy results, it eventually turned out that this is because the result is not text but a binary file (I finally had success using this decoder)

When we turn the base 64 decoded binary output back into text we can see that the base 64 encoded entries are plists.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>$archiver</key>
	<string>NSKeyedArchiver</string>
	<key>$objects</key>
	<array>
		<string>$null</string>
		<dict>
			<key>$class</key>
			<dict>
				<key>CF$UID</key>
				<integer>2</integer>
			</dict>
			<key>kCLLocationCodingKeyAltitude</key>
			<real>0.0</real>
			<key>kCLLocationCodingKeyCoordinateLatitude</key>
			<real>37.761915389999999</real>
			<key>kCLLocationCodingKeyCoordinateLongitude</key>
			<real>-122.40588588999999</real>
			<key>kCLLocationCodingKeyCourse</key>
			<real>24.609999999999999</real>
			<key>kCLLocationCodingKeyHorizontalAccuracy</key>
			<real>5</real>
			<key>kCLLocationCodingKeyLifespan</key>
			<real>30</real>
			<key>kCLLocationCodingKeySpeed</key>
			<real>26.5</real>
			<key>kCLLocationCodingKeyTimestamp</key>
			<real>315868704.34100002</real>
			<key>kCLLocationCodingKeyType</key>
			<integer>1</integer>
			<key>kCLLocationCodingKeyVerticalAccuracy</key>
			<real>-1</real>
		</dict>
		<dict>
			<key>$classes</key>
			<array>
				<string>CLLocation</string>
				<string>NSObject</string>
			</array>
			<key>$classname</key>
			<string>CLLocation</string>
		</dict>
	</array>
	<key>$top</key>
	<dict>
		<key>root</key>
		<dict>
			<key>CF$UID</key>
			<integer>1</integer>
		</dict>
	</dict>
	<key>$version</key>
	<integer>100000</integer>
</dict>
</plist>

These plists are much more interesting as they appears to be NSKeyArchived CLLocation objects each containing all the information for a single GPS location update.

Having found this out, I then manual wrote my own journey plist in the same format as Apple’s but containing my own base 64 encoded, NSArchived CLLocation objects. Once my file was placed alongside Apple’s in the folder mentioned earlier, sure enough my new entry appeared under ‘Location’ (I had to ‘Reset Content and Settings’ of the simulator before it appeared).

Screen Shot 2014-10-18 at 21.07.44

I then wrote a small desktop tool called ‘XJourney’ to automate this process, input a CSV of CLLocations and it will add these as a entry to your simulator, for more information on the XJourney tool or to download the app/or source code please see the XJourney Github page.

Screen Shot 2014-10-18 at 21.09.47

Happy GPS simulating!

Luke Hines

Luke is a husband, father and software engineer who enjoys spending his time in a multitude of technologies. If he's not writing software then he's probably on a motorcycle.

11 thoughts on “Adding your own GPS data to the iOS Simulator

  1. Hey, excellent article and app. Exactly what I was looking for. Is there a way/tool to generate the csv file?

    1. Hey Damian,
      Thanks.
      The data set I was working with was quite bespoke so unfortunately I did not write a generic tool for generating the CSV; however if you wanted to write such a tool the CSV headers in the ExampleData.csv should give you a clear idea of the structure. If you have any issues or need a hand, let me know!
      Cheers,
      Luke

  2. Great work Luke, this was amazingly useful for a few short days for me but it doesn’t work with the new version of Xcode (9) as the journey simulations have been moved to somewhere hidden :(

    1. Hey Julian,
      Thank you for the heads-up.
      I’ve taken a quick look at this and initially it looks to me like Xcode 9 is using ‘corelocation’ libraries from the local system and no longer uses ones packaged in the ‘xcode’ app bundle. It seems like this new location could be ‘/System/Library/Frameworks/CoreLocation.framework’. Once I have some time I will make sure this is the case with the hope of updating the app to support Xcode 9 (while maintaining support for the previous versions).
      Luke

  3. Hi Luke,

    This looks great and just what I need to test the app I am working on!
    I had a look at the source and seems simple enough to change the file location so I will look at that and re-compile.

    Can you please tell me what you used to create your ExampleData.csv, did you export this from gpx data or use something to generate it?

    Thanks for your help!

  4. sadly, now simulator is using these plist files under /System/Library/Frameworks/CoreLocation.
    this folder is protected by SIP, have to enter recovery mode for copying file into the framework folder.
    while, not sure if the os allows it, will it check the signature of the corelocation.framework…?
    why Applie is protecting this so much, they should simply allow browsing to a gps file… :-(

    1. Hi Dave,

      Thanks for responding, I managed to get everything working and generated the XJourney.plist file in the /System/Library/Frameworks/CoreLocation.framework/Support/SimulationScenarios.

      The issue I have now is that it doesn’t display it on the dropdown list in the from the Location menu in the simulator – any idea why that might be or an alternative way to select it somehow?

      Thanks a lot!

  5. Heya, love this post. I know it was a while ago now – but there are other recent comments!
    I created a quick and dirty iOS app to record coordinates, and was about to enter them into the right format for your app – but then thought I would google again. This article came up “http://www.pixeldock.com/blog/setting-a-custom-gps-location-in-the-ios-simulator-and-simulate-movement/” – seems that Apple have incorporated similar functionallity into xCode and you can add GPS coordinates to the project workspace.

    So just thought I would post the link to the other article here if others are interested. Hope thats okay.

Leave a Reply

Your email address will not be published. Required fields are marked *