Along the way I decided to add another widget so the name of the extension didn’t match anymore and I had to rename it.
First I feared that this could be as difficult as renaming a project in Xcode. But it turned out that I just had to replace all the occurrences of the old name.
For now I’ll keep the names of the entitlement files as they have been. This is a problem for future Dominik. :)
]]>Demo code doesn’t need to be good. If you are wondering, this is not how to write unit tests when you are not demonstrating. Try to only have one assertion in a test method.
I got several answers that it’s not practical to write a test for each small little detail one wants to test and that it’s better to test several things in one test if possible. I totally disagree. It might be OK to have several asserts in one test but this should be an exception. Especially in the case of the test code from the WWDC session, I would rather have several tests.
This is the code that was shown in the video:
func testExtractEventCount () throws {
let providerClass = ServerBackedEventProvider.self
// Simple cases
XCTAssertEqual(providerClass.extractEventCount(from: "0 records"), 0)
XCTAssertEqual(providerClass.extractEventCount(from: "1 record"), 1)
XCTAssertEqual(providerClass.extractEventCount(from: " 1 record(s)"), 1)
XCTAssertEqual(providerClass.extractEventCount(from: "25 records"), 25)
XCTAssertEqual(providerClass.extractEventCount(from:"50 records"), 50)
// Cases we expect parsing to return nil
XCTAssertNil(providerClass.extractEventCount(from: "NaN records") )
XCTAssertNil(providerClass.extractEventCount(from: ""))
XCTAssertNil(providerClass.extractEventCount(from: "jUnKdAtA"))
}
In the demo code is a bug and therefore this test fails in the line extracting from “0 records” and from “50 records”. With the information from this test, the engineer can fix the bug quickly.
But if this test would fail on Xcode Cloud, the engineer would only see the following:
testExtractEventCount(): XCTAssertEqual failed: ("nil") is not equal to ("Optional(0)")
Not really helpful in my opinion. Critical information is missing:
I would split the test assertions into several tests. Testing for the extraction from “0 records” would then look like this:
func test_extractEventCount_whenInputIs0Records_shouldExtractO() throws {
// given
let sut = SeverBackedEventProvider.self
// when
let result = sut.extractEventCount(from: "0 records")
// then
XCTAssertEqual(result, 0)
}
In case of a failure of this test we would see in the test result the following:
text_extractEventCount_whenInputIs0Records_shouldExtractO(): XCTAssertEqual failed: ("nil") is not
equal to ("Optional(0)")
Without looking at the test code, I already know what exactly failed. Tests should help my future self and my coworkers to find the reason for the failure as quick as possible. This is the main feature a test should have. Personally I find the failure message of the second test way better and looking at the test I do better understand its purpose and why it was written.
What do you think? Which of these tests is better? Let me know on Twitter.
]]>⌘ J
to move focus between editors.
Let me know what you think about this blog post on Twitter: @dasdom.
]]>For a simple to-do app, the view debugger looks like this:
On the left side the View Debugger presents the view hierachy of the currently visible view. It shows all the views you defined but also the views (and view controllers) iOS created for you. See for example the UILayoutContainerView
or the _UISystemBackgroundView
. This representation of the view hierachy gives you a fantastic overview of what is going on in the visible screen. You can use this presentation to confirm that the user interface elements are added to the expected super views.
When you select a user interface element in the editor in the middle, Xcode shows you the properties and configurations of that element in the inspectors on the right. The object inspector shows the following sections:
The selected object is an UILabel and you also get its address in memeroy. The address is useful if you want to change its properties or inspect it further using lldb.
In the Label section Xcode tells you the values of all the properties of a UILabel. For example you see the text an the text color and how many lines the label can show. In this case it is configured to show as many lines as are needed to present the set text (Lines 0).
In the View section Xcode tells you all the view properties of the label. For example you can read there if the user interaction is enabled and if the label registers multi-touch.
Note the last sub-section called Description. Shown here is the description string of that UI element. Some information, like added gesture recognizers, are only shown here.
The Hierachy section tells you the inheritence tree of the UI element.
The size inspector shows you the frame and bounds and the contstraints of the selected element. These informations are often my first stop to figure out the root of a layout problem.
Let’s assume, there is this app, you are working on.
The text is not shown as you planed. It looks like the labels in the table view cells are shifted to the left. As this is your first project without a Storyboard, you are not sure what is going on. Maybe the constraints are wrong.
You start the view debugger. To check if the labels are in fact shifted to the right, you activate Show Clipped Content
.
This setting tells the view debugger to render the parts of the views that are clipped by their super views or by the screen frame. The result looks like this:
You can see that the label are indeed shifted to the left but it’s hard to see. Fortunately Xcode has you covered. You can Change canvas background color
.
Better! But how is the label added to the cell? Is it put directly onto the content view or is it added to a host view? To figure that out, you can Orient to 3D
by clicking the button with the cube or by dragging the view with the mouse pointer.
The view debugger then shows you the views from an angle and changes z distance between the views to make the hierarchy clearer. The slider on the left lets you change the z distance further.
Better. But you still can’t clearly see what is going on. You’d like to see the constraints for the seleced view in the view debugger editor. To do that, you select the button Show constraints
button.
Then you select one of the labels with the problem and inspect the constraints.
With all the other elements in the way, it’s hard to see what the problem might be. So you narrow down the shown views to the important portion using the range slider on the right. After you have zoomed in and changed the perspective a bit, you can clearly see what is going on with this constraint.
OK, it looks like the constant of the leading constraint of that label is wrong. But you are not that experienced and want to ask your co-worker. So you export the view hierarchy as it is shown in the view debugger with the menu item
You send the resulting file to your co-worker and she can open it on her Mac without even having the project stored on her Mac.
This way, your co-worker can confirm your results and you can finally fix the bug.
The View Debugger is a valuable tool to figure out bugs and problems in the user interfaces of your apps. Even if there is no problem, you can us it to get insight into how the view hierarchy is constructed.
Let me know what you think about this feature and this blog post on Twitter: @dasdom.
]]>The simulator already allows to change the font size and switch between dark and light mode in the settings app on the running simulator. But there is even a faster way.
When the app is running on the simulator and the debugger is attached to it, Xcode shows the debug bar. The icon with the settings handles opens the Environment Overrides pop up.
In the pop up window you can
This way you can easily check if you app works and looks good for the most important environment variables.
For example, the formulary app I’m working on right now supports Dynamic Type and dark mode. With a few click I can change the environment for my app to this:
The result looks like this:
]]>When you place the cursor in the declaration of a class, the action menu looks like this:
You can find most of these features somewhere else in the menus of Xcode but the action menu is often faster. After activating the menu with the shortcut ⇧⌘A you can use the arrow keys to select the feature you need. Or you can start typing to filter the shown features. This way you need to move you hand to the mouse or the touchpad less often.
In the case of a method, the action menu looks like this:
As I’m often working with code I didn’t wrote, finding the callers of a method is extremely useful. This way I can understand the importance of the method and if I can safely change it to implement new features.
As Apple puts a lot of work into SwiftUI, it’s not surprising that the action menu is packed with features when used in a SwiftUI view:
Here is a nice little trick I have seen in a WWDC session video: If you want to embed some code into a scroll view (or something else that’s not in the context menu), select ‘Embed in HStack’ and change the type to what you need.
]]>You could read all that information and try to figure out, which view has the problematic layout constraint. But there is an easier way. In the last line Xcode tells you, which constraint it chose to break.
When you see this message, you should copy the id of the view (0x132ee28c0
in this case) and start the View Debugger in Xcode.
Next, paste the id into the filter field at the lower left and press return.
Xcode shows you exactly which view has the problematic layout constraint.
This should help to figure out what’s going on and how you can fix it.
I read about developers who believe that those issues are normal and can’t be fixed. This is wrong. Fix those issues when you see them.
]]>Don’t live with broken windows.
To learn more about how to make your apps accessible for all users, start with the relevant WWDC videos:
WWDC videos about accessibility
First of all: Think about accessibility right from the beginning. Make it a habit to ask already in the design process how the app will be made accessible for all users. Tell the designers that it’s considered bad design on iOS to use fixed font sizes. Supporting dynamic type should be considered the minimum for an app. (Unfortunately you will hear the question “But how many users will that effect?”. Asking that question is wrong. But nevertheless, here is an answer: This can effect everyone. A friend of mine once had an eyes treatment and could not see properly for a few weeks. He could still use apps that supported dynamic type.)
This post is not about how to make an app accessible. There are plenty of resources in the internet about that. I’m sure you will find the perfect blog post for you. This issue is about how Xcode can help you making your app accessible for all users.
Xcode comes with the app Accessibility Inspector. You can open the app from the Xcode menu in Xcode in the “Open Developer Tool” section.
If it doesn’t work: Sometimes the main window of the Accessibility Inspector does not open. Even quitting and reopening the Accessibility Inspector does not help. What helps in these situations for me is the following.
In the toolbar at the top of the window you can choose the target you want to investigate. (I’m an iOS developer so all my examples are about iOS development. But I guess this works equally well for Mac apps.)
First you should switch to the Audit section my clicking the audit button. Unfortunately there is no shortcut for that action. The button ‘Run Audit’ checks the current screen of the running app for potential accessibility problems. For an app I’m working on right now this looks like this.
(I know, I have to work on the accessibility of that app.) When you select a reported issue, the Accessibility Inspector shows you a preview of that element at the bottom of the window. In this example, the formula is provided as pdf image without an accessibility label. VoiceOver users would not ‘see’ this element. VoiceOver ignores it all together.
When you click the eye button on the right, the Accessibility Inspector shows a screenshot of that view and marks the problematic element.
The left button shows the Inspection section. On the top right is the inspection point button. When you activate that button, you can hover over the user interface of your app and inspect what VoiceOver can figure out about the elements in the user interface.
If you additionally select the speech bubble button, the element is read out like VoiceOver will do. This way you can figure out if all the elements in the user interface are accessible by all users.
But there is more. Using the string next to ‘Label’ in the Basic section, you can write UI tests to figure out if your app works as expected. Or you can use UI tests to automatically create the App Store screenshots before you submit a new version.
With ‘Window / Show Color Contrast Calculator’ you can open a tool that helps to figure out if the contrast between the foreground and the background elements is OK. This is not limited to app development. You should also use it when you create websites of prepare talk at conferences.
The tool is very simple. You provide a background and a foreground color and you and it tell you if the contrast is OK for all font sizes.
]]>The library window hosts different kind of libraries: the Views library, the Modifiers library, the Snippets library, the Media library and the Color library. The Snippet library is the one with the icon showing curly braces.
Xcode comes with many preinstalled snippets for different tasks. You should take a few minutes and scroll through the library and see what could be useful for you. Probably you’ll find snippets there you usually search on the internet. But the real power comes from the snippets you add to the library. To add a snippet, select the code you’d like to transform into a snippet and ⌃click to open the context menu. Select ‘Create Code Snippet’.
Xcode opens the snippet editor with the selected code.
You type in a name and a summary for that snippet to help your future self to figure out what the snippet is for.
To change parts of the code into tokes, put them between <#
and #>
.
When using that snippet in code completion, you can jump to these tokes with the tabulator key. To tell Xcode that you would like to see this snippet in code completion while you are typing in the code editor, add an abbreviation in the ‘Completion’ field. To make the snippet as useful as possible define the ‘Availability’ as specific as possible.
The snippets are stored as .codesnippet-files in the directory ‘~/Library/Developer/Xcode/UserData/CodeSnippets/’. This means you can easily share the snippets you created with other developers. You can even put that directory under version control and use a service like Github or Gitlab to install your snippets on all computers you use.
Follow me on Twitter for more content about iOS development.
]]>In iOS, events (for example touch events) are delivered using the responder chain. The responder chain consists of responder objects. If you have a look at the documentation, you may have noticed that UIView and UIViewController are responder objects. This means they inherit from UIResponder:
When the user taps a view in the view hierarchy, iOS uses hit testing to figure out which responder object should get the touch event first. The process starts at the lowest level, the window. Then is propagates up the view hierarchy and checks for each view if the touch happened within its bounds. The last view in that process that got hit, receives the touch event first. If that view does not respond to the touch event, the event is passed to the next responder in the responder chain. When a view tells iOS that it did not get hit, the subviews of that view aren’t checked.
This has an interesting consequence. When a button is outside of the bounds of its superview but visible because clipsToBounds of the superview is set to false, it does not receive any touch events. So, when ever a button doesn’t work, remember to check if it is in the bound of its superview.
The target-action mechanism can be set up that is also uses the responder chain by setting the target to nil. Then iOS asks the first responder if it handles the action. If not the first responder passes the action to the next responder.
Since Swift 2.2 you set up the action of a button using #selector()
.
The compiler checks if the method within the parentheses is implemented.
But when using the responder chain, you can’t tell if some class in the chain implements this method.
Here is how you can use #selector()
and still send the events to the responder chain.
First we need a protocol for the method that should be executed.
@objc protocol DetailShowable {
@objc func showDetail()
}
Then we can add an extension to Selector
as described in this awesome post by Andyy Hope that looks like this:
private extension Selector {
static let showDetail = #selector(DetailShowable.showDetail)
}
Adding the action to the responder chain is then as easy as this:
button.addTarget(nil,
action: .showDetail,
for: .touchUpInside)
Then some responder object in the responder chain needs to conform to the DetailShowable
protocol.
You can find the code on Github.
]]>