I have been slowing migrating one of my apps to Swift. This post contains a few Swift extensions that help simplify working with Core Data's localization API. The Swift code, for the most part, is a direct port from existing Objective-C Categories I have used for many years.
Core Data Localization
A NSManagedObjectModel
instance provides access to a localization dictionary loaded from a "model strings" file.
var localizationDictionary: [String : String]?
The returned dictionary is an optional. However, my understanding is that a localization dictionary will not be nil
on iOS or OS X v10.5+.
According to Apple's documentation:
On OS X v10.4, localizationDictionary may return nil until Core Data lazily loads the dictionary for its own purposes (for example, reporting a localized error).
I don't recall ever seeing a managed object model return a nil
localization dictionary. Therefore, the following code uses Swift's !
operator to force unwrap the returned optional.
Model Strings File
Every Core Data model file can have a localized strings file. The strings file name is based on the name of the model file. Here is the pattern:
{ModelFileName}Model.strings
For example, let's say you have a model file named:
HighRail.xcdatamodel
The strings file must be named
HighRailModel.strings
Given this convention you should not include Model
in your model file name. Otherwise a strings file for HighRailModel.xcdatamodel
is HighRailModelModel.strings
.
Localizing Entity Names
Let's look at an example strings file to see how to localize an entity name.
"Entity/HRLLayout" = "Layout";
"Entity/HRLEngine" = "Engine";
"Entity/SensorTrackModule" = "SensorTrackā¢";
extension NSManagedObjectModel {
public func localizedEntityNameForEntityWithName(entityName: String) -> String {
let localizationDictionary = self.localizationDictionary!
let localizationKey = "Entity/\(entityName)"
if let localizedName = localizationDictionary[localizationKey] {
return localizedName
} else {
return entityName
}
}
}
Here is a an example:
let localizedName = managedObjectModel.localizedEntityNameForEntityWithName(HRLEngine.entityName())
// Engine
The entityName()
function is discussed in Creating Core Data Objects In Swift
Also, the method returns the given entity name if a localized string is not found in the strings file. You may want to Fail Fast.
That works well. However, it's a little cumbersome to use if, for example, you need to ask an instance of a managed object for its localized entity name.
Asking A Managed Object Instance For Its Localized Name
First let's add a method named localizedName()
to NSEntityDescription
.
extension NSEntityDescription {
public func localizedName() -> String {
guard let name = self.name else { fatalError("Entity \(self) does not have a name.") }
let managedObjectModel = self.managedObjectModel
return managedObjectModel.localizedEntityNameForEntityWithName(name)
}
}
Now let's add a similar localizedName()
method to NSManagedObject
.
extension NSManagedObject {
public func localizedName() -> String {
let entity = self.entity
return entity.localizedName()
}
}
Now we can ask any NSManagedObject
instance for its localized entity name.
let localizedEngineName = someEngine.localizedName()
// Engine
Localizing Properties and Errors
The managed object model localization dictionary may also contain localized property strings and error strings. More on that in a future post.
Wrap Up
I am not sure if porting existing Objective-C Category methods to Swift extensions is the "right way" to do things in Swift.
Be sure to read Apple's Localizing a Managed Object Model for additional information.