├── title.png ├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── BeelineExample ├── Supporting │ ├── Assets.xcassets │ │ ├── Contents.json │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ └── Base.lproj │ │ └── LaunchScreen.storyboard ├── BeelineExample.entitlements ├── ViewController.swift ├── AppDelegate.swift ├── AppRouter.swift └── ViewController.xib ├── .swiftpm └── xcode │ └── package.xcworkspace │ └── contents.xcworkspacedata ├── Beeline.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcshareddata │ └── xcschemes │ │ ├── BeelineTests.xcscheme │ │ └── BeelineExample.xcscheme └── project.pbxproj ├── Beeline.podspec ├── Package.swift ├── BeelineTests ├── Info.plist └── BeelineTests.swift ├── LICENSE ├── .gitignore ├── README.md └── Beeline └── Router.swift /title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TimOliver/Beeline/HEAD/title.png -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: timoliver 2 | custom: https://tim.dev/paypal 3 | -------------------------------------------------------------------------------- /BeelineExample/Supporting/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /BeelineExample/Supporting/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Beeline.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Beeline.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: macos-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Run a one-line script 13 | run: xcodebuild -project Beeline.xcodeproj -scheme BeelineExample -destination 'platform=iOS Simulator,name=iPhone 11' test 14 | -------------------------------------------------------------------------------- /BeelineExample/BeelineExample.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.network.client 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Beeline.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'Beeline' 3 | s.version = '1.0.1' 4 | s.license = { :type => 'MIT', :file => 'LICENSE' } 5 | s.summary = 'An extremely lean implementation on the classic iOS router pattern.' 6 | s.homepage = 'https://github.com/TimOliver/Beeline' 7 | s.author = 'Tim Oliver' 8 | s.source = { :git => 'https://github.com/TimOliver/Beeline.git', :tag => s.version } 9 | s.source_files = 'Beeline/**/*.swift' 10 | s.swift_version = '5.0' 11 | 12 | s.ios.deployment_target = '9.0' 13 | s.tvos.deployment_target = '9.0' 14 | 15 | end 16 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.3 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Beeline", 8 | platforms: [ 9 | .iOS(.v9), 10 | .macOS(.v10_12) 11 | ], 12 | products: [ 13 | .library( 14 | name: "Beeline", 15 | targets: ["Beeline"]), 16 | ], 17 | targets: [ 18 | .target( 19 | name: "Beeline", 20 | path: "Beeline"), 21 | .testTarget( 22 | name: "BeelineTests", 23 | dependencies: [ 24 | "Beeline" 25 | ], 26 | path: "BeelineTests") 27 | ] 28 | ) 29 | -------------------------------------------------------------------------------- /BeelineTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /BeelineExample/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // BeelineTest 4 | // 5 | // Created by Tim Oliver on 17/7/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class ViewController: UIViewController { 11 | @IBOutlet weak var numberLabel: UILabel! 12 | private var number: Int = 0 13 | 14 | init(number: Int) { 15 | super.init(nibName: "ViewController", bundle: nil) 16 | self.number = number 17 | } 18 | 19 | required init?(coder: NSCoder) { 20 | fatalError("init(coder:) has not been implemented") 21 | } 22 | 23 | override func viewDidLoad() { 24 | super.viewDidLoad() 25 | self.numberLabel.text = String(number) 26 | } 27 | 28 | @IBAction func buttonTapped(_ sender: Any) { 29 | // Tell the router to show another view controller 30 | show(AppRoute.viewController(number: number + 1)) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /BeelineExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // BeelineTest 4 | // 5 | // Created by Tim Oliver on 13/7/21. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | var window: UIWindow? 13 | 14 | func application(_ application: UIApplication, didFinishLaunchingWithOptions 15 | launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | 17 | // Register our global router 18 | Router.registerDefaultClass(AppRouter.self) 19 | 20 | // Create the navigation controller 21 | let navigationController = UINavigationController(rootViewController: ViewController(number: 1)) 22 | 23 | // Create and show the window 24 | window = UIWindow(frame: UIScreen.main.bounds) 25 | window?.rootViewController = navigationController 26 | window?.makeKeyAndVisible() 27 | 28 | return true 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /BeelineExample/AppRouter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppRouter.swift 3 | // BeelineTest 4 | // 5 | // Created by Tim Oliver on 17/7/21. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | /// Enum containing all of our destinations 12 | enum AppRoute: Route { 13 | case viewController(number: Int) 14 | } 15 | 16 | // Our subclass of Router to contain our custom presentation logic 17 | public class AppRouter: Router { 18 | 19 | override func show(_ route: Route, 20 | from sourceViewController: UIViewController?) -> Bool { 21 | // We're only interested in routes from the AppRoute enum 22 | guard let appRoute = route as? AppRoute else { return false } 23 | 24 | // Check which enum was requested and produce a view controller for it 25 | switch appRoute { 26 | case .viewController(let number): 27 | let newViewController = ViewController(number: number) 28 | sourceViewController? 29 | .navigationController? 30 | .pushViewController(newViewController, animated: true) 31 | } 32 | 33 | return true 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Tim Oliver 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 | -------------------------------------------------------------------------------- /BeelineExample/Supporting/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSupportsIndirectInputEvents 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /BeelineExample/Supporting/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Beeline.xcodeproj/xcshareddata/xcschemes/BeelineTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 37 | 38 | 44 | 45 | 47 | 48 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /BeelineExample/Supporting/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## User settings 6 | xcuserdata/ 7 | 8 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 9 | *.xcscmblueprint 10 | *.xccheckout 11 | 12 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 13 | build/ 14 | DerivedData/ 15 | *.moved-aside 16 | *.pbxuser 17 | !default.pbxuser 18 | *.mode1v3 19 | !default.mode1v3 20 | *.mode2v3 21 | !default.mode2v3 22 | *.perspectivev3 23 | !default.perspectivev3 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | 28 | ## App packaging 29 | *.ipa 30 | *.dSYM.zip 31 | *.dSYM 32 | 33 | ## Playgrounds 34 | timeline.xctimeline 35 | playground.xcworkspace 36 | 37 | # Swift Package Manager 38 | # 39 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 40 | # Packages/ 41 | # Package.pins 42 | # Package.resolved 43 | # *.xcodeproj 44 | # 45 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 46 | # hence it is not needed unless you have added a package configuration file to your project 47 | # .swiftpm 48 | 49 | .build/ 50 | 51 | # CocoaPods 52 | # 53 | # We recommend against adding the Pods directory to your .gitignore. However 54 | # you should judge for yourself, the pros and cons are mentioned at: 55 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 56 | # 57 | # Pods/ 58 | # 59 | # Add this line if you want to avoid checking in source code from the Xcode workspace 60 | # *.xcworkspace 61 | 62 | # Carthage 63 | # 64 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 65 | # Carthage/Checkouts 66 | 67 | Carthage/Build/ 68 | 69 | # Accio dependency management 70 | Dependencies/ 71 | .accio/ 72 | 73 | # fastlane 74 | # 75 | # It is recommended to not store the screenshots in the git repo. 76 | # Instead, use fastlane to re-generate the screenshots whenever they are needed. 77 | # For more information about the recommended setup visit: 78 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 79 | 80 | fastlane/report.xml 81 | fastlane/Preview.html 82 | fastlane/screenshots/**/*.png 83 | fastlane/test_output 84 | 85 | # Code Injection 86 | # 87 | # After new code Injection tools there's a generated folder /iOSInjectionProject 88 | # https://github.com/johnno1962/injectionforxcode 89 | 90 | iOSInjectionProject/ 91 | 92 | # macOS 93 | 94 | .DS_Store 95 | 96 | -------------------------------------------------------------------------------- /BeelineTests/BeelineTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BeelineTests.swift 3 | // BeelineTests 4 | // 5 | // Created by Tim Oliver on 20/7/21. 6 | // 7 | 8 | import XCTest 9 | 10 | // Create a dummy destination to test 11 | enum TestRoute: Route { 12 | case first 13 | } 14 | 15 | // Create a subclass of Router with an external closure to test 16 | class TestRouter: Router { 17 | var showResultsClosure: ((Route) -> Void)? 18 | 19 | override func show(_ route: Route, from sourceViewController: UIViewController?) -> Bool { 20 | showResultsClosure?(route) 21 | return true 22 | } 23 | } 24 | 25 | class BeelineTests: XCTestCase { 26 | 27 | override class func tearDown() { 28 | // Set the default class back to nil just in case 29 | Router.registerDefaultClass(nil) 30 | } 31 | 32 | // Test setting and getting the same router instance from a VC 33 | func testRouterAssigning() { 34 | let viewController = UIViewController() 35 | let testRouter = TestRouter() 36 | viewController.router = testRouter 37 | XCTAssertEqual(testRouter, viewController.router) 38 | } 39 | 40 | // Test show mechanism 41 | func testRouterShowCallback() { 42 | // Set a flag we can track 43 | var success = false 44 | 45 | // Create the router and expectation 46 | let testRouter = TestRouter() 47 | testRouter.showResultsClosure = { route in 48 | if let testRoute = route as? TestRoute { 49 | success = (testRoute == .first) 50 | } 51 | } 52 | 53 | // Attach the router to a view controller nested in another view controller 54 | let viewController = UIViewController() 55 | let parentViewController = UINavigationController(rootViewController: viewController) 56 | parentViewController.router = testRouter 57 | 58 | // Call show on the view controller 59 | viewController.show(TestRoute.first) 60 | 61 | // Capture if the call was passed back up to the router 62 | XCTAssertTrue(success) 63 | } 64 | 65 | // Test default mechanism 66 | func testRouterDefaultClass() { 67 | // Register the default class 68 | Router.registerDefaultClass(TestRouter.self) 69 | 70 | // Create a nested view controller setup 71 | let viewController = UIViewController() 72 | let parentViewController = UINavigationController(rootViewController: viewController) 73 | 74 | // Call show on the child, which will auto-generate a router on the nav controller 75 | viewController.show(TestRoute.first) 76 | 77 | // Check the expected output was correct 78 | XCTAssertNotNil(parentViewController.router) 79 | XCTAssertTrue(parentViewController.router is TestRouter) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /BeelineExample/ViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 31 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /Beeline.xcodeproj/xcshareddata/xcschemes/BeelineExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 47 | 53 | 54 | 55 | 56 | 57 | 67 | 69 | 75 | 76 | 77 | 78 | 84 | 86 | 92 | 93 | 94 | 95 | 97 | 98 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Beeline 2 | 3 | 4 | 5 | [![CI](https://github.com/TimOliver/Beeline/workflows/CI/badge.svg)](https://github.com/TimOliver/Beeline/actions?query=workflow%3ACI) 6 | [![Version](https://img.shields.io/cocoapods/v/Beeline.svg?style=flat)](http://cocoadocs.org/docsets/Beeline) 7 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/TimOliver/Beeline/main/LICENSE) 8 | [![Platform](https://img.shields.io/cocoapods/p/Beeline.svg?style=flat)](http://cocoadocs.org/docsets/Beeline) 9 | 10 | 11 | 12 | Beeline is a very small library that aims to provide a lean, automatic implementation of the classic iOS router pattern. It extends `UIViewController` to retain a `Router` object that serves as the source of truth for controlling navigation flows for all of the view controller's children. When a child view controller wishes to transition to a new screen, it can call `show()` on itself and this request is passed up the view controller chain to the routing view controller. 13 | 14 | # Instructions 15 | 16 | A very basic custom implementation looks as the following. First, we create a Swift enum conforming to `Route` where we can define the types of destinations with which we want to move: 17 | 18 | ```swift 19 | enum AppRoute: Route { 20 | case viewController(number: Int) 21 | } 22 | ``` 23 | Thanks to Swift associated enums, we can also include any custom parameters the new destination may need. 24 | 25 | We then also make a new class which subclasses `Router`, which serves as our single point of truth for controlling the app flow based off the destinations we defined above: 26 | 27 | ```swift 28 | public class AppRouter: Router { 29 | override func show(_ route: Route, 30 | from sourceViewController: UIViewController?) -> Bool { 31 | 32 | // Optionally, filter out routes we don't support 33 | guard let appRoute = route as? AppRoute else { return false } 34 | 35 | // Check the requested enum, and perform the transition 36 | switch appRoute { 37 | case .viewController(let number): 38 | let newViewController = ViewController(number: number) 39 | sourceViewController? 40 | .navigationController? 41 | .pushViewController(newViewController, animated: true) 42 | } 43 | 44 | return true 45 | } 46 | } 47 | 48 | ``` 49 | 50 | Using Objective-C associated objects, we can assign this router to any parent view controller that contains all of the view controllers that might want to perform these transitions: 51 | 52 | ```swift 53 | let navigationController = UINavigationController(rootViewController: ViewController()) 54 | navigationController.router = AppRouter() 55 | ``` 56 | 57 | Finally, without any further modification to any of the child view controllers, they can start a transition by simply calling `show()` with the desired destination: 58 | 59 | ```swift 60 | class ViewController: UIViewController { 61 | func moveToNewViewController() { 62 | show(AppRoute.viewController(number: 2)) 63 | } 64 | } 65 | ``` 66 | 67 | And that's the entire library! 🎉 68 | 69 | # Requirements 70 | * Swift 5 71 | * UIKit-compatible platforms (iOS, tvOS, Mac Catalyst) 72 | 73 | # Installation 74 | 75 | Beeline is a very small framework, with all of its code contained in `Router.swift`. You can install it in the following ways: 76 | 77 | ## Manual Installation 78 | 79 | Drag the `Beeline/Router.swift` file into your Xcode project. 80 | 81 | ### CocoaPods 82 | 83 | ``` 84 | pod 'Beeline' 85 | ``` 86 | 87 | ### SPM 88 | 89 | You can add Beeline to an Xcode project by adding it as a package dependency. 90 | 91 | https://github.com/TimOliver/Beeline 92 | 93 | or, add the following to the dependencies in package.swift. 94 | 95 | ```swift 96 | dependencies: [ 97 | .package(url: "https://github.com/TimOliver/Beeline", from: "1.0.2") 98 | ] 99 | ``` 100 | 101 | ### Carthage 102 | 103 | No plans to support Carthage at the moment, but please consider filing a PR if you would like it! 104 | 105 | # Credits 106 | 107 | Beeline was built as a component of iComics 2 by [Tim Oliver](https://twitter.com/TimOliverAU) 108 | 109 | # License 110 | 111 | Beeline is available under the MIT License. Please check the [LICENSE](LICENSE) file for more information. 112 | -------------------------------------------------------------------------------- /Beeline/Router.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Router.swift 3 | // 4 | // Copyright 2021 Timothy Oliver. All rights reserved. 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to 8 | // deal in the Software without restriction, including without limitation the 9 | // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | // sell copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in 14 | // all copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | // WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 21 | // IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | 23 | import UIKit 24 | 25 | /// A router is an object that is used to serve as the single source 26 | /// of truth when dealing with the navigation flow of screens in complex 27 | /// applications. Instead of child screens setting up and performing the transitions 28 | /// themselves, 'show' requests are dispatched up the view controller chain 29 | /// to a parent view controller with a router assigned, that will then perform its 30 | /// own business logic on what action to perform. 31 | /// 32 | /// `Router` is an abstract class that when subclassed, allows you to receive 33 | /// transition requests from child view controllers, and to have a single place 34 | /// to determine how the current state should transition to the requested one. 35 | /// 36 | open class Router: NSObject { 37 | 38 | /// A reference back to the view controller to which this router is assigned. 39 | weak var rootViewController: UIViewController? 40 | 41 | /// Normally view controllers that should serve as a router should have 42 | /// their router property manually configured. But for convenience, it is also 43 | /// possible to define a "default" router class. If a view controller calls `show` 44 | /// inside a view controller chain with no router, the view controller at the top 45 | /// of the chain will automatically instantiate and configure one 46 | /// - Parameter routerClass: The class (as `Class.self`) that should created by default. Must be a subclass of `Router`. 47 | class func registerDefaultClass(_ routerClass: AnyClass?) { 48 | // If nil was supplied, clear the currently registered class 49 | guard let routerClass = routerClass else { 50 | defaultRouterClass = nil 51 | return 52 | } 53 | 54 | // Ensure that the provided class is actually a subclass of Router 55 | guard routerClass.isSubclass(of: Router.self) else { 56 | fatalError("Router: Default router classes must be a subclass of the Router class.") 57 | } 58 | 59 | // Assign it as our global router 60 | defaultRouterClass = routerClass 61 | } 62 | 63 | /// Transitions the current view controller state of the view controller 64 | /// associated with this router object to the requested route destination. 65 | /// Override `show` in your subclasses in order to receive and perform 66 | /// the necessary transitions between screens that you want in your application 67 | /// - Parameters: 68 | /// - route: A custom object conforming to the `Route` protocol used to identify the intended destination of this transition 69 | /// - sourceViewController: The view controller that created this request (can be nil if the router itself directly called it) 70 | /// - Returns: Returns true if this router successfully handled the request, or false if the router decided to skip it 71 | func show(_ route: Route, from sourceViewController: UIViewController?) -> Bool { 72 | fatalError("Router: This class must be subclassed and cannot be used directly.") 73 | } 74 | } 75 | 76 | /// A route is a protocol which identifies any kind of object that can represent 77 | /// a new routing destination to a router. Any type of object can be made to conform to 78 | /// `Route`, and when submitted to a router, its type can then be checked 79 | /// by the custom overrided logic in that router's `show` method to determine what to do. 80 | public protocol Route { } 81 | 82 | // MARK: - UIKit Integration - 83 | 84 | public extension UIViewController { 85 | 86 | /// A router object that is associated with this 87 | /// view controller. This router will capture all of the 88 | /// 'show' events sent upwards from child view controllers 89 | var router: Router? { 90 | get { objc_getAssociatedObject(self, &routerKey) as? Router } 91 | set { 92 | newValue?.rootViewController = self 93 | objc_setAssociatedObject(self, &routerKey, 94 | newValue, .OBJC_ASSOCIATION_RETAIN) 95 | } 96 | } 97 | 98 | /// Transitions the calling view controller to the requested route destination 99 | /// with any associated parameters. The request is sent up the view controller 100 | /// chain until it reaches a view controller with an assigned router, which will 101 | /// then serve as the source of truth for transition 102 | /// - Parameter route: Any object conforming to `Route` used to uniquely identify the desired destination 103 | func show(_ route: Route) { 104 | var viewController: UIViewController? = self 105 | 106 | // Starting at the calling view controller, go up the parent view controller 107 | // chain until we find one with an associated router that will handle the transition for us 108 | repeat { 109 | // If the view controller doesn't have a parent 110 | // (ie, it's the top of the chain), and we have a default 111 | // class registered, make and assign a router to it 112 | if let defaultRouterClass = defaultRouterClass, 113 | viewController != nil, viewController!.parent == nil, 114 | viewController?.router == nil { 115 | let objectClass = defaultRouterClass as! NSObject.Type 116 | viewController?.router = objectClass.init() as? Router 117 | } 118 | 119 | // If this view controller has an associated router, 120 | // forward it the show command. If the router successfully 121 | // handles it, stop here. Otherwise keep going up the chain 122 | if let router = viewController?.router { 123 | if router.show(route, from: self) { return } 124 | } 125 | 126 | // Keep going up the chain until we hit the end 127 | viewController = viewController?.parent 128 | } while viewController != nil 129 | } 130 | } 131 | 132 | // MARK: - Private Global Properties - 133 | 134 | // An associated key value for storing routers inside view controllers 135 | private var routerKey: Void? 136 | 137 | // Optionally, a default Router subclass that can be deferred to by default 138 | private var defaultRouterClass: AnyClass? 139 | -------------------------------------------------------------------------------- /Beeline.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2284DE02298028C7003A3371 /* BeelineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2284DE01298028C7003A3371 /* BeelineTests.swift */; }; 11 | 2284DE08298028F8003A3371 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22B3A607269DCFA500E1E843 /* Router.swift */; }; 12 | 22B3A5F2269DC48000E1E843 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22B3A5F1269DC48000E1E843 /* AppDelegate.swift */; }; 13 | 22B3A5FB269DC48100E1E843 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 22B3A5FA269DC48100E1E843 /* Assets.xcassets */; }; 14 | 22B3A5FE269DC48100E1E843 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 22B3A5FC269DC48100E1E843 /* LaunchScreen.storyboard */; }; 15 | 22B3A608269DCFA500E1E843 /* Router.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22B3A607269DCFA500E1E843 /* Router.swift */; }; 16 | 22BCBE6F26A29DCB0081CADE /* AppRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22BCBE6E26A29DCB0081CADE /* AppRouter.swift */; }; 17 | 22BCBE7226A29F350081CADE /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22BCBE7026A29F350081CADE /* ViewController.swift */; }; 18 | 22BCBE7326A29F350081CADE /* ViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 22BCBE7126A29F350081CADE /* ViewController.xib */; }; 19 | /* End PBXBuildFile section */ 20 | 21 | /* Begin PBXContainerItemProxy section */ 22 | 2284DE03298028C7003A3371 /* PBXContainerItemProxy */ = { 23 | isa = PBXContainerItemProxy; 24 | containerPortal = 22B3A5E6269DC48000E1E843 /* Project object */; 25 | proxyType = 1; 26 | remoteGlobalIDString = 22B3A5ED269DC48000E1E843; 27 | remoteInfo = BeelineExample; 28 | }; 29 | /* End PBXContainerItemProxy section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 227B686026A719FA0000DD8D /* BeelineExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = BeelineExample.entitlements; sourceTree = ""; }; 33 | 2284DDF72980283D003A3371 /* Package.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 34 | 2284DDF92980284A003A3371 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 35 | 2284DDFA2980285C003A3371 /* Beeline.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = Beeline.podspec; sourceTree = ""; }; 36 | 2284DDFF298028C7003A3371 /* BeelineTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BeelineTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 2284DE01298028C7003A3371 /* BeelineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BeelineTests.swift; sourceTree = ""; }; 38 | 22B3A5EE269DC48000E1E843 /* BeelineExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BeelineExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 39 | 22B3A5F1269DC48000E1E843 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 40 | 22B3A5FA269DC48100E1E843 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 41 | 22B3A5FD269DC48100E1E843 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 42 | 22B3A5FF269DC48100E1E843 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 43 | 22B3A607269DCFA500E1E843 /* Router.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Router.swift; sourceTree = ""; }; 44 | 22BCBE6E26A29DCB0081CADE /* AppRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouter.swift; sourceTree = ""; }; 45 | 22BCBE7026A29F350081CADE /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 46 | 22BCBE7126A29F350081CADE /* ViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ViewController.xib; sourceTree = ""; }; 47 | /* End PBXFileReference section */ 48 | 49 | /* Begin PBXFrameworksBuildPhase section */ 50 | 2284DDFC298028C7003A3371 /* Frameworks */ = { 51 | isa = PBXFrameworksBuildPhase; 52 | buildActionMask = 2147483647; 53 | files = ( 54 | ); 55 | runOnlyForDeploymentPostprocessing = 0; 56 | }; 57 | 22B3A5EB269DC48000E1E843 /* Frameworks */ = { 58 | isa = PBXFrameworksBuildPhase; 59 | buildActionMask = 2147483647; 60 | files = ( 61 | ); 62 | runOnlyForDeploymentPostprocessing = 0; 63 | }; 64 | /* End PBXFrameworksBuildPhase section */ 65 | 66 | /* Begin PBXGroup section */ 67 | 2284DE00298028C7003A3371 /* BeelineTests */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | 2284DE01298028C7003A3371 /* BeelineTests.swift */, 71 | ); 72 | path = BeelineTests; 73 | sourceTree = ""; 74 | }; 75 | 22B3A5E5269DC48000E1E843 = { 76 | isa = PBXGroup; 77 | children = ( 78 | 22BCBE6826A293310081CADE /* Beeline */, 79 | 22B3A5F0269DC48000E1E843 /* BeelineExample */, 80 | 2284DDF72980283D003A3371 /* Package.swift */, 81 | 2284DDFA2980285C003A3371 /* Beeline.podspec */, 82 | 2284DDF92980284A003A3371 /* README.md */, 83 | 2284DE00298028C7003A3371 /* BeelineTests */, 84 | 22B3A5EF269DC48000E1E843 /* Products */, 85 | ); 86 | sourceTree = ""; 87 | }; 88 | 22B3A5EF269DC48000E1E843 /* Products */ = { 89 | isa = PBXGroup; 90 | children = ( 91 | 22B3A5EE269DC48000E1E843 /* BeelineExample.app */, 92 | 2284DDFF298028C7003A3371 /* BeelineTests.xctest */, 93 | ); 94 | name = Products; 95 | sourceTree = ""; 96 | }; 97 | 22B3A5F0269DC48000E1E843 /* BeelineExample */ = { 98 | isa = PBXGroup; 99 | children = ( 100 | 227B686026A719FA0000DD8D /* BeelineExample.entitlements */, 101 | 22BCBE6E26A29DCB0081CADE /* AppRouter.swift */, 102 | 22B3A5F1269DC48000E1E843 /* AppDelegate.swift */, 103 | 22BCBE7026A29F350081CADE /* ViewController.swift */, 104 | 22BCBE7126A29F350081CADE /* ViewController.xib */, 105 | 22BCBE6D26A29DAF0081CADE /* Supporting */, 106 | ); 107 | path = BeelineExample; 108 | sourceTree = ""; 109 | }; 110 | 22BCBE6826A293310081CADE /* Beeline */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 22B3A607269DCFA500E1E843 /* Router.swift */, 114 | ); 115 | path = Beeline; 116 | sourceTree = ""; 117 | }; 118 | 22BCBE6D26A29DAF0081CADE /* Supporting */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 22B3A5FA269DC48100E1E843 /* Assets.xcassets */, 122 | 22B3A5FC269DC48100E1E843 /* LaunchScreen.storyboard */, 123 | 22B3A5FF269DC48100E1E843 /* Info.plist */, 124 | ); 125 | path = Supporting; 126 | sourceTree = ""; 127 | }; 128 | /* End PBXGroup section */ 129 | 130 | /* Begin PBXNativeTarget section */ 131 | 2284DDFE298028C7003A3371 /* BeelineTests */ = { 132 | isa = PBXNativeTarget; 133 | buildConfigurationList = 2284DE05298028C7003A3371 /* Build configuration list for PBXNativeTarget "BeelineTests" */; 134 | buildPhases = ( 135 | 2284DDFB298028C7003A3371 /* Sources */, 136 | 2284DDFC298028C7003A3371 /* Frameworks */, 137 | 2284DDFD298028C7003A3371 /* Resources */, 138 | ); 139 | buildRules = ( 140 | ); 141 | dependencies = ( 142 | 2284DE04298028C7003A3371 /* PBXTargetDependency */, 143 | ); 144 | name = BeelineTests; 145 | productName = BeelineTests; 146 | productReference = 2284DDFF298028C7003A3371 /* BeelineTests.xctest */; 147 | productType = "com.apple.product-type.bundle.unit-test"; 148 | }; 149 | 22B3A5ED269DC48000E1E843 /* BeelineExample */ = { 150 | isa = PBXNativeTarget; 151 | buildConfigurationList = 22B3A602269DC48100E1E843 /* Build configuration list for PBXNativeTarget "BeelineExample" */; 152 | buildPhases = ( 153 | 22B3A5EA269DC48000E1E843 /* Sources */, 154 | 22B3A5EB269DC48000E1E843 /* Frameworks */, 155 | 22B3A5EC269DC48000E1E843 /* Resources */, 156 | ); 157 | buildRules = ( 158 | ); 159 | dependencies = ( 160 | ); 161 | name = BeelineExample; 162 | productName = RouterTest; 163 | productReference = 22B3A5EE269DC48000E1E843 /* BeelineExample.app */; 164 | productType = "com.apple.product-type.application"; 165 | }; 166 | /* End PBXNativeTarget section */ 167 | 168 | /* Begin PBXProject section */ 169 | 22B3A5E6269DC48000E1E843 /* Project object */ = { 170 | isa = PBXProject; 171 | attributes = { 172 | LastSwiftUpdateCheck = 1410; 173 | LastUpgradeCheck = 1410; 174 | TargetAttributes = { 175 | 2284DDFE298028C7003A3371 = { 176 | CreatedOnToolsVersion = 14.1; 177 | TestTargetID = 22B3A5ED269DC48000E1E843; 178 | }; 179 | 22B3A5ED269DC48000E1E843 = { 180 | CreatedOnToolsVersion = 12.5; 181 | }; 182 | }; 183 | }; 184 | buildConfigurationList = 22B3A5E9269DC48000E1E843 /* Build configuration list for PBXProject "Beeline" */; 185 | compatibilityVersion = "Xcode 9.3"; 186 | developmentRegion = en; 187 | hasScannedForEncodings = 0; 188 | knownRegions = ( 189 | en, 190 | Base, 191 | ); 192 | mainGroup = 22B3A5E5269DC48000E1E843; 193 | productRefGroup = 22B3A5EF269DC48000E1E843 /* Products */; 194 | projectDirPath = ""; 195 | projectRoot = ""; 196 | targets = ( 197 | 22B3A5ED269DC48000E1E843 /* BeelineExample */, 198 | 2284DDFE298028C7003A3371 /* BeelineTests */, 199 | ); 200 | }; 201 | /* End PBXProject section */ 202 | 203 | /* Begin PBXResourcesBuildPhase section */ 204 | 2284DDFD298028C7003A3371 /* Resources */ = { 205 | isa = PBXResourcesBuildPhase; 206 | buildActionMask = 2147483647; 207 | files = ( 208 | ); 209 | runOnlyForDeploymentPostprocessing = 0; 210 | }; 211 | 22B3A5EC269DC48000E1E843 /* Resources */ = { 212 | isa = PBXResourcesBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | 22B3A5FE269DC48100E1E843 /* LaunchScreen.storyboard in Resources */, 216 | 22BCBE7326A29F350081CADE /* ViewController.xib in Resources */, 217 | 22B3A5FB269DC48100E1E843 /* Assets.xcassets in Resources */, 218 | ); 219 | runOnlyForDeploymentPostprocessing = 0; 220 | }; 221 | /* End PBXResourcesBuildPhase section */ 222 | 223 | /* Begin PBXSourcesBuildPhase section */ 224 | 2284DDFB298028C7003A3371 /* Sources */ = { 225 | isa = PBXSourcesBuildPhase; 226 | buildActionMask = 2147483647; 227 | files = ( 228 | 2284DE08298028F8003A3371 /* Router.swift in Sources */, 229 | 2284DE02298028C7003A3371 /* BeelineTests.swift in Sources */, 230 | ); 231 | runOnlyForDeploymentPostprocessing = 0; 232 | }; 233 | 22B3A5EA269DC48000E1E843 /* Sources */ = { 234 | isa = PBXSourcesBuildPhase; 235 | buildActionMask = 2147483647; 236 | files = ( 237 | 22BCBE6F26A29DCB0081CADE /* AppRouter.swift in Sources */, 238 | 22B3A608269DCFA500E1E843 /* Router.swift in Sources */, 239 | 22BCBE7226A29F350081CADE /* ViewController.swift in Sources */, 240 | 22B3A5F2269DC48000E1E843 /* AppDelegate.swift in Sources */, 241 | ); 242 | runOnlyForDeploymentPostprocessing = 0; 243 | }; 244 | /* End PBXSourcesBuildPhase section */ 245 | 246 | /* Begin PBXTargetDependency section */ 247 | 2284DE04298028C7003A3371 /* PBXTargetDependency */ = { 248 | isa = PBXTargetDependency; 249 | target = 22B3A5ED269DC48000E1E843 /* BeelineExample */; 250 | targetProxy = 2284DE03298028C7003A3371 /* PBXContainerItemProxy */; 251 | }; 252 | /* End PBXTargetDependency section */ 253 | 254 | /* Begin PBXVariantGroup section */ 255 | 22B3A5FC269DC48100E1E843 /* LaunchScreen.storyboard */ = { 256 | isa = PBXVariantGroup; 257 | children = ( 258 | 22B3A5FD269DC48100E1E843 /* Base */, 259 | ); 260 | name = LaunchScreen.storyboard; 261 | sourceTree = ""; 262 | }; 263 | /* End PBXVariantGroup section */ 264 | 265 | /* Begin XCBuildConfiguration section */ 266 | 2284DE06298028C7003A3371 /* Debug */ = { 267 | isa = XCBuildConfiguration; 268 | buildSettings = { 269 | BUNDLE_LOADER = "$(TEST_HOST)"; 270 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 271 | CODE_SIGN_STYLE = Automatic; 272 | CURRENT_PROJECT_VERSION = 1; 273 | DEVELOPMENT_TEAM = 6LF3GMKZAB; 274 | GENERATE_INFOPLIST_FILE = YES; 275 | IPHONEOS_DEPLOYMENT_TARGET = 16.1; 276 | MARKETING_VERSION = 1.0; 277 | PRODUCT_BUNDLE_IDENTIFIER = dev.tim.BeelineTests; 278 | PRODUCT_NAME = "$(TARGET_NAME)"; 279 | SWIFT_EMIT_LOC_STRINGS = NO; 280 | SWIFT_VERSION = 5.0; 281 | TARGETED_DEVICE_FAMILY = "1,2"; 282 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/BeelineExample.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/BeelineExample"; 283 | }; 284 | name = Debug; 285 | }; 286 | 2284DE07298028C7003A3371 /* Release */ = { 287 | isa = XCBuildConfiguration; 288 | buildSettings = { 289 | BUNDLE_LOADER = "$(TEST_HOST)"; 290 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; 291 | CODE_SIGN_STYLE = Automatic; 292 | CURRENT_PROJECT_VERSION = 1; 293 | DEVELOPMENT_TEAM = 6LF3GMKZAB; 294 | GENERATE_INFOPLIST_FILE = YES; 295 | IPHONEOS_DEPLOYMENT_TARGET = 16.1; 296 | MARKETING_VERSION = 1.0; 297 | PRODUCT_BUNDLE_IDENTIFIER = dev.tim.BeelineTests; 298 | PRODUCT_NAME = "$(TARGET_NAME)"; 299 | SWIFT_EMIT_LOC_STRINGS = NO; 300 | SWIFT_VERSION = 5.0; 301 | TARGETED_DEVICE_FAMILY = "1,2"; 302 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/BeelineExample.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/BeelineExample"; 303 | }; 304 | name = Release; 305 | }; 306 | 22B3A600269DC48100E1E843 /* Debug */ = { 307 | isa = XCBuildConfiguration; 308 | buildSettings = { 309 | ALWAYS_SEARCH_USER_PATHS = NO; 310 | CLANG_ANALYZER_NONNULL = YES; 311 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 312 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 313 | CLANG_CXX_LIBRARY = "libc++"; 314 | CLANG_ENABLE_MODULES = YES; 315 | CLANG_ENABLE_OBJC_ARC = YES; 316 | CLANG_ENABLE_OBJC_WEAK = YES; 317 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 318 | CLANG_WARN_BOOL_CONVERSION = YES; 319 | CLANG_WARN_COMMA = YES; 320 | CLANG_WARN_CONSTANT_CONVERSION = YES; 321 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 322 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 323 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 324 | CLANG_WARN_EMPTY_BODY = YES; 325 | CLANG_WARN_ENUM_CONVERSION = YES; 326 | CLANG_WARN_INFINITE_RECURSION = YES; 327 | CLANG_WARN_INT_CONVERSION = YES; 328 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 329 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 330 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 331 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 332 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 333 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 334 | CLANG_WARN_STRICT_PROTOTYPES = YES; 335 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 336 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 337 | CLANG_WARN_UNREACHABLE_CODE = YES; 338 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 339 | COPY_PHASE_STRIP = NO; 340 | DEBUG_INFORMATION_FORMAT = dwarf; 341 | ENABLE_STRICT_OBJC_MSGSEND = YES; 342 | ENABLE_TESTABILITY = YES; 343 | GCC_C_LANGUAGE_STANDARD = gnu11; 344 | GCC_DYNAMIC_NO_PIC = NO; 345 | GCC_NO_COMMON_BLOCKS = YES; 346 | GCC_OPTIMIZATION_LEVEL = 0; 347 | GCC_PREPROCESSOR_DEFINITIONS = ( 348 | "DEBUG=1", 349 | "$(inherited)", 350 | ); 351 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 352 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 353 | GCC_WARN_UNDECLARED_SELECTOR = YES; 354 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 355 | GCC_WARN_UNUSED_FUNCTION = YES; 356 | GCC_WARN_UNUSED_VARIABLE = YES; 357 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 358 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 359 | MTL_FAST_MATH = YES; 360 | ONLY_ACTIVE_ARCH = YES; 361 | SDKROOT = iphoneos; 362 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 363 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 364 | }; 365 | name = Debug; 366 | }; 367 | 22B3A601269DC48100E1E843 /* Release */ = { 368 | isa = XCBuildConfiguration; 369 | buildSettings = { 370 | ALWAYS_SEARCH_USER_PATHS = NO; 371 | CLANG_ANALYZER_NONNULL = YES; 372 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 373 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 374 | CLANG_CXX_LIBRARY = "libc++"; 375 | CLANG_ENABLE_MODULES = YES; 376 | CLANG_ENABLE_OBJC_ARC = YES; 377 | CLANG_ENABLE_OBJC_WEAK = YES; 378 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 379 | CLANG_WARN_BOOL_CONVERSION = YES; 380 | CLANG_WARN_COMMA = YES; 381 | CLANG_WARN_CONSTANT_CONVERSION = YES; 382 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 383 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 384 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 385 | CLANG_WARN_EMPTY_BODY = YES; 386 | CLANG_WARN_ENUM_CONVERSION = YES; 387 | CLANG_WARN_INFINITE_RECURSION = YES; 388 | CLANG_WARN_INT_CONVERSION = YES; 389 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 390 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 391 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 392 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 393 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 394 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 395 | CLANG_WARN_STRICT_PROTOTYPES = YES; 396 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 397 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 398 | CLANG_WARN_UNREACHABLE_CODE = YES; 399 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 400 | COPY_PHASE_STRIP = NO; 401 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 402 | ENABLE_NS_ASSERTIONS = NO; 403 | ENABLE_STRICT_OBJC_MSGSEND = YES; 404 | GCC_C_LANGUAGE_STANDARD = gnu11; 405 | GCC_NO_COMMON_BLOCKS = YES; 406 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 407 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 408 | GCC_WARN_UNDECLARED_SELECTOR = YES; 409 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 410 | GCC_WARN_UNUSED_FUNCTION = YES; 411 | GCC_WARN_UNUSED_VARIABLE = YES; 412 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 413 | MTL_ENABLE_DEBUG_INFO = NO; 414 | MTL_FAST_MATH = YES; 415 | SDKROOT = iphoneos; 416 | SWIFT_COMPILATION_MODE = wholemodule; 417 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 418 | VALIDATE_PRODUCT = YES; 419 | }; 420 | name = Release; 421 | }; 422 | 22B3A603269DC48100E1E843 /* Debug */ = { 423 | isa = XCBuildConfiguration; 424 | buildSettings = { 425 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 426 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 427 | CODE_SIGN_ENTITLEMENTS = BeelineExample/BeelineExample.entitlements; 428 | CODE_SIGN_STYLE = Automatic; 429 | DEVELOPMENT_TEAM = 6LF3GMKZAB; 430 | INFOPLIST_FILE = "$(SRCROOT)/BeelineExample/Supporting/Info.plist"; 431 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 432 | LD_RUNPATH_SEARCH_PATHS = ( 433 | "$(inherited)", 434 | "@executable_path/Frameworks", 435 | ); 436 | PRODUCT_BUNDLE_IDENTIFIER = dev.tim.BeelineTest; 437 | PRODUCT_NAME = "$(TARGET_NAME)"; 438 | SUPPORTS_MACCATALYST = NO; 439 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; 440 | SWIFT_VERSION = 5.0; 441 | TARGETED_DEVICE_FAMILY = "1,2"; 442 | }; 443 | name = Debug; 444 | }; 445 | 22B3A604269DC48100E1E843 /* Release */ = { 446 | isa = XCBuildConfiguration; 447 | buildSettings = { 448 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 449 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 450 | CODE_SIGN_ENTITLEMENTS = BeelineExample/BeelineExample.entitlements; 451 | CODE_SIGN_STYLE = Automatic; 452 | DEVELOPMENT_TEAM = 6LF3GMKZAB; 453 | INFOPLIST_FILE = "$(SRCROOT)/BeelineExample/Supporting/Info.plist"; 454 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 455 | LD_RUNPATH_SEARCH_PATHS = ( 456 | "$(inherited)", 457 | "@executable_path/Frameworks", 458 | ); 459 | PRODUCT_BUNDLE_IDENTIFIER = dev.tim.BeelineTest; 460 | PRODUCT_NAME = "$(TARGET_NAME)"; 461 | SUPPORTS_MACCATALYST = NO; 462 | SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; 463 | SWIFT_VERSION = 5.0; 464 | TARGETED_DEVICE_FAMILY = "1,2"; 465 | }; 466 | name = Release; 467 | }; 468 | /* End XCBuildConfiguration section */ 469 | 470 | /* Begin XCConfigurationList section */ 471 | 2284DE05298028C7003A3371 /* Build configuration list for PBXNativeTarget "BeelineTests" */ = { 472 | isa = XCConfigurationList; 473 | buildConfigurations = ( 474 | 2284DE06298028C7003A3371 /* Debug */, 475 | 2284DE07298028C7003A3371 /* Release */, 476 | ); 477 | defaultConfigurationIsVisible = 0; 478 | defaultConfigurationName = Release; 479 | }; 480 | 22B3A5E9269DC48000E1E843 /* Build configuration list for PBXProject "Beeline" */ = { 481 | isa = XCConfigurationList; 482 | buildConfigurations = ( 483 | 22B3A600269DC48100E1E843 /* Debug */, 484 | 22B3A601269DC48100E1E843 /* Release */, 485 | ); 486 | defaultConfigurationIsVisible = 0; 487 | defaultConfigurationName = Release; 488 | }; 489 | 22B3A602269DC48100E1E843 /* Build configuration list for PBXNativeTarget "BeelineExample" */ = { 490 | isa = XCConfigurationList; 491 | buildConfigurations = ( 492 | 22B3A603269DC48100E1E843 /* Debug */, 493 | 22B3A604269DC48100E1E843 /* Release */, 494 | ); 495 | defaultConfigurationIsVisible = 0; 496 | defaultConfigurationName = Release; 497 | }; 498 | /* End XCConfigurationList section */ 499 | }; 500 | rootObject = 22B3A5E6269DC48000E1E843 /* Project object */; 501 | } 502 | --------------------------------------------------------------------------------