├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── Package.swift ├── README.md ├── Sources └── Then │ └── Then.swift ├── Tests └── ThenTests │ └── ThenTests.swift ├── Then.podspec └── codecov.yml /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | test: 9 | runs-on: macOS-latest 10 | env: 11 | PROJECT: Then.xcodeproj 12 | SCHEME: Then-Package 13 | CODECOV_PACKAGE_NAME: Then 14 | strategy: 15 | matrix: 16 | env: 17 | - sdk: iphonesimulator 18 | destination: platform=iOS Simulator,name=iPhone 11 Pro,OS=latest 19 | 20 | - sdk: macosx 21 | destination: arch=x86_64 22 | 23 | - sdk: appletvsimulator 24 | destination: platform=tvOS Simulator,name=Apple TV,OS=latest 25 | 26 | steps: 27 | - uses: actions/checkout@v1 28 | 29 | - name: List SDKs and Devices 30 | run: xcodebuild -showsdks; instruments -s 31 | 32 | - name: Generate Xcode Project 33 | run: swift package generate-xcodeproj 34 | 35 | - name: Build and Test 36 | run: | 37 | set -o pipefail && xcodebuild clean build test \ 38 | -project "$PROJECT" \ 39 | -scheme "$SCHEME" \ 40 | -sdk "$SDK" \ 41 | -destination "$DESTINATION" \ 42 | -configuration Debug \ 43 | -enableCodeCoverage YES \ 44 | CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty -c; 45 | env: 46 | SDK: ${{ matrix.env.sdk }} 47 | DESTINATION: ${{ matrix.env.destination }} 48 | 49 | - name: Upload Code Coverage 50 | run: | 51 | bash <(curl -s https://codecov.io/bash) \ 52 | -X xcodeplist \ 53 | -J "$CODECOV_PACKAGE_NAME" 54 | env: 55 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | sourcekitten-output.json 3 | docs/ 4 | /.build 5 | /Packages 6 | /*.xcodeproj 7 | **/xcuserdata 8 | **/xcshareddata 9 | Pods/ 10 | Carthage/ 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Suyeol Jeon (xoul.kr) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "Then", 7 | platforms: [ 8 | .macOS(.v10_10), .iOS(.v9), .tvOS(.v9), .watchOS(.v2) 9 | ], 10 | products: [ 11 | .library(name: "Then", targets: ["Then"]), 12 | ], 13 | targets: [ 14 | .target(name: "Then"), 15 | .testTarget(name: "ThenTests", dependencies: ["Then"]), 16 | ] 17 | ) 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Then 2 | 3 | ![Swift](https://img.shields.io/badge/Swift-5.0-orange.svg) 4 | [![CocoaPods](http://img.shields.io/cocoapods/v/Then.svg)](https://cocoapods.org/pods/Then) 5 | [![Build Status](https://travis-ci.org/devxoul/Then.svg?branch=master)](https://travis-ci.org/devxoul/Then) 6 | 7 | ✨ Super sweet syntactic sugar for Swift initializers. 8 | 9 | ## At a Glance 10 | 11 | Initialize UILabel **then** set its properties. 12 | 13 | ```swift 14 | let label = UILabel().then { 15 | $0.textAlignment = .center 16 | $0.textColor = .black 17 | $0.text = "Hello, World!" 18 | } 19 | ``` 20 | 21 | This is equivalent to: 22 | 23 | ```swift 24 | let label: UILabel = { 25 | let label = UILabel() 26 | label.textAlignment = .center 27 | label.textColor = .black 28 | label.text = "Hello, World!" 29 | return label 30 | }() 31 | ``` 32 | 33 | ## Tips and Tricks 34 | 35 | - You can use `then()` to all of `NSObject` subclasses. 36 | 37 | ```swift 38 | let queue = OperationQueue().then { 39 | $0.maxConcurrentOperationCount = 1 40 | } 41 | ``` 42 | 43 | - Want to use with your own types? Just make extensions. 44 | 45 | ```swift 46 | extension MyType: Then {} 47 | 48 | let instance = MyType().then { 49 | $0.really = "awesome!" 50 | } 51 | ``` 52 | 53 | - Use `with()` when copying the value types. 54 | 55 | ```swift 56 | let newFrame = oldFrame.with { 57 | $0.size.width = 200 58 | $0.size.height = 100 59 | } 60 | newFrame.width // 200 61 | newFrame.height // 100 62 | ``` 63 | 64 | - Use `do()` to do something with less typing. 65 | 66 | ```swift 67 | UserDefaults.standard.do { 68 | $0.set("devxoul", forKey: "username") 69 | $0.set("devxoul@gmail.com", forKey: "email") 70 | $0.synchronize() 71 | } 72 | ``` 73 | 74 | ## Real World Example 75 | 76 | Here's an example usage in an UIViewController subclass. 77 | 78 | ```swift 79 | final class MyViewController: UIViewController { 80 | 81 | let titleLabel = UILabel().then { 82 | $0.textColor = .black 83 | $0.textAlignment = .center 84 | } 85 | 86 | let tableView = UITableView().then { 87 | $0.backgroundColor = .clear 88 | $0.separatorStyle = .none 89 | $0.register(MyCell.self, forCellReuseIdentifier: "myCell") 90 | } 91 | 92 | override func viewDidLoad() { 93 | super.viewDidLoad() 94 | self.view.addSubview(self.titleLabel) 95 | self.view.addSubview(self.tableView) 96 | } 97 | 98 | } 99 | ``` 100 | 101 | ## Installation 102 | 103 | - **Using [CocoaPods](https://cocoapods.org)**: 104 | 105 | ```ruby 106 | pod 'Then' 107 | ``` 108 | 109 | - **Using [Swift Package Manager](https://swift.org/package-manager)**: 110 | 111 | ```swift 112 | import PackageDescription 113 | 114 | let package = Package( 115 | name: "MyAwesomeApp", 116 | dependencies: [ 117 | .Package(url: "https://github.com/devxoul/Then", majorVersion: 2), 118 | ] 119 | ) 120 | ``` 121 | 122 | ## License 123 | 124 | **Then** is under MIT license. See the [LICENSE](LICENSE) file for more info. 125 | -------------------------------------------------------------------------------- /Sources/Then/Then.swift: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2015 Suyeol Jeon (xoul.kr) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | // SOFTWARE. 22 | 23 | import Foundation 24 | #if !os(Linux) 25 | import CoreGraphics 26 | #endif 27 | #if os(iOS) || os(tvOS) 28 | import UIKit.UIGeometry 29 | #endif 30 | 31 | public protocol Then {} 32 | 33 | extension Then where Self: Any { 34 | 35 | /// Makes it available to set properties with closures just after initializing and copying the value types. 36 | /// 37 | /// let frame = CGRect().with { 38 | /// $0.origin.x = 10 39 | /// $0.size.width = 100 40 | /// } 41 | @inlinable 42 | public func with(_ block: (inout Self) throws -> Void) rethrows -> Self { 43 | var copy = self 44 | try block(©) 45 | return copy 46 | } 47 | 48 | /// Makes it available to execute something with closures. 49 | /// 50 | /// UserDefaults.standard.do { 51 | /// $0.set("devxoul", forKey: "username") 52 | /// $0.set("devxoul@gmail.com", forKey: "email") 53 | /// $0.synchronize() 54 | /// } 55 | @inlinable 56 | public func `do`(_ block: (Self) throws -> Void) rethrows { 57 | try block(self) 58 | } 59 | 60 | } 61 | 62 | extension Then where Self: AnyObject { 63 | 64 | /// Makes it available to set properties with closures just after initializing. 65 | /// 66 | /// let label = UILabel().then { 67 | /// $0.textAlignment = .center 68 | /// $0.textColor = UIColor.black 69 | /// $0.text = "Hello, World!" 70 | /// } 71 | @inlinable 72 | public func then(_ block: (Self) throws -> Void) rethrows -> Self { 73 | try block(self) 74 | return self 75 | } 76 | 77 | } 78 | 79 | extension NSObject: Then {} 80 | 81 | #if !os(Linux) 82 | extension CGPoint: Then {} 83 | extension CGRect: Then {} 84 | extension CGSize: Then {} 85 | extension CGVector: Then {} 86 | #endif 87 | 88 | extension Array: Then {} 89 | extension Dictionary: Then {} 90 | extension Set: Then {} 91 | extension JSONDecoder: Then {} 92 | extension JSONEncoder: Then {} 93 | 94 | #if os(iOS) || os(tvOS) 95 | extension UIEdgeInsets: Then {} 96 | extension UIOffset: Then {} 97 | extension UIRectEdge: Then {} 98 | #endif 99 | -------------------------------------------------------------------------------- /Tests/ThenTests/ThenTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ThenTests.swift 3 | // ThenTests 4 | // 5 | // Created by 전수열 on 12/24/15. 6 | // Copyright © 2015 Suyeol Jeon. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Then 11 | 12 | struct User { 13 | var name: String? 14 | var email: String? 15 | } 16 | extension User: Then {} 17 | 18 | class ThenTests: XCTestCase { 19 | 20 | func testThen_NSObject() { 21 | let queue = OperationQueue().then { 22 | $0.name = "awesome" 23 | $0.maxConcurrentOperationCount = 5 24 | } 25 | XCTAssertEqual(queue.name, "awesome") 26 | XCTAssertEqual(queue.maxConcurrentOperationCount, 5) 27 | } 28 | 29 | func testWith() { 30 | let user = User().with { 31 | $0.name = "devxoul" 32 | $0.email = "devxoul@gmail.com" 33 | } 34 | XCTAssertEqual(user.name, "devxoul") 35 | XCTAssertEqual(user.email, "devxoul@gmail.com") 36 | } 37 | 38 | func testWith_Array() { 39 | let array = [1, 2, 3].with { $0.append(4) } 40 | XCTAssertEqual(array, [1, 2, 3, 4]) 41 | } 42 | 43 | func testWith_Dictionary() { 44 | let dict = ["Korea": "Seoul", "Japan": "Tokyo"].with { 45 | $0["China"] = "Beijing" 46 | } 47 | XCTAssertEqual(dict, ["Korea": "Seoul", "Japan": "Tokyo", "China": "Beijing"]) 48 | } 49 | 50 | func testWith_Set() { 51 | let set = Set(["A", "B", "C"]).with { 52 | $0.insert("D") 53 | } 54 | XCTAssertEqual(set, Set(["A", "B", "C", "D"])) 55 | } 56 | 57 | func testDo() { 58 | UserDefaults.standard.do { 59 | $0.removeObject(forKey: "username") 60 | $0.set("devxoul", forKey: "username") 61 | $0.synchronize() 62 | } 63 | XCTAssertEqual(UserDefaults.standard.string(forKey: "username"), "devxoul") 64 | } 65 | 66 | func testRethrows() { 67 | XCTAssertThrowsError( 68 | try NSObject().do { _ in 69 | throw NSError(domain: "", code: 0) 70 | } 71 | ) 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /Then.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "Then" 3 | s.version = "3.0.0" 4 | s.summary = "Super sweet syntactic sugar for Swift initializers." 5 | s.homepage = "https://github.com/devxoul/Then" 6 | s.license = { :type => "MIT", :file => "LICENSE" } 7 | s.author = { "Suyeol Jeon" => "devxoul@gmail.com" } 8 | s.source = { :git => "https://github.com/devxoul/Then.git", 9 | :tag => s.version.to_s } 10 | s.source_files = "Sources/Then/*.swift" 11 | s.requires_arc = true 12 | 13 | s.swift_version = "5.0" 14 | s.ios.deployment_target = "9.0" 15 | s.osx.deployment_target = "10.9" 16 | s.tvos.deployment_target = "9.0" 17 | s.watchos.deployment_target = "2.0" 18 | end 19 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "Tests/" 3 | 4 | coverage: 5 | status: 6 | project: no 7 | patch: no 8 | changes: no 9 | --------------------------------------------------------------------------------