Dan Rosenstark, author of MIDI Designer, on Tech & Music

Dan Rosenstark

Dan Rosenstark, Author & CEO of MIDI Designer, muses about all things tech. Particularly: Notes on software development in Swift, Objective-C, and many non-Apple languages. Also: lots of random technology notes on OS X and iOS.

Show Only Apps in Search Results, iOS 11

I want my iPad (and iPhone) to show Apps first when I hit CMD-space or slide down for search. This is the setting to change.



And this is what search results look like without all the clutter.


Optional Block Parameters and Fix-Its

Consider this fix-it:



Now if this happens to you -- as it happened to me -- as part of a massive code conversion to Swift 3, you might be tempted to follow the fix it. Then your code would read like this:

var blockWithOptionalParam: ((NSString?)->())?

func assignBlockWithNonOptionalParameter() {
let tempBlock: (NSString)->() = {
print("count of string \($0.appending("appendMe"))")
}
blockWithOptionalParam = tempBlock as! ((NSString?) -> ())
}

This might be fine, but would result in a crash if you ever passed a nil to your blockWithOptionalParam. The only safe thing to do, really, is to ignore the fix it and handle the optional properly:

func assignBlockWithNonOptionalParameter() {
let tempBlock: (NSString?)->() = {
guard let text = $0 else { return }
print("count of string \(text.appending("appendMe"))")
}
blockWithOptionalParam = tempBlock
}


So What?
For some other cases -- and I'm not sure what the difference is, yet -- Xcode always suggests casting the block rather than fixing the optional in the block. I'm unable to show this example in the lab, but it does show up in the codebase I'm working on currently.



Enabling Command-Line Pipe for Atom on OS X

Pipe to Atom Command-Line Tools

This allows you to pipe from the command line to Atom. Simplest example:

ls -l | atom

Set It Up

Make sure you've run File -> Install Shell Commands first.




Edit your /usr/local/bin/atom (in Atom, for instance):

atom /usr/local/bin/atom

Below this code:

if [ $REDIRECT_STDERR ]; then
exec 2> /dev/null
fi

insert this code

if [ ! -t 0 ]; then
TEMPFILE="$(mktemp)"
cat > "$TEMPFILE"
set "$TEMPFILE" "$@"
fi

Save the file and now piping should work.

This comment is also on GitHub here.


2018-02-15: Update
This does tend to break from time to time (perhaps on Atom update?). You'll need to reapply.


* osx os x macos

Breakpoint Swift Deinit in Xcode


Setting up the Breakpoint
It's actually very simple to breakpoint the deinit method of a Swift class. Given a class called Test47, the symbolic breakpoint merely looks like this:


And that's about it.


Notes
  1. If there is no body to your deinit method, the breakpoint will not fire (it gets optimized out or something similar).
  2. There is no difference in syntax, for the Xcode symbolic breakpoints, between class and instance methods.
  3. There are no deinit methods for structs, so your SOL on that one.

Swift 3.x Calls Objective-C

Maybe you or a colleague named a constant with PascalCase in an Objective-C method (class or like this:

+ (UIColor *)SKDarkBlue;

and in Swift 2, you could call it easily with

self.checkStateBand.backgroundColor = UIColor.SKDarkBlue;

But in Swift 3, the casing is automatically changed for you... isn't that nice!?

self.checkStateBand.backgroundColor = UIColor.skDarkBlue;

This example is great because you can see what happens to two initial caps.

What if it's just one?

- (void) FirstCapMethod;

looks like this in Swift 3:

ObjCObject().firstCapMethod()

Fun!


Does Your Constant End in Notification?

Objective-C Notification

extern NSString * const SKMagTekSwiperDisconnectedNotification;

Gets called like this from Swift

let someNotification = NSNotification.Name.SKMagTekSwiperDisconnected

Swift and Object-C Interop, Simplest Possible Example

Start with a Single View App (Swift) in Xcode 9 Beta 6 called SwiftObjCInterop.


Swift Calls Objective-C

Create an Objective-C Class

Create an Objective-C class called ObjCObject

This will automatically give you the Bridging Header to call the Objective-C from Swift (that was easy!):

Select Create Bridging Header which will produce the file SwiftObjCInterop-Bridging-Header

Add a Method to ObjCObject

Add a method like this in your .m file

- (void) test {
    NSLog(@"Yes we are ObjCObject and we are testing");
}

and this in your .h

- (void) test;

Add ObjCObject to your Bridging Header

#import "ObjCObject.h"

Test Swift Calls Objective-C

Put this in your viewDidLoad:

ObjCObject().test()

Run the project. Now Swift calls Objective-C.


Objective-C Calls Swift

This is slightly trickier in an existing project, but in a new project there are no issues.

Include this in your ViewController.swift file:

@objc(testSwift)
func testSwift() {
    print("yes ViewController is printing")
}

For some reason, you need the @objC tag to be able to see your method from Objective-C.

Include this import in your .m file:

#import "SwiftObjCInterop-Swift.h"

And add this to your test method in your Objective-C class:

ViewController *vc = [[ViewController alloc] init];
[vc testSwift];

That's it.

Using Markdown with Postach.io

Hello World!!!

This HTML is highlighted with Prism using code marks for the highlighting like language-html

<!DOCTYPE html>
<html>
<head>
   ...
   <link href="themes/prism.css" rel="stylesheet" />
</head>
<body>
   ...
   <script src="prism.js"></script>
</body>
</html>

Why Bother?

I do love my work on code highlighting in Evernote. But this markdown stuff may be cooler? Hard to decide. With Markdown, it's definitely more flexible to pop out to a text editor and then back in.

[Testing edit from iPad... just added this]

Are there extra lines or not?

let stringSet = Set(["car", "boat", "bike", "toy"])
let stringArray = stringSet.sorted()

print(stringArray)
// will print ["bike", "boat", "car", "toy"]

Okay, the extra lines are only there if you switch the format using Make Plain Text. Do NOT use Make Plain Text, and always use Simplify Formatting




What About Embeds?

Will Postachio deal with the embeds, still?

[So this embed got removed because I edited this note on iPad... so that's an issue. Maybe a big issue?]

OMG that's absolutely awesome. Which means you could also do iframes and probably even random html.

Lazy Array in Swift

Obviously if you google "lazy array in Swift," you'll find out the right way to do this, which uses NSPointerArray and whatever else. As you should.

But sometimes you want to make your own to see how far you can get in half an hour with generics. Here's my naive implementation.

import Foundation

private class WeakWrapperAnyObject> {
weak var thing: T?
init(_ thing:T) {
self.thing = thing
}
}

struct WeakArrayAnyObject> : Sequence {
private var array: [WeakWrapper<T>] = []
var count: Int { return objects.count }
init(_ objects: [T]) {
append(objects)
}
var objects: [T] {
get {
let retVal: [T?] = array.map { $0.thing != nil ? $0.thing : nil }
return retVal.flatMap { $0 }
}
}
mutating func append(_ object: T) {
array.append(WeakWrapper(object))
}
mutating func append(_ objects: [T]) {
array.append(contentsOf: objects.map { WeakWrapper($0) })
}
mutating func compress() {
array = array.filter{ $0.thing != nil }
}
// MARK: - Sequence Protocol methods
typealias Iterator = IndexingIterator<Array<T>>
func makeIterator() -> Iterator {
return objects.makeIterator()
}
}

let firstObject = NSString(string: "one")
var things = WeakArray([firstObject, NSString(string: "two"), NSString(string: "three")])
things.count
for thing in things {
print("Just one \(thing)")
}
things.compress()

Of course, I did have to look some stuff up, particularly to understand how easy Sequence was to implement (since I already have an array).

Method Selection in Swift

When I was growing up, methods were selected based solely on the stuff on the right side of the equals sign. Not so in Swift. Consider this example.

class ThingWithTwoSameNamedMethods {
func doIt() {
let h: Int = doIt()
print("thank you for calling outer doit \(h)")
}

func doIt() -> Int {
return 32
}
}

let _: Void = ThingWithTwoSameNamedMethods().doIt()
let someInt: Int = ThingWithTwoSameNamedMethods().doIt()
let sum = ThingWithTwoSameNamedMethods().doIt() + 4

If this doesn't scare you, it should, because like most things in Swift: if it's compiling, it's wonderful. But if it's not compiling, for any reason, method selection and typing information is just not there... and you cannot infer it.

Code Blocks in Postachio

My final CSS (which is linked from this blog):

/** this is for the markdown stuff **/
:not(pre) > code[class*="language-"], pre[class*="language-"] {
border: 1px solid gray;
border-radius: 5px;
background-color: #ffffe0;
}


/** non-markdown background and border **/
div[style*="box-sizing: border-box;"] {
background-color: #ffffe0;
border: 1px solid gray;
border-radius: 5px;
}

/** non-markdown indention **/
div[style*="box-sizing: border-box;"] div {
margin: 0px;
padding: 5px !important;
overflow: auto;
white-space: pre-wrap;
}

/** non-markdown line-height consistency **/
div[style*="box-sizing: border-box;"] div,
div[style*="box-sizing: border-box;"] div span,
div[style*="box-sizing: border-box;"] div span {
line-height: 15px;
font-size: 14px;
font-family: Monaco, Menlo, Consolas, monospace;
}

div.posts div.item .post-content p,
div.posts div.item .post-content div,
.post-content div {
line-height: 1em !important;
margin-top: .25em;
margin-bottom: .25em;
padding-top: 0px;
padding-bottom: 0px;
}

.post-content div div {
margin: 1px !important;
padding: 1px !important;
}

/** markdown **/
.post-content p {
background-color: white;
line-height: 1em !important;
margin-top: .5em !important;
margin-bottom: 1em !important;
padding: 0px;
}

div.posts h3, div.post-content h3 {
position: relative;
padding: 0 0 12.5px 0;
margin: 0;
font-size: 1.55rem;
font-family: 'Roboto';
font-weight: 300;
color: #0e3029;
}

Which works with this solution in Evernote and this other solution using Markdown in Postachio (make sure to use languages like language-swift for your code blocks to activate Prism).

Also note Elliot's solution here, which attacks this whole problem from another side.