popeyelau / wiki

📒Wiki for many useful notes, source, commands and snippets.
3 stars 0 forks source link

Swift Tips & Extensions #8

Open popeyelau opened 5 years ago

popeyelau commented 5 years ago
extension String {
    var hexColor: UIColor {
        let hex = trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
        var int = UInt32()
        Scanner(string: hex).scanHexInt32(&int)
        let a, r, g, b: UInt32
        switch hex.count {
        case 3: // RGB (12-bit)
            (a, r, g, b) = (255, (int >> 8) * 17, (int >> 4 & 0xF) * 17, (int & 0xF) * 17)
        case 6: // RGB (24-bit)
            (a, r, g, b) = (255, int >> 16, int >> 8 & 0xFF, int & 0xFF)
        case 8: // ARGB (32-bit)
            (a, r, g, b) = (int >> 24, int >> 16 & 0xFF, int >> 8 & 0xFF, int & 0xFF)
        default:
            return .clear
        }
        return UIColor(red: CGFloat(r) / 255, green: CGFloat(g) / 255, blue: CGFloat(b) / 255, alpha: CGFloat(a) / 255)
    }
}
popeyelau commented 5 years ago
extension Bundle {
    var appName: String {
        return infoDictionary?["CFBundleName"] as! String
    }

    var bundleId: String {
        return bundleIdentifier!
    }

    var versionNumber: String {
        return infoDictionary?["CFBundleShortVersionString"] as! String
    }

    var buildNumber: String {
        return infoDictionary?["CFBundleVersion"] as! String
    }
}
popeyelau commented 5 years ago

// Builder allows you to create an instance and setting it up in a simple way:
protocol Builder {}

extension Builder {
    public func with(configure: (inout Self) -> Void) -> Self {
        var this = self
        configure(&this)
        return this
    }
}

// We need NSObject to conform to Builder to enable it for all subclasses of it 😊
extension NSObject: Builder {}

private let tableView = UITableView(frame: .zero, style: .plain).with { tableView in
    tableView.backgroundColor = .white
    tableView.separatorColor = .darkGray
    tableView.separatorInset = UIEdgeInsets(top: 0, left: 0, bottom: 10.0, right: 0)
    tableView.allowsMultipleSelection = true
}
popeyelau commented 5 years ago
extension UIView {
    func addSubviews(_ subviews: UIView...) {
        subviews.forEach(addSubview)
    }
}

// Add multiple subviews in a single line
view.addSubviews(imageView, slider, view, label)
popeyelau commented 5 years ago

extension ClassNameProtocol { static var className: String { return String(describing: self) }

var className: String {
    return type(of: self).className
}

}

extension NSObject: ClassNameProtocol {}

UIView.className // "UIView" UILabel().className // "UILabel"

popeyelau commented 5 years ago
popeyelau commented 5 years ago
extension Bundle {
    func decode<T: Decodable>(_ type: T.Type, from filename: String) -> T {
        guard let json = url(forResource: filename, withExtension: nil) else {
            fatalError("Failed to locate \(filename) in app bundle.")
        }

        guard let jsonData = try? Data(contentsOf: json) else {
            fatalError("Failed to load \(filename) from app bundle.")
        }

        let decoder = JSONDecoder()

        guard let result = try? decoder.decode(T.self, from: jsonData) else {
            fatalError("Failed to decode \(filename) from app bundle.")
        }

        return result
    }
}

let items = Bundle.main.decode([ResponseItem].self, from: "Response.json")
popeyelau commented 5 years ago

dispatchGroup.enter() task1 { dispatchGroup.leave() }

dispatchGroup.enter() task2 { dispatchGroup.leave() }

dispatchGroup.notify(queue: .main) { //done }

popeyelau commented 5 years ago
struct User: CustomStringConvertible {
    let id: Int
    var description: String {
        return "User \(id)"
    }
}

let users = [User(id: 1), User(id: 1), User(id: 2)]
print(users.distinct { $0.id }) //[User 1, User 2]
popeyelau commented 5 years ago

let array = ["foo", "bar"] array.remove(element: "foo") array // ["bar"]

popeyelau commented 5 years ago
public extension UIWindow {

    /// SwifterSwift: Switch current root view controller with a new view controller.
    ///
    /// - Parameters:
    ///   - viewController: new view controller.
    ///   - animated: set to true to animate view controller change (default is true).
    ///   - duration: animation duration in seconds (default is 0.5).
    ///   - options: animation options (default is .transitionFlipFromRight).
    ///   - completion: optional completion handler called after view controller is changed.
    public func switchRootViewController(
        to viewController: UIViewController,
        animated: Bool = true,
        duration: TimeInterval = 0.5,
        options: UIView.AnimationOptions = .transitionFlipFromRight,
        _ completion: (() -> Void)? = nil) {

        guard animated else {
            rootViewController = viewController
            completion?()
            return
        }

        UIView.transition(with: self, duration: duration, options: options, animations: {
            let oldState = UIView.areAnimationsEnabled
            UIView.setAnimationsEnabled(false)
            self.rootViewController = viewController
            UIView.setAnimationsEnabled(oldState)
        }, completion: { _ in
            completion?()
        })
    }

}
popeyelau commented 5 years ago
import Foundation

struct Response<T: Decodable>: Decodable{
    let code: Int
    let msg: String
    let data: T
}

struct User: Decodable {
    let name: String
    let bio: String
}
let json = """
{
    "code": 200,
    "msg": "hello world",
    "data": {
        "name": "Popeye",
        "bio": "五行缺脑"
    }
}
"""

let data = Data(json.utf8)
let decoder = JSONDecoder()

if let user = try? decoder.decode(Response<User>.self, from: data).data {
   print(user.name, user.bio) //Popeye 五行缺脑
}
popeyelau commented 5 years ago

通过实现init(from decoder:) 方法自定义解析过程

struct PagedResponse<T: Decodable>: Decodable {
    let code: Int
    let message: String
    let page: Int
    let total: Int
    let data: [T]?

    enum CodingKeys: String, CodingKey {
        case code
        case message
        case data
    }

    enum SubCodingKeys: String, CodingKey {
        case data
        case page
        case total
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        code = try container.decode(Int.self, forKey: .code)
        message = try container.decode(String.self, forKey: .message)

        let nestedContainer = try container.nestedContainer(keyedBy: SubCodingKeys.self, forKey: .data)
        data = try nestedContainer.decodeIfPresent([T].self, forKey: .data)
        page = try nestedContainer.decode(Int.self, forKey: .page)
        total = try nestedContainer.decode(Int.self, forKey: .total)
    }
}

struct Item: Decodable {
    let title: String
    let key: String
}

let json = """
{
"code": 200,
"message": "success",
"data": {
    "data": [{
                "title" : "title1",
                "key": "key1"
            },
            {
                "title" : "title2",
                "key": "key2"
            },
            {
                "title" : "title3",
                "key": "key3"
            }],
        "page": 1,
        "total": 3
    }
}
"""

let data = Data(json.utf8)
let decoder = JSONDecoder()
guard let resp = try? decoder.decode(PagedResponse<Item>.self, from: data) else {
    fatalError()
}
print(resp.code, resp.message, resp.page, resp.total)
if let items = resp.data {
    items.forEach { print($0.title, $0.key) }
}

/*
200 success 1 3
title1 key1
title1 key1
title1 key1
 */

About Codable

popeyelau commented 5 years ago
struct Popeye: Codable {
    let name: String
    let bio: String
    let github: String
    let telegram: String
    let luckNumbers: [Int]

    enum CodingKeys: String, CodingKey {
        case name
        case meta
    }

    enum SubCodingKeys: String, CodingKey {
        case bio
        case github
        case telegram
        case luckNumbers = "numbers"
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let name = try container.decode(String.self, forKey: .name)

        let nestedContainer = try container.nestedContainer(keyedBy: SubCodingKeys.self, forKey: .meta)
        let bio = try nestedContainer.decode(String.self, forKey: .bio)
        let github = try nestedContainer.decode(String.self, forKey: .github)
        let telegram = try nestedContainer.decode(String.self, forKey: .telegram)

        //数组 nestedUnkeyedContainer
        var numbers: [Int] = []
        var nestedUnkeyedContainer = try nestedContainer.nestedUnkeyedContainer(forKey: .luckNumbers)
        while !nestedUnkeyedContainer.isAtEnd {
            let number = try nestedUnkeyedContainer.decode(Int.self)
            numbers.append(number)
        }

        // call constructor
        self.name = name
        self.bio = bio
        self.github = github
        self.telegram = telegram
        self.luckNumbers = numbers
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(name, forKey: .name)

        var nestedContainer = container.nestedContainer(keyedBy: SubCodingKeys.self, forKey: .meta)
        try nestedContainer.encode(bio, forKey: .bio)
        try nestedContainer.encode(github, forKey: .github)
        try nestedContainer.encode(telegram, forKey: .telegram)

        var nestedUnkeyedContainer = nestedContainer.nestedUnkeyedContainer(forKey: .luckNumbers)
        try luckNumbers.forEach {
            try nestedUnkeyedContainer.encode($0)
        }
    }
}
let json = """
{
"name" : "Popeye",
"meta": {
    "bio": "五行缺脑",
    "github": "https://github.com/popeyelau",
    "telegram": "https://t.me/PopeyeLau",
    "numbers": [1,2,3,4,5]
    }
}
"""

let data = Data(json.utf8)
let decoder = JSONDecoder()
guard let popeye = try? decoder.decode(Popeye.self, from: data) else {
    fatalError()
}

print(popeye)

let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted

if let data = try? encoder.encode(popeye),
    let prettyJson = String(bytes: data, encoding: .utf8) {
   print(prettyJson)
}

/*Output:

Popeye(name: "Popeye", bio: "五行缺脑", github: "https://github.com/popeyelau", telegram: "https://t.me/PopeyeLau", luckNumbers: [1, 2, 3, 4, 5])

{
  "name" : "Popeye",
  "meta" : {
    "github" : "https:\/\/github.com\/popeyelau",
    "telegram" : "https:\/\/t.me\/PopeyeLau",
    "numbers" : [
      1,
      2,
      3,
      4,
      5
    ],
    "bio" : "五行缺脑"
  }
}

*/