Xcode and New iOS Versions

December 4th, 2017

Filed under: Xcode | Be the first to comment!

I have seen a lot of people on Apple’s developer forums having problems running their apps on iOS devices running the latest version of iOS. If you find yourself in this situation, remember the following:

Xcode is unaware of future iOS versions.

What this means is if you’re running Xcode 8, connect an iPhone running iOS 11, and try to build and run your project on that iPhone, it won’t work. Xcode 8 shipped with iOS 10 so it doesn’t know about iOS 11.

This lack of awareness also applies to dot releases. If you’re running Xcode 9.1 and try to run a project on a device running iOS 11.2, it won’t work either because Xcode 9.1 shipped with iOS 11.1.

How do you fix this problem? You have two options.

  1. Stop constantly updating your iOS devices to the latest version of iOS.
  2. Always use the latest version of Xcode, which may involve running a beta version.

Option 1 is easier because you don’t have to be constantly updating Xcode. But if you’re already running the latest version of iOS, Option 2 is your only option.

Facebooktwittergoogle_plusredditmail

Xcode 9: Activity Monitor Instrument Changes

November 27th, 2017

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

In Xcode 9 Apple removed the recording configuration options for the Activity Monitor instrument. The replacement is summaries for the system CPU usage, system memory usage, system disk activity, and system network activity. You can access these summaries from the jump bar.

ActivityMonitorJumpBar

Facebooktwittergoogle_plusredditmail

Saving Settings with Core Data Metadata

November 6th, 2017

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

I’m working on a Core Data app where I needed to save some document settings. Creating Core Data entities for these settings was overkill. In my research for a solution I discovered persistent store metadata. I did not find much information online on using persistent store metadata so I’m sharing what I learned in this article.

This article is not for people new to Core Data. If you’re new to Core Data, start by reading Core Data Programming Guide, which is part of Apple’s developer documentation.

Persistent Store Metadata

Core Data uses persistent stores to save data. While you can use an in-memory store, most persistent stores are files. In a document-based Core Data app the document file contains the persistent store.

Persistent stores have a metadata property to store metadata. The metadata is a dictionary. The dictionary keys are strings, and the dictionary values can be any data type. Core Data stores the store type and unique identifier in the metadata. You can also add your own entries to the metadata to store small pieces of data that don’t belong in a Core Data entity.

The most common reason for someone to use persistent store metadata is to save per-document settings in Core Data apps that use NSPersistentDocument. Most people who use Core Data have no need to deal with the metadata. They can stick with Core Data entities and Apple’s UserDefaults class. But there are edge cases where saving data in the persistent store’s metadata makes sense.

Getting the Persistent Store

The persistent store contains the metadata. If you want to access the metadata, you must access the persistent store. The managed object context has a persistent store coordinator. The coordinator contains an array of persistent stores. The store is the first element in the array.

let store = context.persistentStoreCoordinator?.persistentStores.first

Keep in mind the array of persistent stores can be empty. If you create a new document in a document-based Core Data application, a persistent store is not created until you save the document. Check that the persistentStores array is not empty before accessing its elements.

Updating the Metadata

Once you have access to the persistent store, take the following steps to fill the metadata:

  1. Create a variable for the metadata.
  2. Set the variable to the store’s metadata property.
  3. Add dictionary entries for your fields.
  4. Set the store’s metadata property to your variable.

Core Data saves some internal data in the metadata. You should be adding your fields to the existing metadata, not overwriting the existing metadata with your fields. That’s why you create a variable for the metadata and set its initial value to the store’s existing metadata before you add any fields to the metadata.

The following Swift code demonstrates how to update the metadata to store the author of a document:

class Document: NSPersistentDocument {
    // Initial value for author. You would set the author's
    // value from a text field in your app's user interface.
    var author = ""
}

if let store = context.persistentStoreCoordinator?.persistentStores.first {
    let metadata = fillMetadata(store: store)
    store.metadata = metadata
}

func fillMetadata(store: NSPersistentStore) -> [String: Any] {
    var metadata: [String: Any] = store.metadata
    metadata["Author"] = author
    return metadata
}

After updating the metadata, save the managed object context to save the metadata. If you stick with simpler data types like strings and integers, you can save without having to write any more code. If you try to save something more complicated, such as an enum, you will have to write code to conform to the Codable protocol or your app will crash when you save.

NSPersistentStoreCoordinator also has class and instance methods, both named setMetadata, to set the metadata. I went with the simplest solution, setting the store’s metadata property.

When to Update the Metadata?

A good time to update the metadata is when you’re about to save the managed object context. Register for the notification NSManagedObjectContextWillSave.

let center = NotificationCenter.default
center.addObserver(self, selector: #selector(Document.contextWillSave(_:)), 
    name: Notification.Name.NSManagedObjectContextWillSave, object: nil)

Handle the notification by calling your function to update the metadata.

func contextWillSave(_ aNotification: Notification) {
    updateMetadata()
}

Retrieving the Metadata

Call the NSPersistentStoreCoordinator instance method metadataForPersistentStore to retrieve the metadata for your persistent store. Supply the persistent store as the argument to metadataForPersistentStore.

The following Swift code demonstrates how to retrieve the document’s author from the metadata:

if let store = context.persistentStoreCoordinator?.persistentStores.first,
    let metadata = context.persistentStoreCoordinator?.metadata(for: store) {        
        fillSettings(metadata: metadata)
}

func fillSettings(metadata: [String: Any]) {
    if let theAuthor = metadata["Author"] as? String {
        author = theAuthor
    }
}

NSPersistentStoreCoordinator also has a class method to retrieve the metadata, but it requires you to write a do-try-catch block. Calling the instance method is easier.

Facebooktwittergoogle_plusredditmail

UITextView Word Count in Swift

October 24th, 2017

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

Getting the word count of an iOS text view is a little more difficult than a Mac text view because the NSTextStorage class’s words property is not available on iOS. A starting point for getting the word count is to separate the components of the text view’s string by whitespace and newline characters.

let words = textView.text.components(separatedBy: .whitespacesAndNewlines)

The problem is the componentsSeparatedBy function treats empty strings as words. If you have an empty text view, the word count will be 1. Pressing the space bar increments the word count. If you hit the space bar 5 times, the word count goes up by 5 even though you didn’t type any words.

The solution to get an accurate word count is to filter the empty strings out of the array of words.

@IBOutlet weak var textView: UITextView!

let words = textView.text.components(separatedBy: .whitespacesAndNewlines)
let filteredWords = words.filter({ (word) -> Bool in
    word != ""
})
let wordCount = filteredWords.count
Facebooktwittergoogle_plusredditmail

A Note About Swift for Job Recruiters

October 16th, 2017

Filed under: Uncategorized | Be the first to comment!

Apple first showed the Swift language to developers in June 2014. At the time I am writing this, that means Swift has been out for a little over three years. That means no developer can have more than three years of Swift experience. You can’t require 5+ years of Swift experience in your job description when the language hasn’t been out that long. No one will have 5 years of Swift experience until June 2019.

Facebooktwittergoogle_plusredditmail