├── RGStack.gif
├── RGStack
├── Assets.xcassets
│ ├── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
├── AppDelegate.swift
├── Base.lproj
│ └── LaunchScreen.storyboard
├── ContentView.swift
├── Info.plist
├── Helpers.swift
├── SceneDelegate.swift
└── RGStack.swift
├── RGStack.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcuserdata
│ └── roberageleta.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── project.pbxproj
├── RGStack.podspec
├── RGStackTests
├── Info.plist
└── RGStackTests.swift
├── RGStackUITests
├── Info.plist
└── RGStackUITests.swift
├── LICENSE
└── README.md
/RGStack.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/terminatorover/RGStack/HEAD/RGStack.gif
--------------------------------------------------------------------------------
/RGStack/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/RGStack/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/RGStack.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/RGStack.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/RGStack.xcodeproj/xcuserdata/roberageleta.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | RGStack.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/RGStack.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 |
3 | s.name = "RGStack"
4 | s.version = "1.2"
5 | s.summary = "SwiftUI View Component"
6 | s.description = "This UI attempts to capture the Quibi Card Stack and the associated User Interaction."
7 |
8 | s.author = "Robera Geleta"
9 | s.license = {:type => 'MIT', :file => 'LICENSE'}
10 | s.homepage = "https://github.com/terminatorover/RGStack"
11 |
12 | s.platform = :ios, "13.0"
13 | s.source = { :git => "https://github.com/terminatorover/RGStack.git", :tag => "1.2" }
14 | s.frameworks = "SwiftUI", "Foundation"
15 | s.source_files = "RGStack/*.swift"
16 | s.module_name = "RGStack"
17 | s.swift_version = '4.0'
18 | end
19 |
--------------------------------------------------------------------------------
/RGStackTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/RGStackUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/RGStackTests/RGStackTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RGStackTests.swift
3 | // RGStackTests
4 | //
5 | // Created by ROBERA GELETA on 5/25/20.
6 | // Copyright © 2020 ROBERA GELETA. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import RGStack
11 |
12 | class RGStackTests: XCTestCase {
13 |
14 | override func setUpWithError() throws {
15 | // Put setup code here. This method is called before the invocation of each test method in the class.
16 | }
17 |
18 | override func tearDownWithError() throws {
19 | // Put teardown code here. This method is called after the invocation of each test method in the class.
20 | }
21 |
22 | func testExample() throws {
23 | // This is an example of a functional test case.
24 | // Use XCTAssert and related functions to verify your tests produce the correct results.
25 | }
26 |
27 | func testPerformanceExample() throws {
28 | // This is an example of a performance test case.
29 | self.measure {
30 | // Put the code you want to measure the time of here.
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 RGeleta
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RGStack
2 |
3 | This UI attempts to capture the Quibi Card Stack and the associated User Interaction.
4 |
5 | 
6 |
7 | ### Required
8 | A View that conforms to the `ConfigurableCard` protocol
9 |
10 | RGStack is available through [CocoaPods](http://cocoapods.org). To install it, simply add the following line to your Podfile:
11 |
12 | ```ruby
13 | pod 'RGStack'
14 | ```
15 |
16 | ### Simplest Setup
17 |
18 | ```swift
19 | import RGStack
20 |
21 | struct ContentView: View {
22 | let demos: [Demo]
23 | var body: some View {
24 | RGStack(data: demos, size: .init(width: 320, height: 530))//convenience initializer
25 | }
26 | }
27 | ```
28 |
29 | ### More configuration
30 |
31 | Use the CardInfo, which is passed to the RGStack initializer, to control more aspects of the UI Layout
32 | ```swift
33 | struct CardInfo {
34 | let size: CGSize
35 | let gapDistance: CGFloat
36 | let minScaleForBackCard: CGFloat
37 | let visibleFractionOfBottomCard: CGFloat
38 | }
39 | ```
40 |
41 | A full Demo is included in the `ContentView` of the Project.
42 |
43 | ## License
44 | [](https://opensource.org/licenses/MIT)
45 |
46 |
--------------------------------------------------------------------------------
/RGStack/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // RGStack
4 | //
5 | // Created by ROBERA GELETA on 5/25/20.
6 | // Copyright © 2020 ROBERA GELETA. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 |
15 |
16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
17 | // Override point for customization after application launch.
18 | return true
19 | }
20 |
21 | // MARK: UISceneSession Lifecycle
22 |
23 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
24 | // Called when a new scene session is being created.
25 | // Use this method to select a configuration to create the new scene with.
26 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
27 | }
28 |
29 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
30 | // Called when the user discards a scene session.
31 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
32 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
33 | }
34 |
35 |
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/RGStackUITests/RGStackUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RGStackUITests.swift
3 | // RGStackUITests
4 | //
5 | // Created by ROBERA GELETA on 5/25/20.
6 | // Copyright © 2020 ROBERA GELETA. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class RGStackUITests: XCTestCase {
12 |
13 | override func setUpWithError() throws {
14 | // Put setup code here. This method is called before the invocation of each test method in the class.
15 |
16 | // In UI tests it is usually best to stop immediately when a failure occurs.
17 | continueAfterFailure = false
18 |
19 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
20 | }
21 |
22 | override func tearDownWithError() throws {
23 | // Put teardown code here. This method is called after the invocation of each test method in the class.
24 | }
25 |
26 | func testExample() throws {
27 | // UI tests must launch the application that they test.
28 | let app = XCUIApplication()
29 | app.launch()
30 |
31 | // Use recording to get started writing UI tests.
32 | // Use XCTAssert and related functions to verify your tests produce the correct results.
33 | }
34 |
35 | func testLaunchPerformance() throws {
36 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) {
37 | // This measures how long it takes to launch your application.
38 | measure(metrics: [XCTOSSignpostMetric.applicationLaunch]) {
39 | XCUIApplication().launch()
40 | }
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/RGStack/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/RGStack/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/RGStack/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // RGStack
4 | //
5 | // Created by ROBERA GELETA on 5/25/20.
6 | // Copyright © 2020 ROBERA GELETA. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | enum Test {
12 | static var testDemos: [DemoCard.Demo] {
13 | return (0 ... 10).map { DemoCard.Demo(color: Test.colors[$0 % Test.colors.count], text: "\($0)") }
14 | }
15 | static let colors: [Color] = [.blue, .red, .orange, .green, .yellow]
16 | static let width: CGFloat = 320
17 | static let cardSize = CGSize(width: Test.width, height: Test.width * 1.5)
18 | }
19 |
20 | struct DemoCard: View {
21 | struct Demo {
22 | let color: Color
23 | let text: String
24 | }
25 |
26 | let demo: Demo
27 |
28 | var body: some View {
29 | ZStack {
30 | RoundedRectangle(cornerRadius: 27, style: .continuous)
31 | .fill(demo.color)
32 | .cornerRadius(22)
33 | .shadow(radius: 5)
34 | Text(demo.text)
35 | .font(.largeTitle)
36 | .foregroundColor(.white)
37 | }
38 | }
39 | }
40 |
41 | extension DemoCard: ConfigurableCard {
42 | static func new(data: Demo?) -> DemoCard {
43 | guard let demoValue = data else { return DemoCard(demo: .default) }
44 | return DemoCard(demo: demoValue)
45 | }
46 | }
47 |
48 | extension DemoCard.Demo: DefaultValue {
49 | static var `default`: DemoCard.Demo {
50 | return .init(color: .black, text: "*E*")
51 | }
52 | }
53 |
54 | struct ContentView: View {
55 | var body: some View {
56 | ContentView_Previews.previews
57 | }
58 | }
59 |
60 | struct ContentView_Previews: PreviewProvider {
61 | static var previews: some View {
62 | VStack {
63 | Text("Headline")
64 | .font(.headline)
65 | RGStack(data: Test.testDemos,
66 | size: Test.cardSize,
67 | gapDistance: 30,
68 | minScaleForBackCard: 0.8)
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/RGStack/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | $(MARKETING_VERSION)
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 | UISceneConfigurations
28 |
29 | UIWindowSceneSessionRoleApplication
30 |
31 |
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneDelegateClassName
35 | $(PRODUCT_MODULE_NAME).SceneDelegate
36 |
37 |
38 |
39 |
40 | UILaunchStoryboardName
41 | LaunchScreen
42 | UIRequiredDeviceCapabilities
43 |
44 | armv7
45 |
46 | UISupportedInterfaceOrientations
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationLandscapeLeft
50 | UIInterfaceOrientationLandscapeRight
51 |
52 | UISupportedInterfaceOrientations~ipad
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationPortraitUpsideDown
56 | UIInterfaceOrientationLandscapeLeft
57 | UIInterfaceOrientationLandscapeRight
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/RGStack/Helpers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Helpers.swift
3 | // UI
4 | //
5 | // Created by ROBERA GELETA on 4/23/20.
6 | // Copyright © 2020 ROBERA GELETA. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | // swiftlint:disable identifier_name
11 | extension Array {
12 | func mapIndex(_ closure: (Int, Element) -> T) -> [T] {
13 | var mapped: [T] = []
14 | for (index, item) in enumerated() {
15 | mapped.append(closure(index, item))
16 | }
17 | return mapped
18 | }
19 |
20 | func rightShift() -> Array {
21 | var new = self
22 | guard let lastElement = last else { return [] }
23 | for index in indices where index > 0 {
24 | new[index] = self[index - 1]
25 | }
26 | new[0] = lastElement
27 | return new
28 | }
29 |
30 | func leftShift() -> Array {
31 | var new = self
32 | guard let firstElement = first else { return [] }
33 | for index in indices where index - 1 >= 0 {
34 | let toIndex = index - 1
35 | new[toIndex] = self[index]
36 | }
37 | new[count - 1] = firstElement
38 | return new
39 | }
40 |
41 | func get(index: Int) -> Element? {
42 | guard
43 | index >= 0,
44 | index <= (count - 1) else { return nil }
45 | return self[index]
46 | }
47 | }
48 |
49 | extension CGFloat {
50 | static func interpolate(from: CGFloat, to: CGFloat, progress: CGFloat) -> CGFloat {
51 | let diff = to - from
52 | guard progress != 0 else { return from }
53 | return (diff * progress) + from
54 | }
55 |
56 | func interpolate(to: CGFloat, progress: CGFloat) -> CGFloat {
57 | return CGFloat.interpolate(from: self, to: to, progress: progress)
58 | }
59 | }
60 |
61 | extension Double {
62 | func interpolate(to: Double, progress: Double) -> Double {
63 | let diff = to - self
64 | guard progress != 0 else { return self }
65 | return (diff * progress) + self
66 | }
67 | }
68 |
69 | extension CGPoint {
70 | func interpolate(to: CGPoint, progress: CGFloat) -> CGPoint {
71 | return CGPoint(x: x.interpolate(to: to.x, progress: progress),
72 | y: y.interpolate(to: to.y, progress: progress))
73 | }
74 | }
75 |
76 | extension CGSize {
77 | func interpolate(to: CGSize, progress: CGFloat) -> CGSize {
78 | return CGSize(width: width.interpolate(to: to.width, progress: progress),
79 | height: height.interpolate(to: to.height, progress: progress))
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/RGStack/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // RGStack
4 | //
5 | // Created by ROBERA GELETA on 5/25/20.
6 | // Copyright © 2020 ROBERA GELETA. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SwiftUI
11 |
12 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
18 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
19 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
20 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
21 |
22 | // Create the SwiftUI view that provides the window contents.
23 | let contentView = ContentView()
24 |
25 | // Use a UIHostingController as window root view controller.
26 | if let windowScene = scene as? UIWindowScene {
27 | let window = UIWindow(windowScene: windowScene)
28 | window.rootViewController = UIHostingController(rootView: contentView)
29 | self.window = window
30 | window.makeKeyAndVisible()
31 | }
32 | }
33 |
34 | func sceneDidDisconnect(_ scene: UIScene) {
35 | // Called as the scene is being released by the system.
36 | // This occurs shortly after the scene enters the background, or when its session is discarded.
37 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
38 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
39 | }
40 |
41 | func sceneDidBecomeActive(_ scene: UIScene) {
42 | // Called when the scene has moved from an inactive state to an active state.
43 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
44 | }
45 |
46 | func sceneWillResignActive(_ scene: UIScene) {
47 | // Called when the scene will move from an active state to an inactive state.
48 | // This may occur due to temporary interruptions (ex. an incoming phone call).
49 | }
50 |
51 | func sceneWillEnterForeground(_ scene: UIScene) {
52 | // Called as the scene transitions from the background to the foreground.
53 | // Use this method to undo the changes made on entering the background.
54 | }
55 |
56 | func sceneDidEnterBackground(_ scene: UIScene) {
57 | // Called as the scene transitions from the foreground to the background.
58 | // Use this method to save data, release shared resources, and store enough scene-specific state information
59 | // to restore the scene back to its current state.
60 | }
61 |
62 |
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/RGStack/RGStack.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RGStack.swift
3 | // UI
4 | //
5 | // Created by ROBERA GELETA on 4/15/20.
6 | // Copyright © 2020 ROBERA GELETA. All rights reserved.
7 | //
8 | //swiftlint:disable file_length
9 |
10 | import SwiftUI
11 | import UIKit
12 |
13 | public protocol DefaultValue {
14 | static var `default`: Self { get }
15 | }
16 |
17 | public protocol ConfigurableCard: View {
18 | associatedtype Data: DefaultValue
19 | static func new(data: Data?) -> Self
20 | }
21 |
22 | public enum CardMachine {
23 | public enum Position {
24 | case back, front, bottom, off
25 | }
26 |
27 | public struct Layout {
28 | let size: CGSize
29 | let offset: CGPoint
30 | let scale: CGFloat
31 | let zIndex: Double
32 | let opacity: Double
33 | var animated: Bool = false
34 | }
35 |
36 | public struct CardInfo {
37 | let size: CGSize
38 | let gapDistance: CGFloat
39 | let minScaleForBackCard: CGFloat
40 | let visibleFractionOfBottomCard: CGFloat
41 | }
42 |
43 | public enum Movement {
44 | case forward(CGFloat)
45 | case backward(CGFloat)
46 | case none
47 |
48 | init(_ value: CGFloat) {
49 | if value > 0 {
50 | self = .forward(value)
51 | } else if value < 0 {
52 | self = .backward(value.magnitude)
53 | } else {
54 | self = .none
55 | }
56 | }
57 |
58 | var isForward: Bool {
59 | if case .forward = self { return true }
60 | return false
61 | }
62 |
63 | var isBackward: Bool {
64 | if case .backward = self { return true }
65 | return false
66 | }
67 |
68 | var isDragging: Bool {
69 | switch self {
70 | case .none:
71 | return false
72 | case .backward, .forward:
73 | return true
74 | }
75 | }
76 | }
77 |
78 | public enum Direction {
79 | case next, back
80 | }
81 | }
82 |
83 | public struct RGStack: View {
84 |
85 | init(data: [CardView.Data], cardInfo: CardMachine.CardInfo) {
86 | self.data = data
87 | self.cardInfo = cardInfo
88 | }
89 |
90 | private var data: [CardView.Data]
91 | var cardInfo: CardMachine.CardInfo
92 | var size: CGSize { return cardInfo.size }
93 |
94 | // MARK: Constants
95 | let snapThresholdMangnitude: CGFloat = 0.2
96 | let index0Positions: [CardMachine.Position] = [.front, .bottom, .off, .off]
97 | let index1Positions: [CardMachine.Position] = [.back, .front, .bottom, .off]
98 |
99 | private var currentPositons: [CardMachine.Position] { return positions.current }
100 | private var previousPositions: [CardMachine.Position] {
101 | let lastMovement: CardMachine.Movement = .init(previousDrag)
102 | if positions.last == positions.current {
103 | return currentPositons.map { $0.position(for: lastMovement) }
104 | } else {
105 | return positions.last
106 | }
107 | }
108 |
109 | // MARK: State
110 | @State private var positions: (last: [CardMachine.Position], current: [CardMachine.Position]) =
111 | ([.front, .bottom, .off, .off], [.front, .bottom, .off, .off])
112 | @State private var index: Int = 0
113 | @State private var drags: (last: CGFloat, current: CGFloat) = (0.0, 0.0)
114 |
115 | var drag: CGFloat { return drags.current }
116 | var previousDrag: CGFloat { return drags.last }
117 |
118 | func currentConfiguration() -> RGStackConfiguration {
119 | return configuration(with: .init(drag),
120 | currentIndex: index,
121 | positions: currentPositons,
122 | previousPositions: previousPositions)
123 | }
124 |
125 | func variance(from translation: CGSize) -> CGFloat {
126 | return -1 * (translation.height / (size.height + cardInfo.gapDistance))
127 | }
128 |
129 | private func stackSize(style: CardMachine.CardInfo) -> CGSize {
130 | let cardSize = style.size
131 | let totalHeight = ((1 + style.visibleFractionOfBottomCard) * cardSize.height) + style.gapDistance
132 | return CGSize(width: cardSize.width,
133 | height: totalHeight)
134 | }
135 |
136 | public var body: some View {
137 | let configuration = currentConfiguration()
138 | return ZStack {
139 | ForEach(0...3, id: \.self) {
140 | CardView.new(data: configuration[$0].data)
141 | .apply(layout: configuration[$0].layout)
142 | }
143 | }
144 | .frame(width: stackSize(style: cardInfo).width, height: stackSize(style: cardInfo).height, alignment: .top)
145 | .on(dragged: { value in
146 | self.set(drag: self.variance(from: value.translation))
147 | }, ended: { value in
148 | self.endedDrag(translation: value.translation, threshold: self.snapThresholdMangnitude)
149 | })
150 | .onAppear {
151 | self.set(drag: 0)
152 | }
153 | }
154 | }
155 |
156 | // MARK: Moving to the next card and going back to the preivous card
157 |
158 | public extension RGStack {
159 |
160 | func move(to direction: CardMachine.Direction) {
161 | let newIndex = direction == .next ? index + 1 : index - 1
162 | guard let newPositions = move(to: direction, currentIndex: index, currentPositions: currentPositons) else {
163 | set(drag: 0)
164 | return
165 | }
166 | index = newIndex
167 | set(drag: 0)
168 | set(positions: newPositions)
169 | }
170 |
171 | private func move(to direction: CardMachine.Direction,
172 | currentIndex: Int,
173 | currentPositions: [CardMachine.Position]) -> [CardMachine.Position]? {
174 | let newIndex = direction == .next ? currentIndex + 1 : currentIndex - 1
175 | guard
176 | newIndex >= 0,
177 | newIndex < data.count
178 | else { return nil }
179 | return updatedPositions(for: newIndex, direction: direction, previous: currentPositions)
180 | }
181 |
182 | private func updatedPositions(for index: Int,
183 | direction: CardMachine.Direction,
184 | previous: [CardMachine.Position]) -> [CardMachine.Position] {
185 | switch index {
186 | case 0:
187 | return index0Positions
188 | case 1:
189 | return index1Positions
190 | default:
191 | return updated(positions: previous, in: direction)
192 | }
193 | }
194 |
195 | private func updated(positions: [CardMachine.Position],
196 | in direction: CardMachine.Direction) -> [CardMachine.Position] {
197 | return direction == .next ? positions.rightShift() : positions.leftShift()
198 | }
199 |
200 | private func set(positions new: [CardMachine.Position]) {
201 | positions = (last: positions.current, current: new)
202 | }
203 |
204 | private func set(drag: CGFloat) {
205 | drags = (last: drags.current, current: drag)
206 | set(positions: currentPositons)
207 | }
208 | }
209 |
210 | // MARK: Buildling Configurations
211 |
212 | public extension RGStack {
213 | internal func configuration(with movement: CardMachine.Movement,
214 | currentIndex: Int,
215 | positions: [CardMachine.Position],
216 | previousPositions: [CardMachine.Position]) -> RGStackConfiguration {
217 | return buildConfiguration(from: positions,
218 | previousPositions: previousPositions,
219 | data: data,
220 | currentIndex: currentIndex,
221 | movement: movement)
222 | }
223 |
224 | private func dataIndex(with currentIndex: Int, for position: CardMachine.Position) -> Int {
225 | switch position {
226 | case .back:
227 | return currentIndex - 1
228 | case .front:
229 | return currentIndex
230 | case .bottom:
231 | return currentIndex + 1
232 | case .off:
233 | return currentIndex + 2
234 | }
235 | }
236 |
237 | private func buildConfiguration(
238 | from positions: [CardMachine.Position],
239 | previousPositions: [CardMachine.Position],
240 | data: [CardView.Data],
241 | currentIndex: Int,
242 | movement: CardMachine.Movement
243 | ) -> RGStackConfiguration {
244 | let isLastCard = currentIndex == (data.count - 1)
245 | let isFirstCard = currentIndex == 0
246 |
247 | return positions.mapIndex { (positionIndex, position) -> CardConfiguration in
248 | let index = self.dataIndex(with: currentIndex, for: position)
249 | var config = CardConfiguration(position,
250 | data: data.get(index: index),
251 | movement: movement,
252 | cardInfo: self.cardInfo)
253 |
254 | let previousPosition = previousPositions[positionIndex]
255 | config.layout.animated = movement.isDragging ?
256 | false :
257 | previousPosition.shouldAnimateTransition(to: position)
258 |
259 | if isLastCard {
260 | if position == .bottom || position == .off {
261 | config.set(size: .zero)
262 | config.layout.animated = false
263 | } else if movement.isForward && position == .back {
264 | config.set(size: .zero)
265 | config.layout.animated = false
266 | }
267 | } else if isFirstCard, position == .off, movement.isBackward {
268 | config.set(size: .zero)
269 | config.layout.animated = false
270 | }
271 |
272 | return config
273 | }
274 | }
275 | }
276 |
277 | // MARK: Handling the Ending of the Dragging Gesture
278 |
279 | public extension RGStack {
280 | private func endedDrag(translation: CGSize, threshold: CGFloat) {
281 | let endMove: CardMachine.Movement = .init(variance(from: translation))
282 | switch endMove {
283 | case .none:
284 | set(drag: 0)
285 | case let .backward(progress):
286 | reconfigurePostInteraction(progress: progress, direction: .back, threshold: threshold)
287 | case let .forward(progress):
288 | reconfigurePostInteraction(progress: progress, direction: .next, threshold: threshold)
289 | }
290 | }
291 |
292 | private func reconfigurePostInteraction(progress: CGFloat, direction: CardMachine.Direction, threshold: CGFloat) {
293 | if progress > threshold {
294 | UIImpactFeedbackGenerator(style: .medium).impactOccurred()
295 | move(to: direction)
296 | } else {
297 | set(drag: 0)
298 | }
299 | }
300 | }
301 |
302 | // MARK: Card Configuration
303 |
304 | typealias RGStackConfiguration = [CardConfiguration]
305 |
306 | struct CardConfiguration {
307 | var layout: CardMachine.Layout
308 | var data: Data?
309 |
310 | init(_ position: CardMachine.Position,
311 | data: Data?,
312 | movement: CardMachine.Movement,
313 | cardInfo info: CardMachine.CardInfo) {
314 | self.layout = .init(position, movement: movement, cardInfo: info)
315 | self.data = data
316 | }
317 |
318 | mutating func set(size new: CGSize) {
319 | layout = CardMachine.Layout(size: new,
320 | offset: layout.offset,
321 | scale: layout.scale,
322 | zIndex: layout.zIndex,
323 | opacity: layout.opacity)
324 | }
325 | }
326 |
327 | // MARK: Layout Initializer
328 |
329 | public extension CardMachine.Layout {
330 |
331 | init(_ position: CardMachine.Position, cardInfo info: CardMachine.CardInfo) {
332 | let cardSize = info.size
333 | switch position {
334 | case .back:
335 | self = .init(size: cardSize,
336 | offset: .zero,
337 | scale: info.minScaleForBackCard,
338 | zIndex: 1,
339 | opacity: 1)
340 | case .bottom:
341 | self = .init(size: cardSize,
342 | offset: CGPoint(x: 0,
343 | y: cardSize.height + info.gapDistance),
344 | scale: 1,
345 | zIndex: 300,
346 | opacity: 1)
347 | case .front:
348 | self = .init(size: cardSize,
349 | offset: .zero,
350 | scale: 1,
351 | zIndex: 200,
352 | opacity: 1)
353 | case .off:
354 | self = .init(size: cardSize,
355 | offset: CGPoint(x: 0, y: cardSize.height * 2.0),
356 | scale: 1,
357 | zIndex: 0,
358 | opacity: 1)
359 | }
360 | }
361 |
362 | init(_ position: CardMachine.Position, movement: CardMachine.Movement, cardInfo info: CardMachine.CardInfo) {
363 | let currentLayout: CardMachine.Layout = .init(position, cardInfo: info)
364 | switch movement {
365 | case .none:
366 | self = currentLayout
367 | case let .backward(progress):
368 | self = currentLayout.interpolate(
369 | to: .init(position.position(for: movement), cardInfo: info),
370 | progerss: position.previousTransition.interpolated ? progress : (progress > 0 ? 1 : 0)
371 | )
372 | case let .forward(progress):
373 | self = currentLayout.interpolate(
374 | to: .init(position.position(for: movement), cardInfo: info),
375 | progerss: position.nextTransition.interpolated ? progress : (progress > 0 ? 1 : 0)
376 | )
377 | }
378 | }
379 | }
380 |
381 | // MARK: Position Transition Specification
382 |
383 | public extension CardMachine.Position {
384 |
385 | struct Transition {
386 | let position: CardMachine.Position
387 | var interpolated: Bool = true
388 | }
389 |
390 | func shouldAnimateTransition(to position: CardMachine.Position) -> Bool {
391 | let next = transitionNeighbours.next.position
392 | let previous = transitionNeighbours.previous.position
393 | switch position {
394 | case next:
395 | return transitionNeighbours.next.interpolated
396 | case previous:
397 | return transitionNeighbours.previous.interpolated
398 | default:
399 | return false
400 | }
401 | }
402 |
403 | func position(for movement: CardMachine.Movement) -> CardMachine.Position {
404 | guard let transit = transition(for: movement) else { return self }
405 | return transit.position
406 | }
407 |
408 | func transition(for movement: CardMachine.Movement) -> Transition? {
409 | switch movement {
410 | case .none:
411 | return nil
412 | case .forward:
413 | return nextTransition
414 | case .backward:
415 | return previousTransition
416 | }
417 | }
418 |
419 | var nextTransition: Transition { return transitionNeighbours.next }
420 | var previousTransition: Transition { return transitionNeighbours.previous }
421 | var nextPosition: CardMachine.Position { return nextTransition.position }
422 | var previousPosition: CardMachine.Position { return previousTransition.position }
423 |
424 | var transitionNeighbours: (previous: Transition, next: Transition) {
425 | switch self {
426 | case .back:
427 | return (previous: Transition(position: .front), next: Transition(position: .off, interpolated: false))
428 | case .front:
429 | return (previous: Transition(position: .bottom), next: Transition(position: .back))
430 | case .bottom:
431 | return (previous: Transition(position: .off), next: Transition(position: .front))
432 | case .off:
433 | return (previous: Transition(position: .back, interpolated: false), next: Transition(position: .bottom))
434 | }
435 | }
436 | }
437 |
438 | // MARK: View Extension
439 |
440 | public extension View {
441 | func apply(layout: CardMachine.Layout) -> some View {
442 | let animation: Animation? = layout.animated ?
443 | .interactiveSpring(response: 0.25, dampingFraction: 0.75, blendDuration: 0.95) :
444 | nil
445 | return frame(width: layout.size.width, height: layout.size.height)
446 | .scaleEffect(layout.scale)
447 | .offset(x: layout.offset.x, y: layout.offset.y)
448 | .zIndex(layout.zIndex)
449 | .opacity(layout.opacity)
450 | .animation(animation)
451 | }
452 |
453 | func on(dragged: @escaping (DragGesture.Value) -> Void,
454 | ended: @escaping (DragGesture.Value) -> Void) -> some View {
455 | return gesture(
456 | DragGesture().onChanged(dragged).onEnded(ended)
457 | )
458 | }
459 | }
460 |
461 | // MARK: Layout Interpolation
462 |
463 | public extension CardMachine.Layout {
464 | // swiftlint:disable identifier_name
465 | func interpolate(to: CardMachine.Layout,
466 | progerss: CGFloat) -> CardMachine.Layout {
467 | return CardMachine.Layout(size: size.interpolate(to: to.size, progress: progerss),
468 | offset: offset.interpolate(to: to.offset, progress: progerss),
469 | scale: scale.interpolate(to: to.scale, progress: progerss),
470 | zIndex: zIndex.interpolate(to: to.zIndex, progress: Double(progerss)),
471 | opacity: opacity.interpolate(to: to.opacity, progress: Double(progerss)))
472 | }
473 | }
474 |
475 | // MARK: Convenience Initializer
476 |
477 | public extension RGStack {
478 | init(data: [CardView.Data],
479 | size: CGSize,
480 | gapDistance: CGFloat = 50,
481 | minScaleForBackCard: CGFloat = 0.8,
482 | visibleFractionOfBottomCard: CGFloat = 0.2) {
483 | self.data = data
484 | cardInfo = CardMachine.CardInfo(size: size,
485 | gapDistance: gapDistance,
486 | minScaleForBackCard: minScaleForBackCard,
487 | visibleFractionOfBottomCard: visibleFractionOfBottomCard)
488 | }
489 | }
490 |
491 |
--------------------------------------------------------------------------------
/RGStack.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | A12FDABA247C93300099511F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A12FDAB9247C93300099511F /* AppDelegate.swift */; };
11 | A12FDABC247C93300099511F /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A12FDABB247C93300099511F /* SceneDelegate.swift */; };
12 | A12FDABE247C93300099511F /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A12FDABD247C93300099511F /* ContentView.swift */; };
13 | A12FDAC0247C93330099511F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A12FDABF247C93330099511F /* Assets.xcassets */; };
14 | A12FDAC3247C93330099511F /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A12FDAC2247C93330099511F /* Preview Assets.xcassets */; };
15 | A12FDAC6247C93330099511F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A12FDAC4247C93330099511F /* LaunchScreen.storyboard */; };
16 | A12FDAD1247C93330099511F /* RGStackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A12FDAD0247C93330099511F /* RGStackTests.swift */; };
17 | A12FDADC247C93330099511F /* RGStackUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A12FDADB247C93330099511F /* RGStackUITests.swift */; };
18 | A12FDAEB247C93530099511F /* RGStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = A12FDAE9247C93530099511F /* RGStack.swift */; };
19 | A12FDAEC247C93530099511F /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A12FDAEA247C93530099511F /* Helpers.swift */; };
20 | /* End PBXBuildFile section */
21 |
22 | /* Begin PBXContainerItemProxy section */
23 | A12FDACD247C93330099511F /* PBXContainerItemProxy */ = {
24 | isa = PBXContainerItemProxy;
25 | containerPortal = A12FDAAE247C93300099511F /* Project object */;
26 | proxyType = 1;
27 | remoteGlobalIDString = A12FDAB5247C93300099511F;
28 | remoteInfo = RGStack;
29 | };
30 | A12FDAD8247C93330099511F /* PBXContainerItemProxy */ = {
31 | isa = PBXContainerItemProxy;
32 | containerPortal = A12FDAAE247C93300099511F /* Project object */;
33 | proxyType = 1;
34 | remoteGlobalIDString = A12FDAB5247C93300099511F;
35 | remoteInfo = RGStack;
36 | };
37 | /* End PBXContainerItemProxy section */
38 |
39 | /* Begin PBXFileReference section */
40 | A12FDAB6247C93300099511F /* RGStack.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RGStack.app; sourceTree = BUILT_PRODUCTS_DIR; };
41 | A12FDAB9247C93300099511F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
42 | A12FDABB247C93300099511F /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
43 | A12FDABD247C93300099511F /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
44 | A12FDABF247C93330099511F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
45 | A12FDAC2247C93330099511F /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
46 | A12FDAC5247C93330099511F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
47 | A12FDAC7247C93330099511F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
48 | A12FDACC247C93330099511F /* RGStackTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RGStackTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
49 | A12FDAD0247C93330099511F /* RGStackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RGStackTests.swift; sourceTree = ""; };
50 | A12FDAD2247C93330099511F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
51 | A12FDAD7247C93330099511F /* RGStackUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RGStackUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
52 | A12FDADB247C93330099511F /* RGStackUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RGStackUITests.swift; sourceTree = ""; };
53 | A12FDADD247C93330099511F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
54 | A12FDAE9247C93530099511F /* RGStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RGStack.swift; sourceTree = ""; };
55 | A12FDAEA247C93530099511F /* Helpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = ""; };
56 | /* End PBXFileReference section */
57 |
58 | /* Begin PBXFrameworksBuildPhase section */
59 | A12FDAB3247C93300099511F /* Frameworks */ = {
60 | isa = PBXFrameworksBuildPhase;
61 | buildActionMask = 2147483647;
62 | files = (
63 | );
64 | runOnlyForDeploymentPostprocessing = 0;
65 | };
66 | A12FDAC9247C93330099511F /* Frameworks */ = {
67 | isa = PBXFrameworksBuildPhase;
68 | buildActionMask = 2147483647;
69 | files = (
70 | );
71 | runOnlyForDeploymentPostprocessing = 0;
72 | };
73 | A12FDAD4247C93330099511F /* Frameworks */ = {
74 | isa = PBXFrameworksBuildPhase;
75 | buildActionMask = 2147483647;
76 | files = (
77 | );
78 | runOnlyForDeploymentPostprocessing = 0;
79 | };
80 | /* End PBXFrameworksBuildPhase section */
81 |
82 | /* Begin PBXGroup section */
83 | A12FDAAD247C93300099511F = {
84 | isa = PBXGroup;
85 | children = (
86 | A12FDAB8247C93300099511F /* RGStack */,
87 | A12FDACF247C93330099511F /* RGStackTests */,
88 | A12FDADA247C93330099511F /* RGStackUITests */,
89 | A12FDAB7247C93300099511F /* Products */,
90 | );
91 | sourceTree = "";
92 | };
93 | A12FDAB7247C93300099511F /* Products */ = {
94 | isa = PBXGroup;
95 | children = (
96 | A12FDAB6247C93300099511F /* RGStack.app */,
97 | A12FDACC247C93330099511F /* RGStackTests.xctest */,
98 | A12FDAD7247C93330099511F /* RGStackUITests.xctest */,
99 | );
100 | name = Products;
101 | sourceTree = "";
102 | };
103 | A12FDAB8247C93300099511F /* RGStack */ = {
104 | isa = PBXGroup;
105 | children = (
106 | A12FDAB9247C93300099511F /* AppDelegate.swift */,
107 | A12FDABB247C93300099511F /* SceneDelegate.swift */,
108 | A12FDABD247C93300099511F /* ContentView.swift */,
109 | A12FDAE9247C93530099511F /* RGStack.swift */,
110 | A12FDAEA247C93530099511F /* Helpers.swift */,
111 | A12FDABF247C93330099511F /* Assets.xcassets */,
112 | A12FDAC4247C93330099511F /* LaunchScreen.storyboard */,
113 | A12FDAC7247C93330099511F /* Info.plist */,
114 | A12FDAC1247C93330099511F /* Preview Content */,
115 | );
116 | path = RGStack;
117 | sourceTree = "";
118 | };
119 | A12FDAC1247C93330099511F /* Preview Content */ = {
120 | isa = PBXGroup;
121 | children = (
122 | A12FDAC2247C93330099511F /* Preview Assets.xcassets */,
123 | );
124 | path = "Preview Content";
125 | sourceTree = "";
126 | };
127 | A12FDACF247C93330099511F /* RGStackTests */ = {
128 | isa = PBXGroup;
129 | children = (
130 | A12FDAD0247C93330099511F /* RGStackTests.swift */,
131 | A12FDAD2247C93330099511F /* Info.plist */,
132 | );
133 | path = RGStackTests;
134 | sourceTree = "";
135 | };
136 | A12FDADA247C93330099511F /* RGStackUITests */ = {
137 | isa = PBXGroup;
138 | children = (
139 | A12FDADB247C93330099511F /* RGStackUITests.swift */,
140 | A12FDADD247C93330099511F /* Info.plist */,
141 | );
142 | path = RGStackUITests;
143 | sourceTree = "";
144 | };
145 | /* End PBXGroup section */
146 |
147 | /* Begin PBXNativeTarget section */
148 | A12FDAB5247C93300099511F /* RGStack */ = {
149 | isa = PBXNativeTarget;
150 | buildConfigurationList = A12FDAE0247C93330099511F /* Build configuration list for PBXNativeTarget "RGStack" */;
151 | buildPhases = (
152 | A12FDAB2247C93300099511F /* Sources */,
153 | A12FDAB3247C93300099511F /* Frameworks */,
154 | A12FDAB4247C93300099511F /* Resources */,
155 | );
156 | buildRules = (
157 | );
158 | dependencies = (
159 | );
160 | name = RGStack;
161 | productName = RGStack;
162 | productReference = A12FDAB6247C93300099511F /* RGStack.app */;
163 | productType = "com.apple.product-type.application";
164 | };
165 | A12FDACB247C93330099511F /* RGStackTests */ = {
166 | isa = PBXNativeTarget;
167 | buildConfigurationList = A12FDAE3247C93330099511F /* Build configuration list for PBXNativeTarget "RGStackTests" */;
168 | buildPhases = (
169 | A12FDAC8247C93330099511F /* Sources */,
170 | A12FDAC9247C93330099511F /* Frameworks */,
171 | A12FDACA247C93330099511F /* Resources */,
172 | );
173 | buildRules = (
174 | );
175 | dependencies = (
176 | A12FDACE247C93330099511F /* PBXTargetDependency */,
177 | );
178 | name = RGStackTests;
179 | productName = RGStackTests;
180 | productReference = A12FDACC247C93330099511F /* RGStackTests.xctest */;
181 | productType = "com.apple.product-type.bundle.unit-test";
182 | };
183 | A12FDAD6247C93330099511F /* RGStackUITests */ = {
184 | isa = PBXNativeTarget;
185 | buildConfigurationList = A12FDAE6247C93330099511F /* Build configuration list for PBXNativeTarget "RGStackUITests" */;
186 | buildPhases = (
187 | A12FDAD3247C93330099511F /* Sources */,
188 | A12FDAD4247C93330099511F /* Frameworks */,
189 | A12FDAD5247C93330099511F /* Resources */,
190 | );
191 | buildRules = (
192 | );
193 | dependencies = (
194 | A12FDAD9247C93330099511F /* PBXTargetDependency */,
195 | );
196 | name = RGStackUITests;
197 | productName = RGStackUITests;
198 | productReference = A12FDAD7247C93330099511F /* RGStackUITests.xctest */;
199 | productType = "com.apple.product-type.bundle.ui-testing";
200 | };
201 | /* End PBXNativeTarget section */
202 |
203 | /* Begin PBXProject section */
204 | A12FDAAE247C93300099511F /* Project object */ = {
205 | isa = PBXProject;
206 | attributes = {
207 | LastSwiftUpdateCheck = 1150;
208 | LastUpgradeCheck = 1150;
209 | ORGANIZATIONNAME = "ROBERA GELETA";
210 | TargetAttributes = {
211 | A12FDAB5247C93300099511F = {
212 | CreatedOnToolsVersion = 11.5;
213 | };
214 | A12FDACB247C93330099511F = {
215 | CreatedOnToolsVersion = 11.5;
216 | TestTargetID = A12FDAB5247C93300099511F;
217 | };
218 | A12FDAD6247C93330099511F = {
219 | CreatedOnToolsVersion = 11.5;
220 | TestTargetID = A12FDAB5247C93300099511F;
221 | };
222 | };
223 | };
224 | buildConfigurationList = A12FDAB1247C93300099511F /* Build configuration list for PBXProject "RGStack" */;
225 | compatibilityVersion = "Xcode 9.3";
226 | developmentRegion = en;
227 | hasScannedForEncodings = 0;
228 | knownRegions = (
229 | en,
230 | Base,
231 | );
232 | mainGroup = A12FDAAD247C93300099511F;
233 | productRefGroup = A12FDAB7247C93300099511F /* Products */;
234 | projectDirPath = "";
235 | projectRoot = "";
236 | targets = (
237 | A12FDAB5247C93300099511F /* RGStack */,
238 | A12FDACB247C93330099511F /* RGStackTests */,
239 | A12FDAD6247C93330099511F /* RGStackUITests */,
240 | );
241 | };
242 | /* End PBXProject section */
243 |
244 | /* Begin PBXResourcesBuildPhase section */
245 | A12FDAB4247C93300099511F /* Resources */ = {
246 | isa = PBXResourcesBuildPhase;
247 | buildActionMask = 2147483647;
248 | files = (
249 | A12FDAC6247C93330099511F /* LaunchScreen.storyboard in Resources */,
250 | A12FDAC3247C93330099511F /* Preview Assets.xcassets in Resources */,
251 | A12FDAC0247C93330099511F /* Assets.xcassets in Resources */,
252 | );
253 | runOnlyForDeploymentPostprocessing = 0;
254 | };
255 | A12FDACA247C93330099511F /* Resources */ = {
256 | isa = PBXResourcesBuildPhase;
257 | buildActionMask = 2147483647;
258 | files = (
259 | );
260 | runOnlyForDeploymentPostprocessing = 0;
261 | };
262 | A12FDAD5247C93330099511F /* Resources */ = {
263 | isa = PBXResourcesBuildPhase;
264 | buildActionMask = 2147483647;
265 | files = (
266 | );
267 | runOnlyForDeploymentPostprocessing = 0;
268 | };
269 | /* End PBXResourcesBuildPhase section */
270 |
271 | /* Begin PBXSourcesBuildPhase section */
272 | A12FDAB2247C93300099511F /* Sources */ = {
273 | isa = PBXSourcesBuildPhase;
274 | buildActionMask = 2147483647;
275 | files = (
276 | A12FDABA247C93300099511F /* AppDelegate.swift in Sources */,
277 | A12FDABC247C93300099511F /* SceneDelegate.swift in Sources */,
278 | A12FDABE247C93300099511F /* ContentView.swift in Sources */,
279 | A12FDAEC247C93530099511F /* Helpers.swift in Sources */,
280 | A12FDAEB247C93530099511F /* RGStack.swift in Sources */,
281 | );
282 | runOnlyForDeploymentPostprocessing = 0;
283 | };
284 | A12FDAC8247C93330099511F /* Sources */ = {
285 | isa = PBXSourcesBuildPhase;
286 | buildActionMask = 2147483647;
287 | files = (
288 | A12FDAD1247C93330099511F /* RGStackTests.swift in Sources */,
289 | );
290 | runOnlyForDeploymentPostprocessing = 0;
291 | };
292 | A12FDAD3247C93330099511F /* Sources */ = {
293 | isa = PBXSourcesBuildPhase;
294 | buildActionMask = 2147483647;
295 | files = (
296 | A12FDADC247C93330099511F /* RGStackUITests.swift in Sources */,
297 | );
298 | runOnlyForDeploymentPostprocessing = 0;
299 | };
300 | /* End PBXSourcesBuildPhase section */
301 |
302 | /* Begin PBXTargetDependency section */
303 | A12FDACE247C93330099511F /* PBXTargetDependency */ = {
304 | isa = PBXTargetDependency;
305 | target = A12FDAB5247C93300099511F /* RGStack */;
306 | targetProxy = A12FDACD247C93330099511F /* PBXContainerItemProxy */;
307 | };
308 | A12FDAD9247C93330099511F /* PBXTargetDependency */ = {
309 | isa = PBXTargetDependency;
310 | target = A12FDAB5247C93300099511F /* RGStack */;
311 | targetProxy = A12FDAD8247C93330099511F /* PBXContainerItemProxy */;
312 | };
313 | /* End PBXTargetDependency section */
314 |
315 | /* Begin PBXVariantGroup section */
316 | A12FDAC4247C93330099511F /* LaunchScreen.storyboard */ = {
317 | isa = PBXVariantGroup;
318 | children = (
319 | A12FDAC5247C93330099511F /* Base */,
320 | );
321 | name = LaunchScreen.storyboard;
322 | sourceTree = "";
323 | };
324 | /* End PBXVariantGroup section */
325 |
326 | /* Begin XCBuildConfiguration section */
327 | A12FDADE247C93330099511F /* Debug */ = {
328 | isa = XCBuildConfiguration;
329 | buildSettings = {
330 | ALWAYS_SEARCH_USER_PATHS = NO;
331 | CLANG_ANALYZER_NONNULL = YES;
332 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
333 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
334 | CLANG_CXX_LIBRARY = "libc++";
335 | CLANG_ENABLE_MODULES = YES;
336 | CLANG_ENABLE_OBJC_ARC = YES;
337 | CLANG_ENABLE_OBJC_WEAK = YES;
338 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
339 | CLANG_WARN_BOOL_CONVERSION = YES;
340 | CLANG_WARN_COMMA = YES;
341 | CLANG_WARN_CONSTANT_CONVERSION = YES;
342 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
343 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
344 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
345 | CLANG_WARN_EMPTY_BODY = YES;
346 | CLANG_WARN_ENUM_CONVERSION = YES;
347 | CLANG_WARN_INFINITE_RECURSION = YES;
348 | CLANG_WARN_INT_CONVERSION = YES;
349 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
350 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
351 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
352 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
353 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
354 | CLANG_WARN_STRICT_PROTOTYPES = YES;
355 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
356 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
357 | CLANG_WARN_UNREACHABLE_CODE = YES;
358 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
359 | COPY_PHASE_STRIP = NO;
360 | DEBUG_INFORMATION_FORMAT = dwarf;
361 | ENABLE_STRICT_OBJC_MSGSEND = YES;
362 | ENABLE_TESTABILITY = YES;
363 | GCC_C_LANGUAGE_STANDARD = gnu11;
364 | GCC_DYNAMIC_NO_PIC = NO;
365 | GCC_NO_COMMON_BLOCKS = YES;
366 | GCC_OPTIMIZATION_LEVEL = 0;
367 | GCC_PREPROCESSOR_DEFINITIONS = (
368 | "DEBUG=1",
369 | "$(inherited)",
370 | );
371 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
372 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
373 | GCC_WARN_UNDECLARED_SELECTOR = YES;
374 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
375 | GCC_WARN_UNUSED_FUNCTION = YES;
376 | GCC_WARN_UNUSED_VARIABLE = YES;
377 | IPHONEOS_DEPLOYMENT_TARGET = 13.5;
378 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
379 | MTL_FAST_MATH = YES;
380 | ONLY_ACTIVE_ARCH = YES;
381 | SDKROOT = iphoneos;
382 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
383 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
384 | };
385 | name = Debug;
386 | };
387 | A12FDADF247C93330099511F /* Release */ = {
388 | isa = XCBuildConfiguration;
389 | buildSettings = {
390 | ALWAYS_SEARCH_USER_PATHS = NO;
391 | CLANG_ANALYZER_NONNULL = YES;
392 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
393 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
394 | CLANG_CXX_LIBRARY = "libc++";
395 | CLANG_ENABLE_MODULES = YES;
396 | CLANG_ENABLE_OBJC_ARC = YES;
397 | CLANG_ENABLE_OBJC_WEAK = YES;
398 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
399 | CLANG_WARN_BOOL_CONVERSION = YES;
400 | CLANG_WARN_COMMA = YES;
401 | CLANG_WARN_CONSTANT_CONVERSION = YES;
402 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
403 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
404 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
405 | CLANG_WARN_EMPTY_BODY = YES;
406 | CLANG_WARN_ENUM_CONVERSION = YES;
407 | CLANG_WARN_INFINITE_RECURSION = YES;
408 | CLANG_WARN_INT_CONVERSION = YES;
409 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
410 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
411 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
412 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
413 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
414 | CLANG_WARN_STRICT_PROTOTYPES = YES;
415 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
416 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
417 | CLANG_WARN_UNREACHABLE_CODE = YES;
418 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
419 | COPY_PHASE_STRIP = NO;
420 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
421 | ENABLE_NS_ASSERTIONS = NO;
422 | ENABLE_STRICT_OBJC_MSGSEND = YES;
423 | GCC_C_LANGUAGE_STANDARD = gnu11;
424 | GCC_NO_COMMON_BLOCKS = YES;
425 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
426 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
427 | GCC_WARN_UNDECLARED_SELECTOR = YES;
428 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
429 | GCC_WARN_UNUSED_FUNCTION = YES;
430 | GCC_WARN_UNUSED_VARIABLE = YES;
431 | IPHONEOS_DEPLOYMENT_TARGET = 13.5;
432 | MTL_ENABLE_DEBUG_INFO = NO;
433 | MTL_FAST_MATH = YES;
434 | SDKROOT = iphoneos;
435 | SWIFT_COMPILATION_MODE = wholemodule;
436 | SWIFT_OPTIMIZATION_LEVEL = "-O";
437 | VALIDATE_PRODUCT = YES;
438 | };
439 | name = Release;
440 | };
441 | A12FDAE1247C93330099511F /* Debug */ = {
442 | isa = XCBuildConfiguration;
443 | buildSettings = {
444 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
445 | CODE_SIGN_STYLE = Automatic;
446 | DEVELOPMENT_ASSET_PATHS = "\"RGStack/Preview Content\"";
447 | DEVELOPMENT_TEAM = FZY78ZK5D5;
448 | ENABLE_PREVIEWS = YES;
449 | INFOPLIST_FILE = RGStack/Info.plist;
450 | LD_RUNPATH_SEARCH_PATHS = (
451 | "$(inherited)",
452 | "@executable_path/Frameworks",
453 | );
454 | MARKETING_VERSION = 1.2;
455 | PRODUCT_BUNDLE_IDENTIFIER = com.enterwithboldnress.RGStack;
456 | PRODUCT_NAME = "$(TARGET_NAME)";
457 | SWIFT_VERSION = 5.0;
458 | TARGETED_DEVICE_FAMILY = "1,2";
459 | };
460 | name = Debug;
461 | };
462 | A12FDAE2247C93330099511F /* Release */ = {
463 | isa = XCBuildConfiguration;
464 | buildSettings = {
465 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
466 | CODE_SIGN_STYLE = Automatic;
467 | DEVELOPMENT_ASSET_PATHS = "\"RGStack/Preview Content\"";
468 | DEVELOPMENT_TEAM = FZY78ZK5D5;
469 | ENABLE_PREVIEWS = YES;
470 | INFOPLIST_FILE = RGStack/Info.plist;
471 | LD_RUNPATH_SEARCH_PATHS = (
472 | "$(inherited)",
473 | "@executable_path/Frameworks",
474 | );
475 | MARKETING_VERSION = 1.2;
476 | PRODUCT_BUNDLE_IDENTIFIER = com.enterwithboldnress.RGStack;
477 | PRODUCT_NAME = "$(TARGET_NAME)";
478 | SWIFT_VERSION = 5.0;
479 | TARGETED_DEVICE_FAMILY = "1,2";
480 | };
481 | name = Release;
482 | };
483 | A12FDAE4247C93330099511F /* Debug */ = {
484 | isa = XCBuildConfiguration;
485 | buildSettings = {
486 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
487 | BUNDLE_LOADER = "$(TEST_HOST)";
488 | CODE_SIGN_STYLE = Automatic;
489 | DEVELOPMENT_TEAM = FZY78ZK5D5;
490 | INFOPLIST_FILE = RGStackTests/Info.plist;
491 | IPHONEOS_DEPLOYMENT_TARGET = 13.5;
492 | LD_RUNPATH_SEARCH_PATHS = (
493 | "$(inherited)",
494 | "@executable_path/Frameworks",
495 | "@loader_path/Frameworks",
496 | );
497 | PRODUCT_BUNDLE_IDENTIFIER = com.enterwithboldnress.RGStackTests;
498 | PRODUCT_NAME = "$(TARGET_NAME)";
499 | SWIFT_VERSION = 5.0;
500 | TARGETED_DEVICE_FAMILY = "1,2";
501 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RGStack.app/RGStack";
502 | };
503 | name = Debug;
504 | };
505 | A12FDAE5247C93330099511F /* Release */ = {
506 | isa = XCBuildConfiguration;
507 | buildSettings = {
508 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
509 | BUNDLE_LOADER = "$(TEST_HOST)";
510 | CODE_SIGN_STYLE = Automatic;
511 | DEVELOPMENT_TEAM = FZY78ZK5D5;
512 | INFOPLIST_FILE = RGStackTests/Info.plist;
513 | IPHONEOS_DEPLOYMENT_TARGET = 13.5;
514 | LD_RUNPATH_SEARCH_PATHS = (
515 | "$(inherited)",
516 | "@executable_path/Frameworks",
517 | "@loader_path/Frameworks",
518 | );
519 | PRODUCT_BUNDLE_IDENTIFIER = com.enterwithboldnress.RGStackTests;
520 | PRODUCT_NAME = "$(TARGET_NAME)";
521 | SWIFT_VERSION = 5.0;
522 | TARGETED_DEVICE_FAMILY = "1,2";
523 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/RGStack.app/RGStack";
524 | };
525 | name = Release;
526 | };
527 | A12FDAE7247C93330099511F /* Debug */ = {
528 | isa = XCBuildConfiguration;
529 | buildSettings = {
530 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
531 | CODE_SIGN_STYLE = Automatic;
532 | DEVELOPMENT_TEAM = FZY78ZK5D5;
533 | INFOPLIST_FILE = RGStackUITests/Info.plist;
534 | LD_RUNPATH_SEARCH_PATHS = (
535 | "$(inherited)",
536 | "@executable_path/Frameworks",
537 | "@loader_path/Frameworks",
538 | );
539 | PRODUCT_BUNDLE_IDENTIFIER = com.enterwithboldnress.RGStackUITests;
540 | PRODUCT_NAME = "$(TARGET_NAME)";
541 | SWIFT_VERSION = 5.0;
542 | TARGETED_DEVICE_FAMILY = "1,2";
543 | TEST_TARGET_NAME = RGStack;
544 | };
545 | name = Debug;
546 | };
547 | A12FDAE8247C93330099511F /* Release */ = {
548 | isa = XCBuildConfiguration;
549 | buildSettings = {
550 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
551 | CODE_SIGN_STYLE = Automatic;
552 | DEVELOPMENT_TEAM = FZY78ZK5D5;
553 | INFOPLIST_FILE = RGStackUITests/Info.plist;
554 | LD_RUNPATH_SEARCH_PATHS = (
555 | "$(inherited)",
556 | "@executable_path/Frameworks",
557 | "@loader_path/Frameworks",
558 | );
559 | PRODUCT_BUNDLE_IDENTIFIER = com.enterwithboldnress.RGStackUITests;
560 | PRODUCT_NAME = "$(TARGET_NAME)";
561 | SWIFT_VERSION = 5.0;
562 | TARGETED_DEVICE_FAMILY = "1,2";
563 | TEST_TARGET_NAME = RGStack;
564 | };
565 | name = Release;
566 | };
567 | /* End XCBuildConfiguration section */
568 |
569 | /* Begin XCConfigurationList section */
570 | A12FDAB1247C93300099511F /* Build configuration list for PBXProject "RGStack" */ = {
571 | isa = XCConfigurationList;
572 | buildConfigurations = (
573 | A12FDADE247C93330099511F /* Debug */,
574 | A12FDADF247C93330099511F /* Release */,
575 | );
576 | defaultConfigurationIsVisible = 0;
577 | defaultConfigurationName = Release;
578 | };
579 | A12FDAE0247C93330099511F /* Build configuration list for PBXNativeTarget "RGStack" */ = {
580 | isa = XCConfigurationList;
581 | buildConfigurations = (
582 | A12FDAE1247C93330099511F /* Debug */,
583 | A12FDAE2247C93330099511F /* Release */,
584 | );
585 | defaultConfigurationIsVisible = 0;
586 | defaultConfigurationName = Release;
587 | };
588 | A12FDAE3247C93330099511F /* Build configuration list for PBXNativeTarget "RGStackTests" */ = {
589 | isa = XCConfigurationList;
590 | buildConfigurations = (
591 | A12FDAE4247C93330099511F /* Debug */,
592 | A12FDAE5247C93330099511F /* Release */,
593 | );
594 | defaultConfigurationIsVisible = 0;
595 | defaultConfigurationName = Release;
596 | };
597 | A12FDAE6247C93330099511F /* Build configuration list for PBXNativeTarget "RGStackUITests" */ = {
598 | isa = XCConfigurationList;
599 | buildConfigurations = (
600 | A12FDAE7247C93330099511F /* Debug */,
601 | A12FDAE8247C93330099511F /* Release */,
602 | );
603 | defaultConfigurationIsVisible = 0;
604 | defaultConfigurationName = Release;
605 | };
606 | /* End XCConfigurationList section */
607 | };
608 | rootObject = A12FDAAE247C93300099511F /* Project object */;
609 | }
610 |
--------------------------------------------------------------------------------