├── 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 | --------------------------------------------------------------------------------