Quantcast
Channel: NSHipster
Viewing all 382 articles
Browse latest View live

NSHipster Quiz #1

$
0
0

On April 9th, the first-ever NSHipster Pub Quiz was held in Berlin. Think of your traditional pub quiz crossed with "Stump the Experts", with questions about things that you know and care about: computers, programming, Apple trivia—that sort of thing. The event was hosted by UIKonf, and made possible by its organizers Chris Eidhof, Matt Patterson, and Peter Bihr. Thanks again to Chris, Matt, and Peter, and everyone who came out to make it such an amazing event.

All told, a whopping 50-some folks came out, composing a dozen or so teams of up to 6 people, with names such as "NSBeep", "alloc] win_it]", & "- Bug Fixes / - Performance Improvements". At the end of the evening, it was the CodeKollectiv team that claimed top prize, with a score of 30pts.

Everyone had such a great time, that we'll be doing it again:

NSHipster will be hosting another trivia night during the week of WWDC 2013 in San Francisco. More details to come... you know, as soon as Apple actually announces the dates for WWDC.

Sign up here to be the first to be notified about Trivia Night. The event is sure to fill up quickly, so keep on the lookout for further announcements.


In the meantime, enjoy these questions from the Berlin Pub Quiz.

Here are the rules to play along at home:

  • There are 4 Rounds, with 10 questions each
  • Record answers on a separate sheet of paper
  • Each correct answer to a question gets you 1 point
  • Play with up to 5 friends for maximum enjoyment
  • Don't be lame and look things up on the internet or in Xcode

Round 1: General Knowledge

  1. What does NS stand for?
  2. When Steve Jobs introduced the iPhone, he made a prank call to Starbucks. How many lattés did he order to-go? a. 3000 b. 4000 c. 6000
  3. NSOperation has 4 properties used as keypaths for operation object states. What are they?
  4. On your answer sheet, draw a UITableViewCell with UITableViewCellStyleValue2.
  5. Which UIKit protocol contains the method –tableView:heightForRowAtIndexPath:?
  6. What is the storage type of BOOL? (i.e. typedef equivalent)
  7. When was the Unix Epoch? Hint: NSDate has an initializer referencing this.
  8. What is the current version of Xcode?
  9. What was the first article written on NSHipster?
  10. How many apps were on on the home screen of the first iPhone?

Answers for Round 1

  1. NeXTSTEP
  2. 4000
  3. isReady, isExecuting, isFinished, isCancelled
  4. textLabel detailTextLabel
  5. UITableViewDelegate
  6. signed char
  7. Midnight UTC, 1 January 1970
  8. 4.6.2 (4H1003)
  9. NSIndexSet
  10. 16

Round 2: APIs

You will be given the name of the class, and the description of the property or method from the documentation. You need to tell me the name of that method or property.

  1. UIView: "A flag used to determine how a view lays out its content when its bounds change."
  2. UIAccessibility: "A brief description of the result of performing an action on the accessibility element, in a localized string."
  3. UIColor: "Returns a color object whose RGB values are 0.0, 1.0, and 1.0 and whose alpha value is 1.0."
  4. UIAlertView: "Sent to the delegate when the user clicks a button on an alert view."
  5. UIButton: "A Boolean value that determines whether tapping the button causes it to glow."
  6. UITableView: "Reloads the specified rows using a certain animation effect."
  7. UITableViewDataSource: "Tells the data source to return the number of rows in a given section of a table view."
  8. UIWebView: "Sets the main page content and base URL."
  9. UIGestureRecognizer: "Sent to the receiver when one or more fingers touch down in the associated view."
  10. UIDictationPhrase: "The most likely textual interpretation of a dictated phrase."

Answers for Round 2

  1. @contentMode
  2. @accessibilityHint
  3. +cyanColor
  4. -alertView:clickedButtonAtIndex:
  5. @showsTouchWhenHighlighted
  6. -reloadRowsAtIndexPaths:withRowAnimation:
  7. -tableView:numberOfRowsInSection:
  8. -loadHTMLString:baseURL:
  9. -touchesBegan:withEvent:
  10. @text

Round 3: Picture Round

  • 1. What is this?

Question 1

  • 2. What is this?

Question 2

  • 3. What is this?

Question 3

  • 4. What is this?

Question 4

  • 5. WTF is this?

Question 5

  • 6. Who is this?

Question 6

  • 7. Who is this?

Question 7

  • 8. Who is this?

Question 8

  • 9. Who is this?

Question 9

  • 10. In this photo, Bill Gates & Steve Jobs are being interviewed at the D5 conference in 2007 by a man and a woman just off-screen to the left. Who are they? (One point for each person)

Question 10


Answers for Round 3

  1. Apple I
  2. Apple eMac
  3. Apple Bandai Pippin
  4. Apple QuickTake
  5. New Proposed Apple Campus / "Mothership"
  6. Sir Jonathan "Jony" Ive
  7. Scott Forstall
  8. Bob Mansfield
  9. Susan Kare
  10. Kara Swisher & Walt Mossberg

Round 4: Name That Framework!

For each question, a list of three classes from the same framework have been listed without their two-letter namespace prefix. Name the framework that they all belong to!


  1. Color List, Matrix, Sound
  2. Composition, URL Asset, Capture Session
  3. Enclosure, Author, Feed
  4. Geocoder, Location, Region
  5. Merge Policy, Mapping Model, Incremental Store
  6. Analysis, Summary, Search
  7. Record, Person, MultiValue
  8. View, View Controller, Skybox Effect
  9. Central Manager, Descriptor, Peripheral Delegate
  10. Filter, Face Feature, Vector

Answers for Round 4

  1. App Kit
  2. AV Foundation
  3. Publication Subscription
  4. Core Location
  5. Core Data
  6. Search Kit
  7. Address Book
  8. GLKit
  9. Core Bluetooth
  10. Core Image

So how did you fare? Tweet out your score to see how you stack up to your peers!

And again, be sure to sign up here to be the first to know about the next NSHipster Pub Quiz to be held in San Francisco during the week WWDC. Hope to see you there!


MKLocalSearch

$
0
0

Look, we get it: people are upset about Apple Maps.

What should have been a crowning feature for iOS 6 became the subject of an official apology due to its embarrassing inaccuracies and the removal of public transportation information.

In all of the hubbub of torch burning and pitchfork raising, you may have completely missed a slew of additions to MapKit in iOS 6.1. Namely: MKLocalSearch.


MKLocalSearch allows developers to find nearby points of interest within a geographic region.

But before you go and rush into using MKLocalSearch, you'll have to know a few things about its friends. You see, MKLocalSearch has its functionality divided across MKLocalSearchRequest and MKLocalSearchResponse:

MKLocalSearchRequest*request=[[MKLocalSearchRequestalloc]init];request.naturalLanguageQuery=@"Restaurants";request.region=mapView.region;MKLocalSearch*search=[[MKLocalSearchalloc]initWithRequest:request];[searchstartWithCompletionHandler:^(MKLocalSearchResponse*response,NSError*error){NSLog(@"Map Items: %@",response.mapItems);}];

MKLocalSearchRequest takes a naturalLanguageQuery, such as "Taxidermists", and an optional bounding geographic region to constrain results. In practice, the region is usually passed from an MKMapView.

MKLocalSearchResponse is returned in the eponymous block handler of MKLocalSearch -startWithCompletionHandler:, and returns an array of MKMapItem objects. Each MKMapItem contains information like name, phoneNumber, url and address information via the placemark property.

If you keep a reference to your MKLocalSearch object, you can optionally -cancel the request, such as on -viewWillDisappear: or the like.

Where's The Beef?

MKLocalSearch is a relatively straight-forward API (albeit perhaps worse off for eschewing a simpler single-class interface)... so what's the big deal?

API limits. Or rather, the lack of them. Let me explain:

Perhaps the most counter-intuitive things about MapKit in iOS 6 is that it's still widely used. Nevermind the "Apple Maps-gate" melodrama, MapKit, even with the introduction of impressive iOS mapping SDKs from Google and MapBox, developers are still using MapKit.

Part of this may be aesthetics, but a lot has to do with a certain level of home-field advantage, too. Because of MapKit's close ties to UIKit, it can be customized more easily and more extensively by third-party developers.

This brings us back to API call limits. When developing with another mapping SDK or geospatial webservice, licensing terms are almost necessarily going to be more limited than what Apple makes available for free. Free is a tough price to beat, and it's all-the-more compelling because there is no worry of going over API limits for tile loading or API calls.

Where Do We Go From Here?

With the introduction of MKLocalSearch, one can be hopeful of more first-party webservices being exposed in a similar fashion. Expanded geospatial search? Or perhaps first-party APIs to iTunes media streaming?

One can dare to dream, after all...


MKLocalSearch provides a simple way to find local points of interest. Because of its no-hassle webservice integration and tight integration with MapKit, any location-based app would do well to take advantage of it.

GPUImage

$
0
0

Here at NSHipster, we're all about diving into the darker corners of Objective-C to learn something new about the systems we interact with every day. Often, this means sifting through Apple frameworks or language features (it is, after all, a lot of what it means to work in Objective-C). However, on occasion, it's nice to take a look to the burgeoning landscape of third-party libraries and frameworks (and there are some truly remarkable ones) for a glimpse of what's new and great outside of Cupertino.

This week, we'll be taking a look at one of the most impressive open source projects you'll find: GPUImage. Buckle up, NSHipsters—if you're not careful, you may well end up creating a camera app by the end of the article.


GPUImage is a BSD-licensed iOS library written by Brad Larson that lets you apply GPU-accelerated filters and other effects to images, live camera video, and movies.

GPU vs. CPU

Every iPhone ships with two processors: a CPU, or Central Processing Unit and a GPU, or Graphics Processing Unit. Each processor has its own strengths, and modern chip architecture (like in the iPhone's A4) integrate the CPU and GPU onto the same physical die.

When you write C or Objective-C code in Xcode, you're generating instructions that will be handled almost exclusively by the CPU. The GPU, by contrast, is a specialized chip that is especially well-suited for computation that can be split out into many small, independent operations, such as graphics rendering. The kinds of instructions understood by the GPU are quite different from that of the CPU, and as such, we write this code in a different language: OpenGL (or specifically, OpenGL ES on the iPhone & iPad).

Check out Jeff LaMarche's GLProgram OpenGL ES 2.0 book for a great introduction to OpenGL ES and the rendering pipeline.

Comparing the performance of GPU-based rendering to CPU rendering for something like video, the differences are staggering:

CPU vs. GPU Frame-Rate (Larger FPS is Better)
CalculationGPU FPSCPU FPSΔ
Thresholding ⨉ 160.004.2114.3⨉
Thresholding ⨉ 233.632.3614.3⨉
Thresholding ⨉ 1001.450.0528.7⨉

"Oh, so it's like Instagram?"

Let me put it this way:

Instagram : GPUImage :: Disposable Camera : NASA Space Optics Manufacturing Center

To put it another way, within GPUImage's APIs lay thousands of camera apps, just waiting for the right combination of filters and a little spark of imagination.

Here's a table of the 125 (!) filters that come with GPUImage:

Color AdjustmentsImage ProcessingBlending ModesVisual Effects
  • Brightness Filter
  • Exposure Filter
  • Contrast Filter
  • Saturation Filter
  • Gamma Filter
  • Levels Filter
  • Color Matrix Filter
  • RGB Filter
  • Hue Filter
  • Tone Curve Filter
  • Highlight Shadow Filter
  • Lookup Filter
  • Amatorka Filter
  • Miss Etikate Filter
  • Soft Elegance Filter
  • Color Invert Filter
  • Grayscale Filter
  • Monochrome Filter
  • False Color Filter
  • Haze Filter
  • Sepia Filter
  • Opacity Filter
  • Solid Color Generator
  • Luminance Threshold Filter
  • Adaptive Threshold Filter
  • Average Luminance Threshold Filter
  • Histogram Filter
  • Histogram Generator
  • Average Color
  • Luminosity
  • Chroma Key Filter
  • Transform Filter
  • Crop Filter
  • Lanczos Resampling Filter
  • Sharpen Filter
  • Unsharp Mask Filter
  • Fast Blur Filter
  • Single Component Fast Blur Filter
  • Gaussian Blur Filter
  • Single Component Gaussian Blur Filter
  • Gaussian Selective Blur Filter
  • Gaussian Blur Position Filter
  • Median Filter
  • Bilateral Filter
  • Tilt Shift Filter
  • Box Blur Filter
  • 3x3 Convolution Filter
  • Sobel Edge Detection Filter
  • Threshold Edge Detection Filter
  • Canny Edge Detection Filter
  • Harris Corner Detection Filter
  • Noble Corner Detection Filter
  • Shi-Tomasi Corner Detection Filter
  • Non Maximum Suppression Filter
  • X/Y Derivative Filter
  • Crosshair Generator
  • Dilation Filter
  • RGB Dilation Filter
  • Erosion Filter
  • RGB Erosion Filter
  • Opening Filter
  • RGB Opening Filter
  • Closing Filter
  • RGB Closing Filter
  • Local Binary Pattern Filter
  • Low Pass Filter
  • High Pass Filter
  • Motion Detector
  • Hough Transform Line Detector
  • Line Generator
  • Motion Blur Filter
  • Zoom Blur Filter
  • Chroma Key Blend Filter
  • Dissolve Blend Filter
  • Multiply Blend Filter
  • Add Blend Filter
  • Subtract Blend Filter
  • Divide Blend Filter
  • Overlay Blend Filter
  • Darken Blend Filter
  • Lighten Blend Filter
  • Color Burn Blend Filter
  • Color Dodge Blend Filter
  • Screen Blend Filter
  • Exclusion Blend Filter
  • Difference Blend Filter
  • Hard Light Blend Filter
  • Soft Light Blend Filter
  • Alpha Blend Filter
  • Source Over Blend Filter
  • Color Burn Blend Filter
  • Color Dodge Blend Filter
  • Normal Blend Filter
  • Color Blend Filter
  • Hue Blend Filter
  • Saturation Blend Filter
  • Luminosity Blend Filter
  • Linear Burn Blend Filter
  • Poisson Blend Filter
  • Mask Filter
  • Pixellate Filter
  • Polar Pixellate Filter
  • Polka Dot Filter
  • Halftone Filter
  • Crosshatch Filter
  • Sketch Filter
  • Threshold Sketch Filter
  • Toon Filter
  • Smooth Toon Filter
  • Emboss Filter
  • Posterize Filter
  • Swirl Filter
  • Bulge Distortion Filter
  • Pinch Distortion Filter
  • Stretch Distortion Filter
  • Sphere Refraction Filter
  • Glass Sphere Filter
  • Vignette Filter
  • Kuwahara Filter
  • Kuwahara Radius 3 Filter
  • Perlin Noise Filter
  • CGAColorspace Filter
  • Mosaic Filter
  • JFAVoronoi Filter
  • Voronoi Consumer Filter

Seriously, the Filter Showcase Example App that comes bundled in the repository could easily retail on the AppStore for $3.99, as-is. Add Twitter integration and a few sound effects, and you could bump that up to a respectable $6.99.

Rendering Pipeline

GPUImage is, at its core, an Objective-C abstraction around a rendering pipeline. Source images from the camera, network, or disk are loaded and manipulated according to a chain of filters, and finally outputted either a view, graphics context, or data stream.

For example, images from the video camera could have a Color Levels filter applied to simulate different types of color blindness and displayed in a live view.

GPUImageVideoCamera*videoCamera=[[GPUImageVideoCameraalloc]initWithSessionPreset:AVCaptureSessionPreset640x480cameraPosition:AVCaptureDevicePositionBack];videoCamera.outputImageOrientation=UIInterfaceOrientationPortrait;GPUImageFilter*filter=[[GPUImageLevelsFilteralloc]initWithFragmentShaderFromFile:@"CustomShader"];[filtersetRedMin:0.299gamma:1.0max:1.0minOut:0.0maxOut:1.0];[filtersetGreenMin:0.587gamma:1.0max:1.0minOut:0.0maxOut:1.0];[filtersetBlueMin:0.114gamma:1.0max:1.0minOut:0.0maxOut:1.0];[videoCameraaddTarget:filter];GPUImageView*filteredVideoView=[[GPUImageViewalloc]initWithFrame:self.view.bounds)];[filteraddTarget:filteredVideoView];[self.viewaddSubView:filteredVideoView];[videoCamerastartCameraCapture];

Or, combining various color blending modes, image effects, and adjustments, you could transform still images into something worthy of sharing with your hipster friends (example taken from FilterKit, which is built on GPUImage):

GPUImageFilterGroup*filter=[[FKGPUFilterGroupalloc]init];GPUImageSaturationFilter*saturationFilter=[[GPUImageSaturationFilteralloc]init];[saturationFiltersetSaturation:0.5];GPUImageMonochromeFilter*monochromeFilter=[[GPUImageMonochromeFilteralloc]init];[monochromeFiltersetColor:(GPUVector4){0.0f,0.0f,1.0f,1.0f}];[monochromeFiltersetIntensity:0.2];GPUImageVignetteFilter*vignetteFilter=[[GPUImageVignetteFilteralloc]init];[vignetteFiltersetVignetteEnd:0.7];GPUImageExposureFilter*exposureFilter=[[GPUImageExposureFilteralloc]init];[exposureFiltersetExposure:0.3];[filteraddGPUFilter:exposureFilter];[filteraddGPUFilter:monochromeFilter];[filteraddGPUFilter:saturationFilter];[filteraddGPUFilter:vignetteFilter];

Looking through all of what GPUImage can do, one can't help but get excited. Easy enough to get started immediately (without needing to know anything about OpenGL) yet performant enough to power whatever you dream up. And not just that, but it also comes with a dizzying number of building blocks—all of the color adjustments, blending modes, and visual effects you could ever want (or never knew you needed).

GPUImage is a rare treat for the open source community, and we as Mac & iOS developers are lucky to have it at our disposal. Use it to make something great, and show others the world in a whole new way.

NSCoding / NSKeyedArchiver

$
0
0

Among the most important architectural decisions made when building an app is how to persist data between launches. The question of how, exactly, to re-create the state of the app from the time it was last opened; of how to describe the object graph in such a way that it can be flawlessly reconstructed next time.

On iOS and Mac OS X, Apple provides two options: Core Data or NSKeyedArchiver / NSKeyedUnarchiver (which serializes <NSCoding>-compliant classes to and from a data representation).

Or rather: three, if you include NSURLCache. In the case of a client-server application, having the client load necessary data on each launch is a viable design, especially when combined with a disk-based cache, which allows stored server responses to be returned immediately from matching requests. In practice, some combination of network and object caching is advisable.

When it comes to modeling, querying, traversing and persisting complex object graphs, there is no substitute for Core Data. Core Data is a big hammer, but not every problem is a nail—much less a sufficiently large nail.

A fair and common comparison of Core Data to NSKeyedArchiver might go something like this:

Core DataNSKeyedArchiver
Entity ModelingYesNo
QueryingYesNo
SpeedFastSlow
Serialization FormatSQLite, XML, or NSDataNSData
MigrationsAutomaticManual
Undo ManagerAutomaticManual

Et cetera. In a heads-up, apples to apples comparison, it looks rather one-sided.

...that is, until you look at it from a slightly different perspective:

Core DataNSKeyedArchiver
Persists StateYesYes
Pain in the AssYesNo

By these measure, NSKeyedArchiver becomes a perfectly reasonable choice in certain situations. Not all apps need to query data. Not all apps need automatic migrations. Not all apps work with large or complex object graphs. And even apps that do may have certain components better served by a simpler solution.

This article will look at the how's, when's, and why's of NSKeyedArchiver and NSCoding. And with this understanding, hopefully provide you, dear reader, with the wisdom to choose the best tool for the job.


NSCoding is a simple protocol, with two methods: -initWithCoder: and encodeWithCoder:. Classes that conform to NSCoding can be serialized and deserialized into data that can be either be archived to disk or distributed across a network.

For example:

@interfaceBook : NSObject<NSCoding>@propertyNSString*title;@propertyNSString*author;@propertyNSUIntegerpageCount;@propertyNSSet*categories;@property(getter=isAvailable)BOOLavailable;@end@implementationBook#pragma mark - NSCoding-(id)initWithCoder:(NSCoder*)decoder{self=[superinit];if(!self){returnnil;}self.title=[decoderdecodeObjectForKey:@"title"];self.author=[decoderdecodeObjectForKey:@"author"];self.pageCount=[decoderdecodeIntegerForKey:@"pageCount"];self.categories=[decoderdecodeObjectForKey:@"categories"];self.available=[decoderdecodeBoolForKey:@"available"];returnself;}-(void)encodeWithCoder:(NSCoder*)encoder{[encoderencodeObject:self.titleforKey:@"title"];[encoderencodeObject:self.authorforKey:@"author"];[encoderencodeInteger:self.pageCountforKey:@"pageCount"];[encoderencodeObject:self.categoriesforKey:@"categories"];[encoderencodeBool:[selfisAvailable]forKey:@"available"];}@end

As you can see, NSCoding is mostly boilerplate. Each property is encoded or decoded as an object or type, using the name of the property of as the key each time. (Some developers prefer to define NSString * constants for each keypath, but this is usually unnecessary).

But boilerplate can be a good things sometimes—with direct control over the entire serialization process, it remains flexible to account for things like:

  • Migrations: If a data model changes—such as adding, renaming, or removing a field—it should maintain compatibility with data serialized in the old format. Apple provides some guidelines on how to go about this in "Forward and Backward Compatibility for Keyed Archives".
  • Archiving non-NSCoding-compatible Classes: According to object-oriented design, objects should take responsibility for encoding and decoding to and from a serialization format. However, when a class doesn't come with NSCoding support built in, it may be left up to class that uses it to help out.

One library that aims to cut down the boilerplate of NSCoding is Mantle, from the good folks over at GitHub. If you're looking for more of the conveniences of Core Data modeling with NSCoding, Mantle is definitely worth a look.


Of course, serialization is only one part of the story. Determining where this data will persist is another question. Again, there are two approaches: writing to the local file system and using NSUserDefaults.

File System

NSKeyedArchiver and NSKeyedUnarchiver provide a convenient API to read / write objects directly to / from disk.

An NSCoding-backed table view controller might, for instance, set its collection property from the file manager

Archiving

[NSKeyedArchiverarchiveRootObject:bookstoFile:@"/path/to/archive"];

Unarchiving

[NSKeyedUnarchiverunarchiveObjectWithFile:@"/path/to/archive"];

NSUserDefaults

Each app has its own database of user preferences, which can store and retrieve any NSCoding-compatible object or C value.

While it is not advisable to store an entire object graph into NSUserDefaults, it can be useful to encode compound objects in this way, such as "current user" objects or API credentials(use Keychain instead).

Archiving

NSData*data=[NSKeyedArchiverarchivedDataWithRootObject:books];[[NSUserDefaultsstandardUserDefaults]setObject:dataforKey:@"books"];

Unarchiving

NSData*data=[[NSUserDefaultsstandardUserDefaults]objectForKey:@"books"];NSArray*books=[NSKeyedUnarchiverunarchiveObjectWithData:data];

As developers, it is our responsibility to understand the goals and needs of our applications, and to resist the urge to over-engineer and prematurely optimize our solutions.

The decision to use Core Data in an application may appear to be a no-brainer, if not harmless. But in many cases, Core Data is discovered to be so unwieldy or unnecessary as to become a real hindrance to making something useful, let alone functional.

And even if most applications would benefit from Core Data at some point, there is wisdom to letting complexity evolve from a simple as necessary. And as far as persistence goes, it doesn't get much simpler than NSCoding.

Core Data Libraries & Utilities

$
0
0

So let's say that, having determined your particular needs and compared all of the alternatives, you've chosen Core Data for your next app.

Nothing wrong with that! Core Data is a great choice for apps that model, persist, and query large object graphs.

Sure it's complicated, cumbersome, and yes, at times, a real pain in the ass—but gosh darn it, some of the best and most popular apps ever built use Core Data. And if it's good enough for them, it's probably good enough for you, too.

...but that's not to say that Core Data can't be improved.

And while there have been many libraries attempting to replace Core Data, there are many more that attempt to make it better. These libraries range from the much-needed dose of syntactic sugar to comprehensive, full-stack frameworks.

This week on NSHipster: a guided tour of the best open source libraries for working with Core Data. Read on to see how you might make the most from your Core Data experience.


For your convenience, the following table is provided. Contained within are the most significant open source libraries and utilities for working with Core Data. This is list is by no means comprehensive, so if you think something is missing or out of place, please tweet @NSHipster—or better yet, submit a pull request.

Wrappers
Magical RecordSaul Mora
Objective-RecordMarin Usalj
SSDataKitSam Soffes
ios-queryableMarty Dill
ReactiveCoreDataJacob Gorban
Adapters
RestKitBlake Watters
AFIncrementalStoreMattt Thompson
MMRecordConrad Stoll
SLRESTfulCoreDataOliver Letterer
OvercoatGuillermo Gonzalez
MantleGitHub
Synchronizers
TICoreDataSyncTim Isted, Michael Fey, Kevin Hoctor, Christian Beer, Tony Arnold, and Danny Greg
UbiquityStoreManagerMaarten Billemont
FireDataJonathan Younger
Utilities
mogeneratorJonathan 'Wolf' Rentzsch

Wrappers

Wrapper libraries provide some much needed syntactic sugar and convenience methods to Core Data's verbose and complicated APIs.

For example, to insert a new managed object into a managed object context, it's a class method on, not NSManagedObject or NSManagedObjectContext as one might reasonably expect, but NSEntityDescription. NSEntityDescription +insertNewObjectForEntityForName:inManagedObjectContext:. What?

There are a number of open source libraries that collectively identify and correct for the roughest patches of the Core Data APIs. Managing a main and private context outside of AppDelegate, convenience method for manipulating and querying managed objects, and so on.

SSDataKit

There is a lot of boilerplate code required to write a Core Data application. This is annoying. In pretty much everything I've written since Core Data came to iOS, I have used the following class.

Inspired by Active Record

It should be no surprise that programmers, having learned how to do things a certain way, will bring those ideas and conventions to other technologies. For the large influx of Ruby developers coming over to iOS, that familiar paradigm was Active Record.

Contrary to popular belief, Core Data is not an Object-Relational Mappers, but rather an object graph and persistence framework, capable of much more than the Active Record pattern alone is capable of. Using Core Data as an ORM necessarily limits the capabilities of Core Data and muddies its conceptual purity. But for many developers longing for the familiarity of an ORM, this trade-off is a deal at twice the price!

Magical Record

MagicalRecord was inspired by the ease of Ruby on Rails' Active Record fetching. The goals of this code are to clean up Core Data related code, allow for clear, simple, one-line fetches, and still allow the modification of the NSFetchRequest when request optimizations are needed.

Objective-Record

This is a lightweight ActiveRecord way of managing Core Data objects. The syntax is borrowed from Ruby on Rails. And yeah, no AppDelegate code. It's fully tested with Kiwi.

Inspired by LINQ

Here's a fun game: the next time you meet a developer coming over from the .NET world, set a timer to see how long it takes them to start raving about LINQ. Seriously, people love LINQ.

For the uninitiated, LINQ is like SQL, but integrated as a language feature. Think NSPredicate, NSSortDescriptor, and Key-Value Coding with a much nicer syntax:

from c in SomeCollection
  where c.SomeProperty < 10
  select new {c.SomeProperty, c.OtherProperty};

ios-queryable

ios-queryable supports LINQ-style query composition and deferred execution, and implements a subset of IEnumerable's methods, including where, take, skip, orderBy, first/firstOrDefault, single/singleOrDefault, count, any, and all.

Inspired by ReactiveCocoa

ReactiveCocoa, which itself brings the functional reactive paradigm to Objective-C, is now being used to bring some functional sanity and order to Core Data. This is still uncharted territory, but the initial results are indeed promising.

ReactiveCoreData

ReactiveCoreData (RCD) is an attempt to bring Core Data into the ReactiveCocoa (RAC) world.

Adapters

Most iOS apps communicate with a webservice in some capacity. For apps using Core Data, it's common for records to be fetched, updated, and deleted from a REST or RPC-style webservice. Maintaining consistency between a local cache and the server is a deceptively tricky enterprise.

Keeping objects up-to-date, removing duplicate records, mapping entities to API endpoints, reconciling conflicts, managing network reachability... these are just some of the challenges a developer faces when creating a robust client-server application.

Fortunately, there are a wealth of open-source libraries that can help alleviate some of this pain.

RestKit

RestKit is a modern Objective-C framework for implementing RESTful web services clients on iOS and Mac OS X. It provides a powerful object mapping engine that seamlessly integrates with Core Data and a simple set of networking primitives for mapping HTTP requests and responses built on top of AFNetworking. It has an elegant, carefully designed set of APIs that make accessing and modeling RESTful resources feel almost magical.

AFIncrementalStore

AFIncrementalStore is an NSIncrementalStore subclass that uses AFNetworking to automatically request resources as properties and relationships are needed.

MMRecord

MMRecord is a block-based seamless web service integration library for iOS and Mac OS X. It leverages the Core Data model configuration to automatically create and populate a complete object graph from an API response. It works with any networking library, is simple to setup, and includes many popular features that make working with web services even easier.

SLRESTfulCoreData

SLRESTfulCoreData builds on top of AFNetworking and SLCoreDataStack and let's you map your JSON REST API to your CoreData model in minutes.

Overcoat

Overcoat is an AFNetworking extension that makes it super simple for developers to use Mantle model objects with a REST client.

Mantle

Mantle makes it easy to write a simple model layer for your Cocoa or Cocoa Touch application.

Synchronizers

Whereas adapters synchronize information through an existing, general purpose interface such as REST, synchronizers use a more direct protocol, offering better integration and performance at the expense of portability and generality.

TICoreDataSync

Automatic synchronization for Core Data Apps, between any combination of Mac OS X and iOS: Mac to iPhone to iPad to iPod touch and back again

UbiquityStoreManager

UbiquityStoreManager is a controller that implements iCloud integration with Core Data for you.

FireData

FireData seamlessly integrates Core Data with Firebase.

Utilities

We would be remiss to survey the open source Core Data ecosystem without mentioning Mogenerator. Among one of the surviving projects from the pre-iPhone era, Mogenerator has become indispensable to developers over the years. Although much has changed about Core Data over the years, the one constant has been Apple's characteristic lack of comprehensive tooling. Fortunately, Mr. Wolf Rentzsch has us covered.

Mogenerator

mogenerator is a command-line tool that, given an .xcdatamodel file, will generate two classes per entity. The first class, _MyEntity, is intended solely for machine consumption and will be continuously overwritten to stay in sync with your data model. The second class, MyEntity, subclasses _MyEntity, won't ever be overwritten and is a great place to put your custom logic.


Remember: there is no silver bullet. There is no one-size-fits-all solution. Just as Core Data may only be advisable in particular circumstances, so too are the aforementioned Core Data libraries.

Dividing the ecosystem up into broad categories is informative if only to help identify the relative strengths and trade-offs of each library. Only you can determine (yes, sometimes through trial and error) which solution is the best for you.

Unit Testing

$
0
0

Unit Testing is an emotional topic for developers. It inspires a sense of superiority to its most zealous adherents, and evokes a feeling of inadequacy to non-practitioners. Cargo Cults like TDD& BDD stake their reputation on unit testing to the point of co-opting and conflating utility with morality.

It's as close to a religious debate as programmers get... nevermind the matter of tabs-versus-spaces.

Objective-C developers have, for the most part, remained relatively apathetic to Unit Testing ("There's that SenTest thing, but who uses that, really?"). Between static typing, typically manageable project sizes, and a compiler advanced enough to rewrite code for you, unit testing isn't as much of a necessity as it is for more dynamic languages like Ruby (at least in practice).

But that's not to say that Objective-C developers wouldn't benefit from unit testing. In fact, as Objective-C continues to become more collaborative, with growing participation in the open source community, automated testing will become a necessity.

This week NSHipster will explore the world of unit testing frameworks, and how to set up an automated build system with Travis CI.


Unit Testing is a tool, just like any other tool. It's purpose is to make us better at our jobs, which is to produce robust, maintainable software.

It's a simple enough premise: write code to construct environments that exercise the particular behavior of a given method, function, class, or feature. Variables are isolated in a scientific manner, so as to test assumptions with logical atomicity.

OCUnit

OCUnit, a.k.a. SenTestingKit, was integrated into Xcode 2.1 circa WWDC 2005, as a result of its use in the development of Core Data 1.0. Developed by Sen:Te, OCUnit is actually one of the first unit testing libraries written for any language.

Unit Tests were added into a separate testing target in the Xcode Project. Each test file defines an SenTestCase subclass, which implements a series of methods beginning with the word test. C assert-style macros are used to fail tests if the specified condition is not met. Each test is run in sequence, independently of one another, with the results logged afterwards:

#import <SenTestingKit/SenTestingKit.h>#import "Person.h"@interfaceTestPerson : SenTestCase@end@implementationTestPerson-(void)testFullName{Person*person=[[Personalloc]init];person.firstName=@"Pablo";person.lastName=@"Picasso";STAssertEqualObjects([personfullName],@"Pablo Picasso",nil);}

The SenTestingKit assertions are about what you'd expect, offering bread-and-butter equality, existence, and truth checks:

  • STAssertNil()
  • STAssertNotNil()
  • STAssertTrue()
  • STAssertFalse()
  • STAssertEqualObjects()
  • STAssertEquals()
  • STAssertEqualsWithAccuracy()
  • STAssertThrows()
  • STAssertThrowsSpecific()
  • STAssertThrowsSpecificNamed()
  • STAssertNoThrow()
  • STAssertNoThrowSpecific()
  • STAssertNoThrowSpecificNamed()
  • STAssertTrueNoThrow()
  • STAssertFalseNoThrow()
  • STFail()

And yet, as useful as tests are, they necessarily introduce friction into a development cycle. When project pressures begin to weigh, tests are the first thing to be thrown overboard. At some point, the tests stop passing ("we can worry about that later—now we have to ship!")

The only chance testing has to remain relevant in high-pressure situations is to reduce that friction in development. Essentially, tests need to become both easier to write and easier to run.

Open Source Libraries

There are a myriad of open source libraries that attempt to make testing more palatable by way of syntactic sugar and features like method stubs, mock objects, and promises.

Here's a list of some of the most useful open source libraries for unit testing:

Mock Objects
OCMockErik Doernenburg
OCMockitoJon Reid
Matchers
ExpectaPeter Jihoon Kim
OCHamcrestJon Reid
BDD / TDD
SpectaPeter Jihoon Kim
KiwiAllen Ding
CedarPivotal Labs
Frameworks
GHUnitGabriel Handford

Automated Testing

Making tests easier to write is one thing, but getting them to run without affecting productivity is quite another.

Jenkins

For a long time, installing Jenkins on a dedicated Mac Mini was the state-of-the-art for automated build servers.

Aside from the fact that it's kinda the worst thing ever to set-up, you can do a lot of cool things like notifying build status over IM or IRC, automatically distributing builds to TestFlight or HockeyApp with Shenzhen, and generating documentation with AppleDoc.

Travis

Until recently, automated unit testing for Objective-C was the privilege of projects that could dedicate the time and money to setup a CI server. Travis CI made CI available to the masses.

CI for Objective-C is more difficult than for other languages, because it needs to be done on a Mac. For economic reasons, there just isn't a market for cloud-based OS X environments like there is for Linux. Fortunately, SauceLabs has built such a virtualized Mac cloud, and is graciously donating some of it to run tests for open source Objective-C projects on Travis-CI.

For an example of automated Objective-C unit testing in the wild, check out how AFNetworking does it.

The Tests subdirectory contains separate projects for iOS and OS X targets, as well as a Podfile, which specifies all of the testing library dependencies. AFNetworking executes a Rake task, which shells out to xctool.

All of the configuration for setup is defined in .travis.yml:

.travis.yml

language: objective-c
before_install:
  - brew update
  - brew install xctool --HEAD
  - cd Tests && pod install && cd $TRAVIS_BUILD_DIR
  - mkdir -p "Tests/AFNetworking Tests.xcodeproj/xcshareddata/xcschemes" && cp Tests/Schemes/*.xcscheme "Tests/AFNetworking Tests.xcodeproj/xcshareddata/xcschemes/"
script: rake test

Full documentation for the Travis configuration file can be found on Travis-CI.org.


Once again, the direction of Objective-C has been directly influenced by the Ruby community. Those guys and gals are serious about testing. It's not like we should complain, though: between CocoaPods, RubyMotion, and Nomad, Ruby has made Objective-C development better by several orders of magnitude.

The bottom line is that testing has come to Objective-C. It's not always necessary, and it's certainly not a silver bullet for writing great software, but it's proven itself invaluable (especially for open source development). So give it a try now, before not testing becomes seriously uncool.

NSDataDetector

$
0
0

Machines speak in binary, while humans speak in riddles, half-truths, and omissions.

And until humanity embraces RDF for all of their daily interactions, a large chunk of artificial intelligence is going to go into figuring out what the heck we're all talking about.

Because in the basic interactions of our daily lives—meeting people, making plans, finding information online—there is immense value in automatically converting from implicit human language to explicit structured data, so that it can be easily added to our calendars, address books, maps, and reminders.

Fortunately for Cocoa developers, there's an easy solution: NSDataDetector.


NSDataDetector is a subclass of NSRegularExpression, but instead of matching on an ICU pattern, it detects semi-structured information: dates, addresses, links, phone numbers and transit information.

It does all of this with frightening accuracy. NSDataDetector will match flight numbers, address snippets, oddly formatted digits, and even relative deictic expressions like "next Saturday at 5".

You can think of it as a regexp matcher with incredibly complicated expressions that can extract information from natural language (though its actual implementation details may be somewhat more complicated than that).

NSDataDetector objects are initialized with a bitmask of types of information to check, and then passed strings to match on. Like NSRegularExpression, each match found in a string is represented by a NSTextCheckingResult, which has details like character range and match type. However, NSDataDetector-specific types may also contain metadata such as address or date components.

NSError*error=nil;NSDataDetector*detector=[NSDataDetectordataDetectorWithTypes:NSTextCheckingTypeLink|NSTextCheckingTypePhoneNumbererror:&error];NSString*string=@"123 Main St. / (555) 555-5555";[detectorenumerateMatchesInString:stringoptions:kNilOptionsrange:NSMakeRange(0,[stringlength])usingBlock:^(NSTextCheckingResult*result,NSMatchingFlagsflags,BOOL*stop){NSLog(@"Match: %@",result);}];

When initializing NSDataDetector, be sure to specify only the types you're interested in. With each additional type to be checked comes a nontrivial performance cost.

Data Detector Match Types

Because of how much NSTextCheckingResult is used for, it's not immediately clear which properties are specific to NSDataDetector. For your reference, here is a table of the different NSTextCheckingTypes for NSDataDetector matches, and their associated properties:

TypeProperties
NSTextCheckingTypeDate
  • date
  • duration
  • timeZone
NSTextCheckingTypeAddress
  • addressComponents*
    • NSTextCheckingNameKey
    • NSTextCheckingJobTitleKey
    • NSTextCheckingOrganizationKey
    • NSTextCheckingStreetKey
    • NSTextCheckingCityKey
    • NSTextCheckingStateKey
    • NSTextCheckingZIPKey
    • NSTextCheckingCountryKey
    • NSTextCheckingPhoneKey
NSTextCheckingTypeLink
  • url
NSTextCheckingTypePhoneNumber
  • phoneNumber
NSTextCheckingTypeTransitInformation
  • components*
    • NSTextCheckingAirlineKey
    • NSTextCheckingFlightKey
*NSDictionary properties have values at defined keys.

Data Detection on iOS

Somewhat confusingly, iOS also defines UIDataDetectorTypes. A bitmask of these values can be set as the dataDetectorTypes of a UITextView to have detected data automatically linked in the displayed text.

UIDataDetectorTypes is distinct from NSTextCheckingTypes in that equivalent enum constants (e.g. UIDataDetectorTypePhoneNumber and NSTextCheckingTypePhoneNumber) do not have the same integer value, and not all values in one are found in the other. Converting from UIDataDetectorTypes to NSTextCheckingTypes can be accomplished with a function:

staticinlineNSTextCheckingTypeNSTextCheckingTypesFromUIDataDetectorTypes(UIDataDetectorTypesdataDetectorType){NSTextCheckingTypetextCheckingType=0;if(dataDetectorType&UIDataDetectorTypeAddress){textCheckingType|=NSTextCheckingTypeAddress;}if(dataDetectorType&UIDataDetectorTypeCalendarEvent){textCheckingType|=NSTextCheckingTypeDate;}if(dataDetectorType&UIDataDetectorTypeLink){textCheckingType|=NSTextCheckingTypeLink;}if(dataDetectorType&UIDataDetectorTypePhoneNumber){textCheckingType|=NSTextCheckingTypePhoneNumber;}returntextCheckingType;}

If you're looking for an easy way to use NSDataDetector in your iOS app, you may want to check out TTTAttributedLabel, a drop-in replacement for UILabel that supports attributed strings, and (as of 1.7.0) automatic data detection of NSTextCheckingTypes.


Do I detect some disbelief of how easy it is to translate between natural language and structured data? This should not be surprising, given how insanelygreat Cocoa's linguistic APIs are.

Don't make your users re-enter information by hand just because of a programming oversight. Take advantage of NSDataDetector in your app to unlock the structured information already hiding in plain sight.

NSHipster Quiz #2

$
0
0

On June 11th, we organized an NSHipster Pub Quiz for WWDC attendees. Like our first quiz, questions ranged from random Apple trivia to obscure framework questions. The event was hosted by New Relic, and sponsored by Heroku& Mutual Mobile. About 100 developers attended the event, with the team "UIResponders" taking top prize.

For everyone that couldn't make it to the event, here's an opportunity to play along at home. Here are some ground rules:

  • There are 4 Rounds, with 10 questions each
  • Record answers on a separate sheet of paper
  • Each correct answer to a question gets you 1 point (unless otherwise specified)
  • Play with up to 5 friends for maximum enjoyment
  • Don't be lame and look things up on the Internet or in Xcode

Round 1: General Knowledge

  1. Mac OS 10.9 Mavericks is named after a surf spot in Northern California. What was the surf spot named after?
  2. What is the Objective-C Type Encoding of NSError **?
  3. What is the name of the command line utility that generates a .strings file from Objective-C source code with NSLocalizedString?
  4. What NSArray method was available since 10.6, but only made public as of 10.9?
  5. What is the name of the open source project whose C/C++ libraries power NSRegularExpression, CFStringTransform, and other Unicode features in Cocoa?
  6. On May 19, 2001 Apple opened its first 2 retail stores. Where were they? (1 point for each exact location, or ½‎ for each state)
  7. What is the name of the private Apple TV framework responsible for appliance UI?
  8. Which Apple VP said "Can't innovate anymore,
 my ass!"?
  9. Complete the following verse:

We've come too far To give up who we are



She's up all night to the sun...

  1. List all of the rooms in Moscone West named after San Francisco neighborhoods, starting at Level 2 (1 point for each room)

Answers for Round 1

  1. A white-haired German Shepherd named Maverick (anything about a dog gets the point)
  2. ^@
  3. genstrings
  4. NSArray -firstObject
  5. ICU (International Components for Unicode)
  6. Tysons Corner, Virginia & Glendale, California
  7. BackRow
  8. Phil Schiller
  9. "So let's raise the bar / And our cups to the stars"
  10. Pacific Heights, Mission, Nob Hill, Russian Hill, Marina, Presidio

Round 2: Before & After

What are the following better known as today?

  1. Yellow Box
  2. AppleSearch
  3. Jaguar (i.e. "Mac OS X 10.__"?)
  4. Rendezvous
  5. SoundJam
  6. Six Pack
  7. Universal Access
  8. Graphics Group (semi-related to Apple)
  9. 20525 Mariani Ave.
  10. Yerba Buena (has to do with California, not Apple)

Answers for Round 2

  1. Cocoa
  2. Sherlock
  3. Mac OS X 10.2
  4. Bonjour
  5. iTunes
  6. System 6 / Mac OS 6
  7. Accessibility
  8. Pixar
  9. 1 Infinite Loop
  10. San Francisco

Round 3: Picture Round

  • 1. Which WWDC keynote was this from?

Question 1

  • 2. Which WWDC keynote was this from?

Question 2

  • 3. Which WWDC keynote was this from?

Question 3

  • 4. Which WWDC keynote was this from?

Question 4

  • 5. WTF is this?

Question 5

  • 6. What is this?

Question 6

  • 7. What is this?

Question 7

  • 8. What is this? (and which generation?)

Question 8

  • 9. Which "Core" framework is represented by this logo?

Question 9

  • 10. Everybody loves Craig /fɛdɹ̩igi/ (Pictured). How do you spell his last name?

Question 10


Answers for Round 3

  1. 2011
  2. 2009
  3. 2012
  4. 2008
  5. eMate 300
  6. Xserve RAID
  7. iSight
  8. 3rd gen. iPod Shuffle
  9. Core Audio
  10. "Federighi"

Round 4: Name That Framework!

For each question, a list of three classes from the same framework have been listed without their two-letter namespace prefix. Name the framework that they all belong to!


  1. Call, CallCenter, Carrier
  2. Attitude, LogItem, Magnetometer Data
  3. IdentifierManager
  4. Request, ComposeViewController, ServiceTypes.h
  5. Metadata, FileWrapper, FileManager
  6. Alarm, RecurrenceEnd, Source
  7. NotificationBanner, Player, SessionError
  8. Null, Zone, Coder
  9. Attribute Description, Relationship Description, Property Mapping
  10. Map Table, Hash Table, Pointer Array

Answers for Round 4

  1. Core Telephony
  2. Core Motion
  3. Ad Support
  4. Social
  5. Foundation
  6. EventKit
  7. Game Kit
  8. Foundation
  9. Core Data
  10. Foundation or Core Foundation (2 points if you got both)

So how did you fare? Tweet out your score to see how you stack up to your peers!

We'll be announcing another pub quiz soon, so be sure to sign up here to be the first to know about it!


Object Subscripting

$
0
0

Xcode 4.4 quietly introduced a syntactic revolution to Objective-C. Like all revolutions, however, its origins and agitators require some effort to trace: Xcode 4.4 shipped with Apple LLVM Compiler 4.0, which incorporated changes effective in the Clang front-end as of version 3.1.

For the uninitiated, Clang is the open source C language family front end to the LLVM compiler. Clang is responsible for all of the killer language features in Objective-C going back a few years, such as "Build & Analyze", ARC, blocks, and a nearly 3× performance boost when compiling over GCC.

Clang 3.1 added three features to Objective-C whose aesthetic & cosmetic impact is comparable to the changes brought about in Objective-C 2.0: NSNumber Literals, Collection Literals, and Object Subscripting.

In a single Xcode release, Objective-C went from this:

NSDictionary*dictionary=[NSDictionarydictionaryWithObject:[NSNumbernumberWithInteger:42]forKey:@"foo"];idvalue=[dictionaryobjectForKey:@"foo"];

...to this:

NSDictionary*dictionary=@{@"foo":@42};idvalue=dictionary[@"foo"];

Concision is the essence of clarity.

Shorter code means typing less, but it also means understanding more. Even a sprinkle of syntactic sugar can be enough to transform a language, and unlock new design patterns.

Collection literals become preferable to property lists for configuration.
Single-element array parameters become more acceptable.
APIs requiring boxed numeric values become more palatable.

However, what remains relatively under-utilized even now—a year after the these language features were added—is object subscripting. Perhaps after reading the rest of this article, though, you'll help to change this.


Elements in a C array are laid out contiguously in memory, and is referenced by the address of its first element. To get the value at a particular index, one would offset this address by the size of an array element, multiplied by the desired index. This pointer arithmetic is provided by the [] operator.

Over time, scripting languages began to take greater liberties with this familiar convention, expanding its role to get & set values in arrays, as well as hashes and objects.

With Clang 3.1, everything has come full-circle: what began as a C operator and co-opted by scripting languages, has now been rolled back into Objective-C. And like the aforementioned scripting languages of yore, the [] subscripting operator in Objective-C has been similarly overloaded to handle both integer-indexed and object-keyed accessors.

dictionary[@"foo"]=@42;array[0]=@"bar"

If Objective-C is a superset of C, how can Object Subscripting overload the [] C operator? The modern Objective-C runtime prohibits pointer arithmetic on objects, making this semantic pivot possible.

Where this really becomes interesting is when you extend your own classes with subscripting support:

Custom Indexed Subscripting

To add custom-indexed subscripting support to your class, simply declare and implement the following methods:

-(id)objectAtIndexedSubscript:(NSUInteger)idx;-(void)setObject:(id)objatIndexedSubscript:(NSUInteger)idx;

Custom Keyed Subscripting

Similarly, custom-keyed subscripting can be added to your class by declaring and implementing these methods:

-(id)objectForKeyedSubscript:(id<NSCopying>)key;-(void)setObject:(id)objforKeyedSubscript:(id<NSCopying>)key;

Custom Subscripting as DSL

The whole point in describing all of this is to encourage unconventional thinking about this whole language feature. At the moment, a majority of custom subscripting in classes is used as a convenience accessor to a private collection class. But there's nothing to stop you from, for instance, doing this:

routes[@"GET /users/:id"]=^(NSNumber*userID){// ...}

...or this:

idpiece=chessBoard[@"E1"];

...or this:

NSArray*results=managedObjectContext[@"Product WHERE stock > 20"];

Because of how flexible and concise subscripting is, it is extremely well-purposed for creating DSLs. When defining custom subscripting methods on your own class, there are no restrictions on how they are implemented. You can use this syntax to provide a shorthand for defining application routes, search queries, compound property accessors, or plain-old KVO.


This is, of course, dangerous thinking. Subscripting isn't your new bicycle. It isn't a giant hammer. Hell, it isn't even a giant screwdriver! If there is one thing Object Subscripting DSLs are, it's trouble. Here be dragons.

That said, it does open up some interesting opportunities to bend syntactic conventions to useful ends.

NSUUID / CFUUIDRef / UIDevice -uniqueIdentifier / -identifierForVendor

$
0
0

Let's say you're making privacy software that also prevents piracy. I mean, it's an obvious idea—someone's going to do it. You're just trying to be that person.

Until recently, it was trivial to uniquely identify devices between application launches, and even across applications: a simple call to UIDevice -uniqueIdentifier, and you were all set.

However, UIDevice -uniqueIdentifier was deprecated in iOS 5 with the following notes:

Use the identifierForVendor property of [UIDevice] or the advertisingIdentifier property of the ASIdentifierManager class instead, as appropriate, or use the UUID method of the NSUUID class to create a UUID and write it to the user defaults database.

As of May 1st, Apple began enforcing this deprecation on all new app submissions, even for apps targeting earlier versions of iOS. Any use of uniqueIdentifier is grounds for immediate rejection of new binaries.

Just as privacy and piracy have phonetic and conceptual similarities, device identifiers, whether UUID / GUID, UDID, or otherwise can be rather confusing:

  • UUID (Universally Unique Identifier): A sequence of 128 bits that can guarantee uniqueness across space and time, defined by RFC 4122.
  • GUID (Globally Unique Identifier): Microsoft's implementation of the UUID specification; often used interchangeably with UUID.
  • UDID (Unique Device Identifier): A sequence of 40 hexadecimal characters that uniquely identify an iOS device (the device's Social Security Number, if you will). This value can be retrieved through iTunes, or found using UIDevice -uniqueIdentifier. Derived from hardware details like MAC address.

Incidentally, all of the suggested replacements for UIDevice -uniqueIdentifier listed in its deprecation notes return UUID, whether created automatically with UIDevice -identifierForVendor& ASIdentifierManager -advertisingIdentifier or manually with NSUUID (or CFUUIDCreate).

Vendor Identifier

The value of this property is the same for apps that come from the same vendor running on the same device. A different value is returned for apps on the same device that come from different vendors, and for apps on different devices regardless of vendor.

The value in this property remains the same while the app (or another app from the same vendor) is installed on the iOS device. The value changes when the user deletes all of that vendor’s apps from the device and subsequently reinstalls one or more of them. Therefore, if your app stores the value of this property anywhere, you should gracefully handle situations where the identifier changes.

In many ways, this is what should have been used the whole time. App developers now have a way to identify devices uniquely—even across other apps by the same developer—without being entrusted with something as sensitive as the device's UDID.

For most applications, doing a find-and-replace of uniqueIdentifier to identifierForVendor is enough.

However, for advertising networks, which require a consistent identifier across applications from many developers, a different approach is required:

Advertising Identifier

iOS 6 introduces the Advertising Identifier, a non-permanent, non-personal, device identifier, that advertising networks will use to give you more control over advertisers’ ability to use tracking methods. If you choose to limit ad tracking, advertising networks using the Advertising Identifier may no longer gather information to serve you targeted ads. In the future all advertising networks will be required to use the Advertising Identifier. However, until advertising networks transition to using the Advertising Identifier you may still receive targeted ads from other networks.

As the sole component of the Ad Support framework, ASIdentifierManager's modus operandi is clear: provide a way for ad networks to track users between different applications in a way that doesn't compromise privacy.

Users can opt out of ad targeting in a Settings screen added in iOS 6.1, found at Settings > General > About > Advertising:

Limit Ad Tracking

NSUUID & CFUUIDRef

NSUUID was added to Foundation in iOS 6 as a way to easily create UUIDs. How easy?

NSString*UUID=[[NSUUIDUUID]UUIDString];

If your app targets iOS 5 or earlier, however, you have to settle for Core Foundation functions on CFUUIDRef:

CFUUIDRefuuid=CFUUIDCreate(NULL);NSString*UUID=CFUUIDCreateString(NULL,uuid);

For apps building against a base SDK without the vendor or advertising identifier APIs, a similar effect can be achieved—as recommended in the deprecation notes—by using NSUserDefaults:

-(BOOL)application:(UIApplication*)applicationdidFinishLaunchingWithOptions:(NSDictionary*)launchOptions{NSString*UUID=[[NSUserDefaultsstandardUserDefaults]objectForKey:kApplicationUUIDKey];if(!UUID){CFUUIDRefuuid=CFUUIDCreate(NULL);UUID=(__bridge_transferNSString*)CFUUIDCreateString(NULL,uuid);CFRelease(uuid);[[NSUserDefaultsstandardUserDefaults]setObject:UUIDforKey:kApplicationUUIDKey];[[NSUserDefaultsstandardUserDefaults]synchronize];}}

This way, a UUID will be generated once when the app is launched for the first time, and then stored in NSUserDefaults to be retrieved on each subsequent app launch. Unlike advertising or vendor identifiers, these identifiers would not be shared across other apps, but for most intents and purposes, this is works just fine.


Of course, UUIDs have many other uses: primary identifiers for records in distributed systems, names for temporary files, or even a bulk color generator (chunk the hexadecimal representation into 5 groups of 6!). But on iOS, it's all about tracking, about finding what was lost in a sea of network traffic and possibilities. Knowing where you stand on uniqueness is the first step to understanding all of this.

NSFastEnumeration / NSEnumerator / -enumerateObjectsUsingBlock:

$
0
0

Enumeration is where computation gets interesting. It's one thing to encode logic that's executed once, but applying it across a collection—that's what makes programming so powerful.

Each programming paradigm has its own way to iterate over a collection:

  • Procedural increments a pointer within a loop
  • Object Oriented applies a function or block to each object in a collection
  • Functional works through a data structure recursively

Objective-C, to echo one of the central themes of this blog, plays a fascinating role as a bridge between the Procedural traditions of C and the Object Oriented model pioneered in Smalltalk. In many ways, enumeration is where the proverbial rubber hits the road.

This article will cover all of the different ways collections are enumerated in Objective-C & Cocoa. How do I love thee? Let me count the ways.


C Loops (for/while)

for and while loops are the "classic" method of iterating over a collection. Anyone who's taken Computer Science 101 has written code like this before:

for(NSUIntegeri=0;i<[arraycount];i++){idobject=array[i];NSLog(@"%@",object)}

But as anyone who has used C-style loops knows, this method is prone to off-by-one errors—particularly when used in a non-standard way.

Fortunately, Smalltalk significantly improved this state of affairs with an idea called list comprehensions, which are commonly known today as for/in loops.

List Comprehension (for/in)

By using a higher level of abstraction, declaring the intention of iterating through all elements of a collection, not only are we less prone to error, but there's a lot less to type:

for(idobjectinarray){NSLog(@"%@",object);}

In Cocoa, comprehensions are available to any class that implements the NSFastEnumeration protocol, including NSArray, NSSet, and NSDictionary.

<NSFastEnumeration>

NSFastEnumeration contains a single method:

-(NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState*)stateobjects:(id*)stackbufcount:(NSUInteger)len
  • state: Context information that is used in the enumeration to, in addition to other possibilities, ensure that the collection has not been mutated.
  • stackbuf: A C array of objects over which the sender is to iterate.
  • len: The maximum number of objects to return in stackbuf.

One single, deceptively complicated method. There's that stackbuf out pointer parameter, and a state parameter of type NSFastEnumerationState *. Let's take a closer look at that...

NSFastEnumerationState

typedefstruct{unsignedlongstate;id*itemsPtr;unsignedlong*mutationsPtr;unsignedlongextra[5];}NSFastEnumerationState;
  • state: Arbitrary state information used by the iterator. Typically this is set to 0 at the beginning of the iteration.
  • itemsPtr: A C array of objects.
  • mutationsPtr: Arbitrary state information used to detect whether the collection has been mutated.
  • extra: A C array that you can use to hold returned values.

Under every elegant abstraction is an underlying implementation deserving to be hidden from the eyes of God. itemsPtr? mutationsPtr? extra‽ Seriously, what gives?

For the curious, Mike Ash has a fantastic blog post where he dives into the internals, providing several reference implementations of NSFastEnumeration.

What you should know about NSFastEnumeration is that it is fast. At least as fast if not significantly faster than rolling your own for loop, in fact. The secret behind its speed is how -countByEnumeratingWithState:objects:count: buffers collection members, loading them in as necessary. Unlike a single-threaded for loop implementation, objects can be loaded concurrently, making better use of available system resources.

Apple recommends that you use NSFastEnumerationfor/in-style enumeration for your collections wherever possible and appropriate. And to be honest, for how easy it is to use and how well it performs, that's a pretty easy sell. Seriously, use it.

NSEnumerator

But of course, before NSFastEnumeration (circa OS X 10.5 / iOS 2.0), there was the venerable NSEnumerator.

For the uninitiated, NSEnumerator is an abstract class that implements two methods:

-(id)nextObject-(NSArray*)allObjects

nextObject returns the next object in the collection, or nil if unavailable. allObjects returns all of the remaining objects, if any. NSEnumerators can only go forward, and only in single increments.

To enumerate through all elements in a collection, one would use NSEnumerator thusly:

idobject=nil;NSEnumerator*enumerator=...;while((object=[enumeratornextObject])){NSLog(@"%@",object);}

...or because NSEnumerator itself conforms to <NSFastEnumeration> in an attempt to stay hip to the way kids do things these days:

for(idobjectinenumerator){NSLog(@"%@",object);}

If you're looking for a convenient way to add fast enumeration to your own non-collection-class-backed objects, NSEnumerator is likely a much more palatable option than getting your hands messy with NSFastEnumeration's implementation details.

Some quick points of interest about NSEnumeration:

  • Reverse an array in one line of code with (and excuse the excessive dot syntax) array.reverseObjectEnumerator.allObjects.
  • Add LINQ-style operations with NSEnumeratorLinq, a third-party library using chained NSEnumerator subclasses.
  • Shake things up with your collection classes in style with TTTRandomizedEnumerator, another third-party library, which iterates through elements in a random order.

Enumerate With Blocks

Finally, with the introduction of blocks in OS X 10.6 / iOS 4, came a new block-based way to enumerate collections:

[arrayenumerateObjectsUsingBlock:^(idobject,NSUIntegeridx,BOOL*stop){NSLog(@"%@",object);}];

Collection classes like NSArray, NSSet, NSDictionary, and NSIndexSet include a similar set of block enumeration methods.

One of the advantages of this approach is that the current object index (idx) is passed along with the object itself. The BOOL pointer allows for early returns, equivalent to a break statement in a regular C loop.

Unless you actually need the numerical index while iterating, it's almost always faster to use a for/inNSFastEnumeration loop instead.

One last thing to be aware of are the expanded method variants with an options parameter:

-(void)enumerateObjectsWithOptions:(NSEnumerationOptions)optsusingBlock:(void(^)(idobj,NSUIntegeridx,BOOL*stop))block

NSEnumerationOptions

enum{NSEnumerationConcurrent=(1UL<<0),NSEnumerationReverse=(1UL<<1),};typedefNSUIntegerNSEnumerationOptions;
  • NSEnumerationConcurrent: Specifies that the Block enumeration should be concurrent. The order of invocation is nondeterministic and undefined; this flag is a hint and may be ignored by the implementation under some circumstances; the code of the Block must be safe against concurrent invocation.
  • NSEnumerationReverse: Specifies that the enumeration should be performed in reverse. This option is available for NSArray and NSIndexSet classes; its behavior is undefined for NSDictionary and NSSet classes, or when combined with the NSEnumerationConcurrent flag.

Again, fast enumeration is almost certain to be much faster than block enumeration, but these options may be useful if you're resigned to using blocks.


So there you have all of the conventional forms of enumeration in Objective-C and Cocoa.

What's especially interesting is that in looking at these approaches, we learn a lesson about the power of abstraction. Higher levels of abstraction are not just easier to write and comprehend, but can often be much faster than doing it the "hard way".

High-level commands that declare intention, like "iterate through all of the elements of this collection" lend themselves to high-level compiler optimization in a way that just isn't possible with pointer arithmetic in a loop. Context is a powerful thing, and designing APIs and functionality accordingly ultimately fulfill that great promise of abstraction: to solve larger problems more easily.

NSExpression

$
0
0

Cocoa is the envy of other standard libraries when it comes to querying and arranging information. With NSPredicate, NSSortDescriptor, and an occasional NSFetchRequest, even the most complex data tasks can be reduced into just a few, extremely-understandable lines of code.

Now, NSHipsters are no doubt already familiar with NSPredicate (and if you aren't, be sure to tune in next week!), but if we take a closer look at NSPredicate, we see that NSPredicate is actually made up of smaller, atomic parts: two NSExpressions (a left-hand value & a right-hand value), compared with an operator (e.g. <, IN, LIKE, etc.).

Because most developers only use NSPredicate by means of +predicateWithFormat:, NSExpression is a relatively obscure class. Which is a shame, because NSExpression is quite an incredible piece of functionality in its own right.

So allow me, dear readers, to express my respect and fascination with NSExpression:

Evaluating Math

The first thing you should know about NSExpression is that it lives to reduce terms. If you think about the process of evaluating an NSPredicate, there are two terms and a comparator, so those two terms need to simplify into something that the operator can handle—very much like the process of compiling a line of code.

Which leads us to NSExpression's first trick: doing math.

NSExpression*expression=[NSExpressionexpressionWithFormat:@"4 + 5 - 2**3"];idvalue=[expressionexpressionValueWithObject:nilcontext:nil];// => 1

It's no Wolfram Alpha, but if your app does anything where evaluating mathematical expressions would be useful, well... there you go.

Functions

But we've only just scratched the surface with NSExpression. Not impressed by a computer doing primary-school maths? How about high school statistics, then?

NSArray*numbers=@[@1,@2,@3,@4,@4,@5,@9,@11];NSExpression*expression=[NSExpressionexpressionForFunction:@"stddev:"arguments:@[[NSExpressionexpressionForConstantValue:numbers]]];idvalue=[expressionexpressionValueWithObject:nilcontext:nil];// => 3.21859...

NSExpression functions take a given number of sub-expression arguments. For instance, in the above example, to get the standard deviation of the collection, the array of numbers had to be wrapped with +expressionForConstantValue:. A minor inconvenience (which ultimately allows NSExpression to be incredibly flexible), but enough to trip up anyone trying things out for the first time.

If you found the Key-Value Coding Simple Collection Operators (@avg, @sum, et al.) lacking, perhaps NSExpression's built-in statistical, arithmetic, and bitwise functions will pique your interest.

A word of caution: according to this table in Apple's documentation for NSExpression, there is apparently no overlap between the availability of functions between Mac OS X & iOS. It would appear that recent versions of iOS do, indeed, support functions like stddev:, but this is not reflected in headers or documentation. Any details in the form of a pull request would be greatly appreciated.

Statistics

  • average:
  • sum:
  • count:
  • min:
  • max:
  • median:
  • mode:
  • stddev:

Basic Arithmetic

These functions take two NSExpression objects representing numbers.

  • add:to:
  • from:subtract:
  • multiply:by:
  • divide:by:
  • modulus:by:
  • abs:

Advanced Arithmetic

  • sqrt:
  • log:
  • ln:
  • raise:toPower:
  • exp:

Bounding Functions

  • ceiling: - (the smallest integral value not less than the value in the array)
  • trunc: - (the integral value nearest to but no greater than the value in the array)

Functions Shadowing math.h Functions

So mentioned, because ceiling is easily confused with ceil(3). Whereas ceiling acts on an array of numbers, while ceil(3) takes a double (and doesn't have a corresponding built-in NSExpression function). floor: here acts the same as floor(3).

  • floor:

Random Functions

Two variations—one with and one without an argument. Taking no argument, random returns an equivalent of rand(3), while random: takes a random element from the NSExpression of an array of numbers.

  • random
  • random:

Binary Arithmetic

  • bitwiseAnd:with:
  • bitwiseOr:with:
  • bitwiseXor:with:
  • leftshift:by:
  • rightshift:by:
  • onesComplement:

Date Functions

  • now

String Functions

  • lowercase:
  • uppercase:

No-op

  • noindex:

Custom Functions

In addition to these built-in functions, it's possible to invoke custom functions in an NSExpression. This article by Dave DeLong describes the process.

First, define the corresponding method in a category:

@interfaceNSNumber(Factorial)-(NSNumber*)factorial;@end@implementationNSNumber(Factorial)-(NSNumber*)factorial{return@(tgamma([selfdoubleValue]+1));}@end

Then, use the function thusly (the FUNCTION() macro in +expressionWithFormat: is shorthand for the process of building out with -expressionForFunction:, et al.):

NSExpression*expression=[NSExpressionexpressionWithFormat:@"FUNCTION(4.2, 'factorial')"];idvalue=[expressionexpressionValueWithObject:nilcontext:nil];// 32.578...

The advantage here, over calling -factorial directly is the ability to invoke the function in an NSPredicate query. For example, a location:withinRadius: method might be defined to easily query managed objects nearby a user's current location.

As Dave mentions in his article, the use cases are rather marginal, but it's certainly an interesting trick to have in your repertoire.


Next week, we'll build on what we just learned about NSExpression to further explore NSPredicate, and everything it has hidden up its sleeves. Stay tuned!

NSPredicate

$
0
0

NSPredicate is a Foundation class that specifies how data should be fetched or filtered. Its query language, which is like a cross between a SQL WHERE clause and a regular expressions, provides an expressive, natural language interface to define logical conditions on which a collection is searched.

It's easier to show NSPredicate in use, rather than talk about it in the abstract, so we're going to revisit the example data set used in the NSSortDescriptor article:

index0123
firstNameAliceBobCharlieQuentin
lastNameSmithJonesSmithAlberts
age24273331
@interfacePerson : NSObject@propertyNSString*firstName;@propertyNSString*lastName;@propertyNSNumber*age;@end@implementationPerson-(NSString*)description{return[NSStringstringWithFormat:@"%@ %@",self.firstName,self.lastName];}@end#pragma mark -NSArray*firstNames=@[@"Alice",@"Bob",@"Charlie",@"Quentin"];NSArray*lastNames=@[@"Smith",@"Jones",@"Smith",@"Alberts"];NSArray*ages=@[@24,@27,@33,@31];NSMutableArray*people=[NSMutableArrayarray];[firstNamesenumerateObjectsUsingBlock:^(idobj,NSUIntegeridx,BOOL*stop){Person*person=[[Personalloc]init];person.firstName=firstNames[idx];person.lastName=lastNames[idx];person.age=ages[idx];[peopleaddObject:person];}];NSPredicate*bobPredicate=[NSPredicatepredicateWithFormat:@"firstName = 'Bob'"];NSPredicate*smithPredicate=[NSPredicatepredicateWithFormat:@"lastName = %@",@"Smith"];NSPredicate*thirtiesPredicate=[NSPredicatepredicateWithFormat:@"age >= 30"];// ["Bob Jones"]NSLog(@"Bobs: %@",[peoplefilteredArrayUsingPredicate:bobPredicate]);// ["Alice Smith", "Charlie Smith"]NSLog(@"Smiths: %@",[peoplefilteredArrayUsingPredicate:smithPredicate]);// ["Charlie Smith", "Quentin Alberts"]NSLog(@"30's: %@",[peoplefilteredArrayUsingPredicate:thirtiesPredicate]);

Using NSPredicate with Collections

Foundation provides methods to filter NSArray / NSMutableArray& NSSet / NSMutableSet with predicates.

Immutable collections, NSArray& NSSet, have the methods filteredArrayUsingPredicate: and filteredSetUsingPredicate: which return a immutable collection by evaluating a predicate on the receiver.

Mutable collections, NSMutableArray& NSMutableSet have the method filterUsingPredicate:, which removes any objects that evaluate to FALSE when running the predicate on the receiver.

NSDictionary can use predicates by filtering its keys or values (both NSArray objects). NSOrderedSet can either create new ordered sets from a filtered NSArray or NSSet, or alternatively, NSMutableSet can simply removeObjectsInArray:, passing objects filtered with the negated predicate.

Using NSPredicate with Core Data

NSFetchRequest has a predicate property, which specifies the logical conditions under which managed objects should be retrieved. The same rules apply, except that predicates are evaluated by the persistent store coordinator within a managed object context, rather than collections being filtered in-memory.

Predicate Syntax

Substitutions

  • %@ is a var arg substitution for an object value—often a string, number, or date.
  • %K is a var arg substitution for a key path.
NSPredicate*ageIs33Predicate=[NSPredicatepredicateWithFormat:@"%K = %@",@"age",@33];// ["Charlie Smith"]NSLog(@"Age 33: %@",[peoplefilteredArrayUsingPredicate:ageIs33Predicate]);
  • $VARIABLE_NAME is a value that can be substituted with NSPredicate -predicateWithSubstitutionVariables:.
NSPredicate*namesBeginningWithLetterPredicate=[NSPredicatepredicateWithFormat:@"(firstName BEGINSWITH[cd] $letter) OR (lastName BEGINSWITH[cd] $letter)"];// ["Alice Smith", "Quentin Alberts"]NSLog(@"'A' Names: %@",[peoplefilteredArrayUsingPredicate:[namesBeginningWithLetterPredicatepredicateWithSubstitutionVariables:@{@"letter":@"A"}]]);

Basic Comparisons

  • =, ==: The left-hand expression is equal to the right-hand expression.
  • >=, =>: The left-hand expression is greater than or equal to the right-hand expression.
  • <=, =<: The left-hand expression is less than or equal to the right-hand expression.
  • >: The left-hand expression is greater than the right-hand expression.
  • <: The left-hand expression is less than the right-hand expression. !=, <>: The left-hand expression is not equal to the right-hand expression. BETWEEN: The left-hand expression is between, or equal to either of, the values specified in the right-hand side. The right-hand side is a two value array (an array is required to specify order) giving upper and lower bounds. For example, 1 BETWEEN { 0 , 33 }, or $INPUT BETWEEN { $LOWER, $UPPER }.

Basic Compound Predicates

  • AND, &&: Logical AND.
  • OR, ||: Logical OR.
  • NOT, !: Logical NOT.

String Comparisons

String comparisons are by default case and diacritic sensitive. You can modify an operator using the key characters c and d within square braces to specify case and diacritic insensitivity respectively, for example firstName BEGINSWITH[cd] $FIRST_NAME.

  • BEGINSWITH: The left-hand expression begins with the right-hand expression.
  • CONTAINS: The left-hand expression contains the right-hand expression.
  • ENDSWITH: The left-hand expression ends with the right-hand expression.
  • LIKE: The left hand expression equals the right-hand expression: ? and * are allowed as wildcard characters, where ? matches 1 character and * matches 0 or more characters.
  • MATCHES: The left hand expression equals the right hand expression using a regex-style comparison according to ICU v3 (for more details see the ICU User Guide for Regular Expressions).

Aggregate Operations

Relational Operations

  • ANY, SOME: Specifies any of the elements in the following expression. For example, ANY children.age < 18.
  • ALL: Specifies all of the elements in the following expression. For example, ALL children.age < 18.
  • NONE: Specifies none of the elements in the following expression. For example, NONE children.age < 18. This is logically equivalent to NOT (ANY ...).
  • IN: Equivalent to an SQL IN operation, the left-hand side must appear in the collection specified by the right-hand side. For example, name IN { 'Ben', 'Melissa', 'Nick' }.

Array Operations

  • array[index]: Specifies the element at the specified index in array.
  • array[FIRST]: Specifies the first element in array.
  • array[LAST]: Specifies the last element in array.
  • array[SIZE]: Specifies the size of array.

Boolean Value Predicates

  • TRUEPREDICATE: A predicate that always evaluates to TRUE.
  • FALSEPREDICATE: A predicate that always evaluates to FALSE.

NSCompoundPredicate

We saw that AND& OR can be used in predicate format strings to create compound predicates. However, the same can be accomplished using an NSCompoundPredicate.

For example, the following predicates are equivalent:

[NSCompoundPredicateandPredicateWithSubpredicates:@[[NSPredicatepredicateWithFormat:@"age > 25"],[NSPredicatepredicateWithFormat:@"firstName = %@",@"Quentin"]]];[NSPredicatepredicateWithFormat:@"(age > 25) AND (firstName = %@)",@"Quentin"];

While the syntax string literal is certainly easier to type, there are occasions where you may need to combine existing predicates. In these cases, NSCompoundPredicate -andPredicateWithSubpredicates:& -orPredicateWithSubpredicates: is the way to go.

NSComparisonPredicate

Similarly, if after reading last week's article you now find yourself with more NSExpression objects than you know what to do with, NSComparisonPredicate can help you out.

Like NSCompoundPredicate, NSComparisonPredicate constructs an NSPredicate from subcomponents—in this case, NSExpressions on the left and right hand sides. Analyzing its class constructor provides a glimpse into the way NSPredicate format strings are parsed:

+(NSPredicate*)predicateWithLeftExpression:(NSExpression*)lhsrightExpression:(NSExpression*)rhsmodifier:(NSComparisonPredicateModifier)modifiertype:(NSPredicateOperatorType)typeoptions:(NSUInteger)options

Parameters

  • lhs: The left hand expression.
  • rhs: The right hand expression.
  • modifier: The modifier to apply. (ANY or ALL)
  • type: The predicate operator type.
  • options: The options to apply. For no options, pass 0.

NSComparisonPredicate Types

enum{NSLessThanPredicateOperatorType=0,NSLessThanOrEqualToPredicateOperatorType,NSGreaterThanPredicateOperatorType,NSGreaterThanOrEqualToPredicateOperatorType,NSEqualToPredicateOperatorType,NSNotEqualToPredicateOperatorType,NSMatchesPredicateOperatorType,NSLikePredicateOperatorType,NSBeginsWithPredicateOperatorType,NSEndsWithPredicateOperatorType,NSInPredicateOperatorType,NSCustomSelectorPredicateOperatorType,NSContainsPredicateOperatorType,NSBetweenPredicateOperatorType};typedefNSUIntegerNSPredicateOperatorType;

NSComparisonPredicate Options

  • NSCaseInsensitivePredicateOption: A case-insensitive predicate. You represent this option in a predicate format string using a [c] following a string operation (for example, "NeXT" like[c] "next").
  • NSDiacriticInsensitivePredicateOption: A diacritic-insensitive predicate. You represent this option in a predicate format string using a [d] following a string operation (for example, "naïve" like[d] "naive").
  • NSNormalizedPredicateOption: Indicates that the strings to be compared have been preprocessed. This option supersedes NSCaseInsensitivePredicateOption and NSDiacriticInsensitivePredicateOption, and is intended as a performance optimization option. You represent this option in a predicate format string using a [n] following a string operation (for example, "WXYZlan" matches[n] ".lan").
  • NSLocaleSensitivePredicateOption: Indicates that strings to be compared using <, <=, =, =>, > should be handled in a locale-aware fashion. You represent this option in a predicate format string using a [l] following one of the <, <=, =, =>, > operators (for example, "straße" >[l] "strasse").

Block Predicates

Finally, if you just can't be bothered to learn the NSPredicate format syntax, you can go through the motions with NSPredicate +predicateWithBlock:.

NSPredicate*shortNamePredicate=[NSPredicatepredicateWithBlock:^BOOL(idevaluatedObject,NSDictionary*bindings){return[[evaluatedObjectfirstName]length]<=5;}];// ["Alice Smith", "Bob Jones"]NSLog(@"Short Names: %@",[peoplefilteredArrayUsingPredicate:shortNamePredicate]);

...Alright, that whole dig on predicateWithBlock: as being the lazy way out wasn't entirely charitable.

Actually, since blocks can encapsulate any kind of calculation, there is a whole class of queries that can't be expressed with the NSPredicate format string (such as evaluating against values dynamically calculated at run-time). And while its possible to accomplish the same using an NSExpression with a custom selector, blocks provide a convenient interface to get the job done.

One important note: NSPredicates created with predicateWithBlock: cannot be used for Core Data fetch requests backed by a SQLite store.


NSPredicate is, and I know this is said a lot, truly one of the jewels of Cocoa. Other languages would be lucky to have something with half of its capabilities in a third-party library—let alone the standard library. Having it as a standard-issue component affords us as application and framework developers an incredible amount of leverage in working with data.

Together with NSExpression, NSPredicate reminds us what a treat Foundation is: a framework that is not only incredibly useful, but meticulously architected and engineered, to be taken as inspiration for how we should write our own code.

UIMenuController

$
0
0

Mobile usability today is truly quite remarkable—especially considering how far it's come in just the last decade. What was once a clumsy technology relegated to the tech elite has now become the primary mode of computation for a significant portion of the general population.

Yet despite its advances, one can't help but feel occasionally... trapped.

All too often, there will be information on the screen that you just can't access. Whether its flight information stuck in a table view cell or an unlinked URL, users are forced to solve problems creatively for lack of a provided solution.

In the past, we've mentioned localization and accessibility as two factors that distinguish great apps from the rest of the pack. This week, we'll add another item to that list: Edit Actions.

Copy, Cut, Paste, Delete, Select

iOS 3's killer feature was undoubtedly push notifications, but the ability to copy-paste is probably a close second. For how much we use it everyday, it's difficult to imagine how we got along without it. And yet, it remains a relatively obscure feature for 3rd-party apps.

This may be due to how cumbersome it is to implement. Let's look at a simple implementation, and then dive into some specifics about the APIs:

HipsterLabel.{h,m}

@interfaceHipsterLabel : UILabel@end@implementationHipsterLabel-(BOOL)canBecomeFirstResponder{returnYES;}-(BOOL)canPerformAction:(SEL)actionwithSender:(id)sender{return(action==@selector(copy:));}#pragma mark - UIResponderStandardEditActions-(void)copy:(id)sender{[[UIPasteboardgeneralPasteboard]setString:self.text];}@end

ViewController.m

-(void)viewDidLoad{HipsterLabel*label=...;label.userInteractionEnabled=YES;[self.viewaddSubview:label];UIGestureRecognizer*gestureRecognizer=[[UILongPressGestureRecognizeralloc]initWithTarget:selfaction:@selector(handleLongPressGesture:)];[labeladdGestureRecognizer:gestureRecognizer];}#pragma mark - UIGestureRecognizer-(void)handleLongPressGesture:(UIGestureRecognizer*)recognizer{UIMenuController*menuController=[UIMenuControllersharedMenuController];[menuControllersetTargetRect:recognizer.view.frameinView:recognizer.view.superview];[menuControllersetMenuVisible:YESanimated:YES];[recognizer.viewbecomeFirstResponder];}

So, to recap, in order to allow a label's text to be copied, the following must happen:

  • UILabel must be subclassed to implement canBecomeFirstResponder& canPerformAction:withSender:
  • Each performable action must implement a corresponding method that interacts with UIPasteboard
  • When instantiated by a controller, the label must have userInteractionEnabled set to YES (it is not recommended that this be hard-coded into the subclass implementation)
  • A UIGestureRecognizer must be added to the label (else, UIResponder methods like touchesBegan:withEvent: are implemented manually in the subclass)
  • In the method implementation corresponding to the gesture recognizer action, UIMenuController must be positioned and made visible
  • Finally, the label must become first responder

If you're wondering why, oh why, this isn't just built into UILabel, well... join the club.

UIMenuController

UIMenuController is responsible for presenting edit action menu items. Each app has its own singleton instance, sharedMenuController. By default, a menu controller will show commands for any methods in the UIResponderStandardEditActions informal protocol that the responder returns YES for in canPerformAction:withSender:.

Handling Copy, Cut, Delete, and Paste Commands

Each command travels from the first responder up the responder chain until it is handled; it is ignored if no responder handles it. If a responder doesn't handle the command in the current context, it should pass it to the next responder.

copy: This method is invoked when the user taps the Copy command of the editing menu. A subclass of UIResponder typically implements this method. Using the methods of the UIPasteboard class, it should convert the selection into an appropriate object (if necessary) and write that object to a pasteboard.

cut: This method is invoked when the user taps the Cut command of the editing menu. A subclass of UIResponder typically implements this method. Using the methods of the UIPasteboard class, it should convert the selection into an appropriate object (if necessary) and write that object to a pasteboard. It should also remove the selected object from the user interface and, if applicable, from the application's data model.

delete: This method is invoked when the user taps the Delete command of the editing menu. A subclass of UIResponder typically implements this method by removing the selected object from the user interface and, if applicable, from the application's data model. It should not write any data to the pasteboard.

paste: This method is invoked when the user taps the Paste command of the editing menu. A subclass of UIResponder typically implements this method. Using the methods of the UIPasteboard class, it should read the data in the pasteboard, convert the data into an appropriate internal representation (if necessary), and display it in the user interface.

Handling Selection Commands

select: This method is invoked when the user taps the Select command of the editing menu. This command is used for targeted selection of items in the receiving view that can be broken up into chunks. This could be, for example, words in a text view. Another example might be a view that puts lists of visible objects in multiple groups; the select: command could be implemented to select all the items in the same group as the currently selected item.

selectAll: This method is invoked when the user taps the Select All command of the editing menu.

In addition to these basic editing commands, there are commands that deal with rich text editing (toggleBoldface:, ,toggleItalics:, andtoggleUnderline:) and writing direction changes (makeTextWritingDirectionLeftToLeft:&makeTextWritingDirectionLeftToRight:`). As these are not generally applicable outside of writing an editor, we'll just mention them in passing.

UIMenuItem

With iOS 3.2, developers could now add their own commands to the menu controller. As yet unmentioned, but familiar commands like "Define" or spell check suggestions take advantage of this.

UIMenuController has an items property, which is an NSArray of UIMenuItem objects. Each UIMenuItem object has a title and action. In order to have a menu item command di splay in a menu controller, the responder must implement the corresponding selector.


Just as a skilled coder designs software to be flexible and adaptable to unforeseen use cases, any app developer worth their salt understands the need to accommodate users with different needs from themselves.

As you develop your app, take to heart the following guidelines:

  • For every control, think about what you would expect a right-click (control-click) to do if used from the desktop.
  • Any time information is shown to the user, consider whether it should be copyable.
  • With formatted or multi-faceted information, consider whether multiple kinds of copy commands are appropriate.
  • When implementing copy: make sure to copy only valuable information to the pasteboard.
  • For editable controls, ensure that your implementation paste: can handle a wide range of valid and invalid input.

If mobile is to become most things to most people, the least we can do is make our best effort to allow users to be more productive. Your thoughtful use of UIMenuController will not go unnoticed.

NSHipster Quiz #3

$
0
0

NSHipster Pub Quiz came to New York City on July 30th. Like our first and second quizzes, questions ranged from random Apple trivia to obscure framework questions—this time, with a particular focus on hardware rumors and questions about iOS [REDACTED].

The event was hosted by Meetup, and sponsored by Heroku. Dozens of the best and brightest minds in Objective-C attended the event, with the team "The Forstall Five" (@mb, @bcapps, @ethicalpaul, @grantjbutler, and @conbrolution) taking first place.

If you'd like to play along at home, or test your team's mettle at work, here are some guidelines:

  • There are 4 Rounds, with 10 questions each
  • Record answers on a separate sheet of paper
  • Each correct answer to a question gets you 1 point (unless otherwise specified)
  • Play with up to 5 friends for maximum enjoyment
  • Don't be lame and look things up on the Internet or in Xcode

Round 1: General Knowledge

In honor of hosting this pub quiz in New York City, a couple questions about city planning are thrown in towards the end for good measure.


  1. iOS 7 Beta 4 hints at a new hardware feature in the next iPhone related to the acquisition of mobile security firm AuthenTec. What is that feature?
  2. iOS 7 Beta 4 also added UIApplicationDidTakeScreenshotNotification ...which is good news for which popular social networking app?
  3. According to a leaked photo from Chinese site WeiPhone, what is the name of the rumored low-cost, plastic iPhone?
  4. JOBS, starring Ashton Kutcher hits theaters August 16... but which acclaimed screenwriter is behind the other Steve Jobs biopic, currently in production?
  5. Which country has planned 11 "Steve Jobs schools", featuring an iPad-based curriculum, to open in August?
  6. Which computing pioneer, famous for his 1968
"Mother of All Demos", passed away on July 2, 2013?
  7. Which New York-based directions and mapping startup was purchased by Apple in July 2013?
  8. Of the world's major subway systems, London's is the oldest, while Moscow has the greatest ridership. By what measure is the New York subway system #1?
  9. Who is the author of "The Death and Life of Great American Cities", which famously argues against the urban renewal plans for Greenwich Village in the 1950's & 60's?
  10. What 5 channels were Apple TV with the version 5.3 update (1 point each)

Answers for Round 1

  1. Biometric Authentication
  2. SnapChat
  3. iPhone 5C
  4. Aaron Sorkin
  5. The Netherlands
  6. Douglas Engelbart
  7. HopStop.com
  8. Number of Stations (468)
  9. Jane Jacobs
  10. HBO Go, WatchESPN, Sky News, Qello, & CrunchyRoll

Round 2: Public, Private, or Fake?

There are 152 frameworks in iOS. You may know of perhaps a few dozen public ones, but there are many more private frameworks hidden in the OS. For each of the following, tell me if the framework (as of iOS 6.1) is public, private, or something we just made up.


  1. ActorKit
  2. Celestial
  3. MediaToolbox
  4. URLify
  5. SocialDarwin
  6. Marco
  7. LinguisticsKit
  8. QuickLook
  9. AdSupport
  10. NSAKit

Answers for Round 2

Source: https://github.com/nst/iOS-Runtime-Headers

  1. Private (iOS 3.0–)
  2. Private (iOS 2.1–)
  3. Public (iOS 6.0–), formerly Private (iOS 2.1—5.1)
  4. Private (iOS 2.1)
  5. Fake
  6. Private (iOS 4.0—4.3)
  7. Fake
  8. Public (iOS 4.0—)
  9. Public (iOS 6.0—)
  10. Fake

Round 3: Name that App! (Picture Round)

With over 1 Million iOS & Mac Apps on the App Store, it's clear that the true secret to success boils down to one thing: having a pretty icon. For each icon, tell me the name of the app.


  • 1. What is the name of this iOS game?

Question 1

  • 2. What is the name of this iOS game?

Question 2

  • 3. What is the name of this iOS app?

Question 3

  • 4. What is the name of this popular iOS app?

Question 4

  • 5. While not on the App Store, jailbreakers will know this icon well. What's its name?

Question 5

  • 6. Which classic Mac app sports this delightful moving truck?

Question 6

  • 7. Which indispensible development tool has this incongruous icon?

Question 7

  • 8. Which app sports this sleek icon?

Question 8

  • 9. Which app is represented by this delightful mail bag?

Question 9

  • 10. Which (unfortunately stalled) app has this beautiful icon?

Question 10


Answers for Round 3

  1. Toca Boca Hair Salon
  2. Tall Chess (Icon has since changed to something less Giraffe-ish)
  3. Days
  4. Paper
  5. Cydia
  6. Transmit
  7. Charles
  8. Take Five
  9. Courier
  10. Induction

Round 4: [REDACTED]

For each question, a class or protocol from iOS [REDACTED] is described. For 1 point each, fill in the blank.


  1. "An [REDACTED] object captures map-based imagery asynchronously."
  2. "A [REDACTED] object represents a connected physical game controller."
  3. "An [REDACTED] object represents a scene of content in Sprite Kit."
  4. "An [REDACTED] object specifies a gravity vector that applies to all of its dynamic items."
  5. "[REDACTED] objects provide a mechanism to describe a font with a dictionary of attributes."
  6. "[REDACTED] is a new class for managing the acquisition of network-based resources in the background"
  7. "An [REDACTED] object provides you with route-based directions data from Apple servers."
  8. "An [REDACTED] object facilitates communication among all peers in a Multipeer Connectivity session."
  9. "The [REDACTED] class defines a region in which text is laid out."
  10. "You can use a [REDACTED] to do either or both of the following:
    • Run a custom animation—including an interactive animation—inside the animation block of an in-progress view controller transition
    • Register a completion handler that is called after a view controller transition completes, such as to return state to what it was prior to the transition"

Answers for Round 4

  1. MKMapSnapshot
  2. GCController
  3. SKScene
  4. UIGravityBehavior
  5. UIFontDescriptor
  6. NSURLSession
  7. MKDirections
  8. MCSession
  9. NSTextContainer
  10. UIViewControllerTransitionCoordinator (protocol for object returned by UIViewController -transitionCoordinator)

So how did you fare? Tweet out your score to see how you stack up to your peers!

We'll be announcing another pub quiz soon, so be sure to sign up here to be the first to know about it!


Documentation

$
0
0

There's an adage among Cocoa developers that Objective-C's verbosity lends its code to being effectively self-documenting. Between longMethodNamesWithNamedParameters: and the explicit typing of those parameters, Objective-C methods don't leave much to the imagination.

But even self-documenting code can be improved with documentation, and just a small amount of effort can yield significant benefit to others.

Listen—I know programmers don't like to be told what to do, and prescriptive arguments of "thou shalt" and "thou shalt not" have the rhetorical impact of a trombone, so I'll cut to the chase:

Do you like Apple's documentation? Don't you want that for your own libraries? Follow just a few simple conventions, and your code can get the documentation it deserves.


Every modern programming language has comments: non-executable natural language annotations denoted by a special character sequence, such as //, /* */, #, and --. Documentation provides auxiliary explanation and context to code using specially-formatted comments, which can be extracted and parsed by a build tool.

In Objective-C, the documentation tool of choice is appledoc. Using a Javadoc-like syntax, appledoc is able to generate HTML and Xcode-compatible .docset docs from .h files that look nearly identical to Apple's official documentation.

Doxygen, used primarily for C++, is another viable option for Objective-C, but is generally dispreffered by the iOS / Mac OS X developer community.

Here are some examples from well-documented Objective-C projects:

Guidelines for Writing Objective-C Documentation

Objective-C documentation is designated by a /** */ comment block (note the extra initial star), which precedes any @interface or @protocol, as well as any method or @property declarations.

For classes, categories, and protocols, documentation should describe the purpose of that particular component, offering suggestions and guidelines for how it should be used. Structure it like a news article: start with a top-level "tweet-sized" overview, and then explore further topics in more detail as necessary. Concerns like how a class should (or should not) be subclassed, or any caveats in behavior for standard protocols (like NSCopying) should always be documented.

Each method should similarly begin with a concise description of its functionality, followed by any caveats or additional details. Method documentation also contains Javadoc-style @ labels for common fields like parameters and return value:

  • @param [param] [Description]: Describes what value should be passed for this parameter
  • @return [Description]: Describes the return value of the method
  • @see [selector]: `Provide "see also" reference to related method
  • @warning [description]: Call out exceptional or potentially dangerous behavior

Properties are often described in a single sentence, and should include what its default value is.

Related properties and methods should be grouped by an @name declaration, which functions similarly to a #pragma mark, and can be used with the triple-slash (///) comment variant.

Try reading other documentation before writing some yourself, in order to get a sense of the correct tone and style. When in doubt about terminology or verbiage, follow the lead of the closest thing you can find from Apple's official docs.

To help speed up the process of documenting your project, you may want to check out the VVDocumenter-Xcode project, which automatically adds @param and @return labels for methods according to their signature.


Just by following these simple guidelines, you can add great-looking, informative documentation to your own project. Once you get the hang of it, you'll find yourself cranking docs out in no time.

Thanks to @orta for suggesting this week's topic, and for his ongoing work on CocoaDocs which provides automatically-generated documentation for projects published on CocoaPods.

rand(3) / random(3) / arc4random(3) / et al.

$
0
0

What passes for randomness is merely a hidden chain of causality.

In a mechanical universe of material interactions expressed through mathematical equations, it is unclear whether nature encodes an element of chance, or if it's a uniquely human way to reconcile uncertainty.

We can be sure of one thing, however: in the closed, digital universe of CPU cycles, processes, and threads, there is no true randomness, only pseudorandomness.

Pseudorandomness, is often implemented in a way very similar to a cryptographic hash, as a deterministic function that returns a value based on the current time (salted, of course, by some initial seed value). Also like hash functions, there are a number of PRNG, or pseudorandom number generators, each of with optimizing for particular performance characteristics: uniformity, periodicity, and computational complexity.

Of course, for app developers, all of this is an academic exercise. And rather than bore you with any more high-minded, long-winded treatises on the philosophical nature of randomness, we're going to tackle this one FAQ-style.

Our goal this week: to clear up all of the lingering questions and misunderstandings about doing random things in Objective-C. Let's dive in!


How Do I Generate a Random Number in Objective-C?

tl;dr: Use arc4random() and its related functions.

Specifically, to generate a random number between 0 and N - 1, use arc4random_uniform(), which avoids modulo bias.

Random int between 0 and N - 1

NSUIntegerr=arc4random_uniform(N);

Random int between 1 and N

NSUIntegerr=arc4random_uniform(N)+1;

Random double between 0 and 1

If you are generating a random double or float, another good option are the more obscure rand48 family of functions, including drand48(3).

srand48(time(0));doubler=drand48();

rand48 functions, unlike arc4random functions, require an initial value to be seeded before generating random numbers. This seed function, srand48(3), should only be run once.

How Do I Pick a Random Element from an NSArray

Use arc4random_uniform(3) to generate a random number in the range of a non-empty array.

if([arraycount]>0){idobj=array[arc4random_uniform([arraycount])];}

How Do I Randomly Order an NSArray

NSMutableArray*mutableArray=[NSMutableArrayarrayWithArray:array];NSUIntegercount=[mutableArraycount];// See http://en.wikipedia.org/wiki/Fisher–Yates_shuffleif(count>1){for(NSUIntegeri=count-1;i>0;--i){[mutableArrayexchangeObjectAtIndex:iwithObjectAtIndex:arc4random_uniform((int32_t)i)];}}NSArray*randomArray=[NSArrayarrayWithArray:mutableArray];

This code is borrowed from TTTRandomizedEnumerator, which also provides randomized enumerators for NSSet, NSOrderedSet, and NSDictionary.

How Do I Generate a Random String?

If you're looking to generate "lorem ipsum"-style sentences, try constructing a Markov Chain from a corpus.

Otherwise, if you're looking to just get random letters, try one of the following methods:

Generate a Random Lowercase NSString

If you are operating on a known, contiguous range of Unicode characters, such as the lowercase letters (U+0061U+007A), you can do a simple conversion from a char:

NSString*letter=[NSStringstringWithFormat:@"%c",arc4random_uniform(26)+'a'];

Pick a Random Character From an NSString

Otherwise, a simple way to pick random letters from a set of your choosing is to simply create a string containing all of the possible letters:

NSString*vowels=@"aeiouy";NSString*letter=[vowelssubstringWithRange:NSMakeRange(arc4random_uniform([vowelslength]),1)];

Why Should I Use arc4random(3) instead of rand(3) or random(3)?

C functions are typically denoted with a number 3 inside of parentheses, following the organizational convention of man pages.

  • arc4random does not require an initial seed (with srand or srandom), making it that much easier to use.
  • arc4random has a range up to 0x100000000 (4294967296), whereas rand and random top out at RAND_MAX = 0x7fffffff (2147483647).
  • rand has often been implementated in such a way that regularly cycles low bits, making it more predictable.

What are rand(3), random(3), and arc4random(3), and Where Do They Come From?


If you have any additional questions about randomness on Objective-C, feel free to tweet @NSHipster. As always, corrections are welcome in the form of a pull request.

NSHashTable & NSMapTable

$
0
0

NSSet and NSDictionary, along with NSArray are the workhorse collection classes of Foundation. Unlike other standard libraries, implementation details are hidden from developers, allowing them to write simple code and trust that it will be (reasonably) performant.

However, even the best abstractions break down; their underlying assumptions overturned. In these cases, developers either venture further down the abstraction, or, if available use a more general-purpose solution.

For NSSet and NSDictionary, the breaking assumption was in the memory behavior when storing objects in the collection. For NSSet, objects are a strongly referenced, as are NSDictionary values. Keys, on the other hand, are copied by NSDictionary. If a developer wanted to store a weak value, or use a non-<NSCopying>-conforming object as a key, she could be clever and use `NSValue +valueWithNonretainedObject. Or, as of iOS 6 (and as far back as Mac OS X 10.5), she could use NSHashTable or NSMapTable, the more general-case counterparts to NSSet or NSDictionary, respectively.

So without further ado, here's everything you need to know about two of the more obscure members of Foundation's collections classes:

NSHashTable

NSHashTable is a general-purpose analogue of NSSet. Contrasted with the behavior of NSSet / NSMutableSet, NSHashTable has the following characteristics:

  • NSSet / NSMutableSet holds strong references to members, which are tested for hashing and equality using the methods hash and isEqual:.
  • NSHashTable is mutable, without an immutable counterpart.
  • NSHashTable can hold weak references to its members.
  • NSHashTable can copy members on input.
  • NSHashTable can contain arbitrary pointers, and use pointer identity for equality and hashing checks.

Usage

NSHashTable*hashTable=[NSHashTablehashTableWithOptions:NSPointerFunctionsCopyIn];[hashTableaddObject:@"foo"];[hashTableaddObject:@"bar"];[hashTableaddObject:@42];[hashTableremoveObject:@"bar"];NSLog(@"Members: %@",[hashTableallObjects]);

NSHashTable objects are initialized with an option for any of the following behaviors. Deprecated enum values are due to NSHashTable being ported from Garbage-Collected Mac OS X to ARC-ified iOS. Other values are aliased to options defined by NSPointerFunctions, which will be covered next week on NSHipster.

  • NSHashTableStrongMemory: Equal to NSPointerFunctionsStrongMemory. This is default behavior, equivalent to NSSet member storage.
  • NSHashTableWeakMemory: Equal to NSPointerFunctionsWeakMemory. Uses weak read and write barriers. Using NSPointerFunctionsWeakMemory object references will turn to NULL on last release.
  • NSHashTableZeroingWeakMemory: This option has been deprecated. Instead use the NSHashTableWeakMemory option.
  • NSHashTableCopyIn: Use the memory acquire function to allocate and copy items on input (see NSPointerFunction -acquireFunction). Equal to NSPointerFunctionsCopyIn.
  • NSHashTableObjectPointerPersonality: Use shifted pointer for the hash value and direct comparison to determine equality; use the description method for a description. Equal to NSPointerFunctionsObjectPointerPersonality.

NSMapTable

NSMapTable is a general-purpose analogue of NSDictionary. Contrasted with the behavior of NSDictionary / NSMutableDictionary, NSMapTable has the following characteristics:

  • NSDictionary / NSMutableDictionary copies keys, and holds strong references to values.
  • NSMapTable is mutable, without an immutable counterpart.
  • NSMapTable can hold keys and values with weak references, in such a way that entries are removed when either the key or value is dereferenced.
  • NSMapTable can copy its values on input.
  • NSMapTable can contain arbitrary pointers, and use pointer identity for equality and hashing checks.

Usage

Instances where one might use NSMapTable include non-copyable keys and storing weak references to keyed delegates or another kind of weak object.

iddelegate=...;NSMapTable*mapTable=[NSMapTablemapTableWithKeyOptions:NSMapTableStrongMemoryvalueOptions:NSMapTableWeakMemory];[mapTablesetObject:delegateforKey:@"foo"];NSLog(@"Keys: %@",[[mapTablekeyEnumerator]allObjects]);

NSMapTable objects are initialized with options specifying behavior for both keys and values, using the following enum values:

  • NSMapTableStrongMemory: Specifies a strong reference from the map table to its contents.
  • NSMapTableWeakMemory: Uses weak read and write barriers appropriate for ARC or GC. Using NSPointerFunctionsWeakMemory object references will turn to NULL on last release. Equal to NSMapTableZeroingWeakMemory.
  • NSHashTableZeroingWeakMemory: This option has been superseded by the NSMapTableWeakMemory option.
  • NSMapTableCopyIn Use the memory acquire function to allocate and copy items on input (see acquireFunction (see NSPointerFunction -acquireFunction). Equal to NSPointerFunctionsCopyIn.
  • NSMapTableObjectPointerPersonality: Use shifted pointer hash and direct equality, object description. Equal to NSPointerFunctionsObjectPointerPersonality.

Subscripting

After looking at a few code examples, a clever NSHipster may have thought "why aren't we using object subscripting?". A particularly enterprising NSHipster may even have gotten a few lines of code into implementing a subscripting category for NSMapTable!

So why doesn't NSMapTable implement subscripting? Take a look at these method signatures:

-(id)objectForKeyedSubscript:(id<NSCopying>)key;-(void)setObject:(id)objforKeyedSubscript:(id<NSCopying>)key;

Notice that the key argument is of type <NSCopying>. This is great for NSDictionaryNSMutableDictionary, but we can't make that assumption for NSMapTable. And so we arrive at an impasse: with an id <NSCopying> type, we can't implement for NSMapTable, however if object subscripting methods were to drop the <NSCopying> constraint, then we would miss out on the compiler check in NSMutableDictionary-setObject:forKeyedSubscript:.

So it goes. Honestly, in a situation where NSMapTable is merited, syntactic sugar is probably the least of one's concerns.


As always, it's important to remember that programming is not about being clever: always approach a problem from the highest viable level of abstraction. NSSet and NSDictionary are great classes. For 99% of problems, they are undoubtedly the correct tool for the job. If, however, your problem has any of the particular memory management constraints described above, then NSHashTable& NSMapTable may be worth a look.

Equality

$
0
0

The concept of equality is a central point of debate and inquiry in philosophy and mathematics, with far-reaching implications for matters of ethics, justice, and public policy.

From an empiricist perspective of the universe, two objects are equal if they are indistinguishable from one another in measurable observations. On a human scale, egalitarians hold that individuals should be considered equal members of the societal, economic, political, and judicial systems they inhabit.

It is the task of programmers to reconcile our logical and physical understanding of equality with the semantic domains we model. There is a subtlety to the question of equality, too often overlooked. Jumping into implementation without sufficient understanding of semantics can lead to unnecessary work that produces incorrect results. Though an understanding of the mathematical and logical system underpinning is equally essential to making things work as modeled.

While the temptation for all technical blog posts is to skim for headings and code samples, please take a few minutes to read and understand all of this. Copying relevant-looking code verbatim without knowing why its there may lead to incorrect behavior. With all seriousness, equality is one of those topics—in Objective-C in particular—where there is still a great deal of confusion.

Equality & Identity

First and foremost, it is important to make a distinction between equality and identity.

Two objects may be equal or equivalent to one another, if they share a common set of observable properties. Yet, those two objects may still be thought to be distinct, each with their own identity. In programming, an object's identity is tied to its memory address.

NSObject tests equality with another object with the method isEqual:. In its base implementation, an equality check is essentially a test for identity. Two NSObjects are considered equal if they point to the same memory address.

@implementationNSObject(Approximate)-(BOOL)isEqual:(id)object{returnself==object;}@end

For container classes like NSArray, NSDictionary, and NSString, the expected and indeed more useful behavior would be to do a deep equality comparison, to test that each member in the collection is equal.

Subclasses of NSObject implementing their own isEqual: method are expected to do the following:

  • Implement a new isEqualTo__ClassName__: method, which performs the meaningful value comparison.
  • Override isEqual: to make class and object identity checks, falling back on the aforementioned value comparison method.
  • Override hash, which will be described in the next section.

Here's an idea of how NSArray might do this (ignoring, for this example, that as a class cluster, the actual implementation would be significantly more complicated):

@implementationNSArray(Approximate)-(BOOL)isEqualToArray:(NSArray*)array{if(!array||[selfcount]!=[arraycount]){returnNO;}for(NSUIntegeridx=0;idx<[arraycount];idx++){if(![self[idx]isEqual:array[idx]]){returnNO;}}returnYES;}-(BOOL)isEqual:(id)object{if(self==object){returnYES;}if(![objectisKindOfClass:[NSArrayclass]]){returnNO;}return[selfisEqualToArray:(NSArray*)object];}@end

The following NSObject subclasses in Foundation have custom equality implementations, with the corresponding method:

  • NSAttributedString -isEqualToAttributedString:
  • NSData -isEqualToData:
  • NSDate -isEqualToDate:
  • NSDictionary -isEqualToDictionary:
  • NSHashTable -isEqualToHashTable:
  • NSIndexSet -isEqualToIndexSet:
  • NSNumber -isEqualToNumber:
  • NSOrderedSet -isEqualToOrderedSet:
  • NSSet -isEqualToSet:
  • NSString -isEqualToString:
  • NSTimeZone -isEqualToTimeZone:
  • NSValue -isEqualToValue:

When comparing two instances of any of these classes, one is encouraged to use these high-level methods rather than isEqual:.

However, our theoretical implementation is yet incomplete. Let's turn our attention now to hash (after a quick detour to clear something up about NSString:

The Curious Case of NSString Equality

As an interesting aside, consider the following:

NSString*a=@"Hello";NSString*b=@"Hello";BOOLwtf=(a==b);// YES

Let it be perfectly clear that the correct way to compare NSString objects is to use -isEqualToString:. Under no circumstances should you compare NSString with the == operator.

So what's going on here? Why does this work, when the same code for NSArray or NSDictionary literals wouldn't work?

It all has to do with an optimization technique known as string interning, whereby one copy of immutable string value is copied for each distinct value. NSString *a and *b point to the same copy of the interned string value @"Hello". Note that this only works for _immutable strings._

Interestingly enough, Objective-C selector names are also stored as interned strings in a shared string pool.

themoreyouknow.gif.

Hashing

The primary use case of object equality tests for everyday object-oriented programming is to determine collection membership. To keep this fast, subclasses with custom equality implementations are expected to implement hash as well:

  • Object equality is commutative ([a isEqual:b][b isEqual:a])
  • If objects are equal, then their hash values must also be equal ([a isEqual:b][a hash] == [b hash])
  • However, the converse does not hold: two objects need not be equal in order for their hash values to be equal ([a hash] == [b hash]¬⇒ [a isEqual:b])

Now for a quick flashback to Computer Science 101:


A hash table is a fundamental data structure in programming, and its what enables NSSet& NSDictionary to have fast (O(1)) lookup of elements.

We can best understand hash tables by contrasting them to arrays:

Arrays store elements in sequential indexes, such that an Array of size n will have slots at positions 0, 1, up to n - 1. To determine where an element is stored in the array (if at all), each position would have to be checked one-by-one (unless the array happens to be sorted, but that's another story).

Hash Tables take a slightly different approach. Rather than storing elements sequentially (0, 1, ..., n-1), a hash table allocates n positions in memory, and uses a function to calculate a position within that range. A hash function is deterministic, and a good hash function generates values in a relatively uniform distribution without being too computationally expensive. A hash collision occurs when two different objects calculate the same hash value. When this happens, the hash table will seek from the point of collision and place the new object in the first available place. As a hash table becomes more congested, the likelihood of collision increases, which leads to more time spent looking for a free space (hence why a hash function with a uniform distribution is so desireable).


One of the most common misconceptions about implementing a custom hash function comes from affirming the consequent, thinking that hash values must be distinct. This often leads to needlessly complicated implementations involving the magical incantation of prime numbers copied from Java textbooks. In reality, a simple XOR over the hash values of critical properties is sufficient 99% of the time.

The trick is in thinking about what the critical value of an object is.

For an NSDate, the time interval since a reference date would be sufficient:

@implementationNSDate(Approximate)-(NSUInteger)hash{return(NSUInteger)abs([selftimeIntervalSinceReferenceDate]);}

For a UIColor, a bit-shifted sum of RGB components is a convenient calculation:

@implementationUIColor(Approximate)-(NSUInteger)hash{CGFloatred,green,blue;[colorgetRed:&redgreen:&greenblue:&bluealpha:nil];return((NSUInteger)(red*255)<<16)+((NSUInteger)(green*255)<<8)+(NSUInteger)(blue*255);}@end

Implementing -isEqual: and hash in a Subclass

Bringing it all together, here's how one might override the default equality implementation in a subclass:

@interfacePerson@propertyNSString*name;@propertyNSDate*birthday;-(BOOL)isEqualToPerson:(Person*)person;@end
@implementationPerson-(BOOL)isEqualToPerson:(Person*)person{if(!person){returnNO;}BOOLhaveEqualNames=(!self.name&&!person.name)||[self.nameisEqualToString:person.name];BOOLhaveEqualBirthdays=(!self.birthday&&!person.birthday)||[self.birthdayisEqualToDate:person.birthday];returnhaveEqualNames&&haveEqualBirthdays;}#pragma mark - NSObject-(BOOL)isEqual:(id)object{if(self==object){returnYES;}if(![objectisKindOfClass:[Personclass]]){returnNO;}return[selfisEqualToPerson:(Person*)object];}-(NSUInteger)hash{return[self.namehash]^[self.birthdayhash];}

For the curious and pedantic, see this post from Mike Ash for an explanation of how hash implementations might be improved by bit-shifting or rotating composite values that may overlap.

Don't Overthink It

While all of this has been an interesting exercise in epistemology and computer science, there is a lingering pragmatic detail:

You don't usually need to implement this.

There are many situations where the default identity check (two variables point to the same address in memory) is desirable behavior. This comes as a consequence of the limitations of data modeling.

Take, for instance, the previous example of the Person class. It's not inconceivable that two individuals would share a common name and birthday. In reality, this crisis of identity would be resolved by additional information, whether it's a system-dependent identifier like a Social Security Number, their parents' identities, or any other physical attributes.

Yet even that additional information is not entirely foolproof. After all, that person could be cloned, teleported, or whisked away into a parallel universe. Unlikely? Sure. But much of the challenge in modeling systems is dealing with imperfect assumptions. Just saying.

Ultimately, it's up to the abstraction to isolate the significant, identifying features that the system cares about, and disregard the rest. The developer can then decide whether objects will be used in such a way that set membership calculations should care about. In a program that only records name and birthday, it may perfectly correct to treat congruent instances as distinct entities.


Hopefully, after all of this explanation, we all stand with equal footing on this slippery subject.

As humans, we strive to understand and implement equality in our society and economy; in the laws and leaders that govern us; in the understanding that we extend to one another as we journey through existence. May we continue towards that ideal, where individuals are judged by the contents of their character, just as we judge a variable by the contents of its memory address.

Xcode Snippets

$
0
0

iOS development all but requires the use of Xcode. To its credit, Xcode has improved pretty consistently over the last couple of years. Sure, it still has its... quirks, but hey—things could be much, much worse.

Working in an IDE may not be as cool as working in your favorite decades-old editor (or that other one), but you know what is cool? Autocompletion. Not to mention Build & Analyze, Breakpoints, and Instruments.

This is all to say: if we're resigned to use an IDE in our development workflow, we might as well make the most of it, right? So this week on NSHipster, we're going to talk about one of the more powerful yet underused features of Xcode: Code Snippets.


From @interface declarations to if (!self) return nil; incantations, there is a lot of avoidable typing in Objective-C. Xcode snippets allow these common patterns and boilerplate code to be extracted for quick reuse.

Using Xcode Snippets

To see the available code snippets, show the Utilities panel, to the right of your editor. On the bottom half the Utilities panel, there will be a horizontal divider with 4 icons.

Utilities Divider

Click the { } icon to show the Code Snippets Library.

Utilities Panel

There are two ways to insert a snippet into your code:

You can drag and drop from the code snippets library into your editor:

Drag-and-Drop

...or for snippets that include a text completion shortcut, you can start typing that:

Text Completion Shortcut

To get a sense of what you can do with snippets, here's an overview of the ones built-in to Xcode:

  • C typedef declarations for enum, structunion, and blocks
  • C control flow statements like if, if...else, and switch
  • C loops, such as for, while, and do...while
  • C inline block variable declaration
  • Objective-C declarations for @interface (including for class extensions and categories), @implementation, @protocol
  • Objective-C boilerplate for KVO, including the relatively obscure keyPathsForValuesAffecting<Key>, used for registering dependent keys
  • Objective-C boilerplate for Core Data fetches, property accessors, and property validation
  • Objective-C idioms for enumerating NSIndexSet
  • Objective-C incantation for init, initWithCoder: and initWithFrame: method implementations
  • Objective-C @try / @catch / @finally and @autorelease blocks
  • GCD idioms for dispatch_once and dispatch_after

Creating Xcode Snippets

Of course, what really makes snippets such a powerful feature is the ability to create your own.

The process of creating a snippet is actually pretty unintuitive and difficult to explain. It uses an obscure OS X system feature that allows users to create a "Text Clipping" by dragging and dropping selected text. Much easier to just show it in action:

Text Completion Shortcut

After being added to the code snippet library, a user-defined snippet can be edited by double-clicking its listing:

Text Completion Shortcut

Each snippet has the following fields:

  • Title - The name of the snippet (appears in text completion and in snippet library listing)
  • Summary - A brief description of what it does (appears only in snippet library listing)
  • Platform - Limits the snippet visibility for text completion to the specified platform. OS X, iOS, or both ("All")
  • Language - Limits the snippet visibility for text completion to the specified language. Most commonly C, Objective-C, C++, or Objective-C++.
  • Completion Shortcut - The text completion shortcut. For commonly-used snippets, this should be relatively short. Xcode does not warn about conflicting / overlapping shortcuts, so make sure yours doesn't overlap with an existing one.
  • Completion Scopes - Limits the snippet visibility for text completion to the specified scopes. For example, an if / else statement should only be auto-completed from within a method or function implementation. Any combination of the following:
    • All
    • Class Implementation
    • Class Interface Methods
    • Class Interface Variables
    • Code Expression
    • Function or Method
    • Preprocessor Directive
    • String or Comment
    • Top Level

Each Xcode snippet has a file representation in ~/Library/Developer/Xcode/UserData/CodeSnippets/

Third-Party Xcode Snippets

A list of generally useful code snippets can be found in this GitHub project (pull requests welcome!). If nothing else, this also serves as an example of what's possible in creating your own snippets.


Programming isn't about being an expert typist, so don't make it any more difficult for yourself than it needs to be. If you find yourself groaning while typing some inane, rote-memorized code idiom, take a minute to create a snippet for it instead!

Viewing all 382 articles
Browse latest View live