├── Carthage
└── .resolved.md5
├── Cartfile
├── AbsurdGitter
├── Assets.xcassets
│ ├── Contents.json
│ ├── chat.imageset
│ │ ├── chat.png
│ │ └── Contents.json
│ ├── avatar.imageset
│ │ ├── avatar.png
│ │ └── Contents.json
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── FrameworkInfo.plist
├── AppDelegate.swift
├── Base.lproj
│ └── LaunchScreen.storyboard
└── Info.plist
├── Cartfile.resolved
├── AbsurdGitter.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
└── project.pbxproj
├── README.md
├── API
├── GitterError.swift
├── API.h
├── Endpoints
│ ├── Message.swift
│ ├── OAuth.swift
│ ├── User.swift
│ └── Room.swift
├── GitterRequest.swift
└── GitterClient.swift
├── Views
├── Views.h
├── LoginViewController.swift
├── ProfileViewController.swift
├── RoomListViewController.swift
└── RoomViewController.swift
├── Client
├── Client.h
├── HTTPMethod.swift
├── LoggingClient.swift
├── Logger.swift
├── Request.swift
├── Result.swift
├── Utilities.swift
├── RequestParameters.swift
└── Client.swift
├── Binders
├── Binders.h
├── RoomNavigationController.swift
├── MainWindow.swift
├── MainTabBarController.swift
├── LoginViewController.swift
├── RoomViewController.swift
├── ProfileViewController.swift
├── Extensions.swift
└── RoomListViewController.swift
├── Entities
├── AccessToken.swift
├── Entities.h
├── User.swift
├── Message.swift
├── ApplicationError.swift
└── Room.swift
├── Services
├── Services.h
├── UserService.swift
├── RoomService.swift
├── MessageService.swift
├── UnauthenticatedSession.swift
├── AuthenticatedSession.swift
├── LoginService.swift
└── SessionManager.swift
├── .gitignore
└── LICENSE
/Carthage/.resolved.md5:
--------------------------------------------------------------------------------
1 | 505b1dba7130983b1476e9bf518477fd
--------------------------------------------------------------------------------
/Cartfile:
--------------------------------------------------------------------------------
1 | github "DeclarativeHub/ReactiveKit"
2 | github "DeclarativeHub/Bond"
3 | github "DeclarativeHub/Layoutless"
4 |
--------------------------------------------------------------------------------
/AbsurdGitter/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/AbsurdGitter/Assets.xcassets/chat.imageset/chat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeclarativeHub/AbsurdGitter/HEAD/AbsurdGitter/Assets.xcassets/chat.imageset/chat.png
--------------------------------------------------------------------------------
/AbsurdGitter/Assets.xcassets/avatar.imageset/avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DeclarativeHub/AbsurdGitter/HEAD/AbsurdGitter/Assets.xcassets/avatar.imageset/avatar.png
--------------------------------------------------------------------------------
/Cartfile.resolved:
--------------------------------------------------------------------------------
1 | github "DeclarativeHub/Bond" "7.7.1"
2 | github "DeclarativeHub/Layoutless" "0.4.2"
3 | github "DeclarativeHub/ReactiveKit" "v3.17.1"
4 | github "tonyarnold/Differ" "1.4.5"
5 |
--------------------------------------------------------------------------------
/AbsurdGitter.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/AbsurdGitter.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Absurd Gitter
2 |
3 | Absurd Gitter is a simple [Gitter](http://gitter.im) client app that demonstrates [The Binder Architecture](https://github.com/DeclarativeHub/TheBinderArchitecture) for iOS.
4 |
5 | Before trying out the app, please install Carthage dependencies:
6 |
7 | ```
8 | carthage bootstrap --platform ios
9 | ```
10 |
--------------------------------------------------------------------------------
/API/GitterError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GitterError.swift
3 | // API
4 | //
5 | // Created by Srdan Rasic on 17/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | public struct GitterError: Swift.Error, LocalizedError, Codable {
10 |
11 | public let message: String
12 |
13 | public var errorDescription: String? {
14 | return message
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/AbsurdGitter/Assets.xcassets/chat.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "chat.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/AbsurdGitter/Assets.xcassets/avatar.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "filename" : "avatar.png",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/API/API.h:
--------------------------------------------------------------------------------
1 | //
2 | // API.h
3 | // API
4 | //
5 | // Created by Srdan Rasic on 17/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for API.
12 | FOUNDATION_EXPORT double APIVersionNumber;
13 |
14 | //! Project version string for API.
15 | FOUNDATION_EXPORT const unsigned char APIVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Views/Views.h:
--------------------------------------------------------------------------------
1 | //
2 | // Views.h
3 | // Views
4 | //
5 | // Created by Srdan Rasic on 18/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for Views.
12 | FOUNDATION_EXPORT double ViewsVersionNumber;
13 |
14 | //! Project version string for Views.
15 | FOUNDATION_EXPORT const unsigned char ViewsVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Client/Client.h:
--------------------------------------------------------------------------------
1 | //
2 | // Client.h
3 | // Client
4 | //
5 | // Created by Srdan Rasic on 17/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for Client.
12 | FOUNDATION_EXPORT double ClientVersionNumber;
13 |
14 | //! Project version string for Client.
15 | FOUNDATION_EXPORT const unsigned char ClientVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Binders/Binders.h:
--------------------------------------------------------------------------------
1 | //
2 | // Binders.h
3 | // Binders
4 | //
5 | // Created by Srdan Rasic on 18/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for Binders.
12 | FOUNDATION_EXPORT double BindersVersionNumber;
13 |
14 | //! Project version string for Binders.
15 | FOUNDATION_EXPORT const unsigned char BindersVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Entities/AccessToken.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Token.swift
3 | // Entities
4 | //
5 | // Created by Srdan Rasic on 17/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | public struct AccessToken: Codable {
10 |
11 | public enum CodingKeys: String, CodingKey {
12 | case accessToken = "access_token"
13 | case tokenType = "token_type"
14 | }
15 |
16 | public let accessToken: String // The token that can be used to access the Gitter API.
17 | public let tokenType: String // The type of token received: bearer.
18 | }
19 |
--------------------------------------------------------------------------------
/Entities/Entities.h:
--------------------------------------------------------------------------------
1 | //
2 | // Entities.h
3 | // Entities
4 | //
5 | // Created by Srdan Rasic on 17/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for Entities.
12 | FOUNDATION_EXPORT double EntitiesVersionNumber;
13 |
14 | //! Project version string for Entities.
15 | FOUNDATION_EXPORT const unsigned char EntitiesVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Services/Services.h:
--------------------------------------------------------------------------------
1 | //
2 | // Services.h
3 | // Services
4 | //
5 | // Created by Srdan Rasic on 17/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for Services.
12 | FOUNDATION_EXPORT double ServicesVersionNumber;
13 |
14 | //! Project version string for Services.
15 | FOUNDATION_EXPORT const unsigned char ServicesVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Entities/User.swift:
--------------------------------------------------------------------------------
1 | //
2 | // User.swift
3 | // Entities
4 | //
5 | // Created by Srdan Rasic on 17/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | public struct User: Codable {
10 | public let id: String // Gitter User ID.
11 | public let username: String // Gitter/GitHub username.
12 | public let displayName: String // Gitter/GitHub user real name.
13 | public let url: String // Path to the user on Gitter.
14 | public let avatarUrlSmall: String // User avatar URI (small).
15 | public let avatarUrlMedium: String // User avatar URI (medium).
16 | }
17 |
--------------------------------------------------------------------------------
/Services/UserService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserService.swift
3 | // Services
4 | //
5 | // Created by Srdan Rasic on 17/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import API
10 | import Client
11 | import Entities
12 | import ReactiveKit
13 |
14 | /// Loads currently logged in user
15 | public class UserService {
16 |
17 | public let currentUser: LoadingSignal
18 |
19 | public init(_ client: GitterClient) {
20 | currentUser = User
21 | .me()
22 | .response(using: client)
23 | .toLoadingSignal()
24 | .shareReplayValues(limit: 1)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | build/
4 | *.pbxuser
5 | !default.pbxuser
6 | *.mode1v3
7 | !default.mode1v3
8 | *.mode2v3
9 | !default.mode2v3
10 | *.perspectivev3
11 | !default.perspectivev3
12 | xcuserdata
13 | *.xccheckout
14 | *.moved-aside
15 | DerivedData
16 | *.hmap
17 | *.ipa
18 | *.xcuserstate
19 | .DS_Store
20 |
21 | # CocoaPods
22 | #
23 | # We recommend against adding the Pods directory to your .gitignore. However
24 | # you should judge for yourself, the pros and cons are mentioned at:
25 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
26 | #
27 | # Pods/
28 |
29 | # Carthage
30 | #
31 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
32 | Carthage/Checkouts
33 | Carthage/Build
34 |
35 | # Swift Package Manager
36 |
37 | .build
38 |
--------------------------------------------------------------------------------
/Entities/Message.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Message.swift
3 | // Entities
4 | //
5 | // Created by Srdan Rasic on 17/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | public struct Message: Codable {
10 | public let id: String // ID of the message.
11 | public let text: String // Original message in plain-text/markdown.
12 | public let html: String // HTML formatted message.
13 | // public let sent: Date // ISO formatted date of the message.
14 | // public let editedAt: Date? // ISO formatted date of the message if edited.
15 | public let fromUser: User // (User)[user-resource] that sent the message.
16 | public let unread: Bool // Boolean that indicates if the current user has read the message.
17 | public let readBy: Int // Number of users that have read the message.
18 | }
19 |
--------------------------------------------------------------------------------
/AbsurdGitter/FrameworkInfo.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 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/API/Endpoints/Message.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Message.swift
3 | // API
4 | //
5 | // Created by Srdan Rasic on 17/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import Entities
10 | import Client
11 |
12 | // We can model our API by defining requests on our entities as extensions.
13 | // GitterRequest does not do any work, it's just a descriptor that defines
14 | // how to make the URLRequest (done by the Client).
15 |
16 | // Static functions operate on all entities (resource index).
17 | // Instance function operate on the entity instance (single resource).
18 |
19 | extension Message {
20 |
21 | public func update(text: String, roomId: String) -> GitterRequest {
22 | return GitterRequest(
23 | path: "rooms/\(roomId)/chatMessages/\(id)",
24 | method: .put,
25 | parameters: JSONParameters(["text": text]),
26 | authorization: .required
27 | )
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Entities/ApplicationError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ApplicationError.swift
3 | // API
4 | //
5 | // Created by Srdan Rasic on 18/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | public struct ApplicationError: Error, LocalizedError {
10 |
11 | public struct SimpleError: Error, LocalizedError {
12 | public let message: String
13 |
14 | public var errorDescription: String? {
15 | return message
16 | }
17 | }
18 |
19 | public let underlyingError: Error
20 |
21 | public init(_ underlyingError: Error) {
22 | self.underlyingError = underlyingError
23 | }
24 |
25 | public init(_ message: String) {
26 | self.underlyingError = SimpleError(message: message)
27 | }
28 |
29 | public var errorDescription: String? {
30 | if let underlyingError = underlyingError as? LocalizedError {
31 | return underlyingError.errorDescription
32 | } else {
33 | return underlyingError.localizedDescription
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Services/RoomService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RoomService.swift
3 | // Services
4 | //
5 | // Created by Srdan Rasic on 17/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import API
10 | import Client
11 | import Entities
12 | import ReactiveKit
13 |
14 | /// Loads all rooms for the current user.
15 | public class RoomService {
16 |
17 | public let client: GitterClient
18 |
19 | public let rooms: LoadingProperty<[Room], ApplicationError>
20 |
21 | public init(_ client: GitterClient, userService: UserService) {
22 | self.client = client
23 |
24 | rooms = LoadingProperty {
25 | userService.currentUser.flatMapValue(.latest) { user in
26 | user.getRooms().response(using: client)
27 | }
28 | }
29 | }
30 |
31 | public func leaveRoom(_ room: Room) -> LoadingSignal {
32 | return room
33 | .leave()
34 | .response(using: client)
35 | .reloading(rooms)
36 | .toLoadingSignal()
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/AbsurdGitter/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // AbsurdGitter
4 | //
5 | // Created by Srdan Rasic on 17/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Binders
11 | import Services
12 |
13 | @UIApplicationMain
14 | class AppDelegate: UIResponder, UIApplicationDelegate {
15 |
16 | var window: UIWindow?
17 |
18 | /// SessionManager manages current user session.
19 | let sessionManager = SessionManager()
20 |
21 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
22 |
23 | window = UIWindow.makeMainWindow(sessionManager)
24 | window?.makeKeyAndVisible()
25 |
26 | return true
27 | }
28 |
29 | /// Observe url scheme deep link - contains OAuth code when user logs in.
30 | func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
31 | return sessionManager.handleOpenUrl(url)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Services/MessageService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MessageService.swift
3 | // Services
4 | //
5 | // Created by Srdan Rasic on 18/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import API
10 | import Client
11 | import Entities
12 | import ReactiveKit
13 |
14 | /// Loads messages for the given room.
15 | public class MessageService {
16 |
17 | public let client: GitterClient
18 | public let room: Room
19 |
20 | public let messages: LoadingProperty<[Message], ApplicationError>
21 |
22 | public init(_ client: GitterClient, room: Room) {
23 | self.client = client
24 | self.room = room
25 |
26 | messages = LoadingProperty {
27 | room.getMessages(limit: 50).response(using: client).toLoadingSignal()
28 | }
29 | }
30 |
31 | /// Send a message with the body
32 | public func sendMessage(_ body: String) -> LoadingSignal {
33 | return room
34 | .sendMessage(body)
35 | .response(using: client)
36 | .reloading(messages)
37 | .toLoadingSignal()
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Srđan Rašić
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.
22 |
--------------------------------------------------------------------------------
/Binders/RoomNavigationController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RoomNavigationController.swift
3 | // Binders
4 | //
5 | // Created by Srdan Rasic on 29/03/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import Views
10 | import Services
11 | import Entities
12 | import Bond
13 | import ReactiveKit
14 |
15 | class RoomNavigationController: UINavigationController {}
16 |
17 | /// Navigation controller for the first tab: Rooms.
18 | extension RoomNavigationController {
19 |
20 | /// Binder is a function that creates and configures the view controller. It binds the data
21 | /// from the service to the view controller and user actions from the view controller
22 | /// to the service.
23 | static func makeViewController(_ session: AuthenticatedSession) -> RoomNavigationController {
24 |
25 | // Room list view controller is navigation controller's root view controller
26 | let roomListViewController = RoomListViewController.makeViewController(session)
27 | let navigationController = RoomNavigationController(rootViewController: roomListViewController)
28 |
29 | return navigationController
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Binders/MainWindow.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RootViewController.swift
3 | // Binders
4 | //
5 | // Created by Srdan Rasic on 18/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import Views
10 | import Services
11 | import Entities
12 | import Bond
13 | import ReactiveKit
14 |
15 | extension UIWindow {
16 |
17 | /// Binder for the main window? Why not. Updates window's rootViewController whenever the session is changed.
18 | public static func makeMainWindow(_ sessionManager: SessionManager) -> UIWindow {
19 | let window = UIWindow(frame: UIScreen.main.bounds)
20 |
21 | // Observe currentSession and update rootViewController accordingly.
22 | sessionManager.currentSession.bind(to: window) { window, session in
23 | switch session {
24 | case .authenticated(let session):
25 | window.rootViewController = MainTabBarController.makeViewController(session)
26 | case .unauthenticated(let session):
27 | window.rootViewController = LoginViewController.makeViewController(session.loginService)
28 | }
29 | }
30 |
31 | return window
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Binders/MainTabBarController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainTabBarController.swift
3 | // Binders
4 | //
5 | // Created by Srdan Rasic on 29/03/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import Views
10 | import Services
11 | import Entities
12 | import Bond
13 | import ReactiveKit
14 |
15 | class MainTabBarController: UITabBarController {}
16 |
17 | /// MainTabBarController is application window's `rootViewController` when the user IS logged in.
18 | extension MainTabBarController {
19 |
20 | /// Binder is a function that creates and configures the view controller. It binds the data
21 | /// from the service to the view controller and user actions from the view controller
22 | /// to the service.
23 | static func makeViewController(_ session: AuthenticatedSession) -> MainTabBarController {
24 | let tabBarController = MainTabBarController()
25 |
26 | // Use binders to create child view controllers:
27 | tabBarController.setViewControllers([
28 | RoomNavigationController.makeViewController(session),
29 | ProfileViewController.makeViewController(session)
30 | ], animated: false)
31 |
32 | return tabBarController
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Binders/LoginViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoginViewController.swift
3 | // Binders
4 | //
5 | // Created by Srdan Rasic on 18/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import Views
10 | import Services
11 | import Entities
12 | import Bond
13 | import ReactiveKit
14 |
15 | /// LoginViewController is application window's `rootViewController` when the user IS NOT logged in.
16 | extension LoginViewController {
17 |
18 | /// Creates login view controller.
19 | static func makeViewController(_ loginService: LoginService) -> LoginViewController {
20 | let viewController = LoginViewController()
21 |
22 | // All data, including strings, should be set from the binder (this method).
23 | viewController.loginButton.setTitle("Log in", for: .normal)
24 |
25 | // When user taps the login button, call `loginService.startLogin`.
26 | viewController.loginButton.reactive.tap
27 | // Binding to the VC ensures that the binding
28 | // is active only while the VC is alive.
29 | .bind(to: viewController) { _ in
30 | loginService.startLogin()
31 | }
32 |
33 | return viewController
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/API/Endpoints/OAuth.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Oauth.swift
3 | // API
4 | //
5 | // Created by Srdan Rasic on 17/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import Client
10 | import Entities
11 |
12 | // We can model our API by defining requests on our entities as extensions.
13 | // GitterRequest does not do any work, it's just a descriptor that defines
14 | // how to make the URLRequest (done by the Client).
15 |
16 | // Static functions operate on all entities (resource index).
17 | // Instance function operate on the entity instance (single resource).
18 |
19 | public enum OAuth {
20 |
21 | public static func token(clientID: String, secret: String, code: String, redirectURI: String) -> GitterRequest {
22 | let parameters: [String: String] = [
23 | "client_id": clientID,
24 | "client_secret": secret,
25 | "code": code,
26 | "redirect_uri": redirectURI,
27 | "grant_type": "authorization_code"
28 | ]
29 | return GitterRequest(
30 | path: "token",
31 | method: .post,
32 | parameters: JSONParameters(parameters),
33 | authorization: .none
34 | )
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Views/LoginViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoginView.swift
3 | // Views
4 | //
5 | // Created by Srdan Rasic on 18/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Layoutless
11 |
12 | public class LoginViewController: UI.ViewController {
13 |
14 | public let loginButton = UI.Button(type: .system).styled(with: Stylesheet.loginButton)
15 |
16 | public override func viewDidLoad() {
17 | super.viewDidLoad()
18 | Stylesheet.view.apply(to: view)
19 | }
20 |
21 | // Using Layoutless to declaratively define layout.
22 | public override var subviewsLayout: AnyLayout {
23 | return loginButton.centeringInParent()
24 | }
25 | }
26 |
27 | extension LoginViewController {
28 |
29 | /// We can define a stylesheet for the view controller and its subviews in the view controller extension.
30 | public enum Stylesheet {
31 |
32 | public static let view = Style {
33 | $0.backgroundColor = .white
34 | }
35 |
36 | public static let loginButton = Style {
37 | $0.setTitleColor(.purple, for: .normal)
38 | $0.titleLabel?.font = .systemFont(ofSize: 20)
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Entities/Room.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Room.swift
3 | // Entities
4 | //
5 | // Created by Srdan Rasic on 17/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | public struct Room: Codable {
10 | public let id: String
11 | public let name: String // Room name.
12 | public let uri: String? // Room URI on Gitter.
13 | public let topic: String // Room topic. (default: GitHub repo description)
14 | public let oneToOne: Bool // Indicates if the room is a one-to-one chat.
15 | public let userCount: Int // Count of users in the room.
16 | public let unreadItems: Int // Number of unread messages for the current user.
17 | public let mentions: Int // Number of unread mentions for the current user.
18 | // public let lastAccessTime: Date? // Last time the current user accessed the room in ISO format.
19 | // public let favourite: Bool // Indicates if the room is on of your favourites.
20 | public let lurk: Bool // Indicates if the current user has disabled notifications.
21 | public let url: String // Path to the room on gitter.
22 | public let githubType: String // Type of the room.
23 | public let tags: [String] // Tags that define the room.
24 | }
25 |
--------------------------------------------------------------------------------
/Binders/RoomViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RoomViewController.swift
3 | // Binders
4 | //
5 | // Created by Srdan Rasic on 18/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import Views
10 | import Services
11 | import Entities
12 | import Bond
13 | import ReactiveKit
14 |
15 | extension RoomViewController {
16 |
17 | /// Binder is a function that creates and configures the view controller. It binds the data
18 | /// from the service to the view controller and user actions from the view controller
19 | /// to the service.
20 | static func makeViewController(_ messageService: MessageService) -> RoomViewController {
21 | let viewContoller = RoomViewController()
22 |
23 | // Date that is available at the binding time can just be assigned...
24 | viewContoller.navigationItem.title = messageService.room.name
25 |
26 | // ...while the data available asynchronously (as a signal) should be bound to the view controller
27 | // or its subviews.
28 | messageService.messages
29 | .consumeLoadingState(by: viewContoller) // Display a spinner while loading the data.
30 | .bind(to: viewContoller.messagesCollectionView, cellType: MessageCell.self) { cell, message in
31 | cell.bodyLabel.text = message.text
32 | cell.userNameLabel.text = message.fromUser.displayName
33 | }
34 |
35 | return viewContoller
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Client/HTTPMethod.swift:
--------------------------------------------------------------------------------
1 | //
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2017 Srdan Rasic (@srdanrasic)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 | //
24 |
25 | public enum HTTPMethod: String {
26 | case get = "GET"
27 | case put = "PUT"
28 | case post = "POST"
29 | case patch = "PATCH"
30 | case delete = "DELETE"
31 | case head = "HEAD"
32 | }
33 |
--------------------------------------------------------------------------------
/Services/UnauthenticatedSession.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UnauthenticatedSession.swift
3 | // Services
4 | //
5 | // Created by Srdan Rasic on 18/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import API
10 | import Entities
11 | import ReactiveKit
12 |
13 | public protocol UnauthenticatedSessionDelegate: class {
14 | func didObtainAccessToken(_ accessToken: AccessToken)
15 | }
16 |
17 | public class UnauthenticatedSession: DisposeBagProvider {
18 |
19 | private let gitterAuthenticationAPIBaseURL = "https://gitter.im/login/oauth"
20 |
21 | // HTTP client is usually defined on the session.
22 | public let client: GitterClient
23 |
24 | // Services that should be alive during the session
25 | // should be declared on the session itself. No sigletons.
26 | public let loginService: LoginService
27 |
28 | public weak var delegate: UnauthenticatedSessionDelegate?
29 | public let bag = DisposeBag()
30 |
31 | public init() {
32 | client = GitterClient(baseURL: gitterAuthenticationAPIBaseURL)
33 | loginService = LoginService(client)
34 |
35 | // When the token is obtained, inform the SessionManager (our delegate) so that it can
36 | // create and set a new authenticated session with the obtained token.
37 | loginService.accessToken.bind(to: self, context: .immediateOnMain) { me, accessToken in
38 | me.delegate?.didObtainAccessToken(accessToken)
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Binders/ProfileViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProfileViewController.swift
3 | // Binders
4 | //
5 | // Created by Srdan Rasic on 29/03/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import Views
10 | import Services
11 | import Entities
12 | import Bond
13 | import ReactiveKit
14 |
15 | extension ProfileViewController {
16 |
17 | /// Binder is a function that creates and configures the view controller. It binds the data
18 | /// from the service to the view controller and user actions from the view controller
19 | /// to the service.
20 | static func makeViewController(_ session: AuthenticatedSession) -> ProfileViewController {
21 | let viewController = ProfileViewController()
22 |
23 | // All data, including strings, should be set from the binder (this method).
24 | viewController.title = "Profile"
25 | viewController.logoutButton.setTitle("Log out", for: .normal)
26 |
27 | // Data that is available asynchronously should be bound to the view controller
28 | // or its subviews.
29 | session.userService.currentUser
30 | .consumeLoadingState(by: viewController)
31 | .bind(to: viewController) { viewContoller, user in
32 | viewContoller.nameLabel.text = user.displayName
33 | viewContoller.usernameLabel.text = user.username
34 | }
35 |
36 | // User actions should trigger actions on services.
37 | viewController.logoutButton.reactive.tap
38 | .bind(to: viewController) { _ in
39 | session.invalidate()
40 | }
41 |
42 | return viewController
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/AbsurdGitter/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 |
--------------------------------------------------------------------------------
/Services/AuthenticatedSession.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Session.swift
3 | // Services
4 | //
5 | // Created by Srdan Rasic on 17/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import API
10 | import Entities
11 | import ReactiveKit
12 |
13 | public protocol AuthenticatedSessionDelegate: class {
14 | func didInvalidateAuthenticatedSession(_ session: AuthenticatedSession)
15 | }
16 |
17 | public class AuthenticatedSession {
18 |
19 | private let gitterAPIBaseURL = "https://api.gitter.im/v1"
20 | private let accessToken: AccessToken
21 |
22 | // HTTP client is usually defined on the session.
23 | public let client: GitterClient
24 |
25 | // Services that should be alive during the session
26 | // should be declared on the session itself. No sigletons.
27 | public let userService: UserService
28 | public let roomService: RoomService
29 |
30 | public weak var delegate: AuthenticatedSessionDelegate?
31 |
32 | public init(_ accessToken: AccessToken) {
33 | self.accessToken = accessToken
34 | client = GitterClient(baseURL: gitterAPIBaseURL)
35 | userService = UserService(client)
36 | roomService = RoomService(client, userService: userService)
37 | client.delegate = self
38 | }
39 |
40 | public func invalidate() {
41 | delegate?.didInvalidateAuthenticatedSession(self)
42 | }
43 | }
44 |
45 | extension AuthenticatedSession: GitterClientDelegate {
46 |
47 | // Session is a delegate of the our HTTP client. The client asks the session to authorize each request with the token.
48 | public func authorize(_ request: GitterRequest, client: GitterClient) -> Signal, ApplicationError> {
49 | return Signal(just: request.authorized(with: accessToken))
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Views/ProfileViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProfileViewController.swift
3 | // Views
4 | //
5 | // Created by Srdan Rasic on 29/03/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Layoutless
11 |
12 | public class ProfileViewController: UI.ViewController {
13 |
14 | public let nameLabel = UI.Label(style: Stylesheet.name)
15 | public let usernameLabel = UI.Label(style: Stylesheet.username)
16 | public let logoutButton = UI.Button(type: .system)
17 |
18 | public override func setup() {
19 | tabBarItem.image = #imageLiteral(resourceName: "avatar")
20 | }
21 |
22 | public override func viewDidLoad() {
23 | super.viewDidLoad()
24 | Stylesheet.view.apply(to: view)
25 | }
26 |
27 | // Using Layoutless to declaratively define layout.
28 | public override var subviewsLayout: AnyLayout {
29 | return stack(.vertical, spacing: 10)(
30 | nameLabel,
31 | usernameLabel,
32 | verticalSpacing(20),
33 | logoutButton
34 | ).centeringInParent(relativeToSafeArea: true)
35 | }
36 | }
37 |
38 | extension ProfileViewController {
39 |
40 | /// We can define a stylesheet for the view controller and its subviews in the view controller extension.
41 | public enum Stylesheet {
42 |
43 | public static let view = Style {
44 | $0.backgroundColor = .white
45 | }
46 |
47 | public static let name = Style {
48 | $0.font = UIFont.systemFont(ofSize: 24, weight: .bold)
49 | $0.textAlignment = .center
50 | }
51 |
52 | public static let username = Style {
53 | $0.font = UIFont.systemFont(ofSize: 19, weight: .regular)
54 | $0.textAlignment = .center
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/AbsurdGitter/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 | APPL
17 | CFBundleShortVersionString
18 | 1.0.0
19 | CFBundleURLTypes
20 |
21 |
22 | CFBundleTypeRole
23 | Editor
24 | CFBundleURLName
25 | absurd-gitter
26 | CFBundleURLSchemes
27 |
28 | absurd-gitter
29 |
30 |
31 |
32 | CFBundleVersion
33 | 1
34 | LSRequiresIPhoneOS
35 |
36 | UILaunchStoryboardName
37 | LaunchScreen
38 | UIRequiredDeviceCapabilities
39 |
40 | armv7
41 |
42 | UISupportedInterfaceOrientations
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationLandscapeLeft
46 | UIInterfaceOrientationLandscapeRight
47 |
48 | UISupportedInterfaceOrientations~ipad
49 |
50 | UIInterfaceOrientationPortrait
51 | UIInterfaceOrientationPortraitUpsideDown
52 | UIInterfaceOrientationLandscapeLeft
53 | UIInterfaceOrientationLandscapeRight
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/AbsurdGitter/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/API/Endpoints/User.swift:
--------------------------------------------------------------------------------
1 | //
2 | // User.swift
3 | // API
4 | //
5 | // Created by Srdan Rasic on 17/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import Entities
10 | import Client
11 |
12 | // We can model our API by defining requests on our entities as extensions.
13 | // GitterRequest does not do any work, it's just a descriptor that defines
14 | // how to make the URLRequest (done by the Client).
15 |
16 | // Static functions operate on all entities (resource index).
17 | // Instance function operate on the entity instance (single resource).
18 |
19 | extension User {
20 |
21 | public static func me() -> GitterRequest {
22 | return GitterRequest(
23 | path: "user/me",
24 | method: .get,
25 | authorization: .required
26 | )
27 | }
28 |
29 | public static func get(limit: Int? = nil, skip: Int? = nil) -> GitterRequest<[User]> {
30 | let parameters: [String: Any?] = ["limit": limit, "skip": skip]
31 | return GitterRequest(
32 | path: "user",
33 | method: .get,
34 | parameters: JSONParameters(parameters),
35 | authorization: .required
36 | )
37 | }
38 |
39 | public static func query(_ q: String, limit: Int? = nil, skip: Int? = nil) -> GitterRequest<[User]> {
40 | let parameters: [String: Any?] = ["q": q, "limit": limit, "skip": skip]
41 | return GitterRequest(
42 | path: "user",
43 | method: .get,
44 | parameters: JSONParameters(parameters),
45 | authorization: .required
46 | )
47 | }
48 |
49 | public func getRooms() -> GitterRequest<[Room]> {
50 | return GitterRequest(
51 | path: "user/\(id)/rooms",
52 | method: .get,
53 | authorization: .required
54 | )
55 | }
56 |
57 | public func getChannels() -> GitterRequest<[Room]> {
58 | return GitterRequest(
59 | path: "user/\(id)/channels",
60 | method: .get,
61 | authorization: .required
62 | )
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/Client/LoggingClient.swift:
--------------------------------------------------------------------------------
1 | //
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2018 Srdan Rasic (@srdanrasic)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 | //
24 |
25 | import Foundation
26 |
27 | /// A Client subclass that adds logging layer.
28 | open class LoggingClient: Client {
29 |
30 | open override func perform(_ request: Request, completion: @escaping (Result) -> Void) -> URLSessionTask {
31 | let requestDescription: String
32 | if let parameters = request.parameters {
33 | requestDescription = "\(request.method.rawValue) \(request.path); \(String(describing: parameters))"
34 | } else {
35 | requestDescription = "\(request.method.rawValue) \(request.path)"
36 | }
37 | log.info("Sent request: " + requestDescription)
38 |
39 | return super.perform(request, completion: { (result) in
40 |
41 | switch result {
42 | case .success(let value):
43 | log.info("Received response for: " + requestDescription)
44 | log.debug("Parsed response data: \(value)")
45 | case .failure(let error):
46 | log.error("Request failed: " + requestDescription + "\nWith error: " + error.localizedDescription)
47 | }
48 |
49 | completion(result)
50 | })
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/API/GitterRequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Request.swift
3 | // API
4 | //
5 | // Created by Srdan Rasic on 17/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Entities
11 | import Client
12 | import ReactiveKit
13 |
14 | /// Describes an HTTP request / GitterAPI endpoint.
15 | public struct GitterRequest {
16 |
17 | public enum Authorization {
18 | case none
19 | case required
20 | }
21 |
22 | public var path: String
23 | public var method: HTTPMethod
24 | public var parameters: RequestParameters?
25 | public var headers: [String: String]?
26 | public var authorization: Authorization
27 |
28 | public init(path: String,
29 | method: HTTPMethod,
30 | parameters: RequestParameters? = nil,
31 | headers: [String: String]? = nil,
32 | authorization: Authorization) {
33 |
34 | self.path = path
35 | self.method = method
36 | self.parameters = parameters
37 | self.headers = headers
38 | self.authorization = authorization
39 | }
40 | }
41 |
42 | extension GitterRequest {
43 |
44 | /// Convert GitterRequest into Client.Request. GitterRequest resources are JSON decodable.
45 | var asClientRequest: Request {
46 | return Request(
47 | path: path,
48 | method: method,
49 | parameters: parameters,
50 | headers: headers,
51 | resource: {
52 | let decoder = JSONDecoder()
53 | decoder.dateDecodingStrategy = .iso8601
54 | return try decoder.decode(Resource.self, from: $0)
55 | },
56 | error: {
57 | let decoder = JSONDecoder()
58 | return try decoder.decode(GitterError.self, from: $0)
59 | }
60 | )
61 | }
62 | }
63 |
64 | extension GitterRequest {
65 |
66 | mutating func set(_ value: String?, forHttpHeader key: String) {
67 | var headers = self.headers ?? [:]
68 | headers[key] = value
69 | self.headers = headers
70 | }
71 | }
72 |
73 | extension GitterRequest {
74 |
75 | /// A convenience method that creates a signal that will execute the request
76 | /// using the given client.
77 | public func response(using client: GitterClient) -> Signal {
78 | return client.perform(self)
79 | }
80 | }
81 |
82 |
--------------------------------------------------------------------------------
/Client/Logger.swift:
--------------------------------------------------------------------------------
1 | //
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2017 Srdan Rasic (@srdanrasic)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 | //
24 |
25 | import Foundation
26 |
27 | public protocol Logger {
28 | func error(_ message: Any)
29 | func warning(_ message: Any)
30 | func info(_ message: Any)
31 | func debug(_ message: Any)
32 | }
33 |
34 | public class SimpleLogger: Logger {
35 |
36 | public enum Level: Int {
37 | case none = 0
38 | case error = 1
39 | case warning = 2
40 | case info = 3
41 | case debug = 4
42 | }
43 |
44 | public let prefix: String
45 |
46 | public var level: Level = .debug
47 |
48 | public func error(_ message: Any) {
49 | guard level.rawValue >= Level.error.rawValue else { return }
50 | print("[\(prefix)] 💥 \(message)")
51 | }
52 |
53 | public func warning(_ message: Any) {
54 | guard level.rawValue >= Level.warning.rawValue else { return }
55 | print("[\(prefix)] ⚠️ \(message)")
56 | }
57 |
58 | public func info(_ message: Any) {
59 | guard level.rawValue >= Level.info.rawValue else { return }
60 | print("[\(prefix)] 💚 \(message)")
61 | }
62 |
63 | public func debug(_ message: Any) {
64 | guard level.rawValue >= Level.debug.rawValue else { return }
65 | print("[\(prefix)] 🐞 \(message)")
66 | }
67 |
68 | public init(prefix: String) {
69 | self.prefix = prefix
70 | }
71 | }
72 |
73 | public var log: Logger = SimpleLogger(prefix: "Client")
74 |
--------------------------------------------------------------------------------
/API/GitterClient.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GitterClient.swift
3 | // API
4 | //
5 | // Created by Srdan Rasic on 17/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import Client
10 | import Entities
11 | import ReactiveKit
12 |
13 | public protocol GitterClientDelegate: class {
14 | func authorize(_ request: GitterRequest, client: GitterClient) -> Signal, ApplicationError>
15 | }
16 |
17 | /// A Client subclass that adds authentication layer.
18 | open class GitterClient: LoggingClient {
19 |
20 | public weak var delegate: GitterClientDelegate?
21 |
22 | private func authorize(_ request: GitterRequest) -> Signal, ApplicationError> {
23 | guard let delegate = delegate else {
24 | return Signal.failed(ApplicationError("Client delegate not set."))
25 | }
26 | return delegate.authorize(request, client: self)
27 | }
28 |
29 | private func justPerform(_ request: Request) -> Signal {
30 | return Signal { observer in
31 | let task = self.perform(request) { result in
32 | switch result {
33 | case .success(let resource):
34 | observer.receive(lastElement: resource)
35 | case .failure(let error):
36 | observer.receive(completion: .failure(ApplicationError(error)))
37 | }
38 | }
39 | return BlockDisposable {
40 | task.cancel()
41 | }
42 | }
43 | }
44 |
45 | open func perform(_ request: GitterRequest) -> Signal {
46 | switch request.authorization {
47 | case .none:
48 | return self.justPerform(request.asClientRequest)
49 | case .required:
50 | return Signal { observer in
51 | return self.authorize(request).flatMapLatest { request in
52 | return self.justPerform(request.asClientRequest)
53 | }.observe(with: observer)
54 | }
55 | }
56 | }
57 | }
58 |
59 | extension Client.Error {
60 |
61 | var isUnauthorizedError: Bool {
62 | guard let code = code else { return false }
63 | return [401].contains(code)
64 | }
65 | }
66 |
67 | extension GitterRequest {
68 |
69 | public func authorized(with token: AccessToken) -> GitterRequest {
70 | var copy = self
71 | copy.set("Bearer " + token.accessToken, forHttpHeader: "Authorization")
72 | return copy
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/Services/LoginService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoginService.swift
3 | // Services
4 | //
5 | // Created by Srdan Rasic on 18/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import API
10 | import Client
11 | import Entities
12 | import ReactiveKit
13 |
14 | /// Authenticates the user by exchanging the OAuth code for access token.
15 | public class LoginService {
16 |
17 | private static let authorizationBaseURL = "https://gitter.im/login/oauth/authorize"
18 | private static let clientID = "c1c237bf98bfac127978ff0af4b0515a9824bb62"
19 | private static let secret = "257812a491dc0cedb46f116b2751f2183a690c54"
20 | private static let redirectURI = "absurd-gitter://authenticationToken"
21 |
22 | private var authorizationURL: URL {
23 | return URL(string: "\(LoginService.authorizationBaseURL)?client_id=\(LoginService.clientID)&response_type=code&redirect_uri=\(LoginService.redirectURI)")!
24 | }
25 |
26 | private let tokenCode = PassthroughSubject()
27 |
28 | /// Will emit an access token when the user authorizes the app
29 | public let accessToken: SafeSignal
30 |
31 | public init(_ client: GitterClient) {
32 | // When we get the code, request the token
33 | accessToken = tokenCode.flatMapLatest { code in
34 | OAuth
35 | .token(clientID: LoginService.clientID, secret: LoginService.secret, code: code, redirectURI: LoginService.redirectURI)
36 | .response(using: client)
37 | .suppressError(logging: true)
38 | }.share()
39 | }
40 |
41 | /// Observe url scheme deep link - contains OAuth code when user logs in.
42 | public func handleOpenUrl(_ url: URL) -> Bool {
43 | if isRedirect(url) {
44 | if let code = parseCodeFrom(url) {
45 | tokenCode.send(code)
46 | } else {
47 | log.error("No token code received.")
48 | }
49 | return true
50 | } else {
51 | return false
52 | }
53 | }
54 |
55 | public func startLogin() {
56 | UIApplication.shared.open(authorizationURL)
57 | }
58 |
59 | private func isRedirect(_ url: URL) -> Bool {
60 | return url.absoluteString.lowercased().hasPrefix(LoginService.redirectURI.lowercased())
61 | }
62 |
63 | private func parseCodeFrom(_ url: URL) -> String? {
64 | let components = URLComponents(url: url as URL, resolvingAgainstBaseURL: false)!
65 |
66 | if let keyValue = components.queryItems?.filter({ $0.name == "code" }).first, let code = keyValue.value {
67 | return code
68 | } else {
69 | return nil
70 | }
71 | }
72 | }
73 |
74 |
--------------------------------------------------------------------------------
/Binders/Extensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Extensions.swift
3 | // Binders
4 | //
5 | // Created by Srdan Rasic on 18/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import ReactiveKit
11 | import Bond
12 | import Views
13 | import Layoutless
14 | import Services
15 |
16 | func itemAtIndex(_ index: C.Index, from collection: C) -> C.Element {
17 | return collection[index]
18 | }
19 |
20 | func itemAtIndexPath(_ index: IndexPath, from collection: C) -> C.Element where C.Index == Int {
21 | return collection[index.row]
22 | }
23 |
24 | extension UIView {
25 |
26 | private static let loadingIndicatorTag = 10001
27 |
28 | private var loadingIndicator: UIActivityIndicatorView? {
29 | return viewWithTag(UIView.loadingIndicatorTag) as? UIActivityIndicatorView
30 | }
31 |
32 | /// Adds UIActivityIndicatorView as a subview when true.
33 | var isLoadingIndicatorVisible: Bool {
34 | get {
35 | return loadingIndicator != nil
36 | }
37 | set {
38 | if newValue {
39 | guard loadingIndicator == nil else { return }
40 | let indicator = UIActivityIndicatorView(style: .gray)
41 | indicator.tag = UIView.loadingIndicatorTag
42 | indicator
43 | .sizing(toWidth: 44, height: 44)
44 | .centeringInParent(xOffset: 0, yOffset: 0, relativeToSafeArea: true)
45 | .layout(in: self)
46 | indicator.startAnimating()
47 | } else {
48 | loadingIndicator?.removeFromSuperview()
49 | }
50 | }
51 | }
52 | }
53 |
54 | // Makes UIViewController a LoadingStateListener. That means that we can pass an instance of UIViewController
55 | // to `consumeLoadingState` operator of a LoadingSignal to convert it into regular SafeSignal.
56 | // Loading state is "consumed" by the view controller by displaying the loading indicator when needed.
57 | extension UIViewController: LoadingStateListener {
58 |
59 | public func setLoadingState(_ state: ObservedLoadingState) {
60 | switch state {
61 | case .loading:
62 | view.isLoadingIndicatorVisible = true
63 | case .reloading:
64 | view.isLoadingIndicatorVisible = true
65 | case .loaded:
66 | view.isLoadingIndicatorVisible = false
67 | case .failed(let error):
68 | view.isLoadingIndicatorVisible = false
69 | displayError(error)
70 | }
71 | }
72 |
73 | public func displayError(_ error: Error) {
74 | // Advice: Present error popup or error overlay view instead
75 | print(error)
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/Client/Request.swift:
--------------------------------------------------------------------------------
1 | //
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2017 Srdan Rasic (@srdanrasic)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 | //
24 |
25 | import Foundation
26 |
27 | /// A simple HTTP request descriptor.
28 | /// Instances of Request can be performed by the Client.
29 | /// Inspired by: http://chris.eidhof.nl/post/tiny-networking-in-swift/
30 | public struct Request {
31 |
32 | public var path: String
33 | public var method: HTTPMethod
34 | public var parameters: RequestParameters?
35 | public var headers: [String: String]?
36 | public var resource: (Data) throws -> Resource // Resource parser
37 | public var error: (Data) throws -> Error // Error parser
38 |
39 | public init(path: String,
40 | method: HTTPMethod,
41 | parameters: RequestParameters? = nil,
42 | headers: [String: String]? = nil,
43 | resource: @escaping (Data) throws -> Resource,
44 | error: @escaping (Data) throws -> Error) {
45 |
46 | self.path = path
47 | self.method = method
48 | self.parameters = parameters
49 | self.headers = headers
50 | self.resource = resource
51 | self.error = error
52 | }
53 | }
54 |
55 | extension Request {
56 |
57 | @discardableResult
58 | public func response(using client: Client, completion: @escaping (Result) -> Void) -> URLSessionTask {
59 | return client.perform(self, completion: completion)
60 | }
61 |
62 | public mutating func set(_ value: String?, forHttpHeaderKey key: String) {
63 | var headers = self.headers ?? [:]
64 | headers[key] = value
65 | self.headers = headers
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Client/Result.swift:
--------------------------------------------------------------------------------
1 | //
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2017 Srdan Rasic (@srdanrasic)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 | //
24 |
25 | public protocol ResultProtocol {
26 | associatedtype Value
27 | associatedtype Error: Swift.Error
28 |
29 | var value: Value? { get }
30 | var error: Error? { get }
31 | }
32 |
33 | /// An enum representing either a failure or a success.
34 | public enum Result: CustomStringConvertible {
35 |
36 | case success(T)
37 | case failure(E)
38 |
39 | /// Constructs a result with a success value.
40 | public init(_ value: T) {
41 | self = .success(value)
42 | }
43 |
44 | /// Constructs a result with an error.
45 | public init(_ error: E) {
46 | self = .failure(error)
47 | }
48 |
49 | public var description: String {
50 | switch self {
51 | case let .success(value):
52 | return ".success(\(value))"
53 | case let .failure(error):
54 | return ".failure(\(error))"
55 | }
56 | }
57 | }
58 |
59 | extension Result: ResultProtocol {
60 |
61 | public var value: T? {
62 | if case .success(let value) = self {
63 | return value
64 | } else {
65 | return nil
66 | }
67 | }
68 |
69 | public var error: E? {
70 | if case .failure(let error) = self {
71 | return error
72 | } else {
73 | return nil
74 | }
75 | }
76 |
77 | public var unbox: Result {
78 | return self
79 | }
80 |
81 | public func map(_ transform: (T) -> U) -> Result {
82 | switch self {
83 | case let .success(value):
84 | return .success(transform(value))
85 | case let .failure(error):
86 | return .failure(error)
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/Binders/RoomListViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RoomListViewController.swift
3 | // Binders
4 | //
5 | // Created by Srdan Rasic on 18/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import Views
10 | import Services
11 | import Entities
12 | import Bond
13 | import ReactiveKit
14 |
15 | extension RoomListViewController {
16 |
17 | /// Binder is a function that creates and configures the view controller. It binds the data
18 | /// from the service to the view controller and user actions from the view controller
19 | /// to the service.
20 | static func makeViewController(_ session: AuthenticatedSession) -> RoomListViewController {
21 |
22 | // We create the view controller instance...
23 | let viewContoller = RoomListViewController()
24 | // ...get the service...
25 | let roomService = session.roomService
26 | // ...and then we proceed by setting and binding the data:
27 |
28 | // Data that is available at the binding time, including strings, can just be assigned...
29 | viewContoller.title = NSLocalizedString("Rooms", comment: "")
30 |
31 | // ...while the data that is exposed as a signal should be bound to the view controller or its subviews.
32 | roomService.rooms
33 | // Convert LoadingSignal into SafeSignal by consuming it loading state.
34 | // UIViewController conforms to LoadingStateListener protocol by displaying an activity
35 | // indicator view while the signal is in the loading state.
36 | .consumeLoadingState(by: viewContoller)
37 | // Using Bond, we can bind rooms signal to a collection view.
38 | .bind(to: viewContoller.roomsCollectionView, cellType: RoomCell.self) { cell, room in
39 | cell.nameLabel.text = room.name
40 | }
41 |
42 | // A signal that emits an index path when user selects a room cell.
43 | viewContoller.roomsCollectionView.reactive.selectedItemIndexPath
44 | // Map index path into the respective Room instance
45 | .with(latestFrom: roomService.rooms.value(), combine: itemAtIndexPath)
46 | // Binding to viewContoller ensures that the signal is alive only while viewContoller is alive.
47 | .bind(to: viewContoller) { viewContoller, room in
48 | // We tell to router to navigate to the RoomViewController.
49 | // Actual routing is handled by the navigation controller.
50 | viewContoller.presentRoom(room, session: session)
51 | }
52 |
53 | // Binder always returns a view controller.
54 | return viewContoller
55 | }
56 |
57 | /// Navigation route can be implemented as an extension on the view controller.
58 | func presentRoom(_ room: Room, session: AuthenticatedSession) {
59 | let service = MessageService(session.client, room: room)
60 | let viewController = RoomViewController.makeViewController(service)
61 | navigationController?.pushViewController(viewController, animated: true)
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Views/RoomListViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RoomListViewController.swift
3 | // Views
4 | //
5 | // Created by Srdan Rasic on 18/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Layoutless
11 |
12 | public class RoomListViewController: UI.ViewController, UICollectionViewDelegateFlowLayout {
13 |
14 | public let roomsCollectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
15 | .styled(with: Stylesheet.collectionView)
16 |
17 | public override func setup() {
18 | tabBarItem.image = #imageLiteral(resourceName: "chat")
19 | roomsCollectionView.delegate = self
20 | }
21 |
22 | // Using Layoutless to declaratively define layout.
23 | public override var subviewsLayout: AnyLayout {
24 | return roomsCollectionView.fillingParent()
25 | }
26 |
27 | // MARK: UICollectionViewDelegateFlowLayout
28 |
29 | public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
30 | return CGSize(width: collectionView.bounds.width - 30, height: 100)
31 | }
32 | }
33 |
34 | extension RoomListViewController {
35 |
36 | /// Layoutless makes view classes trivial so it's perfectly fine to define then in the view controller extensions.
37 | public class RoomCell: UI.CollectionViewCell {
38 |
39 | public let nameLabel = UI.Label(style: Stylesheet.RoomCell.name)
40 |
41 | public override func setup() {
42 | Stylesheet.RoomCell.contentView.apply(to: contentView)
43 | }
44 |
45 | // Using Layoutless to declaratively define layout.
46 | public override var subviewsLayout: AnyLayout {
47 | return nameLabel.fillingParent(insets: 20)
48 | }
49 | }
50 | }
51 |
52 | extension RoomListViewController {
53 |
54 | /// We can define a stylesheet for the view controller and its subviews in the view controller extension.
55 | public enum Stylesheet {
56 |
57 | public static let collectionView = Style {
58 | $0.backgroundColor = UIColor(white: 0.99, alpha: 1)
59 | guard let layout = $0.collectionViewLayout as? UICollectionViewFlowLayout else { return }
60 | layout.minimumLineSpacing = 15
61 | layout.sectionInset = UIEdgeInsets(top: 15, left: 15, bottom: 15, right: 15)
62 | }
63 |
64 | public enum RoomCell {
65 |
66 | public static let contentView = Style {
67 | $0.backgroundColor = .white
68 | $0.layer.cornerRadius = 8
69 | $0.layer.shadowOpacity = 0.05
70 | $0.layer.shadowColor = UIColor.black.cgColor
71 | $0.layer.shadowOffset = .zero
72 | $0.layer.shadowRadius = 10
73 | }
74 |
75 | public static let name = Style {
76 | $0.font = .systemFont(ofSize: 18, weight: .black)
77 | $0.textColor = .purple
78 | }
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/Views/RoomViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RoomViewController.swift
3 | // Views
4 | //
5 | // Created by Srdan Rasic on 18/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Layoutless
11 |
12 | public class RoomViewController: UI.ViewController, UICollectionViewDelegateFlowLayout {
13 |
14 | public let messagesCollectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout())
15 | .styled(with: Stylesheet.collectionView)
16 |
17 | public override func setup() {
18 | messagesCollectionView.delegate = self
19 | }
20 |
21 | // Using Layoutless to declaratively define layout.
22 | public override var subviewsLayout: AnyLayout {
23 | return messagesCollectionView.fillingParent()
24 | }
25 |
26 | // MARK: UICollectionViewDelegateFlowLayout
27 |
28 | public func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
29 | return CGSize(width: collectionView.bounds.width, height: 80)
30 | }
31 | }
32 |
33 | extension RoomViewController {
34 |
35 | /// Layoutless makes view classes trivial so it's perfectly fine to define then in the view controller extensions.
36 | public class MessageCell: UI.CollectionViewCell {
37 |
38 | public let userNameLabel = UI.Label(style: Stylesheet.MessageCell.userName)
39 | public let bodyLabel = UI.Label(style: Stylesheet.MessageCell.body)
40 |
41 | public override func setup() {
42 | Stylesheet.MessageCell.contentView.apply(to: contentView)
43 | }
44 |
45 | // Using Layoutless to declaratively define layout.
46 | public override var subviewsLayout: AnyLayout {
47 | return stack(.vertical, spacing: 4)(
48 | userNameLabel,
49 | bodyLabel
50 | ).fillingParent(insets: (left: 20, right: 20, top: 10, bottom: 10))
51 | }
52 | }
53 | }
54 |
55 | extension RoomViewController {
56 |
57 | /// We can define a stylesheet for the view controller and its subviews in the view controller extension.
58 | public enum Stylesheet {
59 |
60 | public static let collectionView = Style {
61 | $0.backgroundColor = UIColor(white: 0.99, alpha: 1)
62 | guard let layout = $0.collectionViewLayout as? UICollectionViewFlowLayout else { return }
63 | layout.minimumLineSpacing = 0
64 | }
65 |
66 | public enum MessageCell {
67 |
68 | public static let contentView = Style {
69 | $0.backgroundColor = .white
70 | }
71 |
72 | public static let userName = Style {
73 | $0.font = .systemFont(ofSize: 14, weight: .bold)
74 | $0.textColor = .purple
75 | }
76 |
77 | public static let body = Style {
78 | $0.font = .systemFont(ofSize: 14)
79 | $0.textColor = .black
80 | $0.numberOfLines = 0
81 | $0.preferredMaxLayoutWidth = 300
82 | }
83 | }
84 | }
85 | }
86 |
87 |
--------------------------------------------------------------------------------
/API/Endpoints/Room.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Room.swift
3 | // API
4 | //
5 | // Created by Srdan Rasic on 17/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import Entities
10 | import Client
11 |
12 | // We can model our API by defining requests on our entities as extensions.
13 | // GitterRequest does not do any work, it's just a descriptor that defines
14 | // how to make the URLRequest (done by the Client).
15 |
16 | // Static functions operate on all entities (resource index).
17 | // Instance function operate on the entity instance (single resource).
18 |
19 | extension Room {
20 |
21 | public static func get() -> GitterRequest<[Room]> {
22 | return GitterRequest(
23 | path: "rooms",
24 | method: .get,
25 | authorization: .required
26 | )
27 | }
28 |
29 | public static func query(_ q: String) -> GitterRequest<[Room]> {
30 | return GitterRequest(
31 | path: "rooms",
32 | method: .get,
33 | parameters: JSONParameters(["q": q]),
34 | authorization: .required
35 | )
36 | }
37 |
38 | public static func join(_ uri: String) -> GitterRequest {
39 | return GitterRequest(
40 | path: "rooms",
41 | method: .post,
42 | parameters: JSONParameters(["uri": uri]),
43 | authorization: .required
44 | )
45 | }
46 |
47 | public func getChannels() -> GitterRequest<[Room]> {
48 | return GitterRequest(
49 | path: "rooms/\(id)/channels",
50 | method: .get,
51 | authorization: .required
52 | )
53 | }
54 |
55 | public func sendMessage(_ text: String) -> GitterRequest {
56 | return GitterRequest(
57 | path: "rooms/\(id)/chatMessages",
58 | method: .post,
59 | parameters: JSONParameters(["text": text]),
60 | authorization: .required
61 | )
62 | }
63 |
64 | public func getMessages(limit: Int? = nil, skip: Int? = nil) -> GitterRequest<[Message]> {
65 | let parameters: [String: String?] = ["limit": limit.flatMap { "\($0)"}, "skip": skip.flatMap { "\($0)" }]
66 | return GitterRequest(
67 | path: "rooms/\(id)/chatMessages",
68 | method: .get,
69 | parameters: QueryParameters(parameters.nonNils),
70 | authorization: .required
71 | )
72 | }
73 |
74 | public func getUsers() -> GitterRequest<[User]> {
75 | return GitterRequest(
76 | path: "rooms/\(id)/users",
77 | method: .get,
78 | authorization: .required
79 | )
80 | }
81 |
82 | public func update(topic: String? = nil, noindex: Bool? = nil, tags: String? = nil) -> GitterRequest {
83 | let parameters: [String: Any?] = ["topic": topic, "noindex": noindex, "tags": tags]
84 | return GitterRequest(
85 | path: "rooms",
86 | method: .put,
87 | parameters: JSONParameters(parameters.nonNils),
88 | authorization: .required
89 | )
90 | }
91 |
92 | public func leave() -> GitterRequest { // void?
93 | return GitterRequest(
94 | path: "rooms",
95 | method: .delete,
96 | authorization: .required
97 | )
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/Client/Utilities.swift:
--------------------------------------------------------------------------------
1 | //
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2017 Srdan Rasic (@srdanrasic)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 | //
24 |
25 | import Foundation
26 |
27 | extension String {
28 |
29 | func appendingPathComponent(_ pathComponent: String) -> String {
30 | return NSString(string: self).appendingPathComponent(pathComponent)
31 | }
32 | }
33 |
34 | extension Dictionary {
35 |
36 | var jsonString: String {
37 | let json = try! JSONSerialization.data(withJSONObject: self, options: [])
38 | return String(data: json, encoding: .utf8)!
39 | }
40 | }
41 |
42 |
43 | extension Array {
44 |
45 | var jsonString: String {
46 | let json = try! JSONSerialization.data(withJSONObject: self, options: [])
47 | return String(data: json, encoding: .utf8)!
48 | }
49 | }
50 |
51 | extension Dictionary {
52 |
53 | var keyValuePairs: String {
54 | return map { kv in
55 | let key = kv.key
56 | let value = "\(kv.value)".addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
57 | return "\(key)=\(value)"
58 | }.joined(separator: "&")
59 | }
60 | }
61 |
62 | extension Dictionary {
63 |
64 | public mutating func merge(contentsOf dictionary: [Key: Value]) {
65 | dictionary.forEach { key, value in
66 | self[key] = value
67 | }
68 | }
69 |
70 | public func merging(contentsOf dictionary: [Key: Value]) -> [Key: Value] {
71 | var me = self
72 | me.merge(contentsOf: dictionary)
73 | return me
74 | }
75 | }
76 |
77 | public protocol OptionalProtocol {
78 | associatedtype Wrapped
79 | var _unbox: Optional { get }
80 | init(nilLiteral: ())
81 | init(_ some: Wrapped)
82 | }
83 |
84 | extension Optional: OptionalProtocol {
85 | public var _unbox: Optional {
86 | return self
87 | }
88 | }
89 |
90 | extension Dictionary where Value: OptionalProtocol {
91 |
92 | public var nonNils: [Key: Value.Wrapped] {
93 | var result: [Key: Value.Wrapped] = [:]
94 |
95 | forEach { pair in
96 | if let value = pair.value._unbox {
97 | result[pair.key] = value
98 | }
99 | }
100 |
101 | return result
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/Services/SessionManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SessionManager.swift
3 | // Services
4 | //
5 | // Created by Srdan Rasic on 18/02/2018.
6 | // Copyright © 2018 DeclarativeHub. All rights reserved.
7 | //
8 |
9 | import API
10 | import Entities
11 | import ReactiveKit
12 |
13 | /// Manages current session.
14 | /// When user logs in, new AuthenticatedSession is created and set as a current session.
15 | /// When user logs out, new UnauthenticatedSession is created and set as a current session.
16 | public class SessionManager: DisposeBagProvider {
17 |
18 | public enum Session {
19 | case authenticated(AuthenticatedSession)
20 | case unauthenticated(UnauthenticatedSession)
21 | }
22 |
23 | public let currentSession: Property
24 | public let bag = DisposeBag()
25 |
26 | public init() {
27 | // If we already have an access token, start with AuthenticatedSession,
28 | // otherwise start with UnauthenticatedSession.
29 | if let accessToken = SessionManager.savedAccessToken {
30 | let session = AuthenticatedSession(accessToken)
31 | currentSession = Property(.authenticated(session))
32 | session.delegate = self
33 | } else {
34 | let session = UnauthenticatedSession()
35 | currentSession = Property(.unauthenticated(session))
36 | session.delegate = self
37 | }
38 | }
39 |
40 | /// Called by the AppDelegete when the app receives `application(:open:)`.
41 | public func handleOpenUrl(_ url: URL) -> Bool {
42 | switch currentSession.value {
43 | case .unauthenticated(let session):
44 | return session.loginService.handleOpenUrl(url)
45 | default:
46 | return false
47 | }
48 | }
49 |
50 | // MARK: Access Token Management
51 | // Note: Never save your access token to UserDefaults, use Keychain instead! This is just a demo app.
52 |
53 | private func saveAccessToken(_ accessToken: AccessToken) {
54 | let encoder = JSONEncoder()
55 | if let data = try? encoder.encode(accessToken) {
56 | UserDefaults.standard.set(data, forKey: "access-token")
57 | UserDefaults.standard.synchronize()
58 | }
59 | }
60 |
61 | private func removeAccessToken() {
62 | UserDefaults.standard.set(nil, forKey: "access-token")
63 | UserDefaults.standard.synchronize()
64 | }
65 |
66 | private static var savedAccessToken: AccessToken? {
67 | guard let data = UserDefaults.standard.object(forKey: "access-token") as? Data else { return nil }
68 | let decoder = JSONDecoder()
69 | return try? decoder.decode(AccessToken.self, from: data)
70 | }
71 | }
72 |
73 | extension SessionManager: UnauthenticatedSessionDelegate {
74 |
75 | public func didObtainAccessToken(_ accessToken: AccessToken) {
76 | /// Save access token and create new authenticated session
77 | saveAccessToken(accessToken)
78 | let session = AuthenticatedSession(accessToken)
79 | session.delegate = self
80 | currentSession.value = .authenticated(session)
81 | }
82 | }
83 |
84 | extension SessionManager: AuthenticatedSessionDelegate {
85 |
86 | public func didInvalidateAuthenticatedSession(_ session: AuthenticatedSession) {
87 | /// Remove access token and create new unauthenticated session
88 | removeAccessToken()
89 | let session = UnauthenticatedSession()
90 | session.delegate = self
91 | currentSession.value = .unauthenticated(session)
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/Client/RequestParameters.swift:
--------------------------------------------------------------------------------
1 | //
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2017 Srdan Rasic (@srdanrasic)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 | //
24 |
25 | import Foundation
26 |
27 | public protocol RequestParameters {
28 | func apply(urlRequest: URLRequest) -> URLRequest
29 | }
30 |
31 | public struct FormParameters: RequestParameters {
32 |
33 | public let data: [String: Any]
34 |
35 | public init(_ data: [String: Any]) {
36 | self.data = data
37 | }
38 |
39 | public func apply(urlRequest: URLRequest) -> URLRequest {
40 | var request = urlRequest
41 | request.setValue("application/x-www-form-urlencoded;charset=UTF-8", forHTTPHeaderField: "Content-Type")
42 | request.httpBody = data.keyValuePairs.data(using: .utf8)
43 | return request
44 | }
45 | }
46 |
47 | public struct JSONParameters: RequestParameters {
48 |
49 | public let json: Any
50 |
51 | public init(_ json: Any) {
52 | self.json = json
53 | }
54 |
55 | public func apply(urlRequest: URLRequest) -> URLRequest {
56 | var request = urlRequest
57 | request.setValue("application/json", forHTTPHeaderField: "Content-Type")
58 |
59 | if let data = try? JSONSerialization.data(withJSONObject: json, options: []) {
60 | request.httpBody = data
61 | } else {
62 | fatalError()
63 | }
64 |
65 | return request
66 | }
67 | }
68 |
69 | public struct QueryParameters: RequestParameters {
70 |
71 | public let query: [String: String]
72 |
73 | public init(_ query: [String: String]) {
74 | self.query = query
75 | }
76 |
77 | public func apply(urlRequest: URLRequest) -> URLRequest {
78 | var request = urlRequest
79 | var urlComponents = URLComponents(url: request.url!, resolvingAgainstBaseURL: false)!
80 | var items = urlComponents.queryItems ?? []
81 | items.append(contentsOf: query.map { URLQueryItem(name: $0.key, value: $0.value) })
82 | urlComponents.queryItems = items
83 | request.url = urlComponents.url
84 | return request
85 | }
86 | }
87 |
88 | public struct StringParameter: RequestParameters {
89 |
90 | public let string: String
91 |
92 | public init(_ string: String) {
93 | self.string = string
94 | }
95 |
96 | public func apply(urlRequest: URLRequest) -> URLRequest {
97 | var request = urlRequest
98 | request.httpBody = string.data(using: .utf8)
99 | return request
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/Client/Client.swift:
--------------------------------------------------------------------------------
1 | //
2 | // The MIT License (MIT)
3 | //
4 | // Copyright (c) 2017 Srdan Rasic (@srdanrasic)
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in
14 | // all copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | // THE SOFTWARE.
23 | //
24 |
25 | import Foundation
26 | import UIKit
27 |
28 | open class Client {
29 |
30 | public enum Error: Swift.Error, LocalizedError {
31 | case network(Swift.Error, Int)
32 | case remote(Swift.Error, Int)
33 | case parser(Swift.Error)
34 | case client(String)
35 | }
36 |
37 | public let baseURL: String
38 | public let session: URLSession
39 | public var defaultHeaders: [String: String] = [:]
40 |
41 | public init(baseURL: String, session: URLSession = URLSession(configuration: URLSessionConfiguration.default)) {
42 | self.baseURL = baseURL
43 | self.session = session
44 | }
45 |
46 | /// For example, authorize request with the token.
47 | open func prepare(request: Request) -> Request {
48 | return request
49 | }
50 |
51 | open func requestUrl(for request: Request) -> URL {
52 | return URL(string: baseURL.appendingPathComponent(request.path))!
53 | }
54 |
55 | /// Perform request.
56 | @discardableResult
57 | open func perform(_ request: Request, completion: @escaping (Result) -> Void) -> URLSessionTask {
58 |
59 | let request = prepare(request: request)
60 | let headers = defaultHeaders.merging(contentsOf: request.headers ?? [:])
61 | let url = requestUrl(for: request)
62 |
63 | var urlRequest = URLRequest(url: url)
64 | urlRequest.httpMethod = request.method.rawValue
65 | headers.forEach { urlRequest.addValue($1, forHTTPHeaderField: $0) }
66 |
67 | if let parameters = request.parameters {
68 | urlRequest = parameters.apply(urlRequest: urlRequest)
69 | }
70 |
71 | DispatchQueue.main.async {
72 | UIApplication.shared.isNetworkActivityIndicatorVisible = true
73 | }
74 |
75 | let task = self.session.dataTask(with: urlRequest) { (data, urlResponse, error) in
76 | DispatchQueue.main.async {
77 | UIApplication.shared.isNetworkActivityIndicatorVisible = false
78 | }
79 |
80 | guard let urlResponse = urlResponse as? HTTPURLResponse else {
81 | if let error = error {
82 | completion(.failure(.network(error, 0)))
83 | } else {
84 | completion(.failure(.client("Did not receive HTTPURLResponse. Huh?")))
85 | }
86 | return
87 | }
88 |
89 | if let error = error {
90 | if let data = data, let serverError = try? request.error(data) {
91 | completion(.failure(.remote(serverError, urlResponse.statusCode)))
92 | } else {
93 | completion(.failure(.network(error, urlResponse.statusCode)))
94 | }
95 | return
96 | }
97 |
98 | guard (200..<300).contains(urlResponse.statusCode) else {
99 | if let data = data, let error = try? request.error(data) {
100 | completion(.failure(.remote(error, urlResponse.statusCode)))
101 | } else {
102 | let message = "HTTP status code validation failed. Received \(urlResponse.statusCode)."
103 | let error = Client.Error.client(message)
104 | completion(.failure(.remote(error, urlResponse.statusCode)))
105 | }
106 | return
107 | }
108 |
109 | if let data = data {
110 | do {
111 | let resource = try request.resource(data)
112 | completion(.success(resource))
113 | } catch let error as Client.Error {
114 | completion(.failure(error))
115 | } catch let error {
116 | completion(.failure(.parser(error)))
117 | }
118 | } else {
119 | // no error, no data - valid empty response
120 | do {
121 | let resource = try request.resource(Data())
122 | completion(.success(resource))
123 | } catch let error as Client.Error {
124 | completion(.failure(error))
125 | } catch let error {
126 | completion(.failure(.parser(error)))
127 | }
128 | }
129 | }
130 |
131 | task.resume()
132 | return task
133 | }
134 | }
135 |
136 | extension Client.Error {
137 |
138 | public var code: Int? {
139 | switch self {
140 | case .network(_, let code):
141 | return code
142 | case .remote(_, let code):
143 | return code
144 | default:
145 | return 0
146 | }
147 | }
148 |
149 | public var errorDescription: String? {
150 | switch self {
151 | case .network(let error, _):
152 | return error.localizedDescription
153 | case .remote(let error, _):
154 | return error.localizedDescription
155 | case .parser(let error):
156 | return error.localizedDescription
157 | case .client(let message):
158 | return message
159 | }
160 | }
161 | }
162 |
--------------------------------------------------------------------------------
/AbsurdGitter.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 48;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | EC966F08204B14630016B4FC /* SessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B81E2039AD64006C40B4 /* SessionManager.swift */; };
11 | EC966F09204B146C0016B4FC /* AuthenticatedSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B7DE20389C5B006C40B4 /* AuthenticatedSession.swift */; };
12 | EC966F0A204B14720016B4FC /* UnauthenticatedSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B81C2039AD13006C40B4 /* UnauthenticatedSession.swift */; };
13 | EC966F0B204B147C0016B4FC /* LoginService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B8202039AD7E006C40B4 /* LoginService.swift */; };
14 | EC966F0C204B147C0016B4FC /* UserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B7E020389C9F006C40B4 /* UserService.swift */; };
15 | EC966F0D204B147C0016B4FC /* RoomService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B7E720389E66006C40B4 /* RoomService.swift */; };
16 | EC966F0E204B147C0016B4FC /* MessageService.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B7EB20398A7B006C40B4 /* MessageService.swift */; };
17 | ECA3006B206D0F7300F6EA37 /* MainTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA3006A206D0F7300F6EA37 /* MainTabBarController.swift */; };
18 | ECA3006D206D142E00F6EA37 /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA3006C206D142E00F6EA37 /* ProfileViewController.swift */; };
19 | ECA3006F206D14FE00F6EA37 /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA3006E206D14FE00F6EA37 /* ProfileViewController.swift */; };
20 | ECA30070206D1C3100F6EA37 /* ApplicationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B7E920398932006C40B4 /* ApplicationError.swift */; };
21 | ECA30072206D40AD00F6EA37 /* RoomNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECA30068206CD93100F6EA37 /* RoomNavigationController.swift */; };
22 | ECE7B74C203867B0006C40B4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B74B203867B0006C40B4 /* AppDelegate.swift */; };
23 | ECE7B753203867B0006C40B4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = ECE7B752203867B0006C40B4 /* Assets.xcassets */; };
24 | ECE7B756203867B0006C40B4 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = ECE7B754203867B0006C40B4 /* LaunchScreen.storyboard */; };
25 | ECE7B76A20386931006C40B4 /* Client.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B76320386931006C40B4 /* Client.framework */; };
26 | ECE7B76C20386932006C40B4 /* Client.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B76320386931006C40B4 /* Client.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
27 | ECE7B77520386948006C40B4 /* ReactiveKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B77120386948006C40B4 /* ReactiveKit.framework */; };
28 | ECE7B77620386948006C40B4 /* Bond.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B77220386948006C40B4 /* Bond.framework */; };
29 | ECE7B77720386948006C40B4 /* Differ.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B77320386948006C40B4 /* Differ.framework */; };
30 | ECE7B77820386948006C40B4 /* Layoutless.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B77420386948006C40B4 /* Layoutless.framework */; };
31 | ECE7B78120386973006C40B4 /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B77A20386972006C40B4 /* Utilities.swift */; };
32 | ECE7B78220386973006C40B4 /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B77B20386972006C40B4 /* Result.swift */; };
33 | ECE7B78320386973006C40B4 /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B77C20386972006C40B4 /* HTTPMethod.swift */; };
34 | ECE7B78420386973006C40B4 /* Client.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B77D20386972006C40B4 /* Client.swift */; };
35 | ECE7B78520386973006C40B4 /* Request.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B77E20386972006C40B4 /* Request.swift */; };
36 | ECE7B78620386973006C40B4 /* LoggingClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B77F20386972006C40B4 /* LoggingClient.swift */; };
37 | ECE7B78720386973006C40B4 /* RequestParameters.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B78020386972006C40B4 /* RequestParameters.swift */; };
38 | ECE7B78A203869D7006C40B4 /* FrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = ECE7B789203869D7006C40B4 /* FrameworkInfo.plist */; };
39 | ECE7B78C20386AAB006C40B4 /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B78B20386AAB006C40B4 /* Logger.swift */; };
40 | ECE7B79920386EA7006C40B4 /* Entities.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B79220386EA7006C40B4 /* Entities.framework */; };
41 | ECE7B79A20386EA7006C40B4 /* Entities.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B79220386EA7006C40B4 /* Entities.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
42 | ECE7B79E20386EFD006C40B4 /* Client.h in Headers */ = {isa = PBXBuildFile; fileRef = ECE7B76520386931006C40B4 /* Client.h */; settings = {ATTRIBUTES = (Public, ); }; };
43 | ECE7B79F20386F0E006C40B4 /* Entities.h in Headers */ = {isa = PBXBuildFile; fileRef = ECE7B79420386EA7006C40B4 /* Entities.h */; settings = {ATTRIBUTES = (Public, ); }; };
44 | ECE7B7A120386F2F006C40B4 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B7A020386F2F006C40B4 /* Message.swift */; };
45 | ECE7B7A320386FB8006C40B4 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B7A220386FB8006C40B4 /* User.swift */; };
46 | ECE7B7A520386FE7006C40B4 /* Room.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B7A420386FE7006C40B4 /* Room.swift */; };
47 | ECE7B7A72038703C006C40B4 /* AccessToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B7A62038703C006C40B4 /* AccessToken.swift */; };
48 | ECE7B7B1203889DD006C40B4 /* API.h in Headers */ = {isa = PBXBuildFile; fileRef = ECE7B7AF203889DD006C40B4 /* API.h */; settings = {ATTRIBUTES = (Public, ); }; };
49 | ECE7B7B4203889DD006C40B4 /* API.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B7AD203889DD006C40B4 /* API.framework */; };
50 | ECE7B7B5203889DD006C40B4 /* API.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B7AD203889DD006C40B4 /* API.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
51 | ECE7B7BA20388A1B006C40B4 /* Message.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B7B920388A1B006C40B4 /* Message.swift */; };
52 | ECE7B7BD20388A82006C40B4 /* GitterRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B7BC20388A82006C40B4 /* GitterRequest.swift */; };
53 | ECE7B7BE20388B0A006C40B4 /* ReactiveKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B77120386948006C40B4 /* ReactiveKit.framework */; };
54 | ECE7B7C020388B60006C40B4 /* GitterClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B7BF20388B60006C40B4 /* GitterClient.swift */; };
55 | ECE7B7C220388B74006C40B4 /* GitterError.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B7C120388B74006C40B4 /* GitterError.swift */; };
56 | ECE7B7C420388F7B006C40B4 /* Room.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B7C320388F7B006C40B4 /* Room.swift */; };
57 | ECE7B7C620389175006C40B4 /* User.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B7C520389175006C40B4 /* User.swift */; };
58 | ECE7B7C8203891E5006C40B4 /* OAuth.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B7C7203891E5006C40B4 /* OAuth.swift */; };
59 | ECE7B7D620389C2C006C40B4 /* Services.h in Headers */ = {isa = PBXBuildFile; fileRef = ECE7B7D420389C2C006C40B4 /* Services.h */; settings = {ATTRIBUTES = (Public, ); }; };
60 | ECE7B7D920389C2C006C40B4 /* Services.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B7D220389C2C006C40B4 /* Services.framework */; };
61 | ECE7B7DA20389C2C006C40B4 /* Services.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B7D220389C2C006C40B4 /* Services.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
62 | ECE7B7E420389CFC006C40B4 /* ReactiveKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B77120386948006C40B4 /* ReactiveKit.framework */; };
63 | ECE7B7F62039A76B006C40B4 /* Views.h in Headers */ = {isa = PBXBuildFile; fileRef = ECE7B7F42039A76B006C40B4 /* Views.h */; settings = {ATTRIBUTES = (Public, ); }; };
64 | ECE7B7F92039A76B006C40B4 /* Views.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B7F22039A76B006C40B4 /* Views.framework */; };
65 | ECE7B7FA2039A76B006C40B4 /* Views.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B7F22039A76B006C40B4 /* Views.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
66 | ECE7B7FF2039A79B006C40B4 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B7FE2039A79B006C40B4 /* LoginViewController.swift */; };
67 | ECE7B8002039A7B0006C40B4 /* Layoutless.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B77420386948006C40B4 /* Layoutless.framework */; };
68 | ECE7B8022039A840006C40B4 /* RoomListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B8012039A840006C40B4 /* RoomListViewController.swift */; };
69 | ECE7B80C2039AA14006C40B4 /* Binders.h in Headers */ = {isa = PBXBuildFile; fileRef = ECE7B80A2039AA14006C40B4 /* Binders.h */; settings = {ATTRIBUTES = (Public, ); }; };
70 | ECE7B80F2039AA14006C40B4 /* Binders.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B8082039AA14006C40B4 /* Binders.framework */; };
71 | ECE7B8102039AA14006C40B4 /* Binders.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B8082039AA14006C40B4 /* Binders.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
72 | ECE7B8152039AA34006C40B4 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B8142039AA34006C40B4 /* LoginViewController.swift */; };
73 | ECE7B8172039AA55006C40B4 /* MainWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B8162039AA55006C40B4 /* MainWindow.swift */; };
74 | ECE7B8222039AF9D006C40B4 /* Bond.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B77220386948006C40B4 /* Bond.framework */; };
75 | ECE7B8232039AFA0006C40B4 /* Differ.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B77320386948006C40B4 /* Differ.framework */; };
76 | ECE7B8242039B330006C40B4 /* ReactiveKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B77120386948006C40B4 /* ReactiveKit.framework */; };
77 | ECE7B8252039B341006C40B4 /* Layoutless.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ECE7B77420386948006C40B4 /* Layoutless.framework */; };
78 | ECE7B8272039B62B006C40B4 /* RoomListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B8262039B62B006C40B4 /* RoomListViewController.swift */; };
79 | ECE7B82B2039C2CE006C40B4 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B82A2039C2CE006C40B4 /* Extensions.swift */; };
80 | ECE7B82F2039D791006C40B4 /* RoomViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B82E2039D791006C40B4 /* RoomViewController.swift */; };
81 | ECE7B8312039D819006C40B4 /* RoomViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ECE7B8302039D819006C40B4 /* RoomViewController.swift */; };
82 | /* End PBXBuildFile section */
83 |
84 | /* Begin PBXContainerItemProxy section */
85 | ECE7B76820386931006C40B4 /* PBXContainerItemProxy */ = {
86 | isa = PBXContainerItemProxy;
87 | containerPortal = ECE7B740203867AF006C40B4 /* Project object */;
88 | proxyType = 1;
89 | remoteGlobalIDString = ECE7B76220386931006C40B4;
90 | remoteInfo = Client;
91 | };
92 | ECE7B79720386EA7006C40B4 /* PBXContainerItemProxy */ = {
93 | isa = PBXContainerItemProxy;
94 | containerPortal = ECE7B740203867AF006C40B4 /* Project object */;
95 | proxyType = 1;
96 | remoteGlobalIDString = ECE7B79120386EA7006C40B4;
97 | remoteInfo = Entities;
98 | };
99 | ECE7B7B2203889DD006C40B4 /* PBXContainerItemProxy */ = {
100 | isa = PBXContainerItemProxy;
101 | containerPortal = ECE7B740203867AF006C40B4 /* Project object */;
102 | proxyType = 1;
103 | remoteGlobalIDString = ECE7B7AC203889DD006C40B4;
104 | remoteInfo = API;
105 | };
106 | ECE7B7C92038928A006C40B4 /* PBXContainerItemProxy */ = {
107 | isa = PBXContainerItemProxy;
108 | containerPortal = ECE7B740203867AF006C40B4 /* Project object */;
109 | proxyType = 1;
110 | remoteGlobalIDString = ECE7B76220386931006C40B4;
111 | remoteInfo = Client;
112 | };
113 | ECE7B7CB2038928A006C40B4 /* PBXContainerItemProxy */ = {
114 | isa = PBXContainerItemProxy;
115 | containerPortal = ECE7B740203867AF006C40B4 /* Project object */;
116 | proxyType = 1;
117 | remoteGlobalIDString = ECE7B79120386EA7006C40B4;
118 | remoteInfo = Entities;
119 | };
120 | ECE7B7D720389C2C006C40B4 /* PBXContainerItemProxy */ = {
121 | isa = PBXContainerItemProxy;
122 | containerPortal = ECE7B740203867AF006C40B4 /* Project object */;
123 | proxyType = 1;
124 | remoteGlobalIDString = ECE7B7D120389C2C006C40B4;
125 | remoteInfo = Services;
126 | };
127 | ECE7B7E520389DD4006C40B4 /* PBXContainerItemProxy */ = {
128 | isa = PBXContainerItemProxy;
129 | containerPortal = ECE7B740203867AF006C40B4 /* Project object */;
130 | proxyType = 1;
131 | remoteGlobalIDString = ECE7B7AC203889DD006C40B4;
132 | remoteInfo = API;
133 | };
134 | ECE7B7F72039A76B006C40B4 /* PBXContainerItemProxy */ = {
135 | isa = PBXContainerItemProxy;
136 | containerPortal = ECE7B740203867AF006C40B4 /* Project object */;
137 | proxyType = 1;
138 | remoteGlobalIDString = ECE7B7F12039A76B006C40B4;
139 | remoteInfo = Views;
140 | };
141 | ECE7B80D2039AA14006C40B4 /* PBXContainerItemProxy */ = {
142 | isa = PBXContainerItemProxy;
143 | containerPortal = ECE7B740203867AF006C40B4 /* Project object */;
144 | proxyType = 1;
145 | remoteGlobalIDString = ECE7B8072039AA14006C40B4;
146 | remoteInfo = Binders;
147 | };
148 | ECE7B8182039AACF006C40B4 /* PBXContainerItemProxy */ = {
149 | isa = PBXContainerItemProxy;
150 | containerPortal = ECE7B740203867AF006C40B4 /* Project object */;
151 | proxyType = 1;
152 | remoteGlobalIDString = ECE7B7D120389C2C006C40B4;
153 | remoteInfo = Services;
154 | };
155 | ECE7B81A2039AACF006C40B4 /* PBXContainerItemProxy */ = {
156 | isa = PBXContainerItemProxy;
157 | containerPortal = ECE7B740203867AF006C40B4 /* Project object */;
158 | proxyType = 1;
159 | remoteGlobalIDString = ECE7B7F12039A76B006C40B4;
160 | remoteInfo = Views;
161 | };
162 | /* End PBXContainerItemProxy section */
163 |
164 | /* Begin PBXCopyFilesBuildPhase section */
165 | ECE7B76B20386931006C40B4 /* Embed Frameworks */ = {
166 | isa = PBXCopyFilesBuildPhase;
167 | buildActionMask = 2147483647;
168 | dstPath = "";
169 | dstSubfolderSpec = 10;
170 | files = (
171 | ECE7B8102039AA14006C40B4 /* Binders.framework in Embed Frameworks */,
172 | ECE7B76C20386932006C40B4 /* Client.framework in Embed Frameworks */,
173 | ECE7B7FA2039A76B006C40B4 /* Views.framework in Embed Frameworks */,
174 | ECE7B7B5203889DD006C40B4 /* API.framework in Embed Frameworks */,
175 | ECE7B7DA20389C2C006C40B4 /* Services.framework in Embed Frameworks */,
176 | ECE7B79A20386EA7006C40B4 /* Entities.framework in Embed Frameworks */,
177 | );
178 | name = "Embed Frameworks";
179 | runOnlyForDeploymentPostprocessing = 0;
180 | };
181 | /* End PBXCopyFilesBuildPhase section */
182 |
183 | /* Begin PBXFileReference section */
184 | ECA30068206CD93100F6EA37 /* RoomNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNavigationController.swift; sourceTree = ""; };
185 | ECA3006A206D0F7300F6EA37 /* MainTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabBarController.swift; sourceTree = ""; };
186 | ECA3006C206D142E00F6EA37 /* ProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = ""; };
187 | ECA3006E206D14FE00F6EA37 /* ProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = ""; };
188 | ECE7B748203867B0006C40B4 /* AbsurdGitter.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AbsurdGitter.app; sourceTree = BUILT_PRODUCTS_DIR; };
189 | ECE7B74B203867B0006C40B4 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
190 | ECE7B752203867B0006C40B4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
191 | ECE7B755203867B0006C40B4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
192 | ECE7B757203867B0006C40B4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
193 | ECE7B76320386931006C40B4 /* Client.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Client.framework; sourceTree = BUILT_PRODUCTS_DIR; };
194 | ECE7B76520386931006C40B4 /* Client.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Client.h; sourceTree = ""; };
195 | ECE7B77120386948006C40B4 /* ReactiveKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReactiveKit.framework; path = Carthage/Build/iOS/ReactiveKit.framework; sourceTree = ""; };
196 | ECE7B77220386948006C40B4 /* Bond.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Bond.framework; path = Carthage/Build/iOS/Bond.framework; sourceTree = ""; };
197 | ECE7B77320386948006C40B4 /* Differ.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Differ.framework; path = Carthage/Build/iOS/Differ.framework; sourceTree = ""; };
198 | ECE7B77420386948006C40B4 /* Layoutless.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Layoutless.framework; path = Carthage/Build/iOS/Layoutless.framework; sourceTree = ""; };
199 | ECE7B77A20386972006C40B4 /* Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = ""; };
200 | ECE7B77B20386972006C40B4 /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = ""; };
201 | ECE7B77C20386972006C40B4 /* HTTPMethod.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPMethod.swift; sourceTree = ""; };
202 | ECE7B77D20386972006C40B4 /* Client.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Client.swift; sourceTree = ""; };
203 | ECE7B77E20386972006C40B4 /* Request.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Request.swift; sourceTree = ""; };
204 | ECE7B77F20386972006C40B4 /* LoggingClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoggingClient.swift; sourceTree = ""; };
205 | ECE7B78020386972006C40B4 /* RequestParameters.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestParameters.swift; sourceTree = ""; };
206 | ECE7B789203869D7006C40B4 /* FrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = FrameworkInfo.plist; sourceTree = ""; };
207 | ECE7B78B20386AAB006C40B4 /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; };
208 | ECE7B79220386EA7006C40B4 /* Entities.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Entities.framework; sourceTree = BUILT_PRODUCTS_DIR; };
209 | ECE7B79420386EA7006C40B4 /* Entities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Entities.h; sourceTree = ""; };
210 | ECE7B7A020386F2F006C40B4 /* Message.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = ""; };
211 | ECE7B7A220386FB8006C40B4 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; };
212 | ECE7B7A420386FE7006C40B4 /* Room.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Room.swift; sourceTree = ""; };
213 | ECE7B7A62038703C006C40B4 /* AccessToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessToken.swift; sourceTree = ""; };
214 | ECE7B7AD203889DD006C40B4 /* API.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = API.framework; sourceTree = BUILT_PRODUCTS_DIR; };
215 | ECE7B7AF203889DD006C40B4 /* API.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = API.h; sourceTree = ""; };
216 | ECE7B7B920388A1B006C40B4 /* Message.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Message.swift; sourceTree = ""; };
217 | ECE7B7BC20388A82006C40B4 /* GitterRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitterRequest.swift; sourceTree = ""; };
218 | ECE7B7BF20388B60006C40B4 /* GitterClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitterClient.swift; sourceTree = ""; };
219 | ECE7B7C120388B74006C40B4 /* GitterError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitterError.swift; sourceTree = ""; };
220 | ECE7B7C320388F7B006C40B4 /* Room.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Room.swift; sourceTree = ""; };
221 | ECE7B7C520389175006C40B4 /* User.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = User.swift; sourceTree = ""; };
222 | ECE7B7C7203891E5006C40B4 /* OAuth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuth.swift; sourceTree = ""; };
223 | ECE7B7D220389C2C006C40B4 /* Services.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Services.framework; sourceTree = BUILT_PRODUCTS_DIR; };
224 | ECE7B7D420389C2C006C40B4 /* Services.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Services.h; sourceTree = ""; };
225 | ECE7B7DE20389C5B006C40B4 /* AuthenticatedSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticatedSession.swift; sourceTree = ""; };
226 | ECE7B7E020389C9F006C40B4 /* UserService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserService.swift; sourceTree = ""; };
227 | ECE7B7E720389E66006C40B4 /* RoomService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomService.swift; sourceTree = ""; };
228 | ECE7B7E920398932006C40B4 /* ApplicationError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationError.swift; sourceTree = ""; };
229 | ECE7B7EB20398A7B006C40B4 /* MessageService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageService.swift; sourceTree = ""; };
230 | ECE7B7F22039A76B006C40B4 /* Views.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Views.framework; sourceTree = BUILT_PRODUCTS_DIR; };
231 | ECE7B7F42039A76B006C40B4 /* Views.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Views.h; sourceTree = ""; };
232 | ECE7B7FE2039A79B006C40B4 /* LoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; };
233 | ECE7B8012039A840006C40B4 /* RoomListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomListViewController.swift; sourceTree = ""; };
234 | ECE7B8082039AA14006C40B4 /* Binders.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Binders.framework; sourceTree = BUILT_PRODUCTS_DIR; };
235 | ECE7B80A2039AA14006C40B4 /* Binders.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Binders.h; sourceTree = ""; };
236 | ECE7B8142039AA34006C40B4 /* LoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; };
237 | ECE7B8162039AA55006C40B4 /* MainWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainWindow.swift; sourceTree = ""; };
238 | ECE7B81C2039AD13006C40B4 /* UnauthenticatedSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnauthenticatedSession.swift; sourceTree = ""; };
239 | ECE7B81E2039AD64006C40B4 /* SessionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionManager.swift; sourceTree = ""; };
240 | ECE7B8202039AD7E006C40B4 /* LoginService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginService.swift; sourceTree = ""; };
241 | ECE7B8262039B62B006C40B4 /* RoomListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomListViewController.swift; sourceTree = ""; };
242 | ECE7B82A2039C2CE006C40B4 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; };
243 | ECE7B82E2039D791006C40B4 /* RoomViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomViewController.swift; sourceTree = ""; };
244 | ECE7B8302039D819006C40B4 /* RoomViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomViewController.swift; sourceTree = ""; };
245 | /* End PBXFileReference section */
246 |
247 | /* Begin PBXFrameworksBuildPhase section */
248 | ECE7B745203867B0006C40B4 /* Frameworks */ = {
249 | isa = PBXFrameworksBuildPhase;
250 | buildActionMask = 2147483647;
251 | files = (
252 | ECE7B7B4203889DD006C40B4 /* API.framework in Frameworks */,
253 | ECE7B77520386948006C40B4 /* ReactiveKit.framework in Frameworks */,
254 | ECE7B7D920389C2C006C40B4 /* Services.framework in Frameworks */,
255 | ECE7B77620386948006C40B4 /* Bond.framework in Frameworks */,
256 | ECE7B77720386948006C40B4 /* Differ.framework in Frameworks */,
257 | ECE7B7F92039A76B006C40B4 /* Views.framework in Frameworks */,
258 | ECE7B80F2039AA14006C40B4 /* Binders.framework in Frameworks */,
259 | ECE7B77820386948006C40B4 /* Layoutless.framework in Frameworks */,
260 | ECE7B76A20386931006C40B4 /* Client.framework in Frameworks */,
261 | ECE7B79920386EA7006C40B4 /* Entities.framework in Frameworks */,
262 | );
263 | runOnlyForDeploymentPostprocessing = 0;
264 | };
265 | ECE7B75F20386931006C40B4 /* Frameworks */ = {
266 | isa = PBXFrameworksBuildPhase;
267 | buildActionMask = 2147483647;
268 | files = (
269 | );
270 | runOnlyForDeploymentPostprocessing = 0;
271 | };
272 | ECE7B78E20386EA7006C40B4 /* Frameworks */ = {
273 | isa = PBXFrameworksBuildPhase;
274 | buildActionMask = 2147483647;
275 | files = (
276 | );
277 | runOnlyForDeploymentPostprocessing = 0;
278 | };
279 | ECE7B7A9203889DD006C40B4 /* Frameworks */ = {
280 | isa = PBXFrameworksBuildPhase;
281 | buildActionMask = 2147483647;
282 | files = (
283 | ECE7B7BE20388B0A006C40B4 /* ReactiveKit.framework in Frameworks */,
284 | );
285 | runOnlyForDeploymentPostprocessing = 0;
286 | };
287 | ECE7B7CE20389C2C006C40B4 /* Frameworks */ = {
288 | isa = PBXFrameworksBuildPhase;
289 | buildActionMask = 2147483647;
290 | files = (
291 | ECE7B7E420389CFC006C40B4 /* ReactiveKit.framework in Frameworks */,
292 | );
293 | runOnlyForDeploymentPostprocessing = 0;
294 | };
295 | ECE7B7EE2039A76B006C40B4 /* Frameworks */ = {
296 | isa = PBXFrameworksBuildPhase;
297 | buildActionMask = 2147483647;
298 | files = (
299 | ECE7B8002039A7B0006C40B4 /* Layoutless.framework in Frameworks */,
300 | );
301 | runOnlyForDeploymentPostprocessing = 0;
302 | };
303 | ECE7B8042039AA14006C40B4 /* Frameworks */ = {
304 | isa = PBXFrameworksBuildPhase;
305 | buildActionMask = 2147483647;
306 | files = (
307 | ECE7B8232039AFA0006C40B4 /* Differ.framework in Frameworks */,
308 | ECE7B8252039B341006C40B4 /* Layoutless.framework in Frameworks */,
309 | ECE7B8242039B330006C40B4 /* ReactiveKit.framework in Frameworks */,
310 | ECE7B8222039AF9D006C40B4 /* Bond.framework in Frameworks */,
311 | );
312 | runOnlyForDeploymentPostprocessing = 0;
313 | };
314 | /* End PBXFrameworksBuildPhase section */
315 |
316 | /* Begin PBXGroup section */
317 | ECE7B73F203867AF006C40B4 = {
318 | isa = PBXGroup;
319 | children = (
320 | ECE7B74A203867B0006C40B4 /* AbsurdGitter */,
321 | ECE7B8092039AA14006C40B4 /* Binders */,
322 | ECE7B7F32039A76B006C40B4 /* Views */,
323 | ECE7B7D320389C2C006C40B4 /* Services */,
324 | ECE7B7AE203889DD006C40B4 /* API */,
325 | ECE7B79320386EA7006C40B4 /* Entities */,
326 | ECE7B76420386931006C40B4 /* Client */,
327 | ECE7B749203867B0006C40B4 /* Products */,
328 | ECE7B77020386948006C40B4 /* Frameworks */,
329 | );
330 | sourceTree = "";
331 | };
332 | ECE7B749203867B0006C40B4 /* Products */ = {
333 | isa = PBXGroup;
334 | children = (
335 | ECE7B748203867B0006C40B4 /* AbsurdGitter.app */,
336 | ECE7B76320386931006C40B4 /* Client.framework */,
337 | ECE7B79220386EA7006C40B4 /* Entities.framework */,
338 | ECE7B7AD203889DD006C40B4 /* API.framework */,
339 | ECE7B7D220389C2C006C40B4 /* Services.framework */,
340 | ECE7B7F22039A76B006C40B4 /* Views.framework */,
341 | ECE7B8082039AA14006C40B4 /* Binders.framework */,
342 | );
343 | name = Products;
344 | sourceTree = "";
345 | };
346 | ECE7B74A203867B0006C40B4 /* AbsurdGitter */ = {
347 | isa = PBXGroup;
348 | children = (
349 | ECE7B74B203867B0006C40B4 /* AppDelegate.swift */,
350 | ECE7B752203867B0006C40B4 /* Assets.xcassets */,
351 | ECE7B754203867B0006C40B4 /* LaunchScreen.storyboard */,
352 | ECE7B757203867B0006C40B4 /* Info.plist */,
353 | ECE7B789203869D7006C40B4 /* FrameworkInfo.plist */,
354 | );
355 | path = AbsurdGitter;
356 | sourceTree = "";
357 | };
358 | ECE7B76420386931006C40B4 /* Client */ = {
359 | isa = PBXGroup;
360 | children = (
361 | ECE7B76520386931006C40B4 /* Client.h */,
362 | ECE7B77B20386972006C40B4 /* Result.swift */,
363 | ECE7B77E20386972006C40B4 /* Request.swift */,
364 | ECE7B78020386972006C40B4 /* RequestParameters.swift */,
365 | ECE7B77C20386972006C40B4 /* HTTPMethod.swift */,
366 | ECE7B77D20386972006C40B4 /* Client.swift */,
367 | ECE7B78B20386AAB006C40B4 /* Logger.swift */,
368 | ECE7B77F20386972006C40B4 /* LoggingClient.swift */,
369 | ECE7B77A20386972006C40B4 /* Utilities.swift */,
370 | );
371 | path = Client;
372 | sourceTree = "";
373 | };
374 | ECE7B77020386948006C40B4 /* Frameworks */ = {
375 | isa = PBXGroup;
376 | children = (
377 | ECE7B77220386948006C40B4 /* Bond.framework */,
378 | ECE7B77320386948006C40B4 /* Differ.framework */,
379 | ECE7B77420386948006C40B4 /* Layoutless.framework */,
380 | ECE7B77120386948006C40B4 /* ReactiveKit.framework */,
381 | );
382 | name = Frameworks;
383 | sourceTree = "";
384 | };
385 | ECE7B79320386EA7006C40B4 /* Entities */ = {
386 | isa = PBXGroup;
387 | children = (
388 | ECE7B79420386EA7006C40B4 /* Entities.h */,
389 | ECE7B7A220386FB8006C40B4 /* User.swift */,
390 | ECE7B7A420386FE7006C40B4 /* Room.swift */,
391 | ECE7B7A020386F2F006C40B4 /* Message.swift */,
392 | ECE7B7A62038703C006C40B4 /* AccessToken.swift */,
393 | ECE7B7E920398932006C40B4 /* ApplicationError.swift */,
394 | );
395 | path = Entities;
396 | sourceTree = "";
397 | };
398 | ECE7B7AE203889DD006C40B4 /* API */ = {
399 | isa = PBXGroup;
400 | children = (
401 | ECE7B7AF203889DD006C40B4 /* API.h */,
402 | ECE7B7BC20388A82006C40B4 /* GitterRequest.swift */,
403 | ECE7B7C120388B74006C40B4 /* GitterError.swift */,
404 | ECE7B7BF20388B60006C40B4 /* GitterClient.swift */,
405 | ECE7B7BB20388A1F006C40B4 /* Endpoints */,
406 | );
407 | path = API;
408 | sourceTree = "";
409 | };
410 | ECE7B7BB20388A1F006C40B4 /* Endpoints */ = {
411 | isa = PBXGroup;
412 | children = (
413 | ECE7B7B920388A1B006C40B4 /* Message.swift */,
414 | ECE7B7C320388F7B006C40B4 /* Room.swift */,
415 | ECE7B7C520389175006C40B4 /* User.swift */,
416 | ECE7B7C7203891E5006C40B4 /* OAuth.swift */,
417 | );
418 | path = Endpoints;
419 | sourceTree = "";
420 | };
421 | ECE7B7D320389C2C006C40B4 /* Services */ = {
422 | isa = PBXGroup;
423 | children = (
424 | ECE7B7D420389C2C006C40B4 /* Services.h */,
425 | ECE7B81E2039AD64006C40B4 /* SessionManager.swift */,
426 | ECE7B7DE20389C5B006C40B4 /* AuthenticatedSession.swift */,
427 | ECE7B81C2039AD13006C40B4 /* UnauthenticatedSession.swift */,
428 | ECE7B8202039AD7E006C40B4 /* LoginService.swift */,
429 | ECE7B7E020389C9F006C40B4 /* UserService.swift */,
430 | ECE7B7E720389E66006C40B4 /* RoomService.swift */,
431 | ECE7B7EB20398A7B006C40B4 /* MessageService.swift */,
432 | );
433 | path = Services;
434 | sourceTree = "";
435 | };
436 | ECE7B7F32039A76B006C40B4 /* Views */ = {
437 | isa = PBXGroup;
438 | children = (
439 | ECE7B7F42039A76B006C40B4 /* Views.h */,
440 | ECE7B7FE2039A79B006C40B4 /* LoginViewController.swift */,
441 | ECE7B8012039A840006C40B4 /* RoomListViewController.swift */,
442 | ECE7B82E2039D791006C40B4 /* RoomViewController.swift */,
443 | ECA3006C206D142E00F6EA37 /* ProfileViewController.swift */,
444 | );
445 | path = Views;
446 | sourceTree = "";
447 | };
448 | ECE7B8092039AA14006C40B4 /* Binders */ = {
449 | isa = PBXGroup;
450 | children = (
451 | ECE7B80A2039AA14006C40B4 /* Binders.h */,
452 | ECE7B8162039AA55006C40B4 /* MainWindow.swift */,
453 | ECE7B8142039AA34006C40B4 /* LoginViewController.swift */,
454 | ECA3006A206D0F7300F6EA37 /* MainTabBarController.swift */,
455 | ECA30068206CD93100F6EA37 /* RoomNavigationController.swift */,
456 | ECE7B8262039B62B006C40B4 /* RoomListViewController.swift */,
457 | ECE7B8302039D819006C40B4 /* RoomViewController.swift */,
458 | ECA3006E206D14FE00F6EA37 /* ProfileViewController.swift */,
459 | ECE7B82A2039C2CE006C40B4 /* Extensions.swift */,
460 | );
461 | path = Binders;
462 | sourceTree = "";
463 | };
464 | /* End PBXGroup section */
465 |
466 | /* Begin PBXHeadersBuildPhase section */
467 | ECE7B76020386931006C40B4 /* Headers */ = {
468 | isa = PBXHeadersBuildPhase;
469 | buildActionMask = 2147483647;
470 | files = (
471 | ECE7B79E20386EFD006C40B4 /* Client.h in Headers */,
472 | );
473 | runOnlyForDeploymentPostprocessing = 0;
474 | };
475 | ECE7B78F20386EA7006C40B4 /* Headers */ = {
476 | isa = PBXHeadersBuildPhase;
477 | buildActionMask = 2147483647;
478 | files = (
479 | ECE7B79F20386F0E006C40B4 /* Entities.h in Headers */,
480 | );
481 | runOnlyForDeploymentPostprocessing = 0;
482 | };
483 | ECE7B7AA203889DD006C40B4 /* Headers */ = {
484 | isa = PBXHeadersBuildPhase;
485 | buildActionMask = 2147483647;
486 | files = (
487 | ECE7B7B1203889DD006C40B4 /* API.h in Headers */,
488 | );
489 | runOnlyForDeploymentPostprocessing = 0;
490 | };
491 | ECE7B7CF20389C2C006C40B4 /* Headers */ = {
492 | isa = PBXHeadersBuildPhase;
493 | buildActionMask = 2147483647;
494 | files = (
495 | ECE7B7D620389C2C006C40B4 /* Services.h in Headers */,
496 | );
497 | runOnlyForDeploymentPostprocessing = 0;
498 | };
499 | ECE7B7EF2039A76B006C40B4 /* Headers */ = {
500 | isa = PBXHeadersBuildPhase;
501 | buildActionMask = 2147483647;
502 | files = (
503 | ECE7B7F62039A76B006C40B4 /* Views.h in Headers */,
504 | );
505 | runOnlyForDeploymentPostprocessing = 0;
506 | };
507 | ECE7B8052039AA14006C40B4 /* Headers */ = {
508 | isa = PBXHeadersBuildPhase;
509 | buildActionMask = 2147483647;
510 | files = (
511 | ECE7B80C2039AA14006C40B4 /* Binders.h in Headers */,
512 | );
513 | runOnlyForDeploymentPostprocessing = 0;
514 | };
515 | /* End PBXHeadersBuildPhase section */
516 |
517 | /* Begin PBXNativeTarget section */
518 | ECE7B747203867B0006C40B4 /* AbsurdGitter */ = {
519 | isa = PBXNativeTarget;
520 | buildConfigurationList = ECE7B75A203867B0006C40B4 /* Build configuration list for PBXNativeTarget "AbsurdGitter" */;
521 | buildPhases = (
522 | ECE7B744203867B0006C40B4 /* Sources */,
523 | ECE7B745203867B0006C40B4 /* Frameworks */,
524 | ECE7B746203867B0006C40B4 /* Resources */,
525 | ECE7B75D203868AF006C40B4 /* Carthage */,
526 | ECE7B76B20386931006C40B4 /* Embed Frameworks */,
527 | );
528 | buildRules = (
529 | );
530 | dependencies = (
531 | ECE7B76920386931006C40B4 /* PBXTargetDependency */,
532 | ECE7B79820386EA7006C40B4 /* PBXTargetDependency */,
533 | ECE7B7B3203889DD006C40B4 /* PBXTargetDependency */,
534 | ECE7B7D820389C2C006C40B4 /* PBXTargetDependency */,
535 | ECE7B7F82039A76B006C40B4 /* PBXTargetDependency */,
536 | ECE7B80E2039AA14006C40B4 /* PBXTargetDependency */,
537 | );
538 | name = AbsurdGitter;
539 | productName = AbsurdGitter;
540 | productReference = ECE7B748203867B0006C40B4 /* AbsurdGitter.app */;
541 | productType = "com.apple.product-type.application";
542 | };
543 | ECE7B76220386931006C40B4 /* Client */ = {
544 | isa = PBXNativeTarget;
545 | buildConfigurationList = ECE7B76D20386932006C40B4 /* Build configuration list for PBXNativeTarget "Client" */;
546 | buildPhases = (
547 | ECE7B75E20386931006C40B4 /* Sources */,
548 | ECE7B75F20386931006C40B4 /* Frameworks */,
549 | ECE7B76020386931006C40B4 /* Headers */,
550 | ECE7B76120386931006C40B4 /* Resources */,
551 | );
552 | buildRules = (
553 | );
554 | dependencies = (
555 | );
556 | name = Client;
557 | productName = Client;
558 | productReference = ECE7B76320386931006C40B4 /* Client.framework */;
559 | productType = "com.apple.product-type.framework";
560 | };
561 | ECE7B79120386EA7006C40B4 /* Entities */ = {
562 | isa = PBXNativeTarget;
563 | buildConfigurationList = ECE7B79B20386EA7006C40B4 /* Build configuration list for PBXNativeTarget "Entities" */;
564 | buildPhases = (
565 | ECE7B78D20386EA7006C40B4 /* Sources */,
566 | ECE7B78E20386EA7006C40B4 /* Frameworks */,
567 | ECE7B78F20386EA7006C40B4 /* Headers */,
568 | ECE7B79020386EA7006C40B4 /* Resources */,
569 | );
570 | buildRules = (
571 | );
572 | dependencies = (
573 | );
574 | name = Entities;
575 | productName = Entities;
576 | productReference = ECE7B79220386EA7006C40B4 /* Entities.framework */;
577 | productType = "com.apple.product-type.framework";
578 | };
579 | ECE7B7AC203889DD006C40B4 /* API */ = {
580 | isa = PBXNativeTarget;
581 | buildConfigurationList = ECE7B7B6203889DD006C40B4 /* Build configuration list for PBXNativeTarget "API" */;
582 | buildPhases = (
583 | ECE7B7A8203889DD006C40B4 /* Sources */,
584 | ECE7B7A9203889DD006C40B4 /* Frameworks */,
585 | ECE7B7AA203889DD006C40B4 /* Headers */,
586 | ECE7B7AB203889DD006C40B4 /* Resources */,
587 | );
588 | buildRules = (
589 | );
590 | dependencies = (
591 | ECE7B7CA2038928A006C40B4 /* PBXTargetDependency */,
592 | ECE7B7CC2038928A006C40B4 /* PBXTargetDependency */,
593 | );
594 | name = API;
595 | productName = API;
596 | productReference = ECE7B7AD203889DD006C40B4 /* API.framework */;
597 | productType = "com.apple.product-type.framework";
598 | };
599 | ECE7B7D120389C2C006C40B4 /* Services */ = {
600 | isa = PBXNativeTarget;
601 | buildConfigurationList = ECE7B7DB20389C2C006C40B4 /* Build configuration list for PBXNativeTarget "Services" */;
602 | buildPhases = (
603 | ECE7B7CD20389C2C006C40B4 /* Sources */,
604 | ECE7B7CE20389C2C006C40B4 /* Frameworks */,
605 | ECE7B7CF20389C2C006C40B4 /* Headers */,
606 | ECE7B7D020389C2C006C40B4 /* Resources */,
607 | );
608 | buildRules = (
609 | );
610 | dependencies = (
611 | ECE7B7E620389DD4006C40B4 /* PBXTargetDependency */,
612 | );
613 | name = Services;
614 | productName = Services;
615 | productReference = ECE7B7D220389C2C006C40B4 /* Services.framework */;
616 | productType = "com.apple.product-type.framework";
617 | };
618 | ECE7B7F12039A76B006C40B4 /* Views */ = {
619 | isa = PBXNativeTarget;
620 | buildConfigurationList = ECE7B7FB2039A76B006C40B4 /* Build configuration list for PBXNativeTarget "Views" */;
621 | buildPhases = (
622 | ECE7B7ED2039A76B006C40B4 /* Sources */,
623 | ECE7B7EE2039A76B006C40B4 /* Frameworks */,
624 | ECE7B7EF2039A76B006C40B4 /* Headers */,
625 | ECE7B7F02039A76B006C40B4 /* Resources */,
626 | );
627 | buildRules = (
628 | );
629 | dependencies = (
630 | );
631 | name = Views;
632 | productName = Views;
633 | productReference = ECE7B7F22039A76B006C40B4 /* Views.framework */;
634 | productType = "com.apple.product-type.framework";
635 | };
636 | ECE7B8072039AA14006C40B4 /* Binders */ = {
637 | isa = PBXNativeTarget;
638 | buildConfigurationList = ECE7B8112039AA14006C40B4 /* Build configuration list for PBXNativeTarget "Binders" */;
639 | buildPhases = (
640 | ECE7B8032039AA14006C40B4 /* Sources */,
641 | ECE7B8042039AA14006C40B4 /* Frameworks */,
642 | ECE7B8052039AA14006C40B4 /* Headers */,
643 | ECE7B8062039AA14006C40B4 /* Resources */,
644 | );
645 | buildRules = (
646 | );
647 | dependencies = (
648 | ECE7B8192039AACF006C40B4 /* PBXTargetDependency */,
649 | ECE7B81B2039AACF006C40B4 /* PBXTargetDependency */,
650 | );
651 | name = Binders;
652 | productName = Binders;
653 | productReference = ECE7B8082039AA14006C40B4 /* Binders.framework */;
654 | productType = "com.apple.product-type.framework";
655 | };
656 | /* End PBXNativeTarget section */
657 |
658 | /* Begin PBXProject section */
659 | ECE7B740203867AF006C40B4 /* Project object */ = {
660 | isa = PBXProject;
661 | attributes = {
662 | LastSwiftUpdateCheck = 0920;
663 | LastUpgradeCheck = 0930;
664 | ORGANIZATIONNAME = AbsurdAbstractions;
665 | TargetAttributes = {
666 | ECE7B747203867B0006C40B4 = {
667 | CreatedOnToolsVersion = 9.2;
668 | LastSwiftMigration = 1020;
669 | ProvisioningStyle = Automatic;
670 | };
671 | ECE7B76220386931006C40B4 = {
672 | CreatedOnToolsVersion = 9.2;
673 | LastSwiftMigration = 1020;
674 | ProvisioningStyle = Automatic;
675 | };
676 | ECE7B79120386EA7006C40B4 = {
677 | CreatedOnToolsVersion = 9.2;
678 | LastSwiftMigration = 1020;
679 | ProvisioningStyle = Automatic;
680 | };
681 | ECE7B7AC203889DD006C40B4 = {
682 | CreatedOnToolsVersion = 9.2;
683 | LastSwiftMigration = 1020;
684 | ProvisioningStyle = Automatic;
685 | };
686 | ECE7B7D120389C2C006C40B4 = {
687 | CreatedOnToolsVersion = 9.2;
688 | LastSwiftMigration = 1020;
689 | ProvisioningStyle = Automatic;
690 | };
691 | ECE7B7F12039A76B006C40B4 = {
692 | CreatedOnToolsVersion = 9.2;
693 | LastSwiftMigration = 1020;
694 | ProvisioningStyle = Automatic;
695 | };
696 | ECE7B8072039AA14006C40B4 = {
697 | CreatedOnToolsVersion = 9.2;
698 | LastSwiftMigration = 1020;
699 | ProvisioningStyle = Automatic;
700 | };
701 | };
702 | };
703 | buildConfigurationList = ECE7B743203867AF006C40B4 /* Build configuration list for PBXProject "AbsurdGitter" */;
704 | compatibilityVersion = "Xcode 8.0";
705 | developmentRegion = en;
706 | hasScannedForEncodings = 0;
707 | knownRegions = (
708 | en,
709 | Base,
710 | );
711 | mainGroup = ECE7B73F203867AF006C40B4;
712 | productRefGroup = ECE7B749203867B0006C40B4 /* Products */;
713 | projectDirPath = "";
714 | projectRoot = "";
715 | targets = (
716 | ECE7B747203867B0006C40B4 /* AbsurdGitter */,
717 | ECE7B76220386931006C40B4 /* Client */,
718 | ECE7B79120386EA7006C40B4 /* Entities */,
719 | ECE7B7AC203889DD006C40B4 /* API */,
720 | ECE7B7D120389C2C006C40B4 /* Services */,
721 | ECE7B7F12039A76B006C40B4 /* Views */,
722 | ECE7B8072039AA14006C40B4 /* Binders */,
723 | );
724 | };
725 | /* End PBXProject section */
726 |
727 | /* Begin PBXResourcesBuildPhase section */
728 | ECE7B746203867B0006C40B4 /* Resources */ = {
729 | isa = PBXResourcesBuildPhase;
730 | buildActionMask = 2147483647;
731 | files = (
732 | ECE7B756203867B0006C40B4 /* LaunchScreen.storyboard in Resources */,
733 | ECE7B753203867B0006C40B4 /* Assets.xcassets in Resources */,
734 | ECE7B78A203869D7006C40B4 /* FrameworkInfo.plist in Resources */,
735 | );
736 | runOnlyForDeploymentPostprocessing = 0;
737 | };
738 | ECE7B76120386931006C40B4 /* Resources */ = {
739 | isa = PBXResourcesBuildPhase;
740 | buildActionMask = 2147483647;
741 | files = (
742 | );
743 | runOnlyForDeploymentPostprocessing = 0;
744 | };
745 | ECE7B79020386EA7006C40B4 /* Resources */ = {
746 | isa = PBXResourcesBuildPhase;
747 | buildActionMask = 2147483647;
748 | files = (
749 | );
750 | runOnlyForDeploymentPostprocessing = 0;
751 | };
752 | ECE7B7AB203889DD006C40B4 /* Resources */ = {
753 | isa = PBXResourcesBuildPhase;
754 | buildActionMask = 2147483647;
755 | files = (
756 | );
757 | runOnlyForDeploymentPostprocessing = 0;
758 | };
759 | ECE7B7D020389C2C006C40B4 /* Resources */ = {
760 | isa = PBXResourcesBuildPhase;
761 | buildActionMask = 2147483647;
762 | files = (
763 | );
764 | runOnlyForDeploymentPostprocessing = 0;
765 | };
766 | ECE7B7F02039A76B006C40B4 /* Resources */ = {
767 | isa = PBXResourcesBuildPhase;
768 | buildActionMask = 2147483647;
769 | files = (
770 | );
771 | runOnlyForDeploymentPostprocessing = 0;
772 | };
773 | ECE7B8062039AA14006C40B4 /* Resources */ = {
774 | isa = PBXResourcesBuildPhase;
775 | buildActionMask = 2147483647;
776 | files = (
777 | );
778 | runOnlyForDeploymentPostprocessing = 0;
779 | };
780 | /* End PBXResourcesBuildPhase section */
781 |
782 | /* Begin PBXShellScriptBuildPhase section */
783 | ECE7B75D203868AF006C40B4 /* Carthage */ = {
784 | isa = PBXShellScriptBuildPhase;
785 | buildActionMask = 2147483647;
786 | files = (
787 | );
788 | inputPaths = (
789 | "$(SRCROOT)/Carthage/Build/iOS/Differ.framework",
790 | "$(SRCROOT)/Carthage/Build/iOS/ReactiveKit.framework",
791 | "$(SRCROOT)/Carthage/Build/iOS/Bond.framework",
792 | "$(SRCROOT)/Carthage/Build/iOS/Layoutless.framework",
793 | );
794 | name = Carthage;
795 | outputPaths = (
796 | "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Differ.framework",
797 | "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/ReactiveKit.framework",
798 | "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Bond.framework",
799 | "$(BUILT_PRODUCTS_DIR)/$(FRAMEWORKS_FOLDER_PATH)/Layoutless.framework",
800 | );
801 | runOnlyForDeploymentPostprocessing = 0;
802 | shellPath = /bin/sh;
803 | shellScript = "/usr/local/bin/carthage copy-frameworks";
804 | };
805 | /* End PBXShellScriptBuildPhase section */
806 |
807 | /* Begin PBXSourcesBuildPhase section */
808 | ECE7B744203867B0006C40B4 /* Sources */ = {
809 | isa = PBXSourcesBuildPhase;
810 | buildActionMask = 2147483647;
811 | files = (
812 | ECE7B74C203867B0006C40B4 /* AppDelegate.swift in Sources */,
813 | );
814 | runOnlyForDeploymentPostprocessing = 0;
815 | };
816 | ECE7B75E20386931006C40B4 /* Sources */ = {
817 | isa = PBXSourcesBuildPhase;
818 | buildActionMask = 2147483647;
819 | files = (
820 | ECE7B78220386973006C40B4 /* Result.swift in Sources */,
821 | ECE7B78120386973006C40B4 /* Utilities.swift in Sources */,
822 | ECE7B78620386973006C40B4 /* LoggingClient.swift in Sources */,
823 | ECE7B78720386973006C40B4 /* RequestParameters.swift in Sources */,
824 | ECE7B78520386973006C40B4 /* Request.swift in Sources */,
825 | ECE7B78420386973006C40B4 /* Client.swift in Sources */,
826 | ECE7B78320386973006C40B4 /* HTTPMethod.swift in Sources */,
827 | ECE7B78C20386AAB006C40B4 /* Logger.swift in Sources */,
828 | );
829 | runOnlyForDeploymentPostprocessing = 0;
830 | };
831 | ECE7B78D20386EA7006C40B4 /* Sources */ = {
832 | isa = PBXSourcesBuildPhase;
833 | buildActionMask = 2147483647;
834 | files = (
835 | ECE7B7A320386FB8006C40B4 /* User.swift in Sources */,
836 | ECE7B7A120386F2F006C40B4 /* Message.swift in Sources */,
837 | ECE7B7A520386FE7006C40B4 /* Room.swift in Sources */,
838 | ECE7B7A72038703C006C40B4 /* AccessToken.swift in Sources */,
839 | ECA30070206D1C3100F6EA37 /* ApplicationError.swift in Sources */,
840 | );
841 | runOnlyForDeploymentPostprocessing = 0;
842 | };
843 | ECE7B7A8203889DD006C40B4 /* Sources */ = {
844 | isa = PBXSourcesBuildPhase;
845 | buildActionMask = 2147483647;
846 | files = (
847 | ECE7B7BA20388A1B006C40B4 /* Message.swift in Sources */,
848 | ECE7B7C220388B74006C40B4 /* GitterError.swift in Sources */,
849 | ECE7B7C620389175006C40B4 /* User.swift in Sources */,
850 | ECE7B7C8203891E5006C40B4 /* OAuth.swift in Sources */,
851 | ECE7B7BD20388A82006C40B4 /* GitterRequest.swift in Sources */,
852 | ECE7B7C020388B60006C40B4 /* GitterClient.swift in Sources */,
853 | ECE7B7C420388F7B006C40B4 /* Room.swift in Sources */,
854 | );
855 | runOnlyForDeploymentPostprocessing = 0;
856 | };
857 | ECE7B7CD20389C2C006C40B4 /* Sources */ = {
858 | isa = PBXSourcesBuildPhase;
859 | buildActionMask = 2147483647;
860 | files = (
861 | EC966F0E204B147C0016B4FC /* MessageService.swift in Sources */,
862 | EC966F0D204B147C0016B4FC /* RoomService.swift in Sources */,
863 | EC966F0A204B14720016B4FC /* UnauthenticatedSession.swift in Sources */,
864 | EC966F0C204B147C0016B4FC /* UserService.swift in Sources */,
865 | EC966F09204B146C0016B4FC /* AuthenticatedSession.swift in Sources */,
866 | EC966F08204B14630016B4FC /* SessionManager.swift in Sources */,
867 | EC966F0B204B147C0016B4FC /* LoginService.swift in Sources */,
868 | );
869 | runOnlyForDeploymentPostprocessing = 0;
870 | };
871 | ECE7B7ED2039A76B006C40B4 /* Sources */ = {
872 | isa = PBXSourcesBuildPhase;
873 | buildActionMask = 2147483647;
874 | files = (
875 | ECE7B82F2039D791006C40B4 /* RoomViewController.swift in Sources */,
876 | ECE7B7FF2039A79B006C40B4 /* LoginViewController.swift in Sources */,
877 | ECA3006D206D142E00F6EA37 /* ProfileViewController.swift in Sources */,
878 | ECE7B8022039A840006C40B4 /* RoomListViewController.swift in Sources */,
879 | );
880 | runOnlyForDeploymentPostprocessing = 0;
881 | };
882 | ECE7B8032039AA14006C40B4 /* Sources */ = {
883 | isa = PBXSourcesBuildPhase;
884 | buildActionMask = 2147483647;
885 | files = (
886 | ECE7B8272039B62B006C40B4 /* RoomListViewController.swift in Sources */,
887 | ECE7B8172039AA55006C40B4 /* MainWindow.swift in Sources */,
888 | ECA30072206D40AD00F6EA37 /* RoomNavigationController.swift in Sources */,
889 | ECA3006B206D0F7300F6EA37 /* MainTabBarController.swift in Sources */,
890 | ECE7B8152039AA34006C40B4 /* LoginViewController.swift in Sources */,
891 | ECE7B8312039D819006C40B4 /* RoomViewController.swift in Sources */,
892 | ECA3006F206D14FE00F6EA37 /* ProfileViewController.swift in Sources */,
893 | ECE7B82B2039C2CE006C40B4 /* Extensions.swift in Sources */,
894 | );
895 | runOnlyForDeploymentPostprocessing = 0;
896 | };
897 | /* End PBXSourcesBuildPhase section */
898 |
899 | /* Begin PBXTargetDependency section */
900 | ECE7B76920386931006C40B4 /* PBXTargetDependency */ = {
901 | isa = PBXTargetDependency;
902 | target = ECE7B76220386931006C40B4 /* Client */;
903 | targetProxy = ECE7B76820386931006C40B4 /* PBXContainerItemProxy */;
904 | };
905 | ECE7B79820386EA7006C40B4 /* PBXTargetDependency */ = {
906 | isa = PBXTargetDependency;
907 | target = ECE7B79120386EA7006C40B4 /* Entities */;
908 | targetProxy = ECE7B79720386EA7006C40B4 /* PBXContainerItemProxy */;
909 | };
910 | ECE7B7B3203889DD006C40B4 /* PBXTargetDependency */ = {
911 | isa = PBXTargetDependency;
912 | target = ECE7B7AC203889DD006C40B4 /* API */;
913 | targetProxy = ECE7B7B2203889DD006C40B4 /* PBXContainerItemProxy */;
914 | };
915 | ECE7B7CA2038928A006C40B4 /* PBXTargetDependency */ = {
916 | isa = PBXTargetDependency;
917 | target = ECE7B76220386931006C40B4 /* Client */;
918 | targetProxy = ECE7B7C92038928A006C40B4 /* PBXContainerItemProxy */;
919 | };
920 | ECE7B7CC2038928A006C40B4 /* PBXTargetDependency */ = {
921 | isa = PBXTargetDependency;
922 | target = ECE7B79120386EA7006C40B4 /* Entities */;
923 | targetProxy = ECE7B7CB2038928A006C40B4 /* PBXContainerItemProxy */;
924 | };
925 | ECE7B7D820389C2C006C40B4 /* PBXTargetDependency */ = {
926 | isa = PBXTargetDependency;
927 | target = ECE7B7D120389C2C006C40B4 /* Services */;
928 | targetProxy = ECE7B7D720389C2C006C40B4 /* PBXContainerItemProxy */;
929 | };
930 | ECE7B7E620389DD4006C40B4 /* PBXTargetDependency */ = {
931 | isa = PBXTargetDependency;
932 | target = ECE7B7AC203889DD006C40B4 /* API */;
933 | targetProxy = ECE7B7E520389DD4006C40B4 /* PBXContainerItemProxy */;
934 | };
935 | ECE7B7F82039A76B006C40B4 /* PBXTargetDependency */ = {
936 | isa = PBXTargetDependency;
937 | target = ECE7B7F12039A76B006C40B4 /* Views */;
938 | targetProxy = ECE7B7F72039A76B006C40B4 /* PBXContainerItemProxy */;
939 | };
940 | ECE7B80E2039AA14006C40B4 /* PBXTargetDependency */ = {
941 | isa = PBXTargetDependency;
942 | target = ECE7B8072039AA14006C40B4 /* Binders */;
943 | targetProxy = ECE7B80D2039AA14006C40B4 /* PBXContainerItemProxy */;
944 | };
945 | ECE7B8192039AACF006C40B4 /* PBXTargetDependency */ = {
946 | isa = PBXTargetDependency;
947 | target = ECE7B7D120389C2C006C40B4 /* Services */;
948 | targetProxy = ECE7B8182039AACF006C40B4 /* PBXContainerItemProxy */;
949 | };
950 | ECE7B81B2039AACF006C40B4 /* PBXTargetDependency */ = {
951 | isa = PBXTargetDependency;
952 | target = ECE7B7F12039A76B006C40B4 /* Views */;
953 | targetProxy = ECE7B81A2039AACF006C40B4 /* PBXContainerItemProxy */;
954 | };
955 | /* End PBXTargetDependency section */
956 |
957 | /* Begin PBXVariantGroup section */
958 | ECE7B754203867B0006C40B4 /* LaunchScreen.storyboard */ = {
959 | isa = PBXVariantGroup;
960 | children = (
961 | ECE7B755203867B0006C40B4 /* Base */,
962 | );
963 | name = LaunchScreen.storyboard;
964 | sourceTree = "";
965 | };
966 | /* End PBXVariantGroup section */
967 |
968 | /* Begin XCBuildConfiguration section */
969 | ECE7B758203867B0006C40B4 /* Debug */ = {
970 | isa = XCBuildConfiguration;
971 | buildSettings = {
972 | ALWAYS_SEARCH_USER_PATHS = NO;
973 | CLANG_ANALYZER_NONNULL = YES;
974 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
975 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
976 | CLANG_CXX_LIBRARY = "libc++";
977 | CLANG_ENABLE_MODULES = YES;
978 | CLANG_ENABLE_OBJC_ARC = YES;
979 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
980 | CLANG_WARN_BOOL_CONVERSION = YES;
981 | CLANG_WARN_COMMA = YES;
982 | CLANG_WARN_CONSTANT_CONVERSION = YES;
983 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
984 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
985 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
986 | CLANG_WARN_EMPTY_BODY = YES;
987 | CLANG_WARN_ENUM_CONVERSION = YES;
988 | CLANG_WARN_INFINITE_RECURSION = YES;
989 | CLANG_WARN_INT_CONVERSION = YES;
990 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
991 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
992 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
993 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
994 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
995 | CLANG_WARN_STRICT_PROTOTYPES = YES;
996 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
997 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
998 | CLANG_WARN_UNREACHABLE_CODE = YES;
999 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
1000 | CODE_SIGN_IDENTITY = "iPhone Developer";
1001 | COPY_PHASE_STRIP = NO;
1002 | DEBUG_INFORMATION_FORMAT = dwarf;
1003 | ENABLE_STRICT_OBJC_MSGSEND = YES;
1004 | ENABLE_TESTABILITY = YES;
1005 | GCC_C_LANGUAGE_STANDARD = gnu11;
1006 | GCC_DYNAMIC_NO_PIC = NO;
1007 | GCC_NO_COMMON_BLOCKS = YES;
1008 | GCC_OPTIMIZATION_LEVEL = 0;
1009 | GCC_PREPROCESSOR_DEFINITIONS = (
1010 | "DEBUG=1",
1011 | "$(inherited)",
1012 | );
1013 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
1014 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
1015 | GCC_WARN_UNDECLARED_SELECTOR = YES;
1016 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
1017 | GCC_WARN_UNUSED_FUNCTION = YES;
1018 | GCC_WARN_UNUSED_VARIABLE = YES;
1019 | IPHONEOS_DEPLOYMENT_TARGET = 11.2;
1020 | MTL_ENABLE_DEBUG_INFO = YES;
1021 | ONLY_ACTIVE_ARCH = YES;
1022 | SDKROOT = iphoneos;
1023 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
1024 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
1025 | };
1026 | name = Debug;
1027 | };
1028 | ECE7B759203867B0006C40B4 /* Release */ = {
1029 | isa = XCBuildConfiguration;
1030 | buildSettings = {
1031 | ALWAYS_SEARCH_USER_PATHS = NO;
1032 | CLANG_ANALYZER_NONNULL = YES;
1033 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
1034 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
1035 | CLANG_CXX_LIBRARY = "libc++";
1036 | CLANG_ENABLE_MODULES = YES;
1037 | CLANG_ENABLE_OBJC_ARC = YES;
1038 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
1039 | CLANG_WARN_BOOL_CONVERSION = YES;
1040 | CLANG_WARN_COMMA = YES;
1041 | CLANG_WARN_CONSTANT_CONVERSION = YES;
1042 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
1043 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
1044 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
1045 | CLANG_WARN_EMPTY_BODY = YES;
1046 | CLANG_WARN_ENUM_CONVERSION = YES;
1047 | CLANG_WARN_INFINITE_RECURSION = YES;
1048 | CLANG_WARN_INT_CONVERSION = YES;
1049 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
1050 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
1051 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
1052 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
1053 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
1054 | CLANG_WARN_STRICT_PROTOTYPES = YES;
1055 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
1056 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
1057 | CLANG_WARN_UNREACHABLE_CODE = YES;
1058 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
1059 | CODE_SIGN_IDENTITY = "iPhone Developer";
1060 | COPY_PHASE_STRIP = NO;
1061 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
1062 | ENABLE_NS_ASSERTIONS = NO;
1063 | ENABLE_STRICT_OBJC_MSGSEND = YES;
1064 | GCC_C_LANGUAGE_STANDARD = gnu11;
1065 | GCC_NO_COMMON_BLOCKS = YES;
1066 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
1067 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
1068 | GCC_WARN_UNDECLARED_SELECTOR = YES;
1069 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
1070 | GCC_WARN_UNUSED_FUNCTION = YES;
1071 | GCC_WARN_UNUSED_VARIABLE = YES;
1072 | IPHONEOS_DEPLOYMENT_TARGET = 11.2;
1073 | MTL_ENABLE_DEBUG_INFO = NO;
1074 | SDKROOT = iphoneos;
1075 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
1076 | VALIDATE_PRODUCT = YES;
1077 | };
1078 | name = Release;
1079 | };
1080 | ECE7B75B203867B0006C40B4 /* Debug */ = {
1081 | isa = XCBuildConfiguration;
1082 | buildSettings = {
1083 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
1084 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
1085 | CODE_SIGN_STYLE = Automatic;
1086 | FRAMEWORK_SEARCH_PATHS = (
1087 | "$(inherited)",
1088 | "$(PROJECT_DIR)/Carthage/Build/iOS",
1089 | );
1090 | INFOPLIST_FILE = AbsurdGitter/Info.plist;
1091 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
1092 | PRODUCT_BUNDLE_IDENTIFIER = AbsurdAbstractions.AbsurdGitter;
1093 | PRODUCT_NAME = "$(TARGET_NAME)";
1094 | SWIFT_VERSION = 5.0;
1095 | TARGETED_DEVICE_FAMILY = "1,2";
1096 | };
1097 | name = Debug;
1098 | };
1099 | ECE7B75C203867B0006C40B4 /* Release */ = {
1100 | isa = XCBuildConfiguration;
1101 | buildSettings = {
1102 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
1103 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
1104 | CODE_SIGN_STYLE = Automatic;
1105 | FRAMEWORK_SEARCH_PATHS = (
1106 | "$(inherited)",
1107 | "$(PROJECT_DIR)/Carthage/Build/iOS",
1108 | );
1109 | INFOPLIST_FILE = AbsurdGitter/Info.plist;
1110 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
1111 | PRODUCT_BUNDLE_IDENTIFIER = AbsurdAbstractions.AbsurdGitter;
1112 | PRODUCT_NAME = "$(TARGET_NAME)";
1113 | SWIFT_VERSION = 5.0;
1114 | TARGETED_DEVICE_FAMILY = "1,2";
1115 | };
1116 | name = Release;
1117 | };
1118 | ECE7B76E20386932006C40B4 /* Debug */ = {
1119 | isa = XCBuildConfiguration;
1120 | buildSettings = {
1121 | CODE_SIGN_IDENTITY = "";
1122 | CODE_SIGN_STYLE = Automatic;
1123 | CURRENT_PROJECT_VERSION = 1;
1124 | DEFINES_MODULE = YES;
1125 | DYLIB_COMPATIBILITY_VERSION = 1;
1126 | DYLIB_CURRENT_VERSION = 1;
1127 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1128 | INFOPLIST_FILE = AbsurdGitter/FrameworkInfo.plist;
1129 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1130 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1131 | PRODUCT_BUNDLE_IDENTIFIER = AbsurdAbstractions.Client;
1132 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
1133 | SKIP_INSTALL = YES;
1134 | SWIFT_VERSION = 5.0;
1135 | TARGETED_DEVICE_FAMILY = "1,2";
1136 | VERSIONING_SYSTEM = "apple-generic";
1137 | VERSION_INFO_PREFIX = "";
1138 | };
1139 | name = Debug;
1140 | };
1141 | ECE7B76F20386932006C40B4 /* Release */ = {
1142 | isa = XCBuildConfiguration;
1143 | buildSettings = {
1144 | CODE_SIGN_IDENTITY = "";
1145 | CODE_SIGN_STYLE = Automatic;
1146 | CURRENT_PROJECT_VERSION = 1;
1147 | DEFINES_MODULE = YES;
1148 | DYLIB_COMPATIBILITY_VERSION = 1;
1149 | DYLIB_CURRENT_VERSION = 1;
1150 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1151 | INFOPLIST_FILE = AbsurdGitter/FrameworkInfo.plist;
1152 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1153 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1154 | PRODUCT_BUNDLE_IDENTIFIER = AbsurdAbstractions.Client;
1155 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
1156 | SKIP_INSTALL = YES;
1157 | SWIFT_VERSION = 5.0;
1158 | TARGETED_DEVICE_FAMILY = "1,2";
1159 | VERSIONING_SYSTEM = "apple-generic";
1160 | VERSION_INFO_PREFIX = "";
1161 | };
1162 | name = Release;
1163 | };
1164 | ECE7B79C20386EA7006C40B4 /* Debug */ = {
1165 | isa = XCBuildConfiguration;
1166 | buildSettings = {
1167 | CLANG_ENABLE_MODULES = YES;
1168 | CODE_SIGN_IDENTITY = "";
1169 | CODE_SIGN_STYLE = Automatic;
1170 | CURRENT_PROJECT_VERSION = 1;
1171 | DEFINES_MODULE = YES;
1172 | DYLIB_COMPATIBILITY_VERSION = 1;
1173 | DYLIB_CURRENT_VERSION = 1;
1174 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1175 | INFOPLIST_FILE = AbsurdGitter/FrameworkInfo.plist;
1176 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1177 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1178 | PRODUCT_BUNDLE_IDENTIFIER = AbsurdAbstractions.Entities;
1179 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
1180 | SKIP_INSTALL = YES;
1181 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
1182 | SWIFT_VERSION = 5.0;
1183 | TARGETED_DEVICE_FAMILY = "1,2";
1184 | VERSIONING_SYSTEM = "apple-generic";
1185 | VERSION_INFO_PREFIX = "";
1186 | };
1187 | name = Debug;
1188 | };
1189 | ECE7B79D20386EA7006C40B4 /* Release */ = {
1190 | isa = XCBuildConfiguration;
1191 | buildSettings = {
1192 | CLANG_ENABLE_MODULES = YES;
1193 | CODE_SIGN_IDENTITY = "";
1194 | CODE_SIGN_STYLE = Automatic;
1195 | CURRENT_PROJECT_VERSION = 1;
1196 | DEFINES_MODULE = YES;
1197 | DYLIB_COMPATIBILITY_VERSION = 1;
1198 | DYLIB_CURRENT_VERSION = 1;
1199 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1200 | INFOPLIST_FILE = AbsurdGitter/FrameworkInfo.plist;
1201 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1202 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1203 | PRODUCT_BUNDLE_IDENTIFIER = AbsurdAbstractions.Entities;
1204 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
1205 | SKIP_INSTALL = YES;
1206 | SWIFT_VERSION = 5.0;
1207 | TARGETED_DEVICE_FAMILY = "1,2";
1208 | VERSIONING_SYSTEM = "apple-generic";
1209 | VERSION_INFO_PREFIX = "";
1210 | };
1211 | name = Release;
1212 | };
1213 | ECE7B7B7203889DD006C40B4 /* Debug */ = {
1214 | isa = XCBuildConfiguration;
1215 | buildSettings = {
1216 | CLANG_ENABLE_MODULES = YES;
1217 | CODE_SIGN_IDENTITY = "";
1218 | CODE_SIGN_STYLE = Automatic;
1219 | CURRENT_PROJECT_VERSION = 1;
1220 | DEFINES_MODULE = YES;
1221 | DYLIB_COMPATIBILITY_VERSION = 1;
1222 | DYLIB_CURRENT_VERSION = 1;
1223 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1224 | FRAMEWORK_SEARCH_PATHS = (
1225 | "$(inherited)",
1226 | "$(PROJECT_DIR)/Carthage/Build/iOS",
1227 | );
1228 | INFOPLIST_FILE = AbsurdGitter/FrameworkInfo.plist;
1229 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1230 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1231 | PRODUCT_BUNDLE_IDENTIFIER = AbsurdAbstractions.API;
1232 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
1233 | SKIP_INSTALL = YES;
1234 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
1235 | SWIFT_VERSION = 5.0;
1236 | TARGETED_DEVICE_FAMILY = "1,2";
1237 | VERSIONING_SYSTEM = "apple-generic";
1238 | VERSION_INFO_PREFIX = "";
1239 | };
1240 | name = Debug;
1241 | };
1242 | ECE7B7B8203889DD006C40B4 /* Release */ = {
1243 | isa = XCBuildConfiguration;
1244 | buildSettings = {
1245 | CLANG_ENABLE_MODULES = YES;
1246 | CODE_SIGN_IDENTITY = "";
1247 | CODE_SIGN_STYLE = Automatic;
1248 | CURRENT_PROJECT_VERSION = 1;
1249 | DEFINES_MODULE = YES;
1250 | DYLIB_COMPATIBILITY_VERSION = 1;
1251 | DYLIB_CURRENT_VERSION = 1;
1252 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1253 | FRAMEWORK_SEARCH_PATHS = (
1254 | "$(inherited)",
1255 | "$(PROJECT_DIR)/Carthage/Build/iOS",
1256 | );
1257 | INFOPLIST_FILE = AbsurdGitter/FrameworkInfo.plist;
1258 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1259 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1260 | PRODUCT_BUNDLE_IDENTIFIER = AbsurdAbstractions.API;
1261 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
1262 | SKIP_INSTALL = YES;
1263 | SWIFT_VERSION = 5.0;
1264 | TARGETED_DEVICE_FAMILY = "1,2";
1265 | VERSIONING_SYSTEM = "apple-generic";
1266 | VERSION_INFO_PREFIX = "";
1267 | };
1268 | name = Release;
1269 | };
1270 | ECE7B7DC20389C2C006C40B4 /* Debug */ = {
1271 | isa = XCBuildConfiguration;
1272 | buildSettings = {
1273 | CLANG_ENABLE_MODULES = YES;
1274 | CODE_SIGN_IDENTITY = "";
1275 | CODE_SIGN_STYLE = Automatic;
1276 | CURRENT_PROJECT_VERSION = 1;
1277 | DEFINES_MODULE = YES;
1278 | DYLIB_COMPATIBILITY_VERSION = 1;
1279 | DYLIB_CURRENT_VERSION = 1;
1280 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1281 | FRAMEWORK_SEARCH_PATHS = (
1282 | "$(inherited)",
1283 | "$(PROJECT_DIR)/Carthage/Build/iOS",
1284 | );
1285 | INFOPLIST_FILE = AbsurdGitter/FrameworkInfo.plist;
1286 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1287 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1288 | PRODUCT_BUNDLE_IDENTIFIER = AbsurdAbstractions.Services;
1289 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
1290 | SKIP_INSTALL = YES;
1291 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
1292 | SWIFT_VERSION = 5.0;
1293 | TARGETED_DEVICE_FAMILY = "1,2";
1294 | VERSIONING_SYSTEM = "apple-generic";
1295 | VERSION_INFO_PREFIX = "";
1296 | };
1297 | name = Debug;
1298 | };
1299 | ECE7B7DD20389C2C006C40B4 /* Release */ = {
1300 | isa = XCBuildConfiguration;
1301 | buildSettings = {
1302 | CLANG_ENABLE_MODULES = YES;
1303 | CODE_SIGN_IDENTITY = "";
1304 | CODE_SIGN_STYLE = Automatic;
1305 | CURRENT_PROJECT_VERSION = 1;
1306 | DEFINES_MODULE = YES;
1307 | DYLIB_COMPATIBILITY_VERSION = 1;
1308 | DYLIB_CURRENT_VERSION = 1;
1309 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1310 | FRAMEWORK_SEARCH_PATHS = (
1311 | "$(inherited)",
1312 | "$(PROJECT_DIR)/Carthage/Build/iOS",
1313 | );
1314 | INFOPLIST_FILE = AbsurdGitter/FrameworkInfo.plist;
1315 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1316 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1317 | PRODUCT_BUNDLE_IDENTIFIER = AbsurdAbstractions.Services;
1318 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
1319 | SKIP_INSTALL = YES;
1320 | SWIFT_VERSION = 5.0;
1321 | TARGETED_DEVICE_FAMILY = "1,2";
1322 | VERSIONING_SYSTEM = "apple-generic";
1323 | VERSION_INFO_PREFIX = "";
1324 | };
1325 | name = Release;
1326 | };
1327 | ECE7B7FC2039A76B006C40B4 /* Debug */ = {
1328 | isa = XCBuildConfiguration;
1329 | buildSettings = {
1330 | CLANG_ENABLE_MODULES = YES;
1331 | CODE_SIGN_IDENTITY = "";
1332 | CODE_SIGN_STYLE = Automatic;
1333 | CURRENT_PROJECT_VERSION = 1;
1334 | DEFINES_MODULE = YES;
1335 | DYLIB_COMPATIBILITY_VERSION = 1;
1336 | DYLIB_CURRENT_VERSION = 1;
1337 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1338 | FRAMEWORK_SEARCH_PATHS = (
1339 | "$(inherited)",
1340 | "$(PROJECT_DIR)/Carthage/Build/iOS",
1341 | );
1342 | INFOPLIST_FILE = AbsurdGitter/FrameworkInfo.plist;
1343 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1344 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1345 | PRODUCT_BUNDLE_IDENTIFIER = AbsurdAbstractions.Views;
1346 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
1347 | SKIP_INSTALL = YES;
1348 | SWIFT_INSTALL_OBJC_HEADER = NO;
1349 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
1350 | SWIFT_VERSION = 5.0;
1351 | TARGETED_DEVICE_FAMILY = "1,2";
1352 | VERSIONING_SYSTEM = "apple-generic";
1353 | VERSION_INFO_PREFIX = "";
1354 | };
1355 | name = Debug;
1356 | };
1357 | ECE7B7FD2039A76B006C40B4 /* Release */ = {
1358 | isa = XCBuildConfiguration;
1359 | buildSettings = {
1360 | CLANG_ENABLE_MODULES = YES;
1361 | CODE_SIGN_IDENTITY = "";
1362 | CODE_SIGN_STYLE = Automatic;
1363 | CURRENT_PROJECT_VERSION = 1;
1364 | DEFINES_MODULE = YES;
1365 | DYLIB_COMPATIBILITY_VERSION = 1;
1366 | DYLIB_CURRENT_VERSION = 1;
1367 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1368 | FRAMEWORK_SEARCH_PATHS = (
1369 | "$(inherited)",
1370 | "$(PROJECT_DIR)/Carthage/Build/iOS",
1371 | );
1372 | INFOPLIST_FILE = AbsurdGitter/FrameworkInfo.plist;
1373 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1374 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1375 | PRODUCT_BUNDLE_IDENTIFIER = AbsurdAbstractions.Views;
1376 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
1377 | SKIP_INSTALL = YES;
1378 | SWIFT_INSTALL_OBJC_HEADER = NO;
1379 | SWIFT_VERSION = 5.0;
1380 | TARGETED_DEVICE_FAMILY = "1,2";
1381 | VERSIONING_SYSTEM = "apple-generic";
1382 | VERSION_INFO_PREFIX = "";
1383 | };
1384 | name = Release;
1385 | };
1386 | ECE7B8122039AA14006C40B4 /* Debug */ = {
1387 | isa = XCBuildConfiguration;
1388 | buildSettings = {
1389 | CLANG_ENABLE_MODULES = YES;
1390 | CODE_SIGN_IDENTITY = "";
1391 | CODE_SIGN_STYLE = Automatic;
1392 | CURRENT_PROJECT_VERSION = 1;
1393 | DEFINES_MODULE = YES;
1394 | DYLIB_COMPATIBILITY_VERSION = 1;
1395 | DYLIB_CURRENT_VERSION = 1;
1396 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1397 | FRAMEWORK_SEARCH_PATHS = (
1398 | "$(inherited)",
1399 | "$(PROJECT_DIR)/Carthage/Build/iOS",
1400 | );
1401 | INFOPLIST_FILE = AbsurdGitter/FrameworkInfo.plist;
1402 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1403 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1404 | PRODUCT_BUNDLE_IDENTIFIER = AbsurdAbstractions.Binders;
1405 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
1406 | SKIP_INSTALL = YES;
1407 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
1408 | SWIFT_VERSION = 5.0;
1409 | TARGETED_DEVICE_FAMILY = "1,2";
1410 | VERSIONING_SYSTEM = "apple-generic";
1411 | VERSION_INFO_PREFIX = "";
1412 | };
1413 | name = Debug;
1414 | };
1415 | ECE7B8132039AA14006C40B4 /* Release */ = {
1416 | isa = XCBuildConfiguration;
1417 | buildSettings = {
1418 | CLANG_ENABLE_MODULES = YES;
1419 | CODE_SIGN_IDENTITY = "";
1420 | CODE_SIGN_STYLE = Automatic;
1421 | CURRENT_PROJECT_VERSION = 1;
1422 | DEFINES_MODULE = YES;
1423 | DYLIB_COMPATIBILITY_VERSION = 1;
1424 | DYLIB_CURRENT_VERSION = 1;
1425 | DYLIB_INSTALL_NAME_BASE = "@rpath";
1426 | FRAMEWORK_SEARCH_PATHS = (
1427 | "$(inherited)",
1428 | "$(PROJECT_DIR)/Carthage/Build/iOS",
1429 | );
1430 | INFOPLIST_FILE = AbsurdGitter/FrameworkInfo.plist;
1431 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
1432 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
1433 | PRODUCT_BUNDLE_IDENTIFIER = AbsurdAbstractions.Binders;
1434 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
1435 | SKIP_INSTALL = YES;
1436 | SWIFT_VERSION = 5.0;
1437 | TARGETED_DEVICE_FAMILY = "1,2";
1438 | VERSIONING_SYSTEM = "apple-generic";
1439 | VERSION_INFO_PREFIX = "";
1440 | };
1441 | name = Release;
1442 | };
1443 | /* End XCBuildConfiguration section */
1444 |
1445 | /* Begin XCConfigurationList section */
1446 | ECE7B743203867AF006C40B4 /* Build configuration list for PBXProject "AbsurdGitter" */ = {
1447 | isa = XCConfigurationList;
1448 | buildConfigurations = (
1449 | ECE7B758203867B0006C40B4 /* Debug */,
1450 | ECE7B759203867B0006C40B4 /* Release */,
1451 | );
1452 | defaultConfigurationIsVisible = 0;
1453 | defaultConfigurationName = Release;
1454 | };
1455 | ECE7B75A203867B0006C40B4 /* Build configuration list for PBXNativeTarget "AbsurdGitter" */ = {
1456 | isa = XCConfigurationList;
1457 | buildConfigurations = (
1458 | ECE7B75B203867B0006C40B4 /* Debug */,
1459 | ECE7B75C203867B0006C40B4 /* Release */,
1460 | );
1461 | defaultConfigurationIsVisible = 0;
1462 | defaultConfigurationName = Release;
1463 | };
1464 | ECE7B76D20386932006C40B4 /* Build configuration list for PBXNativeTarget "Client" */ = {
1465 | isa = XCConfigurationList;
1466 | buildConfigurations = (
1467 | ECE7B76E20386932006C40B4 /* Debug */,
1468 | ECE7B76F20386932006C40B4 /* Release */,
1469 | );
1470 | defaultConfigurationIsVisible = 0;
1471 | defaultConfigurationName = Release;
1472 | };
1473 | ECE7B79B20386EA7006C40B4 /* Build configuration list for PBXNativeTarget "Entities" */ = {
1474 | isa = XCConfigurationList;
1475 | buildConfigurations = (
1476 | ECE7B79C20386EA7006C40B4 /* Debug */,
1477 | ECE7B79D20386EA7006C40B4 /* Release */,
1478 | );
1479 | defaultConfigurationIsVisible = 0;
1480 | defaultConfigurationName = Release;
1481 | };
1482 | ECE7B7B6203889DD006C40B4 /* Build configuration list for PBXNativeTarget "API" */ = {
1483 | isa = XCConfigurationList;
1484 | buildConfigurations = (
1485 | ECE7B7B7203889DD006C40B4 /* Debug */,
1486 | ECE7B7B8203889DD006C40B4 /* Release */,
1487 | );
1488 | defaultConfigurationIsVisible = 0;
1489 | defaultConfigurationName = Release;
1490 | };
1491 | ECE7B7DB20389C2C006C40B4 /* Build configuration list for PBXNativeTarget "Services" */ = {
1492 | isa = XCConfigurationList;
1493 | buildConfigurations = (
1494 | ECE7B7DC20389C2C006C40B4 /* Debug */,
1495 | ECE7B7DD20389C2C006C40B4 /* Release */,
1496 | );
1497 | defaultConfigurationIsVisible = 0;
1498 | defaultConfigurationName = Release;
1499 | };
1500 | ECE7B7FB2039A76B006C40B4 /* Build configuration list for PBXNativeTarget "Views" */ = {
1501 | isa = XCConfigurationList;
1502 | buildConfigurations = (
1503 | ECE7B7FC2039A76B006C40B4 /* Debug */,
1504 | ECE7B7FD2039A76B006C40B4 /* Release */,
1505 | );
1506 | defaultConfigurationIsVisible = 0;
1507 | defaultConfigurationName = Release;
1508 | };
1509 | ECE7B8112039AA14006C40B4 /* Build configuration list for PBXNativeTarget "Binders" */ = {
1510 | isa = XCConfigurationList;
1511 | buildConfigurations = (
1512 | ECE7B8122039AA14006C40B4 /* Debug */,
1513 | ECE7B8132039AA14006C40B4 /* Release */,
1514 | );
1515 | defaultConfigurationIsVisible = 0;
1516 | defaultConfigurationName = Release;
1517 | };
1518 | /* End XCConfigurationList section */
1519 | };
1520 | rootObject = ECE7B740203867AF006C40B4 /* Project object */;
1521 | }
1522 |
--------------------------------------------------------------------------------