I don't know why, but I've always seemed to be looking for the "perfect" Markdown editor - although I used a lot of really great ones, none really clicked with me.
Come in, LightPaper. Easy file access via the sidebar, multi-tabs, script assets, Jekyll Rendering, Math Rendering, MultiMarkdown or GitHub Flavored Markdown, a preference tab where you write CSS for extra customization on top of the usual CSS-based themes, and much more.
It all started with this post by Joe for updating a plug-in from the terminal, which lead to this script for automating the process of updating all plug-ins.
Even though I could have created an Alfred workflow to easily run the script with a single command, I always wanted to start working on Mac apps, so this was a good opportunity to get my feet wet.
I've had problems with stuck updates, or slow downloads with the MAS for as far as I can remember. softwareupdate never really was of much help, using MAS' Debug menu neither, nor killing softwareupdate related processes.
Yesterday I found the answer to all of this: a gem for manipulating the MAS from the CLI. It uses native APIs, from login (the MAS login pops up), to downloading files (you can even start an update with the MAS and finish it on the CLI - the download files are the same). And you also get a nice, little progress bar.
A few commands available:
$ mas list
446107677 Screens
407963104 Pixelmator
497799835 XcodeWe can already use --graph and --decorate to get a pretty, colored commit tree, but we can create a function for less typing, more flexibility and more info:
git_branch() {
git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/'
}I'm pretty sure this won't suit all cases, but, usually, a label / button should highly resist being vertically shrunk more than its intrinsic size. On the other hand, we won't always mind if it grows larger than its intrinsic size, but we'd like to avoid it, if possible.
I, personally, find this a bit of a mouthful:
label.setContentCompressionResistancePriority(.required, forAxis: .vertical)
label.setContentHuggingResistancePriority(.defaultHigh, forAxis: .vertical)So, let's extract them into a method, with default values as added bonus. We'll also use an enum, so we can have "intermediate" values as cases:
I talked about how we can have a safer and cleaner tableView/collectionView section handling, but we can improve it even further, with protocol extensions:
protocol Countable {
var rawValue: Int { get } // 1
init?(rawValue: Int) // 2
static var count: Int { get } // 3
}This is a protocol that mimics an enum that conforms to Int: it has a rawValue of type Int (1), it has an initializer based on said rawValue (2), and it has a static var (3) that will be used to hold the total number of cases. And here's where extensions come into play:
extension Countable {
There's this handy feature, Debug -> View Debugging -> Show View Frames, which, if turned on, draws borders around views. The sad part is that it only works on the simulator. But we can (somewhat) easily simulate its behavior.
First, we create a helper method that does the coloring:
func activateFramesDebug() {
guard debugFrames else { return }
layer.borderWidth = 1
layer.borderColor = UIColor(
hue: CGFloat(arc4random_uniform(100_000)) / 100_000,
saturation: CGFloat(arc4random_uniform(100_000)) / 100_000,
brightness: 0.5 + CGFloat(arc4random_uniform(50)) / 100,
alpha: 1.0).CGColor
}One usual approach is to create an enum, so your tags are more expressive by having a name:
enum ViewTag: Int {
case none
case titleLabel
case loginButton
}Then tagging and retrieving views by tag will be safer, and easier to remember:
let container = UIView()
let titleLabel = UILabel()
container.addSubview(titleLabel)
titleLabel.tag = ViewTag.titleLabel.rawValue
let loginButton = UIButton(type: .Custom)
container.addSubview(loginButton)
loginButton.tag = ViewTag.loginButton.rawValueThe standard approach for this would be something like this:
struct Theme {
enum Color {
case title
case subtitle
// [...]
var color: UIColor {
switch self {
case .title: return UIColor.red
case .subtitle: return UIColor.blue
...
}
}
}
enum Font {
}
}And using it throughout the app would look like this:
func customLabel(text: String, color uiColor: Theme.Color, font uiFont: Theme.Font) {
[...]
label.textColor = color.uiColor
label.font = font.uiFont
[...]
}First, we need to ask for the user's permission to access the folder, but for that, we need an NSSavePanel:
let op = NSSavePanel()
op.message = "Descriptive message here"
op.canCreateDirectories = false
op.canChooseDirectories = false
op.showsHiddenFiles = false
op.prompt = "Allow"
op.title = "Allow access"
op.isExtensionHidden = true
op.directoryURL = URL(string: "/path/to/folder")
// Depending on your purpose, you might need these to true
op.allowsMultipleSelection = false
op.canChooseFiles = false