Skip to content

Commit

Permalink
Start of SwiftUI integration in generator (#61)
Browse files Browse the repository at this point in the history
* Parameters update

* Generated templates

* Additions for hosting and swiftUI modules

* Hosted SwiftUI templates fixed, native SwiftUI module hidden

* Templates updated with empty lines fixes, StateObject switched to ObservedObject

* Update README.md

* Squashed commit of the following:

commit 42ae805
Merge: 33bbb70 f953f84
Author: Filip Gulan <[email protected]>
Date:   Mon Oct 30 10:48:56 2023 +0100

    Merge pull request #64 from l1Dan/master

    Replace the file header comment with '___FILEHEADER___'

commit f953f84
Author: Leo Lee <[email protected]>
Date:   Sat Sep 2 13:57:15 2023 +0800

    Replace the file header comment with '___FILEHEADER___'

* New lines update,HostingNavigationController added, hiding navigation bar added to viewIsAppearing because of ios 15 issues

* headers added, readme updated, default value added in demo project

* Readme update

---------

Co-authored-by: ilucijabalja <[email protected]>
  • Loading branch information
Truba and ilucijabalja authored Nov 10, 2023
1 parent fcdda2b commit b6ef9e7
Show file tree
Hide file tree
Showing 115 changed files with 802 additions and 219 deletions.
5 changes: 0 additions & 5 deletions Base VIPER Interfaces/PresenterInterface.swift

This file was deleted.

12 changes: 0 additions & 12 deletions Base VIPER Interfaces/UIStoryboardExtension.swift

This file was deleted.

5 changes: 0 additions & 5 deletions Base VIPER Interfaces/ViewInterface.swift

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class LazyHostingViewController<RootView: View>: UIViewController {
var rootView: RootView!
private let isNavigationBarHidden: Bool

init(isNavigationBarHidden: Bool) {
init(isNavigationBarHidden: Bool = true) {
self.isNavigationBarHidden = isNavigationBarHidden
super.init(nibName: nil, bundle: nil)
}
Expand Down
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@

# Versions

Latest version is v4.0.1
* [Viper 4.0 Migration Guide](Documentation/Viper%204.0%20Migration%20Guide.md)

Latest version is v5.0

If you need to use any older version you can find them:
* 4.0 version [branch](https://github.com/infinum/iOS-VIPER-Xcode-Templates/tree/version/4.0)
* 4.0.1 version [branch](https://github.com/infinum/iOS-VIPER-Xcode-Templates/tree/version/4.0.1)
* 4.0 version [branch](https://github.com/infinum/iOS-VIPER-Xcode-Templates/tree/version/4.0) - [Viper 4.0 Migration Guide](Documentation/Viper%204.0%20Migration%20Guide.md)
* 3.2 version [branch](https://github.com/infinum/iOS-VIPER-Xcode-Templates/tree/version/3.2)
* 3.1 version [branch](https://github.com/infinum/iOS-VIPER-Xcode-Templates/tree/version/3.1)
* 3.0 version [branch](https://github.com/infinum/iOS-VIPER-Xcode-Templates/tree/version/3.0)
Expand All @@ -32,6 +31,11 @@ You can find most common VIPER module use cases in it. If you're already familia

If you want to check out how you could use Formatter in your apps, feel free to check out [Formatter Guide](Documentation/Formatter%20documentation.md).

## SwiftUI hosted module documentation and demo

The 5.0 version includes support for integrating SwiftUI into Viper modules. There's a simple demo project in the Demo folder.
The documentation for using SwiftUI hosted modules can be found in [Viper x SwiftUI Guide](Documentation/Viper%20x%20SwiftUI%20Guide.md).

# VIPER short introduction

How to organize all your code and not end up with a couple of <i>Massive View Controllers</i> with millions of lines of code?
Expand Down
38 changes: 20 additions & 18 deletions Templates/Resources/TemplateInfo.plist.erb
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,26 @@
<key>Type</key>
<string>static</string>
</dict>
<dict>
<key>Identifier</key>
<string>interface</string>
<key>Name</key>
<string>Generate Interface Builder:</string>
<key>Description</key>
<string>Which interface builder to generate</string>
<key>Type</key>
<string>popup</string>
<key>Default</key>
<string><%= @interface.default.name%></string>
<key>Values</key>
<array>
<%- @interface.types.each do |type| -%>
<string><%= type.name %></string>
<%- end -%>
</array>
</dict>
<% if @template.generate_interface_selection -%>
<dict>
<key>Identifier</key>
<string>interface</string>
<key>Name</key>
<string>Generate Interface Builder:</string>
<key>Description</key>
<string>Which interface builder to generate</string>
<key>Type</key>
<string>popup</string>
<key>Default</key>
<string><%= @interface.default.name%></string>
<key>Values</key>
<array>
<%- @interface.types.each do |type| -%>
<string><%= type.name %></string>
<%- end -%>
</array>
</dict>
<% end -%>
<dict>
<key>Identifier</key>
<string>complex</string>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
//___FILEHEADER___
// This file was generated by the 🐍 VIPER generator
//

<% if [email protected]_swift_ui_view %>
import UIKit
<% end -%>
<% if @template.generate_io -%>
import RxSwift
import RxCocoa
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
//___FILEHEADER___
// This file was generated by the 🐍 VIPER generator
//

<% if [email protected]_swift_ui_view %>
import UIKit
<% end -%>
<% if @template.generate_io -%>
import RxSwift
import RxCocoa
<% end -%>

<% if @template.generate_ui_kit_wireframe -%>
protocol ___VARIABLE_moduleName___WireframeInterface: WireframeInterface {
}
<% end -%>
<% if [email protected]_using_hosted_vc && [email protected]_swift_ui_view -%>

protocol ___VARIABLE_moduleName___ViewInterface: ViewInterface {
}
<% end -%>
<% if [email protected]_swift_ui_view -%>

protocol ___VARIABLE_moduleName___PresenterInterface: PresenterInterface {
<% if @template.generate_io -%>
func configure(with output: ___VARIABLE_moduleName___.ViewOutput) -> ___VARIABLE_moduleName___.ViewInput
<% end -%>
}
<% end -%>
<% if @complexity.generate_formatter -%>

protocol ___VARIABLE_moduleName___FormatterInterface: FormatterInterface {
Expand Down
34 changes: 22 additions & 12 deletions Templates/Resources/Templates/___FILEBASENAME___Presenter.swift.erb
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,51 @@ import RxSwift
import RxCocoa
<% end -%>

final class ___VARIABLE_moduleName___Presenter {
final class ___VARIABLE_moduleName___Presenter<%= @template.generate_swift_ui_view ? ': ObservableObject' : '' %> {
<% if @template.generate_swift_ui_wireframe -%>

final class Navigation: ObservableObject {

}
<%- end -%>

// MARK: - Private properties -

<% if @template.generate_vc -%>
private unowned let view: ___VARIABLE_moduleName___ViewInterface
<% end -%>
<% if @complexity.generate_formatter -%>
private let formatter: ___VARIABLE_moduleName___FormatterInterface
<% end -%>
<% if @complexity.generate_interactor -%>
private let interactor: ___VARIABLE_moduleName___InteractorInterface
<% end -%>
<% if @template.generate_ui_kit_wireframe -%>
private let wireframe: ___VARIABLE_moduleName___WireframeInterface
<% else -%>
private let navigation: Navigation
<% end -%>

// MARK: - Lifecycle -

init(
view: ___VARIABLE_moduleName___ViewInterface,
<%- if @complexity.generate_formatter -%>
<%= @complexity.generate_formatter ? 'formatter: ___VARIABLE_moduleName___FormatterInterface,' : '' %>
<%- end -%>
<%- if @complexity.generate_interactor -%>
<%= @complexity.generate_interactor ? 'interactor: ___VARIABLE_moduleName___InteractorInterface,' : '' %>
<%- end -%>
wireframe: ___VARIABLE_moduleName___WireframeInterface
) {
init(<%= @template.generate_vc ? 'view: ___VARIABLE_moduleName___ViewInterface, ' : '' %><%= @complexity.generate_formatter ? 'formatter: ___VARIABLE_moduleName___FormatterInterface, ' : '' %><%= @complexity.generate_interactor ? 'interactor: ___VARIABLE_moduleName___InteractorInterface, ' : '' %><%= @template.generate_ui_kit_wireframe ? 'wireframe: ___VARIABLE_moduleName___WireframeInterface' : '' %><%= @template.generate_swift_ui_wireframe ? 'navigation: Navigation' : '' %>) {
<%- if @template.generate_vc -%>
self.view = view
<%- end -%>
<%- if @complexity.generate_formatter -%>
self.formatter = formatter
<%- end -%>
<%- if @complexity.generate_interactor -%>
self.interactor = interactor
<%- end -%>
<%- if @template.generate_ui_kit_wireframe -%>
self.wireframe = wireframe
<%- else -%>
self.navigation = navigation
<%- end -%>
}
}

<% if [email protected]_using_hosted_vc && [email protected]_swift_ui_view %>
// MARK: - Extensions -

extension ___VARIABLE_moduleName___Presenter: ___VARIABLE_moduleName___PresenterInterface {
Expand All @@ -62,3 +71,4 @@ extension ___VARIABLE_moduleName___Presenter: ___VARIABLE_moduleName___Presenter

<%- end -%>
}
<% end -%>
14 changes: 14 additions & 0 deletions Templates/Resources/Templates/___FILEBASENAME___View.swift.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//___FILEHEADER___
// This file was generated by the 🐍 VIPER generator
//

import SwiftUI

struct ___VARIABLE_moduleName___View: View {

@ObservedObject var presenter: ___VARIABLE_moduleName___Presenter

var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//___FILEHEADER___
// This file was generated by the 🐍 VIPER generator
//

import SwiftUI

struct ___VARIABLE_moduleName___Wireframe: View {

@StateObject var navigation = ___VARIABLE_moduleName___Presenter.Navigation()

var body: some View {
<%- if @complexity.generate_formatter -%>
let formatter = ___VARIABLE_moduleName___Formatter()
<%- end -%>
<%- if @complexity.generate_interactor -%>
let interactor = ___VARIABLE_moduleName___Interactor()
<%- end -%>
let presenter = ___VARIABLE_moduleName___Presenter(<%= @complexity.generate_formatter ? 'formatter: formatter, ' : '' %><%= @complexity.generate_interactor ? 'interactor: interactor, ' : '' %>navigation: navigation)

___VARIABLE_moduleName___View(presenter: presenter)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import RxSwift
import RxCocoa
<% end -%>

final class ___VARIABLE_moduleName___Wireframe: BaseWireframe<___VARIABLE_moduleName___ViewController> {
final class ___VARIABLE_moduleName___Wireframe: BaseWireframe<<%= @template.generate_using_hosted_vc ? 'LazyHostingViewController<___VARIABLE_moduleName___View>' : '___VARIABLE_moduleName___ViewController' -%>> {

<% if !@interface.generate_xib -%>
// MARK: - Private properties -
Expand All @@ -21,7 +21,9 @@ final class ___VARIABLE_moduleName___Wireframe: BaseWireframe<___VARIABLE_module
// MARK: - Module setup -

init() {
<%- if @interface.generate_sb -%>
<%- if @template.generate_using_hosted_vc -%>
let moduleViewController = LazyHostingViewController<___VARIABLE_moduleName___View>()
<%- elsif @interface.generate_sb -%>
let moduleViewController = storyboard.instantiateViewController(ofType: ___VARIABLE_moduleName___ViewController.self)
<%- else -%>
let moduleViewController = ___VARIABLE_moduleName___ViewController()
Expand All @@ -34,8 +36,12 @@ final class ___VARIABLE_moduleName___Wireframe: BaseWireframe<___VARIABLE_module
<%- if @complexity.generate_interactor -%>
let interactor = ___VARIABLE_moduleName___Interactor()
<%- end -%>
let presenter = ___VARIABLE_moduleName___Presenter(view: moduleViewController, <%= @complexity.generate_formatter ? 'formatter: formatter, ' : '' %><%= @complexity.generate_interactor ? 'interactor: interactor, ' : '' %>wireframe: self)
let presenter = ___VARIABLE_moduleName___Presenter(<%= !@template.generate_using_hosted_vc ? 'view: moduleViewController, ' : '' %><%= @complexity.generate_formatter ? 'formatter: formatter, ' : '' %><%= @complexity.generate_interactor ? 'interactor: interactor, ' : '' %>wireframe: self)
<%- if @template.generate_using_hosted_vc -%>
moduleViewController.rootView = ___VARIABLE_moduleName___View(presenter: presenter)
<%- else -%>
moduleViewController.presenter = presenter
<%- end -%>
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<plist version="1.0">
<dict>
<key>DefaultCompletionName</key>
<string>BaseVIPER</string>
<string>VIPERModule</string>
<key>Description</key>
<string>This generates base VIPER files used for other VIPER generations.</string>
<key>Kind</key>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//___FILEHEADER___

import UIKit

protocol WireframeInterface: AnyObject {
Expand All @@ -22,7 +24,7 @@ extension BaseWireframe: WireframeInterface {
}

extension BaseWireframe {

var viewController: ViewController {
defer { temporaryStoredViewController = nil }
guard let vc = _viewController else {
Expand Down Expand Up @@ -51,19 +53,19 @@ extension BaseWireframe {
}

extension UIViewController {

func presentWireframe<ViewController>(_ wireframe: BaseWireframe<ViewController>, animated: Bool = true, completion: (() -> Void)? = nil) {
present(wireframe.viewController, animated: animated, completion: completion)
}

}

extension UINavigationController {

func pushWireframe<ViewController>(_ wireframe: BaseWireframe<ViewController>, animated: Bool = true) {
pushViewController(wireframe.viewController, animated: animated)
}

func setRootWireframe<ViewController>(_ wireframe: BaseWireframe<ViewController>, animated: Bool = true) {
setViewControllers([wireframe.viewController], animated: animated)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//___FILEHEADER___

protocol FormatterInterface: AnyObject {
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//___FILEHEADER___

import UIKit

protocol HostingNavigationConfigurable: AnyObject {

var shouldHideNavigationBar: Bool { get }

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//___FILEHEADER___

import UIKit

/// Navigation controller that should be used only in combination with the ``LazyHostingViewController``.
///
/// It overrides the ``setNavigationBarHidden(_:animated:)`` method in a way where it will always ask
/// the currently pushed view controller for its preference stored in the ``HostingNavigationConfigurable/shouldHideNavigationBar``
/// parameter, ignoring any actual parameters passed to it.
///
/// This is done in such a way because at some point during the layout process SwiftUI also
/// tries to change the current navigation bar visibility even thouh it shouldn't.
class HostingNavigationController: UINavigationController {

override func setNavigationBarHidden(_ hidden: Bool, animated: Bool) {
guard let hostingChild = children.last as? HostingNavigationConfigurable
else {
// In case the last pushed controller is not a HostingNavigationConfigurable,
// we can simply call super here since we're not dealing with SwiftUI
// but with regular UIKit.
super.setNavigationBarHidden(hidden, animated: animated)
return
}

super.setNavigationBarHidden(hostingChild.shouldHideNavigationBar, animated: animated)
}

}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//___FILEHEADER___

protocol InteractorInterface: AnyObject {
}

Expand Down
Loading

0 comments on commit b6ef9e7

Please sign in to comment.