diff --git a/2013-08-19-nshashtable-and-nsmaptable.md b/2013-08-19-nshashtable-and-nsmaptable.md index 6aba6254..bfa338b7 100644 --- a/2013-08-19-nshashtable-and-nsmaptable.md +++ b/2013-08-19-nshashtable-and-nsmaptable.md @@ -35,9 +35,8 @@ hashTable.addObject("foo") hashTable.addObject("bar") hashTable.addObject(42) hashTable.removeObject("bar") -print(hashTable.allObjects) +print("Members: \(hashTable.allObjects)") ~~~ - ~~~{objective-c} NSHashTable *hashTable = [NSHashTable hashTableWithOptions:NSPointerFunctionsCopyIn]; [hashTable addObject:@"foo"]; @@ -65,6 +64,8 @@ NSLog(@"Members: %@", [hashTable allObjects]); - `NSMapTable` can `copy` its values on input. - `NSMapTable` can contain arbitrary pointers, and use pointer identity for equality and hashing checks. +> *Note:* `NSMapTable`'s focus on strong and weak references means that Swift's prevalent value types are a no go—reference types only, please. + ### Usage Instances where one might use `NSMapTable` include non-copyable keys and storing weak references to keyed delegates or another kind of weak object. @@ -72,10 +73,10 @@ Instances where one might use `NSMapTable` include non-copyable keys and storing ~~~{swift} let delegate: AnyObject = ... let mapTable = NSMapTable(keyOptions: .StrongMemory, valueOptions: .WeakMemory) + mapTable.setObject(delegate, forKey: "foo") print("Keys: \(mapTable.keyEnumerator().allObjects)") ~~~ - ~~~{objective-c} id delegate = ...; NSMapTable *mapTable = [NSMapTable mapTableWithKeyOptions:NSMapTableStrongMemory @@ -105,7 +106,11 @@ extension NSMapTable { } set { - setObject(newValue, forKey: key) + if newValue != nil { + setObject(newValue, forKey: key) + } else { + removeObjectForKey(key) + } } } } @@ -121,7 +126,11 @@ extension NSMapTable { - (void)setObject:(id)obj forKeyedSubscript:(id)key { - [self setObject:obj forKey:key]; + if (obj != nil) { + [self setObject:obj forKey:key]; + } else { + [self removeObjectForKey:key]; + } } @end diff --git a/2013-09-23-ios7.md b/2013-09-23-ios7.md index 1d7e106a..3c7244a0 100644 --- a/2013-09-23-ios7.md +++ b/2013-09-23-ios7.md @@ -292,12 +292,7 @@ Even though the number of people who have actually read something saved for late import SafariServices let url = NSURL(string: "http://nshipster.com/ios7")! -do { - try SSReadingList.defaultReadingList()?.addReadingListItemWithURL(url, title: "NSHipster", - previewText: "...") -} catch let error { - print("Error: \(error)") -} +try? SSReadingList.defaultReadingList()?.addReadingListItemWithURL(url, title: "NSHipster", previewText: "...") ~~~ ~~~{objective-c} diff --git a/2014-06-09-ios8.md b/2014-06-09-ios8.md index 74f80ab0..1b670635 100644 --- a/2014-06-09-ios8.md +++ b/2014-06-09-ios8.md @@ -86,7 +86,7 @@ import CoreMotion let lengthFormatter = NSLengthFormatter() let pedometer = CMPedometer() -pedometer.startPedometerUpdatesFromDate(NSDate(), withHandler: { data, error in +pedometer.startPedometerUpdatesFromDate(NSDate()) { data, error in if let data = data { print("Steps Taken: \(data.numberOfSteps)") @@ -98,7 +98,7 @@ pedometer.startPedometerUpdatesFromDate(NSDate(), withHandler: { data, error in print("Speed: \(lengthFormatter.stringFromMeters(speed)) / s") } } -}) +} ~~~ ## CMAltimeter @@ -110,11 +110,11 @@ import CoreMotion let altimeter = CMAltimeter() if CMAltimeter.isRelativeAltitudeAvailable() { - altimeter.startRelativeAltitudeUpdatesToQueue(NSOperationQueue.mainQueue(), withHandler: { data, error in + altimeter.startRelativeAltitudeUpdatesToQueue(NSOperationQueue.mainQueue()) { data, error in if let data = data { print("Relative Altitude: \(data.relativeAltitude)") } - }) + } } ~~~ @@ -149,12 +149,12 @@ The following example shows how statistics summed over the duration of the day c ~~~{swift} import HealthKit -let collection: HKStatisticsCollection? = nil//... +let collection: HKStatisticsCollection? = ... let statistics: HKStatistics? = collection?.statisticsForDate(NSDate()) let sources: [HKSource] = statistics?.sources ?? [] for source in sources { - if let quantity: HKQuantity = statistics?.sumQuantityForSource(source) { + if let quantity = statistics?.sumQuantityForSource(source) { if quantity.isCompatibleWithUnit(HKUnit.gramUnitWithMetricPrefix(.Kilo)) { let massFormatter = NSMassFormatter() let kilograms = quantity.doubleValueForUnit(HKUnit.gramUnitWithMetricPrefix(.Kilo)) @@ -194,11 +194,11 @@ NSStream.getStreamsToHostWithName("nshipster.com", ## NSString -localizedCaseInsensitiveContainsString -Also filed under: "small but solid fixes", is this convenience method for `NSString`: +Also filed under: "small but solid fixes", is this convenience method for `String`/`NSString`: ~~~{swift} -let string: String = "Café" -let substring: String = "É" +let string = "Café" +let substring = "É" string.localizedCaseInsensitiveContainsString(substring) // true ~~~ @@ -246,15 +246,15 @@ The Foundation URL Loading System has remained relatively unchanged since last y import Foundation let session = NSURLSession() -let task = session.dataTaskWithURL(NSURL(string: "http://nshipster.com")!, completionHandler: { data, response, error in +let task = session.dataTaskWithURL(NSURL(string: "http://nshipster.com")!) { data, response, error in // ... -}) +} let protectionSpace = NSURLProtectionSpace() let credentialStorage = NSURLCredentialStorage() -credentialStorage.getCredentialsForProtectionSpace(protectionSpace, task: task, completionHandler: { credentials in +credentialStorage.getCredentialsForProtectionSpace(protectionSpace, task: task) { credentials in // ... -}) +} ~~~ ## kUTTypeToDoItem @@ -275,6 +275,23 @@ Most users are completely unaware that most pictures taken with phones these day New to the Image I/O framework is a convenient new option for `CGImageDestination`: `kCGImageMetadataShouldExcludeGPS`, which does what you'd expect. +~~~{swift} +import UIKit +import ImageIO +import MobileCoreServices + +let image = ... +let fileURL = NSURL(fileURLWithPath: "/path/to/output.jpg") +let options: NSDictionary = [kCGImageDestinationLossyCompressionQuality as NSString: 0.75, + kCGImageMetadataShouldExcludeGPS as NSString: true] + +if let imageDestination = CGImageDestinationCreateWithURL(fileURL, kUTTypeJPEG, 1, nil), + let cgImage = image.CGImage +{ + CGImageDestinationAddImage(imageDestination, cgImage, options) + CGImageDestinationFinalize(imageDestination) +} +~~~ ~~~{objective-c} @import UIKit; @import ImageIO; @@ -346,6 +363,22 @@ Imagine: with CloudKit and LocalAuthentication, nearly all of the friction to cr LocalAuthentication works in terms of an `LAContext` class, which evaluates a specified policy, and gives a thumbs up or thumbs down on user authentication. At no point is any biometric information made available to the application—everything is kept safe on the hardware itself. +~~~{swift} +let context = LAContext() +var error: NSError? + +if context.canEvaluatePolicy(.DeviceOwnerAuthenticationWithBiometrics, error: &error) { + context.evaluatePolicy(.DeviceOwnerAuthenticationWithBiometrics, localizedReason: "...") { success, error in + if success { + // ... + } else { + print("Error: \(error)") + } + } +} else { + print("Error: \(error)") +} +~~~ ~~~{objective-c} LAContext *context = [[LAContext alloc] init]; NSError *error = nil; diff --git a/2014-11-03-uisplitviewcontroller.md b/2014-11-03-uisplitviewcontroller.md index 2c4ef0a2..9d993a86 100644 --- a/2014-11-03-uisplitviewcontroller.md +++ b/2014-11-03-uisplitviewcontroller.md @@ -82,7 +82,6 @@ override func viewDidLoad() { navigationItem.leftItemsSupplementBackButton = true } ~~~ - ~~~{objective-c} - (void)viewDidLoad { [super viewDidLoad]; @@ -111,13 +110,9 @@ There is one more optimization we can do for the iPhone 6+ via [`UISplitViewCont When the user first launches the app, we can make the master view controller fully displayed until the user selects a list item: ~~~{swift} -import UIKit - class SelectColorTableViewController: UITableViewController, UISplitViewControllerDelegate { private var collapseDetailViewController = true - // MARK: UITableViewController - override func viewDidLoad() { super.viewDidLoad() @@ -126,7 +121,7 @@ class SelectColorTableViewController: UITableViewController, UISplitViewControll // ... - // MARK: UITableViewDelegate + // MARK: - UITableViewDelegate override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { collapseDetailViewController = false @@ -139,13 +134,14 @@ class SelectColorTableViewController: UITableViewController, UISplitViewControll } } ~~~ - ~~~{objective-c} -@import UIKit; +// SelectColorTableViewController.h @interface SelectColorTableViewController : UITableViewController @end +// SelectColorTableViewController.m + @interface SelectColorTableViewController () @property (nonatomic) BOOL shouldCollapseDetailViewController; @@ -154,8 +150,6 @@ class SelectColorTableViewController: UITableViewController, UISplitViewControll @implementation SelectColorTableViewController -#pragma mark - UITableViewController - - (void)viewDidLoad { [super viewDidLoad];