├── Images ├── main.gif ├── coordinator.png ├── onboarding.gif └── communication.png ├── CoordinatorProject ├── Assets.xcassets │ ├── Contents.json │ ├── cat.imageset │ │ ├── cat.jpg │ │ └── Contents.json │ ├── lama.imageset │ │ ├── lama.jpg │ │ └── Contents.json │ ├── rose.imageset │ │ ├── rose.jpg │ │ └── Contents.json │ ├── AccentColor.colorset │ │ └── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── Protocols │ └── Coordinator.swift ├── Main │ ├── First │ │ ├── FirstTabViewModel.swift │ │ ├── FirstDetailView.swift │ │ ├── FirstTabCoordinator.swift │ │ └── FirstViewController.swift │ ├── Second │ │ ├── SecondTabCoordinator.swift │ │ ├── SecondViewController.swift │ │ └── SecondViewController.xib │ └── MainCoordinator.swift ├── ViewController.swift ├── ContentView.swift ├── Helper │ └── ScaledImageView.swift ├── Info.plist ├── Onboarding │ ├── OnboardingCoordinator.swift │ └── OnboardingView.swift ├── SceneDelegate.swift ├── AppDelegate.swift ├── Base.lproj │ └── LaunchScreen.storyboard └── ApplicationCoordinator.swift ├── CoordinatorProject.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── xcuserdata │ └── karinprater.xcuserdatad │ │ └── xcschemes │ │ └── xcschememanagement.plist └── project.pbxproj └── README.md /Images/main.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gahntpo/CoordinatorProject/HEAD/Images/main.gif -------------------------------------------------------------------------------- /Images/coordinator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gahntpo/CoordinatorProject/HEAD/Images/coordinator.png -------------------------------------------------------------------------------- /Images/onboarding.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gahntpo/CoordinatorProject/HEAD/Images/onboarding.gif -------------------------------------------------------------------------------- /Images/communication.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gahntpo/CoordinatorProject/HEAD/Images/communication.png -------------------------------------------------------------------------------- /CoordinatorProject/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /CoordinatorProject/Assets.xcassets/cat.imageset/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gahntpo/CoordinatorProject/HEAD/CoordinatorProject/Assets.xcassets/cat.imageset/cat.jpg -------------------------------------------------------------------------------- /CoordinatorProject/Assets.xcassets/lama.imageset/lama.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gahntpo/CoordinatorProject/HEAD/CoordinatorProject/Assets.xcassets/lama.imageset/lama.jpg -------------------------------------------------------------------------------- /CoordinatorProject/Assets.xcassets/rose.imageset/rose.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gahntpo/CoordinatorProject/HEAD/CoordinatorProject/Assets.xcassets/rose.imageset/rose.jpg -------------------------------------------------------------------------------- /CoordinatorProject.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CoordinatorProject/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 | -------------------------------------------------------------------------------- /CoordinatorProject/Protocols/Coordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Coordinator.swift 3 | // CoordinatorProject 4 | // 5 | // Created by Karin Prater on 17.04.22. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol Coordinator { 11 | func start() 12 | } 13 | -------------------------------------------------------------------------------- /CoordinatorProject.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CoordinatorProject/Main/First/FirstTabViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FirstTabViewModel.swift 3 | // CoordinatorProject 4 | // 5 | // Created by Karin Prater on 17.04.22. 6 | // 7 | 8 | import Foundation 9 | 10 | class FirstTabViewModel: ObservableObject { 11 | 12 | @Published var name: String = "" 13 | @Published var email: String = "" 14 | 15 | } 16 | -------------------------------------------------------------------------------- /CoordinatorProject/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // CoordinatorProject 4 | // 5 | // Created by Karin Prater on 17.04.22. 6 | // 7 | 8 | import UIKit 9 | 10 | class ViewController: UIViewController { 11 | 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | view.backgroundColor = .cyan 15 | } 16 | 17 | 18 | } 19 | 20 | -------------------------------------------------------------------------------- /CoordinatorProject/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // CoordinatorProject 4 | // 5 | // Created by Karin Prater on 17.04.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ContentView: View { 11 | var body: some View { 12 | Text("SwiftUI ContentView") 13 | } 14 | } 15 | 16 | struct ContentView_Previews: PreviewProvider { 17 | static var previews: some View { 18 | ContentView() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /CoordinatorProject/Assets.xcassets/cat.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "cat.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /CoordinatorProject/Assets.xcassets/lama.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "lama.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /CoordinatorProject/Assets.xcassets/rose.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "rose.jpg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /CoordinatorProject.xcodeproj/xcuserdata/karinprater.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | CoordinatorProject.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /CoordinatorProject/Helper/ScaledImageView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScaledImageView.swift 3 | // CoordinatorProject 4 | // 5 | // Created by Karin Prater on 17.04.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ScaledImageView: View { 11 | let name: String 12 | var body: some View { 13 | Image(name) 14 | .resizable() 15 | .scaledToFit() 16 | } 17 | } 18 | // 19 | //struct ScaledImageView_Previews: PreviewProvider { 20 | // static var previews: some View { 21 | // ScaledImageView() 22 | // } 23 | //} 24 | -------------------------------------------------------------------------------- /CoordinatorProject/Main/Second/SecondTabCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SecondTabCoordinator.swift 3 | // CoordinatorProject 4 | // 5 | // Created by Karin Prater on 17.04.22. 6 | // 7 | 8 | import UIKit 9 | 10 | class SecondTabCoodinator: Coordinator { 11 | 12 | var rootViewController = UINavigationController() 13 | 14 | lazy var secondViewController: SecondViewController = { 15 | let vc = SecondViewController() 16 | vc.title = "Second" 17 | return vc 18 | }() 19 | 20 | 21 | func start() { 22 | rootViewController.setViewControllers([secondViewController], animated: false) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /CoordinatorProject/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UIApplicationSceneManifest 6 | 7 | UIApplicationSupportsMultipleScenes 8 | 9 | UISceneConfigurations 10 | 11 | UIWindowSceneSessionRoleApplication 12 | 13 | 14 | UISceneConfigurationName 15 | Default Configuration 16 | UISceneDelegateClassName 17 | $(PRODUCT_MODULE_NAME).SceneDelegate 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CoordinatorProject 2 | Demonstrates the Coordinator pattern for an iOS project that included UIKit and SwiftUI screens 3 | 4 | ## Coordinator Pattern 5 | 6 | 7 | ## Overview of Comunnication Patterns 8 | 9 | 10 | ## Subflow Example App 11 | onbording flow 12 | 13 | 14 | 15 | when onboarding is finished the main flow is shown 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /CoordinatorProject/Onboarding/OnboardingCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnboardingCoordinator.swift 3 | // CoordinatorProject 4 | // 5 | // Created by Karin Prater on 17.04.22. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | import Combine 11 | 12 | class OnboardingCoordinator: Coordinator { 13 | 14 | var rootViewController = UIViewController() 15 | 16 | var hasSeenOnboarding: CurrentValueSubject 17 | 18 | init(hasSeenOnboarding: CurrentValueSubject) { 19 | self.hasSeenOnboarding = hasSeenOnboarding 20 | } 21 | 22 | func start() { 23 | let view = OnboardingView { [weak self] in 24 | self?.hasSeenOnboarding.send(true) 25 | } 26 | rootViewController = UIHostingController(rootView: view) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /CoordinatorProject/Main/Second/SecondViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SecondViewController.swift 3 | // CoordinatorProject 4 | // 5 | // Created by Karin Prater on 17.04.22. 6 | // 7 | 8 | import UIKit 9 | 10 | class SecondViewController: UIViewController { 11 | 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | 15 | // Do any additional setup after loading the view. 16 | } 17 | 18 | 19 | /* 20 | // MARK: - Navigation 21 | 22 | // In a storyboard-based application, you will often want to do a little preparation before navigation 23 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 24 | // Get the new view controller using segue.destination. 25 | // Pass the selected object to the new view controller. 26 | } 27 | */ 28 | 29 | } 30 | -------------------------------------------------------------------------------- /CoordinatorProject/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // CoordinatorProject 4 | // 5 | // Created by Karin Prater on 17.04.22. 6 | // 7 | 8 | import UIKit 9 | import SwiftUI 10 | 11 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 12 | 13 | var applicationCoordintor: ApplicationCoordinator? 14 | 15 | 16 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 17 | if let windowScene = scene as? UIWindowScene { 18 | let window = UIWindow(windowScene: windowScene) 19 | let applicationCoordintor = ApplicationCoordinator(window: window) 20 | applicationCoordintor.start() 21 | 22 | self.applicationCoordintor = applicationCoordintor 23 | window.makeKeyAndVisible() 24 | } 25 | 26 | } 27 | 28 | 29 | 30 | 31 | } 32 | 33 | -------------------------------------------------------------------------------- /CoordinatorProject/Main/First/FirstDetailView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FirstDetailView.swift 3 | // CoordinatorProject 4 | // 5 | // Created by Karin Prater on 17.04.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct FirstDetailView: View { 11 | 12 | @ObservedObject var viewModel: FirstTabViewModel 13 | 14 | var body: some View { 15 | 16 | VStack { 17 | Text("First Detail") 18 | 19 | TextField("name", text: $viewModel.name) 20 | .textFieldStyle(RoundedBorderTextFieldStyle()) 21 | 22 | TextField("email", text: $viewModel.email) 23 | .textFieldStyle(RoundedBorderTextFieldStyle()) 24 | } 25 | .padding() 26 | } 27 | } 28 | 29 | struct FirstDetailView_Previews: PreviewProvider { 30 | static var previews: some View { 31 | FirstDetailView(viewModel: FirstTabViewModel()) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /CoordinatorProject/Onboarding/OnboardingView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OnboardingView.swift 3 | // CoordinatorProject 4 | // 5 | // Created by Karin Prater on 17.04.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct OnboardingView: View { 11 | 12 | var doneRequested: () -> () 13 | 14 | var body: some View { 15 | TabView { 16 | 17 | ScaledImageView(name: "cat") 18 | .tag(0) 19 | ScaledImageView(name: "lama") 20 | .tag(1) 21 | ScaledImageView(name: "rose") 22 | .tag(2) 23 | 24 | Button("Done") { 25 | doneRequested() 26 | } 27 | 28 | } 29 | .tabViewStyle(PageTabViewStyle()) 30 | .background(Color.black 31 | .ignoresSafeArea(.all)) 32 | } 33 | } 34 | 35 | struct OnboardingView_Previews: PreviewProvider { 36 | static var previews: some View { 37 | OnboardingView(doneRequested: { }) 38 | } 39 | } 40 | 41 | 42 | -------------------------------------------------------------------------------- /CoordinatorProject/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // CoordinatorProject 4 | // 5 | // Created by Karin Prater on 17.04.22. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | // MARK: UISceneSession Lifecycle 21 | 22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 23 | // Called when a new scene session is being created. 24 | // Use this method to select a configuration to create the new scene with. 25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 26 | } 27 | 28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 29 | // Called when the user discards a scene session. 30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /CoordinatorProject/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 | -------------------------------------------------------------------------------- /CoordinatorProject/Main/First/FirstTabCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FirstTabCoordinator.swift 3 | // CoordinatorProject 4 | // 5 | // Created by Karin Prater on 17.04.22. 6 | // 7 | 8 | import UIKit 9 | import SwiftUI 10 | 11 | class FirstTabCoodinator: NSObject, Coordinator { 12 | 13 | var rootViewController: UINavigationController 14 | 15 | let viewModel = FirstTabViewModel() 16 | 17 | override init() { 18 | rootViewController = UINavigationController() 19 | rootViewController.navigationBar.prefersLargeTitles = true 20 | super.init() 21 | 22 | rootViewController.delegate = self 23 | } 24 | 25 | lazy var firstViewController: FirstViewController = { 26 | let vc = FirstViewController() 27 | vc.viewModel = viewModel 28 | vc.showDetailRequested = { [weak self] in 29 | self?.goToDetail() 30 | } 31 | vc.title = "First title" 32 | return vc 33 | }() 34 | 35 | 36 | func start() { 37 | rootViewController.setViewControllers([firstViewController], animated: false) 38 | } 39 | 40 | func goToDetail() { 41 | let detailViewController = UIHostingController(rootView: FirstDetailView(viewModel: viewModel)) 42 | rootViewController.pushViewController(detailViewController, animated: true) 43 | } 44 | } 45 | 46 | extension FirstTabCoodinator: UINavigationControllerDelegate { 47 | func navigationController(_ navigationController: UINavigationController, 48 | willShow viewController: UIViewController, 49 | animated: Bool) { 50 | 51 | if viewController as? UIHostingController != nil { 52 | print("detail will be shown") 53 | } else if viewController as? FirstViewController != nil { 54 | print("first will be shown") 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /CoordinatorProject/Main/MainCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainCoordinator.swift 3 | // CoordinatorProject 4 | // 5 | // Created by Karin Prater on 17.04.22. 6 | // 7 | 8 | import Foundation 9 | import UIKit 10 | 11 | class MainCoordinator: Coordinator { 12 | 13 | var rootViewController: UITabBarController 14 | 15 | var childCoordinators = [Coordinator]() 16 | 17 | init() { 18 | self.rootViewController = UITabBarController() 19 | rootViewController.tabBar.isTranslucent = true 20 | rootViewController.tabBar.backgroundColor = .lightGray 21 | } 22 | 23 | func start() { 24 | 25 | let firstCoordinator = FirstTabCoodinator() 26 | firstCoordinator.start() 27 | self.childCoordinators.append(firstCoordinator) 28 | let firstViewController = firstCoordinator.rootViewController 29 | setup(vc: firstViewController, 30 | title: "First Tab", 31 | imageName: "paperplane", 32 | selectedImageName: "paperplane.fill") 33 | 34 | 35 | let secondCoordinator = SecondTabCoodinator() 36 | secondCoordinator.start() 37 | self.childCoordinators.append(secondCoordinator) 38 | let secondViewController = secondCoordinator.rootViewController 39 | setup(vc: secondViewController, 40 | title: "Second Tab", 41 | imageName: "bell", 42 | selectedImageName: "bell.fill") 43 | 44 | 45 | self.rootViewController.viewControllers = [firstViewController, secondViewController] 46 | 47 | } 48 | 49 | func setup(vc: UIViewController, title: String, imageName: String, selectedImageName: String) { 50 | let defaultImage = UIImage(systemName: imageName) 51 | let selectedImage = UIImage(systemName: selectedImageName) 52 | let tabBarItem = UITabBarItem(title: title, image: defaultImage, selectedImage: selectedImage) 53 | vc.tabBarItem = tabBarItem 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /CoordinatorProject/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 | -------------------------------------------------------------------------------- /CoordinatorProject/ApplicationCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ApplicationCoordinator.swift 3 | // CoordinatorProject 4 | // 5 | // Created by Karin Prater on 17.04.22. 6 | // 7 | 8 | import SwiftUI 9 | import UIKit 10 | import Combine 11 | 12 | class ApplicationCoordinator: Coordinator { 13 | 14 | let window: UIWindow 15 | 16 | var childCoordinators = [Coordinator]() 17 | 18 | let hasSeenOnboarding = CurrentValueSubject(false) 19 | var subscriptions = Set() 20 | 21 | init(window: UIWindow) { 22 | 23 | self.window = window 24 | 25 | } 26 | 27 | 28 | func start() { 29 | 30 | setupOnboardingValue() 31 | 32 | hasSeenOnboarding 33 | .removeDuplicates() 34 | .sink { [weak self] hasSeen in 35 | if hasSeen { 36 | let mainCoordinator = MainCoordinator() 37 | mainCoordinator.start() 38 | self?.childCoordinators = [mainCoordinator] 39 | self?.window.rootViewController = mainCoordinator.rootViewController 40 | } else if let hasSeenOnboarding = self?.hasSeenOnboarding { 41 | let onboardingCoordinator = OnboardingCoordinator(hasSeenOnboarding: hasSeenOnboarding) 42 | onboardingCoordinator.start() 43 | self?.childCoordinators = [onboardingCoordinator] 44 | self?.window.rootViewController = onboardingCoordinator.rootViewController 45 | } 46 | } 47 | .store(in: &subscriptions) 48 | } 49 | 50 | func setupOnboardingValue() { 51 | 52 | // storing and loading of state/data 53 | 54 | let key = "hasSeenOnboarding" 55 | let value = UserDefaults.standard.bool(forKey: key) //default of false 56 | hasSeenOnboarding.send(value) 57 | 58 | hasSeenOnboarding 59 | .filter({ $0 }) 60 | .sink { (value) in 61 | UserDefaults.standard.setValue(value, forKey: key) 62 | } 63 | .store(in: &subscriptions) 64 | 65 | } 66 | 67 | 68 | } 69 | -------------------------------------------------------------------------------- /CoordinatorProject/Main/First/FirstViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FirstViewController.swift 3 | // CoordinatorProject 4 | // 5 | // Created by Karin Prater on 17.04.22. 6 | // 7 | 8 | import UIKit 9 | import Combine 10 | 11 | class FirstViewController: UIViewController { 12 | 13 | var infoLabel: UILabel? 14 | var viewModel: FirstTabViewModel! 15 | var showDetailRequested: () -> () = { } 16 | 17 | var subscriptions = Set() 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | view.backgroundColor = .white 22 | 23 | setupInfoLabel() 24 | setupDetailButton() 25 | } 26 | 27 | override func viewWillAppear(_ animated: Bool) { 28 | //infoLabel?.text = "\(viewModel.name) with email \(viewModel.email)" 29 | } 30 | 31 | func setupDetailButton() { 32 | let button = UIButton(frame: CGRect(x: 100, 33 | y: 500, 34 | width: 200, 35 | height: 60)) 36 | button.setTitleColor(.systemBlue, 37 | for: .normal) 38 | button.setTitle("Go to Detail", 39 | for: .normal) 40 | 41 | button.addTarget(self, 42 | action: #selector(buttonAction), 43 | for: .touchUpInside) 44 | 45 | self.view.addSubview(button) 46 | } 47 | 48 | func setupInfoLabel() { 49 | let infoLabel = UILabel(frame: CGRect(x: 100, y: 300, width: 300, height: 60)) 50 | self.view.addSubview(infoLabel) 51 | self.infoLabel = infoLabel 52 | 53 | viewModel.$email.combineLatest(viewModel.$name) 54 | .sink { [weak self] (email, name) in 55 | if name.count + email.count > 0 { 56 | self?.infoLabel?.text = "\(name) with email \(email)" 57 | } else { 58 | self?.infoLabel?.text = "" 59 | } 60 | } 61 | .store(in: &subscriptions) 62 | } 63 | 64 | 65 | @objc 66 | func buttonAction() { 67 | showDetailRequested() 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /CoordinatorProject/Main/Second/SecondViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /CoordinatorProject.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 55; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 175393FB280D5790003030D2 /* main.gif in Resources */ = {isa = PBXBuildFile; fileRef = 175393FA280D5790003030D2 /* main.gif */; }; 11 | 175393FD280D57D5003030D2 /* onboarding.gif in Resources */ = {isa = PBXBuildFile; fileRef = 175393FC280D57D5003030D2 /* onboarding.gif */; }; 12 | 1782E7482811775A005772E3 /* coordinator.png in Resources */ = {isa = PBXBuildFile; fileRef = 1782E7472811775A005772E3 /* coordinator.png */; }; 13 | 1782E74A28117761005772E3 /* communication.png in Resources */ = {isa = PBXBuildFile; fileRef = 1782E74928117761005772E3 /* communication.png */; }; 14 | 17ECF631280C438F00D3E87F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17ECF630280C438F00D3E87F /* AppDelegate.swift */; }; 15 | 17ECF633280C438F00D3E87F /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17ECF632280C438F00D3E87F /* SceneDelegate.swift */; }; 16 | 17ECF635280C438F00D3E87F /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17ECF634280C438F00D3E87F /* ViewController.swift */; }; 17 | 17ECF63A280C438F00D3E87F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 17ECF639280C438F00D3E87F /* Assets.xcassets */; }; 18 | 17ECF63D280C438F00D3E87F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 17ECF63B280C438F00D3E87F /* LaunchScreen.storyboard */; }; 19 | 17ECF645280C44B200D3E87F /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17ECF644280C44B200D3E87F /* ContentView.swift */; }; 20 | 17ECF648280C450600D3E87F /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17ECF647280C450600D3E87F /* Coordinator.swift */; }; 21 | 17ECF64A280C455200D3E87F /* ApplicationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17ECF649280C455200D3E87F /* ApplicationCoordinator.swift */; }; 22 | 17ECF64E280C469300D3E87F /* OnboardingCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17ECF64D280C469300D3E87F /* OnboardingCoordinator.swift */; }; 23 | 17ECF650280C46D000D3E87F /* OnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17ECF64F280C46D000D3E87F /* OnboardingView.swift */; }; 24 | 17ECF653280C47B500D3E87F /* ScaledImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17ECF652280C47B500D3E87F /* ScaledImageView.swift */; }; 25 | 17ECF655280C4A6200D3E87F /* MainCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17ECF654280C4A6200D3E87F /* MainCoordinator.swift */; }; 26 | 17ECF659280C4AB000D3E87F /* FirstTabCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17ECF658280C4AB000D3E87F /* FirstTabCoordinator.swift */; }; 27 | 17ECF65B280C4B0B00D3E87F /* SecondTabCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17ECF65A280C4B0B00D3E87F /* SecondTabCoordinator.swift */; }; 28 | 17ECF65D280C4B4C00D3E87F /* FirstViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17ECF65C280C4B4C00D3E87F /* FirstViewController.swift */; }; 29 | 17ECF663280C4BBA00D3E87F /* FirstDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17ECF662280C4BB900D3E87F /* FirstDetailView.swift */; }; 30 | 17ECF666280C4BD200D3E87F /* SecondViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17ECF664280C4BD200D3E87F /* SecondViewController.swift */; }; 31 | 17ECF667280C4BD200D3E87F /* SecondViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 17ECF665280C4BD200D3E87F /* SecondViewController.xib */; }; 32 | 17ECF669280C535E00D3E87F /* FirstTabViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17ECF668280C535E00D3E87F /* FirstTabViewModel.swift */; }; 33 | /* End PBXBuildFile section */ 34 | 35 | /* Begin PBXFileReference section */ 36 | 175393FA280D5790003030D2 /* main.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = main.gif; sourceTree = ""; }; 37 | 175393FC280D57D5003030D2 /* onboarding.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = onboarding.gif; sourceTree = ""; }; 38 | 1782E7472811775A005772E3 /* coordinator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = coordinator.png; sourceTree = ""; }; 39 | 1782E74928117761005772E3 /* communication.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = communication.png; sourceTree = ""; }; 40 | 17ECF62D280C438F00D3E87F /* CoordinatorProject.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CoordinatorProject.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 17ECF630280C438F00D3E87F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 42 | 17ECF632280C438F00D3E87F /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; 43 | 17ECF634280C438F00D3E87F /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 44 | 17ECF639280C438F00D3E87F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 45 | 17ECF63C280C438F00D3E87F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 46 | 17ECF63E280C438F00D3E87F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 47 | 17ECF644280C44B200D3E87F /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 48 | 17ECF647280C450600D3E87F /* Coordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = ""; }; 49 | 17ECF649280C455200D3E87F /* ApplicationCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationCoordinator.swift; sourceTree = ""; }; 50 | 17ECF64D280C469300D3E87F /* OnboardingCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingCoordinator.swift; sourceTree = ""; }; 51 | 17ECF64F280C46D000D3E87F /* OnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingView.swift; sourceTree = ""; }; 52 | 17ECF652280C47B500D3E87F /* ScaledImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScaledImageView.swift; sourceTree = ""; }; 53 | 17ECF654280C4A6200D3E87F /* MainCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainCoordinator.swift; sourceTree = ""; }; 54 | 17ECF658280C4AB000D3E87F /* FirstTabCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstTabCoordinator.swift; sourceTree = ""; }; 55 | 17ECF65A280C4B0B00D3E87F /* SecondTabCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondTabCoordinator.swift; sourceTree = ""; }; 56 | 17ECF65C280C4B4C00D3E87F /* FirstViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstViewController.swift; sourceTree = ""; }; 57 | 17ECF662280C4BB900D3E87F /* FirstDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstDetailView.swift; sourceTree = ""; }; 58 | 17ECF664280C4BD200D3E87F /* SecondViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondViewController.swift; sourceTree = ""; }; 59 | 17ECF665280C4BD200D3E87F /* SecondViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SecondViewController.xib; sourceTree = ""; }; 60 | 17ECF668280C535E00D3E87F /* FirstTabViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirstTabViewModel.swift; sourceTree = ""; }; 61 | /* End PBXFileReference section */ 62 | 63 | /* Begin PBXFrameworksBuildPhase section */ 64 | 17ECF62A280C438F00D3E87F /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | ); 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | /* End PBXFrameworksBuildPhase section */ 72 | 73 | /* Begin PBXGroup section */ 74 | 175393F7280D5761003030D2 /* Images */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 175393FA280D5790003030D2 /* main.gif */, 78 | 175393FC280D57D5003030D2 /* onboarding.gif */, 79 | 1782E7472811775A005772E3 /* coordinator.png */, 80 | 1782E74928117761005772E3 /* communication.png */, 81 | ); 82 | path = Images; 83 | sourceTree = ""; 84 | }; 85 | 17ECF624280C438F00D3E87F = { 86 | isa = PBXGroup; 87 | children = ( 88 | 175393F7280D5761003030D2 /* Images */, 89 | 17ECF62F280C438F00D3E87F /* CoordinatorProject */, 90 | 17ECF62E280C438F00D3E87F /* Products */, 91 | ); 92 | sourceTree = ""; 93 | }; 94 | 17ECF62E280C438F00D3E87F /* Products */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 17ECF62D280C438F00D3E87F /* CoordinatorProject.app */, 98 | ); 99 | name = Products; 100 | sourceTree = ""; 101 | }; 102 | 17ECF62F280C438F00D3E87F /* CoordinatorProject */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 17ECF630280C438F00D3E87F /* AppDelegate.swift */, 106 | 17ECF632280C438F00D3E87F /* SceneDelegate.swift */, 107 | 17ECF649280C455200D3E87F /* ApplicationCoordinator.swift */, 108 | 17ECF644280C44B200D3E87F /* ContentView.swift */, 109 | 17ECF634280C438F00D3E87F /* ViewController.swift */, 110 | 17ECF64C280C466E00D3E87F /* Main */, 111 | 17ECF64B280C466600D3E87F /* Onboarding */, 112 | 17ECF646280C44F700D3E87F /* Protocols */, 113 | 17ECF651280C47A400D3E87F /* Helper */, 114 | 17ECF639280C438F00D3E87F /* Assets.xcassets */, 115 | 17ECF63B280C438F00D3E87F /* LaunchScreen.storyboard */, 116 | 17ECF63E280C438F00D3E87F /* Info.plist */, 117 | ); 118 | path = CoordinatorProject; 119 | sourceTree = ""; 120 | }; 121 | 17ECF646280C44F700D3E87F /* Protocols */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 17ECF647280C450600D3E87F /* Coordinator.swift */, 125 | ); 126 | path = Protocols; 127 | sourceTree = ""; 128 | }; 129 | 17ECF64B280C466600D3E87F /* Onboarding */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | 17ECF64D280C469300D3E87F /* OnboardingCoordinator.swift */, 133 | 17ECF64F280C46D000D3E87F /* OnboardingView.swift */, 134 | ); 135 | path = Onboarding; 136 | sourceTree = ""; 137 | }; 138 | 17ECF64C280C466E00D3E87F /* Main */ = { 139 | isa = PBXGroup; 140 | children = ( 141 | 17ECF654280C4A6200D3E87F /* MainCoordinator.swift */, 142 | 17ECF656280C4A9000D3E87F /* First */, 143 | 17ECF657280C4A9600D3E87F /* Second */, 144 | ); 145 | path = Main; 146 | sourceTree = ""; 147 | }; 148 | 17ECF651280C47A400D3E87F /* Helper */ = { 149 | isa = PBXGroup; 150 | children = ( 151 | 17ECF652280C47B500D3E87F /* ScaledImageView.swift */, 152 | ); 153 | path = Helper; 154 | sourceTree = ""; 155 | }; 156 | 17ECF656280C4A9000D3E87F /* First */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | 17ECF658280C4AB000D3E87F /* FirstTabCoordinator.swift */, 160 | 17ECF65C280C4B4C00D3E87F /* FirstViewController.swift */, 161 | 17ECF662280C4BB900D3E87F /* FirstDetailView.swift */, 162 | 17ECF668280C535E00D3E87F /* FirstTabViewModel.swift */, 163 | ); 164 | path = First; 165 | sourceTree = ""; 166 | }; 167 | 17ECF657280C4A9600D3E87F /* Second */ = { 168 | isa = PBXGroup; 169 | children = ( 170 | 17ECF65A280C4B0B00D3E87F /* SecondTabCoordinator.swift */, 171 | 17ECF664280C4BD200D3E87F /* SecondViewController.swift */, 172 | 17ECF665280C4BD200D3E87F /* SecondViewController.xib */, 173 | ); 174 | path = Second; 175 | sourceTree = ""; 176 | }; 177 | /* End PBXGroup section */ 178 | 179 | /* Begin PBXNativeTarget section */ 180 | 17ECF62C280C438F00D3E87F /* CoordinatorProject */ = { 181 | isa = PBXNativeTarget; 182 | buildConfigurationList = 17ECF641280C438F00D3E87F /* Build configuration list for PBXNativeTarget "CoordinatorProject" */; 183 | buildPhases = ( 184 | 17ECF629280C438F00D3E87F /* Sources */, 185 | 17ECF62A280C438F00D3E87F /* Frameworks */, 186 | 17ECF62B280C438F00D3E87F /* Resources */, 187 | ); 188 | buildRules = ( 189 | ); 190 | dependencies = ( 191 | ); 192 | name = CoordinatorProject; 193 | productName = CoordinatorProject; 194 | productReference = 17ECF62D280C438F00D3E87F /* CoordinatorProject.app */; 195 | productType = "com.apple.product-type.application"; 196 | }; 197 | /* End PBXNativeTarget section */ 198 | 199 | /* Begin PBXProject section */ 200 | 17ECF625280C438F00D3E87F /* Project object */ = { 201 | isa = PBXProject; 202 | attributes = { 203 | BuildIndependentTargetsInParallel = 1; 204 | LastSwiftUpdateCheck = 1330; 205 | LastUpgradeCheck = 1330; 206 | TargetAttributes = { 207 | 17ECF62C280C438F00D3E87F = { 208 | CreatedOnToolsVersion = 13.3; 209 | }; 210 | }; 211 | }; 212 | buildConfigurationList = 17ECF628280C438F00D3E87F /* Build configuration list for PBXProject "CoordinatorProject" */; 213 | compatibilityVersion = "Xcode 13.0"; 214 | developmentRegion = en; 215 | hasScannedForEncodings = 0; 216 | knownRegions = ( 217 | en, 218 | Base, 219 | ); 220 | mainGroup = 17ECF624280C438F00D3E87F; 221 | productRefGroup = 17ECF62E280C438F00D3E87F /* Products */; 222 | projectDirPath = ""; 223 | projectRoot = ""; 224 | targets = ( 225 | 17ECF62C280C438F00D3E87F /* CoordinatorProject */, 226 | ); 227 | }; 228 | /* End PBXProject section */ 229 | 230 | /* Begin PBXResourcesBuildPhase section */ 231 | 17ECF62B280C438F00D3E87F /* Resources */ = { 232 | isa = PBXResourcesBuildPhase; 233 | buildActionMask = 2147483647; 234 | files = ( 235 | 1782E7482811775A005772E3 /* coordinator.png in Resources */, 236 | 1782E74A28117761005772E3 /* communication.png in Resources */, 237 | 17ECF63D280C438F00D3E87F /* LaunchScreen.storyboard in Resources */, 238 | 175393FB280D5790003030D2 /* main.gif in Resources */, 239 | 17ECF63A280C438F00D3E87F /* Assets.xcassets in Resources */, 240 | 17ECF667280C4BD200D3E87F /* SecondViewController.xib in Resources */, 241 | 175393FD280D57D5003030D2 /* onboarding.gif in Resources */, 242 | ); 243 | runOnlyForDeploymentPostprocessing = 0; 244 | }; 245 | /* End PBXResourcesBuildPhase section */ 246 | 247 | /* Begin PBXSourcesBuildPhase section */ 248 | 17ECF629280C438F00D3E87F /* Sources */ = { 249 | isa = PBXSourcesBuildPhase; 250 | buildActionMask = 2147483647; 251 | files = ( 252 | 17ECF635280C438F00D3E87F /* ViewController.swift in Sources */, 253 | 17ECF631280C438F00D3E87F /* AppDelegate.swift in Sources */, 254 | 17ECF648280C450600D3E87F /* Coordinator.swift in Sources */, 255 | 17ECF666280C4BD200D3E87F /* SecondViewController.swift in Sources */, 256 | 17ECF655280C4A6200D3E87F /* MainCoordinator.swift in Sources */, 257 | 17ECF65B280C4B0B00D3E87F /* SecondTabCoordinator.swift in Sources */, 258 | 17ECF65D280C4B4C00D3E87F /* FirstViewController.swift in Sources */, 259 | 17ECF64E280C469300D3E87F /* OnboardingCoordinator.swift in Sources */, 260 | 17ECF645280C44B200D3E87F /* ContentView.swift in Sources */, 261 | 17ECF653280C47B500D3E87F /* ScaledImageView.swift in Sources */, 262 | 17ECF659280C4AB000D3E87F /* FirstTabCoordinator.swift in Sources */, 263 | 17ECF650280C46D000D3E87F /* OnboardingView.swift in Sources */, 264 | 17ECF633280C438F00D3E87F /* SceneDelegate.swift in Sources */, 265 | 17ECF669280C535E00D3E87F /* FirstTabViewModel.swift in Sources */, 266 | 17ECF663280C4BBA00D3E87F /* FirstDetailView.swift in Sources */, 267 | 17ECF64A280C455200D3E87F /* ApplicationCoordinator.swift in Sources */, 268 | ); 269 | runOnlyForDeploymentPostprocessing = 0; 270 | }; 271 | /* End PBXSourcesBuildPhase section */ 272 | 273 | /* Begin PBXVariantGroup section */ 274 | 17ECF63B280C438F00D3E87F /* LaunchScreen.storyboard */ = { 275 | isa = PBXVariantGroup; 276 | children = ( 277 | 17ECF63C280C438F00D3E87F /* Base */, 278 | ); 279 | name = LaunchScreen.storyboard; 280 | sourceTree = ""; 281 | }; 282 | /* End PBXVariantGroup section */ 283 | 284 | /* Begin XCBuildConfiguration section */ 285 | 17ECF63F280C438F00D3E87F /* Debug */ = { 286 | isa = XCBuildConfiguration; 287 | buildSettings = { 288 | ALWAYS_SEARCH_USER_PATHS = NO; 289 | CLANG_ANALYZER_NONNULL = YES; 290 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 291 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 292 | CLANG_ENABLE_MODULES = YES; 293 | CLANG_ENABLE_OBJC_ARC = YES; 294 | CLANG_ENABLE_OBJC_WEAK = YES; 295 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 296 | CLANG_WARN_BOOL_CONVERSION = YES; 297 | CLANG_WARN_COMMA = YES; 298 | CLANG_WARN_CONSTANT_CONVERSION = YES; 299 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 300 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 301 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 302 | CLANG_WARN_EMPTY_BODY = YES; 303 | CLANG_WARN_ENUM_CONVERSION = YES; 304 | CLANG_WARN_INFINITE_RECURSION = YES; 305 | CLANG_WARN_INT_CONVERSION = YES; 306 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 307 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 308 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 309 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 310 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 311 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 312 | CLANG_WARN_STRICT_PROTOTYPES = YES; 313 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 314 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 315 | CLANG_WARN_UNREACHABLE_CODE = YES; 316 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 317 | COPY_PHASE_STRIP = NO; 318 | DEBUG_INFORMATION_FORMAT = dwarf; 319 | ENABLE_STRICT_OBJC_MSGSEND = YES; 320 | ENABLE_TESTABILITY = YES; 321 | GCC_C_LANGUAGE_STANDARD = gnu11; 322 | GCC_DYNAMIC_NO_PIC = NO; 323 | GCC_NO_COMMON_BLOCKS = YES; 324 | GCC_OPTIMIZATION_LEVEL = 0; 325 | GCC_PREPROCESSOR_DEFINITIONS = ( 326 | "DEBUG=1", 327 | "$(inherited)", 328 | ); 329 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 330 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 331 | GCC_WARN_UNDECLARED_SELECTOR = YES; 332 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 333 | GCC_WARN_UNUSED_FUNCTION = YES; 334 | GCC_WARN_UNUSED_VARIABLE = YES; 335 | IPHONEOS_DEPLOYMENT_TARGET = 15.4; 336 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 337 | MTL_FAST_MATH = YES; 338 | ONLY_ACTIVE_ARCH = YES; 339 | SDKROOT = iphoneos; 340 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 341 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 342 | }; 343 | name = Debug; 344 | }; 345 | 17ECF640280C438F00D3E87F /* Release */ = { 346 | isa = XCBuildConfiguration; 347 | buildSettings = { 348 | ALWAYS_SEARCH_USER_PATHS = NO; 349 | CLANG_ANALYZER_NONNULL = YES; 350 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 351 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; 352 | CLANG_ENABLE_MODULES = YES; 353 | CLANG_ENABLE_OBJC_ARC = YES; 354 | CLANG_ENABLE_OBJC_WEAK = YES; 355 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 356 | CLANG_WARN_BOOL_CONVERSION = YES; 357 | CLANG_WARN_COMMA = YES; 358 | CLANG_WARN_CONSTANT_CONVERSION = YES; 359 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 360 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 361 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 362 | CLANG_WARN_EMPTY_BODY = YES; 363 | CLANG_WARN_ENUM_CONVERSION = YES; 364 | CLANG_WARN_INFINITE_RECURSION = YES; 365 | CLANG_WARN_INT_CONVERSION = YES; 366 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 367 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 368 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 369 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 370 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 371 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 372 | CLANG_WARN_STRICT_PROTOTYPES = YES; 373 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 374 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 375 | CLANG_WARN_UNREACHABLE_CODE = YES; 376 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 377 | COPY_PHASE_STRIP = NO; 378 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 379 | ENABLE_NS_ASSERTIONS = NO; 380 | ENABLE_STRICT_OBJC_MSGSEND = YES; 381 | GCC_C_LANGUAGE_STANDARD = gnu11; 382 | GCC_NO_COMMON_BLOCKS = YES; 383 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 384 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 385 | GCC_WARN_UNDECLARED_SELECTOR = YES; 386 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 387 | GCC_WARN_UNUSED_FUNCTION = YES; 388 | GCC_WARN_UNUSED_VARIABLE = YES; 389 | IPHONEOS_DEPLOYMENT_TARGET = 15.4; 390 | MTL_ENABLE_DEBUG_INFO = NO; 391 | MTL_FAST_MATH = YES; 392 | SDKROOT = iphoneos; 393 | SWIFT_COMPILATION_MODE = wholemodule; 394 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 395 | VALIDATE_PRODUCT = YES; 396 | }; 397 | name = Release; 398 | }; 399 | 17ECF642280C438F00D3E87F /* Debug */ = { 400 | isa = XCBuildConfiguration; 401 | buildSettings = { 402 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 403 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 404 | CODE_SIGN_STYLE = Automatic; 405 | CURRENT_PROJECT_VERSION = 1; 406 | DEVELOPMENT_TEAM = 54YLWD89Z8; 407 | GENERATE_INFOPLIST_FILE = YES; 408 | INFOPLIST_FILE = CoordinatorProject/Info.plist; 409 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 410 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 411 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 412 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 413 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 414 | LD_RUNPATH_SEARCH_PATHS = ( 415 | "$(inherited)", 416 | "@executable_path/Frameworks", 417 | ); 418 | MARKETING_VERSION = 1.0; 419 | PRODUCT_BUNDLE_IDENTIFIER = com.karinprater.CoordinatorProject; 420 | PRODUCT_NAME = "$(TARGET_NAME)"; 421 | SWIFT_EMIT_LOC_STRINGS = YES; 422 | SWIFT_VERSION = 5.0; 423 | TARGETED_DEVICE_FAMILY = "1,2"; 424 | }; 425 | name = Debug; 426 | }; 427 | 17ECF643280C438F00D3E87F /* Release */ = { 428 | isa = XCBuildConfiguration; 429 | buildSettings = { 430 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 431 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 432 | CODE_SIGN_STYLE = Automatic; 433 | CURRENT_PROJECT_VERSION = 1; 434 | DEVELOPMENT_TEAM = 54YLWD89Z8; 435 | GENERATE_INFOPLIST_FILE = YES; 436 | INFOPLIST_FILE = CoordinatorProject/Info.plist; 437 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; 438 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; 439 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 440 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; 441 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 442 | LD_RUNPATH_SEARCH_PATHS = ( 443 | "$(inherited)", 444 | "@executable_path/Frameworks", 445 | ); 446 | MARKETING_VERSION = 1.0; 447 | PRODUCT_BUNDLE_IDENTIFIER = com.karinprater.CoordinatorProject; 448 | PRODUCT_NAME = "$(TARGET_NAME)"; 449 | SWIFT_EMIT_LOC_STRINGS = YES; 450 | SWIFT_VERSION = 5.0; 451 | TARGETED_DEVICE_FAMILY = "1,2"; 452 | }; 453 | name = Release; 454 | }; 455 | /* End XCBuildConfiguration section */ 456 | 457 | /* Begin XCConfigurationList section */ 458 | 17ECF628280C438F00D3E87F /* Build configuration list for PBXProject "CoordinatorProject" */ = { 459 | isa = XCConfigurationList; 460 | buildConfigurations = ( 461 | 17ECF63F280C438F00D3E87F /* Debug */, 462 | 17ECF640280C438F00D3E87F /* Release */, 463 | ); 464 | defaultConfigurationIsVisible = 0; 465 | defaultConfigurationName = Release; 466 | }; 467 | 17ECF641280C438F00D3E87F /* Build configuration list for PBXNativeTarget "CoordinatorProject" */ = { 468 | isa = XCConfigurationList; 469 | buildConfigurations = ( 470 | 17ECF642280C438F00D3E87F /* Debug */, 471 | 17ECF643280C438F00D3E87F /* Release */, 472 | ); 473 | defaultConfigurationIsVisible = 0; 474 | defaultConfigurationName = Release; 475 | }; 476 | /* End XCConfigurationList section */ 477 | }; 478 | rootObject = 17ECF625280C438F00D3E87F /* Project object */; 479 | } 480 | --------------------------------------------------------------------------------