Developing on Staxmanade

Debugging iOS AutoLayout Issues

(Comments)

This tip may not be news to you, but it was to me so I'll put this up here to help you, but more to help myself when I get stuck down the road and forget how to do this.

Background

I just stumbled upon a way to help when debugging and diagnosing issues around ambiguous auto layout constraints (you know the kind where iOS just picks the one it feels like to remove?).

Today I was researching why I was receiving the following output in my debugger console:

Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints)
(
    "<NSLayoutConstraint:0x7fc82d3e18a0 H:[UIView:0x7fc82aba1210(768)]>",
    "<NSLayoutConstraint:0x7fc82d6369e0 H:[UIView:0x7fc82aba1210]-(0)-|   (Names: '|':UIView:0x7fc82d6b9f80 )>",
    "<NSLayoutConstraint:0x7fc82d636a30 H:|-(0)-[UIView:0x7fc82aba1210]   (Names: '|':UIView:0x7fc82d6b9f80 )>",
    "<NSLayoutConstraint:0x7fc82d3e7fd0 'UIView-Encapsulated-Layout-Width' H:[UIView:0x7fc82d6b9f80(50)]>"
)

Will attempt to recover by breaking constraint
<NSLayoutConstraint:0x7fc82d3e18a0 H:[UIView:0x7fc82aba1210(768)]>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

Now, I can tell from the above that theres an issue with the height constraint(s), but looking at this, I have no idea what view is being referenced which makes it quite difficult to jump to XCode and inspect the view's constraints.

But I worked through it, and learned the following along the way. Hope this helps you.

How to set auto layout breakpoint in Xcode debugger

Using this text Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger. I found this great post on stack overflow on How to trap on UIViewAlertForUnsatisfiableConstraints but have copied below for reference.

You'll want to add a Symbolic Breakpoint. Apple provides an excellent guide on how to do this.

  1. Open the Breakpoint Navigator cmd+7
  2. Click the Add button in the lower left
  3. Select Add Symbolic Breakpoint...
  4. Where it says Symbol just type in UIViewAlertForUnsatisfiableConstraints

You can also treat it like any other breakpoint, turning it on and off, adding actions, or log messages.

But that's only the first step. Once you've run the app and hit the breakpoint, you're left staring at assembly code and memory addresses, for example:

UIKit`UIViewAlertForUnsatisfiableConstraints:
->  0x1131cc4a2 <+0>:   pushq  %rbp
    0x1131cc4a3 <+1>:   movq   %rsp, %rbp
    0x1131cc4a6 <+4>:   pushq  %r15
    0x1131cc4a8 <+6>:   pushq  %r14
    0x1131cc4aa <+8>:   pushq  %rbx
    0x1131cc4ab <+9>:   pushq  %rax
    0x1131cc4ac <+10>:  movq   %rsi, %r14
    0x1131cc4af <+13>:  movq   %rdi, %r15
    0x1131cc4b2 <+16>:  cmpq   $-0x1, 0x778796(%rip)     ; _UIConstraintBasedLayoutPlaySoundOnUnsatisfiable.result + 7
    0x1131cc4ba <+24>:  jne    0x1131cc529               ; <+135>
    0x1131cc4bc <+26>:  cmpb   $0x0, 0x778785(%rip)      ; _UIConstraintBasedLayoutPlaySoundWhenEngaged.__once + 7
    0x1131cc4c3 <+33>:  je     0x1131cc4ed               ; <+75>
    0x1131cc4c5 <+35>:  movq   0x73cd8c(%rip), %rdi      ; (void *)0x0000000113925248: UIDevice
    0x1131cc4cc <+42>:  movq   0x7108e5(%rip), %rsi      ; "currentDevice"
    0x1131cc4d3 <+49>:  movq   0x4d7aee(%rip), %rbx      ; (void *)0x0000000114598000: objc_msgSend
    0x1131cc4da <+56>:  callq  *%rbx
    0x1131cc4dc <+58>:  movq   0x71e375(%rip), %rsi      ; "_playSystemSound:"
    0x1131cc4e3 <+65>:  movl   $0x3ee, %edx
    0x1131cc4e8 <+70>:  movq   %rax, %rdi
    0x1131cc4eb <+73>:  callq  *%rbx
    0x1131cc4ed <+75>:  cmpq   $-0x1, 0x77877b(%rip)     ; _UIConstraintBasedLayoutLogUnsatisfiable.result + 7
    0x1131cc4f5 <+83>:  jne    0x1131cc541               ; <+159>
    0x1131cc4f7 <+85>:  cmpb   $0x0, 0x77876a(%rip)      ; _UIConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints.__once + 7
    0x1131cc4fe <+92>:  je     0x1131cc51e               ; <+124>
    0x1131cc500 <+94>:  leaq   0x53ba89(%rip), %rdi      ; @"Unable to simultaneously satisfy constraints.\n\tProbably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) \n%@\n\nWill attempt to recover by breaking constraint \n%@\n\nMake a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.\nThe methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful."
    0x1131cc507 <+101>: xorl   %eax, %eax
    0x1131cc509 <+103>: movq   %r14, %rsi
    0x1131cc50c <+106>: movq   %r15, %rdx
    0x1131cc50f <+109>: addq   $0x8, %rsp
    0x1131cc513 <+113>: popq   %rbx
    0x1131cc514 <+114>: popq   %r14
    0x1131cc516 <+116>: popq   %r15
    0x1131cc518 <+118>: popq   %rbp
    0x1131cc519 <+119>: jmp    0x11331930c               ; symbol stub for: NSLog
    0x1131cc51e <+124>: addq   $0x8, %rsp
    0x1131cc522 <+128>: popq   %rbx
    0x1131cc523 <+129>: popq   %r14
    0x1131cc525 <+131>: popq   %r15
    0x1131cc527 <+133>: popq   %rbp
    0x1131cc528 <+134>: retq
    0x1131cc529 <+135>: leaq   0x778720(%rip), %rdi      ; _UIConstraintBasedLayoutPlaySoundOnUnsatisfiable.__once
    0x1131cc530 <+142>: leaq   0x4fdb59(%rip), %rsi      ; __block_literal_global68
    0x1131cc537 <+149>: callq  0x11331a2a8               ; symbol stub for: dispatch_once
    0x1131cc53c <+154>: jmp    0x1131cc4bc               ; <+26>
    0x1131cc541 <+159>: leaq   0x778728(%rip), %rdi      ; _UIConstraintBasedLayoutLogUnsatisfiable.__once
    0x1131cc548 <+166>: leaq   0x4fdbc1(%rip), %rsi      ; __block_literal_global76
    0x1131cc54f <+173>: callq  0x11331a2a8               ; symbol stub for: dispatch_once
    0x1131cc554 <+178>: jmp    0x1131cc4f7               ; <+85>

What next?

We can use the Xcode debug console output to gather more information than we see it initially provides.

In the above example I'm going to take the following output:

Will attempt to recover by breaking constraint <NSLayoutConstraint:0x7fc82d3e18a0 H:[UIView:0x7fc82aba1210(768)]>

Pull out the UIView's memory address 0x7fd8fe59a440 and use the XCode console to dig deeper and try to get a better understanding.

Printing views from memory addresses

The memory address 0x7fd8fe59a440 is a pointer to a UIView. Since we know that a UIView has other selectors we can query, we can begin to use these to dig deeper into the object and get a better understanding of which part of our view have auto layout constraints that are not playing nicely.

First we try to look at just the UIView and see if that helps us get a grasp as to which view is causing our troubles.

po 0x7fc82aba1210

If this doesn't provide enough info as the below sample output shows (not very helpful).

(lldb) po 0x7fc82aba1210
<UIView: 0x7fc82aba1210; frame = (0 0; 768 359); autoresize = RM+BM; layer = <CALayer: 0x7fc82d338960>>

Maybe printing it's recursiveDescription will help out and in my case gives me a better idea of which component I'm actually trying to look at.

(lldb) po [0x7fc82aba1210 recursiveDescription]
<UIView: 0x7fc82aba1210; frame = (0 0; 768 359); autoresize = RM+BM; layer = <CALayer: 0x7fc82d338960>>
   | <MYAPPButton: 0x7fc82d61c800; baseClass = UIButton; frame = (0 0; 768 359); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x7fc82ab96570>>
   |    | <UIImageView: 0x7fc82d3f7b10; frame = (0 0; 0 0); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x7fc82d6e7020>>
   | <UIImageView: 0x7fc82d54a1c0; frame = (314 110; 140 140); autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x7fc82d52def0>>

Or if that's not enough context, then we can look at the view's superview and potentially even walk up the view's tree, printing out a different levels trying to understand which component is having trouble with auto layout.

po [[0x7fc82aba1210 superview] recursiveDescription]

Once you've narrowed down which view inside which component is causing you trouble, now it's up to digging and finding the problematic constraint...

Fix it

This part I can't add much color to help you, except to start by looking in your storyboard, xib or code wherever the constraints are being added to the particular view we diagnosed above.

Hopefully this walk-through helps at least a little in diagnosing your auto layout constraint issues.

Happy debugging!

Fail Fast With iOS Images

(Comments)

Problem: iOS app has unwieldy, unorganized and poorly named images

I've been working on a team that took over a good sized legacy iOS project where it's usage of images became a little unwieldy. Between the various app clones (targets), shared images, and images that aren't even used we'd like to reorganize, rename and just clean up the app in general.

But, one thing that makes me nervous is how easy it is to rename an image, forget or miss a magic-string that tries to load that image and now we've introduced a bug that may be difficult to diagnose or even discover that we introduced it.

Let's fail fast?

Failing fast in this context means, how can we, during development and testing time quickly fail when running the app to determine where an image attempting to be loaded does not.

Ideally we wouldn't even have these magic-strings to deal with. Using some tool to automatically generate a compile-time safe construct that we can use to load images. However until both A) I find the darn tool that I once-upon stumbled across that does this (please leave a comment to remind me if you find it before I do...) and B) we get sed tool integrated, I threw this little helper to quickly fail fast and help us find broken image references within our iOS applications.

How does it work?

It's a simple little Category that overrides the imageNamed: selector of UIImage and if the original cannot load an image (returns nil) then it will fail with a helpful assertion message.

This utility allowed us to quickly find images that were missing while doing our big clean sweep.

How to integrate?

It should be as simple as adding the .h & .m files to your project and their respective targets and you should be off to the races.

For DEBUG mode only...

If you look closely you'll notice #if DEBUG wraps the implementation so when we ship to the app store we don't end up causing a crash in production if a missing image sneaks by.

Happy Missing Image Bashing!

How to install clang-format and formatting Objective-C files

(Comments)

Formatting your code so it follows whatever conventions your team/company/self define is important. As developers who have to read code on a regular basis, you have to train ourselves to mentally parse code and spot where bugs may be lurking.

However, if your code is not formatted in a consistent fashion, the cognitive load placed on your brain increases dramatically. Not only do you have to read, parse, and conceptualize the code you're reviewing, your brain is now having to decide if anomalies in the formatting of your project's source code are because of a problem in the code, or merely a difference in the code formatting or styling.

Easy and automatic code formatting tools were something I really missed when I started using Xcode because Visual Studio's built-in formatting of a file is as simple as CTRL+k+d.

This has definitely improved in recent years with the introduction of Alcatraz and ClangFormat-Xcode and I'd highly recommend you check them out.

But, what if you just acquired a legacy project, that was hacked on by quite a number of different developers, with different styles, that's a total mess (from a style consistency perspective)? Or what if you wanted to have an easy rake task that automatically formatted the necessary code files?

This is where a great little tool from the LLVM project clang-format comes in handy.

Unfortunately it's not as easy to install as brew install clang-format (It's now as easy to install as brew install clang-format), but I'll show you not only how to get it installed manually, but a command to easily format your code.

How to install clang-format manually

Thanks to this post for describing how to get (an older version of the tool), I put together the following steps that I hope you find useful.

  1. Go to the LLVM Download page
  2. In chrome dev tools (On my mac, ⌘+⌥+J aka CMD+Option+J, make sure theElementstab is selected, andCMD+Fwithin the html source formacosx-apple-darwin`.
  3. Copy that URL and place it into the following set of commands.

From a command prompt, navigate to a folder where you'd like to save or store the clang tools.

  1. Remove the older version if there was one, and create a folder to work in rm -rf ./clang-format/ && mkdir -p ./clang-format
  2. Take the previously discovered link from above and use the following to download it into our working folder. curl 'http://llvm.org/releases/3.5.0/clang+llvm-3.5.0-macosx-apple-darwin.tar.xz' -o './clang-format/clang-format.tar.xz'
  3. Extract the tar file tar xvfJ clang-format/clang-format.tar.xz -C ./clang-format
  4. Remove any previously sym-linked linked version you have rm -f ~/bin/clang-format
  5. Link the downloaded clang-format command into our ~/bin folder ln -s $(pwd)/$(find clang-format | grep bin/clang-format$) ~/bin/clang-format
  6. Test the command works clang-format --help

Here are all of the steps above as a script:

rm -rf ./clang-format/ && mkdir -p ./clang-format
curl 'http://llvm.org/releases/3.5.0/clang+llvm-3.5.0-macosx-apple-darwin.tar.xz' -o './clang-format/clang-format.tar.xz'
tar xvfJ clang-format/clang-format.tar.xz -C ./clang-format
rm -f ~/bin/clang-format
ln -s $(pwd)/$(find clang-format | grep bin/clang-format$) ~/bin/clang-format
clang-format --help

How to setup your project style guide

Now that you have the command line clang-format tool installed, you can walk through how to use it to format our Objective-C code files. Let's walk through some steps I used to apply a standard code format to the project.

But, before you use the command line tool to rip through our project, let's first set the standards you'd like clang-format to adhere to when formatting our code.

At the root of your project (probably where your .git folder is), create a file called .clang-format

The .clang-format file contains the formatting rules for a project. Its structure is YAML and is what the clang-format CLI can read to format your project's Objective-C files.

For details about the .clang-format file, you can check out the docs to get a list of all of the options possible in this file.

Here is one I have used before.

BasedOnStyle: Chromium

AlignTrailingComments: true
AllowShortIfStatementsOnASingleLine: false
BreakBeforeBraces: Attach
ColumnLimit: 0
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: true
Language: Cpp
MaxEmptyLinesToKeep: 4
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: false
PointerBindsToType: false
SpacesBeforeTrailingComments: 1
TabWidth: 4
UseTab: Never

If you want to ignore a folder from being touched, you can place a .clang-format in that folder with the following option set:

BasedOnStyle: None

Format all our files at once

Now that you have a .clang-format file which helps to define our project's styling conventions, you can begin our initial formatting sweep.

First let's create a command that gives us all the files you want to process.

Here's my initial example (executed in a zsh shell):

ls MyProject/*.[hm]

Tweak this however you need so that you get a list of files to format from your project. You may want to be careful to exclude a CocoaPods folder, or other third party libraries and once you are happy with it, you can then pipe its output to clang-format.

ls MyProject/*.[hm] | xargs clang-format -i -style=file

You can use the xargs command to execute the clang-format command for each file in the output ls.

clang-format options

In the above example you use the following clang-format options:

  • -i tells it to do an in-place edit of the file
  • style=file this tells clang-format to use our .clang-format style rules when formatting.

Did you have your project under source control before doing this?

I sure hope you have the project in source control. You should now be able to git diff or whatever you do to view your source changes and see the files that have been modified by the clang-format command.

I hope this post was helpful in showing you how to install clang-format and use it to format your existing Objective-C project.

Infinite Scrolling UICollectionView with Asyncronously Loading

The other day I set out to create an iOS UICollectionView that allowed a user to scroll vertically through an infinite-ish list of items where the data came from a paginated back-end web service. Most of the examples out there deal with synchronous in-memory. I needed an asynchronous paginated example.

I'm new enough to iOS development that solutions don't yet roll off the wire of my keyboard, so I still google for how others attack a problem and adapt their strategy to fit my needs.

It seemed like it took me all day to track down the pieces I needed to put together a solution. But I found enough to put together a prototype.

If you need something like this, I've pushed my prototype up to GitHub and hopefully it'll give you enough hints to get started on your own implementation.

I'm open to feedback. Post any suggestions/alternative ideas to the github repo.

Where do I get it?

HERE

Hope you find this useful.