turbo-native-initializer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (140) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/CODE_OF_CONDUCT.md +84 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +53 -0
  6. data/Rakefile +2 -0
  7. data/exe/turbo-native-initializer +3 -0
  8. data/lib/turbo-native-initializer.rb +1 -0
  9. data/lib/turbo_native_initializer/generator.rb +79 -0
  10. data/lib/turbo_native_initializer/templates/android_stack/app/.gitignore +1 -0
  11. data/lib/turbo_native_initializer/templates/android_stack/app/build.gradle.kts.tt +44 -0
  12. data/lib/turbo_native_initializer/templates/android_stack/app/proguard-rules.pro +21 -0
  13. data/lib/turbo_native_initializer/templates/android_stack/app/src/debug/AndroidManifest.xml.tt +4 -0
  14. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/AndroidManifest.xml.tt +29 -0
  15. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/assets/json/configuration.json +11 -0
  16. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/base/NavDestination.kt.tt +69 -0
  17. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/features/web/WebFragment.kt.tt +37 -0
  18. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/features/web/WebHomeFragment.kt.tt +15 -0
  19. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/features/web/WebModalFragment.kt.tt +17 -0
  20. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/main/MainActivity.kt.tt +18 -0
  21. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/main/MainSessionNavHostFragment.kt.tt +37 -0
  22. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/util/Constants.kt.tt +8 -0
  23. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/res/anim/nav_slide_enter.xml +10 -0
  24. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/res/anim/nav_slide_exit.xml +7 -0
  25. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/res/anim/nav_slide_pop_enter.xml +15 -0
  26. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/res/anim/nav_slide_pop_exit.xml +15 -0
  27. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/res/anim/overshoot_interpolator.xml +4 -0
  28. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/res/drawable/ic_close.xml +5 -0
  29. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/res/drawable/ic_launcher_foreground.xml +17 -0
  30. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/res/layout/activity_main.xml.tt +17 -0
  31. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/res/layout/fragment_web_home.xml +36 -0
  32. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/res/layout/toolbar_progress.xml +13 -0
  33. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/res/menu/web.xml +14 -0
  34. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +5 -0
  35. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +5 -0
  36. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  37. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  38. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  39. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  40. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  41. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  42. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/res/values/colors.xml +5 -0
  43. data/lib/turbo_native_initializer/templates/android_stack/app/src/main/res/values/themes.xml.tt +5 -0
  44. data/lib/turbo_native_initializer/templates/android_stack/build.gradle.kts +5 -0
  45. data/lib/turbo_native_initializer/templates/android_stack/gradle/wrapper/gradle-wrapper.jar +0 -0
  46. data/lib/turbo_native_initializer/templates/android_stack/gradle/wrapper/gradle-wrapper.properties +6 -0
  47. data/lib/turbo_native_initializer/templates/android_stack/gradle.properties +23 -0
  48. data/lib/turbo_native_initializer/templates/android_stack/gradlew +185 -0
  49. data/lib/turbo_native_initializer/templates/android_stack/gradlew.bat +89 -0
  50. data/lib/turbo_native_initializer/templates/android_stack/local.properties +10 -0
  51. data/lib/turbo_native_initializer/templates/android_stack/settings.gradle.kts.tt +17 -0
  52. data/lib/turbo_native_initializer/templates/android_tabs/app/.gitignore +1 -0
  53. data/lib/turbo_native_initializer/templates/android_tabs/app/build.gradle.kts.tt +44 -0
  54. data/lib/turbo_native_initializer/templates/android_tabs/app/proguard-rules.pro +21 -0
  55. data/lib/turbo_native_initializer/templates/android_tabs/app/src/debug/AndroidManifest.xml.tt +4 -0
  56. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/AndroidManifest.xml.tt +29 -0
  57. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/assets/json/configuration.json +11 -0
  58. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/base/NavDestination.kt.tt +69 -0
  59. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/features/web/WebFragment.kt.tt +29 -0
  60. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/features/web/WebHomeFragment.kt.tt +15 -0
  61. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/features/web/WebModalFragment.kt.tt +17 -0
  62. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/main/MainActivity.kt.tt +47 -0
  63. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/main/TabOneSessionNavHostFragment.kt.tt +37 -0
  64. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/main/TabTwoSessionNavHostFragment.kt.tt +37 -0
  65. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/util/Constants.kt.tt +8 -0
  66. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/anim/nav_slide_enter.xml +10 -0
  67. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/anim/nav_slide_exit.xml +7 -0
  68. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/anim/nav_slide_pop_enter.xml +15 -0
  69. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/anim/nav_slide_pop_exit.xml +15 -0
  70. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/anim/overshoot_interpolator.xml +4 -0
  71. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/drawable/ic_close.xml +5 -0
  72. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/drawable/ic_home.xml +5 -0
  73. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/drawable/ic_launcher_foreground.xml +17 -0
  74. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/drawable/ic_settings.xml +5 -0
  75. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/layout/activity_main.xml.tt +34 -0
  76. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/layout/fragment_web_home.xml +36 -0
  77. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/layout/toolbar_progress.xml +13 -0
  78. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/menu/bottom_navigation_menu.xml +12 -0
  79. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/menu/web.xml +14 -0
  80. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +5 -0
  81. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +5 -0
  82. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  83. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png +0 -0
  84. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  85. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  86. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  87. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  88. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/values/colors.xml +5 -0
  89. data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/res/values/themes.xml.tt +5 -0
  90. data/lib/turbo_native_initializer/templates/android_tabs/build.gradle.kts +5 -0
  91. data/lib/turbo_native_initializer/templates/android_tabs/gradle/wrapper/gradle-wrapper.jar +0 -0
  92. data/lib/turbo_native_initializer/templates/android_tabs/gradle/wrapper/gradle-wrapper.properties +6 -0
  93. data/lib/turbo_native_initializer/templates/android_tabs/gradle.properties +23 -0
  94. data/lib/turbo_native_initializer/templates/android_tabs/gradlew +185 -0
  95. data/lib/turbo_native_initializer/templates/android_tabs/gradlew.bat +89 -0
  96. data/lib/turbo_native_initializer/templates/android_tabs/local.properties +10 -0
  97. data/lib/turbo_native_initializer/templates/android_tabs/settings.gradle.kts.tt +17 -0
  98. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Configuration/Info.plist +29 -0
  99. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Configuration/path-configuration.json +10 -0
  100. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Controllers/ErrorPresenter.swift +74 -0
  101. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Controllers/TurboNavigationController.swift +123 -0
  102. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Controllers/ViewController.swift +20 -0
  103. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Delegates/AppDelegate.swift +8 -0
  104. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Delegates/SceneDelegate.swift.tt +139 -0
  105. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Resources/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
  106. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json +14 -0
  107. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Resources/Assets.xcassets/AppIcon.appiconset/turbo.png +0 -0
  108. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Resources/Assets.xcassets/Contents.json +6 -0
  109. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Resources/Assets.xcassets/SplashIcon.imageset/Contents.json +21 -0
  110. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Resources/Assets.xcassets/SplashIcon.imageset/turbo.png +0 -0
  111. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Resources/Base.lproj/LaunchScreen.storyboard +39 -0
  112. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Resources/Base.lproj/Main.storyboard.tt +32 -0
  113. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/TurboNativeProject.swift.tt +10 -0
  114. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject.xcodeproj/project.pbxproj.tt +436 -0
  115. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  116. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  117. data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +14 -0
  118. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Configuration/Info.plist +29 -0
  119. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Configuration/path-configuration.json +10 -0
  120. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Controllers/ErrorPresenter.swift +74 -0
  121. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Controllers/TurboNavigationController.swift +123 -0
  122. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Controllers/ViewController.swift +20 -0
  123. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Delegates/AppDelegate.swift +8 -0
  124. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Delegates/SceneDelegate.swift.tt +156 -0
  125. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Resources/Assets.xcassets/AccentColor.colorset/Contents.json +11 -0
  126. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json +14 -0
  127. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Resources/Assets.xcassets/AppIcon.appiconset/turbo.png +0 -0
  128. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Resources/Assets.xcassets/Contents.json +6 -0
  129. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Resources/Assets.xcassets/SplashIcon.imageset/Contents.json +21 -0
  130. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Resources/Assets.xcassets/SplashIcon.imageset/turbo.png +0 -0
  131. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Resources/Base.lproj/LaunchScreen.storyboard +39 -0
  132. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Resources/Base.lproj/Main.storyboard +69 -0
  133. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/TurboNativeProject.swift.tt +10 -0
  134. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject.xcodeproj/project.pbxproj.tt +436 -0
  135. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  136. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  137. data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +14 -0
  138. data/lib/turbo_native_initializer/version.rb +3 -0
  139. data/lib/turbo_native_initializer.rb +5 -0
  140. metadata +199 -0
@@ -0,0 +1,123 @@
1
+ import Foundation
2
+ import UIKit
3
+ import Turbo
4
+
5
+ class TurboNavigationController : UINavigationController {
6
+
7
+ var session: Session!
8
+ var modalSession: Session!
9
+
10
+ func push(url: URL) {
11
+ let properties = session.pathConfiguration?.properties(for: url) ?? [:]
12
+ route(url: url, options: VisitOptions(action: .advance), properties: properties)
13
+ }
14
+
15
+ func route(url: URL, options: VisitOptions, properties: PathProperties) {
16
+ // This is a simplified version of how you might build out the routing
17
+ // and navigation functions of your app. In a real app, these would be separate objects
18
+
19
+ // Dismiss any modals when receiving a new navigation
20
+ if presentedViewController != nil {
21
+ dismiss(animated: true)
22
+ }
23
+
24
+ // - Create view controller appropriate for url/properties
25
+ // - Navigate to that with the correct presentation
26
+ let viewController = makeViewController(for: url, properties: properties)
27
+ navigate(to: viewController, action: options.action, properties: properties)
28
+
29
+ // - Initiate the visit with Turbo
30
+ if isVisitable(properties) {
31
+ visit(viewController: viewController, with: options)
32
+ }
33
+ }
34
+ }
35
+
36
+ extension TurboNavigationController {
37
+ private func isModal(_ properties: PathProperties) -> Bool {
38
+ return properties["presentation"] as? String == "modal"
39
+ }
40
+
41
+ private func isClearAll(_ properties: PathProperties) -> Bool {
42
+ return properties["presentation"] as? String == "clear-all"
43
+ }
44
+
45
+ private func isPop(_ properties: PathProperties) -> Bool {
46
+ return properties["presentation"] as? String == "pop"
47
+ }
48
+
49
+ private func isRefresh(_ properties: PathProperties) -> Bool {
50
+ return properties["presentation"] as? String == "refresh"
51
+ }
52
+
53
+ private func isNone(_ properties: PathProperties) -> Bool {
54
+ return properties["presentation"] as? String == "none"
55
+ }
56
+
57
+ private func isReplaceAll(_ properties: PathProperties) -> Bool {
58
+ return properties["presentation"] as? String == "replace-all"
59
+ }
60
+
61
+ private func isReplace(_ properties: PathProperties) -> Bool {
62
+ return properties["presentation"] as? String == "replace"
63
+ }
64
+
65
+ private func isVisitable(_ properties: PathProperties) -> Bool {
66
+ return properties["visitable"] as? Bool ?? true
67
+ }
68
+
69
+ private func makeViewController(for url: URL, properties: PathProperties = [:]) -> UIViewController {
70
+ // There are many options for determining how to map urls to view controllers
71
+ // The demo uses the path configuration for determining which view controller and presentation
72
+ // to use, but that's completely optional. You can use whatever logic you prefer to determine
73
+ // how you navigate and route different URLs.
74
+
75
+ if let viewController = properties["view-controller"] as? String {
76
+ switch viewController {
77
+ case "numbers":
78
+ assertionFailure("NumbersViewController is not implemented")
79
+ default:
80
+ assertionFailure("Invalid view controller, defaulting to WebView")
81
+ }
82
+ }
83
+
84
+ return ViewController(url: url)
85
+ }
86
+
87
+ private func navigate(to viewController: UIViewController, action: VisitAction, properties: PathProperties = [:], animated: Bool = true) {
88
+ let modalNavController = UINavigationController(rootViewController: viewController)
89
+ modalNavController.modalPresentationStyle = .fullScreen
90
+
91
+ if isModal(properties) {
92
+ present(modalNavController, animated: animated)
93
+ } else if isClearAll(properties) {
94
+ popToRootViewController(animated: true)
95
+ } else if isPop(properties) {
96
+ popViewController(animated: true)
97
+ } else if isRefresh(properties) {
98
+ session.reload()
99
+ } else if isNone(properties) {
100
+ // Will result in no navigation action being taken
101
+ } else if isReplaceAll(properties) {
102
+ setViewControllers([viewController], animated: false)
103
+ } else if isReplace(properties) || action == .replace {
104
+ setViewControllers(Array(viewControllers.dropLast()) + [viewController], animated: false)
105
+ } else {
106
+ pushViewController(viewController, animated: animated)
107
+ }
108
+ }
109
+
110
+ private func visit(viewController: UIViewController, with options: VisitOptions, modal: Bool = false) {
111
+ guard let visitable = viewController as? Visitable else { return }
112
+
113
+ // Each Session corresponds to a single web view. A good rule of thumb
114
+ // is to use a session per navigation stack. Here we're using a different session
115
+ // when presenting a modal. We keep that around for any modal presentations so
116
+ // we don't have to create more than we need since each new session incurs a cold boot visit cost
117
+ if modal {
118
+ modalSession.visit(visitable, options: options)
119
+ } else {
120
+ session.visit(visitable, options: options)
121
+ }
122
+ }
123
+ }
@@ -0,0 +1,20 @@
1
+ import UIKit
2
+ import Turbo
3
+
4
+ final class ViewController: VisitableViewController, ErrorPresenter {
5
+ override func viewDidLoad() {
6
+ super.viewDidLoad()
7
+
8
+ view.backgroundColor = .systemBackground
9
+
10
+ navigationItem.backButtonDisplayMode = .minimal
11
+
12
+ if presentingViewController != nil {
13
+ navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .stop, target: self, action: #selector(dismissModal))
14
+ }
15
+ }
16
+
17
+ @objc func dismissModal() {
18
+ dismiss(animated: true)
19
+ }
20
+ }
@@ -0,0 +1,8 @@
1
+ import UIKit
2
+
3
+ @main
4
+ class AppDelegate: UIResponder, UIApplicationDelegate {
5
+ func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
6
+ return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
7
+ }
8
+ }
@@ -0,0 +1,156 @@
1
+ import UIKit
2
+ import WebKit
3
+ import SafariServices
4
+ import Turbo
5
+
6
+ final class SceneDelegate: UIResponder {
7
+ private static var sharedProcessPool = WKProcessPool()
8
+
9
+ var window: UIWindow?
10
+
11
+ private let baseURL = <%= name %>.baseURL
12
+ private let rootURL1 = <%= name %>.homeURL1
13
+ private let rootURL2 = <%= name %>.homeURL2
14
+
15
+ private var tabBarController: UITabBarController!
16
+
17
+ private lazy var navigationController1 = {
18
+ tabBarController.viewControllers![0] as! TurboNavigationController
19
+ }()
20
+
21
+ private lazy var navigationController2 = {
22
+ tabBarController.viewControllers![1] as! TurboNavigationController
23
+ }()
24
+
25
+ // MARK: - Setup
26
+
27
+ private func configureRootViewController() {
28
+ guard let window = window else { fatalError() }
29
+
30
+ window.tintColor = UIColor(named: "Tint")
31
+
32
+ tabBarController = (window.rootViewController as! UITabBarController)
33
+
34
+ navigationController1.session = session1
35
+ navigationController1.modalSession = modalSession
36
+
37
+ navigationController2.session = session2
38
+ navigationController2.modalSession = modalSession
39
+ }
40
+
41
+ private func navigationController() -> TurboNavigationController {
42
+ tabBarController.selectedViewController as! TurboNavigationController
43
+ }
44
+
45
+ private func session() -> Session {
46
+ navigationController().session
47
+ }
48
+
49
+ // MARK: - Sessions
50
+
51
+ private lazy var session1 = makeSession()
52
+ private lazy var session2 = makeSession()
53
+ private lazy var modalSession = makeSession()
54
+
55
+ private func makeSession() -> Session {
56
+ let configuration = WKWebViewConfiguration()
57
+ configuration.applicationNameForUserAgent = "Turbo Native iOS"
58
+ configuration.processPool = Self.sharedProcessPool
59
+
60
+ let webView = WKWebView(frame: .zero, configuration: configuration)
61
+ webView.uiDelegate = self
62
+
63
+ let session = Session(webView: webView)
64
+ session.delegate = self
65
+ session.pathConfiguration = pathConfiguration
66
+ return session
67
+ }
68
+
69
+ // MARK: - Path Configuration
70
+
71
+ private lazy var pathConfiguration = PathConfiguration(sources: [
72
+ .file(Bundle.main.url(forResource: "path-configuration", withExtension: "json")!),
73
+ ])
74
+ }
75
+
76
+ extension SceneDelegate: UIWindowSceneDelegate {
77
+ func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
78
+ guard let _ = scene as? UIWindowScene else { return }
79
+
80
+ configureRootViewController()
81
+
82
+ navigationController1.route(url: rootURL1, options: VisitOptions(action: .replace), properties: [:])
83
+ navigationController2.route(url: rootURL2, options: VisitOptions(action: .replace), properties: [:])
84
+ }
85
+ }
86
+
87
+ extension SceneDelegate: SessionDelegate {
88
+ func session(_ session: Session, didProposeVisit proposal: VisitProposal) {
89
+ navigationController().route(url: proposal.url, options: proposal.options, properties: proposal.properties)
90
+ }
91
+
92
+ func session(_ session: Session, didFailRequestForVisitable visitable: Visitable, error: Error) {
93
+ if let errorPresenter = visitable as? ErrorPresenter {
94
+ errorPresenter.presentError(error) { [weak self] in
95
+ self?.session().reload()
96
+ }
97
+ } else {
98
+ assertionFailure("Visit failed!")
99
+ }
100
+ }
101
+
102
+ // When a form submission completes in the modal session, we need to
103
+ // manually clear the snapshot cache in the default session, since we
104
+ // don't want potentially stale cached snapshots to be used
105
+ func sessionDidFinishFormSubmission(_ session: Session) {
106
+ if (session == modalSession) {
107
+ self.session().clearSnapshotCache()
108
+ }
109
+ }
110
+
111
+ func sessionDidLoadWebView(_ session: Session) {
112
+ session.webView.navigationDelegate = self
113
+ }
114
+
115
+ func sessionWebViewProcessDidTerminate(_ session: Session) {
116
+ session.reload()
117
+ }
118
+ }
119
+
120
+ extension SceneDelegate: WKNavigationDelegate {
121
+ func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
122
+ if navigationAction.navigationType == .linkActivated {
123
+ // Any link that's not on the same domain as the Turbo root url will go through here
124
+ // Other links on the domain, but that have an extension that is non-html will also go here
125
+ // You can decide how to handle those, by default if you're not the navigationDelegate
126
+ // the Session will open them in the default browser
127
+
128
+ let url = navigationAction.request.url!
129
+
130
+ // For this demo, we'll load files from our domain in a SafariViewController so you
131
+ // don't need to leave the app. You might expand this in your app
132
+ // to open all audio/video/images in a native media viewer
133
+ if url.host == baseURL.host, !url.pathExtension.isEmpty {
134
+ let safariViewController = SFSafariViewController(url: url)
135
+ navigationController().present(safariViewController, animated: true)
136
+ } else {
137
+ UIApplication.shared.open(url)
138
+ }
139
+
140
+ decisionHandler(.cancel)
141
+ } else {
142
+ decisionHandler(.allow)
143
+ }
144
+ }
145
+ }
146
+
147
+ extension SceneDelegate: WKUIDelegate {
148
+ func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
149
+ let confirm = UIAlertController(title: nil, message: message, preferredStyle: .alert)
150
+ confirm.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in completionHandler(false) })
151
+ confirm.addAction(UIAlertAction(title: "OK", style: .default) { _ in completionHandler(true) })
152
+
153
+ // JavaScript alerts in Turbo Native
154
+ navigationController().present(confirm, animated: true)
155
+ }
156
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "colors" : [
3
+ {
4
+ "idiom" : "universal"
5
+ }
6
+ ],
7
+ "info" : {
8
+ "author" : "xcode",
9
+ "version" : 1
10
+ }
11
+ }
@@ -0,0 +1,14 @@
1
+ {
2
+ "images" : [
3
+ {
4
+ "filename" : "turbo.png",
5
+ "idiom" : "universal",
6
+ "platform" : "ios",
7
+ "size" : "1024x1024"
8
+ }
9
+ ],
10
+ "info" : {
11
+ "author" : "xcode",
12
+ "version" : 1
13
+ }
14
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "info" : {
3
+ "author" : "xcode",
4
+ "version" : 1
5
+ }
6
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "images" : [
3
+ {
4
+ "filename" : "turbo.png",
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
+ }
@@ -0,0 +1,39 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
3
+ <device id="retina5_9" orientation="portrait" appearance="light"/>
4
+ <dependencies>
5
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21679"/>
6
+ <capability name="Safe area layout guides" minToolsVersion="9.0"/>
7
+ <capability name="System colors in document resources" minToolsVersion="11.0"/>
8
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
9
+ </dependencies>
10
+ <scenes>
11
+ <!--View Controller-->
12
+ <scene sceneID="EHf-IW-A2E">
13
+ <objects>
14
+ <viewController id="01J-lp-oVM" sceneMemberID="viewController">
15
+ <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
16
+ <rect key="frame" x="0.0" y="0.0" width="375" height="812"/>
17
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
18
+ <subviews>
19
+ <imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" fixedFrame="YES" image="SplashIcon" translatesAutoresizingMaskIntoConstraints="NO" id="eXr-0W-qZz">
20
+ <rect key="frame" x="137" y="356" width="100" height="100"/>
21
+ <autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
22
+ </imageView>
23
+ </subviews>
24
+ <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
25
+ <color key="backgroundColor" systemColor="systemBackgroundColor"/>
26
+ </view>
27
+ </viewController>
28
+ <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
29
+ </objects>
30
+ <point key="canvasLocation" x="50.399999999999999" y="373.89162561576359"/>
31
+ </scene>
32
+ </scenes>
33
+ <resources>
34
+ <image name="SplashIcon" width="1024" height="1024"/>
35
+ <systemColor name="systemBackgroundColor">
36
+ <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
37
+ </systemColor>
38
+ </resources>
39
+ </document>
@@ -0,0 +1,69 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="33v-ad-2dz">
3
+ <device id="retina6_12" orientation="portrait" appearance="light"/>
4
+ <dependencies>
5
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21679"/>
6
+ <capability name="Safe area layout guides" minToolsVersion="9.0"/>
7
+ <capability name="System colors in document resources" minToolsVersion="11.0"/>
8
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
9
+ </dependencies>
10
+ <scenes>
11
+ <!--Item 2-->
12
+ <scene sceneID="GwE-Ch-DRP">
13
+ <objects>
14
+ <viewController id="cXb-wA-NUs" customClass="TurboNavigationController" customModule="ContactsIOS" customModuleProvider="target" sceneMemberID="viewController">
15
+ <view key="view" contentMode="scaleToFill" id="WTF-II-zfk">
16
+ <rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
17
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
18
+ <viewLayoutGuide key="safeArea" id="CoL-dA-Hxa"/>
19
+ <color key="backgroundColor" systemColor="systemBackgroundColor"/>
20
+ </view>
21
+ <tabBarItem key="tabBarItem" title="Item 2" image="gear" catalog="system" id="rhZ-zi-yeq"/>
22
+ </viewController>
23
+ <placeholder placeholderIdentifier="IBFirstResponder" id="ife-PY-okT" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
24
+ </objects>
25
+ <point key="canvasLocation" x="1271" y="-647"/>
26
+ </scene>
27
+ <!--Item 1-->
28
+ <scene sceneID="pAD-IL-Hdw">
29
+ <objects>
30
+ <viewController id="0en-nE-vJA" customClass="TurboNavigationController" customModule="ContactsIOS" customModuleProvider="target" sceneMemberID="viewController">
31
+ <view key="view" contentMode="scaleToFill" id="bFn-PZ-ClM">
32
+ <rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
33
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
34
+ <viewLayoutGuide key="safeArea" id="K5f-nY-Cgh"/>
35
+ <color key="backgroundColor" systemColor="systemBackgroundColor"/>
36
+ </view>
37
+ <tabBarItem key="tabBarItem" title="Item 1" image="house" catalog="system" id="ZWo-HO-m4a"/>
38
+ </viewController>
39
+ <placeholder placeholderIdentifier="IBFirstResponder" id="GRr-XE-fAM" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
40
+ </objects>
41
+ <point key="canvasLocation" x="1270" y="-1254"/>
42
+ </scene>
43
+ <!--Tab Bar Controller-->
44
+ <scene sceneID="So3-hc-mhH">
45
+ <objects>
46
+ <tabBarController id="33v-ad-2dz" sceneMemberID="viewController">
47
+ <tabBar key="tabBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="fR1-GM-qEi">
48
+ <rect key="frame" x="0.0" y="0.0" width="393" height="49"/>
49
+ <autoresizingMask key="autoresizingMask"/>
50
+ <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
51
+ </tabBar>
52
+ <connections>
53
+ <segue destination="0en-nE-vJA" kind="relationship" relationship="viewControllers" id="z0u-hS-Voh"/>
54
+ <segue destination="cXb-wA-NUs" kind="relationship" relationship="viewControllers" id="eJZ-00-JTF"/>
55
+ </connections>
56
+ </tabBarController>
57
+ <placeholder placeholderIdentifier="IBFirstResponder" id="KRh-ez-iFz" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
58
+ </objects>
59
+ <point key="canvasLocation" x="364" y="-951"/>
60
+ </scene>
61
+ </scenes>
62
+ <resources>
63
+ <image name="gear" catalog="system" width="128" height="122"/>
64
+ <image name="house" catalog="system" width="128" height="93"/>
65
+ <systemColor name="systemBackgroundColor">
66
+ <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
67
+ </systemColor>
68
+ </resources>
69
+ </document>
@@ -0,0 +1,10 @@
1
+ import Foundation
2
+
3
+ struct <%= name %> {
4
+ private static let developmentURL = URL(string: "http://localhost:3000")!
5
+ private static let productionURL = URL(string: "https://turbo-native-demo.glitch.me")!
6
+
7
+ static var baseURL: URL { productionURL }
8
+ static var homeURL1: URL { baseURL.appendingPathComponent("/") }
9
+ static var homeURL2: URL { baseURL.appendingPathComponent("/") }
10
+ }