Upload
nissan-tsafrir
View
1.962
Download
0
Embed Size (px)
Citation preview
Swift - One Step Forward from ObjC
Nissan Tsafrir // @ntsafrir // { Pix & Byte }
www.pixandbyte.com
Swift Fast . Modern . Safe . Interactive
AGENDA
Rewrite few familiar Cocoa Touch code examples from Obj-C to Swift by learning to use Closures, Enums, Switch-Case with Pattern matching and more.
Replace complex macros with functions or generics
Replace complex macros with functions
ObjC !
NSLocalizedString(@"OK",@"a comment")
Replace complex macros with functions
Swift !
NSLocalizedString("OK", comment:"comment")
Replace complex macros with functions
#define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment) \ [bundle localizedStringForKey:(key) value:(val) table:(tbl)] !// shorthand macros #define NSLocalizedString(key, comment) … #define NSLocalizedStringFromTable(key, tbl, comment) … !!
ObjC !
NSLocalizedString(@"OK",@"a comment")
Replace complex macros with functions
!// In Foundation Module:
!func NSLocalizedString(key: String, tableName: String? = default, bundle:
NSBundle = default, value: String = default, #comment: String) -> String
Swift
let str = NSLocalizedString("OK", comment:"comment")
Type '(String, tableName: String?, bundle: NSBundle, value: String, comment: String)' does not conform to protocol 'StringLiteralConvertible'
let strError = NSLocalizedString("OK") // Error
func NSLocalizedString(key: String, tableName: String? = default, bundle: NSBundle = default, value: String = default, #comment: String) -> String
# Same external and local param name. Useful for global functions
Functions - Default parameter values - External parameter name used when calling the function - Local parameter name available only in the function scope - Shorthand external parameter names - #comment
let str334 = NSLocalizedString("OK", "") // Error
class FooClass { func updateText(text: String, color: UIColor) -> String { return "\(text) color:\(color)" } } !let fc = FooClass() fc.updateText("tlv", UIColor.blueColor()) !!fc.updateText("tlv", color: UIColor.blueColor()) //OK
!Methods - Functions associated with type “Swift gives the first parameter name in a method a local parameter name by default, and gives the second and subsequent parameter names both local and external parameter names by default.”
What you can do with your exiting complex macros
• Replace the macros with C functions • Create ObjC wrapper class to implement/use the macros as
functions. • Use Swift functions with defaults
Closure
"Function closures capture local state variables!(Objects are state data with attached behavior;!Closures are behaviors with attached state data!
and without the overhead of classes.)"!!
Peter Norvig
ObjC - Blocks
__block NSNumber *someVal = @10; // strong and mutable __weak typeof(self) weakSelf = self; ![locationManager getCurrentLocationWithCompletion:^(CLLocation *location) { if (!location) { return; } ! if (weakSelf.completionBlock) { // Safer to use strongSelf in here weakSelf.completionBlock(location); } someVal = @20; }]; !
!typedef void(^ PBUpdateLocationCompletion)(CLLocation * location); !@property (copy, nonatomic) PBUpdateLocationCompletion completionBlock;
ObjC - Blocks
var successHandler : ((feed: Array) -> ())? var someValue = 1 !successHandler = { feed in self.refreshFeed(feed) someValue = 2 }
Swift - Closure
request.successHandler = { [unowned self] feed in self.refreshFeed(feed) }
Capture List
func blurFilter(radius: Int) -> (image: UIImage) -> (UIImage) { return { image in return BlurImage(image, radius) } } !let blur20 = blurFilter(20) !let blurredImage = blur20(image)
Closure Factory Method
Image Filter Example
Replace Delegate with Closures
class AddViewController : UIViewController { var didCancel : ((AddViewController) -> ())? var didFinish : ((AddViewController, name: String) -> ())? }
class AddViewController : UIViewController { typealias CancelHandler = (AddViewController) -> () typealias FinishHandler = (AddViewController, name: String) -> () var didCancel : CancelHandler? var didFinish : FinishHandler? }
Replace Delegate with Closures
if let addVC = navigationVC.topViewController as? AddViewController { ! addVC.didCancel = { controller in self.dismissViewControllerAnimated(true, completion: nil) } addVC.didFinish = { controller, name in self.dismissViewControllerAnimated(true, completion: nil) self.addObjectWithName(name) } }
Delegate with Closures
Replace if -isEqual-else with switch-case and pattern matching
if ([segue.identifier isEqualToString:@"showDetails"]) { //… } else if ([segue.identifier isEqualToString:"add"]) { //… }
Replace if-isEqual-else with switch-case and pattern matching
ObjC
override func prepareForSegue(segue: UIStoryboardSegue, sender: …) { if segue.identifier == "showDetail" { //... } else if segue.identifier == "add" { //.. } }
Replace if-isEqual-else with switch-case and pattern matching
Swift
override func prepareForSegue(segue: UIStoryboardSegue, sender: …) { switch segue.identifier { case "showDetail": //… case "add": //… default: break } }
Replace if-isEqual-else with switch-case and pattern matching
Swift
Switches support any kind of data and a wide variety of comparison operations.
if let nvc = segue.destinationViewController as? UINavigationController { … }
Replace if-isEqual-else with switch-case and pattern matching
Optional binding that use optional down casting
“Try to access viewController as a navigation controller. If this is successful, set a new temporary constant called nvc to the value
stored in the returned optional UINavigationController.”
override func prepareForSegue(segue: UIStoryboardSegue, sender: … { switch segue.destinationViewController { case let nvc as UINavigationController: … case let dvc as DetailsViewController: … default: break }
}
Replace if-isEqual-else with switch-case and pattern matching
Another switch case pattern matching example
Error Reporting
var error : NSError? let url = NSURL(string: "http://www.apple.com") let data = NSData(contentsOfURL: url, options: NSDataReadingOptions.allZeros, error: &error) !if let anError = error { println("failure: \(anErrror.localizedDescription())") }
Results Enumeration and associated value
With NSErrorPointer (NSError?)
enum ServerResult { case Result (NSData) case Error (NSError) } !let success = ServerResult.Result(data) !let failure = ServerResult.Error(NSError(domain: "MyErrorDomain", code: 1, userInfo: nil))
Results Enumeration and associated value
switch success { case let .Result(data): let serverResponse = "Received data \(data)" case let .Error(error): let serverResponse = "Failure... \(error)" }
Using Enums with associated value
Setting Defaults with ?? operator
ObjC !!
NSString *name = text ? text : "default-name";
Swift var text : String? … !!let name = text ?? "default-name";
Setting Defaults with ?? operator
CoreFoundation and other C API Get free ARC!
/* Shape */ let pathRef = CGPathCreateMutable() CGPathMoveToPoint(pathRef, nil, 0, 0) CGPathAddLineToPoint(pathRef, nil, 400, 0) CGPathAddLineToPoint(pathRef, nil, 400, 320) CGPathAddLineToPoint(pathRef, nil, 0, 320) CGPathAddLineToPoint(pathRef, nil, 0, 0) CGPathCloseSubpath(pathRef) !!// Compiler take care memory management in most cases So no need for these: CGPathRelease (pathRef) or CFRelease(pathRef)
Swift compiler gives CoreFoundation, CoreGraphics and others ARC
// In C ! CGRectMake(0, 0, 320, 480) !// In swift - much more readable ! CGRect(x: 0, y: 0, width: 400, height: 320)
CoreGraphic Structs
GCD a bit more cleaner
GCD a bit more cleaner
let group = dispatch_group_create() dispatch_group_enter(group) dispatch_group_leave(group) dispatch_group_notify(group,dispatch_get_main_queue()) { … } !dispatch_async(dispatch_get_main_queue()) { // trailing closure body }
Type inferred, trailing closure
Singleton : Replace dispatch_once with inner struct
Singleton
You can use dispatch_once but we hope for better wayclass Singleton : NSObject { class var sharedInstance : Singleton { struct Static { static var onceToken : dispatch_once_t = 0 static var instance : Singleton? = nil } dispatch_once(&Static.onceToken) { Static.instance = Singleton() } return Static.instance! } override init() { println("yay"); } } !Singleton.sharedInstance
Singleton: Replace dispatch_once with inner struct
Class variable currently not supported (xcode 6 beta 7) But structs do support static constants
class Singleton : NSObject { class var sharedInstance : Singleton { struct Static { static let instance : Singleton = Singleton() } return Static.instance } override init() { println("yay"); } }
Follow https://github.com/hpique/SwiftSingleton for updates
References
!- Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/il/jEUH0.l
- Apple Inc. “Using Swift with Cocoa and Objective-C.” iBooks. https://itun.es/il/1u3-0.l
- WWDC 14 Swift videos (https://developer.apple.com/videos/wwdc/2014/)
- Apple’s Dev Forums
- https://github.com/hpique/SwiftSingleton