PIXEL
DOCK

I like the smell of Swift in the morning…

How to use your app’s Localizable.strings in your Xcode UITests

Posted: | Author: | Filed under: iOS, UITests, Xcode | Tags: , | No Comments »

When your are writing UITests for a multi language app you need access to your Localizable.strings. For example when you need to press a “Save” Button in your test. Because your app is localized the text “Save” is part of your Localizable.strings file.

In your app you set the title of your “Save” Button like this:

saveButton.setTitle(NSLocalizedString("Button.Save", comment: ""), for: .normal)

When you want to tap that button in a UITest you need to know the title of the button:

XCUIApplication().buttons["Save"].tap()

You could use the quick and dirty approach by just using “Save”. But that will break your test when the button’s title is changed in your Localizable.strings file. And it feels dirty, too 😉

The right way to do this is to retrieve the button title from your Localizable.strings file. To make that work you have to do two things:

1. Add your Localizable.strings file to your UITest target

2. Access the file via the UITest bundle (I use a little helper method for that):

func localized(_ key: String) -> String {
    let uiTestBundle = Bundle(for: AClassFromYourUITests.self)
    return NSLocalizedString(key, bundle: uiTestBundle, comment: "")
}

You can use any class from your UITests to access the UITest bundle. When you are not explicitly asking to use the UITest bundle when initializing the NSLocalizedString the default value Bundle.main will be used. Using Bundle.main does not work when running UITests because it gives you the bundle of the UITest Runner App instead of the UITest bundle.

Voilà! You can now use your localized strings in your UITests:

XCUIApplication().buttons[localized("Button.Save")].tap()

Xcode UITests: How to check if a UISwitch is on

Posted: | Author: | Filed under: Swift, UITests, Xcode | No Comments »

A simple scenario: You are writing a UITest that should check if a UISwitch is switched on.

In a UnitTest you would simply do this:

XCTAssert(yourSwitch.isOn)

When you are running a UITest you cannot do that, because during a running UITest you cannot access the UISwitch directly. Xcode only gives you access to your app’s UI elements via the XCUIElement class. That class is used for all accessible UI elements so it does not have a isOn property like UISwitch has.

So how do you test that the UISwitch is on?

It’s actually pretty easy, but not really obvious. XCUIElement conforms to XCUIElementAttributes which gives you access to a value property of type Any?

So, as value can literally be Any(thing) I tried to cast it to a Bool, because isOn also is a Bool, right?

Not working! Turns out that when you access a UISwitch in your UITest its value property is of type NSTaggedPointerString. Which is a subclass of NSString and can be cast to String (when you are using Swift). So the Bool isOn value has been mapped to a “0” or “1” String.

To test if the first UISwitch in your current view has been switched on, you can do this in a UITest:

let firstSwitch = app.switches.element(boundBy: 0)
XCTAssertEqual(firstSwitch.value as! String, "1")

Permanently simulate location in the iOS Simulator

Posted: | Author: | Filed under: Xcode | Tags: , | No Comments »

In an older blog post I described how to simulate a custom location in the Simulator by using a GPX file. During my current project I have to use simulated locations a lot. But every time I ran my app in the simulator Xcode deactivated the location simulation. So I had to manually select it again with every start of the app. Really annoying.

As it turns out there is way to permanently enable location simulation in the Simulator. It’s just a bit hidden. To always enable location simulation open Edit Scheme for the scheme you are running and then under Run > Options select one of the available locations as Default Location because otherwise the default is None and no location is simulated.

Bildschirmfoto 2017-01-14 um 22.03.33

Voilá, once you select a default location for your scheme the Simulator keeps simulating the location everytime you start it.

Setting a custom GPS location in the iOS Simulator and simulate movement

Posted: | Author: | Filed under: iOS, Xcode | Tags: , , | 3 Comments »

Xcode has a neat built in feature that let’s you simulate a (GPS) location in the iOS Simulator. To switch it on you just need to run your app in the Simulator and then click on the location arrow on top of the debug area. Xcode offers a handful of default locations from Moscow to Honolulu that you can use if you just need any location.

Cool!

But sometimes you need to simulate a certain location that is not part of that default set. For example, let’s say you are developing an app that shows all the cool bars in your hometown. Then being able to simulate the location “Honolulu” won’t be of much use to you (unless your hometown in Honolulu).

Luckily Xcode offers the possibility to add a GPX file to your project that defines the location you would like to simulate. GPX is a XML Format for GPS data. It has a large set of datatypes but to simulate a location we only need the wpt (waypoint) datatype:

To add your custom location to the Simulator you have to add a GPX file to your project. Xcode offers this file type in its New File dialog (File -> New -> File… -> Resources -> GPX File) but it is just a plain XML file so you can create it somewhere else and just add it to your project (just make sure it has a “.gpx” file ending).

So to simulate the location add the following GPX file to your project:

<?xml version="1.0"?>
<gpx version="1.1" creator="Xcode">
    <wpt lat="53.552225" lon="9.935171"></wpt>
</gpx>

Now if you run your app in the Simulator and click on the location arrow on top of the debug area, you will find your custom location on top of the default locations (Xcode displays the file name of your GPX file as the name of your custom location). Select it and the Simulator pretends to be at that location.

Very cool!

But it gets better! What if a static location is still not enough for you? Say, you want to add a feature to your “Best Bars in my Hometown”-App that allows the user to track his way home after his visit to the bar (So he can see the next day how he got home in case he can’t remember…). To test that feature you would have to make the Simulator pretend to be moving from one location to another.

This is where a second GPX datatype comes into play: the time data type. When you add a time element to a waypoint element and you have multiple waypoint elements Xcode simulates a movement between those waypoints:

<?xml version="1.0"?>
<gpx version="1.1" creator="Xcode">
    <wpt lat="53.550904" lon="9.969947">
        <time>2016-01-01T00:00:00Z</time>
    </wpt>
    <wpt lat="53.552300" lon="9.967045">
        <time>2016-01-01T00:01:00Z</time>
    </wpt>
    <wpt lat="53.554168" lon="9.968848">
        <time>2016-01-01T00:02:00Z</time>
    </wpt>
</gpx>

When you select this GPX file from the list of locations Xcode starts to simulate a movement from the first waypoint to the second waypoint and then to the third.

The speed of this movement is determined by the value of the time elements. The absolute time does not matter. Xcode only uses the time difference between the waypoints to calculate a speed. So in my example it takes the Simulator 1 minute to “move” from the first waypoint to the second waypoint and then another minute before it reaches the third waypoint.

Really cool!

Localizable.strings error: “The data couldn’t be read because it isn’t in the correct format”

Posted: | Author: | Filed under: Objective-C, Swift, Xcode | 3 Comments »

I added some new keys to my Localized.strings files:

"HOME_TEAM" = "Home team"
"AWAY_TEAM" = "Away team"

Then I got this error from Xcode:

Read failed: The data couldn't be read because it isn't in the correct format

It took me a minute but when I found what caused the error I had to smile. I simply forgot the semicolons at the end of each line:

"HOME_TEAM" = "Home team";
"AWAY_TEAM" = "Away team";

Those poor semicolons! Coding in Swift really makes you forget they even existed in the first place 😉

Could not launch “APP_NAME”, process launch failed: Security

Posted: | Author: | Filed under: Xcode | Tags: , | No Comments »

After switching to Xcode 6 and the iOS8 SDK I was confronted with a strange error message when trying to test my app on a device. The app was built and deployed to the device but Xcode did not run it. Instead it showed an alert with the following error message:

Could not launch "APP_NAME", process launch failed: Security

The only thing you can do is to press “OK”, but the app still does not run. When you try to run it again from Xcode you just get the same alert.

The solution to this problem is quite simple:

  1. Run the App from Xcode
  2. When the alert shows, press “OK”
  3. Open the app on the device
  4. An alert will be shown, asking you if you want to trust this app. Press “Trust”.

Now you should be able to run the app from Xcode.

Code Sign Error: Provisioning Profile can’t be found.

Posted: | Author: | Filed under: iOS, Xcode | Tags: | 5 Comments »

Sometimes when you transfer your project to another computer and especially when you create a new developer certificate in the progress, you’ll run into difficulties. When you try to run your app on a device, Xcode will throw the following error:

Code Sign Error: Provisioning Profile '###ID###' can't be found.

The problem is that in your project’s project.pbxproj file the old provisioning profile identifiers are still lingering. You need to clean up that file so that Xcode can build your project without errors.

Here’s how you do that:

1. Close Xcode
2. Right click on your project’s .xcodeproj bundle to show it’s contents.
3. Open the .pbxproj file in a text editor of your choice (make a backup copy first if you feel paranoid)
4. Find all lines in that file that include the word PROVISIONING_PROFILE and delete them.
5. Open Xcode and clean your project
6. Build your project

80F04C2F14BDF2CD00851339 /* Ad Hoc */ = {
   isa = XCBuildConfiguration;
   buildSettings = {
      CODE_SIGN_IDENTITY = "iPhone Distribution: Caramelized GmbH";
      "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
      GCC_PRECOMPILE_PREFIX_HEADER = YES;
      GCC_PREFIX_HEADER = "CaramelReader/Caramelized-Prefix.pch";
      INFOPLIST_FILE = "CaramelReader/Caramelized-Info.plist";
      IPHONEOS_DEPLOYMENT_TARGET = 5.1;
      PRODUCT_NAME = Caramelized;
      ↓ DELETE THIS LINE! ↓
      PROVISIONING_PROFILE = "8B3033A4-0E28-42F8-80FC-70B6C3C473C7"; 
      ↑ DELETE THIS LINE! ↑
      WRAPPER_EXTENSION = app;
   };
   name = "Ad Hoc";
};

Embed Javascript files into your iOS App

Posted: | Author: | Filed under: iOS, Xcode | Tags: , | 1 Comment »

When i tried to compile a Javascript file into my iOS App I ran into a strange problem. The javascript files just were not embedded. Whenever I tried to access them using this method:

[[NSBundle mainBundle] pathForResource:@"script.js" ofType:nil];

The method always returned nil. Somehow the javascript file just did not want to be embedded.

At first I could not find out what I was doing wrong, because embedding resources into an App is really easy. Just drag them into your resources folder and you’re done, right? Well, for most file types that’s true. However, with Javascript files things are a bit different:

When compiling the App the compiler treats Javascript files as script files. Meaning that it tries to interpret them instead of just including them into the App bundle uninterpreted.

Here’s how to make the compiler turn a blind eye on the javascript files and just include them into the App bundle without interpreting them:

1. In Xcode’s project navigator (left column) klick on the blue project icon.
2. Select the “Build Phases” tab in the main window
3. Open the “Compile Sources” section
4. Find your Javascript file in the list and remove it.
5. Open the “Copy Bundle Resources” section
6. Add your Javascript file to the list

And that’s it. Now the compiler treats your Javascript file like any other resource file and copies it into the App Bundle.

UPDATE:
Xcode 4.5 now creates a warning when you just add a javascript file to your project without removing it from the “Compile Sources” section:

warning: no rule to process file '*FILEPATH*' of type 
sourcecode.javascript for architecture armv7

So the problem is easier to spot now. You still have to fix it manually as I described above.

CoreData error: “The model used to open the store is incompatible with the one used to create the store”

Posted: | Author: | Filed under: iOS, Xcode | Tags: | 4 Comments »

When I started to play around with CoreData for the first time, I came across a strange error. The error description said “The model used to open the store is incompatible with the one used to create the store”. At first I just couldn’t figure out what the problem was. As soon as I tried to access the NSManagedObjectContext the app crashed.

The solution is really simple: Whenever you change something your CoreData data model, you have to delete the old compiled app from the simulator or device. CoreData uses SQLite3 which is nothing more than a file in your app’s filesystem. If you do not delete the old version of your app the file remains in the file system and does not reflect the changes that you made in the data model. The SQLite file will only be deleted when you delete the whole app.

So the golden rule is: Whenever you change something in the data model of your CoreData driven App, make sure to delete the old version of the app before compiling and running the new one.

Build an ad hoc distribution .ipa file for a project that includes static libraries (e.g. Open Ears)

Posted: | Author: | Filed under: iOS, Xcode | Tags: | No Comments »

When I wanted to generate an .ipa file for my latest project I ran into the following problem: my project uses the Open Ears library for speech recognition. Open Ears is included as a static library. When I tried to archive the app to share it with others I could not create the .ipa file. After clicking on “share” the option to save it as an .ipa file was grayed out with the message “No Packager exists for the type of archive”. So no chance to build the necessary .ipa file that is needed for ad hoc distribution.

The solution for this problem is quite simple but it took me some time to find it. The problem is that you have to make sure that the static libraries are included in the application binary. You achieve this by setting the “Skip Install” build setting to “YES” for the libraries: Right click on the library project icon (the blue one) and find the “Skip Install” property in the “Deployment category” in the build settings. Set it to “YES”.

Important: leave the “Skip Install” property of your main project at “NO”.

When you archive your project now, it should offer the save as .ipa option in the share section.