Xcode 10: Projects Use New Build System

October 1st, 2018

Filed under: Xcode | Be the first to comment!

In Xcode 10 projects use the new build system by default. If you want to use the legacy build system in a project, choose File > Project Settings. Choose Legacy Build System from the Build System menu.


Xcode 10: See Changed Code in Editor

September 27th, 2018

Filed under: Version Control, Xcode | Be the first to comment!

If your Xcode project is under version control, Xcode’s editor highlights the changes you make to the code. When you add or change code, Xcode places a blue bar on the left edge of the editor.

Xcode10VersionControlChangesInEditorHighlighted

Clicking the blue bar opens a popover to discard the change. For some reason you can’t commit changes from the editor. You can only discard changes.

If you remove code, there may be a blue dot instead of a blue bar in the editor. Click the dot to discard the change and restore the code you removed.

Other Bar Colors

The blue bar on the left side of the editor window is the most common color you’ll see. But Xcode also has the following color bars for source control changes:

  • A gray bar indicates changes you committed locally but did not push to a remote repository.
  • A blue outline indicates upstream changes, changes someone else working on the project made.
  • A red bar indicates the changes you made conflict with upsream changes.

Troubleshooting

If you do not see colored bars on the left side of the editor, you may need to turn on showing source control changes from Xcode’s Source Control preferences.

Xcode10SourceControlPreferences

Make sure the Show Source Control changes checkbox is selected. Select the Include upstream changes checkbox to see upstream changes in the editor.


Xcode 10: Improved Bitbucket and GitLab support

September 20th, 2018

Filed under: Version Control, Xcode | Be the first to comment!

Xcode 10 improves version control support for Bitbucket and GitLab. You can add your Bitbucket and GitLab accounts to Xcode from Xcode’s Accounts preferences.

XcodeAccountsPreferencesHighlighted

Click the Add button to add an account. A sheet opens.

Xcode10AddBitbucketAccount

Choose Bitbucket Cloud to add your Bitbucket account. Choose GitLab.com to add your GitLab account.

Starting in Xcode 10 you can add your Xcode project to Bitbucket or GitLab from Xcode. Open the Source Control navigator by choosing View > Navigators > Show Source Control Navigator. Select the Remotes folder, control-click, and choose Create Remote. A sheet opens.

Xcode10AddBitbucketRepoHighlighted

Choose your account from the Account menu. Click the Create button to add your project to Bitbucket or GitLab.


Xcode 10: Library Moved to Toolbar

September 17th, 2018

Filed under: Xcode | Be the first to comment!

In Xcode 10 the Library, where you access things like code snippets and user interface elements, is no longer in the lower right portion of the project window. The button to access the Library has been moved to the right side of the toolbar, next to the buttons to show the various editors.

Xcode10LibraryButtonHighlighted

Click the Library button to open the Library in a separate window. Option-clicking the Library button keeps the window open, which helps a lot when you’re building your app’s user interface.

The specific library that opens depends on the file you’re currently viewing/editing. If you’re in a source code file, clicking the Library button will open the code snippets library. If you’re in a xib file or storyboard, clicking the Library button will open the object library that contains the UI elements.


Supporting Dynamic Type in iOS Text Views

September 10th, 2018

Filed under: iOS Development | 1 comment

Dynamic Type on iOS lets people adjust the size of text to make text easier to read. Someone with poor eyesight can use larger text if the app supports Dynamic Type.

You can support Dynamic Type in your iOS app without writing any code. You have to do the following things:

  • Use plain text in the text view.
  • Tell the text view to support Dynamic Type in Interface Builder.
  • Choose a Dynamic Type text style as the text view’s font.

You can perform all these tasks from the attributes inspector in Interface Builder. Select your text view from the storyboard or xib file.

TextViewDynamicTypeIB

To use plain text in the text view, choose Plain from the Text menu.

Select the Dynamic Type checkbox to support Dynamic Type. If you build your project at this point, you will most likely get the following warning in Xcode:

Automatically Adjusts Font requires using a Dynamic Type text style

Text views in iOS Xcode projects initially use the system font, which does not support Dynamic Type. You must change the font to use a Dynamic Type text style. Click the button on the right side in the Font text field to open a popover to change the font.

TextViewFontPopover

Use the Font menu to select a text style. There’s a Text Styles group of menu items. Choose one of them as the font for the text view to support Dynamic Type.


Linker command failed with exit code 1

September 6th, 2018

Filed under: Xcode | 1 comment

If you have done any programming in Xcode, you have come across the following error message when building your project:

Linker command failed with exit code 1.

This error message is frustrating because it tells you nothing besides the fact the linker command failed. You want to know what the real error is so you can fix it.

Find the Real Error

The first step to fixing a link error is to find out what the real error is. To find the real error, open Xcode’s report navigator by choosing View > Navigator > Show Report Navigator.

ReportNavigatorXcode9

The report navigator shows recent builds. If you have a linker error, there will be a red icon next to the build, which you can see in the screenshot. Select the build to see the build transcript, the list of steps Xcode took to build the project.

BuildLogHighlighted

If you have a link error, the build transcript will provide the real error message. Selecting a step from the build transcript makes a little button appear next to the step (The screenshot arrow is pointing to the button). Click that button to see a more detailed build log for that step.

The two most common link errors are undefined symbol and duplicate symbol.

Undefined Symbol Error

An undefined symbol link error occurs when the linker cannot find a data structure name or a function name. The most common cause of an undefined symbol error is a missing framework or library that contains the symbol. Some things to check to fix an undefined symbol error include the following:

  • Make sure you added the library or framework to your project.
  • Make sure the library or framework you added is in the Link Binary with Libraries build phase.
  • If you’re coding in Swift, make sure you imported the library or framework in your code with the import statement.

LinkBinaryBuildPhase

Header Files

If you’re coding in a language that uses header files, working with header files is another big cause of undefined symbol link errors. Make sure you include the header properly. A common error in C or C++ code is to include a header file that’s not a system header with angle brackets.

#include <MyHeader.h>

Xcode won’t be able to find MyHeader.h in this case because the header is not a system header. Use quotes to include the header file.

#include "MyHeader.h"

Search Paths

If none of the fixes I mentioned earlier work, you may need to add search paths for the library, framework, or header file so Xcode can find them. Xcode has a Search Paths collection of build settings, which you can access from the project editor.

SearchPathsBuildSettings

Duplicate Symbol Error

A duplicate symbol error occurs when the linker finds two or more data structures or functions with the same name. People using Xcode to learn C++ can run into this error if they create a command-line tool project. The command-line tool project includes a main.cpp file that has a main function in it. If you add a new file with its own main function and forget to remove the one Xcode created for you, you get a duplicate symbol error.

When trying to fix a duplicate symbol error, the first thing to check is that you haven’t defined a symbol twice in your project. Xcode’s find navigator helps you find all occurrences of a given symbol in your project.

FindNavigator

If you’re using external frameworks or libraries, make sure your symbol names aren’t duplicates of symbols in the framework or library. If you are using custom namespaces in your code, make sure you set up the namespaces properly.


Creating a Keyboard Input Accessory View

August 10th, 2018

Filed under: iOS Development | Be the first to comment!

In this article you’ll learn about keyboard input accessory views and how to add them to your iOS app to make text editing smoother.

Adding a keyboard input accessory view isn’t too difficult. Add a new xib file to your project for the view. Load the view from the xib file. Set the input accessory view to the view you loaded. There’s less than 10 lines of code involved.

What Is a Keyboard Input Accessory View?

A keyboard input accessory view is a view that appears above the iOS onscreen keyboard. The default iOS behavior is to have no input accessory view, but nothing is stopping you from adding one. A common use of an input accessory view is to show a toolbar with frequently used commands when typing text. The writing app Ulysses uses an input accessory view to insert Markdown characters into the text. If you were writing an iOS HTML editor, you could show a toolbar with buttons to insert the most common HTML tags.

Keep in mind that a keyboard input accessory view has type UIView?, which means the accessory view can be any kind of view. The accessory view does not have to be a toolbar. I’m using a toolbar as an example in this article because many apps have a toolbar as the accessory view.

Step 1: Create a Xib File for the Accessory View

The first step in creating a keyboard input accessory view is to create a new xib file and add it to your project. In Xcode choose File > New > File to add a new file to your project. The xib file templates are in the User Interface section under iOS.

AddNewiOSXibFile

The empty xib file is the best option for toolbars. If you want a custom view for the accessory view, you can choose View instead of Empty.

Step 2: Add the Accessory View to the Xib File

Take a toolbar (or whatever kind of view you want to use) from the object library and drag it to the canvas. The toolbar has one item in it. Add bar button items to the toolbar to fill the toolbar.

While you’re in Interface Builder go to the identity inspector and set the class for the File’s Owner object in the xib file.

FilesOwnerClassBlurred

Set the class to the view controller where you’re going to use the input accessory view. By setting the File’s Owner class to the view controller, you will be able to connect the controls in the input accessory view to IBActions in the view controller to respond to tapping the controls. In the xib file make a connection from the controls to File’s Owner to connect to IBActions.

Step 3: Load the Accessory View from the Xib File

Now it’s time to write some code to load the input accessory view. Call the Bundle class’s loadNibNamed function to load a xib file from the app bundle. Supply the name of the xib file as the first argument. Pass self as the owner argument, and pass nil as the options argument.

Calling loadNibNamed returns an array of the top level objects in the xib file. The input accessory view is the only top level object in the xib file you created so accessing the view is easy. Use the first property to access the first item in the array and grab the accessory view. The last code to write is to set the text view’s input accessory view to the view you loaded from the xib file. The following function sets up an input accessory view for a toolbar:

func setupInputAccessoryView() {
    let nibContents = Bundle.main.loadNibNamed("KeyboardInputAccessoryView", 
        owner: self, options: nil)
    if let toolbar = nibContents?.first as? UIToolbar {
        textView.inputAccessoryView = toolbar
    }    
}

Conclusion

There’s no more code to write. If you build your project and show the keyboard, the toolbar will appear at the top of the keyboard.

I noticed a bug in the iOS 11 Simulator where the toolbar would stay at the bottom of the screen after dismissing the keyboard. I did not run into this issue on iOS devices. If the toolbar is not going away, set the isHidden property of the input accessory view to true when dismissing the keyboard. Set isHidden to false when showing the keyboard.


Make UIDocument File Wrapper Appear as Single File

August 3rd, 2018

Filed under: iOS Development | Be the first to comment!

I was recently working on an iOS document-based app that saves the document in a file wrapper. When I looked in the document browser, the document appeared as a folder instead of a single file. For Mac document types Xcode has a checkbox to select to make the document appear as a single file, but iOS document types do not have this checkbox. How do you make the document appear as a single file?

To make the document appear as a single file in the document browser, the document’s exported UTI must conform to the UTI com.apple.package. In Xcode’s project editor, add com.apple.package to the Conforms To text field for the exported UTI.

ExportedUTIFileWrappersHighlighted

Rebuild your project. Any documents you save should appear as single files instead of folders.


Working with File Wrappers in Swift

July 17th, 2018

Filed under: Cocoa, Mac Development | 1 comment

Back in 2010 I wrote an article on working with file wrappers. Recently I needed to work with file wrappers, and I noticed the file wrapper code is much different in Swift so I figured working with file wrappers in Swift would be a good topic for an article.

This article assumes you’re creating a document-based application. I have only used file wrappers in document-based applications.

What Is a File Wrapper?

A file wrapper is a bundle, which is a collection of one or more directories that looks like a single file in the Finder. Most Mac applications are bundles. You can examine their contents by selecting an application, right-clicking, and choosing Show Package Contents.

When should you use a file wrapper? Use a file wrapper when you want to save your application’s document data in multiple files. A level editor for a game may want to save the level layout, the level’s enemy list, and the level’s treasure list in separate files.

The simplest file wrapper is a single directory that stores all the files. But you can create multiple directories to store your files to keep them better organized.

There are three tasks to perform to add file package support to a document-based application. First, you must tell Xcode that your document type is a file wrapper. Second, you must implement the fileWrapperOfType method in your NSDocument subclass to save to a file wrapper. Third, you must implement the readFromFileWrapper method in your NSDocument subclass to open a file wrapper and read its contents.

Making Your Document Type a File Wrapper

You can access your document type from Xcode’s project editor.

  1. Select your project from the project navigator to open the project editor.
  2. Select the application target from the target list on the left side of the project editor.
  3. Click the Info button at the top of the project editor.

DocumentTypeAsBundleHighlighted

Go to the Document Types section and select the Bundle checkbox. Now your document will be saved as a file wrapper.

Writing a Document File Wrapper

To write a document file wrapper, you must override and implement the fileWrapperOfType method. Here is the Swift signature for fileWrapperOfType.

override func fileWrapper(ofType typeName: String) throws -> FileWrapper

The file wrapper fileWrapperOfType returns is the root directory for the file wrapper.

In your implementation of fileWrapperOfType, you must perform the following tasks:

  • Create a directory file wrapper.
  • Get your app’s data into a Data object.
  • Create a file wrapper and add the file to a directory file wrapper.

Creating a Directory File Wrapper

At a minimum you must create a root directory for the file wrapper. To create a directory in a file wrapper, call the FileWrapper method directoryWithFileWrappers and supply an empty Swift dictionary.

let rootDirectory = FileWrapper(directoryWithFileWrappers: [:])

If you want to add any other directories to your file wrapper, call directoryWithFileWrappers again and supply an empty Swift dictionary.

let htmlDirectory = FileWrapper(directoryWithFileWrappers: [:])

The root directory in a file wrapper does not need a name, but any other directories you create need a name. Set the filename or preferredFilename property for the directory file wrapper to name the directory. The following code gives the HTML directory the name HTML:

htmlDirectory.filename = "HTML"

Getting Your Data into a Data Object

Files in file wrappers store their data in a Data (formerly NSData) object. Converting to Data depends on what you are storing, but the following code converts a string to Data:

let htmlData = htmlString.data(using: .utf8)

Creating a File Wrapper

To create a file wrapper for a regular file, call the FileWrapper method regularFileWithContents and supply the Data object that contains the file’s data.

let wrapper = FileWrapper(regularFileWithContents: htmlData)

Set the filename or preferredFilename property to name the file.

wrapper.filename = "index.html"

In a real app you won’t be hardcoding filenames much. Suppose you have a document that has a list of articles. You would use the title of the article as the filename instead of giving the file a specific name. Remember that you use file wrappers to store multiple files. If you have 20 files to save, hardcoding the name of each file is going to be a pain.

After creating the file wrapper, call the directory’s addFileWrapper method to add it to the directory.

htmlDirectory.addFileWrapper(wrapper)

Call addFileWrapper to add a subdirectory to a file wrapper. The following code adds the HTML directory to the root directory:

rootDirectory.addFileWrapper(htmlDirectory)

Reading from a File Wrapper

At this point you know how to write to a file wrapper. The next step is to read the file wrapper. To read the file wrapper, you must override the readFromFileWrapper method. Here is the signature for readFromFileWrapper.

override func read(from fileWrapper: FileWrapper, ofType typeName: String) throws

The fileWrapper argument is the root directory of the file wrapper.

The fileWrappers property contains the contents of a file wrapper. The contents appear in a Swift dictionary, where the key is the name of a file or directory and the value is a file wrapper, which can be either a file or a directory.

To access a directory or file from the dictionary, supply the key as the dictionary subscript. The following code shows how to access a HTML directory inside the root file wrapper:

if let wrappers = fileWrapper.fileWrappers {
    let htmlDirectory = wrappers["HTML"]
}

Make sure the subscript you supply matches the filename or preferred filename you gave to the file or directory. If you make a typing mistake, you won’t be able to read from the file wrapper.

Reading the Individual Files

Use the fileWrappers property to access the individual files in a file wrapper directory. The following code provides a list of files inside a HTML directory:

htmlFiles = htmlDirectory.fileWrappers

The contents appear in a Swift dictionary, where the key contains the name of the file and the value contains a file wrapper.

Use the regularFileContents property to access the contents of a file. The regularFileContents property has type Data?, which means it’s either a Data object or nil. By using regularFileContents, you can get the Data object.

if let homepage = htmlFiles["index.html"] {
    let homepageData = homepage.regularFileContents
}

Loading the Data

Accessing the file wrapper’s regularFileContents property gives you a Data object to load the file’s contents in your app. Loading the file’s contents depends on the type of data being stored, but the following code loads a file’s contents into a string variable:

let htmlString = String(data: homepageData, encoding: .utf8)

Xcode Turns on the App Sandbox for New Cocoa Projects

July 11th, 2018

Filed under: Cocoa, Mac Development, Xcode | Be the first to comment!

I ran into an issue with a new document-based Cocoa app throwing exceptions when I tried to save the document. It turns out the issue was the App Sandbox, which is turned on when you create a new Cocoa application project in Xcode.

You can see the App Sandbox settings by selecting your project from the project navigator to open the project editor. Select your target and click the Capabilities button at the top of the editor. The following screenshot shows the initial settings:

AppSandboxDefaultSettingsHighlighted

Notice that the App Sandbox initially doesn’t give your app access to anything. It won’t let you print or make any network connections. Surprisingly the App Sandbox does not initially give you permission to open a Save panel to let someone choose where to save a document on their Mac. Apparently opening a Save panel is insecure. Access to user selected files is read only initially. To allow a Save panel to open, you must change the permission for user selected files to Read/Write.

Summary

If Xcode is throwing exceptions when your Cocoa app does simple things like print, open a Save panel, or show a website in a web view, it might not be a problem in your code. It might be the App Sandbox.

There are two ways to work around the App Sandbox. The first workaround is to turn off the App Sandbox. The second workaround is to give your app permission to do things like print and display web content in the App Sandbox.