Apple states that nearly 1,000 fraudulent reviews were detected — and that they’d given the developer notice and had tried to resolve the issue with him.
If this is true, then it would be hard to say that Apple has done anything wrong. In fact, we want Apple to notice fraudulent reviews (since they harm consumers and other developers), get them removed, and work things out with the developer.
I don’t know what’s true here. It wouldn’t be right for Apple to make all the evidence public, and it wouldn’t be right for Apple to publish their correspondence with him. So it’s likely we won’t ever know more than we do right now.
Apple’s statement is consistent with Apple’s doing the right thing, though. There’s a very good chance that they are.
While Apple’s culture of opacity continues to bother me — enough so that I won’t put any of my own apps on an app store — I can’t say for sure that this is a case where they’ve mistreated a developer.
(Update Oct. 10: see Dash and Apple’s statement.)
Apple has a judicial system — that is, they have a system where they make judgments and enforce penalties. It’s not a criminal judicial system, and so the state and federal laws that govern that system don’t apply.
Apple is allowed to run this system however they want to. And we can’t see in, so we don’t know how it runs.
But we have learned — in the case of Dash — that one of the features of the system is that Apple may accuse a developer of fraud, not provide any evidence to the developer, and then remove that developer’s apps, with no appeal allowed.
While this is legal, and within Apple’s rights, it’s not what we’ve come to expect from a moral judicial system. No matter what the context, we expect that the accused see the evidence against them, we expect avenues for appeal to be made available, and we expect proportional penalties.
Otherwise, here’s what happens: if you’re well-known enough and have a good app, other people will raise a fuss on Twitter and on blogs and possibly in the press, and maybe something will happen. Maybe.
That sucks. That’s a middle-ages way of handling things.
Apple is allowed to run it that way, if they want to. Of course. They own it.
But any adult would expect the same basic morality that people accused of crimes get: that is, again, the right to see the evidence against them, an avenue of appeal, and proportional penalties.
In the meantime, it’s our job to presume innocence in the absence of evidence. This is also a moral issue, and it’s true even if you’ve never heard of the developer.
I don’t know if it’s true that Apple’s new campus will be mostly open floors with few offices. But it is true that I could never work without my own office.
Like almost everybody at Omni, I have my own office. We also have lounges with chairs and sofas, and some people work in the lounges some or all of the time. Everyone’s different.
Here’s why I work in an office: when I’m around other people — it doesn’t matter who they are — I feel a constant low-simmering level of anxiety. And I find it extremely difficult to be productive when I feel any level of anxiety at all.
I’m a nerd, and this is something a good number of nerds put up with. It doesn’t go away over time.
When people who decide on workspaces for programmers don’t understand this, I wonder if they understand programmers.
Sound Off is raising money for childcare at Affect, a new conference in Portland. The goal is to pay for two childcare providers by raising $2400.
Here’s the thing: everybody who wants to should be able to go to conferences. Everybody should be able to learn things, meet new people, and help other people.
Just because you have a child who needs care shouldn’t disqualify you. Simple as that.
Affect is a “2-day event about the work, culture, and design of social change.”
I’ve been using Xcode’s new memory graph debugger for just about a day, so I don’t have a ton to share here, but I do have a few things.
To start, hit the rotated Sleestak-fingers button in the debugger. It’s between the Cyberman button (view debugger) and paper airplane (location simulator). In other words: it’s not in Instruments. It’s in Xcode.
Turn off zombies. If your scheme has zombies on, you’re going to get a bunch of extra noise. (Tip: keep zombies off in general until you need them.)
Turn on Malloc Stack Logging in Diagnostics in your scheme. (I think I have it right that this needs to be on in order for the memory graph debugger to show backtraces.)
Open the right-hand sidebar in Xcode. Clicking on an object shows its class, hierarchy, and backtrace.
Lines between objects have a label. A line represents a reference. Click on the label to see if the reference is strong or weak or unknown and what the source and destination are.
Don’t click on anything where the name looks something like
MagicOb(something like that). It crashes Xcode for me every time.
Click on the circle-with-double-arrows to expand a tree. To unexpand, click again in that same spot. (The arrows point inward now instead of outward.) However, there’s a bug where sometimes this disappears. Select something else in the left-hand sidebar and then come back, and the collapse arrows should appear.
In the left-hand sidebar, look for the purple icon with the ! inside. These indicate possible problems.
However, most problems aren’t detected. It’s up to you go through and see what’s hanging around that should not be.
I’ve fixed two bugs using the memory graph debugger, and I saved a bunch of time in both occasions. It’s probably worth telling about them as a reminder of the kinds of problems you can run into.
An NSNotification observer was set up using a block — which is something I myself don’t do, since it litters an init or viewDidLoad with extraneous code and since it’s dangerous.
It’s dangerous because, unless you remember to be careful, it can capture a strong reference to self, and then that object is never going to go away. I don’t like APIs that require the developer to remember extra things like this.
And, sure enough, this was one of those cases.
The tipoff was in the memory graph debugger: the reference was labelled as “capture,” which let me know there was a block doing a capture, and it was then pretty quick to find out where.
(See also, from 2015: How Not to Crash #3: NSNotification.)
View controller / view retain cycle
There’s a general rule of programming that says objects should know about their children but not about their parents.
However, sometimes a view needs to know about its view controller. This is less than ideal, but sometimes it’s the least-bad option. (Well… I’m skeptical — but it happens, and we ship great apps, so there ya go.)
The related rule of programming says that if a child knows about its parent, it still can’t hold a strong reference to its parent.
That’s what was happening here: a view was retaining its view controller. The simple fix was to make that a weak property.
And, again, the memory graph debugger took me right to this. I could see what was happening inside the app in a way I never could before.
It’s marvelous. You should use it.
Adam Rush interviewed me for raywenderlich.com.
Adam asks me about public speaking, and I reply that I have the goal of “making it seem like nothing, as if I just got up and started talking to you” — which is more of a peek into my head than it sounds like.
I remind myself to make it seem like nothing many times per day. Not just with public speaking but especially with writing words and apps.
I think I mean a few different things by that. Don’t be show-offy. Don’t be self-conscious. Don’t do fancy or clever things just because I can. Make it look easy and effortless.
I don’t claim that I succeed at all this — just that that’s my aesthetic mantra.
I was a guest on the Supertop podcast a couple weeks ago, and I spaced on linking to it.
Here’s the podcast. It’s made by the fine folks who make Unread and Castro. (I’m an Unread user. It’s in my iPhone’s dock, even.)
(I’d check out Castro except that I actually don’t listen to podcasts on my iPhone. I listen to them at work via my iTunes on my Mac. Which is dreadfully weird of me, I know.)
One way to explain Donald Trump is to think of him as a fourth-grader who desperately wants the approval of the cool kids. He wants to be in their club.
And then, when he feels like he doesn’t get that approval and respect and an invitation to join in — whether the club is Manhattan society, the billionaire’s club, the serious politicians club — he feels like that approval has been withheld unfairly. The cool kids have it rigged against him.
Well, in the face of that unfairness, retaliation against the cool kids by any means necessary — any bullshit and lies whatsoever — is completely justified. They’re evil, and he’s better than them, and whatever it takes to prove that is within bounds.
(One well-trodden route here is the populist option, as if to say, “Those cool kids all think they’re cool, but real people know better. They know I’m the cool one.”)
Children do this sometimes, and it’s awful, and they learn and they grow up.
When adults do it, it’s because they’re psychos.
Some random notes on my secret project Mac app…
* * *
It’s very close to what I call the Minimally Usable Milestone (MUM). That doesn’t mean all the features are all there — or even that they’re all designed — but that you could use the app for its main purpose, if you don’t mind all the unfinished parts.
This is pretty exciting for me. What this means is that the app has good bones, and now it’s a matter of implementing commands, doing some side windows, that kind of thing. It’s still a ton of work, but it’s rewarding in a specific way: every bit of progress is something I can see and use. Up until now it’s mostly been programming-by-faith.
The app has taken a long time to get to this point for a few reasons. One is that I was working on two apps at the same time. I realized that it wasn’t realistic to do two — so I picked the one I wanted to do the most.
Another is that I work in bits and pieces — 15 minutes here and there, and when I’m lucky a few hours in a row on the weekend. As long as the work is steady I don’t lose context — and even 15 minutes a day adds up after a while (especially as you consider that some of the work is thinking work that happens in the shower, on the bus, and so on).
A third is that I have the luxury of shipping whenever, which means my process goes like this: write the code to understand the problem, then write it again now that I understand it. It’s not fast, but I do it this way because it’s super-important to me that I don’t have to do major surgery later. The bones, the foundation of the app, should need only minimal attention after 1.0.
* * *
I don’t know when the beta will be. I don’t know if it will be public or not. But it won’t go into beta until 1) there are no known crashing bugs, 2) there are no known bugs, and 3) it’s fast. (Of those three, the hard one is really #2.)
However, there will be testers who see it before it hits beta. I like early feedback. But even that is still a ways away.
* * *
All of the code at the app level is in Swift. There are about 10 frameworks (modules) that the app uses: some could conceivably be used in other apps, and others are app-specific. The oldest of these still has a bunch of Objective-C code, while newer modules are in Swift. It’s rare that I write a new line of Objective-C.
I like not just writing modular code but actually enforcing that by using actual modules. Though some modules may depend on lower-level modules, they’re each otherwise self-contained, with their own tests and so on. I like to be able to focus: I select the module in that popup in the Xcode toolbar, and then just work on it and forget about everything else.
* * *
I’ve found a simple organization pattern that I like for my Swift code.
- Properties at the top.
- Init methods
- Public or internal methods.
- Then a
private extension. The public/internal methods can see into the extension, but nothing else can. (This way I never have to mark an individual func as private.)
I also make heavy use of
// MARK: Whatever for organization.
I do not make separate extensions for protocol conformance methods. I tried it and it felt too busy. Instead I just have public/internal and then the private extension.
I also mark things as
final all the damn time. Subclasses are the devil’s classes. I’m a big fan of protocol-oriented-programming.
And: my methods tend to be small. This is probably a function of my available time — I break things into smaller chunks, because I only have time for a small chunk. It’s probably also a function of my having to enlarge my font size in Xcode. Something in my brain responds to the actual physical on-screen size and not the number of lines of code.
* * *
I keep the app to-do list in OmniOutliner (which I work on at my day job), since app to-do lists are hierarchical. I’ve been using an outliner for this purpose since the ’90s, and OmniOutliner specifically for probably more than ten years. I have no idea if anybody else does this, but for me it works great.
I will use a bug tracker later, of course, but for now there’s no need. A big flat list would be unwieldy at this point. I need to see the structure of what needs to be done, and I need to expand and collapse so I can focus. (Obviously OmniFocus might also be good for this purpose.)
I use OmniOutliner very simply. Hide the toolbar. Hide the inspector. One column only. No status checkbox — I just delete lines as they’re completed (because otherwise they add noise).
* * *
Next up on the Secret Project Diary — I’m not sure when — I plan to write about the app I’m not doing. The one that got away.
I spent hours on this. This post exists for anybody Googling this particular problem.
Here’s the issue: I have a mixed Objective-C and Swift app.
I have a Swift class that needs to observe a Notification (aka NSNotification) posted in Objective-C code. The notification name is defined in a .h and .m file:
extern NSString *SomethingHappenedNotification;
NSString *SomethingHappenedNotification = @"SomethingHappenedNotification";
In Swift I tried a number of permutations, trying to get the notification name correct, including using
Notification.Name(rawValue: SomethingHappenedNotification) and similar. Each try resulted in a compile error.
The answer came from Tim Ekl (privately) and Jordan Rose (on Twitter) independently at the same time:
Ah, the word "Notification" is stripped from the name as redundant, so it becomes Notification .Name.Some (or just .Some).
In other words, the syntax for adding an observer looks like this:
NotificationCenter.default.addObserver(self, selector: #selector(someSelector(_:)), name: .SomethingHappened, object: nil)
Note that the Objective-C name
SomethingHappenedNotification becomes just
.SomethingHappened in Swift, and it’s automatically a Notification.Name.
It seems obvious now! But it wasn’t (at least for me). So: if I saved you some time today, then go be nice to somebody. :)