General documentation / cheat sheets for various languages and services

Documentation Comments

/**
 Description starts here. Indented by a single space, per swiftformat.

 Can also use triple-slashes, but I prefer the double-asterisk syntax.
 The content is interpreted as `Markdown` (`CommonMark` to be exact).

 Three keywords are treated as special; they are pulled out of the description
 flow, so you can put them anywhere.
 - Parameter path: input path to read from
 - Parameter encoding: how to interpret raw bytes in file at path
 - Throws: an error if no such file exists
 - Returns: contents of file as decoded string

 Alternatively, the parameters can be structured like so:
 - Parameters:
   - path: input path to read from
   - encoding: how to interpret raw bytes in file at path

 Several other keywords are auto-highlighted, but not removed from the overall flow:
 * Attention, Author, Authors, Bug, Complexity, Copyright, Date, Experiment, Important,
   Invariant, Note, Postcondition, Precondition, Remark, Remarks, Requires, See,
   Seealso, Since, Todo, Version, Warning
 * Like the others, they are triggered by using the `- <keyword>: <content>` syntax.

 All keywords are case-insensitive, but it seems more common to see them capitalized.
 */
func readFile(_ path: String, encoding: String = "utf-8") throws -> String {
  // TODO: open `path` and read bytes, then decode using `encoding`
  return ""
}

References

References

UIFont

Built-in font sizes (with absolute values from iOS Simulator Playground), from smallest to largest:

Constant Pts
UIFont.smallSystemFontSize 12.0
UIFont.systemFontSize 14.0
UIFont.labelFontSize 17.0
UIFont.buttonFontSize 18.0
// small normal system font
UIFont.systemFont(ofSize: UIFont.smallSystemFontSize)

// larger bold system font
UIFont.boldSystemFont(ofSize: UIFont.labelFontSize)

Grand Central Dispatch

Gotchas

Apparently anonymous functions cannot have arguments labels in Swift, which is maybe one reason why anonymous instantiations / reifications of protocols aren’t supported.

Error explanations

Using self initializer from static function

If your subclass has a static method that tries to use self.init(...) to create a new instance:

class MyViewController: UIViewController, UIViewControllerRestoration {
    static func viewController(withRestorationIdentifierPath identifierComponents: [Any], coder: NSCoder) -> UIViewController? {
        return self.init()
    }
}

You might get this error message:

Constructing an object of class type 'MyViewController' with a metatype value must use a 'required' initializer.

One solution is to just call MyViewController(...), but another fix is to set the class to final: final class MyViewController: UIViewController, UIViewControllerRestoration { ... }

Recipes

/**
Get the date of the current build by reading the `creationDate` of the bundle's `Info.plist` file.
This is not always accurate due to Xcode caching parts of the build process that are not materially changed.
To ensure an accurate reading, `Clean` your project before rebuilding.

Based on https://stackoverflow.com/a/38421481
*/
private func readBuildDate() -> Date? {
    let bundleName = Bundle.main.infoDictionary!["CFBundleName"] as? String ?? "Info.plist"
    if let bundleResourcePath = Bundle.main.path(forResource: bundleName, ofType: nil),
        let bundleResourceAttributes = try? FileManager.default.attributesOfItem(atPath: bundleResourcePath),
        let bundleDate = bundleResourceAttributes[.creationDate] as? Date {
        return bundleDate
    }
    return nil
}

Carthage

Carthage is like CocoaPods, but more high-minded (“decentralized”!), while less mature and less reliable. Presumably more flexible, but also more complex. The youth and complexity is not a good combination if you just want to do simple things quickly.

When upgrading from Carthage 0.25 -> 0.30, I started hitting the following error (generalizing on /path/toXcodeProj and SomeLibrary):

Building scheme "SomeLibrary" in SomeLibrary.xcodeproj
Failed to write to /path/toXcodeProj/Carthage/Build/iOS/SomeLibrary.framework: Error Domain=NSCocoaErrorDomain Code=260 "The file “SomeLibrary.framework” couldn’t be opened because there is no such file." UserInfo={NSURL=file:///path/toXcodeProj/Carthage/Checkouts/SomeLibrary/build/ArchiveIntermediates/SomeLibrary/BuildProductsPath/Release-iphoneos/SomeLibrary.framework, NSFilePath=/path/toXcodeProj/Carthage/Checkouts/SomeLibrary/build/ArchiveIntermediates/SomeLibrary/BuildProductsPath/Release-iphoneos/SomeLibrary.framework, NSUnderlyingError=0x7fbec4eeed80 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}

Why wasn’t that SomeLibrary.framework file there? Carthage doesn’t say. (P.S. What’s with the smart quotes in an error message?)

After trying half a dozen other hacks, this one person’s post-closed comment on a GitHub issue, which he attributed to a comment on an answer to a stackoverflow question, was the one thing that fixed it for me (thanks, Google):

For the sake of everyone who may have the same issue, there is another (quite hidden) XCode setting that may cause the NSCocoaErrorDomain 260 error.

You can access it from XCode > Preferences > Locations > DerivedData > Advanced… which may be also set to “Legacy”. Changing it to “Unique” solved my issue.