Swift's Practical Combat Skills

Keywords: iOS Swift network JSON

Swift's Practical Combat Skills

From OC to Swift, the difference is quite big. This article records that I went from OC to Swift. OC Turn to some of the problems I encountered in Swift development process, and then write down the problems I encountered. In general, some of the techniques under Swift language are helpful to those in need.

  • Processing of OC Call Method

To call OC, we need to add @objc tag, general action-target processing method, notification processing method and so on.

@objc func onRefresh(){
    self.refreshCallback?()
}
  • Processing SEL selectors

Use method types such as # selector (method name)
eg.

`#selector(self.onRefresh))`  

A more detailed introduction can be found in this article: http://swifter.tips/selector/

Here's an example of adding callback handlers to mj_header and mj_footer using MJRefresh

self.mj_header.setRefreshingTarget(self, refreshingAction: #selector(self.onRefresh))
self.mj_footer.setRefreshingTarget(self, refreshingAction: #selector(self.onLoadMore))
  • Use of try keyword

Methods that may have exceptions use try? Methods for optional catch exceptions

let jsonStr=try?String(contentsOfFile: jsonPath!)
  • Class object parameters and class object parameter values

AnyClass is the formal parameter of the method, and the class name. self (modelClass.self) is the argument.

    func registerCellNib(nib:UINib,modelClass:AnyClass){
        self.register(nib, forCellReuseIdentifier: String(describing:modelClass.self))
    }

...
self.tableView?.registerCellNib(nib: R.nib.gameCell(), modelClass: GameModel.self)
  • Interthreaded calls

The main thread uses DispatchQueue.main, and the global sub-thread uses DispatchQueue.global(), and the methods can use sync, async, asyncAfter, and so on.

Here is an example of interthread invocation of network requests

let _  = URLSession.shared.dataTask(with: url, completionHandler: { [weak self] (data, response, error) in
            guard let weakSelf = self else {
                return
            }
            if error == nil {
                if let json = try? JSONSerialization.jsonObject(with: data!, options: .mutableContainers) {
                    let data = json as! [Any]
                    DispatchQueue.main.async {
                        weakSelf.suggestions = data[1] as! [String]
                        if weakSelf.suggestions.count > 0 {
                            weakSelf.tableView.reloadData()
                            weakSelf.tableView.isHidden = false
                        } else {
                            weakSelf.tableView.isHidden = true
                        }
                    }
                }
            }
        }).resume()
  • Use weak to prevent looping references in closures
URLSession.shared.dataTask(with: requestURL) {[weak self] (data, response, error) in
    guard let weakSelf = self else {
        return
    }
    weakSelf.tableView.reloadData()
}
  • Escape closure and non-escape closure
    Escape closure, which is called escape closure only after method execution is completed, usually asynchronous processing time-consuming tasks are done in the method. After the task is completed, the result is called escape closure by callback processing, which needs to be modified with the @escaping keyword.
    Non-escaping closures, called before method execution is complete, are called escaping closures, such as those used by the snapkit framework that are processed after method execution is complete.
    Closures after Swift3 default to non-escape( @noescape The declaration cannot be displayed), and this type cannot be displayed with the @noescape keyword modifier
    // Simulated network requests, completion closures are asynchronously delayed, so you need to add `escaping'to modify them.
    class func fetchVideos(completion: @escaping (([Video]) -> Void)) {
        DispatchQueue.global().async {
            let video1 = Video.init(title: "What Does Jared Kushner Believe", channelName: "Nerdwriter1")
            let video2 = Video.init(title: "Moore's Law Is Ending. So, What's Next", channelName: "Seeker")
            let video3 = Video.init(title: "What Bill Gates is afraid of", channelName: "Vox")
            var items = [video1, video2, video3]
            items.shuffle()
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.init(uptimeNanoseconds: 3000000000), execute: {
                completion(items)
            })
        }
    }
  • Packaging of Notification.Name

Notification name in swift3 is a special Notification.Name type. enum is used for encapsulation processing, and an extension of Notification Center is created to handle the sending of notification messages.

// Define Notification.Name enumeration
enum YTNotification: String {
    case scrollMenu
    case didSelectMenu
    case openPage
    case hideBar

    var stringValue: String {
        return "YT" + rawValue
    }

    // Enumeration members return the corresponding Notification.Name type
    var notificationName: NSNotification.Name {
        return Notification.Name.init(stringValue)
    }
}

extension NotificationCenter {
    func yt_post(custom notification: YTNotification, object anObject: Any?, userInfo aUserInfo: [AnyHashable : Any]? = nil) {
        self.post(name: notification.notificationName, object: anObject, userInfo: aUserInfo)
    }
}

Usage method
Adding notification observers uses the Notification.Name type value returned by notificationName of the YTNotification enumeration member
Messages are sent using YTNotification enumeration members

// Add Notification Observation
NotificationCenter.default.addObserver(self, selector: #selector(self.changeTitle(notification:)), name: YTNotification.scrollMenu.notificationName, object: nil)

// send message
NotificationCenter.default.yt_post(custom: YTNotification.scrollMenu, object: nil, userInfo: ["length": scrollIndex])

Lazy lazy lazy loading attributes that initialize variables only when used

    // The way closures are done
    let menuTitles = ["History", "My Videos", "Notifications", "Watch Later"]
    lazy var menuItems : [MenuItem] = {
        var tmenuItems = [MenuItem]()
        for menuTitle in menuTitles {
            let menuItem = MenuItem(iconImage: UIImage.init(named: menuTitle)!, title: menuTitle)
            tmenuItems.append(menuItem)
        }
        return tmenuItems
    }()

    // In the ordinary way,
    lazy var titles = ["A", "B"]
  • Type Judgment

Use is to determine the type and use if-let and as?

// MARK:- Examples of type checking
let sa = [
    Chemistry(physics: "Solid State Physics", equations: "hertz"),
    Maths(physics: "fluid dynamics", formulae: "Gigahertz"),
    Chemistry(physics: "Thermophysics", equations: "Decibel"),
    Maths(physics: "Astrophysics", formulae: "Megahertz"),
    Maths(physics: "differential equation", formulae: "Cosine series")]

var chemCount = 0
var mathsCount = 0
for item in sa {
    // If it is an instance of Chemistry type, return true, instead return false. Equivalent to isKindOfClass
    if item is Chemistry {
        chemCount += 1
    } else if item is Maths {
        mathsCount += 1
    }
}

// Use if-let and as? To determine types
for item in sa {
    // If it is an instance of Chemistry type, return true, instead return false. Equivalent to isKindOfClass
    if let _ = item as? Chemistry {
        chemCount += 1
    } else if let _ = item as? Maths {
        mathsCount += 1
    }
}

Use switch-case and as to determine types

// Any can represent any type, including method type
var exampleany = [Any]()
exampleany.append(12)
exampleany.append(3.14159)
exampleany.append("Any Example")
exampleany.append(Chemistry(physics: "Solid State Physics", equations: "Megahertz"))

// Use switch-case and as to determine types
for item2 in exampleany {
    switch item2 {
    case let someInt as Int:
        print("Integer value \(someInt)")
    case let someDouble as Double where someDouble > 0:
        print("Pi The value is \(someDouble)")
    case let someString as String:
        print("\(someString)")
    case let phy as Chemistry:
        print("theme '\(phy.physics)', \(phy.equations)")
    default:
        print("None")
    }
}
  • Swift uses KVC, and variables that perform KVC operations need to be tagged with @objc
class Feed: NSObject, HandyJSON  {
    // Use KVC to add @objc keyword
    @objc var id = 0
    var type = ""
    var payload: PayLoad?
    var user: PostUser?

    required override init() {}
}
  • CGRect-type operations in swift

The operation of CGRect type is simplified in swift. For example, there is an instance of CGRect type as frame. The following example illustrates the corresponding operation in OC. swift Next Grammar

OC Swift
CGRectGetMaxX(frame) frame.maxX
CGRectGetMinY(frame) frame.minY
CGRectGetMidX(frame) frame.midX
CGRectGetWidth(frame) frame.width
CGRectGetHeight(frame) frame.height
CGRectContainsPoint(frame, point) frame.contains(point)
  • Processing of Pointer in Swift

Detailed descriptions can be found in this article: http://swifter.tips/unsafe/
Here's an example of using the OC library RegexKitLite, where the return value is pointer type and needs to be converted to the corresponding swift object type

func composeAttrStr(text: String) -> NSAttributedString {
    // Rules of expression
    let emotionPattern = "\\[[0-9a-zA-Z\\u4e00-\\u9fa5]+\\]";
    // @ Rules
    let atPattern = "@[0-9a-zA-Z\\u4e00-\\u9fa5-_]+";
    // # Rules for Topic #
    let topicPattern = "#[0-9a-zA-Z\\u4e00-\\u9fa5]+#";
    // Rules for url links
    let urlPattern = "\\b(([\\w-]+://?|www[.])[^\\s()<>]+(?:\\([\\w\\d]+\\)|([^[:punct:]\\s]|/)))";
    let pattern = "\(emotionPattern)|\(atPattern)|\(topicPattern)|\(urlPattern)"

    var textParts = [TextPart]()

    (text as! NSString).enumerateStringsMatched(byRegex: pattern) { (captureCount: Int, capString: UnsafePointer<NSString?>?, capRange: UnsafePointer<NSRange>?, stop: UnsafeMutablePointer<ObjCBool>?) in
        let captureString = capString?.pointee as! String
        let captureRange = capRange?.pointee as! NSRange

        let part = TextPart()
        part.text = captureString
        part.isSpecial = true
        part.range = captureRange
        textParts.append(part)
    }
// ...
}
  • Protocols that can only be implemented by classes have a scenario. As a delegate, protocol needs to specify the type of delegate as ptotocol when it needs to be modified with weak keywords. This ptotocol needs to add class modifiers, such as the protocol below, because the object of class type only has reference counting, so there is weak. Concepts, struct type without reference count is without weak concept
/// Protocol for Interactive Use of ImageViewer and ImageCell
protocol YTImageCellProtocol : class {
    // Cell's click event handles dismiss
    func imageCell(_ imageCell: YTImageCell, didSingleTap: Bool);
}

Posted by Jezza22 on Fri, 09 Aug 2019 04:06:38 -0700