turbo-native-initializer 0.0.10 → 0.0.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +3 -1
- data/lib/turbo_native_initializer/generator.rb +1 -0
- data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/base/NavDestination.kt.tt +17 -2
- data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/features/web/WebFragment.kt.tt +38 -0
- data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/features/web/WebModalFragment.kt.tt +2 -2
- data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/main/MainActivity.kt.tt +7 -0
- data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/main/MainSessionNavHostFragment.kt.tt +5 -5
- data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/BridgeComponentFactories.kt.tt +9 -0
- data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/FlashMessageComponent.kt.tt +41 -0
- data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/FormComponent.kt.tt +87 -0
- data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/NavButtonComponent.kt.tt +70 -0
- data/lib/turbo_native_initializer/templates/android_stack/app/src/main/java/dev/hotwire/turbo/turbonativeproject/util/Extension.kt.tt +19 -0
- data/lib/turbo_native_initializer/templates/android_stack/base/app/build.gradle.kts.tt +9 -3
- data/lib/turbo_native_initializer/templates/android_stack/base/app/src/main/assets/json/configuration.json +1 -1
- data/lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/layout/form_component_submit.xml +12 -0
- data/lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/layout/nav_button_component.xml +14 -0
- data/lib/turbo_native_initializer/templates/android_stack/base/app/src/main/res/values/colors.xml +1 -1
- data/lib/turbo_native_initializer/templates/android_stack/base/build.gradle.kts +2 -1
- data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/base/NavDestination.kt.tt +17 -2
- data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/features/web/WebFragment.kt.tt +38 -0
- data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/features/web/WebModalFragment.kt.tt +2 -2
- data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/main/BaseSessionNavHostFragment.kt.tt +5 -5
- data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/main/MainActivity.kt.tt +4 -0
- data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/BridgeComponentFactories.kt.tt +9 -0
- data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/FlashMessageComponent.kt.tt +41 -0
- data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/FormComponent.kt.tt +87 -0
- data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/strada/NavButtonComponent.kt.tt +70 -0
- data/lib/turbo_native_initializer/templates/android_tabs/app/src/main/java/dev/hotwire/turbo/turbonativeproject/util/Extension.kt.tt +19 -0
- data/lib/turbo_native_initializer/templates/android_tabs/base/app/build.gradle.kts.tt +9 -3
- data/lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/assets/json/configuration.json +1 -1
- data/lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/layout/form_component_submit.xml +12 -0
- data/lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/layout/nav_button_component.xml +14 -0
- data/lib/turbo_native_initializer/templates/android_tabs/base/app/src/main/res/values/colors.xml +1 -1
- data/lib/turbo_native_initializer/templates/android_tabs/base/build.gradle.kts +2 -1
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Configuration/path-configuration.json +1 -1
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Controllers/TurboNavigationController.swift +12 -8
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Controllers/TurboWebViewController.swift +65 -0
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Controllers/UIViewController+Toast.swift +79 -0
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Controllers/WKWebViewConfiguration+App.swift +21 -0
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Delegates/SceneDelegate.swift.tt +4 -5
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Resources/Assets.xcassets/AccentColor.colorset/Contents.json +8 -8
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Resources/Base.lproj/{Main.storyboard.tt → Main.storyboard} +1 -1
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Strada/BridgeComponent+App.swift +8 -0
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Strada/FlashMessageComponent.swift +47 -0
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Strada/FormComponent.swift +78 -0
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Strada/NavButtonComponent.swift +60 -0
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject.xcodeproj/project.pbxproj.tt +55 -6
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +9 -0
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Configuration/path-configuration.json +1 -1
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Controllers/TurboNavigationController.swift +11 -8
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Controllers/TurboWebViewController.swift +65 -0
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Controllers/UIViewController+Toast.swift +79 -0
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Controllers/WKWebViewConfiguration+App.swift +21 -0
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Delegates/SceneDelegate.swift.tt +4 -5
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Resources/Assets.xcassets/AccentColor.colorset/Contents.json +8 -8
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Resources/Base.lproj/Main.storyboard +2 -2
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Strada/BridgeComponent+App.swift +8 -0
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Strada/FlashMessageComponent.swift +47 -0
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Strada/FormComponent.swift +78 -0
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Strada/NavButtonComponent.swift +60 -0
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject.xcodeproj/project.pbxproj.tt +55 -6
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +9 -0
- data/lib/turbo_native_initializer/version.rb +1 -1
- metadata +32 -6
- data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Controllers/ViewController.swift +0 -22
- data/lib/turbo_native_initializer/templates/ios_tabs/TurboNativeProject/Controllers/ViewController.swift +0 -22
@@ -0,0 +1,47 @@
|
|
1
|
+
import Foundation
|
2
|
+
import Strada
|
3
|
+
import UIKit
|
4
|
+
|
5
|
+
final class FlashMessageComponent: BridgeComponent {
|
6
|
+
override class var name: String { "flash-message" }
|
7
|
+
|
8
|
+
override func onReceive(message: Message) {
|
9
|
+
guard let event = Event(rawValue: message.event) else {
|
10
|
+
return
|
11
|
+
}
|
12
|
+
|
13
|
+
if event == .connect {
|
14
|
+
handleConnectEvent(message: message)
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
@objc func performAction() {
|
19
|
+
reply(to: Event.connect.rawValue)
|
20
|
+
}
|
21
|
+
|
22
|
+
// MARK: Private
|
23
|
+
|
24
|
+
private var viewController: UIViewController? {
|
25
|
+
delegate.destination as? UIViewController
|
26
|
+
}
|
27
|
+
|
28
|
+
private func handleConnectEvent(message: Message) {
|
29
|
+
guard let data: MessageData = message.data() else { return }
|
30
|
+
guard let viewController else { return }
|
31
|
+
viewController.parent?.presentToast(data.title)
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
// MARK: Events
|
36
|
+
|
37
|
+
private extension FlashMessageComponent {
|
38
|
+
enum Event: String { case connect }
|
39
|
+
}
|
40
|
+
|
41
|
+
// MARK: Message data
|
42
|
+
|
43
|
+
private extension FlashMessageComponent {
|
44
|
+
struct MessageData: Decodable {
|
45
|
+
let title: String
|
46
|
+
}
|
47
|
+
}
|
data/lib/turbo_native_initializer/templates/ios_stack/TurboNativeProject/Strada/FormComponent.swift
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
import Foundation
|
2
|
+
import Strada
|
3
|
+
import UIKit
|
4
|
+
|
5
|
+
final class FormComponent: BridgeComponent {
|
6
|
+
override class var name: String { "form" }
|
7
|
+
|
8
|
+
override func onReceive(message: Message) {
|
9
|
+
guard let event = Event(rawValue: message.event) else {
|
10
|
+
return
|
11
|
+
}
|
12
|
+
|
13
|
+
switch event {
|
14
|
+
case .connect:
|
15
|
+
handleConnectEvent(message: message)
|
16
|
+
case .submitEnabled:
|
17
|
+
handleSubmitEnabled()
|
18
|
+
case .submitDisabled:
|
19
|
+
handleSubmitDisabled()
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
@objc func performAction() {
|
24
|
+
reply(to: Event.connect.rawValue)
|
25
|
+
}
|
26
|
+
|
27
|
+
// MARK: Private
|
28
|
+
|
29
|
+
private weak var submitBarButtonItem: UIBarButtonItem?
|
30
|
+
|
31
|
+
private var viewController: UIViewController? {
|
32
|
+
delegate.destination as? UIViewController
|
33
|
+
}
|
34
|
+
|
35
|
+
private func handleConnectEvent(message: Message) {
|
36
|
+
guard let data: MessageData = message.data() else { return }
|
37
|
+
configureBarButton(with: data.title)
|
38
|
+
}
|
39
|
+
|
40
|
+
private func handleSubmitEnabled() {
|
41
|
+
submitBarButtonItem?.isEnabled = true
|
42
|
+
}
|
43
|
+
|
44
|
+
private func handleSubmitDisabled() {
|
45
|
+
submitBarButtonItem?.isEnabled = false
|
46
|
+
}
|
47
|
+
|
48
|
+
private func configureBarButton(with title: String) {
|
49
|
+
guard let viewController else { return }
|
50
|
+
|
51
|
+
let button = UIButton(configuration: .filled())
|
52
|
+
button.setTitle(title, for: .normal)
|
53
|
+
button.addTarget(self, action: #selector(performAction), for: .touchUpInside)
|
54
|
+
|
55
|
+
let item = UIBarButtonItem(customView: button)
|
56
|
+
|
57
|
+
viewController.navigationItem.rightBarButtonItem = item
|
58
|
+
submitBarButtonItem = item
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
// MARK: Events
|
63
|
+
|
64
|
+
private extension FormComponent {
|
65
|
+
enum Event: String {
|
66
|
+
case connect
|
67
|
+
case submitEnabled
|
68
|
+
case submitDisabled
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
// MARK: Message data
|
73
|
+
|
74
|
+
private extension FormComponent {
|
75
|
+
struct MessageData: Decodable {
|
76
|
+
let title: String
|
77
|
+
}
|
78
|
+
}
|
@@ -0,0 +1,60 @@
|
|
1
|
+
import Foundation
|
2
|
+
import Strada
|
3
|
+
import UIKit
|
4
|
+
|
5
|
+
final class NavButtonComponent: BridgeComponent {
|
6
|
+
override class var name: String { "nav-button" }
|
7
|
+
|
8
|
+
override func onReceive(message: Message) {
|
9
|
+
guard let event = Event(rawValue: message.event) else {
|
10
|
+
return
|
11
|
+
}
|
12
|
+
|
13
|
+
if event == .connect {
|
14
|
+
handleConnectEvent(message: message)
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
@objc func performAction() {
|
19
|
+
reply(to: Event.connect.rawValue)
|
20
|
+
}
|
21
|
+
|
22
|
+
// MARK: Private
|
23
|
+
|
24
|
+
private weak var navBarButtonItem: UIBarButtonItem?
|
25
|
+
|
26
|
+
private var viewController: UIViewController? {
|
27
|
+
delegate.destination as? UIViewController
|
28
|
+
}
|
29
|
+
|
30
|
+
private func handleConnectEvent(message: Message) {
|
31
|
+
guard let data: MessageData = message.data() else { return }
|
32
|
+
configureBarButton(with: data.title)
|
33
|
+
}
|
34
|
+
|
35
|
+
private func configureBarButton(with title: String) {
|
36
|
+
guard let viewController else { return }
|
37
|
+
|
38
|
+
let item = UIBarButtonItem(title: title,
|
39
|
+
style: .plain,
|
40
|
+
target: self,
|
41
|
+
action: #selector(performAction))
|
42
|
+
|
43
|
+
viewController.navigationItem.rightBarButtonItem = item
|
44
|
+
navBarButtonItem = item
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
// MARK: Events
|
49
|
+
|
50
|
+
private extension NavButtonComponent {
|
51
|
+
enum Event: String { case connect }
|
52
|
+
}
|
53
|
+
|
54
|
+
// MARK: Message data
|
55
|
+
|
56
|
+
private extension NavButtonComponent {
|
57
|
+
struct MessageData: Decodable {
|
58
|
+
let title: String
|
59
|
+
}
|
60
|
+
}
|
@@ -8,9 +8,16 @@
|
|
8
8
|
|
9
9
|
/* Begin PBXBuildFile section */
|
10
10
|
5D0D861F2A9AA70500A525E9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5D0D86212A9AA70500A525E9 /* Main.storyboard */; };
|
11
|
+
5D1F2EC12ABEB12300B2819A /* NavButtonComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D1F2EC02ABEB12300B2819A /* NavButtonComponent.swift */; };
|
12
|
+
5D281BAF2ABC0BBC001CE599 /* Strada in Frameworks */ = {isa = PBXBuildFile; productRef = 5D281BAE2ABC0BBC001CE599 /* Strada */; };
|
13
|
+
5D281BB12ABC0C3D001CE599 /* WKWebViewConfiguration+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D281BB02ABC0C3D001CE599 /* WKWebViewConfiguration+App.swift */; };
|
14
|
+
5D281BB42ABC0CB0001CE599 /* BridgeComponent+App.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D281BB32ABC0CB0001CE599 /* BridgeComponent+App.swift */; };
|
15
|
+
5D281BB62ABC0CB9001CE599 /* FormComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D281BB52ABC0CB9001CE599 /* FormComponent.swift */; };
|
16
|
+
5D91C5402AC538960046D872 /* FlashMessageComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D91C53F2AC538960046D872 /* FlashMessageComponent.swift */; };
|
17
|
+
5DAF71B72AC3FB53002D04FE /* UIViewController+Toast.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAF71B62AC3FB53002D04FE /* UIViewController+Toast.swift */; };
|
11
18
|
5DCC50D42A959DF900B529A0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DCC50D32A959DF900B529A0 /* AppDelegate.swift */; };
|
12
19
|
5DCC50D62A959DF900B529A0 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DCC50D52A959DF900B529A0 /* SceneDelegate.swift */; };
|
13
|
-
5DCC50D82A959DF900B529A0 /*
|
20
|
+
5DCC50D82A959DF900B529A0 /* TurboWebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DCC50D72A959DF900B529A0 /* TurboWebViewController.swift */; };
|
14
21
|
5DCC50DD2A959DFA00B529A0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5DCC50DC2A959DFA00B529A0 /* Assets.xcassets */; };
|
15
22
|
5DCC50E02A959DFA00B529A0 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5DCC50DE2A959DFA00B529A0 /* LaunchScreen.storyboard */; };
|
16
23
|
5DCC50E82A95A0D900B529A0 /* TurboNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DCC50E72A95A0D900B529A0 /* TurboNavigationController.swift */; };
|
@@ -23,10 +30,16 @@
|
|
23
30
|
|
24
31
|
/* Begin PBXFileReference section */
|
25
32
|
5D0D86202A9AA70500A525E9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
33
|
+
5D1F2EC02ABEB12300B2819A /* NavButtonComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavButtonComponent.swift; sourceTree = "<group>"; };
|
34
|
+
5D281BB02ABC0C3D001CE599 /* WKWebViewConfiguration+App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WKWebViewConfiguration+App.swift"; sourceTree = "<group>"; };
|
35
|
+
5D281BB32ABC0CB0001CE599 /* BridgeComponent+App.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BridgeComponent+App.swift"; sourceTree = "<group>"; };
|
36
|
+
5D281BB52ABC0CB9001CE599 /* FormComponent.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormComponent.swift; sourceTree = "<group>"; };
|
37
|
+
5D91C53F2AC538960046D872 /* FlashMessageComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlashMessageComponent.swift; sourceTree = "<group>"; };
|
38
|
+
5DAF71B62AC3FB53002D04FE /* UIViewController+Toast.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Toast.swift"; sourceTree = "<group>"; };
|
26
39
|
5DCC50D02A959DF900B529A0 /* <%= name %>.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = <%= name %>.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
27
40
|
5DCC50D32A959DF900B529A0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
28
41
|
5DCC50D52A959DF900B529A0 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
|
29
|
-
5DCC50D72A959DF900B529A0 /*
|
42
|
+
5DCC50D72A959DF900B529A0 /* TurboWebViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TurboWebViewController.swift; sourceTree = "<group>"; };
|
30
43
|
5DCC50DC2A959DFA00B529A0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
31
44
|
5DCC50DF2A959DFA00B529A0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
32
45
|
5DCC50E12A959DFA00B529A0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
@@ -43,12 +56,24 @@
|
|
43
56
|
buildActionMask = 2147483647;
|
44
57
|
files = (
|
45
58
|
5DCC50F12A95A66700B529A0 /* Turbo in Frameworks */,
|
59
|
+
5D281BAF2ABC0BBC001CE599 /* Strada in Frameworks */,
|
46
60
|
);
|
47
61
|
runOnlyForDeploymentPostprocessing = 0;
|
48
62
|
};
|
49
63
|
/* End PBXFrameworksBuildPhase section */
|
50
64
|
|
51
65
|
/* Begin PBXGroup section */
|
66
|
+
5D281BB22ABC0C8F001CE599 /* Strada */ = {
|
67
|
+
isa = PBXGroup;
|
68
|
+
children = (
|
69
|
+
5D281BB32ABC0CB0001CE599 /* BridgeComponent+App.swift */,
|
70
|
+
5D281BB52ABC0CB9001CE599 /* FormComponent.swift */,
|
71
|
+
5D1F2EC02ABEB12300B2819A /* NavButtonComponent.swift */,
|
72
|
+
5D91C53F2AC538960046D872 /* FlashMessageComponent.swift */,
|
73
|
+
);
|
74
|
+
path = Strada;
|
75
|
+
sourceTree = "<group>";
|
76
|
+
};
|
52
77
|
5D2FA5CD2A9DB1AB001AF2F1 /* Configuration */ = {
|
53
78
|
isa = PBXGroup;
|
54
79
|
children = (
|
@@ -77,6 +102,7 @@
|
|
77
102
|
5DCC50D22A959DF900B529A0 /* <%= name %> */ = {
|
78
103
|
isa = PBXGroup;
|
79
104
|
children = (
|
105
|
+
5D281BB22ABC0C8F001CE599 /* Strada */,
|
80
106
|
5D2FA5CD2A9DB1AB001AF2F1 /* Configuration */,
|
81
107
|
5DCC50F42A95AC4E00B529A0 /* Controllers */,
|
82
108
|
5DCC50FB2A95B55200B529A0 /* Delegates */,
|
@@ -90,8 +116,10 @@
|
|
90
116
|
isa = PBXGroup;
|
91
117
|
children = (
|
92
118
|
5DCC50ED2A95A27600B529A0 /* ErrorPresenter.swift */,
|
119
|
+
5DAF71B62AC3FB53002D04FE /* UIViewController+Toast.swift */,
|
120
|
+
5D281BB02ABC0C3D001CE599 /* WKWebViewConfiguration+App.swift */,
|
93
121
|
5DCC50E72A95A0D900B529A0 /* TurboNavigationController.swift */,
|
94
|
-
5DCC50D72A959DF900B529A0 /*
|
122
|
+
5DCC50D72A959DF900B529A0 /* TurboWebViewController.swift */,
|
95
123
|
5DDD58802AA9A8BE00FAC961 /* NumbersViewController.swift */,
|
96
124
|
);
|
97
125
|
path = Controllers;
|
@@ -134,6 +162,7 @@
|
|
134
162
|
name = <%= name %>;
|
135
163
|
packageProductDependencies = (
|
136
164
|
5DCC50F02A95A66700B529A0 /* Turbo */,
|
165
|
+
5D281BAE2ABC0BBC001CE599 /* Strada */,
|
137
166
|
);
|
138
167
|
productName = <%= name %>;
|
139
168
|
productReference = 5DCC50D02A959DF900B529A0 /* <%= name %>.app */;
|
@@ -165,6 +194,7 @@
|
|
165
194
|
mainGroup = 5DCC50C72A959DF800B529A0;
|
166
195
|
packageReferences = (
|
167
196
|
5DCC50EF2A95A66700B529A0 /* XCRemoteSwiftPackageReference "turbo-ios" */,
|
197
|
+
5D281BAD2ABC0BBC001CE599 /* XCRemoteSwiftPackageReference "strada-ios" */,
|
168
198
|
);
|
169
199
|
productRefGroup = 5DCC50D12A959DF900B529A0 /* Products */;
|
170
200
|
projectDirPath = "";
|
@@ -194,11 +224,17 @@
|
|
194
224
|
isa = PBXSourcesBuildPhase;
|
195
225
|
buildActionMask = 2147483647;
|
196
226
|
files = (
|
197
|
-
5DCC50D82A959DF900B529A0 /*
|
227
|
+
5DCC50D82A959DF900B529A0 /* TurboWebViewController.swift in Sources */,
|
198
228
|
5DDD58812AA9A8BE00FAC961 /* NumbersViewController.swift in Sources */,
|
229
|
+
5D91C5402AC538960046D872 /* FlashMessageComponent.swift in Sources */,
|
230
|
+
5D1F2EC12ABEB12300B2819A /* NavButtonComponent.swift in Sources */,
|
199
231
|
5DCC50D42A959DF900B529A0 /* AppDelegate.swift in Sources */,
|
232
|
+
5D281BB62ABC0CB9001CE599 /* FormComponent.swift in Sources */,
|
200
233
|
5DCC50E82A95A0D900B529A0 /* TurboNavigationController.swift in Sources */,
|
234
|
+
5DAF71B72AC3FB53002D04FE /* UIViewController+Toast.swift in Sources */,
|
235
|
+
5D281BB12ABC0C3D001CE599 /* WKWebViewConfiguration+App.swift in Sources */,
|
201
236
|
5DCC50D62A959DF900B529A0 /* SceneDelegate.swift in Sources */,
|
237
|
+
5D281BB42ABC0CB0001CE599 /* BridgeComponent+App.swift in Sources */,
|
202
238
|
5DCC50F32A95A7E600B529A0 /* <%= name %>.swift in Sources */,
|
203
239
|
5DCC50EE2A95A27600B529A0 /* ErrorPresenter.swift in Sources */,
|
204
240
|
);
|
@@ -418,17 +454,30 @@
|
|
418
454
|
/* End XCConfigurationList section */
|
419
455
|
|
420
456
|
/* Begin XCRemoteSwiftPackageReference section */
|
457
|
+
5D281BAD2ABC0BBC001CE599 /* XCRemoteSwiftPackageReference "strada-ios" */ = {
|
458
|
+
isa = XCRemoteSwiftPackageReference;
|
459
|
+
repositoryURL = "https://github.com/hotwired/strada-ios";
|
460
|
+
requirement = {
|
461
|
+
kind = exactVersion;
|
462
|
+
version = "1.0.0-beta1";
|
463
|
+
};
|
464
|
+
};
|
421
465
|
5DCC50EF2A95A66700B529A0 /* XCRemoteSwiftPackageReference "turbo-ios" */ = {
|
422
466
|
isa = XCRemoteSwiftPackageReference;
|
423
467
|
repositoryURL = "https://github.com/hotwired/turbo-ios";
|
424
468
|
requirement = {
|
425
|
-
kind =
|
426
|
-
|
469
|
+
kind = exactVersion;
|
470
|
+
version = 7.0.0;
|
427
471
|
};
|
428
472
|
};
|
429
473
|
/* End XCRemoteSwiftPackageReference section */
|
430
474
|
|
431
475
|
/* Begin XCSwiftPackageProductDependency section */
|
476
|
+
5D281BAE2ABC0BBC001CE599 /* Strada */ = {
|
477
|
+
isa = XCSwiftPackageProductDependency;
|
478
|
+
package = 5D281BAD2ABC0BBC001CE599 /* XCRemoteSwiftPackageReference "strada-ios" */;
|
479
|
+
productName = Strada;
|
480
|
+
};
|
432
481
|
5DCC50F02A95A66700B529A0 /* Turbo */ = {
|
433
482
|
isa = XCSwiftPackageProductDependency;
|
434
483
|
package = 5DCC50EF2A95A66700B529A0 /* XCRemoteSwiftPackageReference "turbo-ios" */;
|
@@ -1,5 +1,14 @@
|
|
1
1
|
{
|
2
2
|
"pins" : [
|
3
|
+
{
|
4
|
+
"identity" : "strada-ios",
|
5
|
+
"kind" : "remoteSourceControl",
|
6
|
+
"location" : "https://github.com/hotwired/strada-ios",
|
7
|
+
"state" : {
|
8
|
+
"revision" : "d8c1bcf1d9511c03e7beed07c05d29f33f9b6cde",
|
9
|
+
"version" : "1.0.0-beta1"
|
10
|
+
}
|
11
|
+
},
|
3
12
|
{
|
4
13
|
"identity" : "turbo-ios",
|
5
14
|
"kind" : "remoteSourceControl",
|
@@ -5,7 +5,7 @@
|
|
5
5
|
{ "patterns": ["/recede_historical_location"], "properties": { "presentation": "pop", "visitable": false } },
|
6
6
|
{ "patterns": ["/resume_historical_location"], "properties": { "presentation": "none", "visitable": false } },
|
7
7
|
{ "patterns": ["^/$"], "properties": { "presentation": "replace-all" } },
|
8
|
-
{ "patterns": ["/new$", "/edit$"], "properties": { "presentation": "modal" } },
|
8
|
+
{ "patterns": ["/new$", "/edit$", "/signin$", "/strada-form$"], "properties": { "presentation": "modal" } },
|
9
9
|
{ "patterns": ["/numbers$"], "properties": { "view-controller": "numbers" } }
|
10
10
|
]
|
11
11
|
}
|
@@ -24,18 +24,17 @@ class TurboNavigationController : UINavigationController {
|
|
24
24
|
// - Create view controller appropriate for url/properties
|
25
25
|
// - Navigate to that with the correct presentation
|
26
26
|
let viewController = makeViewController(for: url, properties: properties)
|
27
|
-
|
28
|
-
// - Navigate to that with the correct presentation
|
29
|
-
if session.activeVisitable?.visitableURL != url {
|
30
|
-
navigate(to: viewController, action: options.action, properties: properties)
|
31
|
-
} else {
|
32
|
-
navigate(to: viewController, action: .replace, properties: properties)
|
33
|
-
}
|
27
|
+
navigate(to: viewController, action: options.action, properties: properties)
|
34
28
|
|
35
29
|
// Initiate the visit with Turbo
|
36
30
|
if isVisitable(properties) {
|
37
31
|
visit(viewController: viewController, with: options, modal: isModal(properties))
|
38
32
|
}
|
33
|
+
|
34
|
+
// Display notice messages natively
|
35
|
+
if let message = noticeMessage(from: url) {
|
36
|
+
presentToast(message.replacingOccurrences(of: "+", with: " "))
|
37
|
+
}
|
39
38
|
}
|
40
39
|
}
|
41
40
|
|
@@ -72,6 +71,10 @@ extension TurboNavigationController {
|
|
72
71
|
return properties["visitable"] as? Bool ?? true
|
73
72
|
}
|
74
73
|
|
74
|
+
private func noticeMessage(from url: URL) -> String? {
|
75
|
+
URLComponents(url: url, resolvingAgainstBaseURL: false)?.queryItems?.first(where: { $0.name == "notice" })?.value
|
76
|
+
}
|
77
|
+
|
75
78
|
private func makeViewController(for url: URL, properties: PathProperties = [:]) -> UIViewController {
|
76
79
|
// There are many options for determining how to map urls to view controllers
|
77
80
|
// The demo uses the path configuration for determining which view controller and presentation
|
@@ -87,7 +90,7 @@ extension TurboNavigationController {
|
|
87
90
|
}
|
88
91
|
}
|
89
92
|
|
90
|
-
return
|
93
|
+
return TurboWebViewController(url: url)
|
91
94
|
}
|
92
95
|
|
93
96
|
private func navigate(to viewController: UIViewController, action: VisitAction, properties: PathProperties = [:]) {
|
@@ -0,0 +1,65 @@
|
|
1
|
+
import UIKit
|
2
|
+
import Turbo
|
3
|
+
import Strada
|
4
|
+
import WebKit
|
5
|
+
|
6
|
+
final class TurboWebViewController: VisitableViewController, ErrorPresenter, BridgeDestination {
|
7
|
+
|
8
|
+
private lazy var bridgeDelegate: BridgeDelegate = {
|
9
|
+
BridgeDelegate(location: visitableURL.absoluteString, destination: self, componentTypes: BridgeComponent.allTypes)
|
10
|
+
}()
|
11
|
+
|
12
|
+
private lazy var dismissModalButton = {
|
13
|
+
UIBarButtonItem(image: UIImage(systemName: "chevron.down"), style: .plain, target: self, action: #selector(dismissModal))
|
14
|
+
}()
|
15
|
+
|
16
|
+
// MARK: View lifecycle
|
17
|
+
|
18
|
+
override func viewDidLoad() {
|
19
|
+
super.viewDidLoad()
|
20
|
+
|
21
|
+
navigationItem.backButtonTitle = "Back"
|
22
|
+
|
23
|
+
if presentingViewController != nil {
|
24
|
+
navigationItem.leftBarButtonItem = dismissModalButton
|
25
|
+
}
|
26
|
+
|
27
|
+
bridgeDelegate.onViewDidLoad()
|
28
|
+
}
|
29
|
+
|
30
|
+
override func viewWillAppear(_ animated: Bool) {
|
31
|
+
super.viewWillAppear(animated)
|
32
|
+
bridgeDelegate.onViewWillAppear()
|
33
|
+
}
|
34
|
+
|
35
|
+
override func viewDidAppear(_ animated: Bool) {
|
36
|
+
super.viewDidAppear(animated)
|
37
|
+
bridgeDelegate.onViewDidAppear()
|
38
|
+
}
|
39
|
+
|
40
|
+
override func viewWillDisappear(_ animated: Bool) {
|
41
|
+
super.viewWillDisappear(animated)
|
42
|
+
bridgeDelegate.onViewWillDisappear()
|
43
|
+
}
|
44
|
+
|
45
|
+
override func viewDidDisappear(_ animated: Bool) {
|
46
|
+
super.viewDidDisappear(animated)
|
47
|
+
bridgeDelegate.onViewDidDisappear()
|
48
|
+
}
|
49
|
+
|
50
|
+
// MARK: Visitable
|
51
|
+
|
52
|
+
override func visitableDidActivateWebView(_ webView: WKWebView) {
|
53
|
+
bridgeDelegate.webViewDidBecomeActive(webView)
|
54
|
+
}
|
55
|
+
|
56
|
+
override func visitableDidDeactivateWebView() {
|
57
|
+
bridgeDelegate.webViewDidBecomeDeactivated()
|
58
|
+
}
|
59
|
+
|
60
|
+
// MARK: Actions
|
61
|
+
|
62
|
+
@objc func dismissModal() {
|
63
|
+
dismiss(animated: true)
|
64
|
+
}
|
65
|
+
}
|
@@ -0,0 +1,79 @@
|
|
1
|
+
import SwiftUI
|
2
|
+
|
3
|
+
public extension UIViewController {
|
4
|
+
func presentToast(_ message: String) {
|
5
|
+
guard let root = view.window?.rootViewController else { return }
|
6
|
+
|
7
|
+
removeToastViews(from: root)
|
8
|
+
|
9
|
+
let toastView = ToastView(message: message)
|
10
|
+
toastView.translatesAutoresizingMaskIntoConstraints = false
|
11
|
+
|
12
|
+
root.view.addSubview(toastView)
|
13
|
+
|
14
|
+
NSLayoutConstraint.activate([
|
15
|
+
toastView.topAnchor.constraint(equalTo: root.view.safeAreaLayoutGuide.topAnchor),
|
16
|
+
toastView.centerXAnchor.constraint(equalTo: root.view.centerXAnchor)
|
17
|
+
])
|
18
|
+
|
19
|
+
let widthConstraint = toastView.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -10)
|
20
|
+
widthConstraint.priority = .defaultHigh
|
21
|
+
|
22
|
+
let maxWidthConstraint = toastView.widthAnchor.constraint(lessThanOrEqualToConstant: 600)
|
23
|
+
maxWidthConstraint.priority = .required
|
24
|
+
|
25
|
+
NSLayoutConstraint.activate([
|
26
|
+
widthConstraint, maxWidthConstraint
|
27
|
+
])
|
28
|
+
}
|
29
|
+
|
30
|
+
fileprivate func removeToastViews(from root: UIViewController) {
|
31
|
+
root.view.subviews.filter({ $0 is ToastView }).forEach({ toast in
|
32
|
+
toast.removeFromSuperview()
|
33
|
+
})
|
34
|
+
}
|
35
|
+
}
|
36
|
+
|
37
|
+
public class ToastView: UIView {
|
38
|
+
|
39
|
+
private var duration = 2.5
|
40
|
+
|
41
|
+
convenience init(message: String) {
|
42
|
+
self.init(frame: .zero)
|
43
|
+
|
44
|
+
self.alpha = .zero
|
45
|
+
self.backgroundColor = .black
|
46
|
+
self.layer.cornerRadius = 10
|
47
|
+
|
48
|
+
let messageLabel = UILabel()
|
49
|
+
messageLabel.text = message
|
50
|
+
messageLabel.textColor = .white
|
51
|
+
messageLabel.textAlignment = .center
|
52
|
+
messageLabel.translatesAutoresizingMaskIntoConstraints = false
|
53
|
+
|
54
|
+
addSubview(messageLabel)
|
55
|
+
|
56
|
+
NSLayoutConstraint.activate([
|
57
|
+
messageLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 15),
|
58
|
+
messageLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -15),
|
59
|
+
messageLabel.topAnchor.constraint(equalTo: topAnchor, constant: 15),
|
60
|
+
messageLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -15)
|
61
|
+
])
|
62
|
+
|
63
|
+
addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dismiss)))
|
64
|
+
|
65
|
+
UIView.animate(withDuration: 0.5, delay: .zero, options: .curveEaseIn, animations: {
|
66
|
+
self.alpha = 0.9
|
67
|
+
}, completion: { _ in
|
68
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + self.duration) { self.dismiss() }
|
69
|
+
})
|
70
|
+
}
|
71
|
+
|
72
|
+
@objc private func dismiss() {
|
73
|
+
UIView.animate(withDuration: 0.5, delay: .zero, options: .curveEaseOut, animations: {
|
74
|
+
self.alpha = .zero
|
75
|
+
}, completion: { _ in
|
76
|
+
self.removeFromSuperview()
|
77
|
+
})
|
78
|
+
}
|
79
|
+
}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import Foundation
|
2
|
+
import WebKit
|
3
|
+
import Strada
|
4
|
+
|
5
|
+
enum WebViewPool {
|
6
|
+
static var shared = WKProcessPool()
|
7
|
+
}
|
8
|
+
|
9
|
+
extension WKWebViewConfiguration {
|
10
|
+
static var appConfiguration: WKWebViewConfiguration {
|
11
|
+
let stradaSubstring = Strada.userAgentSubstring(for: BridgeComponent.allTypes)
|
12
|
+
let userAgent = "Turbo Native iOS \(stradaSubstring)"
|
13
|
+
|
14
|
+
let configuration = WKWebViewConfiguration()
|
15
|
+
configuration.processPool = WebViewPool.shared
|
16
|
+
configuration.applicationNameForUserAgent = userAgent
|
17
|
+
configuration.defaultWebpagePreferences?.preferredContentMode = .mobile
|
18
|
+
|
19
|
+
return configuration
|
20
|
+
}
|
21
|
+
}
|
@@ -2,6 +2,7 @@ import UIKit
|
|
2
2
|
import WebKit
|
3
3
|
import SafariServices
|
4
4
|
import Turbo
|
5
|
+
import Strada
|
5
6
|
|
6
7
|
final class SceneDelegate: UIResponder {
|
7
8
|
private static var sharedProcessPool = WKProcessPool()
|
@@ -48,14 +49,12 @@ final class SceneDelegate: UIResponder {
|
|
48
49
|
private lazy var modalSession = makeSession()
|
49
50
|
|
50
51
|
private func makeSession() -> Session {
|
51
|
-
let
|
52
|
-
configuration.applicationNameForUserAgent = "Turbo Native iOS"
|
53
|
-
configuration.processPool = Self.sharedProcessPool
|
54
|
-
|
55
|
-
let webView = WKWebView(frame: .zero, configuration: configuration)
|
52
|
+
let webView = WKWebView(frame: .zero, configuration: .appConfiguration)
|
56
53
|
webView.uiDelegate = self
|
57
54
|
webView.allowsLinkPreview = false
|
58
55
|
|
56
|
+
Bridge.initialize(webView) // Initialize Strada bridge.
|
57
|
+
|
59
58
|
let session = Session(webView: webView)
|
60
59
|
session.delegate = self
|
61
60
|
session.pathConfiguration = pathConfiguration
|
@@ -2,12 +2,12 @@
|
|
2
2
|
"colors" : [
|
3
3
|
{
|
4
4
|
"color" : {
|
5
|
-
"color-space" : "
|
5
|
+
"color-space" : "srgb",
|
6
6
|
"components" : {
|
7
7
|
"alpha" : "1.000",
|
8
|
-
"blue" : "0.
|
9
|
-
"green" : "0.
|
10
|
-
"red" : "0.
|
8
|
+
"blue" : "0.882",
|
9
|
+
"green" : "0.386",
|
10
|
+
"red" : "0.076"
|
11
11
|
}
|
12
12
|
},
|
13
13
|
"idiom" : "universal"
|
@@ -20,12 +20,12 @@
|
|
20
20
|
}
|
21
21
|
],
|
22
22
|
"color" : {
|
23
|
-
"color-space" : "
|
23
|
+
"color-space" : "srgb",
|
24
24
|
"components" : {
|
25
25
|
"alpha" : "1.000",
|
26
|
-
"blue" : "0.
|
27
|
-
"green" : "0.
|
28
|
-
"red" : "0.
|
26
|
+
"blue" : "0.882",
|
27
|
+
"green" : "0.386",
|
28
|
+
"red" : "0.076"
|
29
29
|
}
|
30
30
|
},
|
31
31
|
"idiom" : "universal"
|
@@ -9,7 +9,7 @@
|
|
9
9
|
<!--Turbo Navigation Controller-->
|
10
10
|
<scene sceneID="GwE-Ch-DRP">
|
11
11
|
<objects>
|
12
|
-
<viewController id="cXb-wA-NUs" customClass="TurboNavigationController"
|
12
|
+
<viewController id="cXb-wA-NUs" customClass="TurboNavigationController" customModuleProvider="target" sceneMemberID="viewController">
|
13
13
|
<tabBarItem key="tabBarItem" title="" image="gear" catalog="system" id="rhZ-zi-yeq"/>
|
14
14
|
</viewController>
|
15
15
|
<placeholder placeholderIdentifier="IBFirstResponder" id="ife-PY-okT" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
@@ -19,7 +19,7 @@
|
|
19
19
|
<!--Turbo Navigation Controller-->
|
20
20
|
<scene sceneID="pAD-IL-Hdw">
|
21
21
|
<objects>
|
22
|
-
<viewController id="0en-nE-vJA" customClass="TurboNavigationController"
|
22
|
+
<viewController id="0en-nE-vJA" customClass="TurboNavigationController" customModuleProvider="target" sceneMemberID="viewController">
|
23
23
|
<tabBarItem key="tabBarItem" title="" image="house" catalog="system" id="ZWo-HO-m4a"/>
|
24
24
|
</viewController>
|
25
25
|
<placeholder placeholderIdentifier="IBFirstResponder" id="GRr-XE-fAM" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|