Updating SceneKit WWDC 2013 slides for Xcode 7

With recent changes to the AppKit headers, you need to make a couple of changes to the WWDC 2013 SceneKit Slides code to get it to build. There are some cool examples in that year’s talk/sample code that didn’t make it into 2014’s.

In the ASCPresentationViewController, switch from a method declaration for the -view superclass override to a property in the header, and specify @dynamic for that property in the implementation.

@property (strong) SCNView *view;

//- (SCNView *)view;

 

@dynamic view;

//- (SCNView *)view {

//    return (SCNView *)[super view];

//}

I also updated the .xcodeproj to current standards, and fixed a couple of int/NSInteger/NSUinteger mismatches.

I’ve submitted it to Apple as rdar://23829155. In the meantime, here are the diffs:

diff --git a/SceneKit_Slides_WWDC2013/Scene Kit Session WWDC 2013/Sources/ASCPresentationViewController.h b/SceneKit_Slides_WWDC2013/Scene Kit Session WWDC 2013/Sources/ASCPresentationViewController.h
index 7d66316..bb0e54f 100644
--- a/SceneKit_Slides_WWDC2013/Scene Kit Session WWDC 2013/Sources/ASCPresentationViewController.h
+++ b/SceneKit_Slides_WWDC2013/Scene Kit Session WWDC 2013/Sources/ASCPresentationViewController.h
@@ -55,7 +55,9 @@
@property (weak) id <ASCPresentationDelegate> delegate;

// View controller
-- (SCNView *)view;
+// Hal Mueller change: make this a property, @dynamic, to compile under Xcode 7/10.11 SDK
+@property (strong) SCNView *view;
+//- (SCNView *)view;
- (id)initWithContentsOfFile:(NSString *)path;

// Presentation outline
diff --git a/SceneKit_Slides_WWDC2013/Scene Kit Session WWDC 2013/Sources/ASCPresentationViewController.m b/SceneKit_Slides_WWDC2013/Scene Kit Session WWDC 2013/Sources/ASCPresentationViewController.m
index 46d9e00..1c914b6 100644
--- a/SceneKit_Slides_WWDC2013/Scene Kit Session WWDC 2013/Sources/ASCPresentationViewController.m
+++ b/SceneKit_Slides_WWDC2013/Scene Kit Session WWDC 2013/Sources/ASCPresentationViewController.m
@@ -91,9 +91,10 @@ typedef NS_ENUM(NSUInteger, ASCLightName) {

#pragma mark - View controller

-- (SCNView *)view {
- return (SCNView *)[super view];
-}
+@dynamic view;
+//- (SCNView *)view {
+// return (SCNView *)[super view];
+//}

- (id)initWithContentsOfFile:(NSString *)path {
if ((self = [super initWithNibName:nil bundle:nil])) {
@@ -660,12 +661,12 @@ typedef NS_ENUM(NSUInteger, ASCLightName) {

#pragma mark - Misc

-CGFloat _lightSaturationAtSlideIndex(int index) {
+CGFloat _lightSaturationAtSlideIndex(NSInteger index) {
if (index >= 4) return 0.1; // colored
return 0; // black and white
}

-CGFloat _lightHueAtSlideIndex(int index) {
+CGFloat _lightHueAtSlideIndex(NSInteger index) {
if (index == 4) return 0; // red
if (index == 5) return 200/360.0; // blue
return 0; // black and white
diff --git a/SceneKit_Slides_WWDC2013/Scene Kit Session WWDC 2013/Sources/ASCSlideTextManager.m b/SceneKit_Slides_WWDC2013/Scene Kit Session WWDC 2013/Sources/ASCSlideTextManager.m
index ce17c6f..cdc12a4 100644
--- a/SceneKit_Slides_WWDC2013/Scene Kit Session WWDC 2013/Sources/ASCSlideTextManager.m
+++ b/SceneKit_Slides_WWDC2013/Scene Kit Session WWDC 2013/Sources/ASCSlideTextManager.m
@@ -71,7 +71,7 @@ static CGFloat const TEXT_FLATNESS = 0.4;
return self;
}

-- (NSColor *)colorForTextType:(ASCTextType)type level:(int)level {
+- (NSColor *)colorForTextType:(ASCTextType)type level:(NSUInteger)level {
switch (type) {
case ASCTextTypeSubtitle:
return [NSColor colorWithDeviceRed:160/255.0 green:182/255.0 blue:203/255.0 alpha:1];

Options for Full Text Search in Core Data

Last weekend Chris Olds and I were discussing text search engines, and in particular how to take advantage of them to speed up searches of free-form text in Core Data. Here’s a summary of what we found. I haven’t tested or implemented any of these ideas. This is simply a summary of what’s out there.

I’m not including techniques that deal with fast searches of short text fields: normalizing your query strings and searchable text, using case-insensitive searches, etc. That’s all well documented by Apple and in the usual Core Data reference books.

I did run across one very cool article outlining a profiling method I hadn’t ever seen before. The Art & Logic Blog goes one step further in the typical use of com.apple.CoreData.SQLDebug. Take advantage of the fact that you have SQLite installed on your Mac! You can paste the SQL query being logged by your iOS app into SQLite on your Mac, and use the EXPLAIN QUERY command there to understand the search plan.

Full Text Search

Full text search (FTS) is about finding search terms within large bodies of text. This is different from matching someone’s last name to the lastName attribute in a Core Data entity. Imagine instead that your Core Data database contains notes, or newspaper articles, or patent descriptions, or travel resort reviews, and you want to search within the text of those articles. The brute force method is to scan all of the text of each article, searching for matches to the search term. That takes a very long time, and doesn’t always give you the results you want.

Ideally, your FTS within Core Data will respond as quickly as Google or Bing does when you enter a search term. The results will be ranked by relevance, The search will handle word stemming correctly: if I enter a search for “lodge”, I probably want to see results containing “lodges” or “lodging”, too. Core Data does not handle any of these need.

Roll Your Own

Michael Heyeck wrote an 8 part series of blog articles describing how to build your own FTS capability directly within Core Data, using only Core Data tools and constructs. It’s a very comprehensive series, and it’s a shame it isn’t more widely known. He doesn’t just teach you how to do FTS in Core Data. He also shows you how to read and understand the SQL queries that are generated on your behalf, and how to modify your NSPredicates and data model design to make the queries fast.

The series includes source code for a Notes application with FTS, under BSD license.

Search Kit

When you type something into the Spotlight search bar on your Mac, you’re using FTS. Mac OS X has already built an FTS index of the files on your system, and queries that index. Search Kit is the Foundation framework that Apple uses to deliver those search results, and it’s available to you too. The catch? It’s Mac only, and not integrated into Core Data.

When we were chatting, I mentioned to Chris that Search Kit would make a terrific NSHipster topic. The next day, that’s what happened! The NSHipster article also summarizes the technical issues in Full Text Search nicely.

Indragie Karunaratne has a project on Github that uses Search Kit to back Core Data searches. I’ve only read over the source, and haven’t tried it, but it looks solid. His approach is to build a Search Kit index that returns NSManagedObjectIDs of Core Data objects matching a particular full text search.

Commercial Library

Locayta makes their FTS mobile search engine available to iOS developers: free for non-commercial use, $1000 per commercial app. It’s not integrated with Core Data. An approach similar to the one Indragie Karunaratne took with Search Kit integration would probably work, though.

Hackery

The backing store most commonly used with Core Data, SQLite, includes FTS support. It’s just not exposed in any Core Data API (at least, not as of iOS 6.1).

Wolfert de Kraker describes a technique for using the SQLite FTS4 engine simultaneously with Core Data. It involves creating a Virtual Table within the same SQLite database that Core Data uses. Then he uses FMDB to create a search method which uses the FTS4 search to respond to UISearchDisplayController delegate calls. NSManagedObjectIDs are returned as the raw SQLite search results, and then Core Data retrieves these objects.

This 2010 Stack Overflow answer describes a similar approach. A different answer a few months later makes a sideways variation: instead of storing NSManagedObjectIDs in the shadow SQLite table, store SQLite row IDs as Core Data attributes.

These solutions included a custom copy of SQLite in their projects. Although they are iOS projects, I see no reason you couldn’t use the same approach on OS X.

I found two other blog posts describing other implementations of this approach, one from Regular Rate & Rhythm and one from Long Weekend Mobile, both from 2010.

I have to say that it makes me very nervous to think of mucking around in Core Data’s SQLite file. Call me superstitious.

Open Source FTS

We looked at two long-established open source FTS engines, Xapian and Lucene.

Lucene is a Java-based search engine, part of the Apache project. A port to Gnustep, Lucene Kit, was begun in 2005 and seems to have languished for a while. The most current version I found was https://github.com/zbowling/LuceneKit, which was active as recently as 2012.

Xapian is a C++ search engine, and the one that Chris uses in his production code. It is presently licensed under GPL, which would make for some complications if you were to include it  in an iOS project. There was some mention on the Xapian forum of writing an Objective-C binding. The conclusion was that it should be straightforward, but that no one has done it yet.

 

iOS 6 maps are better for applications

I’m very happy to see the new look brought to iOS maps via Apple’s switch to an in-house solution. Much noise has been made about the errors and the lost detail. I think the lost detail is an improvement.

The other night, a friend and I did a side-by-side comparison of my app HistoryPointer on iOS 6 and iOS 5 (bonus points if you can figure out where we were sitting). HistoryPointer displays a bunch of points of interest on a map (further details aren’t especially relevant). On the top is the HistoryPointer running on iOS 6 (with maps from Apple); on the bottom it’s running under iOS 5 (with maps from Google).

IMG 1009

Photo

To my eye, the iOS 6 version is much easier to read when you’re looking for the overlaid points of interest.There are fewer labels on the Apple map than on the Google map. The color scheme is less intrusive. There are some problems with label placement on the Apple map: State Route 99, for example, near the right-hand edge, is missing its street name (Aurora Avenue) on the Apple version even though other less important streets are labeled.

I see lots of potential in this move. There’s a control panel to adjust label size on map views. There’s obviously some dynamic label generation and pruning going on. I like the prospect of enhancements to MapKit to allow programmatic control of many of these parameters. Imagine what you could do with API to do these things:

  • adjust the size of (or omit!) certain kinds of labels.
  • control the orientation of the map to maximize the use of the screen space. North doesn’t always have to be up.
  • apply a custom color scheme, so that your overlaid data is easier to read.
  • omit certain kinds of features. Not everyone wants driving directions. There are many applications where the streets, highways, and manmade features are simply clutter.

Think about the possibilities. How would you like your app’s MKMapView to be different? File those radars! I have several of my own in mind.

Brent Simmons: Notes from Mac programming class guest lecture

Last week, Brent Simmons was kind enough to visit the Mac programming class I’m teaching. He’s posted the notes from his talk online:

Notes from Mac programming class guest lecture: “The idea behind the lecture was to talk about what makes a great Mac app. I took that as an excuse to talk about everything from work habits to UI to marketing. “

(Via Brent Simmons inessential.com.)

iPhone responsiveness and memory usage

From fellow Big Nerd Ranch alum Jonathan Saggau:

iPhone responsiveness and memory usage: “I recently answered a question on a private mailing list about how to make a network – based (XML parsing and such) iPhone application more responsive. I’ve been encouraged to post it here by a few folks (Thanks, guys! You know who you are.). So, I figure ‘why not?’ Here you go. (Slightly modified)

(Via Jonathan Saggau’s Blog.)

A very simple UIScrollView demo

I was tinkering with UIScrollView a while back, just doing a simple demo to understand how to use the API. Here’s a summary of what I learned (some with the help of Apple DTS).

I wanted to get the pinch scrolling to work so that I could pan around an image. For my sample graphic, I grabbed the Quartz sample code from another Apple sample, just to have something to draw. I dropped a UIScrollView onto my nib, and defined (in code) a StarView class to draw those graphics. At launch time (in applicationDidFinishLaunching), I create an instance of StarView and drop it onto the UISCrollView:

	starView = [[StarView alloc] initWithFrame:CGRectMake(0., 90., 500., 500.)];
	starViewScale = 1.0;
	[scrollView addSubview:starView];

02 -0700-1.png

When I zoomed in, the image was very blurry. It was being drawn at the same pixel resolution used when zoomed out:

39 -0700-1.png

I was missing a couple of key points. First, use a CATiledLayer as the layer class for my Starview:

@implementation StarView

+(Class)layerClass
{
	return [CATiledLayer class];
}

Second, I need to trigger a redraw of the image when the zoom level changes. Do this with levelsOfDetailBias and levelsOfDetail:

- (id)initWithFrame:(CGRect)frame {
	if (self = [super initWithFrame:frame]) {
		CATiledLayer *animLayer = (CATiledLayer *) self.layer;
		animLayer.levelsOfDetailBias = 4;
		animLayer.levelsOfDetail = 4;
	}
	return self;
}

Here’s the nice crisp rendering after those changes:

43 -0700-1.png

You can download the demo at http://www.mobilegeographics.com/dev/StarZoom2.zip. Note that that code is still not great. The zoom limits are fixed, but should be dynamic. And the view is completely redrawn each time a small subrectangle is requested, which defeats the purpose of the tiling, at least as far as speed goes.

You’ll also see a bug in my line width control:

	// Draw them with a 2.0 stroke width so they are a bit more visible.
	CGFloat width = 2. / [(AppDelegate *)[[UIApplication sharedApplication] delegate] starViewScale];

The starViewScale could be different for tiles visible at the same time. The line width should actually be pegged to the scale’s levelOfDetail/levelOfDetail bias. It should return discrete values based on powers of 2, and not be continuous as it is in the demo.

Finally, other authors have noted that there’s no way to control the scale of the view when it appears. The scale factor is always 1.0. I’ve tried setting the UIScrollView’s min/max zoom scale to a range that excludes 1.0, like this (from applicationDidFinishLaunching):

	scrollView.maximumZoomScale = 50.;
	scrollView.minimumZoomScale = 5.;

When I do that, the view appears with scale factor 1.0 and then jumps to 5.0 when I do the first pinch zoom. If you need to control the scale factor, you’ll have to use a shadow scale factor yourself, and then rescale everything by that shadow scale to accommodate a zoom scale of 1.0 on each view initialization.

Enjoy the sample.