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.

Swift 2 to 3, Method Calls, Magic First Parameters & Automatic Conversion Woes

Swift 2 Calling Swift 2

Consider these two methods in Swift 2:

func doThis(thing: AnyObject) {}

func doThisThing(thing thing: AnyObject) {}

are called like this in Swift 2.

doThis("")

doThisThing(thing: "")

So:

  • The first parameter in the doThis method cannot be named by the caller.
  • Whether the method name ends in the first parameter name or not, the behavior doesn't change.

Swift 2 Calling Objective-C

Here are two methods in Objective-C:

- (void) doIt:(NSObject *)thing;

- (void) doThisThing:(NSObject *)thing;

Both of these methods get called without their first parameter name from Swift 2:

someObjcObject.doIt("")

someObjcObject.doThisThing("")

So:

  • First parameter name is not used.
  • The method name is the same as the method name in Objective-C.
  • This is true even if the method end in an obvious parameter name, like "doThisWithThing"

Objective-C Calling Swift 2

The method name is the same as that seen in Swift unless you've doubled up on the parameter name as in the above example. So:

func doThis(thing: AnyObject) {}

func doThisThing(thing thing: AnyObject) {}

results in this calling code in Objective-C (note the altered method name in the second example):

[vc doThis:@""];

[vc doThisThingWithThing:@""];

The first method doesn't change even if we use an underscore in the Swift method declaration:

func doThis(_ thing: AnyObject) {}

So:

  • The method names translate normally
  • Doubled-up parameter names get added to the method name in Objective-C with the conjunction "with"

Changes in Swift 3

There is no automatic removal of the first parameter name, so:

func doThisThing(thing: AnyObject) {}

gets called like this:

doThisThing(thing: "" as AnyObject)

If you want to remove the first parameter label for callers, you can use an underscore, of course.

Weird Automatic Conversion to Swift 3

So this converted just fine:

func doThis(thing: AnyObject) {}

func doThisThing(thing thing: AnyObject) {}

// calling code
doThisThing(thing: "what")
doThis("what")

and became this in Swift 3

func doThisThing(thing: AnyObject) {}

func doThis(_ thing: AnyObject) {}

// calling code
doThisThing(thing: "what" as AnyObject)
doThis("what" as AnyObject)

So that makes sense.

Converting Again

Now you realize that your massive conversion needs to forward merge more Swift 2 code into the same target. Xcode warns you not to do automatic conversion again, but you do it anyway.

The two methods now look like this, which makes no sense (the second one has now lost its parameter label):

func doThisThing(_ thing: AnyObject) {
    print("doThisThing \(thing.hash)")
}

func doThis(_ thing: AnyObject) {
    print("doThis \(thing.hash)")
}

// calling code
doThisThing("what" as AnyObject)
doThis("what" as AnyObject)

In this simple case the callers get updated, but the Objective-C callers do not:


And in many cases, your calling code will be broken in Swift as well.

Calling Objective-C from Swift 2 vs. 3

The automatic madness deepens with Swift 3 interop.

Consider this signature in Objective-C:

+ (NSObject *)deserializeNewOrExistingObjectFromAPIV1JSON:(NSDictionary *)json

In Swift 2 you called it this way:

ThatObject.deserializeNewOrExistingObjectFromAPIV1JSON(json.dictionaryObject)

But this changes in Swift 3 to:

ThatObject.deserializeNewOrExistingObject(fromAPIV1JSON: json.dictionaryObject)

And now the result comes back as a forced optional, whereas in Swift 2 it came back as a non-optional.

Which may or may not get picked up by the automatic converter. The more complex your compilation target is, the less likely the automatic conversion is to succeed.

Some capitalization changes as Objective-C gets magically converted:

- (instancetype)initWithJSON:(NSDictionary *)messageBody
// Swift 2
ThatObject(JSON: dict)!

// Swift 3
ThatObject(json: dict)

And some stuff gets even weirder in Objective-C interop:

+ (id)findForUuid:(NSString *)uuid
// Swift 2
ThatObject.findForUuid(uuid)

// Swift 3
ThatObject.find(forUuid: tender.uuid)!

Code Highlighting in Evernote... Kinda

A while back, Evernote for Mac added Code Blocks. To enable this feature, you cannot use the App Store version of Evernote.

In addition, you have to check Enable Code Block in Preferences


Code blocks were okay, allowing you to do nice simple stuff maintaining your plain text formatting like this:

func commonInit() {
self.backgroundColor = UIColor.black;
let imageView = UIImageView(image: UIImage(named: "splash.png")!)
imageView.contentMode = .scaleAspectFit
self.addSubview(imageView)
constrain(self, imageView) { me, imageView in
imageView.size == me.size
imageView.center == me.center
}
}

But there was no way to keep your colored syntax formatting (from Xcode, App Code and others) until Evernote 6.11 for Mac (or sometime like that).

Here's how to do it:
  1. Type one line of text
  2. Turn that into a code block
  3. Replace the contents of that code block with code you paste from Xcode
  4. That's it!


Stubbed Code Block

Stub a code block


Stubbed Code Block, Content Replaced

func commonInit() {
self.backgroundColor = UIColor.black;
let imageView = UIImageView(image: UIImage(named: "splash.png")!)
imageView.contentMode = .scaleAspectFit
self.addSubview(imageView)
constrain(self, imageView) { me, imageView in
imageView.size == me.size
imageView.center == me.center
}
}


In Atom: Use Copy as RTF

The Copy as RTF package in Atom will make this work there too. Fun!

Associated Object Support for Swift 2.3

This is your AssociatedObjectSupport.swift. It's for Swift 2.3 and it's based on this. It can handles nulls, and non-objects, too (by wrapping them in Lifted).
import UIKit

final class Lifted {
let value: Any
init(_ x: Any) {
value = x
} }
extension NSObject {
func setAssociatedObject(value: T, associativeKey: UnsafePointer<Void>, policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN_NONATOMIC) {
if let v: AnyObject = value as? AnyObject {
objc_setAssociatedObject(self, associativeKey, v, policy)
}
else {
let liftedObject = Lifted(value)
objc_setAssociatedObject(self, associativeKey, liftedObject, policy)
}
}

func getAssociatedObject(associativeKey: UnsafePointer<Void>) -> T? {
let value = objc_getAssociatedObject(self, associativeKey)
if let value = value as? Lifted {
return value.value as? T
}
return value as? T
} }

Example UIView Extension
And here's an example extension to UIView that uses it. Note the clever use of a private struct to get static variables on the Extension nicely.
extension UIView {    
private struct AssociatedKeys {
static var viewExtension = "viewExtension"
static var anotherView = "someOtherView"
static var someFloat = "someFloat"
}

var someFloat: Float {
get {
return getAssociatedObject(&AssociatedKeys.someFloat) ?? 0.0
}
set {
setAssociatedObject(newValue, associativeKey: &AssociatedKeys.someFloat)
}
}

var baseTransform: CGAffineTransform? {
get {
return getAssociatedObject(&AssociatedKeys.viewExtension)
}
set {
setAssociatedObject(newValue, associativeKey: &AssociatedKeys.viewExtension)
}
}

var anotherView: UIView? {
get {
return getAssociatedObject(&AssociatedKeys.anotherView)
}

set {
setAssociatedObject(newValue, associativeKey: &AssociatedKeys.anotherView)
}
} }


Using the Example
var view = UIView()
view.baseTransform = CGAffineTransformIdentity
print("what's up \(view.baseTransform)")
view.baseTransform = nil
print("what's up \(view.baseTransform == nil)")
view.anotherView = UIView()
print("you got another view? \(view.anotherView) \(view.anotherView?.anotherView)")
view.anotherView = nil
view.someFloat = 32.5
print("what's it? \(view.someFloat)")

Associated Object Support for Swift 3.x

Of course in Swift 3, this is all trivial... but plus, if you're not using self, why bother? (Credit to this Swift 2.x example)

extension NSObject {
func setAssociated(value: T, associativeKey: UnsafeRawPointer, policy: objc_AssociationPolicy = .OBJC_ASSOCIATION_RETAIN_NONATOMIC) {
objc_setAssociatedObject(self, associativeKey, value, policy)
}

func getAssociated(associativeKey: UnsafeRawPointer) -> T? {
let value = objc_getAssociatedObject(self, associativeKey)
return value as? T
}
}

extension UIView {
private struct AssociatedKeys {
static var viewExtension = "viewExtension"
static var anotherView = "someOtherView"
static var someFloat = "someFloat"
}
var someFloat: Float {
get {
return getAssociated(associativeKey: &AssociatedKeys.someFloat) ?? 0.0
}
set {
setAssociated(value: newValue, associativeKey: &AssociatedKeys.someFloat)
}
}
var baseTransform: CGAffineTransform? {
get {
return getAssociated(associativeKey: &AssociatedKeys.viewExtension)
}
set {
setAssociated(value: newValue, associativeKey: &AssociatedKeys.viewExtension)
}
}
var anotherView: UIView? {
get {
return getAssociated(associativeKey: &AssociatedKeys.anotherView)
}
set {
setAssociated(value: newValue, associativeKey: &AssociatedKeys.anotherView)
}
}
}

var view = UIView()
view.baseTransform = CGAffineTransform.identity
print("what's up \(view.baseTransform)")
view.baseTransform = nil
print("what's up \(view.baseTransform == nil)")
view.anotherView = UIView()
print("you got another view? \(view.anotherView) \(view.anotherView?.anotherView)")
view.anotherView = nil
view.someFloat = 32.5
print("what's it? \(view.someFloat)")

My own notes: you cannot get proper weak object support with Associated Objects, so only use them if you do not extend the actual class yourself.

Later Note
That's really not true: by wrapping your object in a Weak wrapping object, you can always get weak object support. So that's quite cool.

Subprojects in Xcode with Static Libs

Static libraries produce .a files. The catch is: static libraries cannot contain Swift files. But anyway...


Workspaces and Projects
  • You can add projects to projects (subprojects) or to workspaces
  • Projects can be saved as workspaces
  • Add a project to another project -- or a workspaces -- by adding the .xcodeproj file.



Early Setup
Add GBDevice as a submodule
git submodule add https://github.com/lmirosevic/GBDeviceInfo.git GBDeviceInfo



Set It Up
  1. Create a new, Single View Application for iOS
  2. Add the .xcodeproj for an Xcode project that produces a .a file

    In this example we're using GBDevice.

  3. Set up the primary project like this




Import Objective-C to Swift
  • Create a new header file called BridgingHeader.h
  • Add it to the Build Settings for the Single Window App



  • Utilize it in a Swift file

    let version = GBDeviceInfo.init().modelString
    print(version)
  • That's it.


The kicker is: you cannot use Swift in a static library, so there's that. Hence this other article I wrote (longer, and uses Cocoapods)

Presets for Groups, Controlled by a Button Group

This question was asked on the MIDI Designer forums. It's a great example of the flexibility of MIDI Designer.

Initial Setup
Four knobs, that's it


Setup Preset for Group Supercontrol

  1. Add knob
  2. Set Subtype to Presets



  3. Tap "Yes" to create 12 presets



  4. Add the four knobs as subcontrols of the Preset knob


Make Some Presets
Create some presets by moving the presets knob and setting the four knobs to different settings.

Add Four Buttons

Adjust the Buttons' Relationships
  1. Make each button a supercontrol of Knob 5
  2. Verify the supercontrols of Knob 5



Set On Values for Buttons
Set the MIDI On value for each of the buttons. The formula is: (PresetNumber - 1) ÷ 12 x 128


  • Btn 6 = 0 ÷ 12 x 128 = 0, but we put in a MIDI On value of 1 so that it's not equal to the OFF value
  • Btn 7 = 1 ÷ 12 x 128 = 10.6 = MIDI On Value of 11
  • Btn 8 = 2 ÷ 12 x 128 = 21.3 = MIDI On Value of 21
  • Btn 9 = 3 ÷ 12 x 128 = MIDI On Value of 32


Group the Buttons with a Supercontrol Knob
  1. Create a knob
  2. Make it a supercontrol
  3. Make the four buttons subcontrols of the knob
  4. In Design Properties -> Relationships -> Supercontrols Options, turn BUTTONS ARE SEND ON ONLY to ON


Note
You can use "Hide in Play Mode" in advanced control properties for the controls that you only need in Design Mode.

Try It Out!
Here is the page if you want to try it out for yourself now that you've seen how it's done.


Hangouts, Spaces on OSX

So this frustration has existed since Mavericks:


The answer is in the Hangouts App itself... shut this option off.



Focusrite Saffire MixControl for Scarlett 18i8

I was baffled trying to figure out the MixControl, but it's actually quite amazing. Using Mixes as an intermediate bus point (is that right?) is awesome.

SOME INSTRUCTIONS
Define Your Mixes
You have four mixes that you can use:
  • Left: 1, 3, 5 7
  • Right ones cannot be configured separately: 2, 4, 6, 8
  • You can rename the mix
At the top you're configuring which inputs go into the Mix.

Note that the stereo button "aggregates the 2 mono channels together into a single stereo"
  • Analog Ins
    • from Front Panel: 1 and 2, 3 and 4
    • from Back Panel: 5 and 6, 7 and 8
  • Daw Inputs: 1 to 7 (L) and 2-8 (R)

    These are your DAW output destinations, exactly as labeled.

  • SPDIF and ADAT

Mix Output


  • You can use the Mix Output dropdown to define where the Mix outputs
  • Note for Headphone Outputs on Front Panel
    • Line H/P L and R are called "Output 3" and "Output 4" in the Routing Panel
    • Anlg Out 5 and 6 are called "Output 5" and "Output 6" in the Routing Panel

Routing Panel
  • You can use the routing panel to route individual inputs OR mixes to each output.



  • Monitor Outputs 1 and 2 are in back
  • The ones that show a little headphone are the front panel headphone mixes: 1-2 and 3-4

Dan Says
  • Name your mixes
  • Do not route inputs to outputs directly: instead, use mixes
  • Do not use the Routing Panel: use the checkboxes in the Mix Output to define the routing
  • Note: using the checkboxes in the Mix Output requires setting both L and R channels of the Mix Output, so you have to check all boxes separately.


Also see the Manual Online

Postach.io Embeds

Postachio can embed most of what I need. All the embed codes are here, and note you can use an iFrame directly.

SOME EXAMPLES


Test This: Workflowy

This is a simple iframe, width is 100%, height is 700, frameborder is 0. I'm using http instead of https for the src URL. Still not sure how to escape the HTML so I can show it to you. Using the entities for less than and greater than doesn't work.



Soundcloud



Gist



YouTube



Test This



Test This: Hiding

Using simple HTML comments to hide stuff works well!




Hollow Circle with CoreDrawing and Swift

Thanks to Paintcode and a bit of thinking on my part, I was able to put my one-method-to-rule-them-all together. It draws a variable-size donut with a variable angle. The drawShadow bit is still in progress.