Swift interoperability: Using the Cocoa Design model (Swift 2.0 version)-B

Source: Internet
Author: User
Tags instance method type casting



This page contains content:


    • Delegate (delegation)

    • Fault handling (Error handling)

    • Key value observation (Key-value observing)

    • Target-action Mode (target-action)

    • Type matching and uniform specification (introspection)

    • API availability


Using some of the existing design patterns of Cocoa is one of the most effective ways to help developers develop a reasonable design mindset, stable performance, and good extensibility applications. These schemas all depend on the classes defined in Objective-c. Because of the interoperability of swift and objective-c, you can still use these design patterns in swift code. In some cases, you can even use the features of the Swift language to extend or simplify these Cocoa design patterns, making these design patterns more powerful and easier to use.



Commissioned



In Swift and Objective-c, a delegate is typically represented by a protocol that defines the interaction method and follows the specification's delegate properties. When you inherit a delegate in Swift, as opposed to objective-c, the internal implementation has changed, although the inheritance pattern is the same. Just like in Objective-c, before you send a message to a delegate, whether it's nil or not, you're going to look at it, and if the defined method is a non-mandatory method, you'll see whether or not the delegate implements the method. In Swift, these cumbersome and unnecessary behavioral problems can be effectively eliminated by maintaining type-safe features.



The code listed below illustrates this process:


    1. Check mydelegate is not nil.

    2. Check whether the mydelegate implements the inherited Window:willusefullscreencontentsize: method.

    3. If MyDelegate is not nil and implements the Window:willusefullscreencontentsize: method, then call the method and assign the return value of the method to a property named Fullscreensize.

    4. Output the return value of the method in the console.

// @inteface MyObject : NSObject
// @property (nonatomic, weak) id delegate;
// @end
if let fullScreenSize = myDelegate?.window?(myWindow, willUseFullScreenContentSize: mySize) {
    println(NSStringFromSize(fullScreenSize))
}


Note: In an app written entirely using Swift, when defining the delegate property, it acts as an indeterminate nswindowdelegate object and sets the initial value to nil.



Error handling



In Cocoa, the method that produces the error nserror the pointer parameter as the last parameter, which is populated by the Nserror object when the error occurs. Swift automatically translates the method that generates errors in the objective-c into Swift's native error handling functionality.



Note: Methods that produce errors, such as proxy methods or a completion handler that takes a Nserror object as a parameter, will not be handled by Swift as a throw method.



For example, consider the following objective-c method from Nsfilemanager:


-(BOOL) Removeitematurl: (Nsurl *) URL Error: (NSERROR * *) error;


In Swift, it will be imported like this:


Func Removeitematurl (Url:nsurl) throws


Note that when the Removeitematurl (_:) method is imported by Swift, the return value type is Void, there is no error parameter, but a throws declaration.



If the last non-closure parameter of the Objective-c method is a nserror * * type, Swift replaces it with the throws keyword to indicate that the method can throw an error. If the error parameter of the Objective-c method is also its first argument, Swift attempts to further simplify the name of the method by removing the Andreturnerror suffix in the first part of the selector, if one exists. If another method is declared with the resulting selector, the method name cannot be changed.



If the Objective-c method that produces the error returns a BOOL value that represents the success or failure of the method call, Swift converts the return value of the function to void. Similarly, if the Objective-c method that produces the error returns a nil value to indicate the failure of the method call, Swift converts the return value of the function to a non-optional value type.



Otherwise, if there is no contract to infer, the method remains unchanged.



Capturing and handling errors



In Objective-c, error handling is optional, meaning that the error generated by the method is ignored unless you provide an error pointer. In Swift, calling a method that throws an error requires error handling to be displayed.



Here's how to handle the error generated by the calling method in Objective-c:


NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *URL = [NSURL fileURLWithPath:@"/path/to/file"];
NSError *error = nil;
BOOL success = [fileManager removeItemAtURL:URL error:&error];
if (!success && error) {
    NSLog(@"Error: %@", error.domain);
}


The following is the equivalent code in Swift:


let fileManager = NSFileManager.defaultManager()
let URL = NSURL.fileURLWithPath("/path/to/file")
do {
    try fileManager.removeItemAtURL(URL)
} catch let error as NSError {
    print("Error: \(error.domain)")
}


Throw error



If an error is present in the Objective-c method, then the error is used to populate the error pointer parameter of the method.


// an error occurred
if (errorPtr) {
   *errorPtr = [NSError errorWithDomain:NSURLErrorDomain
                                   code:NSURLErrorCannotOpenFile
                               userInfo:nil];
}


If an error is present in the Swift method, then the error is thrown and automatically passed to the caller:


An error Occurredthrow Nserror (Domain:nsurlerrordomain, Code:nsurlerrorcannotopenfile, Userinfo:nil)


If the OBJECTIVE-C code calls the Swift method to throw an error, the error is automatically passed to the wrong pointer parameter of the bridged Objective-c method.



For example, consider the Readfromfilewrapper (_:oftype:) method in Nsdocument. In Objective-c, the last parameter of this method is Nserror * *. When the method is overridden in a subclass of Swift's Nsdocument, the method replaces the error pointer parameter with throws.


class SerializedDocument: NSDocument {
    static let ErrorDomain = "com.example.error.serialized-document"
    var representedObject: [String: AnyObject] = [:]
    override func readFromFileWrapper(fileWrapper: NSFileWrapper, ofType typeName: String) throws {
        guard let data = fileWrapper.regularFileContents else {
            throw NSError(domain: NSURLErrorDomain, code: NSURLErrorCannotOpenFile, userInfo: nil)
        }
        if case let JSON as [String: AnyObject] = try NSJSONSerialization.JSONObjectWithData(data, options: []) {
            self.representedObject = JSON
        } else {
            throw NSError(domain: SerializedDocument.ErrorDomain, code: -1, userInfo: nil)
        }
    }
}


If a method cannot create an object using the contents of a regular file, a Nserror object is thrown. If the method is called from Swift code, the error is passed to its calling domain. If the method is called in the Objective-c code, the error is passed to the error pointer parameter.



In Objective-c, error handling is optional, meaning that the error generated by the method is ignored unless you provide an error pointer. In Swift, calling a method that throws an error requires explicit error handling.



Attention:



Although Swift's error handling is similar to Objective-c's exception handling, it is a completely independent feature. If a objective-c method throws a run-time exception, Swift triggers a run-time error. There is no way to recover exceptions from objective-c directly in Swift. Any exception handling behavior in the OBJECTIVE-C code must be implemented with Swift.



Key value Observation



Key-value observation is a mechanism that allows an object to get notifications of changes to specific properties of other objects. As long as your class inherits from the NSObject class, you can use the key values in the Swift class to observe. You can use the following three steps in Swift to achieve key-value observations:



1. Add dynamic modifiers for the properties you want to observe. For more information on dynamic, see requiring dynamic dispatchclass Myobjecttoobserve:nsobject {


  dynamic var myDate = NSDate()
    func updateDate() {
        myDate = NSDate()
    }
}


2. Create a global context variable.


private var mycontext = 0


3. Add an observer to the key-path, rewrite the observeValueForKeyPath:ofObject:change:context: function, and remove the observer in the Deinit.


class MyObserver: NSObject {
    var objectToObserve = MyObjectToObserve()
    override init() {
        super.init()
        objectToObserve.addObserver(self, forKeyPath: "myDate", options: .New, context: &myContext)
    }    
    override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [NSObject : AnyObject]?, context: UnsafeMutablePointer) {
        if context == &myContext {
            if let newValue = change?[NSKeyValueChangeNewKey] {
                print("Date changed: \(newValue)")
            }
        } else {
            super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
        }
    }
    deinit {
        objectToObserve.removeObserver(self, forKeyPath: "myDate", context: &myContext)
    }
}


Target-action Mode (target-action)



When a particular event occurs and an object is required to send a message to another object, we typically use the Cocoa target-action design pattern. The target-action model in Swift and objective-c is basically similar. In Swift, you can use the Selector type to achieve the selectors effect in objective-c. See an example of using target-action design pattern in Swift in Objective-c selectors.



Type matching and uniform specification (introspection)



In Objective-c, you can use the Iskindofclass: method to check whether an object is a specified type, and you can use the Conformstoprotocol: method to check whether an object follows the specification of a particular protocol. In Swift, you can use the IS operator to do the above functions, or you can use the as? Matches the specified type down.



You can use the IS operator to check whether an instance is a specified subclass. If the instance is a specified subclass, then the is operation evaluates to true, and vice versa to false.


if object is UIButton {
    // object is of type UIButton
} else {
    // object is not of type UIButton
}


You can also use as? Operator tries to match sub-types down, as? The operator returns an indeterminate value, which is used in conjunction with the If-let statement.


if let button = object as? UIButton {
    // object is successfully cast to type UIButton and bound to button
} else {
    // object could not be cast to type UIButton
}


Please see more information in the Type Casting.



The syntax for checking the matching protocol is the same as checking the syntax of the matching class, and the following is the use of as? Check for examples of matching protocols:


if let dataSource = object as? UITableViewDataSource {
    // object conforms to UITableViewDataSource and is bound to dataSource
} else {
    // object not conform to UITableViewDataSource
}


Note that when the match is done, DataSource is converted to the Uitableviewdatasource type, so you can only access and invoke the properties and methods defined by the Uitableviewdatasource protocol. When you want to do something else, you must convert it to a different type.



You can view more information in protocols.



API availability



Some classes and methods are not available for all versions of all platforms in your app. To ensure that your app is functionally adaptable to differences, you need to check the availability of these APIs.



In Objective-c, we use the Respondstoselector: and Instancesrespondtoselector: Methods to check whether a class or instance method is available. If not checked, the calling method throws a Nsinvalidargumentexception "unrecognized selector sent to instance" exception. For example, the Requestwheninuseauthorization method is available only for Cllocationmanager instances in iOS8.0 and OS X 10.10.


if ([CLLocationManager instancesRespondToSelector: @selector (requestWhenInUseAuthorization)]) {
   // method is available
} else {
   // method is not available
}


In Swift, attempts to invoke a method that is not supported by a target platform version will report a compile-time error.



Here is the previous example, using Swift to write:


let locationManager = CLLocationManager()
locationManager.requestWhenInUseAuthorization()
// error: only available on iOS 8.0 or newer


If the target of the app is less than ios8.0 or the Osx10.10,requestwheninuseauthorization () method is not available, the compiler reports an error.



Swift code can use API availability as a runtime condition to judge. Usability checks can be used in conditions of a control flow statement, such as a if,guard or while statement.



With the example above, you can use the IF statement to check availability and call Requestwheninuseauthorization () only if the method is available at run time.


let locationManager = CLLocationManager()
if #available(iOS 8.0, OSX 10.10, *) {
    locationManager.requestWhenInUseAuthorization()
}


Alternatively, you can use the Guard statement to check availability and exit the scope unless the current target meets the requirements. This approach simplifies the logic of handling different platform functions.


let locationManager = CLLocationManager()
guard #available(iOS 8.0, OSX 10.10, *) else { return }
locationManager.requestWhenInUseAuthorization()


Each platform parameter includes the platform name listed below, followed by the corresponding version number. The last parameter is an asterisk (*), which is used to handle future potential platforms.



Platform Name:


    • Ios

    • Iosapplicationextension

    • Os x

    • Osxapplicationextension

    • WatchOS


All Cocoa APIs provide availability information, so you can confidently write code for the platform your application is targeting.



You can simplify the usability check of your API by labeling declarations @available attributes. @available properties Use the same syntax as #available to do run-time checks, and the parameters are separated by commas from the platform version requirements.



For example:


@available(iOS 8.0, OSX 10.10, *)
func useShinyNewFeature() {
    // ...
}


Note: Using the @available attribute tagging method makes it possible to safely use the APIs available to meet specific platform requirements without explicitly checking for availability.



Swift interoperability: Using the Cocoa Design model (Swift 2.0 version)-B


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.