├── CHANGELOG.md ├── Sources └── FirstOpenViews │ ├── FirstOpenType.swift │ ├── InformationDetailView.swift │ ├── WelcomeView.swift │ ├── WhatsNewView.swift │ └── FirstOpenView.swift ├── .gitattributes ├── LICENSE.txt ├── Package.swift ├── .gitignore └── README.md /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | FirstOpenViews Changelog 2 | ================== 3 | 4 | #### v1.0.2 5 | - Cleanup 6 | 7 | #### v1.0.1 8 | - Added Access control levels 9 | 10 | #### v1.0.0 11 | - Initial Release 12 | -------------------------------------------------------------------------------- /Sources/FirstOpenViews/FirstOpenType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FirstOpenType.swift 3 | // 4 | // Created by Lucas Zischka. 5 | // Copyright © 2021 Lucas Zischka. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | internal enum FirstOpenType { 11 | case welcome 12 | case whatsNew 13 | } 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################### 2 | # Git Line Endings # 3 | ############################### 4 | 5 | # Set default behaviour to automatically normalize line endings. 6 | * text=auto 7 | 8 | # Force batch scripts to always use CRLF line endings so that if a repo is accessed 9 | # in Windows via a file share from Linux, the scripts will work. 10 | *.{cmd,[cC][mM][dD]} text eol=crlf 11 | *.{bat,[bB][aA][tT]} text eol=crlf 12 | 13 | # Force bash scripts to always use LF line endings so that if a repo is accessed 14 | # in Unix via a file share from Windows, the scripts will work. 15 | *.sh text eol=lf 16 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Lucas Zischka 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "FirstOpenViews", 8 | platforms: [ 9 | .iOS(.v13) 10 | ], 11 | products: [ 12 | // Products define the executables and libraries a package produces, and make them visible to other packages. 13 | .library( 14 | name: "FirstOpenViews", 15 | targets: ["FirstOpenViews"]) 16 | ], 17 | dependencies: [ 18 | // Dependencies declare other packages that this package depends on. 19 | // .package(url: /* package url */, from: "1.0.0"), 20 | ], 21 | targets: [ 22 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 23 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 24 | .target( 25 | name: "FirstOpenViews", 26 | dependencies: []) 27 | ] 28 | ) 29 | -------------------------------------------------------------------------------- /Sources/FirstOpenViews/InformationDetailView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InformationDetailView.swift 3 | // 4 | // Created by Lucas Zischka. 5 | // Copyright © 2021 Lucas Zischka. All rights reserved. 6 | // 7 | 8 | import SwiftUI 9 | 10 | public struct InformationDetailView: View { 11 | public var title: String 12 | public var subTitle: String 13 | public var image: Image 14 | public var mainColor: Color 15 | 16 | public var body: some View { 17 | HStack(alignment: .center) { 18 | self.image 19 | .font(.largeTitle) 20 | .foregroundColor(self.mainColor) 21 | .padding() 22 | .accessibility(hidden: true) 23 | 24 | VStack(alignment: .leading) { 25 | Text(self.title) 26 | .font(.headline) 27 | .foregroundColor(.primary) 28 | .accessibility(addTraits: .isHeader) 29 | 30 | Text(self.subTitle) 31 | .font(.body) 32 | .foregroundColor(.secondary) 33 | .fixedSize(horizontal: false, vertical: true) 34 | } 35 | } 36 | .padding(.top) 37 | } 38 | 39 | public init(title: String, subTitle: String, image: Image, mainColor: Color = Color.blue) { 40 | self.title = title 41 | self.subTitle = subTitle 42 | self.image = image 43 | self.mainColor = mainColor 44 | } 45 | } 46 | 47 | struct InformationDetailView_Previews: PreviewProvider { 48 | static var previews: some View { 49 | InformationDetailView(title: "Car", subTitle: "A car (or automobile) is a wheeled motor vehicle used for transportation.", image: Image(systemName: "car.fill"), mainColor: Color.blue) 50 | .previewLayout(.sizeThatFits) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### macOS ### 2 | 3 | # General 4 | .DS_Store 5 | .AppleDouble 6 | .LSOverride 7 | 8 | # Icon must end with two \r 9 | Icon 10 | 11 | # Thumbnails 12 | ._* 13 | 14 | # Files that might appear in the root of a volume 15 | .DocumentRevisions-V100 16 | .fseventsd 17 | .Spotlight-V100 18 | .TemporaryItems 19 | .Trashes 20 | .VolumeIcon.icns 21 | .com.apple.timemachine.donotpresent 22 | 23 | # Directories potentially created on remote AFP share 24 | .AppleDB 25 | .AppleDesktop 26 | Network Trash Folder 27 | Temporary Items 28 | .apdisk 29 | 30 | 31 | ### Swift ### 32 | 33 | # Xcode 34 | 35 | ## User settings 36 | xcuserdata/ 37 | 38 | ## Obj-C/Swift specific 39 | *.hmap 40 | 41 | ## App packaging 42 | *.ipa 43 | *.dSYM.zip 44 | *.dSYM 45 | 46 | ## Playgrounds 47 | timeline.xctimeline 48 | playground.xcworkspace 49 | 50 | ## Gcc Patch 51 | /*.gcno 52 | 53 | ## Xcode Patch 54 | *.xcodeproj/* 55 | !*.xcodeproj/project.pbxproj 56 | !*.xcodeproj/xcshareddata/ 57 | !*.xcworkspace/contents.xcworkspacedata 58 | **/xcshareddata/WorkspaceSettings.xcsettings 59 | 60 | # Swift Package Manager 61 | 62 | ## Avoid checking in source code from Swift Package Manager dependencies. 63 | Packages/ 64 | Package.pins 65 | Package.resolved 66 | 67 | .swiftpm 68 | .build/ 69 | 70 | # CocoaPods 71 | 72 | Pods/ 73 | 74 | ## Avoid checking in source code from the Xcode workspace 75 | *.xcworkspace 76 | 77 | # Carthage 78 | 79 | ## Avoid checking in source code from Carthage dependencies. 80 | Carthage/Checkouts 81 | 82 | Carthage/Build/ 83 | 84 | # Accio dependency management 85 | 86 | Dependencies/ 87 | .accio/ 88 | 89 | # fastlane 90 | fastlane/report.xml 91 | fastlane/Preview.html 92 | fastlane/screenshots/**/*.png 93 | fastlane/test_output 94 | 95 | # Code Injection 96 | iOSInjectionProject/ 97 | 98 | 99 | ### Windows ### 100 | 101 | # Windows thumbnail cache files 102 | Thumbs.db 103 | Thumbs.db:encryptable 104 | ehthumbs.db 105 | ehthumbs_vista.db 106 | 107 | # Dump file 108 | *.stackdump 109 | 110 | # Folder config file 111 | [Dd]esktop.ini 112 | 113 | # Recycle Bin used on file shares 114 | $RECYCLE.BIN/ 115 | 116 | # Windows Installer files 117 | *.cab 118 | *.msi 119 | *.msix 120 | *.msm 121 | *.msp 122 | 123 | # Windows shortcuts 124 | *.lnk 125 | -------------------------------------------------------------------------------- /Sources/FirstOpenViews/WelcomeView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WelcomeView.swift 3 | // 4 | // Created by Lucas Zischka. 5 | // Copyright © 2021 Lucas Zischka. All rights reserved. 6 | // 7 | 8 | import SwiftUI 9 | 10 | fileprivate struct WelcomeView: ViewModifier { 11 | 12 | @State private var savedAppVersion: String? = UserDefaults.standard.string(forKey: "savedAppVersion") 13 | @State private var showWelcomeView: Bool = false 14 | 15 | fileprivate var imageName: String? 16 | fileprivate var mainColor: Color = Color.blue 17 | fileprivate var informationDetailViews: [InformationDetailView] 18 | 19 | 20 | fileprivate func body(content: Content) -> some View { 21 | content 22 | .onAppear(perform: self.checkForUpdate) 23 | .background( 24 | EmptyView() 25 | .sheet(isPresented: self.$showWelcomeView, content: { 26 | FirstOpenView(firstOpenType: .welcome, imageName: self.imageName, mainColor: self.mainColor, informationDetailViews: self.informationDetailViews) 27 | }) 28 | ) 29 | } 30 | 31 | private func checkForUpdate() { 32 | let currentAppVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String 33 | 34 | if let currentAppVersion = currentAppVersion, self.savedAppVersion == nil { 35 | self.showWelcomeView = true 36 | UserDefaults.standard.set(currentAppVersion, forKey: "savedAppVersion") 37 | self.savedAppVersion = currentAppVersion 38 | } 39 | } 40 | } 41 | 42 | public extension View { 43 | func welcomeView(imageName: String? = nil, mainColor: Color = Color.blue, informationDetailViews: [InformationDetailView]) -> some View { 44 | self.modifier(WelcomeView(imageName: imageName, mainColor: mainColor, informationDetailViews: informationDetailViews)) 45 | } 46 | } 47 | 48 | struct WelcomeView_Previews: PreviewProvider { 49 | static var previews: some View { 50 | Text("Hello World") 51 | .welcomeView(informationDetailViews: [ 52 | InformationDetailView(title: "Car", 53 | subTitle: "A car (or automobile) is a wheeled motor vehicle used for transportation.", 54 | image: Image(systemName: "car.fill") 55 | ), 56 | InformationDetailView(title: "Airplane", 57 | subTitle: "An airplane or aeroplane (informally plane) is a powered, fixed-wing aircraft that is propelled forward by thrust from a jet engine, propeller or rocket engine.", 58 | image: Image(systemName: "airplane") 59 | ), 60 | InformationDetailView(title: "Tram", 61 | subTitle: "A tram (in North America streetcar or trolley) is a rail vehicle that runs on tramway tracks along public urban streets.", 62 | image: Image(systemName: "tram.fill") 63 | ) 64 | ]) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Sources/FirstOpenViews/WhatsNewView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WhatsNewView.swift 3 | // 4 | // Created by Lucas Zischka. 5 | // Copyright © 2021 Lucas Zischka. All rights reserved. 6 | // 7 | 8 | import SwiftUI 9 | 10 | fileprivate struct WhatsNewView: ViewModifier { 11 | 12 | @State private var savedAppVersion: String? = UserDefaults.standard.string(forKey: "savedAppVersion") 13 | @State private var showWhatsNewView: Bool = false 14 | 15 | fileprivate var imageName: String? 16 | fileprivate var mainColor: Color = Color.blue 17 | fileprivate var informationDetailViews: [InformationDetailView] 18 | 19 | 20 | fileprivate func body(content: Content) -> some View { 21 | content 22 | .onAppear(perform: self.checkForUpdate) 23 | .background( 24 | EmptyView() 25 | .sheet(isPresented: self.$showWhatsNewView, content: { 26 | FirstOpenView(firstOpenType: .whatsNew, imageName: self.imageName, mainColor: self.mainColor, informationDetailViews: self.informationDetailViews) 27 | }) 28 | ) 29 | } 30 | 31 | private func checkForUpdate() { 32 | let currentAppVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String 33 | 34 | if let savedAppVersion = self.savedAppVersion, let currentAppVersion = currentAppVersion, savedAppVersion != currentAppVersion { 35 | self.showWhatsNewView = true 36 | UserDefaults.standard.set(currentAppVersion, forKey: "savedAppVersion") 37 | self.savedAppVersion = currentAppVersion 38 | } 39 | } 40 | } 41 | 42 | public extension View { 43 | func whatsNewView(imageName: String? = nil, mainColor: Color = Color.blue, informationDetailViews: [InformationDetailView]) -> some View { 44 | self.modifier(WhatsNewView(imageName: imageName, mainColor: mainColor, informationDetailViews: informationDetailViews)) 45 | } 46 | } 47 | 48 | struct WhatsNewView_Previews: PreviewProvider { 49 | static var previews: some View { 50 | Text("Hello World") 51 | .whatsNewView(informationDetailViews: [ 52 | InformationDetailView(title: "Car", 53 | subTitle: "A car (or automobile) is a wheeled motor vehicle used for transportation.", 54 | image: Image(systemName: "car.fill") 55 | ), 56 | InformationDetailView(title: "Airplane", 57 | subTitle: "An airplane or aeroplane (informally plane) is a powered, fixed-wing aircraft that is propelled forward by thrust from a jet engine, propeller or rocket engine.", 58 | image: Image(systemName: "airplane") 59 | ), 60 | InformationDetailView(title: "Tram", 61 | subTitle: "A tram (in North America streetcar or trolley) is a rail vehicle that runs on tramway tracks along public urban streets.", 62 | image: Image(systemName: "tram.fill") 63 | ) 64 | ]) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Sources/FirstOpenViews/FirstOpenView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FirstOpenView.swift 3 | // 4 | // Created by Lucas Zischka. 5 | // Copyright © 2021 Lucas Zischka. All rights reserved. 6 | // 7 | 8 | import SwiftUI 9 | 10 | internal struct FirstOpenView: View { 11 | 12 | @Environment(\.presentationMode) private var presentationMode 13 | 14 | internal var firstOpenType: FirstOpenType 15 | internal var imageName: String? 16 | internal var mainColor: Color 17 | internal var informationDetailViews: [InformationDetailView] 18 | 19 | internal var body: some View { 20 | GeometryReader { geometry in 21 | VStack { 22 | Spacer() 23 | 24 | if let imageName = self.imageName { 25 | Image(imageName) 26 | .resizable() 27 | .aspectRatio(contentMode: .fit) 28 | .frame(width: 0.3 * geometry.size.width) 29 | .cornerRadius(25) 30 | .accessibility(hidden: true) 31 | } 32 | 33 | Text(self.firstOpenType == .welcome ? "Welcome to" : "What's New in") 34 | .fontWeight(.black) 35 | .font(.system(size: 36)) 36 | 37 | if let appName = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as? String { 38 | Text(appName) 39 | .foregroundColor(self.mainColor) 40 | .fontWeight(.black) 41 | .font(.system(size: 36)) 42 | } 43 | 44 | Spacer() 45 | 46 | VStack(alignment: .leading) { 47 | ForEach(0..Xcode 11 integrates with libSwiftPM to provide support for iOS, watchOS, and tvOS platforms. 35 | 36 | 1. In Xcode, open your project and navigate to **File** → **Swift Packages** → **Add Package Dependency...** 37 | 2. Paste the repository URL (`https://github.com/lucaszischka/FirstOpenViews`) and click **Next**. 38 | 3. For **Rules**, select **Branch** (with branch set to `main`). 39 | 4. Click **Finish**. 40 | 41 | # Usage 42 | 43 | ## Basic Usage 44 | 45 | **WARNING:** 46 | This is Sample Code for visualisation where and how to use, without a working initializer. Please see [Examples](#examples) for working code 47 | 48 | Same way you use Sheet in SwiftUI 49 | 50 | ### Life Cycle: UIKit App Delegate (IOS 13+) 51 | 52 | ````swift 53 | //SceneDelegate.swift 54 | 55 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 56 | let contentView = ContentView() //1 57 | .welcomeView() //2 58 | .whatsNewView() //3 59 | 60 | if let windowScene = scene as? UIWindowScene { 61 | let window = UIWindow(windowScene: windowScene) 62 | window.rootViewController = UIHostingController(rootView: contentView) 63 | self.window = window 64 | window.makeKeyAndVisible() 65 | } 66 | } 67 | ```` 68 | 69 | ### Life Cycle: SwiftUI App (IOS 14+) 70 | 71 | ````swift 72 | @main 73 | struct TestApp: App { 74 | var body: some Scene { 75 | WindowGroup { 76 | ContentView() //1 77 | .welcomeView() //2 78 | .whatsNewView() //3 79 | } 80 | } 81 | } 82 | ```` 83 | 84 | `//1` You add it to the highest Navigation Level 85 | 86 | `//2` This is how you add the WelcomeView (first launch) 87 | 88 | `//3` This is how you add the WhatsNewView (after update) 89 | 90 | # Parameters 91 | 92 | ## WelcomeView 93 | 94 | **WARNING:** 95 | This is Sample Code for visualisation of the parameters and their default values and value types, without a working initializer. Please see [Examples](#examples) for working code 96 | 97 | ````swift 98 | .welcomeView( 99 | imageName: String? = nil, 100 | mainColor: Color = Color.blue, 101 | informationDetailViews: [InformationDetailView] 102 | ) 103 | ```` 104 | 105 | - `imageName` 106 | Here you can add a image to the sheet if you wish. 107 | 108 | - `mainColor` 109 | Here you can change the color used for the App Name and the Continue Button. 110 | 111 | - `informationDetailViews` 112 | Here you add the InformationDetailView's. Three are recommend. Please see [InformationDetailView](#InformationDetailView) for parameters. 113 | 114 | ## WhatsNewView 115 | 116 | **WARNING:** 117 | This is Sample Code for visualisation of the parameters and their default values and value types, without a working initializer. Please see [Examples](#examples) for working code 118 | 119 | ````swift 120 | .whatsNewView( 121 | imageName: String? = nil, 122 | mainColor: Color = Color.blue, 123 | informationDetailViews: [InformationDetailView] 124 | ) 125 | ```` 126 | 127 | - `imageName` 128 | Here you can add a image to the sheet if you wish. 129 | 130 | - `mainColor` 131 | Here you can change the color used for the App Name and the Continue Button. 132 | 133 | - `informationDetailViews` 134 | Here you add the InformationDetailView's. Three are recommend. Please see [InformationDetailView](#InformationDetailView) for parameters. 135 | 136 | ## InformationDetailView 137 | 138 | **WARNING:** 139 | This is Sample Code for visualisation of the parameters and their default values and value types, without a working initializer. Please see [Examples](#examples) for working code 140 | 141 | ````swift 142 | InformationDetailView( 143 | title: String, 144 | subTitle: Sring, 145 | image: Image, 146 | mainColor: Color = Color.blue 147 | ) 148 | ```` 149 | 150 | - `title` 151 | The title for the view 152 | 153 | - `subTitle` 154 | The text for the view 155 | 156 | - `image` 157 | Here you add the picture for the View. SFSymbols are preferred. 158 | 159 | - `mainColor` 160 | Here you can change the color of the picture if it is an SFSymbol. 161 | 162 | # Examples 163 | 164 | ## Life Cycle: UIKit App Delegate (IOS 13+) 165 | 166 | ````swift 167 | import UIKit 168 | import SwiftUI 169 | import FirstOpenViews 170 | 171 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 172 | 173 | var window: UIWindow? 174 | 175 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 176 | 177 | let contentView = ContentView() 178 | .welcomeView(informationDetailViews: [ 179 | InformationDetailView(title: "Car", subTitle: "A car (or automobile) is a wheeled motor vehicle used for transportation.", image: Image(systemName: "car.fill")), 180 | InformationDetailView(title: "Airplane", subTitle: "An airplane or aeroplane (informally plane) is a powered, fixed-wing aircraft that is propelled forward by thrust from a jet engine, propeller or rocket engine.", image: Image(systemName: "airplane")), 181 | InformationDetailView(title: "Tram", subTitle: "A tram (in North America streetcar or trolley) is a rail vehicle that runs on tramway tracks along public urban streets.", image: Image(systemName: "tram.fill")) 182 | ]) 183 | .whatsNewView(informationDetailViews: [ 184 | InformationDetailView(title: "Car", subTitle: "A car (or automobile) is a wheeled motor vehicle used for transportation.", image: Image(systemName: "car.fill")), 185 | InformationDetailView(title: "Airplane", subTitle: "An airplane or aeroplane (informally plane) is a powered, fixed-wing aircraft that is propelled forward by thrust from a jet engine, propeller or rocket engine.", image: Image(systemName: "airplane")), 186 | InformationDetailView(title: "Tram", subTitle: "A tram (in North America streetcar or trolley) is a rail vehicle that runs on tramway tracks along public urban streets.", image: Image(systemName: "tram.fill")) 187 | ]) 188 | 189 | if let windowScene = scene as? UIWindowScene { 190 | let window = UIWindow(windowScene: windowScene) 191 | window.rootViewController = UIHostingController(rootView: contentView) 192 | self.window = window 193 | window.makeKeyAndVisible() 194 | } 195 | } 196 | 197 | func sceneDidDisconnect(_ scene: UIScene) {} 198 | 199 | func sceneDidBecomeActive(_ scene: UIScene) {} 200 | 201 | func sceneWillResignActive(_ scene: UIScene) {} 202 | 203 | func sceneWillEnterForeground(_ scene: UIScene) {} 204 | 205 | func sceneDidEnterBackground(_ scene: UIScene) {} 206 | } 207 | ```` 208 | 209 | ## Life Cycle: SwiftUI App (IOS 14+) 210 | 211 | ````swift 212 | import SwiftUI 213 | import FirstOpenViews 214 | 215 | @main 216 | struct TestApp: App { 217 | var body: some Scene { 218 | WindowGroup { 219 | ContentView() 220 | .welcomeView(informationDetailViews: [ 221 | InformationDetailView(title: "Car", subTitle: "A car (or automobile) is a wheeled motor vehicle used for transportation.", image: Image(systemName: "car.fill")), 222 | InformationDetailView(title: "Airplane", subTitle: "An airplane or aeroplane (informally plane) is a powered, fixed-wing aircraft that is propelled forward by thrust from a jet engine, propeller or rocket engine.", image: Image(systemName: "airplane")), 223 | InformationDetailView(title: "Tram", subTitle: "A tram (in North America streetcar or trolley) is a rail vehicle that runs on tramway tracks along public urban streets.", image: Image(systemName: "tram.fill")) 224 | ]) 225 | .whatsNewView(informationDetailViews: [ 226 | InformationDetailView(title: "Car", subTitle: "A car (or automobile) is a wheeled motor vehicle used for transportation.", image: Image(systemName: "car.fill")), 227 | InformationDetailView(title: "Airplane", subTitle: "An airplane or aeroplane (informally plane) is a powered, fixed-wing aircraft that is propelled forward by thrust from a jet engine, propeller or rocket engine.", image: Image(systemName: "airplane")), 228 | InformationDetailView(title: "Tram", subTitle: "A tram (in North America streetcar or trolley) is a rail vehicle that runs on tramway tracks along public urban streets.", image: Image(systemName: "tram.fill")) 229 | ]) 230 | } 231 | } 232 | } 233 | ```` 234 | 235 | # Contributing 236 | 237 | FirstOpenViews welcomes contributions in the form of GitHub issues and pull-requests. 238 | 239 | # License 240 | 241 | FirstOpenViews is available under the MIT license. See [the LICENSE file](LICENSE.txt) for more information. 242 | 243 | # Credits 244 | 245 | FirstOpenViews is a project of [@lucaszischka](https://github.com/lucaszischka). 246 | --------------------------------------------------------------------------------